Skip to content

Commit b7a1984

Browse files
Yuiai01dujiaqi
andauthored
chore: Duplicate Able to navigate between disabled dates with keyboard (#621)
Co-authored-by: dujiaqi <[email protected]>
1 parent 994b88b commit b7a1984

File tree

2 files changed

+104
-21
lines changed

2 files changed

+104
-21
lines changed

src/PickerPanel.tsx

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import type {
2929
OnPanelChange,
3030
Components,
3131
} from './interface';
32-
import { isEqual } from './utils/dateUtil';
32+
import { WEEK_DAY_COUNT, isEqual } from './utils/dateUtil';
3333
import PanelContext from './PanelContext';
3434
import type { DateRender } from './panels/DatePanel/DateBody';
3535
import { PickerModeMap } from './utils/uiUtil';
36-
import type { MonthCellRender } from './panels/MonthPanel/MonthBody';
36+
import { MONTH_COL_COUNT, type MonthCellRender } from './panels/MonthPanel/MonthBody';
3737
import RangeContext from './RangeContext';
3838
import getExtraFooter from './utils/getExtraFooter';
3939
import getRanges from './utils/getRanges';
@@ -117,6 +117,9 @@ type MergedPickerPanelProps<DateType> = {
117117
picker?: PickerMode;
118118
} & OmitType<DateType>;
119119

120+
// Calendar picker type
121+
const CALENDAR_PANEL_MODE: PanelMode[] = ['date', 'month'];
122+
120123
function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
121124
const {
122125
prefixCls = 'rc-picker',
@@ -290,9 +293,49 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
290293
}
291294
};
292295

296+
const isSelectable = (key) => {
297+
if (CALENDAR_PANEL_MODE.includes(mergedMode)) {
298+
let date;
299+
let operationFnc;
300+
const isDateMode = mergedMode === 'date';
301+
if (key === KeyCode.PAGE_UP || key === KeyCode.PAGE_DOWN) {
302+
operationFnc = isDateMode ? generateConfig.addMonth : generateConfig.addYear;
303+
} else {
304+
operationFnc = isDateMode ? generateConfig.addDate : generateConfig.addMonth;
305+
}
306+
307+
switch (key) {
308+
case KeyCode.LEFT:
309+
case KeyCode.PAGE_UP:
310+
date = operationFnc(viewDate, -1);
311+
break;
312+
case KeyCode.RIGHT:
313+
case KeyCode.PAGE_DOWN:
314+
date = operationFnc(viewDate, 1);
315+
break;
316+
case KeyCode.UP:
317+
case KeyCode.DOWN:
318+
date = operationFnc(
319+
viewDate,
320+
Number(
321+
`${key === KeyCode.UP ? '-' : ''}${isDateMode ? WEEK_DAY_COUNT : MONTH_COL_COUNT}`,
322+
),
323+
);
324+
break;
325+
}
326+
327+
if (date) {
328+
return !disabledDate?.(date);
329+
}
330+
}
331+
return true;
332+
};
333+
293334
// ========================= Interactive ==========================
294335
const onInternalKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
295336
if (panelRef.current && panelRef.current.onKeyDown) {
337+
let selectable = true;
338+
const { which } = e;
296339
if (
297340
[
298341
KeyCode.LEFT,
@@ -302,11 +345,18 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
302345
KeyCode.PAGE_UP,
303346
KeyCode.PAGE_DOWN,
304347
KeyCode.ENTER,
305-
].includes(e.which)
348+
].includes(which)
306349
) {
307350
e.preventDefault();
351+
if (which !== KeyCode.ENTER && tabIndex === 0) {
352+
selectable = isSelectable(which);
353+
}
354+
}
355+
356+
// Cannot use keyboard to select disabled date
357+
if (selectable) {
358+
return panelRef.current.onKeyDown(e);
308359
}
309-
return panelRef.current.onKeyDown(e);
310360
}
311361

312362
/* istanbul ignore next */

tests/keyboard.spec.tsx

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import React from 'react';
22
import MockDate from 'mockdate';
33
import { act } from 'react-dom/test-utils';
44
import KeyCode from 'rc-util/lib/KeyCode';
5+
import type { Wrapper } from './util/commonUtil';
56
import {
67
mount,
78
getMoment,
89
isSame,
910
MomentPicker,
1011
MomentPickerPanel,
11-
Wrapper,
1212
MomentRangePicker,
1313
} from './util/commonUtil';
1414

@@ -396,10 +396,7 @@ describe('Picker.Keyboard', () => {
396396
jest.runAllTimers();
397397
});
398398
expect(
399-
wrapper
400-
.find('.rc-picker-input')
401-
.last()
402-
.hasClass('rc-picker-input-active'),
399+
wrapper.find('.rc-picker-input').last().hasClass('rc-picker-input-active'),
403400
).toBeTruthy();
404401
onCalendarChange.mockReset();
405402

@@ -436,12 +433,7 @@ describe('Picker.Keyboard', () => {
436433
.first()
437434
.simulate('change', { target: { value: '2000-01-01' } });
438435
wrapper.keyDown(KeyCode.ESC);
439-
expect(
440-
wrapper
441-
.find('input')
442-
.first()
443-
.props().value,
444-
).toEqual('');
436+
expect(wrapper.find('input').first().props().value).toEqual('');
445437
});
446438

447439
it('move based on current date on first keyboard event', () => {
@@ -523,15 +515,56 @@ describe('Picker.Keyboard', () => {
523515

524516
wrapper.find('.rc-picker-panel').simulate('focus');
525517

526-
// 9-10 is disabled
518+
// 9-02、9-04、9-10 is disabled
519+
wrapper.keyDown(KeyCode.LEFT);
520+
wrapper.keyDown(KeyCode.RIGHT);
527521
wrapper.keyDown(KeyCode.DOWN);
528-
expect(isSame(onSelect.mock.calls[0][0], '1990-09-10')).toBeTruthy();
529-
expect(onChange).not.toHaveBeenCalled();
522+
expect(onSelect).not.toHaveBeenCalled();
530523

531-
// 9-17 is enabled
524+
// 7-27、8-27 is enabled
525+
wrapper.keyDown(KeyCode.UP);
526+
expect(isSame(onSelect.mock.calls[0][0], '1990-08-27')).toBeTruthy();
527+
onSelect.mockReset();
528+
wrapper.keyDown(KeyCode.PAGE_UP);
529+
expect(isSame(onSelect.mock.calls[0][0], '1990-07-27')).toBeTruthy();
530+
onSelect.mockReset();
531+
wrapper.keyDown(KeyCode.PAGE_DOWN);
532+
expect(isSame(onSelect.mock.calls[0][0], '1990-08-27')).toBeTruthy();
533+
});
534+
535+
it('month panel', () => {
536+
const onChange = jest.fn();
537+
const onSelect = jest.fn();
538+
const now = new Date();
539+
const wrapper = mount(
540+
<MomentPickerPanel
541+
picker="month"
542+
onSelect={onSelect}
543+
onChange={onChange}
544+
disabledDate={date => date.month() < now.getMonth()}
545+
/>,
546+
);
547+
548+
wrapper.find('.rc-picker-panel').simulate('focus');
549+
550+
// PAGE_UP and PAGE_DOWN do not trigger the select
551+
wrapper.keyDown(KeyCode.PAGE_UP);
552+
wrapper.keyDown(KeyCode.PAGE_DOWN);
553+
expect(onSelect).not.toHaveBeenCalled();
554+
555+
// The disabled date is before August
556+
wrapper.keyDown(KeyCode.LEFT);
557+
wrapper.keyDown(KeyCode.UP);
558+
expect(onSelect).not.toHaveBeenCalled();
559+
560+
// August and subsequent dates are enable
561+
wrapper.keyDown(KeyCode.RIGHT);
562+
expect(isSame(onSelect.mock.calls[0][0], '1990-10-03')).toBeTruthy();
563+
onSelect.mockReset();
564+
wrapper.keyDown(KeyCode.LEFT);
565+
onSelect.mockReset();
532566
wrapper.keyDown(KeyCode.DOWN);
533-
expect(isSame(onSelect.mock.calls[1][0], '1990-09-17')).toBeTruthy();
534-
expect(isSame(onChange.mock.calls[0][0], '1990-09-17')).toBeTruthy();
567+
expect(isSame(onSelect.mock.calls[0][0], '1990-12-03')).toBeTruthy();
535568
});
536569
});
537570
});

0 commit comments

Comments
 (0)