From 01dbc149869d6f1f2d7b97ea53e0ac01268ee497 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Tue, 16 Apr 2024 16:01:58 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Added=20support=20for=20up=20and=20?= =?UTF-8?q?down=20arrow=20key=20movement=20in=20the=20year=20picker=20of?= =?UTF-8?q?=20the=20date=20picker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prevent the default scroll movement when the arrow key is pressed Closes #4446 --- src/year.jsx | 65 ++++++++++++++++++++++-- test/year_picker_test.test.js | 94 +++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 5 deletions(-) diff --git a/src/year.jsx b/src/year.jsx index 6b2009606..55b82efd2 100644 --- a/src/year.jsx +++ b/src/year.jsx @@ -4,6 +4,8 @@ import { getYear, newDate } from "./date_utils"; import * as utils from "./date_utils"; import { clsx } from "clsx"; +const VERTICAL_NAVIGATION_OFFSET = 3; + export default class Year extends React.Component { static propTypes = { clearSelectingDate: PropTypes.func, @@ -77,10 +79,12 @@ export default class Year extends React.Component { if (this.isDisabled(newDate) || this.isExcluded(newDate)) return; this.props.setPreSelection(newDate); - if (newYear - startPeriod === -1) { - this.updateFocusOnPaginate(yearItemNumber - 1); - } else if (newYear - startPeriod === yearItemNumber) { - this.updateFocusOnPaginate(0); + if (newYear - startPeriod < 0) { + this.updateFocusOnPaginate(yearItemNumber - (startPeriod - newYear)); + } else if (newYear - startPeriod >= yearItemNumber) { + this.updateFocusOnPaginate( + Math.abs(yearItemNumber - (newYear - startPeriod)), + ); } else this.YEAR_REFS[newYear - startPeriod].current.focus(); }; @@ -168,7 +172,12 @@ export default class Year extends React.Component { onYearKeyDown = (e, y) => { const { key } = e; - const { handleOnKeyDown } = this.props; + const { date, yearItemNumber, handleOnKeyDown } = this.props; + + if (key !== "Tab") { + // preventDefault on tab event blocks focus change + e.preventDefault(); + } if (!this.props.disabledKeyboardNavigation) { switch (key) { @@ -188,6 +197,52 @@ export default class Year extends React.Component { utils.subYears(this.props.preSelection, 1), ); break; + case "ArrowUp": { + const { startPeriod } = utils.getYearsPeriod(date, yearItemNumber); + let offset = VERTICAL_NAVIGATION_OFFSET; + let newYear = y - offset; + + if (newYear < startPeriod) { + const leftOverOffset = yearItemNumber % offset; + + if (y >= startPeriod && y < startPeriod + leftOverOffset) { + offset = leftOverOffset; + } else { + offset += leftOverOffset; + } + + newYear = y - offset; + } + + this.handleYearNavigation( + newYear, + utils.subYears(this.props.preSelection, offset), + ); + break; + } + case "ArrowDown": { + const { endPeriod } = utils.getYearsPeriod(date, yearItemNumber); + let offset = VERTICAL_NAVIGATION_OFFSET; + let newYear = y + offset; + + if (newYear > endPeriod) { + const leftOverOffset = yearItemNumber % offset; + + if (y <= endPeriod && y > endPeriod - leftOverOffset) { + offset = leftOverOffset; + } else { + offset += leftOverOffset; + } + + newYear = y + offset; + } + + this.handleYearNavigation( + newYear, + utils.addYears(this.props.preSelection, offset), + ); + break; + } } } diff --git a/test/year_picker_test.test.js b/test/year_picker_test.test.js index 0d54815bf..6b58e57a7 100644 --- a/test/year_picker_test.test.js +++ b/test/year_picker_test.test.js @@ -637,6 +637,20 @@ describe("YearPicker", () => { which: 39, }); + const simulateUp = (target) => + fireEvent.keyDown(target, { + key: "ArrowUp", + keyCode: 38, + which: 38, + }); + + const simulateDown = (target) => + fireEvent.keyDown(target, { + key: "ArrowDown", + keyCode: 40, + which: 40, + }); + it("should preSelect and set 2020 on left arrow press", () => { const yearPicker = getPicker("2021-01-01"); @@ -657,6 +671,86 @@ describe("YearPicker", () => { expect(utils.getYear(preSelected)).toBe(2022); }); + it("should preSelect and set 2021 on up arrow press", () => { + const yearPicker = getPicker("2024-01-01"); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateUp(target); + + expect(utils.getYear(preSelected)).toBe(2021); + }); + it("should preSelect and set 2027 on down arrow press", () => { + const yearPicker = getPicker("2024-01-01"); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateDown(target); + + expect(utils.getYear(preSelected)).toBe(2027); + }); + it("should paginate from 2018 to 2015", () => { + const yearPicker = getPicker("2018-01-01"); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateUp(target); + + expect(utils.getYear(preSelected)).toBe(2015); + }); + it("should paginate from 2018 to 2016 with custom yearItemNumber", () => { + const yearPicker = getPicker("2018-01-01", { yearItemNumber: 8 }); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateUp(target); + + expect(utils.getYear(preSelected)).toBe(2016); + }); + it("should paginate from 2019 to 2014 with custom yearItemNumber", () => { + const yearPicker = getPicker("2019-01-01", { yearItemNumber: 8 }); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateUp(target); + + expect(utils.getYear(preSelected)).toBe(2014); + }); + it("should paginate from 2028 to 2031", () => { + const yearPicker = getPicker("2028-01-01"); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateDown(target); + + expect(utils.getYear(preSelected)).toBe(2031); + }); + it("should paginate from 2024 to 2026 with custom yearItemNumber", () => { + const yearPicker = getPicker("2024-01-01", { yearItemNumber: 8 }); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateDown(target); + + expect(utils.getYear(preSelected)).toBe(2026); + }); + it("should paginate from 2022 to 2027 with custom yearItemNumber", () => { + const yearPicker = getPicker("2022-01-01", { yearItemNumber: 8 }); + + const target = yearPicker.querySelector( + ".react-datepicker__year-text--selected", + ); + simulateDown(target); + + expect(utils.getYear(preSelected)).toBe(2027); + }); it("should paginate from 2017 to 2016", () => { const yearPicker = getPicker("2017-01-01");