feat(menu): support extra prop (#50431)

* feat(dropdown): support extra prop

* text: fix items

* Update components/menu/style/index.ts

Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: ice <49827327+coding-ice@users.noreply.github.com>

* Update package.json

Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: ice <49827327+coding-ice@users.noreply.github.com>

* style: css

* chore: bump rc-menu from 9.15.1 to 9.15.0

* style: classname

* test: clsname

* chore: rc-menu to ^9.15.1

* chore: rc-menu version from ^ to ~

* chore: update deps

* feat: full width

* fix: children is array

* fix: default array

* fix: label

* test: update snap

* test: case

---------

Signed-off-by: ice <49827327+coding-ice@users.noreply.github.com>
Co-authored-by: lijianan <574980606@qq.com>
This commit is contained in:
ice 2024-09-03 07:15:18 +08:00 committed by GitHub
parent a0a22bdeeb
commit eac5b6893d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 362 additions and 18 deletions

View File

@ -3893,6 +3893,219 @@ Array [
exports[`renders components/dropdown/demo/event.tsx extend context correctly 2`] = `[]`;
exports[`renders components/dropdown/demo/extra.tsx extend context correctly 1`] = `
Array [
<a
class="ant-dropdown-trigger"
>
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
Hover me
</div>
<div
class="ant-space-item"
>
<span
aria-label="down"
class="anticon anticon-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</div>
</div>
</a>,
<div
class="ant-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-dropdown-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
data-menu-list="true"
role="menu"
tabindex="0"
>
<li
aria-disabled="true"
class="ant-dropdown-menu-item ant-dropdown-menu-item-disabled ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-1"
role="menuitem"
>
<span
class="ant-dropdown-menu-title-content"
>
My Account
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<li
class="ant-dropdown-menu-item-divider"
role="separator"
/>
<li
class="ant-dropdown-menu-item"
data-menu-id="rc-menu-uuid-test-2"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
Profile
<span
class="ant-dropdown-menu-item-extra"
>
⌘P
</span>
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<li
class="ant-dropdown-menu-item"
data-menu-id="rc-menu-uuid-test-3"
role="menuitem"
tabindex="-1"
>
<span
class="ant-dropdown-menu-title-content"
>
Billing
<span
class="ant-dropdown-menu-item-extra"
>
⌘B
</span>
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
<li
class="ant-dropdown-menu-item"
data-menu-id="rc-menu-uuid-test-4"
role="menuitem"
tabindex="-1"
>
<span
aria-label="setting"
class="anticon anticon-setting ant-dropdown-menu-item-icon"
role="img"
>
<svg
aria-hidden="true"
data-icon="setting"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"
/>
</svg>
</span>
<span
class="ant-dropdown-menu-title-content"
>
Settings
<span
class="ant-dropdown-menu-item-extra"
>
⌘S
</span>
</span>
</li>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-tooltip ant-tooltip-placement-right"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
/>
</div>
</div>
</ul>
<div
aria-hidden="true"
style="display: none;"
/>
</div>,
]
`;
exports[`renders components/dropdown/demo/extra.tsx extend context correctly 2`] = `[]`;
exports[`renders components/dropdown/demo/icon-debug.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@ -591,6 +591,45 @@ exports[`renders components/dropdown/demo/event.tsx correctly 1`] = `
</a>
`;
exports[`renders components/dropdown/demo/extra.tsx correctly 1`] = `
<a
class="ant-dropdown-trigger"
>
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
Hover me
</div>
<div
class="ant-space-item"
>
<span
aria-label="down"
class="anticon anticon-down"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</div>
</div>
</a>
`;
exports[`renders components/dropdown/demo/icon-debug.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@ -351,4 +351,15 @@ describe('Dropdown', () => {
);
expect(container3.querySelector('button')).not.toHaveAttribute('disabled');
});
it('menu item with extra prop', () => {
const text = '⌘P';
const { container } = render(
<Dropdown menu={{ items: [{ label: 'profile', key: 1, extra: text }] }} open>
<a />
</Dropdown>,
);
expect(container.querySelector('.ant-dropdown-menu-item-extra')?.textContent).toBe(text);
});
});

View File

@ -0,0 +1,7 @@
## zh-CN
带有快捷方式的下拉菜单。
## en-US
The dropdown menu with shortcut.

View File

@ -0,0 +1,44 @@
import React from 'react';
import { DownOutlined, SettingOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';
const items: MenuProps['items'] = [
{
key: '1',
label: 'My Account',
disabled: true,
},
{
type: 'divider',
},
{
key: '2',
label: 'Profile',
extra: '⌘P',
},
{
key: '3',
label: 'Billing',
extra: '⌘B',
},
{
key: '4',
label: 'Settings',
icon: <SettingOutlined />,
extra: '⌘S',
},
];
const App: React.FC = () => (
<Dropdown menu={{ items }}>
<a onClick={(e) => e.preventDefault()}>
<Space>
Hover me
<DownOutlined />
</Space>
</a>
</Dropdown>
);
export default App;

View File

@ -17,6 +17,7 @@ When there are more than a few options to choose from, you can wrap them in a `D
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">Basic</code>
<code src="./demo/extra.tsx" version="5.21.0">Extra node</code>
<code src="./demo/placement.tsx">Placement</code>
<code src="./demo/arrow.tsx">Arrow</code>
<code src="./demo/item.tsx">Other elements</code>

View File

@ -21,6 +21,7 @@ demo:
<!-- prettier-ignore -->
<code src="./demo/basic.tsx">基本</code>
<code src="./demo/extra.tsx" version="5.21.0">额外节点</code>
<code src="./demo/placement.tsx">弹出位置</code>
<code src="./demo/arrow.tsx">箭头</code>
<code src="./demo/item.tsx">其他元素</code>

View File

@ -237,6 +237,8 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
},
[`${menuCls}-title-content`]: {
display: 'flex',
alignItems: 'center',
flex: 'auto',
'> a': {
@ -253,6 +255,13 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
content: '""',
},
},
[`${menuCls}-item-extra`]: {
paddingInlineStart: token.padding,
marginInlineStart: 'auto',
fontSize: token.fontSizeSM,
color: token.colorTextDescription,
},
},
// =========================== Item ===========================

View File

@ -516,11 +516,6 @@ exports[`renders components/mentions/demo/render-panel.tsx extend context correc
exports[`renders components/mentions/demo/render-panel.tsx extend context correctly 2`] = `
[
"Warning: Received \`%s\` for a non-boolean attribute \`%s\`.
If you want to write it to the DOM, pass a string instead: %s="%s" or %s={value.toString()}.
If you used to conditionally omit it with %s={condition && value}, pass %s={condition ? value : undefined} instead.%s",
"Warning: \`open\` of Mentions is only used for debug usage. Do not use in you production.",
]
`;

View File

@ -45,12 +45,14 @@ const MenuItem: GenericComponent = (props) => {
inlineCollapsed: isInlineCollapsed,
} = React.useContext<MenuContextProps>(MenuContext);
const renderItemChildren = (inlineCollapsed: boolean) => {
const label = (children as React.ReactNode[])?.[0];
const wrapNode = <span className={`${prefixCls}-title-content`}>{children}</span>;
// inline-collapsed.md demo 依赖 span 来隐藏文字,有 icon 属性,则内部包裹一个 span
// ref: https://github.com/ant-design/ant-design/pull/23456
if (!icon || (React.isValidElement(children) && children.type === 'span')) {
if (children && inlineCollapsed && firstLevel && typeof children === 'string') {
return <div className={`${prefixCls}-inline-collapsed-noicon`}>{children.charAt(0)}</div>;
if (children && inlineCollapsed && firstLevel && typeof label === 'string') {
return <div className={`${prefixCls}-inline-collapsed-noicon`}>{label.charAt(0)}</div>;
}
}
return wrapNode;

View File

@ -1160,4 +1160,11 @@ describe('Menu', () => {
);
expect(container.querySelector('.ant-menu-submenu-arrow')).toBeFalsy();
});
it('menu item with extra prop', () => {
const text = '⌘P';
const { container } = render(<Menu items={[{ label: 'profile', key: '1', extra: text }]} />);
expect(container.querySelector('.ant-menu-item-extra')?.textContent).toBe(text);
});
});

View File

@ -77,6 +77,7 @@ Common props ref[Common props](/docs/react/common-props)
| -------- | ------------------------------------ | --------- | ------------- | ------- |
| danger | Display the danger style | boolean | false | |
| disabled | Whether menu item is disabled | boolean | false | |
| extra | The extra of the menu item | ReactNode | - | 5.21.0 |
| icon | The icon of the menu item | ReactNode | - | |
| key | Unique ID of the menu item | string | - | |
| label | Menu label | ReactNode | - | |

View File

@ -74,14 +74,15 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Vn4XSqJFAxcAAA
#### MenuItemType
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------------------ | --------- | ------ | ---- |
| danger | 展示错误状态样式 | boolean | false | |
| disabled | 是否禁用 | boolean | false | |
| icon | 菜单图标 | ReactNode | - | |
| key | item 的唯一标志 | string | - | |
| label | 菜单项标题 | ReactNode | - | |
| title | 设置收缩时展示的悬浮标题 | string | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ------------------------ | --------- | ------ | ------ |
| danger | 展示错误状态样式 | boolean | false | |
| disabled | 是否禁用 | boolean | false | |
| extra | 额外节点 | ReactNode | - | 5.21.0 |
| icon | 菜单图标 | ReactNode | - | |
| key | item 的唯一标志 | string | - | |
| label | 菜单项标题 | ReactNode | - | |
| title | 设置收缩时展示的悬浮标题 | string | - | |
#### SubMenuType

View File

@ -635,13 +635,26 @@ const getBaseStyle: GenerateStyle<MenuToken> = (token) => {
},
[`${componentCls}-title-content`]: {
display: 'inline-flex',
alignItems: 'center',
transition: `color ${motionDurationSlow}`,
'> a:first-child': {
flexGrow: 1,
},
// https://github.com/ant-design/ant-design/issues/41143
[`> ${antCls}-typography-ellipsis-single-line`]: {
display: 'inline',
verticalAlign: 'unset',
},
[`${componentCls}-item-extra`]: {
marginInlineStart: 'auto',
paddingInlineStart: token.padding,
fontSize: token.fontSizeSM,
color: token.colorTextDescription,
},
},
[`${componentCls}-item a`]: {

View File

@ -123,8 +123,8 @@
"rc-image": "~7.9.0",
"rc-input": "~1.6.3",
"rc-input-number": "~9.2.0",
"rc-mentions": "~2.15.0",
"rc-menu": "~9.14.1",
"rc-mentions": "~2.16.0",
"rc-menu": "~9.15.1",
"rc-motion": "^2.9.2",
"rc-notification": "~5.6.0",
"rc-pagination": "~4.2.0",
@ -138,7 +138,7 @@
"rc-steps": "~6.0.1",
"rc-switch": "~4.1.0",
"rc-table": "~7.47.3",
"rc-tabs": "~15.1.1",
"rc-tabs": "~15.2.0",
"rc-textarea": "~1.8.1",
"rc-tooltip": "~6.2.0",
"rc-tree": "~5.8.8",