mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-11 19:42:54 +08:00
Merge remote-tracking branch 'origin/feature' into next
This commit is contained in:
commit
b6fe34254d
@ -47,7 +47,7 @@ An enterprise-class UI design language and React UI library.
|
|||||||
|
|
||||||
[](https://ant.design)
|
[](https://ant.design)
|
||||||
|
|
||||||
English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md) | [Українською](./README-uk_UA.md) | [Spanish](./README-sp_MX.md)
|
English | [Português](./README-pt_BR.md) | [简体中文](./README-zh_CN.md) | [Українською](./README-uk_UA.md) | [Spanish](./README-sp_MX.md) | [日本語](./README-ja_JP.md)
|
||||||
|
|
||||||
## ✨ Features
|
## ✨ Features
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||||||
import RcDrawer from 'rc-drawer';
|
import RcDrawer from 'rc-drawer';
|
||||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ConfigContext, DirectionType } from '../config-provider';
|
import { ConfigContext } from '../config-provider';
|
||||||
import { tuple } from '../_util/type';
|
import { tuple } from '../_util/type';
|
||||||
import useForceUpdate from '../_util/hooks/useForceUpdate';
|
import useForceUpdate from '../_util/hooks/useForceUpdate';
|
||||||
|
|
||||||
@ -64,22 +64,13 @@ export interface DrawerProps {
|
|||||||
footer?: React.ReactNode;
|
footer?: React.ReactNode;
|
||||||
footerStyle?: React.CSSProperties;
|
footerStyle?: React.CSSProperties;
|
||||||
level?: string | string[] | null | undefined;
|
level?: string | string[] | null | undefined;
|
||||||
levelMove?:
|
levelMove?: ILevelMove | ((e: { target: HTMLElement; open: boolean }) => ILevelMove);
|
||||||
| ILevelMove
|
children?: React.ReactNode;
|
||||||
| ((e: { target: HTMLElement; open: boolean }) => ILevelMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDrawerState {
|
|
||||||
push?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InternalDrawerProps extends DrawerProps {
|
|
||||||
direction: DirectionType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPushState: PushState = { distance: 180 };
|
const defaultPushState: PushState = { distance: 180 };
|
||||||
|
|
||||||
const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
width,
|
width,
|
||||||
@ -95,9 +86,7 @@ const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
|||||||
closeIcon = <CloseOutlined />,
|
closeIcon = <CloseOutlined />,
|
||||||
bodyStyle,
|
bodyStyle,
|
||||||
drawerStyle,
|
drawerStyle,
|
||||||
prefixCls,
|
|
||||||
className,
|
className,
|
||||||
direction,
|
|
||||||
visible,
|
visible,
|
||||||
children,
|
children,
|
||||||
zIndex,
|
zIndex,
|
||||||
@ -108,6 +97,8 @@ const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
|||||||
onClose,
|
onClose,
|
||||||
footer,
|
footer,
|
||||||
footerStyle,
|
footerStyle,
|
||||||
|
prefixCls: customizePrefixCls,
|
||||||
|
getContainer: customizeGetContainer,
|
||||||
extra,
|
extra,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
@ -118,6 +109,14 @@ const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
|||||||
const parentDrawer = React.useContext(DrawerContext);
|
const parentDrawer = React.useContext(DrawerContext);
|
||||||
const destroyClose = React.useRef<boolean>(false);
|
const destroyClose = React.useRef<boolean>(false);
|
||||||
|
|
||||||
|
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||||
|
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
|
||||||
|
const getContainer =
|
||||||
|
// 有可能为 false,所以不能直接判断
|
||||||
|
customizeGetContainer === undefined && getPopupContainer
|
||||||
|
? () => getPopupContainer(document.body)
|
||||||
|
: customizeGetContainer;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// fix: delete drawer in child and re-render, no push started.
|
// fix: delete drawer in child and re-render, no push started.
|
||||||
// <Drawer>{show && <Drawer />}</Drawer>
|
// <Drawer>{show && <Drawer />}</Drawer>
|
||||||
@ -320,6 +319,7 @@ const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
|||||||
showMask={mask}
|
showMask={mask}
|
||||||
style={getRcDrawerStyle()}
|
style={getRcDrawerStyle()}
|
||||||
className={drawerClassName}
|
className={drawerClassName}
|
||||||
|
getContainer={getContainer}
|
||||||
>
|
>
|
||||||
{renderBody()}
|
{renderBody()}
|
||||||
</RcDrawer>
|
</RcDrawer>
|
||||||
@ -330,30 +330,4 @@ const Drawer = React.forwardRef<DrawerRef, InternalDrawerProps>(
|
|||||||
|
|
||||||
Drawer.displayName = 'Drawer';
|
Drawer.displayName = 'Drawer';
|
||||||
|
|
||||||
const DrawerWrapper: React.FC<DrawerProps> = React.forwardRef<DrawerRef, DrawerProps>(
|
export default Drawer;
|
||||||
(props, ref) => {
|
|
||||||
const { prefixCls: customizePrefixCls, getContainer: customizeGetContainer } = props;
|
|
||||||
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
|
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
|
|
||||||
const getContainer =
|
|
||||||
// 有可能为 false,所以不能直接判断
|
|
||||||
customizeGetContainer === undefined && getPopupContainer
|
|
||||||
? () => getPopupContainer(document.body)
|
|
||||||
: customizeGetContainer;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Drawer
|
|
||||||
{...props}
|
|
||||||
ref={ref}
|
|
||||||
prefixCls={prefixCls}
|
|
||||||
getContainer={getContainer}
|
|
||||||
direction={direction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
DrawerWrapper.displayName = 'DrawerWrapper';
|
|
||||||
|
|
||||||
export default DrawerWrapper;
|
|
||||||
|
@ -2197,4 +2197,59 @@ describe('Table.filter', () => {
|
|||||||
expect(wrapper.find('Dropdown').first().props().visible).toBe(res2);
|
expect(wrapper.find('Dropdown').first().props().visible).toBe(res2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('filterDropDown should support filterResetToDefaultFilteredValue', () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
const columnFilter = {
|
||||||
|
...column,
|
||||||
|
filterMode: 'tree',
|
||||||
|
filterSearch: true,
|
||||||
|
defaultFilteredValue: ['girl'],
|
||||||
|
};
|
||||||
|
|
||||||
|
let wrapper = mount(
|
||||||
|
createTable({
|
||||||
|
columns: [columnFilter],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
wrapper.find('span.ant-dropdown-trigger').simulate('click', nativeEvent);
|
||||||
|
act(() => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
wrapper.update();
|
||||||
|
});
|
||||||
|
expect(wrapper.find('.ant-tree-checkbox-checked').length).toBe(1);
|
||||||
|
wrapper
|
||||||
|
.find(Checkbox)
|
||||||
|
.find('input')
|
||||||
|
.simulate('change', { target: { checked: true } });
|
||||||
|
expect(wrapper.find('.ant-tree-checkbox-checked').length).toBe(5);
|
||||||
|
wrapper.find('button.ant-btn-link').simulate('click', nativeEvent);
|
||||||
|
expect(wrapper.find('.ant-tree-checkbox-checked').length).toBe(0);
|
||||||
|
|
||||||
|
wrapper = mount(
|
||||||
|
createTable({
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
...columnFilter,
|
||||||
|
filterResetToDefaultFilteredValue: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
wrapper.find('span.ant-dropdown-trigger').simulate('click', nativeEvent);
|
||||||
|
act(() => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
wrapper.update();
|
||||||
|
});
|
||||||
|
wrapper
|
||||||
|
.find(Checkbox)
|
||||||
|
.find('input')
|
||||||
|
.simulate('change', { target: { checked: true } });
|
||||||
|
expect(wrapper.find('.ant-tree-checkbox-checked').length).toBe(5);
|
||||||
|
wrapper.find('button.ant-btn-link').simulate('click', nativeEvent);
|
||||||
|
expect(wrapper.find('.ant-tree-checkbox-checked').length).toBe(1);
|
||||||
|
expect(wrapper.find('.ant-tree-checkbox-checked+span').text()).toBe('Girl');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -111,6 +111,7 @@ export interface FilterDropdownProps<RecordType> {
|
|||||||
triggerFilter: (filterState: FilterState<RecordType>) => void;
|
triggerFilter: (filterState: FilterState<RecordType>) => void;
|
||||||
locale: TableLocale;
|
locale: TableLocale;
|
||||||
getPopupContainer?: GetPopupContainer;
|
getPopupContainer?: GetPopupContainer;
|
||||||
|
filterResetToDefaultFilteredValue?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||||
@ -130,7 +131,12 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
|||||||
getPopupContainer,
|
getPopupContainer,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { filterDropdownVisible, onFilterDropdownVisibleChange } = column;
|
const {
|
||||||
|
filterDropdownVisible,
|
||||||
|
onFilterDropdownVisibleChange,
|
||||||
|
filterResetToDefaultFilteredValue,
|
||||||
|
defaultFilteredValue,
|
||||||
|
} = column;
|
||||||
const [visible, setVisible] = React.useState(false);
|
const [visible, setVisible] = React.useState(false);
|
||||||
|
|
||||||
const filtered: boolean = !!(
|
const filtered: boolean = !!(
|
||||||
@ -231,8 +237,14 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
|||||||
if (closeDropdown) {
|
if (closeDropdown) {
|
||||||
triggerVisible(false);
|
triggerVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
setFilteredKeysSync([]);
|
|
||||||
|
if (filterResetToDefaultFilteredValue) {
|
||||||
|
setFilteredKeysSync((defaultFilteredValue || []).map(key => String(key)));
|
||||||
|
} else {
|
||||||
|
setFilteredKeysSync([]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const doFilter = ({ closeDropdown } = { closeDropdown: true }) => {
|
const doFilter = ({ closeDropdown } = { closeDropdown: true }) => {
|
||||||
|
@ -120,6 +120,7 @@ One of the Table `columns` prop for describing the table's columns, Column has t
|
|||||||
| colSpan | Span of this column's title | number | - | |
|
| colSpan | Span of this column's title | number | - | |
|
||||||
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
|
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
|
||||||
| defaultFilteredValue | Default filtered values | string\[] | - | |
|
| defaultFilteredValue | Default filtered values | string\[] | - | |
|
||||||
|
| filterResetToDefaultFilteredValue | click the reset button, whether to restore the default filter | boolean | false | |
|
||||||
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - | |
|
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - | |
|
||||||
| editable | Whether column can be edited | boolean | false | |
|
| editable | Whether column can be edited | boolean | false | |
|
||||||
| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \| {showTitle?: boolean } | false | showTitle: 4.3.0 |
|
| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \| {showTitle?: boolean } | false | showTitle: 4.3.0 |
|
||||||
|
@ -127,6 +127,7 @@ const columns = [
|
|||||||
| colSpan | 表头列合并,设置为 0 时,不渲染 | number | - | |
|
| colSpan | 表头列合并,设置为 0 时,不渲染 | number | - | |
|
||||||
| dataIndex | 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 | string \| string\[] | - | |
|
| dataIndex | 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 | string \| string\[] | - | |
|
||||||
| defaultFilteredValue | 默认筛选值 | string\[] | - | |
|
| defaultFilteredValue | 默认筛选值 | string\[] | - | |
|
||||||
|
| filterResetToDefaultFilteredValue | 点击重置按钮的时候,是否恢复默认筛选值 | boolean | false | |
|
||||||
| defaultSortOrder | 默认排序顺序 | `ascend` \| `descend` | - | |
|
| defaultSortOrder | 默认排序顺序 | `ascend` \| `descend` | - | |
|
||||||
| editable | 是否可编辑 | boolean | false | |
|
| editable | 是否可编辑 | boolean | false | |
|
||||||
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 或 `{ showTitle?: boolean }` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean \| { showTitle?: boolean } | false | showTitle: 4.3.0 |
|
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 或 `{ showTitle?: boolean }` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean \| { showTitle?: boolean } | false | showTitle: 4.3.0 |
|
||||||
|
@ -117,6 +117,7 @@ export interface ColumnType<RecordType> extends RcColumnType<RecordType> {
|
|||||||
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
||||||
filterDropdownVisible?: boolean;
|
filterDropdownVisible?: boolean;
|
||||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||||
|
filterResetToDefaultFilteredValue?: boolean;
|
||||||
|
|
||||||
// Responsive
|
// Responsive
|
||||||
responsive?: Breakpoint[];
|
responsive?: Breakpoint[];
|
||||||
|
@ -27,7 +27,7 @@ export type BaseType = 'secondary' | 'success' | 'warning' | 'danger';
|
|||||||
|
|
||||||
interface CopyConfig {
|
interface CopyConfig {
|
||||||
text?: string;
|
text?: string;
|
||||||
onCopy?: () => void;
|
onCopy?: (event?: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
tooltips?: boolean | React.ReactNode;
|
tooltips?: boolean | React.ReactNode;
|
||||||
}
|
}
|
||||||
@ -207,7 +207,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
|
|||||||
setCopied(false);
|
setCopied(false);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
copyConfig.onCopy?.();
|
copyConfig.onCopy?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => cleanCopyId, []);
|
React.useEffect(() => cleanCopyId, []);
|
||||||
|
@ -208,6 +208,18 @@ describe('Typography copy', () => {
|
|||||||
expect(onDivClick).not.toBeCalled();
|
expect(onDivClick).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('the first parameter of onCopy is the click event', () => {
|
||||||
|
function onCopy(e: React.MouseEvent<HTMLDivElement>) {
|
||||||
|
expect(e).not.toBeUndefined();
|
||||||
|
}
|
||||||
|
const wrapper = mount(
|
||||||
|
<Base component="p" copyable={{ onCopy }}>
|
||||||
|
test copy
|
||||||
|
</Base>,
|
||||||
|
);
|
||||||
|
wrapper.find('.ant-typography-copy').first().simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
it('copy to clipboard', done => {
|
it('copy to clipboard', done => {
|
||||||
const spy = jest.spyOn(copyObj, 'default');
|
const spy = jest.spyOn(copyObj, 'default');
|
||||||
const originText = 'origin text.';
|
const originText = 'origin text.';
|
||||||
|
@ -71,7 +71,7 @@ Basic text writing, including headings, body text, lists, and more.
|
|||||||
|
|
||||||
{
|
{
|
||||||
text: string,
|
text: string,
|
||||||
onCopy: function,
|
onCopy: function(event),
|
||||||
icon: ReactNode,
|
icon: ReactNode,
|
||||||
tooltips: false | [ReactNode, ReactNode],
|
tooltips: false | [ReactNode, ReactNode],
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
|
|||||||
|
|
||||||
{
|
{
|
||||||
text: string,
|
text: string,
|
||||||
onCopy: function,
|
onCopy: function(event),
|
||||||
icon: ReactNode,
|
icon: ReactNode,
|
||||||
tooltips: false | [ReactNode, ReactNode],
|
tooltips: false | [ReactNode, ReactNode],
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user