Repository: Hacker0x01/react-datepicker
Pull request #4693 (2024-04-15 17:14:30)

fix: Add aria-label to read the day of the week instead of the shorthand 
## Description
**Linked issue**: #3185

**Problem**
<!-- The problems this PR aims to solve -->
When using a screenreader it reads the days in the header as "Mo", "Tu"... etc. instead of the more understandable full name  See the linked issue for a more detailed description

**Changes**
<!-- Changes you have made to address the issue -->
This PR adds aria-labels to the header dates so that the screenreader now reads days with full name e.g. "Monday" instead of "Mo"

## Contribution checklist
- [x] I have followed the [contributing guidelines](https://github.com/Hacker0x01/react-datepicker/blob/main/CONTRIBUTING.md).
- [x] I have added sufficient test coverage for my changes.
- [x] I have formatted my code with Prettier and checked for linting issues with ESLint for code readability.



Patch:
diff --git a/src/calendar.jsx b/src/calendar.jsx
index 6c7c217e..dca56b40 100644
--- a/src/calendar.jsx
+++ b/src/calendar.jsx
@@ -444,6 +444,7 @@ export default class Calendar extends React.Component {
         return (
           <div
             key={offset}
+            aria-label={formatDate(day, "EEEE", this.props.locale)}
             className={clsx("react-datepicker__day-name", weekDayClassName)}
           >
             {weekDayName}
diff --git a/src/date_utils.js b/src/date_utils.js
index 4e58002b..5f094009 100644
--- a/src/date_utils.js
+++ b/src/date_utils.js
@@ -599,8 +599,7 @@ export function isTimeInList(time, times) {
   return times.some(
     (listTime) =>
       getHours(listTime) === getHours(time) &&
-      getMinutes(listTime) === getMinutes(time) &&
-      getSeconds(listTime) === getSeconds(time),
+      getMinutes(listTime) === getMinutes(time),
   );
 }
 
@@ -620,20 +619,16 @@ export function isTimeInDisabledRange(time, { minTime, maxTime }) {
   if (!minTime || !maxTime) {
     throw new Error("Both minTime and maxTime props required");
   }
-  let baseTime = newDate();
-  baseTime = setHours(baseTime, getHours(time));
-  baseTime = setMinutes(baseTime, getMinutes(time));
-  baseTime = setSeconds(baseTime, getSeconds(time));
-
-  let min = newDate();
-  min = setHours(min, getHours(minTime));
-  min = setMinutes(min, getMinutes(minTime));
-  min = setSeconds(min, getSeconds(minTime));
-
-  let max = newDate();
-  max = setHours(max, getHours(maxTime));
-  max = setMinutes(max, getMinutes(maxTime));
-  max = setSeconds(max, getSeconds(maxTime));
+  const base = newDate();
+  const baseTime = setHours(setMinutes(base, getMinutes(time)), getHours(time));
+  const min = setHours(
+    setMinutes(base, getMinutes(minTime)),
+    getHours(minTime),
+  );
+  const max = setHours(
+    setMinutes(base, getMinutes(maxTime)),
+    getHours(maxTime),
+  );
 
   let valid;
   try {
diff --git a/src/index.jsx b/src/index.jsx
index 627ae4e8..9f572cf3 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -838,10 +838,7 @@ export default class DatePicker extends React.Component {
         const selectorString =
           this.props.showWeekPicker && this.props.showWeekNumbers
             ? '.react-datepicker__week-number[tabindex="0"]'
-            : this.props.showFullMonthYearPicker ||
-                this.props.showMonthYearPicker
-              ? '.react-datepicker__month-text[tabindex="0"]'
-              : '.react-datepicker__day[tabindex="0"]';
+            : '.react-datepicker__day[tabindex="0"]';
         const selectedItem =
           this.calendar.componentNode &&
           this.calendar.componentNode.querySelector(selectorString);


Test patch:
diff --git a/test/calendar_test.test.js b/test/calendar_test.test.js
index f3fbf465..8b70b1fd 100644
--- a/test/calendar_test.test.js
+++ b/test/calendar_test.test.js
@@ -1882,6 +1882,37 @@ describe("Calendar", () => {
     });
   });
 
+  it("should add the aria-label correctly to day names", () => {
+    const expectedAriaLabels = [
+      "Sunday",
+      "Monday",
+      "Tuesday",
+      "Wednesday",
+      "Thursday",
+      "Friday",
+      "Saturday",
+    ];
+
+    const { container } = render(
+      <Calendar
+        dateFormat={DATE_FORMAT}
+        onSelect={() => {}}
+        onClickOutside={() => {}}
+      />,
+    );
+
+    const header = container.querySelector(".react-datepicker__header");
+    const dayNameElements = header.querySelectorAll(
+      ".react-datepicker__day-name",
+    );
+
+    dayNameElements.forEach((element, index) => {
+      expect(element.getAttribute("aria-label")).toBe(
+        expectedAriaLabels[index],
+      );
+    });
+  });
+
   it("should have a next-button with the provided aria-label for year", () => {
     const ariaLabel = "A label in my native language for next year";
     const { container } = render(
diff --git a/test/date_utils_test.test.js b/test/date_utils_test.test.js
index 9659654b..e77adceb 100644
--- a/test/date_utils_test.test.js
+++ b/test/date_utils_test.test.js
@@ -49,7 +49,7 @@ import { setHours } from "date-fns/setHours";
 import { addQuarters } from "date-fns/addQuarters";
 import { ptBR } from "date-fns/locale/pt-BR";
 import { registerLocale } from "../src/date_utils";
-import { addYears, setSeconds } from "date-fns";
+import { addYears } from "date-fns";
 
 registerLocale("pt-BR", ptBR);
 
@@ -888,12 +888,9 @@ describe("date_utils", () => {
   describe("isTimeInDisabledRange", () => {
     it("should tell if time is in disabled range", () => {
       const date = newDate("2016-03-15");
-      let time = setHours(date, 1);
-      time = setMinutes(time, 30);
-      time = setSeconds(time, 30); // 2016-03-15 01:30:30
-
-      const minTime = setHours(setMinutes(date, 30), 0); //2016-03-15 00:30:00
-      const maxTime = setHours(setMinutes(setSeconds(date, 35), 30), 1); //2016-03-15 01:30:35
+      const time = setHours(setMinutes(date, 30), 1);
+      const minTime = setHours(setMinutes(date, 30), 0);
+      const maxTime = setHours(setMinutes(date, 30), 5);
       expect(isTimeInDisabledRange(time, { minTime, maxTime })).toBe(false);
     });
 
diff --git a/test/datepicker_test.test.js b/test/datepicker_test.test.js
index 5bf1d8af..efca1537 100644
--- a/test/datepicker_test.test.js
+++ b/test/datepicker_test.test.js
@@ -1285,25 +1285,6 @@ describe("DatePicker", () => {
     fireEvent.keyDown(data.dateInput, getKey("ArrowDown"));
     expect(data.instance.state.open).toBe(true);
   });
-  it("should focus first month when the down arrow key is pressed", async () => {
-    const div = document.createElement("div");
-    document.body.appendChild(div);
-    render(<DatePicker showMonthYearPicker />, {
-      container: div,
-    });
-
-    // user focuses the input field, the calendar opens
-    const dateInput = div.querySelector("input");
-    fireEvent.focus(dateInput);
-    fireEvent.keyDown(dateInput, getKey("ArrowDown"));
-
-    await waitFor(() => {
-      const selectedMonth = div.querySelector(
-        '.react-datepicker__month-text[tabindex="0"]',
-      );
-      expect(document.activeElement).toBe(selectedMonth);
-    });
-  });
   it("should not open the calendar when the left arrow key is pressed", () => {
     const data = getOnInputKeyDownStuff();
     act(() => {
diff --git a/test/include_times_test.test.js b/test/include_times_test.test.js
index 533e1811..758c0236 100644
--- a/test/include_times_test.test.js
+++ b/test/include_times_test.test.js
@@ -54,29 +54,4 @@ describe("TimeComponent", () => {
     });
     expect(enabledTimeItemsHasNoAriaDisabled).toBe(true);
   });
-
-  it("should factor in seconds", () => {
-    const includeHoursMinutesSeconds = [
-      utils.addHours(utils.addSeconds(today, 30), 1), //01:00:30
-      utils.addSeconds(today, 30), //00:00:30
-    ];
-    const { container: timeComponent } = render(
-      <TimeComponent
-        format="HH:mm:ss"
-        includeTimes={includeHoursMinutesSeconds}
-      />,
-    );
-
-    const disabledTimeItems = timeComponent.querySelectorAll(
-      ".react-datepicker__time-list-item--disabled",
-    );
-
-    // 01:00:00 and 00:00:00 should be correctly disabled because they are not included
-    expect(
-      Array.from(disabledTimeItems).map((node) => node.textContent),
-    ).toContain("01:00:00");
-    expect(
-      Array.from(disabledTimeItems).map((node) => node.textContent),
-    ).toContain("00:00:00");
-  });
 });