Skip to content

Commit

Permalink
fix: Calendar should not trigger onChange when date is disabled (a…
Browse files Browse the repository at this point in the history
…nt-design#183)

* fix: Not trigger onChange

* fix: should pass onSelect

* test: Add test case
  • Loading branch information
zombieJ authored Nov 26, 2020
1 parent 59f8f07 commit 04b43d5
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 46 deletions.
3 changes: 3 additions & 0 deletions examples/calendar.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.rc-picker-cell-selected {
background: red;
}
9 changes: 9 additions & 0 deletions examples/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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 () => (
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
<div>
Expand All @@ -29,13 +36,15 @@ export default () => (
// picker="month"
generateConfig={momentGenerateConfig}
dateRender={dateRender}
{...disabledProps}
/>
</div>
<div>
<Picker<Moment>
locale={zhCN}
generateConfig={momentGenerateConfig}
dateRender={dateRender}
{...disabledProps}
/>
</div>
</div>
Expand Down
29 changes: 17 additions & 12 deletions src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
onMouseLeave,
onContextMenu,
onClick,
onSelect,
direction,
autoComplete = 'off',
} = props as MergedPickerProps<DateType>;
Expand All @@ -195,16 +196,16 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
const [selectedValue, setSelectedValue] = React.useState<DateType | null>(mergedValue);

// Operation ref
const operationRef: React.MutableRefObject<ContextOperationRefProps | null> = React.useRef<
ContextOperationRefProps
>(null);
const operationRef: React.MutableRefObject<ContextOperationRefProps | null> = React.useRef<ContextOperationRefProps>(
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);
}
Expand All @@ -224,7 +225,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {

const [text, triggerTextChange, resetText] = useTextValueMapping({
valueTexts,
onTextChange: newText => {
onTextChange: (newText) => {
const inputDate = parseValue(newText, {
locale,
formatList,
Expand Down Expand Up @@ -292,7 +293,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
value: text,
triggerOpen,
forwardKeyDown,
isClickOutside: target =>
isClickOutside: (target) =>
!elementsContains([panelDivRef.current, inputDivRef.current], target as HTMLElement),
onSubmit: () => {
if (disabledDate && disabledDate(selectedValue)) {
Expand Down Expand Up @@ -364,6 +365,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
style: undefined,
pickerValue: undefined,
onPickerValueChange: undefined,
onChange: null,
};

let panelNode: React.ReactNode = (
Expand All @@ -376,7 +378,10 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
value={selectedValue}
locale={locale}
tabIndex={-1}
onChange={setSelectedValue}
onSelect={(date) => {
onSelect?.(date);
setSelectedValue(date);
}}
direction={direction}
/>
);
Expand All @@ -388,7 +393,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
const panel = (
<div
className={`${prefixCls}-panel-container`}
onMouseDown={e => {
onMouseDown={(e) => {
e.preventDefault();
}}
>
Expand All @@ -405,11 +410,11 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
if (allowClear && mergedValue && !disabled) {
clearNode = (
<span
onMouseDown={e => {
onMouseDown={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onMouseUp={e => {
onMouseUp={(e) => {
e.preventDefault();
e.stopPropagation();
triggerChange(null);
Expand Down Expand Up @@ -497,7 +502,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
disabled={disabled}
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !typing}
value={hoverValue || text}
onChange={e => {
onChange={(e) => {
triggerTextChange(e.target.value);
}}
autoFocus={autoFocus}
Expand Down
8 changes: 4 additions & 4 deletions src/PickerPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
const [mergedValue, setInnerValue] = useMergedState(null, {
value,
defaultValue,
postState: val => {
postState: (val) => {
if (!val && defaultOpenValue && picker === 'time') {
return defaultOpenValue;
}
Expand All @@ -205,7 +205,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
const [viewDate, setInnerViewDate] = useMergedState<DateType | null, DateType>(null, {
value: pickerValue,
defaultValue: defaultPickerValue || mergedValue,
postState: date => date || generateConfig.getNow(),
postState: (date) => date || generateConfig.getNow(),
});

const setViewDate = (date: DateType) => {
Expand Down Expand Up @@ -270,7 +270,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
onContextSelect(date, type);
}

if (onChange && !isEqual(generateConfig, date, mergedValue)) {
if (onChange && !isEqual(generateConfig, date, mergedValue) && !disabledDate?.(date)) {
onChange(date);
}
}
Expand Down Expand Up @@ -307,7 +307,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
/* eslint-enable no-lone-blocks */
};

const onInternalBlur: React.FocusEventHandler<HTMLElement> = e => {
const onInternalBlur: React.FocusEventHandler<HTMLElement> = (e) => {
if (panelRef.current && panelRef.current.onBlur) {
panelRef.current.onBlur(e);
}
Expand Down
80 changes: 53 additions & 27 deletions tests/keyboard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<MomentPicker
showTime
onSelect={onSelect}
onChange={onChange}
disabledDate={date => 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(
<MomentPicker
showTime
onSelect={onSelect}
onChange={onChange}
disabledDate={date => 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(
<MomentPickerPanel
onSelect={onSelect}
onChange={onChange}
disabledDate={date => 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();
});
});
});
10 changes: 7 additions & 3 deletions tests/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down

0 comments on commit 04b43d5

Please sign in to comment.