Skip to content

Commit a99b330

Browse files
authored
feat: support classNames and styles for menu (#775)
* feat: support classNames and styles for menu * fix
1 parent b7fd486 commit a99b330

File tree

4 files changed

+97
-31
lines changed

4 files changed

+97
-31
lines changed

docs/examples/menuItemGroup.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import '../../assets/index.less';
88
export default () => (
99
<div>
1010
<h2>menu item group</h2>
11-
<Menu style={{ margin: 20, width: 300 }} onClick={() => console.log('click')}>
11+
<Menu
12+
style={{ margin: 20, width: 300 }}
13+
onClick={() => console.log('click')}
14+
classNames={{ listTitle: 'test-title', list: 'test-list' }}
15+
>
1216
<MenuItemGroup title="group 1" key="2">
1317
<MenuItem key="21">2</MenuItem>
1418
<MenuItem key="22">3</MenuItem>

src/Menu.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import type {
3030
PopupRender,
3131
} from './interface';
3232
import MenuItem from './MenuItem';
33-
import SubMenu from './SubMenu';
33+
import SubMenu, { SemanticName } from './SubMenu';
3434
import { parseItems } from './utils/nodeUtil';
3535
import { warnItemProp } from './utils/warnUtil';
3636

@@ -54,6 +54,8 @@ export interface MenuProps
5454
extends Omit<React.HTMLAttributes<HTMLUListElement>, 'onClick' | 'onSelect' | 'dir'> {
5555
prefixCls?: string;
5656
rootClassName?: string;
57+
classNames?: Partial<Record<SemanticName, string>>;
58+
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
5759
items?: ItemType[];
5860

5961
/** @deprecated Please use `items` instead */
@@ -168,6 +170,8 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
168170
rootClassName,
169171
style,
170172
className,
173+
styles,
174+
classNames: menuClassNames,
171175
tabIndex = 0,
172176
items,
173177
children,
@@ -544,7 +548,12 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
544548
: // Need wrap for overflow dropdown that do not response for open
545549
childList.map((child, index) => (
546550
// Always wrap provider to avoid sub node re-mount
547-
<MenuContextProvider key={child.key} overflowDisabled={index > lastVisibleIndex}>
551+
<MenuContextProvider
552+
key={child.key}
553+
overflowDisabled={index > lastVisibleIndex}
554+
classNames={menuClassNames}
555+
styles={styles}
556+
>
548557
{child}
549558
</MenuContextProvider>
550559
));
@@ -614,6 +623,8 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
614623
<MenuContextProvider
615624
prefixCls={prefixCls}
616625
rootClassName={rootClassName}
626+
classNames={menuClassNames}
627+
styles={styles}
617628
mode={internalMode}
618629
openKeys={mergedOpenKeys}
619630
rtl={isRtl}

tests/SubMenu.spec.tsx

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { act, fireEvent, render } from '@testing-library/react';
33
import { resetWarned } from '@rc-component/util/lib/warning';
44
import React from 'react';
5-
import Menu, { MenuItem, MenuItemGroup, SubMenu } from '../src';
5+
import Menu, { MenuItem, SubMenu } from '../src';
66
import { isActive, last } from './util';
77

88
jest.mock('@rc-component/trigger', () => {
@@ -483,32 +483,5 @@ describe('SubMenu', () => {
483483
'150px',
484484
);
485485
});
486-
it('support classNames and styles', () => {
487-
const testClassNames = {
488-
list: 'test-list',
489-
listTitle: 'test-list-title',
490-
};
491-
const testStyles = {
492-
list: { color: 'red' },
493-
listTitle: { color: 'blue' },
494-
};
495-
const { container } = render(
496-
<Menu openKeys={['s1']}>
497-
<SubMenu key="s1" title="submenu1" classNames={testClassNames} styles={testStyles}>
498-
<MenuItemGroup title="group 1" key="2">
499-
<MenuItem key="s1-1">1</MenuItem>
500-
</MenuItemGroup>
501-
</SubMenu>
502-
</Menu>,
503-
);
504-
fireEvent.mouseEnter(container.querySelector('.rc-menu-submenu-title'));
505-
runAllTimer();
506-
const list = container.querySelector('.rc-menu-item-group-list');
507-
const listTitle = container.querySelector('.rc-menu-item-group-title');
508-
expect(list).toHaveClass(testClassNames.list);
509-
expect(list).toHaveStyle(testStyles.list);
510-
expect(listTitle).toHaveClass(testClassNames.listTitle);
511-
expect(listTitle).toHaveStyle(testStyles.listTitle);
512-
});
513486
});
514487
/* eslint-enable */

tests/semantic.spec.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* eslint-disable no-undef */
2+
import { act, fireEvent, render } from '@testing-library/react';
3+
import React from 'react';
4+
import Menu, { MenuItem, MenuItemGroup, SubMenu } from '../src';
5+
describe('semantic', () => {
6+
function runAllTimer() {
7+
for (let i = 0; i < 10; i += 1) {
8+
act(() => {
9+
jest.runAllTimers();
10+
});
11+
}
12+
}
13+
beforeEach(() => {
14+
global.triggerProps = null;
15+
global.popupTriggerProps = null;
16+
jest.useFakeTimers();
17+
});
18+
19+
afterEach(() => {
20+
jest.useRealTimers();
21+
});
22+
23+
it('support classNames and styles for subMenu', () => {
24+
const testClassNames = {
25+
list: 'test-list',
26+
listTitle: 'test-list-title',
27+
};
28+
const testStyles = {
29+
list: { color: 'red' },
30+
listTitle: { color: 'blue' },
31+
};
32+
const { container } = render(
33+
<Menu openKeys={['s1']}>
34+
<SubMenu key="s1" title="submenu1" classNames={testClassNames} styles={testStyles}>
35+
<MenuItemGroup title="group 1" key="2">
36+
<MenuItem key="s1-1">1</MenuItem>
37+
</MenuItemGroup>
38+
</SubMenu>
39+
</Menu>,
40+
);
41+
fireEvent.mouseEnter(container.querySelector('.rc-menu-submenu-title'));
42+
runAllTimer();
43+
const list = container.querySelector('.rc-menu-item-group-list');
44+
const listTitle = container.querySelector('.rc-menu-item-group-title');
45+
expect(list).toHaveClass(testClassNames.list);
46+
expect(list).toHaveStyle(testStyles.list);
47+
expect(listTitle).toHaveClass(testClassNames.listTitle);
48+
expect(listTitle).toHaveStyle(testStyles.listTitle);
49+
});
50+
it('support classNames and styles for Menu', () => {
51+
const testClassNames = {
52+
list: 'test-list',
53+
listTitle: 'test-list-title',
54+
};
55+
const testStyles = {
56+
list: { color: 'red' },
57+
listTitle: { color: 'blue' },
58+
};
59+
const { container } = render(
60+
<Menu onClick={() => console.log('click')} classNames={testClassNames} styles={testStyles}>
61+
<MenuItemGroup title="group 1" key="2">
62+
<MenuItem key="21">2</MenuItem>
63+
<MenuItem key="22">3</MenuItem>
64+
</MenuItemGroup>
65+
<MenuItemGroup title="group 2" key="3">
66+
<MenuItem key="31">4</MenuItem>
67+
<MenuItem key="32">5</MenuItem>
68+
</MenuItemGroup>
69+
</Menu>,
70+
);
71+
const list = container.querySelector('.rc-menu-item-group-list');
72+
const listTitle = container.querySelector('.rc-menu-item-group-title');
73+
expect(list).toHaveClass(testClassNames.list);
74+
expect(list).toHaveStyle(testStyles.list);
75+
expect(listTitle).toHaveClass(testClassNames.listTitle);
76+
expect(listTitle).toHaveStyle(testStyles.listTitle);
77+
});
78+
});

0 commit comments

Comments
 (0)