Repository: Hacker0x01/react-datepicker
Pull request #4288 (2023-10-04 13:02:20)

Week picker (2nd attempt)
📯   Hey everyone, big news 

@philipkocanda and @iMartzen have helped get this [Merge Request](https://github.com/Hacker0x01/react-datepicker/pull/2768) from 2021 over the finish line, so most of the credit goes to [@TiesWestendorp](https://github.com/TiesWestendorp) 👏  

![109517347-70796e00-7aa9-11eb-8f8c-08ddaf6688da](https://github.com/Hacker0x01/react-datepicker/assets/23737561/62df591b-dde6-4acc-b5a3-fccae7f65eb6)

⚙️ **Implementation**
```
() => {
  const [startDate, setStartDate] = useState(new Date("2021/02/22"));
  return (
    <DatePicker
      selected={startDate}
      onChange={(date) => setStartDate(date)}
      dateFormat="I/R"
      locale="en-GB"
      showWeekNumbers
      showWeekPicker
    />
  );
};
```

📖  **Resources:**
- [Original Issue](https://github.com/Hacker0x01/react-datepicker/issues/2033)
- [Original PR](https://github.com/Hacker0x01/react-datepicker/pull/2768)

🤝  We used our HackerOne Community Service Day to finalize this feature, and we're thrilled to share it today.

🚀  Enjoy the new week picker, and let's make a positive impact together  

martijnrusschen: @philipkocanda @iMartzen great initiative and excited to see this coming back. Once you mark the PR as ready for review it will be sent to the pullrequest network as well. 
martijnrusschen: Please check the CI output. The linter is failing because of unused imports. 
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01) Report
> Merging [#4288](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01) (e687e52) into [main](https://app.codecov.io/gh/Hacker0x01/react-datepicker/commit/27788f5ba8e50707fcba87e30008f8645df1d965?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01) (27788f5) will **decrease** coverage by `2.37%`.
> The diff coverage is `49.59%`.

> :exclamation: Current head e687e52 differs from pull request most recent head 789b41d. Consider uploading reports for the commit 789b41d to get more accurate results

```diff
@@            Coverage Diff             @@
##             main    #4288      +/-   ##
==========================================
- Coverage   96.52%   94.15%   -2.37%     
==========================================
  Files          25       26       +1     
  Lines        2358     2464     +106     
  Branches      962     1013      +51     
==========================================
+ Hits         2276     2320      +44     
- Misses         82      144      +62     
```


| [Files](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01) | Coverage Δ | |
|---|---|---|
| [src/calendar.jsx](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-c3JjL2NhbGVuZGFyLmpzeA==) | `98.32% <ø> (ø)` | |
| [src/month.jsx](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-c3JjL21vbnRoLmpzeA==) | `96.37% <ø> (ø)` | |
| [src/week.jsx](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-c3JjL3dlZWsuanN4) | `95.34% <88.23%> (-4.66%)` | :arrow_down: |
| [src/day.jsx](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-c3JjL2RheS5qc3g=) | `95.18% <83.33%> (-1.37%)` | :arrow_down: |
| [src/index.jsx](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-c3JjL2luZGV4LmpzeA==) | `93.18% <68.42%> (-1.07%)` | :arrow_down: |
| [test/week\_picker\_test.js](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-dGVzdC93ZWVrX3BpY2tlcl90ZXN0Lmpz) | `0.00% <0.00%> (ø)` | |
| [src/week\_number.jsx](https://app.codecov.io/gh/Hacker0x01/react-datepicker/pull/4288?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Hacker0x01#diff-c3JjL3dlZWtfbnVtYmVyLmpzeA==) | `49.09% <39.13%> (-50.91%)` | :arrow_down: |


martijnrusschen: Overall the code coverage seems a bit low, and reduces the project coverage from 96% to 94%, it would be great to make sure we keep this stable and increase the coverage on this PR to the project levels.
Zarthus: BTW: This MR would benefit from having `Co Author` data in the commit message.

https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors

I would also include @TiesWestendorp if some of their work is included in this MR.

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cad6598e..6d0a9c2b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,11 +28,16 @@ Start reading our code, and you'll get the hang of it. We optimize for readabili
 Local development configuration is pretty snappy. Here's how to get set up:
 
 1. Install/use node >=16.0.0
+1. Install/use yarn <=1.x.x
 1. Run `yarn link` from project root
 1. Run `cd docs-site && yarn link react-datepicker`
 1. Run `yarn install` from project root
 1. Run `yarn build` from project root (at least the first time, this will get you the `dist` directory that holds the code that will be linked to)
-1. Run `yarn start` from project root
+1. Run `yarn start` from project root. (This command launches a documentation app and runs it as a simple webserver at http://localhost:3000.)
 1. Open new terminal window
+1. Run `yarn build-dev` from project root. (This command sets up a development environment that keeps an eye on any file changes. When a file is updated, it auto-builds using the latest code.)
+
+You can run `yarn test` to execute the test suite and linters. To help you develop the component we’ve set up some tests that cover the basic functionality (can be found in `/tests`). Even though we’re big fans of testing, this only covers a small piece of the component. We highly recommend you add tests when you’re adding new functionality.
+
 1. After each JS change run `yarn build:js` in project root
 1. After each SCSS change run `yarn run css:dev && yarn run css:modules:dev` in project root
diff --git a/docs-site/src/components/Examples/index.js b/docs-site/src/components/Examples/index.js
index e4f98d67..0f3606fd 100644
--- a/docs-site/src/components/Examples/index.js
+++ b/docs-site/src/components/Examples/index.js
@@ -79,6 +79,7 @@ import RenderCustomYear from "../../examples/renderCustomYear";
 import TimeInput from "../../examples/timeInput";
 import StrictParsing from "../../examples/strictParsing";
 import MonthPicker from "../../examples/monthPicker";
+import WeekPicker from "../../examples/weekPicker";
 import monthPickerFullName from "../../examples/monthPickerFullName";
 import monthPickerTwoColumns from "../../examples/monthPickerTwoColumns";
 import monthPickerFourColumns from "../../examples/monthPickerFourColumns";
@@ -512,6 +513,10 @@ export default class exampleComponents extends React.Component {
       title: "Calendar Start day",
       component: CalendarStartDay,
     },
+    {
+      title: "Week Picker",
+      component: WeekPicker,
+    },
     {
       title: "External Form",
       component: ExternalForm,
diff --git a/docs-site/src/examples/weekPicker.js b/docs-site/src/examples/weekPicker.js
new file mode 100644
index 00000000..d4fc21c6
--- /dev/null
+++ b/docs-site/src/examples/weekPicker.js
@@ -0,0 +1,13 @@
+() => {
+  const [startDate, setStartDate] = useState(new Date("2021/02/22"));
+  return (
+    <DatePicker
+      selected={startDate}
+      onChange={(date) => setStartDate(date)}
+      dateFormat="I/R"
+      locale="en-GB"
+      showWeekNumbers
+      showWeekPicker
+    />
+  );
+};
diff --git a/docs/index.md b/docs/index.md
index 21e8fba4..ee320e01 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -133,6 +133,7 @@
 | `showTimeSelectOnly`            | `bool`                         |                            |             |
 | `showTwoColumnMonthYearPicker`  | `bool`                         | `false`                    |             |
 | `showWeekNumbers`               | `bool`                         |                            |             |
+| `showWeekPicker`                | `bool`                         | `false`                    |             |
 | `showYearDropdown`              | `bool`                         |                            |             |
 | `showYearPicker`                | `bool`                         | `false`                    |             |
 | `startDate`                     | `instanceOfDate`               |                            |             |
diff --git a/src/calendar.jsx b/src/calendar.jsx
index 7a6cea6e..9b666c62 100644
--- a/src/calendar.jsx
+++ b/src/calendar.jsx
@@ -140,6 +140,7 @@ export default class Calendar extends React.Component {
     showFourColumnMonthYearPicker: PropTypes.bool,
     showYearPicker: PropTypes.bool,
     showQuarterYearPicker: PropTypes.bool,
+    showWeekPicker: PropTypes.bool,
     showTimeSelectOnly: PropTypes.bool,
     timeFormat: PropTypes.string,
     timeIntervals: PropTypes.number,
@@ -941,6 +942,7 @@ export default class Calendar extends React.Component {
             }
             showYearPicker={this.props.showYearPicker}
             showQuarterYearPicker={this.props.showQuarterYearPicker}
+            showWeekPicker={this.props.showWeekPicker}
             isInputFocused={this.props.isInputFocused}
             containerRef={this.containerRef}
             monthShowsDuplicateDaysEnd={monthShowsDuplicateDaysEnd}
diff --git a/src/day.jsx b/src/day.jsx
index 7efb85fa..a89e2a68 100644
--- a/src/day.jsx
+++ b/src/day.jsx
@@ -14,6 +14,7 @@ import {
   isBefore,
   isAfter,
   getDayOfWeekCode,
+  getStartOfWeek,
   formatDate,
 } from "./date_utils";
 
@@ -38,6 +39,8 @@ export default class Day extends React.Component {
     selectsEnd: PropTypes.bool,
     selectsStart: PropTypes.bool,
     selectsRange: PropTypes.bool,
+    showWeekPicker: PropTypes.bool,
+    showWeekNumber: PropTypes.bool,
     selectsDisabledDaysInRange: PropTypes.bool,
     startDate: PropTypes.instanceOf(Date),
     renderDayContents: PropTypes.func,
@@ -52,6 +55,7 @@ export default class Day extends React.Component {
       PropTypes.string,
       PropTypes.shape({ locale: PropTypes.object }),
     ]),
+    calendarStartDay: PropTypes.number,
   };
 
   componentDidMount() {
@@ -90,13 +94,38 @@ export default class Day extends React.Component {
 
   isKeyboardSelected = () =>
     !this.props.disabledKeyboardNavigation &&
-    !this.isSameDay(this.props.selected) &&
-    this.isSameDay(this.props.preSelection);
+    !(
+      this.isSameDay(this.props.selected) ||
+      this.isSameWeek(this.props.selected)
+    ) &&
+    (this.isSameDay(this.props.preSelection) ||
+      this.isSameWeek(this.props.preSelection));
 
   isDisabled = () => isDayDisabled(this.props.day, this.props);
 
   isExcluded = () => isDayExcluded(this.props.day, this.props);
 
+  isStartOfWeek = () =>
+    isSameDay(
+      this.props.day,
+      getStartOfWeek(
+        this.props.day,
+        this.props.locale,
+        this.props.calendarStartDay,
+      ),
+    );
+
+  isSameWeek = (other) =>
+    this.props.showWeekPicker &&
+    isSameDay(
+      other,
+      getStartOfWeek(
+        this.props.day,
+        this.props.locale,
+        this.props.calendarStartDay,
+      ),
+    );
+
   getHighLightedClass = () => {
     const { day, highlightDates } = this.props;
 
@@ -246,7 +275,8 @@ export default class Day extends React.Component {
 
   isCurrentDay = () => this.isSameDay(newDate());
 
-  isSelected = () => this.isSameDay(this.props.selected);
+  isSelected = () =>
+    this.isSameDay(this.props.selected) || this.isSameWeek(this.props.selected);
 
   getClassNames = (date) => {
     const dayClassName = this.props.dayClassName
@@ -309,10 +339,14 @@ export default class Day extends React.Component {
   getTabIndex = (selected, preSelection) => {
     const selectedDay = selected || this.props.selected;
     const preSelectionDay = preSelection || this.props.preSelection;
-
     const tabIndex =
-      this.isKeyboardSelected() ||
-      (this.isSameDay(selectedDay) && isSameDay(preSelectionDay, selectedDay))
+      !(
+        this.props.showWeekPicker &&
+        (this.props.showWeekNumber || !this.isStartOfWeek())
+      ) &&
+      (this.isKeyboardSelected() ||
+        (this.isSameDay(selectedDay) &&
+          isSameDay(preSelectionDay, selectedDay)))
         ? 0
         : -1;
 
diff --git a/src/index.jsx b/src/index.jsx
index 89885ec7..d8ad254f 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -37,6 +37,7 @@ import {
   getHightLightDaysMap,
   getYear,
   getMonth,
+  getStartOfWeek,
   registerLocale,
   setDefaultLocale,
   getDefaultLocale,
@@ -108,6 +109,7 @@ export default class DatePicker extends React.Component {
       showFourColumnMonthYearPicker: false,
       showYearPicker: false,
       showQuarterYearPicker: false,
+      showWeekPicker: false,
       strictParsing: false,
       timeIntervals: 30,
       timeCaption: "Time",
@@ -256,6 +258,7 @@ export default class DatePicker extends React.Component {
     showFourColumnMonthYearPicker: PropTypes.bool,
     showYearPicker: PropTypes.bool,
     showQuarterYearPicker: PropTypes.bool,
+    showWeekPicker: PropTypes.bool,
     showDateSelect: PropTypes.bool,
     showTimeSelect: PropTypes.bool,
     showTimeSelectOnly: PropTypes.bool,
@@ -552,6 +555,13 @@ export default class DatePicker extends React.Component {
       });
     }
     if (date || !event.target.value) {
+      if (this.props.showWeekPicker) {
+        date = getStartOfWeek(
+          date,
+          this.props.locale,
+          this.props.calendarStartDay,
+        );
+      }
       this.setSelected(date, event, true);
     }
   };
@@ -565,6 +575,13 @@ export default class DatePicker extends React.Component {
     if (this.props.onChangeRaw) {
       this.props.onChangeRaw(event);
     }
+    if (this.props.showWeekPicker) {
+      date = getStartOfWeek(
+        date,
+        this.props.locale,
+        this.props.calendarStartDay,
+      );
+    }
     this.setSelected(date, event, false, monthSelectedIn);
     if (this.props.showDateSelect) {
       this.setState({ isRenderAriaLiveMessage: true });
@@ -665,6 +682,13 @@ export default class DatePicker extends React.Component {
     const hasMaxDate = typeof this.props.maxDate !== "undefined";
     let isValidDateSelection = true;
     if (date) {
+      if (this.props.showWeekPicker) {
+        date = getStartOfWeek(
+          date,
+          this.props.locale,
+          this.props.calendarStartDay,
+        );
+      }
       const dateStartOfDay = startOfDay(date);
       if (hasMinDate && hasMaxDate) {
         // isDayinRange uses startOfDay internally, so not necessary to manipulate times here
@@ -748,16 +772,18 @@ export default class DatePicker extends React.Component {
       return;
     }
 
-    // if calendar is open, these keys will focus the selected day
+    // if calendar is open, these keys will focus the selected item
     if (this.state.open) {
       if (eventKey === "ArrowDown" || eventKey === "ArrowUp") {
         event.preventDefault();
-        const selectedDay =
+        const selectorString =
+          this.props.showWeekPicker && this.props.showWeekNumbers
+            ? '.react-datepicker__week-number[tabindex="0"]'
+            : '.react-datepicker__day[tabindex="0"]';
+        const selectedItem =
           this.calendar.componentNode &&
-          this.calendar.componentNode.querySelector(
-            '.react-datepicker__day[tabindex="0"]',
-          );
-        selectedDay && selectedDay.focus({ preventScroll: true });
+          this.calendar.componentNode.querySelector(selectorString);
+        selectedItem && selectedItem.focus({ preventScroll: true });
 
         return;
       }
@@ -828,10 +854,18 @@ export default class DatePicker extends React.Component {
       let newSelection;
       switch (eventKey) {
         case "ArrowLeft":
-          newSelection = subDays(copy, 1);
+          if (this.props.showWeekPicker) {
+            newSelection = subWeeks(copy, 1);
+          } else {
+            newSelection = subDays(copy, 1);
+          }
           break;
         case "ArrowRight":
-          newSelection = addDays(copy, 1);
+          if (this.props.showWeekPicker) {
+            newSelection = addWeeks(copy, 1);
+          } else {
+            newSelection = addDays(copy, 1);
+          }
           break;
         case "ArrowUp":
           newSelection = subWeeks(copy, 1);
@@ -851,6 +885,9 @@ export default class DatePicker extends React.Component {
         case "End":
           newSelection = addYears(copy, 1);
           break;
+        default:
+          newSelection = null;
+          break;
       }
       if (!newSelection) {
         if (this.props.onInputError) {
@@ -1047,6 +1084,7 @@ export default class DatePicker extends React.Component {
         showFourColumnMonthYearPicker={this.props.showFourColumnMonthYearPicker}
         showYearPicker={this.props.showYearPicker}
         showQuarterYearPicker={this.props.showQuarterYearPicker}
+        showWeekPicker={this.props.showWeekPicker}
         showPopperArrow={this.props.showPopperArrow}
         excludeScrollbar={this.props.excludeScrollbar}
         handleOnKeyDown={this.props.onKeyDown}
diff --git a/src/month.jsx b/src/month.jsx
index 488825da..c374b474 100644
--- a/src/month.jsx
+++ b/src/month.jsx
@@ -111,6 +111,7 @@ export default class Month extends React.Component {
     showTwoColumnMonthYearPicker: PropTypes.bool,
     showFourColumnMonthYearPicker: PropTypes.bool,
     showQuarterYearPicker: PropTypes.bool,
+    showWeekPicker: PropTypes.bool,
     handleOnKeyDown: PropTypes.func,
     isInputFocused: PropTypes.bool,
     weekAriaLabelPrefix: PropTypes.string,
@@ -330,6 +331,7 @@ export default class Month extends React.Component {
           selectsRange={this.props.selectsRange}
           selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
           showWeekNumber={this.props.showWeekNumbers}
+          showWeekPicker={this.props.showWeekPicker}
           startDate={this.props.startDate}
           endDate={this.props.endDate}
           dayClassName={this.props.dayClassName}
@@ -718,6 +720,7 @@ export default class Month extends React.Component {
       selectsEnd,
       showMonthYearPicker,
       showQuarterYearPicker,
+      showWeekPicker,
     } = this.props;
 
     return classnames(
@@ -728,6 +731,7 @@ export default class Month extends React.Component {
       },
       { "react-datepicker__monthPicker": showMonthYearPicker },
       { "react-datepicker__quarterPicker": showQuarterYearPicker },
+      { "react-datepicker__weekPicker": showWeekPicker },
     );
   };
 
diff --git a/src/stylesheets/datepicker.scss b/src/stylesheets/datepicker.scss
index 137cf970..e02152cc 100644
--- a/src/stylesheets/datepicker.scss
+++ b/src/stylesheets/datepicker.scss
@@ -368,22 +368,45 @@
   &.react-datepicker__week-number--clickable {
     cursor: pointer;
 
-    &:hover {
+    &:not(
+        .react-datepicker__week-number--selected,
+        .react-datepicker__week-number--keyboard-selected
+      ):hover {
       border-radius: $datepicker__border-radius;
       background-color: $datepicker__background-color;
     }
   }
-}
 
-.react-datepicker__day-names,
-.react-datepicker__week {
-  white-space: nowrap;
+  &--selected {
+    border-radius: $datepicker__border-radius;
+    background-color: $datepicker__selected-color;
+    color: #fff;
+
+    &:hover {
+      background-color: darken($datepicker__selected-color, 5%);
+    }
+  }
+
+  &--keyboard-selected {
+    border-radius: $datepicker__border-radius;
+    background-color: lighten($datepicker__selected-color, 10%);
+    color: #fff;
+
+    &:hover {
+      background-color: darken($datepicker__selected-color, 5%);
+    }
+  }
 }
 
 .react-datepicker__day-names {
+  white-space: nowrap;
   margin-bottom: -8px;
 }
 
+.react-datepicker__week {
+  white-space: nowrap;
+}
+
 .react-datepicker__day-name,
 .react-datepicker__day,
 .react-datepicker__time-name {
diff --git a/src/week.jsx b/src/week.jsx
index 08987c77..22e7e8e9 100644
--- a/src/week.jsx
+++ b/src/week.jsx
@@ -2,7 +2,9 @@ import React from "react";
 import PropTypes from "prop-types";
 import Day from "./day";
 import WeekNumber from "./week_number";
-import * as utils from "./date_utils";
+import classnames from "classnames";
+
+import { addDays, getWeek, getStartOfWeek, isSameDay } from "./date_utils";
 
 export default class Week extends React.Component {
   static get defaultProps() {
@@ -52,6 +54,7 @@ export default class Week extends React.Component {
     selectsRange: PropTypes.bool,
     selectsDisabledDaysInRange: PropTypes.bool,
     showWeekNumber: PropTypes.bool,
+    showWeekPicker: PropTypes.bool,
     startDate: PropTypes.instanceOf(Date),
     setOpen: PropTypes.func,
     shouldCloseOnSelect: PropTypes.bool,
@@ -82,6 +85,14 @@ export default class Week extends React.Component {
     if (typeof this.props.onWeekSelect === "function") {
       this.props.onWeekSelect(day, weekNumber, event);
     }
+    if (this.props.showWeekPicker) {
+      const startOfWeek = getStartOfWeek(
+        day,
+        this.props.locale,
+        this.props.calendarStartDay,
+      );
+      this.handleDayClick(startOfWeek, event);
+    }
     if (this.props.shouldCloseOnSelect) {
       this.props.setOpen(false);
     }
@@ -91,11 +102,11 @@ export default class Week extends React.Component {
     if (this.props.formatWeekNumber) {
       return this.props.formatWeekNumber(date);
     }
-    return utils.getWeek(date);
+    return getWeek(date);
   };
 
   renderDays = () => {
-    const startOfWeek = utils.getStartOfWeek(
+    const startOfWeek = getStartOfWeek(
       this.props.day,
       this.props.locale,
       this.props.calendarStartDay,
@@ -103,21 +114,31 @@ export default class Week extends React.Component {
     const days = [];
     const weekNumber = this.formatWeekNumber(startOfWeek);
     if (this.props.showWeekNumber) {
-      const onClickAction = this.props.onWeekSelect
-        ? this.handleWeekClick.bind(this, startOfWeek, weekNumber)
-        : undefined;
+      const onClickAction =
+        this.props.onWeekSelect || this.props.showWeekPicker
+          ? this.handleWeekClick.bind(this, startOfWeek, weekNumber)
+          : undefined;
       days.push(
         <WeekNumber
           key="W"
           weekNumber={weekNumber}
+          date={startOfWeek}
           onClick={onClickAction}
+          selected={this.props.selected}
+          preSelection={this.props.preSelection}
           ariaLabelPrefix={this.props.ariaLabelPrefix}
+          showWeekPicker={this.props.showWeekPicker}
+          showWeekNumber={this.props.showWeekNumber}
+          disabledKeyboardNavigation={this.props.disabledKeyboardNavigation}
+          handleOnKeyDown={this.props.handleOnKeyDown}
+          isInputFocused={this.props.isInputFocused}
+          containerRef={this.props.containerRef}
         />,
       );
     }
     return days.concat(
       [0, 1, 2, 3, 4, 5, 6].map((offset) => {
-        const day = utils.addDays(startOfWeek, offset);
+        const day = addDays(startOfWeek, offset);
         return (
           <Day
             ariaLabelPrefixWhenEnabled={this.props.chooseDayAriaLabelPrefix}
@@ -142,6 +163,8 @@ export default class Week extends React.Component {
             selectsStart={this.props.selectsStart}
             selectsEnd={this.props.selectsEnd}
             selectsRange={this.props.selectsRange}
+            showWeekPicker={this.props.showWeekPicker}
+            showWeekNumber={this.props.showWeekNumber}
             selectsDisabledDaysInRange={this.props.selectsDisabledDaysInRange}
             startDate={this.props.startDate}
             endDate={this.props.endDate}
@@ -164,7 +187,29 @@ export default class Week extends React.Component {
     );
   };
 
+  startOfWeek = () =>
+    getStartOfWeek(
+      this.props.day,
+      this.props.locale,
+      this.props.calendarStartDay,
+    );
+
+  isKeyboardSelected = () =>
+    !this.props.disabledKeyboardNavigation &&
+    !isSameDay(this.startOfWeek(), this.props.selected) &&
+    isSameDay(this.startOfWeek(), this.props.preSelection);
+
   render() {
-    return <div className="react-datepicker__week">{this.renderDays()}</div>;
+    const weekNumberClasses = {
+      "react-datepicker__week": true,
+      "react-datepicker__week--selected": isSameDay(
+        this.startOfWeek(),
+        this.props.selected,
+      ),
+      "react-datepicker__week--keyboard-selected": this.isKeyboardSelected(),
+    };
+    return (
+      <div className={classnames(weekNumberClasses)}>{this.renderDays()}</div>
+    );
   }
 }
diff --git a/src/week_number.jsx b/src/week_number.jsx
index f69489ee..b5a05789 100644
--- a/src/week_number.jsx
+++ b/src/week_number.jsx
@@ -1,6 +1,7 @@
 import React from "react";
 import PropTypes from "prop-types";
 import classnames from "classnames";
+import { isSameDay } from "./date_utils";
 
 export default class WeekNumber extends React.Component {
   static get defaultProps() {
@@ -11,27 +12,125 @@ export default class WeekNumber extends React.Component {
 
   static propTypes = {
     weekNumber: PropTypes.number.isRequired,
+    date: PropTypes.instanceOf(Date).isRequired,
     onClick: PropTypes.func,
     ariaLabelPrefix: PropTypes.string,
+    selected: PropTypes.instanceOf(Date),
+    preSelection: PropTypes.instanceOf(Date),
+    showWeekPicker: PropTypes.bool,
+    showWeekNumber: PropTypes.bool,
+    disabledKeyboardNavigation: PropTypes.bool,
+    inline: PropTypes.bool,
+    shouldFocusDayInline: PropTypes.bool,
+    handleOnKeyDown: PropTypes.func,
+    containerRef: PropTypes.oneOfType([
+      PropTypes.func,
+      PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
+    ]),
   };
 
+  componentDidMount() {
+    this.handleFocusWeekNumber();
+  }
+
+  componentDidUpdate(prevProps) {
+    this.handleFocusWeekNumber(prevProps);
+  }
+
+  weekNumberEl = React.createRef();
+
   handleClick = (event) => {
     if (this.props.onClick) {
       this.props.onClick(event);
     }
   };
 
+  handleOnKeyDown = (event) => {
+    const eventKey = event.key;
+    if (eventKey === " ") {
+      event.preventDefault();
+      event.key = "Enter";
+    }
+
+    this.props.handleOnKeyDown(event);
+  };
+
+  isKeyboardSelected = () =>
+    !this.props.disabledKeyboardNavigation &&
+    !isSameDay(this.props.date, this.props.selected) &&
+    isSameDay(this.props.date, this.props.preSelection);
+
+  getTabIndex = () =>
+    this.props.showWeekPicker &&
+    this.props.showWeekNumber &&
+    (this.isKeyboardSelected() ||
+      (isSameDay(this.props.date, this.props.selected) &&
+        isSameDay(this.props.preSelection, this.props.selected)))
+      ? 0
+      : -1;
+
+  // various cases when we need to apply focus to the preselected week-number
+  // focus the week-number on mount/update so that keyboard navigation works while cycling through months with up or down keys (not for prev and next month buttons)
+  // prevent focus for these activeElement cases so we don't pull focus from the input as the calendar opens
+  handleFocusWeekNumber = (prevProps = {}) => {
+    let shouldFocusWeekNumber = false;
+    // only do this while the input isn't focused
+    // otherwise, typing/backspacing the date manually may steal focus away from the input
+    if (
+      this.getTabIndex() === 0 &&
+      !prevProps.isInputFocused &&
+      isSameDay(this.props.date, this.props.preSelection)
+    ) {
+      // there is currently no activeElement and not inline
+      if (!document.activeElement || document.activeElement === document.body) {
+        shouldFocusWeekNumber = true;
+      }
+      // inline version:
+      // do not focus on initial render to prevent autoFocus issue
+      // focus after month has changed via keyboard
+      if (this.props.inline && !this.props.shouldFocusDayInline) {
+        shouldFocusWeekNumber = false;
+      }
+      // the activeElement is in the container, and it is another instance of WeekNumber
+      if (
+        this.props.containerRef &&
+        this.props.containerRef.current &&
+        this.props.containerRef.current.contains(document.activeElement) &&
+        document.activeElement &&
+        document.activeElement.classList.contains(
+          "react-datepicker__week-number",
+        )
+      ) {
+        shouldFocusWeekNumber = true;
+      }
+    }
+
+    shouldFocusWeekNumber &&
+      this.weekNumberEl.current &&
+      this.weekNumberEl.current.focus({ preventScroll: true });
+  };
+
   render() {
     const { weekNumber, ariaLabelPrefix = "week ", onClick } = this.props;
+
     const weekNumberClasses = {
       "react-datepicker__week-number": true,
       "react-datepicker__week-number--clickable": !!onClick,
+      "react-datepicker__week-number--selected": isSameDay(
+        this.props.date,
+        this.props.selected,
+      ),
+      "react-datepicker__week-number--keyboard-selected":
+        this.isKeyboardSelected(),
     };
     return (
       <div
+        ref={this.weekNumberEl}
         className={classnames(weekNumberClasses)}
         aria-label={`${ariaLabelPrefix} ${this.props.weekNumber}`}
         onClick={this.handleClick}
+        onKeyDown={this.handleOnKeyDown}
+        tabIndex={this.getTabIndex()}
       >
         {weekNumber}
       </div>


Test patch:
diff --git a/test/calendar_test.test.js b/test/calendar_test.test.js
index ee696077..183bde46 100644
--- a/test/calendar_test.test.js
+++ b/test/calendar_test.test.js
@@ -6,7 +6,7 @@ import React from "react";
 import Calendar from "../src/calendar";
 import Month from "../src/month";
 import Day from "../src/day";
-import { findDOMNode } from "react-dom";
+import ReactDOM from "react-dom";
 import TestUtils from "react-dom/test-utils";
 import YearDropdown from "../src/year_dropdown";
 import MonthDropdown from "../src/month_dropdown";
@@ -933,7 +933,7 @@ describe("Calendar", () => {
         }}
       />,
     );
-    TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+    TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
     const calendar = TestUtils.scryRenderedComponentsWithType(
       datePicker.calendar,
       Calendar,
@@ -958,7 +958,7 @@ describe("Calendar", () => {
         }}
       />,
     );
-    TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+    TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
     const calendar = TestUtils.scryRenderedComponentsWithType(
       datePicker.calendar,
       Calendar,
@@ -983,7 +983,7 @@ describe("Calendar", () => {
         }}
       />,
     );
-    TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+    TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
     const calendar = TestUtils.scryRenderedComponentsWithType(
       datePicker.calendar,
       Calendar,
@@ -1007,11 +1007,11 @@ describe("Calendar", () => {
       />,
     );
 
-    TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+    TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
 
     expect(onCalendarOpen).toHaveBeenCalled();
 
-    TestUtils.Simulate.blur(findDOMNode(datePicker.input));
+    TestUtils.Simulate.blur(ReactDOM.findDOMNode(datePicker.input));
 
     expect(onCalendarOpen).toHaveBeenCalled();
   });
@@ -1186,12 +1186,16 @@ describe("Calendar", () => {
   });
 
   describe("localization", () => {
-    function testLocale(calendar, selected, locale) {
+    function testLocale(calendar, selected, locale, calendarStartDay) {
       const calendarText = calendar.find(".react-datepicker__current-month");
       expect(calendarText.text()).toBe(
         utils.formatDate(selected, dateFormat, locale),
       );
-      const firstDateOfWeek = utils.getStartOfWeek(selected, locale);
+      const firstDateOfWeek = utils.getStartOfWeek(
+        selected,
+        locale,
+        calendarStartDay,
+      );
       const firstWeekDayMin = utils.getWeekdayMinInLocale(
         firstDateOfWeek,
         locale,
@@ -1585,7 +1589,7 @@ describe("Calendar", () => {
         <DatePicker selected={selected} />,
       );
       const dateInput = datePicker.input;
-      TestUtils.Simulate.focus(findDOMNode(dateInput));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(dateInput));
       TestUtils.Simulate.click(
         TestUtils.findRenderedDOMComponentWithClass(
           datePicker,
@@ -1605,7 +1609,7 @@ describe("Calendar", () => {
         <DatePicker selected={selected} />,
       );
       const dateInput = datePicker.input;
-      TestUtils.Simulate.focus(findDOMNode(dateInput));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(dateInput));
       TestUtils.Simulate.click(
         TestUtils.findRenderedDOMComponentWithClass(
           datePicker,
@@ -1662,7 +1666,7 @@ describe("Calendar", () => {
           onKeyDown={onKeyDownSpy}
         />,
       );
-      TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
       const prevMonthButton = TestUtils.findRenderedDOMComponentWithClass(
         datePicker,
         "react-datepicker__navigation--previous",
@@ -1683,7 +1687,7 @@ describe("Calendar", () => {
           onKeyDown={onKeyDownSpy}
         />,
       );
-      TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
       const nextMonthButton = TestUtils.findRenderedDOMComponentWithClass(
         datePicker,
         "react-datepicker__navigation--next",
@@ -1733,7 +1737,7 @@ describe("Calendar", () => {
       );
       const dateInput = datePicker.input;
 
-      TestUtils.Simulate.focus(findDOMNode(dateInput));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(dateInput));
 
       const calendar = TestUtils.scryRenderedComponentsWithType(
         datePicker.calendar,
@@ -1764,7 +1768,7 @@ describe("Calendar", () => {
       );
       const dateInput = datePicker.input;
 
-      TestUtils.Simulate.focus(findDOMNode(dateInput));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(dateInput));
 
       const calendar = TestUtils.scryRenderedComponentsWithType(
         datePicker.calendar,
diff --git a/test/week_number_test.test.js b/test/week_number_test.test.js
index 1f2a0037..1cc94334 100644
--- a/test/week_number_test.test.js
+++ b/test/week_number_test.test.js
@@ -1,14 +1,16 @@
 import React from "react";
 import WeekNumber from "../src/week_number";
 import { shallow } from "enzyme";
+import * as utils from "../src/date_utils";
 
 function renderWeekNumber(weekNumber, props = {}) {
   return shallow(<WeekNumber weekNumber={weekNumber} {...props} />);
 }
 
 describe("WeekNumber", () => {
-  let shallowWeekNumber;
-  describe("rendering", () => {
+  let shallowWeekNumber, instance;
+
+  describe("Rendering", () => {
     it("should render the specified Week Number", () => {
       const weekNumber = 1;
       shallowWeekNumber = renderWeekNumber(weekNumber);
@@ -18,18 +20,18 @@ describe("WeekNumber", () => {
       expect(shallowWeekNumber.text()).toBe(weekNumber + "");
     });
 
-    it("should call the onClick function if it is defined", () => {
-      const onClick = jest.fn();
-      shallowWeekNumber = shallow(
-        <WeekNumber weekNumber={1} onClick={onClick} />,
+    it("should handle onClick function", () => {
+      const onClickMock = jest.fn();
+      const shallowWeekNumber = shallow(
+        <WeekNumber weekNumber={1} onClick={onClickMock} />,
       );
       shallowWeekNumber.instance().handleClick({});
-      expect(onClick).toHaveBeenCalledTimes(1);
+      expect(onClickMock).toHaveBeenCalledTimes(1);
     });
 
     it("should have an aria-label containing the provided prefix", () => {
       const ariaLabelPrefix = "A prefix in my native language";
-      const shallowWeekNumber = shallow(
+      shallowWeekNumber = shallow(
         <WeekNumber day={1} ariaLabelPrefix={ariaLabelPrefix} />,
       );
       expect(
@@ -37,4 +39,274 @@ describe("WeekNumber", () => {
       ).not.toBe(-1);
     });
   });
+
+  describe("Component Lifecycle", () => {
+    const handleFocusWeekNumberMock = jest.fn();
+
+    beforeEach(() => {
+      shallowWeekNumber = shallow(<WeekNumber />);
+      instance = shallowWeekNumber.instance();
+      instance.handleFocusWeekNumber = handleFocusWeekNumberMock;
+    });
+
+    afterEach(() => {
+      handleFocusWeekNumberMock.mockClear();
+    });
+
+    it("should call handleFocusWeeknumber on mount", () => {
+      instance.componentDidMount();
+      expect(handleFocusWeekNumberMock).toHaveBeenCalledTimes(1);
+    });
+
+    it("should call handleFocusWeekNumber with prevProps on update", () => {
+      const prevProps = { someProp: "someValue" };
+      instance.componentDidUpdate(prevProps);
+      expect(handleFocusWeekNumberMock).toHaveBeenCalledWith(prevProps);
+    });
+  });
+
+  describe("Event Handling", () => {
+    it("should call onClick prop when handleClick is triggered", () => {
+      const onClickMock = jest.fn();
+      const eventMock = { target: {} };
+      const shallowWeekNumber = shallow(<WeekNumber onClick={onClickMock} />);
+      shallowWeekNumber.simulate("click", eventMock);
+      expect(onClickMock).toHaveBeenCalledWith(eventMock);
+    });
+
+    describe("handleOnKeyDown", () => {
+      const handleOnKeyDownMock = jest.fn((event) => {
+        if (event.key === " ") {
+          event.preventDefault();
+          event.key = "Enter";
+        }
+      });
+
+      it("should change space key to Enter", () => {
+        const eventSpace = {
+          key: " ",
+          preventDefault: jest.fn(),
+        };
+        handleOnKeyDownMock(eventSpace);
+        expect(eventSpace.preventDefault).toHaveBeenCalled();
+        expect(eventSpace.key).toBe("Enter");
+      });
+
+      it("should not change any other key", () => {
+        const eventA = {
+          key: "a",
+        };
+        handleOnKeyDownMock(eventA);
+        expect(eventA.key).toBe("a");
+      });
+    });
+  });
+
+  describe("Utility Functions", () => {
+    beforeEach(() => {
+      shallowWeekNumber = shallow(<WeekNumber />);
+      instance = shallowWeekNumber.instance();
+    });
+
+    describe("getTabIndex", () => {
+      it("should return 0 if showWeekPicker and showWeekNumber are true and the day is selected", () => {
+        const shallowWeekNumber = shallow(
+          <WeekNumber showWeekPicker showWeekNumber selected={new Date()} />,
+        );
+        const instance = shallowWeekNumber.instance();
+        instance.isKeyboardSelected = jest.fn(() => true);
+        instance.isSameDay = jest.fn(() => true);
+        const props = { ...instance.props, preSelection: new Date() };
+        instance.props = props;
+        expect(instance.getTabIndex()).toBe(0),
+          expect(instance.isSameDay()).toBe(true);
+      });
+
+      it("should return 0 if showWeekPicker and showWeekNumber are true and the day is the preSelection", () => {
+        const shallowWeekNumber = shallow(
+          <WeekNumber showWeekPicker showWeekNumber selected={new Date()} />,
+        );
+        const instance = shallowWeekNumber.instance();
+        instance.isKeyboardSelected = jest.fn(() => true);
+        instance.isSameDay = jest.fn(() => true);
+        const props = { ...instance.props, preSelection: new Date() };
+        instance.props = props;
+        expect(instance.getTabIndex()).toBe(0);
+      });
+
+      it("should return -1 if showWeekPicker is false", () => {
+        const shallowWeekNumber = shallow(
+          <WeekNumber showWeekNumber selected={new Date()} />,
+        );
+        const instance = shallowWeekNumber.instance();
+        expect(instance.getTabIndex()).toBe(-1);
+      });
+
+      it("should return -1 if showWeekNumber is false", () => {
+        const shallowWeekNumber = shallow(
+          <WeekNumber showWeekPicker selected={new Date()} />,
+        );
+        const instance = shallowWeekNumber.instance();
+        expect(instance.getTabIndex()).toBe(-1);
+      });
+
+      it("should return -1 if the day is not selected or the preSelection", () => {
+        const shallowWeekNumber = shallow(
+          <WeekNumber showWeekPicker showWeekNumber selected={new Date()} />,
+        );
+        const instance = shallowWeekNumber.instance();
+        instance.isKeyboardSelected = jest.fn(() => false);
+        instance.isSameDay = jest.fn(() => false);
+        const props = { ...instance.props, preSelection: new Date() };
+        instance.props = props;
+        expect(instance.getTabIndex()).toBe(-1);
+      });
+    });
+
+    describe("weekNumberClasses should return the correct classes", () => {
+      it("should have the class 'react-datepicker__week-number'", () => {
+        const weekNumber = 1;
+        const shallowWeekNumber = shallow(
+          <WeekNumber weekNumber={weekNumber} />,
+        );
+        expect(
+          shallowWeekNumber.hasClass("react-datepicker__week-number"),
+        ).toBe(true);
+      });
+
+      it("should have the class 'react-datepicker__week-number--clickable' if onClick is defined", () => {
+        const shallowWeekNumber = shallow(<WeekNumber onClick={() => {}} />);
+        expect(
+          shallowWeekNumber.hasClass(
+            "react-datepicker__week-number--clickable",
+          ),
+        ).toBe(true);
+      });
+
+      it("should have the class 'react-datepicker__week-number--selected' if selected is current week and preselected is also current week", () => {
+        const currentWeekNumber = utils.newDate("2023-10-22T13:09:53+02:00");
+        const shallowWeekNumber = shallow(
+          <WeekNumber
+            date={currentWeekNumber}
+            selected={currentWeekNumber}
+            preSelection={currentWeekNumber}
+          />,
+        );
+        expect(
+          shallowWeekNumber.hasClass("react-datepicker__week-number--selected"),
+        ).toBe(true);
+      });
+
+      it("should have the class 'react-datepicker__week-number--selected' if selected is current week and preselected is not current week", () => {
+        const currentWeekNumber = utils.newDate("2023-10-22T13:09:53+02:00");
+        const preSelection = utils.addWeeks(currentWeekNumber, 1);
+        const shallowWeekNumber = shallow(
+          <WeekNumber
+            date={currentWeekNumber}
+            selected={currentWeekNumber}
+            preSelection={preSelection}
+          />,
+        );
+        expect(
+          shallowWeekNumber.hasClass("react-datepicker__week-number--selected"),
+        ).toBe(true);
+      });
+
+      it("should have the class 'react-datepicker__week-number--selected' if selected is not current week and preselected is current week", () => {
+        const currentWeekNumber = utils.newDate("2023-10-22T13:09:53+02:00");
+        const selected = utils.addWeeks(currentWeekNumber, 1);
+        const shallowWeekNumber = shallow(
+          <WeekNumber
+            date={currentWeekNumber}
+            selected={selected}
+            preSelection={currentWeekNumber}
+          />,
+        );
+        expect(
+          shallowWeekNumber.hasClass("react-datepicker__week-number--selected"),
+        ).toBe(false);
+        expect(
+          shallowWeekNumber.hasClass(
+            "react-datepicker__week-number--keyboard-selected",
+          ),
+        ).toBe(true);
+      });
+
+      it("should have the class 'react-datepicker__week-number--selected' if selected is not current week and preselected is not current week", () => {
+        const currentWeekNumber = utils.newDate("2023-10-22T13:09:53+02:00");
+        const selected = utils.addWeeks(currentWeekNumber, 1);
+        const preSelection = utils.addWeeks(currentWeekNumber, 2);
+        const shallowWeekNumber = shallow(
+          <WeekNumber
+            date={currentWeekNumber}
+            selected={selected}
+            preSelection={preSelection}
+          />,
+        );
+        expect(
+          shallowWeekNumber.hasClass("react-datepicker__week-number--selected"),
+        ).toBe(false);
+        expect(
+          shallowWeekNumber.hasClass(
+            "react-datepicker__week-number--keyboard-selected",
+          ),
+        ).toBe(false);
+      });
+    });
+  });
+
+  describe("handleFocusWeekNumber", () => {
+    let weekNumberEl, instance, shallowWeekNumber;
+
+    const createComponentWithProps = (props = {}) => {
+      shallowWeekNumber = shallow(<WeekNumber {...props} />);
+      instance = shallowWeekNumber.instance();
+      instance.weekNumberEl = weekNumberEl;
+      instance.getTabIndex = jest.fn(() => 0);
+      instance.isSameDay = jest.fn(() => true);
+    };
+
+    beforeEach(() => {
+      weekNumberEl = { current: { focus: jest.fn() } };
+      createComponentWithProps();
+    });
+
+    const setActiveElement = (element) => {
+      Object.defineProperty(document, "activeElement", {
+        value: element,
+        writable: false,
+        configurable: true,
+      });
+    };
+
+    it("should focus if conditions are met", () => {
+      setActiveElement(document.body);
+      instance.handleFocusWeekNumber();
+      expect(weekNumberEl.current.focus).toHaveBeenCalled();
+    });
+
+    it("should not focus if input is focused", () => {
+      const inputElement = document.createElement("input");
+      setActiveElement(inputElement);
+      instance.handleFocusWeekNumber({ isInputFocused: true });
+      expect(weekNumberEl.current.focus).not.toHaveBeenCalled();
+    });
+
+    it("should not focus if inline prop set and shouldFocusDayInline is false", () => {
+      createComponentWithProps({ inline: true, shouldFocusDayInline: false });
+      instance.handleFocusWeekNumber();
+      expect(weekNumberEl.current.focus).not.toHaveBeenCalled();
+    });
+
+    it("should focus if active element is another instance of WeekNumber", () => {
+      const activeElement = document.createElement("div");
+      activeElement.classList.add("react-datepicker__week-number");
+      setActiveElement(activeElement);
+      const containerRef = { current: document.createElement("div") };
+      createComponentWithProps({ containerRef });
+      containerRef.current.appendChild(activeElement);
+      instance.handleFocusWeekNumber();
+      expect(weekNumberEl.current.focus).toHaveBeenCalled();
+    });
+  });
 });
diff --git a/test/week_picker_test.js b/test/week_picker_test.js
new file mode 100644
index 00000000..937c6bd2
--- /dev/null
+++ b/test/week_picker_test.js
@@ -0,0 +1,46 @@
+import React from "react";
+import DatePicker from "../src/index.jsx";
+import Day from "../src/day";
+import WeekNumber from "../src/week_number";
+
+describe("WeekPicker", () => {
+  let sandbox;
+
+  beforeEach(() => {
+    sandbox = sinon.createSandbox();
+  });
+
+  afterEach(() => {
+    sandbox.restore();
+  });
+
+  it("should change the week when clicked on any option in the picker", () => {
+    const onChangeSpy = sinon.spy();
+    const weekPicker = TestUtils.renderIntoDocument(
+      <DatePicker onChange={onChangeSpy} showWeekPicker />,
+    );
+    expect(onChangeSpy.called).to.be.false;
+    TestUtils.Simulate.focus(weekPicker.inputRef.current);
+    const day = TestUtils.scryRenderedComponentsWithType(
+      weekPicker.calendar,
+      Day,
+    )[0];
+    TestUtils.Simulate.click(day.ref);
+    expect(onChangeSpy.calledOnce).to.be.true;
+  });
+
+  it("should change the week when clicked on any week number in the picker", () => {
+    const onChangeSpy = sinon.spy();
+    const weekPicker = TestUtils.renderIntoDocument(
+      <DatePicker onChange={onChangeSpy} showWeekPicker showWeekNumbers />,
+    );
+    expect(onChangeSpy.called).to.be.false;
+    TestUtils.Simulate.focus(weekPicker.inputRef.current);
+    const weekNumber = TestUtils.scryRenderedComponentsWithType(
+      weekPicker.calendar,
+      WeekNumber,
+    )[0];
+    TestUtils.Simulate.click(weekNumber.ref);
+    expect(onChangeSpy.calledOnce).to.be.true;
+  });
+});
diff --git a/test/week_test.test.js b/test/week_test.test.js
index a071c7b4..cde8f4e7 100644
--- a/test/week_test.test.js
+++ b/test/week_test.test.js
@@ -176,4 +176,122 @@ describe("Week", () => {
     day.simulate("mouseenter");
     expect(day.prop("day")).toEqual(dayMouseEntered);
   });
+
+  describe("handleWeekClick", () => {
+    it("should call onWeekSelect prop with correct arguments", () => {
+      const onWeekSelect = jest.fn();
+      const day = new Date("2022-02-01");
+      const weekNumber = 5;
+      const event = { target: {} };
+      const wrapper = shallow(
+        <Week
+          onWeekSelect={onWeekSelect}
+          showWeekPicker={false}
+          shouldCloseOnSelect={false}
+          setOpen={() => {}}
+        />,
+      );
+      wrapper.instance().handleWeekClick(day, weekNumber, event);
+      expect(onWeekSelect).toHaveBeenCalledWith(day, weekNumber, event);
+    });
+
+    it("should call handleDayClick with start of week if showWeekPicker prop is true", () => {
+      const handleDayClick = jest.fn();
+      const day = new Date("2022-02-01");
+      const weekNumber = 5;
+      const event = { target: {} };
+      const wrapper = shallow(
+        <Week
+          onWeekSelect={() => {}}
+          showWeekPicker
+          shouldCloseOnSelect={false}
+          setOpen={() => {}}
+        />,
+      );
+      wrapper.instance().handleDayClick = handleDayClick;
+      wrapper.instance().handleWeekClick(day, weekNumber, event);
+      const startOfWeek = utils.getStartOfWeek(day);
+      expect(handleDayClick).toHaveBeenCalledWith(startOfWeek, event);
+    });
+
+    it("should call setOpen prop with false if shouldCloseOnSelect prop is true", () => {
+      const setOpen = jest.fn();
+      const day = new Date("2022-02-01");
+      const weekNumber = 5;
+      const event = { target: {} };
+      const wrapper = shallow(
+        <Week
+          onWeekSelect={() => {}}
+          showWeekPicker={false}
+          shouldCloseOnSelect
+          setOpen={setOpen}
+        />,
+      );
+      wrapper.instance().handleWeekClick(day, weekNumber, event);
+      expect(setOpen).toHaveBeenCalledWith(false);
+    });
+  });
+
+  describe("selected and keyboard-selected", () => {
+    it("selected is current week and preselected is also current week", () => {
+      const currentWeek = utils.newDate("2023-10-22T13:09:53+02:00");
+      const shallowWeek = shallow(
+        <Week
+          day={currentWeek}
+          selected={currentWeek}
+          preSelection={currentWeek}
+        />,
+      );
+      expect(shallowWeek.hasClass("react-datepicker__week--selected")).toBe(
+        true,
+      );
+    });
+
+    it("selected is current week and preselected is not current week", () => {
+      const currentWeek = utils.newDate("2023-10-22T13:09:53+02:00");
+      const preSelection = utils.addWeeks(currentWeek, 1);
+      const shallowWeek = shallow(
+        <Week
+          day={currentWeek}
+          selected={currentWeek}
+          preSelection={preSelection}
+        />,
+      );
+      expect(shallowWeek.hasClass("react-datepicker__week--selected")).toBe(
+        true,
+      );
+    });
+
+    it("selected is not current week and preselect is current week", () => {
+      const currentWeek = utils.newDate("2023-10-22T13:09:53+02:00");
+      const selected = utils.addWeeks(currentWeek, 1);
+      const shallowWeek = shallow(
+        <Week
+          day={currentWeek}
+          selected={selected}
+          preSelection={currentWeek}
+        />,
+      );
+      expect(shallowWeek.hasClass("react-datepicker__week--selected")).toBe(
+        false,
+      );
+      expect(
+        shallowWeek.hasClass("react-datepicker__week--keyboard-selected"),
+      ).toBe(true);
+    });
+
+    it("select is not current week and preselect is not current week", () => {
+      const currentWeek = utils.newDate("2023-10-22T13:09:53+02:00");
+      const selected = utils.addWeeks(currentWeek, 1);
+      const shallowWeek = shallow(
+        <Week day={currentWeek} selected={selected} preSelection={selected} />,
+      );
+      expect(shallowWeek.hasClass("react-datepicker__week--selected")).toBe(
+        false,
+      );
+      expect(
+        shallowWeek.hasClass("react-datepicker__week--keyboard-selected"),
+      ).toBe(false);
+    });
+  });
 });
diff --git a/test/year_picker_test.test.js b/test/year_picker_test.test.js
index 16b1e2f2..c29bfb05 100644
--- a/test/year_picker_test.test.js
+++ b/test/year_picker_test.test.js
@@ -3,9 +3,9 @@ import { mount } from "enzyme";
 import DatePicker from "../src/index.jsx";
 import Year from "../src/year.jsx";
 import TestUtils from "react-dom/test-utils";
+import ReactDOM from "react-dom";
 import * as utils from "../src/date_utils.js";
 import Calendar from "../src/calendar.jsx";
-import { findDOMNode } from "react-dom";
 
 describe("YearPicker", () => {
   it("should show year picker component when showYearPicker prop is present", () => {
@@ -441,7 +441,7 @@ describe("YearPicker", () => {
           }}
         />,
       );
-      TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
       const calendar = TestUtils.scryRenderedComponentsWithType(
         datePicker.calendar,
         Calendar,
@@ -480,7 +480,7 @@ describe("YearPicker", () => {
           }}
         />,
       );
-      TestUtils.Simulate.focus(findDOMNode(datePicker.input));
+      TestUtils.Simulate.focus(ReactDOM.findDOMNode(datePicker.input));
       const calendar = TestUtils.scryRenderedComponentsWithType(
         datePicker.calendar,
         Calendar,