diff --git a/README.md b/README.md
index 2cd8c9fbd..c3a271adc 100644
--- a/README.md
+++ b/README.md
@@ -53,23 +53,22 @@ render(, mountNode);
| popupStyle | React.CSSProperties | | customize popup style |
| transitionName | String | '' | css class for animation |
| locale | Object | import from 'rc-picker/lib/locale/en_US' | rc-picker locale |
-| inputReadOnly | Boolean | false | set input to read only |
-| allowClear | Boolean | false | whether show clear button |
-| autoFocus | Boolean | false | whether auto focus |
-| showTime | Boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
+| inputReadOnly | boolean | false | set input to read only |
+| allowClear | boolean \| { clearIcon?: ReactNode } | false | whether show clear button or customize clear button |
+| autoFocus | boolean | false | whether auto focus |
+| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
| picker | time \| date \| week \| month \| year | | control which kind of panel should be shown |
| format | String \| String[] | depends on whether you set timePicker and your locale | use to format/parse date(without time) value to/from input. When an array is provided, all values are used for parsing and first value for display |
-| use12Hours | Boolean | false | 12 hours display mode |
+| use12Hours | boolean | false | 12 hours display mode |
| value | moment | | current value like input's value |
| defaultValue | moment | | defaultValue like input's defaultValue |
-| open | Boolean | false | current open state of picker. controlled prop |
+| open | boolean | false | current open state of picker. controlled prop |
| suffixIcon | ReactNode | | The custom suffix icon |
-| clearIcon | ReactNode | | The custom clear icon |
| prevIcon | ReactNode | | The custom prev icon |
| nextIcon | ReactNode | | The custom next icon |
| superPrevIcon | ReactNode | | The custom super prev icon |
| superNextIcon | ReactNode | | The custom super next icon |
-| disabled | Boolean | false | whether the picker is disabled |
+| disabled | boolean | false | whether the picker is disabled |
| placeholder | String | | picker input's placeholder |
| getPopupContainer | function(trigger) | | to set the container of the floating layer, while the default is to create a div element in body |
| onChange | Function(date: moment, dateString: string) | | a callback function, can be executed when the selected time is changing |
@@ -93,9 +92,9 @@ render(, mountNode);
| mode | time \| datetime \| date \| week \| month \| year \| decade | | control which kind of panel |
| picker | time \| date \| week \| month \| year | | control which kind of panel |
| tabIndex | Number | 0 | view [tabIndex](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) |
-| showTime | Boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
-| showToday | Boolean | false | whether to show today button |
-| disabledDate | Function(date:moment) => Boolean | | whether to disable select of current date |
+| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
+| showToday | boolean | false | whether to show today button |
+| disabledDate | Function(date:moment) => boolean | | whether to disable select of current date |
| dateRender | Function(currentDate:moment, today:moment) => React.Node | | custom rendering function for date cells |
| monthCellRender | Function(currentDate:moment, locale:Locale) => React.Node | | Custom month cell render method |
| renderExtraFooter | (mode) => React.Node | | extra footer |
@@ -118,33 +117,33 @@ render(, mountNode);
| separator | String | '~' | set separator between inputs |
| picker | time \| date \| week \| month \| year | | control which kind of panel |
| placeholder | [String, String] | | placeholder of date input |
-| showTime | Boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
+| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
| showTime.defaultValue | [moment, moment] | | to set default time of selected date |
-| use12Hours | Boolean | false | 12 hours display mode |
+| use12Hours | boolean | false | 12 hours display mode |
| disabledTime | Function(date: moment, type:'start'\|'end'):Object | | | to specify the time that cannot be selected |
| ranges | { String \| [range: string]: moment[] } \| { [range: string]: () => moment[] } | | preseted ranges for quick selection |
| format | String \| String[] | depends on whether you set timePicker and your locale | use to format/parse date(without time) value to/from input. When an array is provided, all values are used for parsing and first value for display |
-| allowEmpty | [Boolean, Boolean] | | allow range picker clearing text |
-| selectable | [Boolean, Boolean] | | whether to selected picker |
-| disabled | Boolean | false | whether the range picker is disabled |
+| allowEmpty | [boolean, boolean] | | allow range picker clearing text |
+| selectable | [boolean, boolean] | | whether to selected picker |
+| disabled | boolean | false | whether the range picker is disabled |
| onChange | Function(value:[moment], formatString: [string, string]) | | a callback function, can be executed when the selected time is changing |
| onCalendarChange | Function(value:[moment], formatString: [string, string], info: { range:'start'\|'end' }) | | a callback function, can be executed when the start time or the end time of the range is changing |
| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. |
-| order | Boolean | true | (TimeRangePicker only) `false` to disable auto order |
+| order | boolean | true | (TimeRangePicker only) `false` to disable auto order |
### showTime-options
| Property | Type | Default | Description |
| ------------------- | ------- | ------- | ---------------------------------- |
| format | String | | moment format |
-| showHour | Boolean | true | whether show hour |
-| showMinute | Boolean | true | whether show minute |
-| showSecond | Boolean | true | whether show second |
-| use12Hours | Boolean | false | 12 hours display mode |
+| showHour | boolean | true | whether show hour |
+| showMinute | boolean | true | whether show minute |
+| showSecond | boolean | true | whether show second |
+| use12Hours | boolean | false | 12 hours display mode |
| hourStep | Number | 1 | interval between hours in picker |
| minuteStep | Number | 1 | interval between minutes in picker |
| secondStep | Number | 1 | interval between seconds in picker |
-| hideDisabledOptions | Boolean | false | whether hide disabled options |
+| hideDisabledOptions | boolean | false | whether hide disabled options |
| defaultValue | moment | null | default initial value |
## Development
diff --git a/src/Picker.tsx b/src/Picker.tsx
index af70305da..85e7b2525 100644
--- a/src/Picker.tsx
+++ b/src/Picker.tsx
@@ -37,6 +37,7 @@ import { formatValue, isEqual, parseValue } from './utils/dateUtil';
import { toArray } from './utils/miscUtil';
import { elementsContains, getDefaultFormat, getInputSize } from './utils/uiUtil';
import { legacyPropsWarning } from './utils/warnUtil';
+import { getClearIcon } from './utils/getClearIcon';
export type PickerRefConfig = {
focus: () => void;
@@ -49,7 +50,7 @@ export type PickerSharedProps = {
popupStyle?: React.CSSProperties;
transitionName?: string;
placeholder?: string;
- allowClear?: boolean;
+ allowClear?: boolean | { clearIcon?: React.ReactNode };
autoFocus?: boolean;
disabled?: boolean;
tabIndex?: number;
@@ -66,6 +67,10 @@ export type PickerSharedProps = {
// Render
suffixIcon?: React.ReactNode;
+ /**
+ * Clear all icon
+ * @deprecated Please use `allowClear` instead
+ **/
clearIcon?: React.ReactNode;
prevIcon?: React.ReactNode;
nextIcon?: React.ReactNode;
@@ -477,28 +482,41 @@ function InnerPicker(props: PickerProps) {
);
}
- let clearNode: React.ReactNode;
- if (allowClear && mergedValue && !disabled) {
- clearNode = (
- {
- e.preventDefault();
- e.stopPropagation();
- }}
- onMouseUp={(e) => {
- e.preventDefault();
- e.stopPropagation();
- triggerChange(null);
- triggerOpen(false);
- }}
- className={`${prefixCls}-clear`}
- role="button"
- >
- {clearIcon || }
-
+ // ============================ Clear ============================
+ if (process.env.NODE_ENV !== 'production') {
+ warning(
+ !props.clearIcon,
+ '`clearIcon` will be removed in future. Please use `allowClear` instead.',
);
}
+ const mergedClearIcon: React.ReactNode = getClearIcon(
+ prefixCls,
+ allowClear,
+ clearIcon,
+ );
+
+ const clearNode: React.ReactNode = (
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ }}
+ onMouseUp={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ triggerChange(null);
+ triggerOpen(false);
+ }}
+ className={`${prefixCls}-clear`}
+ role="button"
+ >
+ {mergedClearIcon}
+
+ );
+
+ const mergedAllowClear = !!allowClear && mergedValue && !disabled;
+
const mergedInputProps: React.InputHTMLAttributes & { ref: React.MutableRefObject } = {
id,
tabIndex,
@@ -515,7 +533,7 @@ function InnerPicker(props: PickerProps) {
...inputProps,
size: getInputSize(picker, formatList[0], generateConfig),
name,
- ...pickAttrs(props, { aria: true, data: true}),
+ ...pickAttrs(props, { aria: true, data: true }),
autoComplete,
};
@@ -590,7 +608,7 @@ function InnerPicker(props: PickerProps) {
>
{inputNode}
{suffixNode}
- {clearNode}
+ {mergedAllowClear && clearNode}
diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx
index 38f9b8db3..9421e8585 100644
--- a/src/RangePicker.tsx
+++ b/src/RangePicker.tsx
@@ -48,6 +48,7 @@ import getRanges from './utils/getRanges';
import { getValue, toArray, updateValues } from './utils/miscUtil';
import { elementsContains, getDefaultFormat, getInputSize } from './utils/uiUtil';
import { legacyPropsWarning } from './utils/warnUtil';
+import { getClearIcon } from './utils/getClearIcon';
function reorderValues(
values: RangeValue,
@@ -689,18 +690,18 @@ function InnerRangePicker(props: RangePickerProps) {
const startStr =
mergedValue && mergedValue[0]
? formatValue(mergedValue[0], {
- locale,
- format: 'YYYYMMDDHHmmss',
- generateConfig,
- })
+ locale,
+ format: 'YYYYMMDDHHmmss',
+ generateConfig,
+ })
: '';
const endStr =
mergedValue && mergedValue[1]
? formatValue(mergedValue[1], {
- locale,
- format: 'YYYYMMDDHHmmss',
- generateConfig,
- })
+ locale,
+ format: 'YYYYMMDDHHmmss',
+ generateConfig,
+ })
: '';
useEffect(() => {
@@ -752,6 +753,10 @@ function InnerRangePicker(props: RangePickerProps) {
}
warning(!dateRender, `'dateRender' is deprecated. Please use 'cellRender' instead.`);
warning(!monthCellRender, `'monthCellRender' is deprecated. Please use 'cellRender' instead.`);
+ warning(
+ !clearIcon,
+ '`clearIcon` will be removed in future. Please use `allowClear` instead.',
+ );
}
// ============================ Private ============================
@@ -859,7 +864,7 @@ function InnerRangePicker(props: RangePickerProps) {
defaultValue={
mergedActivePickerIndex === 0 ? getValue(selectedValue, 1) : getValue(selectedValue, 0)
}
- // defaultPickerValue={undefined}
+ // defaultPickerValue={undefined}
/>
);
@@ -1038,39 +1043,44 @@ function InnerRangePicker(props: RangePickerProps) {
);
}
- let clearNode: React.ReactNode;
- if (
- allowClear &&
- ((getValue(mergedValue, 0) && !mergedDisabled[0]) ||
- (getValue(mergedValue, 1) && !mergedDisabled[1]))
- ) {
- clearNode = (
- {
- e.preventDefault();
- e.stopPropagation();
- }}
- onMouseUp={(e) => {
- e.preventDefault();
- e.stopPropagation();
- let values = mergedValue;
+ const mergedClearIcon: React.ReactNode = getClearIcon(
+ prefixCls,
+ allowClear,
+ clearIcon,
+ );
- if (!mergedDisabled[0]) {
- values = updateValues(values, null, 0);
- }
- if (!mergedDisabled[1]) {
- values = updateValues(values, null, 1);
- }
+ const clearNode: React.ReactNode = (
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ }}
+ onMouseUp={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ let values = mergedValue;
- triggerChange(values, null);
- triggerOpen(false, mergedActivePickerIndex, 'clear');
- }}
- className={`${prefixCls}-clear`}
- >
- {clearIcon || }
-
- );
- }
+ if (!mergedDisabled[0]) {
+ values = updateValues(values, null, 0);
+ }
+ if (!mergedDisabled[1]) {
+ values = updateValues(values, null, 1);
+ }
+
+ triggerChange(values, null);
+ triggerOpen(false, mergedActivePickerIndex, 'clear');
+ }}
+ className={`${prefixCls}-clear`}
+ role="button"
+ >
+ {mergedClearIcon}
+
+ );
+
+ const mergedAllowClear = allowClear && (
+ (getValue(mergedValue as RangeValue, 0) && !mergedDisabled[0]) ||
+ (getValue(mergedValue as RangeValue, 1) && !mergedDisabled[1])
+ );
const inputSharedProps = {
size: getInputSize(picker, formatList[0], generateConfig),
@@ -1209,7 +1219,7 @@ function InnerRangePicker(props: RangePickerProps) {
}}
/>
{suffixNode}
- {clearNode}
+ {mergedAllowClear && clearNode}
diff --git a/src/utils/getClearIcon.tsx b/src/utils/getClearIcon.tsx
new file mode 100644
index 000000000..af0ced5a4
--- /dev/null
+++ b/src/utils/getClearIcon.tsx
@@ -0,0 +1,15 @@
+import type { ReactNode } from "react";
+import React from "react";
+
+export function getClearIcon(
+ prefixCls,
+ allowClear?: boolean | { clearIcon?: ReactNode },
+ clearIcon?: ReactNode,
+) {
+
+ const mergedClearIcon = typeof allowClear === "object" ? allowClear.clearIcon : clearIcon;
+
+ return (
+ mergedClearIcon ||
+ );
+}
\ No newline at end of file
diff --git a/tests/__snapshots__/range.spec.tsx.snap b/tests/__snapshots__/range.spec.tsx.snap
index 820a81c6f..cc4931eab 100644
--- a/tests/__snapshots__/range.spec.tsx.snap
+++ b/tests/__snapshots__/range.spec.tsx.snap
@@ -45,6 +45,7 @@ exports[`Picker.Range icon 1`] = `
{
+ let errorSpy;
beforeEach(() => {
jest.useFakeTimers().setSystemTime(fakeTime);
});
+ beforeAll(() => {
+ errorSpy = jest.spyOn(console, 'error').mockImplementation(() => null);
+ });
+
+ beforeEach(() => {
+ errorSpy.mockReset();
+ resetWarned();
+ });
afterAll(() => {
jest.clearAllTimers();
jest.useRealTimers();
@@ -535,6 +544,7 @@ describe('Picker.Basic', () => {
});
it('icon', () => {
+ expect(errorSpy).not.toHaveBeenCalled();
render(
{
allowClear
/>,
);
-
expect(document.querySelector('.rc-picker-input')).toMatchSnapshot();
+ expect(errorSpy).toHaveBeenCalledWith(
+ 'Warning: `clearIcon` will be removed in future. Please use `allowClear` instead.'
+ );
});
it('inputRender', () => {
@@ -611,34 +623,28 @@ describe('Picker.Basic', () => {
});
it('should show warning when hour step is invalid', () => {
- const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
- expect(spy).not.toBeCalled();
+ expect(errorSpy).not.toBeCalled();
const { container } = render();
openPicker(container);
- expect(spy).toBeCalledWith('Warning: `hourStep` 9 is invalid. It should be a factor of 24.');
- spy.mockRestore();
+ expect(errorSpy).toBeCalledWith('Warning: `hourStep` 9 is invalid. It should be a factor of 24.');
});
it('should show warning when minute step is invalid', () => {
- const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
- expect(spy).not.toBeCalled();
+ expect(errorSpy).not.toBeCalled();
const { container } = render();
openPicker(container);
- expect(spy).toBeCalledWith(
+ expect(errorSpy).toBeCalledWith(
'Warning: `minuteStep` 9 is invalid. It should be a factor of 60.',
);
- spy.mockRestore();
});
it('should show warning when second step is invalid', () => {
- const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
- expect(spy).not.toBeCalled();
+ expect(errorSpy).not.toBeCalled();
const { container } = render();
openPicker(container);
- expect(spy).toBeCalledWith(
+ expect(errorSpy).toBeCalledWith(
'Warning: `secondStep` 9 is invalid. It should be a factor of 60.',
);
- spy.mockRestore();
});
// https://github.com/ant-design/ant-design/issues/40914
@@ -650,7 +656,7 @@ describe('Picker.Basic', () => {
const { container } = render();
openPicker(container);
- const column = document.querySelector(`.rc-picker-time-panel-column:nth-child(${index+1})`);
+ const column = document.querySelector(`.rc-picker-time-panel-column:nth-child(${index + 1})`);
expect(column).toBeTruthy();
const cells = column.querySelectorAll('.rc-picker-time-panel-cell-inner');
@@ -742,7 +748,7 @@ describe('Picker.Basic', () => {
it('defaultOpenValue in timePicker', () => {
resetWarned();
const onChange = jest.fn();
- const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
+ const errSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
const { container } = render(
{
expect(document.querySelector('input').value).toEqual('');
});
+ it('custom clear icon', () => {
+ render(
+ clear }}
+ defaultValue={getMoment('2020-09-17')}
+ />,
+ );
+
+ // clear
+ expect(document.querySelector('.custom-clear')).toBeTruthy();
+ clearValue();
+ expect(document.querySelector('input').value).toEqual('');
+ });
+
it('panelRender', () => {
render( Light
} />);
expect(document.querySelector('.rc-picker')).toMatchSnapshot();
diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx
index 11ca11940..5dae9aef9 100644
--- a/tests/range.spec.tsx
+++ b/tests/range.spec.tsx
@@ -20,8 +20,15 @@ import {
selectCell,
} from './util/commonUtil';
import type { RangePickerProps } from '../src/RangePicker';
+import { resetWarned } from 'rc-util/lib/warning';
describe('Picker.Range', () => {
+ let errorSpy;
+
+ beforeAll(() => {
+ errorSpy = jest.spyOn(console, 'error').mockImplementation(() => null);
+ });
+
function matchValues(container: HTMLElement, value1: string, value2: string) {
expect(container.querySelectorAll('input')[0].value).toEqual(value1);
expect(container.querySelectorAll('input')[1].value).toEqual(value2);
@@ -33,6 +40,8 @@ describe('Picker.Range', () => {
}
beforeEach(() => {
+ errorSpy.mockReset();
+ resetWarned();
global.scrollCalled = false;
jest.useFakeTimers().setSystemTime(getMoment('1990-09-03 00:00:00').valueOf());
});
@@ -319,13 +328,11 @@ describe('Picker.Range', () => {
});
it('null value with disabled', () => {
- const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render();
- expect(errSpy).toHaveBeenCalledWith(
+ expect(errorSpy).toHaveBeenCalledWith(
'Warning: `disabled` should not set with empty `value`. You should set `allowEmpty` or `value` instead.',
);
- errSpy.mockReset();
});
it('clear should trigger change', () => {
@@ -747,6 +754,9 @@ describe('Picker.Range', () => {
);
expect(container).toMatchSnapshot();
+ expect(errorSpy).toHaveBeenCalledWith(
+ 'Warning: `clearIcon` will be removed in future. Please use `allowClear` instead.'
+ );
});
it('block native mouseDown in panel to prevent focus changed', () => {
@@ -1101,19 +1111,19 @@ describe('Picker.Range', () => {
targetCell: string;
match: string[];
}[] = [
- {
- picker: 'week',
- defaultValue: ['2020-06-13'],
- targetCell: '9',
- match: ['2020-24th'],
- },
- {
- picker: 'quarter',
- defaultValue: ['2020-03-30', '2020-05-20'],
- targetCell: 'Q1',
- match: ['2020-Q1'],
- },
- ];
+ {
+ picker: 'week',
+ defaultValue: ['2020-06-13'],
+ targetCell: '9',
+ match: ['2020-24th'],
+ },
+ {
+ picker: 'quarter',
+ defaultValue: ['2020-03-30', '2020-05-20'],
+ targetCell: 'Q1',
+ match: ['2020-Q1'],
+ },
+ ];
list.forEach(({ picker, defaultValue, match, targetCell }) => {
it(picker, () => {
@@ -1310,7 +1320,7 @@ describe('Picker.Range', () => {
openPicker(container, 1);
inputValue('1989-01-07', 1);
- console.log('close!');
+
keyDown(container, 1, KeyCode.ENTER);
expect(isOpen()).toBeTruthy();
@@ -1595,8 +1605,6 @@ describe('Picker.Range', () => {
fireEvent.focus(document.querySelectorAll('input')[0]);
inputValue('', 0);
- console.log(container.querySelector('.rc-picker').innerHTML);
-
// reselect date
selectCell(9, 0);
expect(document.querySelectorAll('input')[0].value).toBe('1990-09-09');
@@ -1898,4 +1906,19 @@ describe('Picker.Range', () => {
expect(document.querySelector('.rc-picker-cell-disabled')).toBeFalsy();
});
+
+ it('custom clear icon', () => {
+ render(
+ clear }}
+ defaultValue={[getMoment('2000-09-03'), getMoment('2000-09-03')]}
+ />,
+ );
+
+ // clear
+ expect(document.querySelector('.custom-clear')).toBeTruthy();
+ clearValue();
+ expect(document.querySelector('input').value).toEqual('');
+ });
+
});
diff --git a/tests/util/commonUtil.tsx b/tests/util/commonUtil.tsx
index 8e91df9a8..e3ccb7c15 100644
--- a/tests/util/commonUtil.tsx
+++ b/tests/util/commonUtil.tsx
@@ -175,7 +175,7 @@ export function confirmOK() {
}
export function clearValue() {
- const clearBtn = document.querySelector('.rc-picker-clear-btn');
+ const clearBtn = document.querySelector('.rc-picker-clear');
fireEvent.mouseDown(clearBtn);
fireEvent.mouseUp(clearBtn);
}