Merge remote-tracking branch 'origin/feature' into next

This commit is contained in:
zombiej 2022-03-24 14:06:08 +08:00
commit b6fe34254d
11 changed files with 105 additions and 49 deletions

View File

@ -47,7 +47,7 @@ An enterprise-class UI design language and React UI library.
[![](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*Yl83RJhUE7kAAAAAAAAAAABkARQnAQ)](https://ant.design) [![](https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*Yl83RJhUE7kAAAAAAAAAAABkARQnAQ)](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

View File

@ -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;

View File

@ -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');
});
}); });

View File

@ -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 }) => {

View File

@ -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 |

View File

@ -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 |

View File

@ -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[];

View File

@ -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, []);

View File

@ -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.';

View File

@ -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],
} }

View File

@ -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],
} }