Skip to content

Commit 0c7afe5

Browse files
Yuiai01dujiaqi
andauthored
fix: Able to navigate between disabled dates with keyboard (#616)
* fix: Able to navigate between disabled dates with keyboard * chore: add test case --------- Co-authored-by: dujiaqi <[email protected]>
1 parent e87b629 commit 0c7afe5

File tree

2 files changed

+101
-12
lines changed

2 files changed

+101
-12
lines changed

src/PickerPanel.tsx

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ import type { DateRender } from './panels/DatePanel/DateBody';
2727
import DatetimePanel from './panels/DatetimePanel';
2828
import DecadePanel from './panels/DecadePanel';
2929
import MonthPanel from './panels/MonthPanel';
30-
import type { MonthCellRender } from './panels/MonthPanel/MonthBody';
30+
import { MONTH_COL_COUNT, type MonthCellRender } from './panels/MonthPanel/MonthBody';
3131
import QuarterPanel from './panels/QuarterPanel';
3232
import type { SharedTimeProps } from './panels/TimePanel';
3333
import TimePanel from './panels/TimePanel';
3434
import WeekPanel from './panels/WeekPanel';
3535
import YearPanel from './panels/YearPanel';
3636
import RangeContext from './RangeContext';
37-
import { isEqual } from './utils/dateUtil';
37+
import { isEqual, WEEK_DAY_COUNT } from './utils/dateUtil';
3838
import getExtraFooter from './utils/getExtraFooter';
3939
import getRanges from './utils/getRanges';
4040
import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil';
@@ -125,6 +125,9 @@ type MergedPickerPanelProps<DateType> = {
125125
picker?: PickerMode;
126126
} & OmitType<DateType>;
127127

128+
// Calendar picker type
129+
const CALENDAR_PANEL_MODE: PanelMode[] = ['date', 'month'];
130+
128131
function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
129132
const {
130133
prefixCls = 'rc-picker',
@@ -297,9 +300,49 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
297300
}
298301
};
299302

303+
const isSelectable = (key) => {
304+
if (CALENDAR_PANEL_MODE.includes(mergedMode)) {
305+
let date;
306+
let operationFnc;
307+
const isDateMode = mergedMode === 'date';
308+
if (key === KeyCode.PAGE_UP || key === KeyCode.PAGE_DOWN) {
309+
operationFnc = isDateMode ? generateConfig.addMonth : generateConfig.addYear;
310+
} else {
311+
operationFnc = isDateMode ? generateConfig.addDate : generateConfig.addMonth;
312+
}
313+
314+
switch (key) {
315+
case KeyCode.LEFT:
316+
case KeyCode.PAGE_UP:
317+
date = operationFnc(viewDate, -1);
318+
break;
319+
case KeyCode.RIGHT:
320+
case KeyCode.PAGE_DOWN:
321+
date = operationFnc(viewDate, 1);
322+
break;
323+
case KeyCode.UP:
324+
case KeyCode.DOWN:
325+
date = operationFnc(
326+
viewDate,
327+
Number(
328+
`${key === KeyCode.UP ? '-' : ''}${isDateMode ? WEEK_DAY_COUNT : MONTH_COL_COUNT}`,
329+
),
330+
);
331+
break;
332+
}
333+
334+
if (date) {
335+
return !disabledDate?.(date);
336+
}
337+
}
338+
return true;
339+
};
340+
300341
// ========================= Interactive ==========================
301342
const onInternalKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
302343
if (panelRef.current && panelRef.current.onKeyDown) {
344+
let selectable = true;
345+
const { which } = e;
303346
if (
304347
[
305348
KeyCode.LEFT,
@@ -309,11 +352,18 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
309352
KeyCode.PAGE_UP,
310353
KeyCode.PAGE_DOWN,
311354
KeyCode.ENTER,
312-
].includes(e.which)
355+
].includes(which)
313356
) {
314357
e.preventDefault();
358+
if (which !== KeyCode.ENTER && tabIndex === 0) {
359+
selectable = isSelectable(which);
360+
}
361+
}
362+
363+
// Cannot use keyboard to select disabled date
364+
if (selectable) {
365+
return panelRef.current.onKeyDown(e);
315366
}
316-
return panelRef.current.onKeyDown(e);
317367
}
318368

319369
/* istanbul ignore next */
@@ -379,7 +429,6 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
379429
delete pickerProps.onChange;
380430
delete pickerProps.onSelect;
381431

382-
383432
switch (mergedMode) {
384433
case 'decade':
385434
panelNode = (

tests/keyboard.spec.tsx

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -534,16 +534,56 @@ describe('Picker.Keyboard', () => {
534534
);
535535

536536
fireEvent.focus(document.querySelector('.rc-picker-panel'));
537-
538-
// 9-10 is disabled
537+
// 9-02、9-04、9-10 is disabled
538+
panelKeyDown(KeyCode.LEFT);
539+
panelKeyDown(KeyCode.RIGHT);
539540
panelKeyDown(KeyCode.DOWN);
540-
expect(isSame(onSelect.mock.calls[0][0], '1990-09-10')).toBeTruthy();
541-
expect(onChange).not.toHaveBeenCalled();
541+
expect(onSelect).not.toHaveBeenCalled();
542+
543+
// 7-27、8-27 is enabled
544+
panelKeyDown(KeyCode.UP);
545+
expect(isSame(onSelect.mock.calls[0][0], '1990-08-27')).toBeTruthy();
546+
onSelect.mockReset();
547+
panelKeyDown(KeyCode.PAGE_UP);
548+
expect(isSame(onSelect.mock.calls[0][0], '1990-07-27')).toBeTruthy();
549+
onSelect.mockReset();
550+
panelKeyDown(KeyCode.PAGE_DOWN);
551+
expect(isSame(onSelect.mock.calls[0][0], '1990-08-27')).toBeTruthy();
552+
});
553+
554+
it('month panel', () => {
555+
const onChange = jest.fn();
556+
const onSelect = jest.fn();
557+
const now = new Date();
558+
render(
559+
<MomentPickerPanel
560+
picker="month"
561+
onSelect={onSelect}
562+
onChange={onChange}
563+
disabledDate={(date) => date.month() < now.getMonth()}
564+
/>,
565+
);
566+
567+
fireEvent.focus(document.querySelector('.rc-picker-panel'));
542568

543-
// 9-17 is enabled
569+
// PAGE_UP and PAGE_DOWN do not trigger the select
570+
panelKeyDown(KeyCode.PAGE_UP);
571+
panelKeyDown(KeyCode.PAGE_DOWN);
572+
expect(onSelect).not.toHaveBeenCalled();
573+
574+
// The disabled date is before August
575+
panelKeyDown(KeyCode.LEFT);
576+
panelKeyDown(KeyCode.UP);
577+
expect(onSelect).not.toHaveBeenCalled();
578+
579+
// August and subsequent dates are enable
580+
panelKeyDown(KeyCode.RIGHT);
581+
expect(isSame(onSelect.mock.calls[0][0], '1990-10-03')).toBeTruthy();
582+
onSelect.mockReset();
583+
panelKeyDown(KeyCode.LEFT);
584+
onSelect.mockReset();
544585
panelKeyDown(KeyCode.DOWN);
545-
expect(isSame(onSelect.mock.calls[1][0], '1990-09-17')).toBeTruthy();
546-
expect(isSame(onChange.mock.calls[0][0], '1990-09-17')).toBeTruthy();
586+
expect(isSame(onSelect.mock.calls[0][0], '1990-12-03')).toBeTruthy();
547587
});
548588
});
549589
});

0 commit comments

Comments
 (0)