diff --git a/examples/calendar.less b/examples/calendar.less new file mode 100644 index 000000000000..5fc2cb4dc4cb --- /dev/null +++ b/examples/calendar.less @@ -0,0 +1,3 @@ +.rc-picker-cell-selected { + background: red; +} \ No newline at end of file diff --git a/examples/calendar.tsx b/examples/calendar.tsx index 3dcd00569b87..d71c964d0809 100644 --- a/examples/calendar.tsx +++ b/examples/calendar.tsx @@ -5,6 +5,7 @@ import PickerPanel from '../src/PickerPanel'; import momentGenerateConfig from '../src/generate/moment'; import zhCN from '../src/locale/zh_CN'; import '../assets/index.less'; +import './calendar.less'; function dateRender(date: Moment, today: Moment) { return ( @@ -21,6 +22,12 @@ function dateRender(date: Moment, today: Moment) { ); } +const disabledProps = { + disabledDate: (date) => date.date() === 10, + onSelect: (d) => console.log('Select:', d.format('YYYY-MM-DD')), + onChange: (d) => console.log('Change:', d.format('YYYY-MM-DD')), +}; + export default () => (
@@ -29,6 +36,7 @@ export default () => ( // picker="month" generateConfig={momentGenerateConfig} dateRender={dateRender} + {...disabledProps} />
@@ -36,6 +44,7 @@ export default () => ( locale={zhCN} generateConfig={momentGenerateConfig} dateRender={dateRender} + {...disabledProps} />
diff --git a/src/Picker.tsx b/src/Picker.tsx index f0980a32401f..953d9894f509 100644 --- a/src/Picker.tsx +++ b/src/Picker.tsx @@ -170,6 +170,7 @@ function InnerPicker(props: PickerProps) { onMouseLeave, onContextMenu, onClick, + onSelect, direction, autoComplete = 'off', } = props as MergedPickerProps; @@ -195,16 +196,16 @@ function InnerPicker(props: PickerProps) { const [selectedValue, setSelectedValue] = React.useState(mergedValue); // Operation ref - const operationRef: React.MutableRefObject = React.useRef< - ContextOperationRefProps - >(null); + const operationRef: React.MutableRefObject = React.useRef( + null, + ); // Open const [mergedOpen, triggerInnerOpen] = useMergedState(false, { value: open, defaultValue: defaultOpen, - postState: postOpen => (disabled ? false : postOpen), - onChange: newOpen => { + postState: (postOpen) => (disabled ? false : postOpen), + onChange: (newOpen) => { if (onOpenChange) { onOpenChange(newOpen); } @@ -224,7 +225,7 @@ function InnerPicker(props: PickerProps) { const [text, triggerTextChange, resetText] = useTextValueMapping({ valueTexts, - onTextChange: newText => { + onTextChange: (newText) => { const inputDate = parseValue(newText, { locale, formatList, @@ -292,7 +293,7 @@ function InnerPicker(props: PickerProps) { value: text, triggerOpen, forwardKeyDown, - isClickOutside: target => + isClickOutside: (target) => !elementsContains([panelDivRef.current, inputDivRef.current], target as HTMLElement), onSubmit: () => { if (disabledDate && disabledDate(selectedValue)) { @@ -364,6 +365,7 @@ function InnerPicker(props: PickerProps) { style: undefined, pickerValue: undefined, onPickerValueChange: undefined, + onChange: null, }; let panelNode: React.ReactNode = ( @@ -376,7 +378,10 @@ function InnerPicker(props: PickerProps) { value={selectedValue} locale={locale} tabIndex={-1} - onChange={setSelectedValue} + onSelect={(date) => { + onSelect?.(date); + setSelectedValue(date); + }} direction={direction} /> ); @@ -388,7 +393,7 @@ function InnerPicker(props: PickerProps) { const panel = (
{ + onMouseDown={(e) => { e.preventDefault(); }} > @@ -405,11 +410,11 @@ function InnerPicker(props: PickerProps) { if (allowClear && mergedValue && !disabled) { clearNode = ( { + onMouseDown={(e) => { e.preventDefault(); e.stopPropagation(); }} - onMouseUp={e => { + onMouseUp={(e) => { e.preventDefault(); e.stopPropagation(); triggerChange(null); @@ -497,7 +502,7 @@ function InnerPicker(props: PickerProps) { disabled={disabled} readOnly={inputReadOnly || typeof formatList[0] === 'function' || !typing} value={hoverValue || text} - onChange={e => { + onChange={(e) => { triggerTextChange(e.target.value); }} autoFocus={autoFocus} diff --git a/src/PickerPanel.tsx b/src/PickerPanel.tsx index 55fde1585e06..55b32e669c66 100644 --- a/src/PickerPanel.tsx +++ b/src/PickerPanel.tsx @@ -193,7 +193,7 @@ function PickerPanel(props: PickerPanelProps) { const [mergedValue, setInnerValue] = useMergedState(null, { value, defaultValue, - postState: val => { + postState: (val) => { if (!val && defaultOpenValue && picker === 'time') { return defaultOpenValue; } @@ -205,7 +205,7 @@ function PickerPanel(props: PickerPanelProps) { const [viewDate, setInnerViewDate] = useMergedState(null, { value: pickerValue, defaultValue: defaultPickerValue || mergedValue, - postState: date => date || generateConfig.getNow(), + postState: (date) => date || generateConfig.getNow(), }); const setViewDate = (date: DateType) => { @@ -270,7 +270,7 @@ function PickerPanel(props: PickerPanelProps) { onContextSelect(date, type); } - if (onChange && !isEqual(generateConfig, date, mergedValue)) { + if (onChange && !isEqual(generateConfig, date, mergedValue) && !disabledDate?.(date)) { onChange(date); } } @@ -307,7 +307,7 @@ function PickerPanel(props: PickerPanelProps) { /* eslint-enable no-lone-blocks */ }; - const onInternalBlur: React.FocusEventHandler = e => { + const onInternalBlur: React.FocusEventHandler = (e) => { if (panelRef.current && panelRef.current.onBlur) { panelRef.current.onBlur(e); } diff --git a/tests/keyboard.spec.tsx b/tests/keyboard.spec.tsx index ad8fbfe4d92c..e8855d5decea 100644 --- a/tests/keyboard.spec.tsx +++ b/tests/keyboard.spec.tsx @@ -477,35 +477,61 @@ describe('Picker.Keyboard', () => { expect(preventDefault).toHaveBeenCalled(); }); - it('keyboard should not trigger on disabledDate', () => { - const onChange = jest.fn(); - const onSelect = jest.fn(); - const wrapper = mount( - date.date() % 2 === 0} - />, - ); - wrapper.find('input').simulate('focus'); - wrapper.keyDown(KeyCode.ENTER); - wrapper.keyDown(KeyCode.TAB); - wrapper.keyDown(KeyCode.TAB); - wrapper.keyDown(KeyCode.DOWN); - expect(isSame(onSelect.mock.calls[0][0], '1990-09-10')).toBeTruthy(); + describe('keyboard should not trigger on disabledDate', () => { + it('picker', () => { + const onChange = jest.fn(); + const onSelect = jest.fn(); + const wrapper = mount( + date.date() % 2 === 0} + />, + ); + wrapper.find('input').simulate('focus'); + wrapper.keyDown(KeyCode.ENTER); + wrapper.keyDown(KeyCode.TAB); + wrapper.keyDown(KeyCode.TAB); + wrapper.keyDown(KeyCode.DOWN); + expect(isSame(onSelect.mock.calls[0][0], '1990-09-10')).toBeTruthy(); - // Not enter to change - wrapper.keyDown(KeyCode.ENTER); - expect(onChange).not.toHaveBeenCalled(); + // Not enter to change + wrapper.keyDown(KeyCode.ENTER); + expect(onChange).not.toHaveBeenCalled(); - // Not button enabled - expect(wrapper.find('.rc-picker-ok button').props().disabled).toBeTruthy(); + // Not button enabled + expect(wrapper.find('.rc-picker-ok button').props().disabled).toBeTruthy(); - // Another can be enter - wrapper.keyDown(KeyCode.RIGHT); - expect(wrapper.find('.rc-picker-ok button').props().disabled).toBeFalsy(); - wrapper.keyDown(KeyCode.ENTER); - expect(onChange).toHaveBeenCalled(); + // Another can be enter + wrapper.keyDown(KeyCode.RIGHT); + expect(wrapper.find('.rc-picker-ok button').props().disabled).toBeFalsy(); + wrapper.keyDown(KeyCode.ENTER); + expect(onChange).toHaveBeenCalled(); + }); + + it('panel', () => { + const onChange = jest.fn(); + const onSelect = jest.fn(); + const wrapper = mount( + date.date() % 2 === 0} + />, + ); + + wrapper.find('.rc-picker-panel').simulate('focus'); + + // 9-10 is disabled + wrapper.keyDown(KeyCode.DOWN); + expect(isSame(onSelect.mock.calls[0][0], '1990-09-10')).toBeTruthy(); + expect(onChange).not.toHaveBeenCalled(); + + // 9-17 is enabled + wrapper.keyDown(KeyCode.DOWN); + expect(isSame(onSelect.mock.calls[1][0], '1990-09-17')).toBeTruthy(); + expect(isSame(onChange.mock.calls[0][0], '1990-09-17')).toBeTruthy(); + }); }); }); diff --git a/tests/setup.js b/tests/setup.js index 16881991b898..c0d938da6273 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -70,9 +70,13 @@ Object.assign(Enzyme.ReactWrapper.prototype, { this.find('.rc-picker-clear-btn').simulate('mouseUp'); }, keyDown(which, info = {}, index = 0) { - this.find('input') - .at(index) - .simulate('keydown', { ...info, which }); + let component = this.find('input'); + + if (component.length === 0) { + component = this.find('.rc-picker-panel'); + } + + component.at(index).simulate('keydown', { ...info, which }); }, inputValue(text, index = 0) { this.find('input')