Skip to content

Commit 18cb788

Browse files
committed
feat: Support custom rendering
1 parent c1b586d commit 18cb788

File tree

4 files changed

+75
-22
lines changed

4 files changed

+75
-22
lines changed

docs/examples/items.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ export default () => (
1313
key: 'top',
1414
extra: '⌘B',
1515
},
16+
{
17+
key: 'ToOriginNode',
18+
label: 'Navigation Two',
19+
render: originNode => (
20+
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
21+
{originNode}
22+
</a>
23+
),
24+
},
25+
{
26+
key: 'ToOriginNode',
27+
label: 'SubMenu',
28+
render: (_originNode, item) => <p>item: extra: {JSON.stringify(item)}</p>,
29+
},
1630
{
1731
// MenuGroup
1832
type: 'group',

src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface ItemSharedProps {
66
ref?: React.Ref<HTMLLIElement | null>;
77
style?: React.CSSProperties;
88
className?: string;
9+
render?: (originalNode: React.ReactNode, item: ItemType) => React.ReactNode;
910
}
1011

1112
export interface SubMenuType extends ItemSharedProps {

src/utils/nodeUtil.tsx

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,41 +21,47 @@ function convertItemsToNodes(
2121
return (list || [])
2222
.map((opt, index) => {
2323
if (opt && typeof opt === 'object') {
24-
const { label, children, key, type, extra, ...restProps } = opt as any;
24+
const { label, children, key, type, extra, render, ...restProps } = opt as any;
2525
const mergedKey = key ?? `tmp-${index}`;
2626

27-
// MenuItemGroup & SubMenuItem
27+
let originNode: React.ReactNode = null;
28+
29+
// MenuItemGroup & SubMenu
2830
if (children || type === 'group') {
2931
if (type === 'group') {
30-
// Group
31-
return (
32+
originNode = (
3233
<MergedMenuItemGroup key={mergedKey} {...restProps} title={label}>
3334
{convertItemsToNodes(children, components, prefixCls)}
3435
</MergedMenuItemGroup>
3536
);
37+
} else {
38+
originNode = (
39+
<MergedSubMenu key={mergedKey} {...restProps} title={label}>
40+
{convertItemsToNodes(children, components, prefixCls)}
41+
</MergedSubMenu>
42+
);
3643
}
37-
38-
// Sub Menu
39-
return (
40-
<MergedSubMenu key={mergedKey} {...restProps} title={label}>
41-
{convertItemsToNodes(children, components, prefixCls)}
42-
</MergedSubMenu>
44+
}
45+
// Divider
46+
else if (type === 'divider') {
47+
originNode = <MergedDivider key={mergedKey} {...restProps} />;
48+
}
49+
// MenuItem
50+
else {
51+
originNode = (
52+
<MergedMenuItem key={mergedKey} {...restProps} extra={extra}>
53+
{label}
54+
{(!!extra || extra === 0) && (
55+
<span className={`${prefixCls}-item-extra`}>{extra}</span>
56+
)}
57+
</MergedMenuItem>
4358
);
4459
}
4560

46-
// MenuItem & Divider
47-
if (type === 'divider') {
48-
return <MergedDivider key={mergedKey} {...restProps} />;
61+
if (typeof render === 'function') {
62+
return render(originNode, opt);
4963
}
50-
51-
return (
52-
<MergedMenuItem key={mergedKey} {...restProps} extra={extra}>
53-
{label}
54-
{(!!extra || extra === 0) && (
55-
<span className={`${prefixCls}-item-extra`}>{extra}</span>
56-
)}
57-
</MergedMenuItem>
58-
);
64+
return originNode;
5965
}
6066

6167
return null;

tests/MenuItem.spec.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,5 +228,37 @@ describe('MenuItem', () => {
228228

229229
expect(container.querySelector('li')).toMatchSnapshot();
230230
});
231+
232+
it('should wrap originNode with custom render', () => {
233+
const { container } = render(
234+
<Menu
235+
items={[
236+
{
237+
key: 'mail',
238+
label: 'Navigation One',
239+
},
240+
{
241+
key: 'app',
242+
label: 'Navigation Two',
243+
render: originNode => (
244+
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
245+
{originNode}
246+
</a>
247+
),
248+
},
249+
{
250+
key: 'upload',
251+
label: 'Upload File',
252+
render: () => <div id="upload">extra</div>,
253+
},
254+
]}
255+
/>,
256+
);
257+
258+
const link = container.querySelector('a');
259+
expect(link).toHaveAttribute('href', 'https://ant.design');
260+
261+
expect(container.querySelector('#upload')).toHaveTextContent('extra');
262+
});
231263
});
232264
});

0 commit comments

Comments
 (0)