Skip to content

Commit 7184ed5

Browse files
刘欢claude
andcommitted
feat: add info.item to onSelect and onClick callback
- Add info.item to MenuInfo interface to expose menu item config (label, icon, disabled, extra, etc.) - Support both items config and children mode - Add unit tests for info.item in onSelect and onClick - Update README documentation Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 038c4b3 commit 7184ed5

5 files changed

Lines changed: 91 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,13 @@ ReactDOM.render(
135135
</tr>
136136
<tr>
137137
<td>onSelect</td>
138-
<td>function({key:String, item:ReactComponent, domEvent:Event, selectedKeys:String[]})</td>
138+
<td>function({key:String, item:ReactComponent, domEvent:Event, selectedKeys:String[], info:{item:MenuItemType}})</td>
139139
<th></th>
140140
<td>called when select a menu item</td>
141141
</tr>
142142
<tr>
143143
<td>onClick</td>
144-
<td>function({key:String, item:ReactComponent, domEvent:Event, keyPath: String[]})</td>
144+
<td>function({key:String, item:ReactComponent, domEvent:Event, keyPath: String[], info:{item:MenuItemType}})</td>
145145
<th></th>
146146
<td>called when click a menu item</td>
147147
</tr>

src/MenuItem.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export interface MenuItemProps
3232

3333
/** @deprecated No place to use this. Should remove */
3434
attribute?: Record<string, string>;
35+
36+
/** @private Origin item config from items prop */
37+
info?: { item: MenuItemType };
3538
}
3639

3740
// Since Menu event provide the `info.item` which point to the MenuItem node instance.
@@ -77,6 +80,7 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
7780
disabled,
7881
itemIcon,
7982
children,
83+
info: propsInfo,
8084

8185
// Aria
8286
role,
@@ -133,12 +137,22 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
133137
const getEventInfo = (
134138
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
135139
): MenuInfo => {
140+
// If propsInfo exists (items mode), use it; otherwise build from props (children mode)
141+
const infoItem: MenuItemType = propsInfo?.item || {
142+
key: eventKey || '',
143+
label: children,
144+
disabled,
145+
itemIcon,
146+
extra: props.extra,
147+
};
148+
136149
return {
137150
key: eventKey,
138151
// Note: For legacy code is reversed which not like other antd component
139152
keyPath: [...connectedKeys].reverse(),
140153
item: legacyMenuItemRef.current,
141154
domEvent: e,
155+
info: propsInfo || { item: infoItem },
142156
};
143157
};
144158

src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export interface MenuInfo {
9999
/** @deprecated This will not support in future. You should avoid to use this */
100100
item: React.ReactInstance;
101101
domEvent: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>;
102+
info: { item: MenuItemType };
102103
}
103104

104105
export interface MenuTitleInfo {

src/utils/nodeUtil.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function convertItemsToNodes(
5151
const hasExtra = !!extra || extra === 0;
5252

5353
return (
54-
<MergedMenuItem key={mergedKey} {...restProps} extra={extra}>
54+
<MergedMenuItem key={mergedKey} {...restProps} extra={extra} info={{ item: opt }}>
5555
{hasExtra ? (
5656
<>
5757
<span className={`${prefixCls}-item-label`}>{label}</span>

tests/MenuItem.spec.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,79 @@ describe('MenuItem', () => {
150150
});
151151
});
152152

153+
describe('info.item in event', () => {
154+
it('should pass info.item in onSelect and onClick with children', () => {
155+
const onSelect = jest.fn();
156+
const onClick = jest.fn();
157+
const { container } = render(
158+
<Menu onSelect={onSelect} onClick={onClick} selectable>
159+
<MenuItem key="1">Menu Item</MenuItem>
160+
</Menu>,
161+
);
162+
163+
fireEvent.click(container.querySelector('.rc-menu-item')!);
164+
expect(onSelect).toHaveBeenCalledWith(
165+
expect.objectContaining({
166+
key: '1',
167+
info: expect.objectContaining({
168+
item: expect.objectContaining({
169+
key: '1',
170+
label: 'Menu Item',
171+
}),
172+
}),
173+
}),
174+
);
175+
expect(onClick).toHaveBeenCalledWith(
176+
expect.objectContaining({
177+
key: '1',
178+
info: expect.objectContaining({
179+
item: expect.objectContaining({
180+
key: '1',
181+
label: 'Menu Item',
182+
}),
183+
}),
184+
}),
185+
);
186+
});
187+
188+
it('should pass info.item in onSelect and onClick with items', () => {
189+
const onSelect = jest.fn();
190+
const onClick = jest.fn();
191+
const { container } = render(
192+
<Menu
193+
onSelect={onSelect}
194+
onClick={onClick}
195+
selectable
196+
items={[{ key: '1', label: 'Menu Item' }]}
197+
/>,
198+
);
199+
200+
fireEvent.click(container.querySelector('.rc-menu-item')!);
201+
expect(onSelect).toHaveBeenCalledWith(
202+
expect.objectContaining({
203+
key: '1',
204+
info: expect.objectContaining({
205+
item: expect.objectContaining({
206+
key: '1',
207+
label: 'Menu Item',
208+
}),
209+
}),
210+
}),
211+
);
212+
expect(onClick).toHaveBeenCalledWith(
213+
expect.objectContaining({
214+
key: '1',
215+
info: expect.objectContaining({
216+
item: expect.objectContaining({
217+
key: '1',
218+
label: 'Menu Item',
219+
}),
220+
}),
221+
}),
222+
);
223+
});
224+
});
225+
153226
describe('overwrite default role', () => {
154227
it('should set role to none if null', () => {
155228
const { container } = render(

0 commit comments

Comments
 (0)