mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-08 01:53:34 +08:00
fix: Table customize filterDropdown with Menu should not block default selectable (#36098)
* fix: Table customize Menu should be selectable * test: Add test case * test: Update snapshow
This commit is contained in:
parent
f19cf66c88
commit
63fc5055f9
@ -8194,6 +8194,232 @@ exports[`renders ./components/dropdown/demo/placement.md extend context correctl
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/dropdown/demo/selectable.md extend context correctly 1`] = `
|
||||||
|
Array [
|
||||||
|
<a
|
||||||
|
class="ant-typography ant-dropdown-trigger"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-space-item"
|
||||||
|
style="margin-right:8px"
|
||||||
|
>
|
||||||
|
Selectable
|
||||||
|
</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>
|
||||||
|
<div
|
||||||
|
class="ant-dropdown"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<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
|
||||||
|
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-dropdown-menu-title-content"
|
||||||
|
>
|
||||||
|
Item 1
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-arrow"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-tooltip-arrow-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-inner"
|
||||||
|
role="tooltip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<li
|
||||||
|
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-dropdown-menu-title-content"
|
||||||
|
>
|
||||||
|
Item 2
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-arrow"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-tooltip-arrow-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-inner"
|
||||||
|
role="tooltip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<li
|
||||||
|
class="ant-dropdown-menu-item ant-dropdown-menu-item-selected ant-dropdown-menu-item-only-child"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-dropdown-menu-title-content"
|
||||||
|
>
|
||||||
|
Item 3
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-arrow"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-tooltip-arrow-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-inner"
|
||||||
|
role="tooltip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
style="display:none"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-arrow"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-tooltip-arrow-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-inner"
|
||||||
|
role="tooltip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-arrow"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-tooltip-arrow-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-inner"
|
||||||
|
role="tooltip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
|
||||||
|
style="opacity:0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-content"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-arrow"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="ant-tooltip-arrow-content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="ant-tooltip-inner"
|
||||||
|
role="tooltip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/dropdown/demo/sub-menu.md extend context correctly 1`] = `
|
exports[`renders ./components/dropdown/demo/sub-menu.md extend context correctly 1`] = `
|
||||||
Array [
|
Array [
|
||||||
<a
|
<a
|
||||||
|
@ -858,6 +858,46 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/dropdown/demo/selectable.md correctly 1`] = `
|
||||||
|
<a
|
||||||
|
class="ant-typography ant-dropdown-trigger"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ant-space-item"
|
||||||
|
style="margin-right:8px"
|
||||||
|
>
|
||||||
|
Selectable
|
||||||
|
</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/sub-menu.md correctly 1`] = `
|
exports[`renders ./components/dropdown/demo/sub-menu.md correctly 1`] = `
|
||||||
<a
|
<a
|
||||||
class="ant-dropdown-trigger"
|
class="ant-dropdown-trigger"
|
||||||
|
54
components/dropdown/demo/selectable.md
Normal file
54
components/dropdown/demo/selectable.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
order: 10
|
||||||
|
title:
|
||||||
|
zh-CN: 菜单可选选择
|
||||||
|
en-US: Selectable Menu
|
||||||
|
---
|
||||||
|
|
||||||
|
## zh-CN
|
||||||
|
|
||||||
|
为 Menu 添加 `selectable` 属性可以开启选择能力。
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
Config Menu `selectable` prop to enable selectable ability.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { DownOutlined } from '@ant-design/icons';
|
||||||
|
import { Dropdown, Menu, Space, Typography } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu
|
||||||
|
selectable
|
||||||
|
defaultSelectedKeys={['3']}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'Item 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: 'Item 2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: 'Item 3',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const App: React.FC = () => (
|
||||||
|
<Dropdown overlay={menu}>
|
||||||
|
<Typography.Link>
|
||||||
|
<Space>
|
||||||
|
Selectable
|
||||||
|
<DownOutlined />
|
||||||
|
</Space>
|
||||||
|
</Typography.Link>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
```
|
@ -3,8 +3,7 @@ import classNames from 'classnames';
|
|||||||
import RcDropdown from 'rc-dropdown';
|
import RcDropdown from 'rc-dropdown';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ConfigContext } from '../config-provider';
|
import { ConfigContext } from '../config-provider';
|
||||||
import type { OverrideContextProps } from '../menu/OverrideContext';
|
import { OverrideProvider } from '../menu/OverrideContext';
|
||||||
import OverrideContext from '../menu/OverrideContext';
|
|
||||||
import getPlacements from '../_util/placements';
|
import getPlacements from '../_util/placements';
|
||||||
import { cloneElement } from '../_util/reactNode';
|
import { cloneElement } from '../_util/reactNode';
|
||||||
import { tuple } from '../_util/type';
|
import { tuple } from '../_util/type';
|
||||||
@ -148,28 +147,6 @@ const Dropdown: DropdownInterface = props => {
|
|||||||
autoAdjustOverflow: true,
|
autoAdjustOverflow: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const overlayContext = React.useMemo<OverrideContextProps>(
|
|
||||||
() => ({
|
|
||||||
prefixCls: `${prefixCls}-menu`,
|
|
||||||
expandIcon: (
|
|
||||||
<span className={`${prefixCls}-menu-submenu-arrow`}>
|
|
||||||
<RightOutlined className={`${prefixCls}-menu-submenu-arrow-icon`} />
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
mode: 'vertical',
|
|
||||||
selectable: false,
|
|
||||||
validator: ({ mode }) => {
|
|
||||||
// Warning if use other mode
|
|
||||||
warning(
|
|
||||||
!mode || mode === 'vertical',
|
|
||||||
'Dropdown',
|
|
||||||
`mode="${mode}" is not supported for Dropdown's Menu.`,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[prefixCls],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderOverlay = () => {
|
const renderOverlay = () => {
|
||||||
// rc-dropdown already can process the function of overlay, but we have check logic here.
|
// rc-dropdown already can process the function of overlay, but we have check logic here.
|
||||||
// So we need render the element to check and pass back to rc-dropdown.
|
// So we need render the element to check and pass back to rc-dropdown.
|
||||||
@ -186,7 +163,26 @@ const Dropdown: DropdownInterface = props => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OverrideContext.Provider value={overlayContext}>{overlayNode}</OverrideContext.Provider>
|
<OverrideProvider
|
||||||
|
prefixCls={`${prefixCls}-menu`}
|
||||||
|
expandIcon={
|
||||||
|
<span className={`${prefixCls}-menu-submenu-arrow`}>
|
||||||
|
<RightOutlined className={`${prefixCls}-menu-submenu-arrow-icon`} />
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
mode="vertical"
|
||||||
|
selectable={false}
|
||||||
|
validator={({ mode }) => {
|
||||||
|
// Warning if use other mode
|
||||||
|
warning(
|
||||||
|
!mode || mode === 'vertical',
|
||||||
|
'Dropdown',
|
||||||
|
`mode="${mode}" is not supported for Dropdown's Menu.`,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{overlayNode}
|
||||||
|
</OverrideProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import type { MenuProps } from '.';
|
|
||||||
|
|
||||||
// Used for Dropdown only
|
|
||||||
export interface OverrideContextProps {
|
|
||||||
prefixCls?: string;
|
|
||||||
expandIcon?: React.ReactNode;
|
|
||||||
mode?: MenuProps['mode'];
|
|
||||||
selectable?: boolean;
|
|
||||||
validator?: (menuProps: Pick<MenuProps, 'mode'>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @private Internal Usage. Only used for Dropdown component. Do not use this in your production. */
|
|
||||||
const OverrideContext = React.createContext<OverrideContextProps | null>(null);
|
|
||||||
|
|
||||||
export default OverrideContext;
|
|
41
components/menu/OverrideContext.tsx
Normal file
41
components/menu/OverrideContext.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import type { MenuProps } from '.';
|
||||||
|
|
||||||
|
// Used for Dropdown only
|
||||||
|
export interface OverrideContextProps {
|
||||||
|
prefixCls?: string;
|
||||||
|
expandIcon?: React.ReactNode;
|
||||||
|
mode?: MenuProps['mode'];
|
||||||
|
selectable?: boolean;
|
||||||
|
validator?: (menuProps: Pick<MenuProps, 'mode'>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private Internal Usage. Only used for Dropdown component. Do not use this in your production. */
|
||||||
|
const OverrideContext = React.createContext<OverrideContextProps | null>(null);
|
||||||
|
|
||||||
|
/** @private Internal Usage. Only used for Dropdown component. Do not use this in your production. */
|
||||||
|
export const OverrideProvider = ({
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: OverrideContextProps & { children: React.ReactNode }) => {
|
||||||
|
const override = React.useContext(OverrideContext);
|
||||||
|
|
||||||
|
const context = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
...override,
|
||||||
|
...restProps,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
override,
|
||||||
|
restProps.prefixCls,
|
||||||
|
// restProps.expandIcon, Not mark as deps since this is a ReactNode
|
||||||
|
restProps.mode,
|
||||||
|
restProps.selectable,
|
||||||
|
// restProps.validator, Not mark as deps since this is a function
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <OverrideContext.Provider value={context}>{children}</OverrideContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OverrideContext;
|
@ -1,13 +1,14 @@
|
|||||||
/* eslint-disable react/no-multi-comp */
|
/* eslint-disable react/no-multi-comp */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { render, fireEvent, waitFor } from '../../../tests/utils';
|
|
||||||
import Table from '..';
|
import Table from '..';
|
||||||
import Input from '../../input';
|
import { fireEvent, render, waitFor } from '../../../tests/utils';
|
||||||
import Tooltip from '../../tooltip';
|
|
||||||
import Button from '../../button';
|
import Button from '../../button';
|
||||||
import Select from '../../select';
|
|
||||||
import ConfigProvider from '../../config-provider';
|
import ConfigProvider from '../../config-provider';
|
||||||
|
import Input from '../../input';
|
||||||
|
import Menu from '../../menu';
|
||||||
|
import Select from '../../select';
|
||||||
|
import Tooltip from '../../tooltip';
|
||||||
|
|
||||||
// https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73
|
// https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73
|
||||||
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
|
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
|
||||||
@ -65,6 +66,14 @@ describe('Table.filter', () => {
|
|||||||
return namesList;
|
return namesList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it('not show filter icon when undefined', () => {
|
it('not show filter icon when undefined', () => {
|
||||||
const noFilterColumn = { ...column, filters: undefined };
|
const noFilterColumn = { ...column, filters: undefined };
|
||||||
delete noFilterColumn.onFilter;
|
delete noFilterColumn.onFilter;
|
||||||
@ -106,8 +115,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders empty menu correctly', () => {
|
it('renders empty menu correctly', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
|
|
||||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -129,8 +136,6 @@ describe('Table.filter', () => {
|
|||||||
expect(container.querySelector('.ant-empty')).toBeTruthy();
|
expect(container.querySelector('.ant-empty')).toBeTruthy();
|
||||||
expect(errorSpy).not.toHaveBeenCalled();
|
expect(errorSpy).not.toHaveBeenCalled();
|
||||||
errorSpy.mockRestore();
|
errorSpy.mockRestore();
|
||||||
|
|
||||||
jest.useRealTimers();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders radio filter correctly', async () => {
|
it('renders radio filter correctly', async () => {
|
||||||
@ -613,7 +618,6 @@ describe('Table.filter', () => {
|
|||||||
onChange,
|
onChange,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
jest.useFakeTimers();
|
|
||||||
|
|
||||||
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||||
|
|
||||||
@ -665,8 +669,6 @@ describe('Table.filter', () => {
|
|||||||
// What's this? Is that a coverage case? Or check a crash?
|
// What's this? Is that a coverage case? Or check a crash?
|
||||||
const latestItems = getFilterMenu().querySelectorAll('li.ant-dropdown-menu-item');
|
const latestItems = getFilterMenu().querySelectorAll('li.ant-dropdown-menu-item');
|
||||||
fireEvent.click(latestItems[latestItems.length - 1]);
|
fireEvent.click(latestItems[latestItems.length - 1]);
|
||||||
|
|
||||||
jest.useRealTimers();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should support value types', () => {
|
describe('should support value types', () => {
|
||||||
@ -676,7 +678,6 @@ describe('Table.filter', () => {
|
|||||||
['Bamboo', false],
|
['Bamboo', false],
|
||||||
].forEach(([text, value]) => {
|
].forEach(([text, value]) => {
|
||||||
it(`${typeof value} type`, async () => {
|
it(`${typeof value} type`, async () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
const filters = [{ text, value }];
|
const filters = [{ text, value }];
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
@ -698,8 +699,6 @@ describe('Table.filter', () => {
|
|||||||
|
|
||||||
fireEvent.click(container.querySelector('.ant-dropdown-trigger'));
|
fireEvent.click(container.querySelector('.ant-dropdown-trigger'));
|
||||||
|
|
||||||
jest.useFakeTimers();
|
|
||||||
|
|
||||||
fireEvent.click(container.querySelectorAll('.ant-dropdown-menu-item')[0]);
|
fireEvent.click(container.querySelectorAll('.ant-dropdown-menu-item')[0]);
|
||||||
|
|
||||||
// This test can be remove if refactor
|
// This test can be remove if refactor
|
||||||
@ -733,7 +732,6 @@ describe('Table.filter', () => {
|
|||||||
.querySelector('.ant-table-filter-dropdown')
|
.querySelector('.ant-table-filter-dropdown')
|
||||||
.querySelectorAll('.ant-checkbox-input')[0].checked,
|
.querySelectorAll('.ant-checkbox-input')[0].checked,
|
||||||
).toEqual(false);
|
).toEqual(false);
|
||||||
jest.useRealTimers();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1831,7 +1829,6 @@ describe('Table.filter', () => {
|
|||||||
|
|
||||||
describe('filter tree mode', () => {
|
describe('filter tree mode', () => {
|
||||||
it('supports filter tree', () => {
|
it('supports filter tree', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -1852,7 +1849,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('supports search input in filter tree', () => {
|
it('supports search input in filter tree', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -1875,7 +1871,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('supports search input in filter menu', () => {
|
it('supports search input in filter menu', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -1897,7 +1892,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should skip search when filters[0].text is ReactNode', () => {
|
it('should skip search when filters[0].text is ReactNode', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -1936,7 +1930,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should supports filterSearch has type of function', () => {
|
it('should supports filterSearch has type of function', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -1974,7 +1967,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('supports check all items', () => {
|
it('supports check all items', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -2007,7 +1999,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('supports check item by selecting it', () => {
|
it('supports check item by selecting it', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -2043,7 +2034,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('select-all checkbox should change when all items are selected', () => {
|
it('select-all checkbox should change when all items are selected', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -2075,7 +2065,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('filterMultiple is false - check item', () => {
|
it('filterMultiple is false - check item', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -2123,7 +2112,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('filterMultiple is false - select item', () => {
|
it('filterMultiple is false - select item', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -2170,7 +2158,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should select children when select parent', () => {
|
it('should select children when select parent', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
createTable({
|
createTable({
|
||||||
@ -2299,7 +2286,6 @@ describe('Table.filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('filterDropdown should support filterResetToDefaultFilteredValue', () => {
|
it('filterDropdown should support filterResetToDefaultFilteredValue', () => {
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
|
|
||||||
const columnFilter = {
|
const columnFilter = {
|
||||||
@ -2347,6 +2333,44 @@ describe('Table.filter', () => {
|
|||||||
expect(container.querySelector('.ant-tree-checkbox-checked+span').textContent).toBe('Girl');
|
expect(container.querySelector('.ant-tree-checkbox-checked+span').textContent).toBe('Girl');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('filterDropdown should not override customize Menu selectable', () => {
|
||||||
|
const onSelect = jest.fn();
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
createTable({
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
...column,
|
||||||
|
filterDropdown: (
|
||||||
|
<div className="custom-filter-dropdown">
|
||||||
|
<Menu
|
||||||
|
onSelect={onSelect}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'Item 1',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open Filter
|
||||||
|
fireEvent.click(container.querySelector('span.ant-dropdown-trigger'));
|
||||||
|
act(() => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click Item
|
||||||
|
fireEvent.click(container.querySelector('.ant-table-filter-dropdown .ant-dropdown-menu-item'));
|
||||||
|
|
||||||
|
expect(onSelect).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('filteredKeys should all be controlled or not controlled', () => {
|
it('filteredKeys should all be controlled or not controlled', () => {
|
||||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
errorSpy.mockReset();
|
errorSpy.mockReset();
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
import * as React from 'react';
|
import FilterFilled from '@ant-design/icons/FilterFilled';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
import type { FieldDataNode } from 'rc-tree';
|
import type { FieldDataNode } from 'rc-tree';
|
||||||
import FilterFilled from '@ant-design/icons/FilterFilled';
|
import * as React from 'react';
|
||||||
import Button from '../../../button';
|
|
||||||
import Menu from '../../../menu';
|
|
||||||
import type { MenuProps } from '../../../menu';
|
|
||||||
import Tree from '../../../tree';
|
|
||||||
import type { EventDataNode } from '../../../tree';
|
|
||||||
import Checkbox from '../../../checkbox';
|
|
||||||
import type { CheckboxChangeEvent } from '../../../checkbox';
|
|
||||||
import Radio from '../../../radio';
|
|
||||||
import Dropdown from '../../../dropdown';
|
|
||||||
import Empty from '../../../empty';
|
|
||||||
import type {
|
|
||||||
ColumnType,
|
|
||||||
ColumnFilterItem,
|
|
||||||
Key,
|
|
||||||
TableLocale,
|
|
||||||
GetPopupContainer,
|
|
||||||
FilterSearchType,
|
|
||||||
} from '../../interface';
|
|
||||||
import FilterDropdownMenuWrapper from './FilterWrapper';
|
|
||||||
import FilterSearch from './FilterSearch';
|
|
||||||
import type { FilterState } from '.';
|
import type { FilterState } from '.';
|
||||||
import { flattenKeys } from '.';
|
import { flattenKeys } from '.';
|
||||||
import useSyncState from '../../../_util/hooks/useSyncState';
|
import Button from '../../../button';
|
||||||
|
import type { CheckboxChangeEvent } from '../../../checkbox';
|
||||||
|
import Checkbox from '../../../checkbox';
|
||||||
import { ConfigContext } from '../../../config-provider/context';
|
import { ConfigContext } from '../../../config-provider/context';
|
||||||
|
import Dropdown from '../../../dropdown';
|
||||||
|
import Empty from '../../../empty';
|
||||||
|
import type { MenuProps } from '../../../menu';
|
||||||
|
import Menu from '../../../menu';
|
||||||
|
import { OverrideProvider } from '../../../menu/OverrideContext';
|
||||||
|
import Radio from '../../../radio';
|
||||||
|
import type { EventDataNode } from '../../../tree';
|
||||||
|
import Tree from '../../../tree';
|
||||||
|
import useSyncState from '../../../_util/hooks/useSyncState';
|
||||||
|
import type {
|
||||||
|
ColumnFilterItem,
|
||||||
|
ColumnType,
|
||||||
|
FilterSearchType,
|
||||||
|
GetPopupContainer,
|
||||||
|
Key,
|
||||||
|
TableLocale,
|
||||||
|
} from '../../interface';
|
||||||
|
import FilterSearch from './FilterSearch';
|
||||||
|
import FilterDropdownMenuWrapper from './FilterWrapper';
|
||||||
|
|
||||||
type FilterTreeDataNode = FieldDataNode<{ title: React.ReactNode; key: React.Key }>;
|
type FilterTreeDataNode = FieldDataNode<{ title: React.ReactNode; key: React.Key }>;
|
||||||
|
|
||||||
@ -303,6 +304,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let dropdownContent: React.ReactNode;
|
let dropdownContent: React.ReactNode;
|
||||||
|
|
||||||
if (typeof column.filterDropdown === 'function') {
|
if (typeof column.filterDropdown === 'function') {
|
||||||
dropdownContent = column.filterDropdown({
|
dropdownContent = column.filterDropdown({
|
||||||
prefixCls: `${dropdownPrefixCls}-custom`,
|
prefixCls: `${dropdownPrefixCls}-custom`,
|
||||||
@ -441,6 +443,11 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should not block customize Menu with additional props
|
||||||
|
if (column.filterDropdown) {
|
||||||
|
dropdownContent = <OverrideProvider selectable={undefined}>{dropdownContent}</OverrideProvider>;
|
||||||
|
}
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<FilterDropdownMenuWrapper className={`${prefixCls}-dropdown`}>
|
<FilterDropdownMenuWrapper className={`${prefixCls}-dropdown`}>
|
||||||
{dropdownContent}
|
{dropdownContent}
|
||||||
|
Loading…
Reference in New Issue
Block a user