mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
feat: CP support Menu expandIcon (#47561)
* feat: CP support Menu expandIcon * fix: test case fix * fix: test case fix * fix: fix * fix: fix * chore: rename * type: add type --------- Signed-off-by: lijianan <574980606@qq.com>
This commit is contained in:
parent
f46af9366b
commit
b28b0d883f
@ -28,6 +28,7 @@ import Layout from '../../layout';
|
||||
import List from '../../list';
|
||||
import Mentions from '../../mentions';
|
||||
import Menu from '../../menu';
|
||||
import type { MenuProps } from '../../menu';
|
||||
import message from '../../message';
|
||||
import Modal from '../../modal';
|
||||
import notification from '../../notification';
|
||||
@ -575,24 +576,32 @@ describe('ConfigProvider support style and className props', () => {
|
||||
expect(container.querySelector('.ant-list')).toHaveStyle('color: red; font-size: 16px;');
|
||||
});
|
||||
|
||||
it('Should Menu className works', () => {
|
||||
const menuItems = [
|
||||
it('Should Menu className & expandIcon works', () => {
|
||||
const menuItems: MenuProps['items'] = [
|
||||
{
|
||||
label: 'Test Label',
|
||||
label: <span>Test Label</span>,
|
||||
key: 'test',
|
||||
children: [
|
||||
{
|
||||
label: <span>Test Label children</span>,
|
||||
key: 'test-children',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
menu={{
|
||||
className: 'test-class',
|
||||
}}
|
||||
>
|
||||
const App: React.FC<{ expand?: React.ReactNode }> = ({ expand }) => (
|
||||
<ConfigProvider menu={{ className: 'test-class', expandIcon: expand }}>
|
||||
<Menu items={menuItems} />
|
||||
</ConfigProvider>,
|
||||
</ConfigProvider>
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-menu')).toHaveClass('test-class');
|
||||
const { container, rerender } = render(<App />);
|
||||
expect(container.querySelector<HTMLElement>('.ant-menu')).toHaveClass('test-class');
|
||||
rerender(<App expand={<span className="test-cp-icon">test-cp-icon</span>} />);
|
||||
expect(container.querySelector<HTMLSpanElement>('.ant-menu .test-cp-icon')).toBeTruthy();
|
||||
rerender(<App expand={null} />);
|
||||
expect(container.querySelector<HTMLElement>('.ant-menu-submenu-arrow')).toBeFalsy();
|
||||
rerender(<App expand={false} />);
|
||||
expect(container.querySelector<HTMLElement>('.ant-menu-submenu-arrow')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should Menu style works', () => {
|
||||
|
@ -12,6 +12,7 @@ import type { FlexProps } from '../flex/interface';
|
||||
import type { FormProps } from '../form/Form';
|
||||
import type { InputProps } from '../input';
|
||||
import type { Locale } from '../locale';
|
||||
import type { MenuProps } from '../menu';
|
||||
import type { ModalProps } from '../modal';
|
||||
import type { ArgsProps } from '../notification/interface';
|
||||
import type { PaginationProps } from '../pagination';
|
||||
@ -85,6 +86,8 @@ export interface ImageConfig extends ComponentStyleConfig {
|
||||
|
||||
export type CollapseConfig = ComponentStyleConfig & Pick<CollapseProps, 'expandIcon'>;
|
||||
|
||||
export type MenuConfig = ComponentStyleConfig & Pick<MenuProps, 'expandIcon'>;
|
||||
|
||||
export type TourConfig = Pick<TourProps, 'closeIcon'>;
|
||||
|
||||
export type ModalConfig = ComponentStyleConfig &
|
||||
@ -167,7 +170,7 @@ export interface ConfigConsumerProps {
|
||||
result?: ComponentStyleConfig;
|
||||
slider?: ComponentStyleConfig;
|
||||
breadcrumb?: ComponentStyleConfig;
|
||||
menu?: ComponentStyleConfig;
|
||||
menu?: MenuConfig;
|
||||
checkbox?: ComponentStyleConfig;
|
||||
descriptions?: ComponentStyleConfig;
|
||||
empty?: ComponentStyleConfig;
|
||||
|
@ -127,7 +127,7 @@ const {
|
||||
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 4.2.0 |
|
||||
| layout | Set Layout common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| list | Set List common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| menu | Set Menu common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| menu | Set Menu common props | { className?: string, style?: React.CSSProperties, expandIcon?: ReactNode \| props => ReactNode } | - | 5.7.0, expandIcon: 5.15.0 |
|
||||
| mentions | Set Mentions common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| message | Set Message common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| modal | Set Modal common props | { className?: string, style?: React.CSSProperties, classNames?: [ModalProps\["classNames"\]](/components/modal-cn#api), styles?: [ModalProps\["styles"\]](/components/modal-cn#api), closeIcon?: React.ReactNode } | - | 5.7.0, `classNames` and `styles`: 5.10.0, `closeIcon`: 5.14.0 |
|
||||
|
@ -32,6 +32,7 @@ import type {
|
||||
DrawerConfig,
|
||||
FlexConfig,
|
||||
ImageConfig,
|
||||
MenuConfig,
|
||||
ModalConfig,
|
||||
NotificationConfig,
|
||||
PopupOverflow,
|
||||
@ -161,7 +162,7 @@ export interface ConfigProviderProps {
|
||||
result?: ComponentStyleConfig;
|
||||
slider?: ComponentStyleConfig;
|
||||
breadcrumb?: ComponentStyleConfig;
|
||||
menu?: ComponentStyleConfig;
|
||||
menu?: MenuConfig;
|
||||
checkbox?: ComponentStyleConfig;
|
||||
descriptions?: ComponentStyleConfig;
|
||||
empty?: ComponentStyleConfig;
|
||||
|
@ -129,7 +129,7 @@ const {
|
||||
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| layout | 设置 Layout 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| list | 设置 List 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| menu | 设置 Menu 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| menu | 设置 Menu 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandIcon?: ReactNode \| props => ReactNode } | - | 5.7.0, expandIcon: 5.15.0 |
|
||||
| mentions | 设置 Mentions 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| message | 设置 Message 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| modal | 设置 Modal 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ModalProps\["classNames"\]](/components/modal-cn#api), styles?: [ModalProps\["styles"\]](/components/modal-cn#api), closeIcon?: React.ReactNode } | - | 5.7.0, `classNames` 和 `styles`: 5.10.0, `closeIcon`: 5.14.0 |
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
AppstoreOutlined,
|
||||
InboxOutlined,
|
||||
@ -5,15 +6,15 @@ import {
|
||||
PieChartOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import type { MenuProps, MenuRef } from '..';
|
||||
import Menu from '..';
|
||||
import initCollapseMotion from '../../_util/motion';
|
||||
import { noop } from '../../_util/warning';
|
||||
import { TriggerMockContext } from '../../../tests/shared/demoTestContext';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act, fireEvent, render } from '../../../tests/utils';
|
||||
import initCollapseMotion from '../../_util/motion';
|
||||
import { noop } from '../../_util/warning';
|
||||
import Layout from '../../layout';
|
||||
import OverrideContext from '../OverrideContext';
|
||||
|
||||
@ -1126,7 +1127,7 @@ describe('Menu', () => {
|
||||
});
|
||||
|
||||
it('hide expand icon when pass null or false into expandIcon', () => {
|
||||
const App = ({ expand }: { expand?: React.ReactNode }) => (
|
||||
const App: React.FC<{ expand?: React.ReactNode }> = ({ expand }) => (
|
||||
<Menu
|
||||
expandIcon={expand}
|
||||
items={[
|
||||
|
@ -20,6 +20,10 @@ import MenuContext from './MenuContext';
|
||||
import OverrideContext from './OverrideContext';
|
||||
import useStyle from './style';
|
||||
|
||||
function isEmptyIcon(icon?: React.ReactNode) {
|
||||
return icon === null || icon === false;
|
||||
}
|
||||
|
||||
export interface MenuProps extends Omit<RcMenuProps, 'items'> {
|
||||
theme?: MenuTheme;
|
||||
inlineIndent?: number;
|
||||
@ -114,7 +118,7 @@ const InternalMenu = forwardRef<RcMenuRef, InternalMenuProps>((props, ref) => {
|
||||
return inlineCollapsed;
|
||||
}, [inlineCollapsed, siderCollapsed]);
|
||||
|
||||
const defaultMotions = {
|
||||
const defaultMotions: MenuProps['defaultMotions'] = {
|
||||
horizontal: { motionName: `${rootPrefixCls}-slide-up` },
|
||||
inline: initCollapseMotion(rootPrefixCls),
|
||||
other: { motionName: `${rootPrefixCls}-zoom-big` },
|
||||
@ -125,23 +129,25 @@ const InternalMenu = forwardRef<RcMenuRef, InternalMenuProps>((props, ref) => {
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls, !override);
|
||||
const menuClassName = classNames(`${prefixCls}-${theme}`, menu?.className, className);
|
||||
|
||||
// ====================== Expand Icon ========================
|
||||
let mergedExpandIcon: MenuProps['expandIcon'];
|
||||
if (typeof expandIcon === 'function') {
|
||||
mergedExpandIcon = expandIcon;
|
||||
} else if (expandIcon === null || expandIcon === false) {
|
||||
mergedExpandIcon = null;
|
||||
} else if (overrideObj.expandIcon === null || overrideObj.expandIcon === false) {
|
||||
mergedExpandIcon = null;
|
||||
} else {
|
||||
const beClone: React.ReactNode = (expandIcon ?? overrideObj.expandIcon) as React.ReactNode;
|
||||
mergedExpandIcon = cloneElement(beClone, {
|
||||
// ====================== ExpandIcon ========================
|
||||
const mergedExpandIcon = React.useMemo<MenuProps['expandIcon']>(() => {
|
||||
if (typeof expandIcon === 'function' || isEmptyIcon(expandIcon)) {
|
||||
return expandIcon || null;
|
||||
}
|
||||
if (typeof overrideObj.expandIcon === 'function' || isEmptyIcon(overrideObj.expandIcon)) {
|
||||
return overrideObj.expandIcon || null;
|
||||
}
|
||||
if (typeof menu?.expandIcon === 'function' || isEmptyIcon(menu?.expandIcon)) {
|
||||
return menu?.expandIcon || null;
|
||||
}
|
||||
const mergedIcon = expandIcon ?? overrideObj?.expandIcon ?? menu?.expandIcon;
|
||||
return cloneElement(mergedIcon, {
|
||||
className: classNames(
|
||||
`${prefixCls}-submenu-expand-icon`,
|
||||
React.isValidElement(beClone) ? beClone.props?.className : '',
|
||||
React.isValidElement(mergedIcon) ? mergedIcon.props?.className : undefined,
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [expandIcon, overrideObj?.expandIcon, menu?.expandIcon, prefixCls]);
|
||||
|
||||
// ======================== Context ==========================
|
||||
const contextValue = React.useMemo<MenuContextProps>(
|
||||
|
Loading…
Reference in New Issue
Block a user