From cf752c3d68cefbede4ba9c464555854be254ca6d Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Fri, 26 Jul 2024 14:48:03 +0530 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20Restrict=20the=20focus=20to?= =?UTF-8?q?=20the=20disabled=20months/quarter/year=20using=20the=20initial?= =?UTF-8?q?=20Tab=20key=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5010 --- src/month.tsx | 16 +++++- src/test/month_test.test.tsx | 83 +++++++++++++++++++++++++++++- src/test/test_utils.ts | 15 ++++++ src/test/year_picker_test.test.tsx | 47 ++++++++++++++++- src/year.tsx | 3 +- 5 files changed, 159 insertions(+), 5 deletions(-) diff --git a/src/month.tsx b/src/month.tsx index c4e3a5a76..63d7dab59 100644 --- a/src/month.tsx +++ b/src/month.tsx @@ -843,8 +843,13 @@ export default class Month extends Component { return "-1"; } const preSelectedMonth = getMonth(this.props.preSelection); + const { isDisabled: isPreSelectedMonthDisabled } = + this.isMonthDisabledForLabelDate(preSelectedMonth); + const tabIndex = - !this.props.disabledKeyboardNavigation && m === preSelectedMonth + !this.props.disabledKeyboardNavigation && + m === preSelectedMonth && + !isPreSelectedMonthDisabled ? "0" : "-1"; @@ -856,8 +861,15 @@ export default class Month extends Component { return "-1"; } const preSelectedQuarter = getQuarter(this.props.preSelection); + const isCurrentQuarterDisabled = isQuarterDisabled( + this.props.day, + this.props, + ); + const tabIndex = - !this.props.disabledKeyboardNavigation && q === preSelectedQuarter + !this.props.disabledKeyboardNavigation && + q === preSelectedQuarter && + !isCurrentQuarterDisabled ? "0" : "-1"; diff --git a/src/test/month_test.test.tsx b/src/test/month_test.test.tsx index 7c0d3bd36..22bc184aa 100644 --- a/src/test/month_test.test.tsx +++ b/src/test/month_test.test.tsx @@ -28,7 +28,7 @@ import { import Month from "../month"; import { runAxe } from "./run_axe"; -import { getKey, range } from "./test_utils"; +import { getKey, range, openDateInput, gotoNextView } from "./test_utils"; import type { RenderResult } from "@testing-library/react"; @@ -2525,4 +2525,85 @@ describe("Month", () => { ).toBeNull(); }); }); + + describe("Auto-Focus", () => { + it("should auto-focus on the same selected month when navigating to the next/previous year", () => { + const date = newDate("2024-06-01"); + const selectedMonth = date.getMonth(); + + const { container } = render( + , + ); + + openDateInput(container); + gotoNextView(container); + + const preSelectedDateElement = container.querySelector( + `.react-datepicker__month-text.react-datepicker__month-${selectedMonth}`, + )!; + expect(preSelectedDateElement.getAttribute("tabindex")).toBe("0"); + }); + + it("shouldn't auto-focus on the same selected month when navigating to the next/previous year if the corresponding month is disabled", () => { + const date = newDate("2024-06-01"); + const selectedMonth = date.getMonth(); + const excludeDate = newDate(`2025-0${selectedMonth + 1}-01`); + + const { container } = render( + , + ); + + openDateInput(container); + gotoNextView(container); + + const preSelectedDateElement = container.querySelector( + `.react-datepicker__month-text.react-datepicker__month-${selectedMonth}`, + )!; + expect(preSelectedDateElement.getAttribute("tabindex")).toBe("-1"); + }); + + it("should auto-focus on the same selected quarter when navigating to the next/previous year", () => { + const date = newDate("2024-06-01"); + + const { container } = render( + , + ); + + openDateInput(container); + const selectedQuarterValue = getQuarter(date); + gotoNextView(container); + + const preSelectedDateElement = container.querySelector( + `.react-datepicker__quarter-text.react-datepicker__quarter-${selectedQuarterValue}`, + )!; + expect(preSelectedDateElement.getAttribute("tabindex")).toBe("0"); + }); + + it("shouldn't auto-focus on the same selected quarter when navigating to the next/previous year if the corresponding quarter date is disabled", () => { + const date = newDate("2024-06-01"); + const selectedMonth = date.getMonth(); + const excludeDate = newDate(`2025-0${selectedMonth + 1}-01`); + + const { container } = render( + , + ); + + openDateInput(container); + const selectedQuarterValue = getQuarter(date); + gotoNextView(container); + + const preSelectedDateElement = container.querySelector( + `.react-datepicker__quarter-text.react-datepicker__quarter-${selectedQuarterValue}`, + )!; + expect(preSelectedDateElement.getAttribute("tabindex")).toBe("-1"); + }); + }); }); diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index 2d3662370..72cf1fbb9 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -1,3 +1,5 @@ +import { fireEvent } from "@testing-library/react"; + import { KeyType } from "../date_utils"; interface KeyEvent { @@ -65,3 +67,16 @@ export const range = (from: number, to: number): number[] => { } return list; }; + +export const openDateInput = (container: Element) => { + const dateInput = container.querySelector("input")!; + fireEvent.focus(dateInput); +}; + +export const gotoNextView = (container: Element) => { + const calendar = container.querySelector(".react-datepicker")!; + const nextButton = calendar.querySelector( + ".react-datepicker__navigation--next", + )!; + fireEvent.click(nextButton); +}; diff --git a/src/test/year_picker_test.test.tsx b/src/test/year_picker_test.test.tsx index a1a802646..abfe3e27d 100644 --- a/src/test/year_picker_test.test.tsx +++ b/src/test/year_picker_test.test.tsx @@ -14,7 +14,15 @@ import { import DatePicker from "../index"; import Year from "../year"; -import { getKey } from "./test_utils"; +import { getKey, gotoNextView, openDateInput } from "./test_utils"; + +const getYearOffset = (calendar: Element, date: Date): number => { + const dateNode = calendar.querySelector( + `.react-datepicker__year-text.react-datepicker__year-${date.getFullYear()}`, + )!; + const yearPicker = calendar.querySelector(".react-datepicker__year-wrapper")!; + return Array.from(yearPicker.children).indexOf(dateNode); +}; describe("YearPicker", () => { it("should show year picker component when showYearPicker prop is present", () => { @@ -872,4 +880,41 @@ describe("YearPicker", () => { container.querySelector(`.react-datepicker__year-${date.getFullYear()}`), ).not.toBeNull(); }); + + describe("Auto-Focus", () => { + it("should auto-focus on the same year offset in the next/previous view when navigating", () => { + const date = newDate("2024-06-01"); + const { container } = render( + , + ); + openDateInput(container); + const calendar = container.querySelector(".react-datepicker")!; + const selectedElementOffset = getYearOffset(calendar, date); + gotoNextView(container); + const preSelectedDateElement = container.querySelector( + `.react-datepicker__year-wrapper :nth-child(${selectedElementOffset + 1}).react-datepicker__year-text`, + )!; + expect(preSelectedDateElement.getAttribute("tabindex")).toBe("0"); + }); + it("shouldn't auto-focus on the same year offset in the next/previous view when navigating if the year in the corresponding offset is disabled", () => { + const date = newDate("2024-06-01"); + const excludeDate = newDate("2036-05-01"); // 2036 is in the same 8th offset like 2024 + const { container } = render( + , + ); + openDateInput(container); + const calendar = container.querySelector(".react-datepicker")!; + const selectedElementOffset = getYearOffset(calendar, date); + gotoNextView(container); + const preSelectedDateElement = container.querySelector( + `.react-datepicker__year-wrapper :nth-child(${selectedElementOffset + 1}).react-datepicker__year-${excludeDate.getFullYear()}`, + )!; + expect(preSelectedDateElement.getAttribute("tabindex")).toBe("-1"); + }); + }); }); diff --git a/src/year.tsx b/src/year.tsx index a382691c2..bb8c36a96 100644 --- a/src/year.tsx +++ b/src/year.tsx @@ -402,8 +402,9 @@ export default class Year extends Component { return "-1"; } const preSelected = getYear(this.props.preSelection); + const isPreSelectedYearDisabled = isYearDisabled(y, this.props); - return y === preSelected ? "0" : "-1"; + return y === preSelected && !isPreSelectedYearDisabled ? "0" : "-1"; }; getYearContainerClassNames = () => { From 6df79947bb050c545f0e5c9e9750342aa38fc3bf Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Sun, 28 Jul 2024 20:18:22 +0530 Subject: [PATCH 2/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20the=20tabIn?= =?UTF-8?q?dex=20finding=20logic=20for=20a=20better=20readability=20as=20p?= =?UTF-8?q?er=20the=20PR=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/Hacker0x01/react-datepicker/pull/5011#discussion_r1693445919 --- src/month.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/month.tsx b/src/month.tsx index 63d7dab59..41498ea85 100644 --- a/src/month.tsx +++ b/src/month.tsx @@ -847,9 +847,8 @@ export default class Month extends Component { this.isMonthDisabledForLabelDate(preSelectedMonth); const tabIndex = - !this.props.disabledKeyboardNavigation && m === preSelectedMonth && - !isPreSelectedMonthDisabled + !(isPreSelectedMonthDisabled || this.props.disabledKeyboardNavigation) ? "0" : "-1"; @@ -867,9 +866,8 @@ export default class Month extends Component { ); const tabIndex = - !this.props.disabledKeyboardNavigation && q === preSelectedQuarter && - !isCurrentQuarterDisabled + !(isCurrentQuarterDisabled || this.props.disabledKeyboardNavigation) ? "0" : "-1";