mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-25 00:08:27 +08:00
6a62d9e7ea
* fix(dropdown): dropdown menu title content style * chore: update rc-mentions to version 2.17.0 and rc-tabs to version 15.4.0 * test: add extra style debug * test: snap * test: snap * Refactor menu demo: remove extra-style-debug component and rename extra-style-debug files to extra-style * Refactor menu demo: remove extra-style-debug component and rename extra-style-debug files to extra-style * Refactor dropdown and menu styles: Add width to extra-style elements * test: snap * fix: Add Space component to menu demo The Space component was missing from the import statement in the menu demo file. This caused a compilation error. The fix adds the Space component to the import statement, resolving the issue. Refactor the menu demo to use Space component for vertical spacing between menus. This improves the visual layout and readability of the menus. Fixes #51492 * test: snap * refactor: Update dropdown and menu title content class names --------- Co-authored-by: afc163 <afc163@gmail.com>
369 lines
9.4 KiB
TypeScript
369 lines
9.4 KiB
TypeScript
import React from 'react';
|
|
import type { TriggerProps } from '@rc-component/trigger';
|
|
|
|
import type { DropDownProps } from '..';
|
|
import Dropdown from '..';
|
|
import { resetWarned } from '../../_util/warning';
|
|
import mountTest from '../../../tests/shared/mountTest';
|
|
import rtlTest from '../../../tests/shared/rtlTest';
|
|
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
|
|
|
let triggerProps: TriggerProps;
|
|
|
|
jest.mock('@rc-component/trigger', () => {
|
|
let Trigger = jest.requireActual('@rc-component/trigger/lib/mock');
|
|
Trigger = Trigger.default || Trigger;
|
|
const h: typeof React = jest.requireActual('react');
|
|
|
|
return {
|
|
default: h.forwardRef<HTMLElement, TriggerProps>((props, ref) => {
|
|
triggerProps = props;
|
|
return h.createElement(Trigger, { ref, ...props });
|
|
}),
|
|
__esModule: true,
|
|
};
|
|
});
|
|
|
|
describe('Dropdown', () => {
|
|
const items = [
|
|
{
|
|
label: 'foo',
|
|
key: '1',
|
|
},
|
|
];
|
|
|
|
mountTest(() => (
|
|
<Dropdown menu={{ items }}>
|
|
<span />
|
|
</Dropdown>
|
|
));
|
|
|
|
rtlTest(() => (
|
|
<Dropdown menu={{ items }}>
|
|
<span />
|
|
</Dropdown>
|
|
));
|
|
|
|
it('overlay is function and has custom transitionName', () => {
|
|
const { asFragment } = render(
|
|
<Dropdown overlay={() => <div>menu</div>} transitionName="move-up" open>
|
|
<button type="button">button</button>
|
|
</Dropdown>,
|
|
);
|
|
expect(Array.from(asFragment().childNodes)).toMatchSnapshot();
|
|
});
|
|
|
|
it('overlay is string', () => {
|
|
const { asFragment } = render(
|
|
<Dropdown overlay={'string' as any} open>
|
|
<button type="button">button</button>
|
|
</Dropdown>,
|
|
);
|
|
expect(Array.from(asFragment().childNodes)).toMatchSnapshot();
|
|
});
|
|
|
|
it('should render custom dropdown correctly', () => {
|
|
const { asFragment } = render(
|
|
<Dropdown
|
|
open
|
|
menu={{ items }}
|
|
dropdownRender={(menu) => (
|
|
<div>
|
|
{menu}
|
|
<div className="dropdown-custom-node">CUSTOM NODE</div>
|
|
</div>
|
|
)}
|
|
>
|
|
<button type="button">button</button>
|
|
</Dropdown>,
|
|
);
|
|
expect(Array.from(asFragment().childNodes)).toMatchSnapshot();
|
|
});
|
|
|
|
it('support Menu expandIcon', async () => {
|
|
jest.useFakeTimers();
|
|
const props: DropDownProps = {
|
|
menu: {
|
|
items: [
|
|
{
|
|
label: 'foo',
|
|
key: '1',
|
|
},
|
|
{
|
|
label: 'SubMenu',
|
|
key: 'submenu',
|
|
children: [
|
|
{
|
|
label: 'foo',
|
|
key: '1',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
expandIcon: <span id="customExpandIcon" />,
|
|
},
|
|
open: true,
|
|
getPopupContainer: (node) => node,
|
|
};
|
|
|
|
const { container } = render(
|
|
<Dropdown {...props}>
|
|
<button type="button">button</button>
|
|
</Dropdown>,
|
|
);
|
|
await waitFakeTimer();
|
|
expect(container.querySelectorAll('#customExpandIcon').length).toBe(1);
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it('should warn if use topCenter or bottomCenter', () => {
|
|
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
render(
|
|
<div>
|
|
<Dropdown menu={{ items }} placement="bottomCenter">
|
|
<button type="button">bottomCenter</button>
|
|
</Dropdown>
|
|
<Dropdown menu={{ items }} placement="topCenter">
|
|
<button type="button">topCenter</button>
|
|
</Dropdown>
|
|
</div>,
|
|
);
|
|
expect(error).toHaveBeenCalledWith(
|
|
expect.stringContaining("[antd: Dropdown] You are using 'bottomCenter'"),
|
|
);
|
|
expect(error).toHaveBeenCalledWith(
|
|
expect.stringContaining("[antd: Dropdown] You are using 'topCenter'"),
|
|
);
|
|
error.mockRestore();
|
|
});
|
|
|
|
// zombieJ: when replaced with react test lib, it may be mock fully content
|
|
it('dropdown should support auto adjust placement', () => {
|
|
render(
|
|
<Dropdown menu={{ items }} open>
|
|
<button type="button">button</button>
|
|
</Dropdown>,
|
|
);
|
|
|
|
expect(triggerProps.builtinPlacements).toEqual(
|
|
expect.objectContaining({
|
|
bottomLeft: expect.objectContaining({
|
|
overflow: {
|
|
adjustX: true,
|
|
adjustY: true,
|
|
},
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('menu item with group', () => {
|
|
jest.useFakeTimers();
|
|
const { container } = render(
|
|
<Dropdown
|
|
trigger={['click']}
|
|
menu={{
|
|
items: [
|
|
{
|
|
label: 'grp',
|
|
type: 'group',
|
|
children: [
|
|
{
|
|
label: '1',
|
|
key: 1,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}}
|
|
>
|
|
<a />
|
|
</Dropdown>,
|
|
);
|
|
|
|
// Open
|
|
fireEvent.click(container.querySelector('a')!);
|
|
act(() => {
|
|
jest.runAllTimers();
|
|
});
|
|
|
|
// Close
|
|
fireEvent.click(container.querySelector('.ant-dropdown-menu-item')!);
|
|
|
|
// Force Motion move on
|
|
for (let i = 0; i < 10; i += 1) {
|
|
act(() => {
|
|
jest.runAllTimers();
|
|
});
|
|
}
|
|
|
|
// Motion End
|
|
fireEvent.animationEnd(container.querySelector('.ant-slide-up-leave-active')!);
|
|
|
|
expect(container.querySelector('.ant-dropdown-hidden')).toBeTruthy();
|
|
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it('legacy visible', () => {
|
|
resetWarned();
|
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
const onOpenChange = jest.fn();
|
|
const onVisibleChange = jest.fn();
|
|
|
|
const { container, rerender } = render(
|
|
<Dropdown
|
|
visible
|
|
onOpenChange={onOpenChange}
|
|
onVisibleChange={onVisibleChange}
|
|
trigger={['click']}
|
|
menu={{
|
|
items: [
|
|
{
|
|
label: <div className="bamboo" />,
|
|
key: 'bamboo',
|
|
},
|
|
],
|
|
}}
|
|
>
|
|
<a className="little" />
|
|
</Dropdown>,
|
|
);
|
|
|
|
expect(document.querySelector('.bamboo')).toBeTruthy();
|
|
expect(errorSpy).toHaveBeenCalledWith(
|
|
'Warning: [antd: Dropdown] `visible` is deprecated. Please use `open` instead.',
|
|
);
|
|
expect(errorSpy).toHaveBeenCalledWith(
|
|
'Warning: [antd: Dropdown] `onVisibleChange` is deprecated. Please use `onOpenChange` instead.',
|
|
);
|
|
|
|
fireEvent.click(container.querySelector('.little')!);
|
|
expect(onOpenChange).toHaveBeenCalled();
|
|
expect(onVisibleChange).toHaveBeenCalled();
|
|
|
|
rerender(
|
|
<Dropdown overlay={<div>menu</div>}>
|
|
<a className="little" />
|
|
</Dropdown>,
|
|
);
|
|
expect(errorSpy).toHaveBeenCalledWith(
|
|
'Warning: [antd: Dropdown] `overlay` is deprecated. Please use `menu` instead.',
|
|
);
|
|
|
|
errorSpy.mockRestore();
|
|
});
|
|
|
|
it('not block ref', () => {
|
|
const divRef = React.createRef<HTMLDivElement>();
|
|
render(
|
|
<Dropdown open dropdownRender={() => <div ref={divRef} />}>
|
|
<a />
|
|
</Dropdown>,
|
|
);
|
|
|
|
expect(divRef.current).toBeTruthy();
|
|
});
|
|
|
|
it('should trigger open event when click on item', () => {
|
|
const onOpenChange = jest.fn();
|
|
render(
|
|
<Dropdown
|
|
onOpenChange={onOpenChange}
|
|
open
|
|
menu={{
|
|
items: [
|
|
{
|
|
label: <div className="bamboo" />,
|
|
key: 1,
|
|
},
|
|
],
|
|
}}
|
|
>
|
|
<a />
|
|
</Dropdown>,
|
|
);
|
|
|
|
fireEvent.click(document.body.querySelector('.bamboo')!);
|
|
expect(onOpenChange).toHaveBeenCalledWith(false, { source: 'menu' });
|
|
});
|
|
|
|
it('is still open after selection in multiple mode', () => {
|
|
jest.useFakeTimers();
|
|
const { container } = render(
|
|
<Dropdown
|
|
trigger={['click']}
|
|
menu={{
|
|
selectable: true,
|
|
multiple: true,
|
|
items: [
|
|
{ label: '1', key: 1 },
|
|
{ label: '2', key: 2 },
|
|
],
|
|
}}
|
|
>
|
|
<a />
|
|
</Dropdown>,
|
|
);
|
|
|
|
// Open
|
|
fireEvent.click(container.querySelector('a')!);
|
|
act(() => {
|
|
jest.runAllTimers();
|
|
});
|
|
|
|
// Selecting item
|
|
fireEvent.click(container.querySelector('.ant-dropdown-menu-item')!);
|
|
|
|
// Force Motion move on
|
|
for (let i = 0; i < 10; i += 1) {
|
|
act(() => {
|
|
jest.runAllTimers();
|
|
});
|
|
}
|
|
expect(container.querySelector('.ant-dropdown-hidden')).toBeFalsy();
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it('should respect trigger disabled prop', () => {
|
|
const { container: container1 } = render(
|
|
<Dropdown menu={{ items }} disabled>
|
|
<button type="button">button</button>
|
|
</Dropdown>,
|
|
);
|
|
expect(container1.querySelector('button')).toHaveAttribute('disabled');
|
|
|
|
const { container: container2 } = render(
|
|
<Dropdown menu={{ items }}>
|
|
<button type="button" disabled>
|
|
button
|
|
</button>
|
|
</Dropdown>,
|
|
);
|
|
expect(container2.querySelector('button')).toHaveAttribute('disabled');
|
|
|
|
const { container: container3 } = render(
|
|
<Dropdown menu={{ items }} disabled>
|
|
<button type="button" disabled={false}>
|
|
button
|
|
</button>
|
|
</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-title-content-with-extra'),
|
|
).toBeInTheDocument();
|
|
expect(container.querySelector('.ant-dropdown-menu-item-extra')?.textContent).toBe(text);
|
|
});
|
|
});
|