Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,13 @@ ReactDOM.render(
</tr>
<tr>
<td>onSelect</td>
<td>function({key:String, item:ReactComponent, domEvent:Event, selectedKeys:String[]})</td>
<td>function({key:String, item:ReactComponent, domEvent:Event, selectedKeys:String[], info:{item:MenuItemInfo}})</td>
<th></th>
<td>called when select a menu item</td>
</tr>
<tr>
<td>onClick</td>
<td>function({key:String, item:ReactComponent, domEvent:Event, keyPath: String[]})</td>
<td>function({key:String, item:ReactComponent, domEvent:Event, keyPath: String[], info:{item:MenuItemInfo}})</td>
<th></th>
<td>called when click a menu item</td>
</tr>
Expand Down
15 changes: 14 additions & 1 deletion src/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import PrivateContext from './context/PrivateContext';
import useActive from './hooks/useActive';
import useDirectionStyle from './hooks/useDirectionStyle';
import Icon from './Icon';
import type { MenuInfo, MenuItemType } from './interface';
import type { MenuInfo, MenuItemInfo, MenuItemType } from './interface';
import { warnItemProp } from './utils/warnUtil';

export interface MenuItemProps
Expand All @@ -32,6 +32,9 @@ export interface MenuItemProps

/** @deprecated No place to use this. Should remove */
attribute?: Record<string, string>;

/** @private Origin item config from items prop */
info?: { item: MenuItemInfo };
}

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

// Aria
role,
Expand Down Expand Up @@ -133,12 +137,21 @@ const InternalMenuItem = React.forwardRef((props: MenuItemProps, ref: React.Ref<
const getEventInfo = (
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
): MenuInfo => {
// If propsInfo exists (items mode), use it; otherwise build from props (children mode)
const infoItem: MenuItemInfo = propsInfo?.item || {
key: eventKey || '',
label: children,
itemIcon,
extra: props.extra,
};

return {
key: eventKey,
// Note: For legacy code is reversed which not like other antd component
keyPath: [...connectedKeys].reverse(),
item: legacyMenuItemRef.current,
domEvent: e,
info: propsInfo || { item: infoItem },
};
};

Expand Down
9 changes: 9 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export interface MenuItemType extends ItemSharedProps {
onClick?: MenuClickEventHandler;
}

/** Info item type passed to onSelect/onClick callbacks, excluding event handlers */
export type MenuItemInfo = {
label?: React.ReactNode;
itemIcon?: RenderIconType;
extra?: React.ReactNode;
key: React.Key;
};

export interface MenuItemGroupType extends ItemSharedProps {
type: 'group';

Expand Down Expand Up @@ -99,6 +107,7 @@ export interface MenuInfo {
/** @deprecated This will not support in future. You should avoid to use this */
item: React.ReactInstance;
domEvent: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>;
info: { item: MenuItemInfo };
}

export interface MenuTitleInfo {
Expand Down
7 changes: 6 additions & 1 deletion src/utils/nodeUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ function convertItemsToNodes(
const hasExtra = !!extra || extra === 0;

return (
<MergedMenuItem key={mergedKey} {...restProps} extra={extra}>
<MergedMenuItem
key={mergedKey}
{...restProps}
extra={extra}
info={{ item: { label, key: mergedKey, itemIcon: restProps?.itemIcon, extra } }}
>
{hasExtra ? (
<>
<span className={`${prefixCls}-item-label`}>{label}</span>
Expand Down
73 changes: 73 additions & 0 deletions tests/MenuItem.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,79 @@ describe('MenuItem', () => {
});
});

describe('info.item in event', () => {
it('should pass info.item in onSelect and onClick with children', () => {
const onSelect = jest.fn();
const onClick = jest.fn();
const { container } = render(
<Menu onSelect={onSelect} onClick={onClick} selectable>
<MenuItem key="1">Menu Item</MenuItem>
</Menu>,
);

fireEvent.click(container.querySelector('.rc-menu-item')!);
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({
key: '1',
info: expect.objectContaining({
item: expect.objectContaining({
key: '1',
label: 'Menu Item',
}),
}),
}),
);
expect(onClick).toHaveBeenCalledWith(
expect.objectContaining({
key: '1',
info: expect.objectContaining({
item: expect.objectContaining({
key: '1',
label: 'Menu Item',
}),
}),
}),
);
});

it('should pass info.item in onSelect and onClick with items', () => {
const onSelect = jest.fn();
const onClick = jest.fn();
const { container } = render(
<Menu
onSelect={onSelect}
onClick={onClick}
selectable
items={[{ key: '1', label: 'Menu Item' }]}
/>,
);

fireEvent.click(container.querySelector('.rc-menu-item')!);
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({
key: '1',
info: expect.objectContaining({
item: expect.objectContaining({
key: '1',
label: 'Menu Item',
}),
}),
}),
);
expect(onClick).toHaveBeenCalledWith(
expect.objectContaining({
key: '1',
info: expect.objectContaining({
item: expect.objectContaining({
key: '1',
label: 'Menu Item',
}),
}),
}),
);
});
});

describe('overwrite default role', () => {
it('should set role to none if null', () => {
const { container } = render(
Expand Down
Loading