@simplybusiness/mobius-datepicker 9.0.1 → 9.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export declare function mockMatchMedia(matches: boolean): void;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simplybusiness/mobius-datepicker",
3
3
  "license": "UNLICENSED",
4
- "version": "9.0.1",
4
+ "version": "9.0.3",
5
5
  "description": "Mobius date picker component",
6
6
  "repository": {
7
7
  "type": "git",
@@ -10,8 +10,7 @@
10
10
  "simplyBusiness": {
11
11
  "publishToPublicNpm": true
12
12
  },
13
- "type": "module",
14
- "main": "dist/esm/index.js",
13
+ "main": "dist/cjs/index.js",
15
14
  "types": "./dist/types/index.d.ts",
16
15
  "files": [
17
16
  "src",
@@ -20,18 +19,20 @@
20
19
  "exports": {
21
20
  ".": {
22
21
  "types": "./dist/types/index.d.ts",
22
+ "require": "./dist/cjs/index.js",
23
23
  "import": "./dist/esm/index.js",
24
24
  "default": "./dist/esm/index.js"
25
25
  },
26
26
  "./src/*.css": "./src/*.css"
27
27
  },
28
28
  "publishConfig": {
29
- "main": "dist/esm/index.js",
29
+ "main": "dist/cjs/index.js",
30
30
  "module": "dist/esm/index.js",
31
31
  "types": "./dist/types/index.d.ts",
32
32
  "exports": {
33
33
  ".": {
34
34
  "types": "./dist/types/index.d.ts",
35
+ "require": "./dist/cjs/index.js",
35
36
  "import": "./dist/esm/index.js",
36
37
  "default": "./dist/esm/index.js"
37
38
  },
@@ -42,13 +43,14 @@
42
43
  "clean": "rm -rf dist",
43
44
  "build": "yarn run -T turbo run turbo:build",
44
45
  "prepack": "yarn run build",
45
- "turbo:build": "yarn build:esm && yarn build:types",
46
+ "turbo:build": "yarn build:esm && yarn build:cjs && yarn build:types",
46
47
  "build:esm": "build-package esm",
48
+ "build:cjs": "build-package cjs",
47
49
  "build:types": "tsc --emitDeclarationOnly --project tsconfig.build.json",
48
50
  "lint": "eslint",
49
51
  "lint:fix": "eslint --fix",
50
- "test": "NODE_OPTIONS=--experimental-vm-modules jest",
51
- "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --collect-coverage",
52
+ "test": "vitest run",
53
+ "test:coverage": "vitest run --coverage",
52
54
  "check-types": "tsc --noEmit --pretty",
53
55
  "lint:css": "lint-css",
54
56
  "lint:css:fix": "lint-css --fix"
@@ -60,17 +62,15 @@
60
62
  "@eslint/js": "^9.39.2",
61
63
  "@simplybusiness/build-scripts": "^2.0.1",
62
64
  "@simplybusiness/eslint-config": "^2.0.1",
63
- "@swc/core": "^1.12.5",
64
- "@swc/jest": "^0.2.39",
65
65
  "@testing-library/dom": "^10.4.1",
66
66
  "@testing-library/jest-dom": "6.9.1",
67
67
  "@testing-library/react": "^16.3.2",
68
68
  "@testing-library/user-event": "^14.6.1",
69
- "@types/jest": "^30.0.0",
70
69
  "@types/react": "^19.2.11",
71
70
  "@types/react-dom": "^19.2.3",
72
71
  "@typescript-eslint/eslint-plugin": "^8.54.0",
73
72
  "@typescript-eslint/parser": "^8.54.0",
73
+ "@vitest/coverage-v8": "^4.0.18",
74
74
  "eslint": "^9.39.2",
75
75
  "eslint-config-prettier": "^10.1.8",
76
76
  "eslint-import-resolver-typescript": "^4.4.4",
@@ -81,23 +81,21 @@
81
81
  "eslint-plugin-react-hooks": "^7.0.1",
82
82
  "eslint-plugin-ssr-friendly": "^1.3.0",
83
83
  "eslint-plugin-testing-library": "^7.15.4",
84
- "identity-obj-proxy": "^3.0.0",
85
- "jest": "^30.2.0",
86
- "jest-config": "^30.2.0",
87
- "jest-environment-jsdom": "^30.2.0",
88
84
  "prettier": "^3.8.1",
89
85
  "react": "^19.2.4",
90
86
  "react-dom": "^19.2.4",
91
87
  "tslib": "^2.8.1",
92
- "typescript": "^5.9.3"
88
+ "typescript": "^5.9.3",
89
+ "vitest": "^4.0.18"
93
90
  },
94
91
  "peerDependencies": {
95
92
  "react": "^19.2.0",
96
93
  "react-dom": "^19.2.0"
97
94
  },
98
95
  "dependencies": {
99
- "@simplybusiness/icons": "^5.0.1",
100
- "@simplybusiness/mobius": "^8.0.1",
96
+ "@simplybusiness/icons": "^5.0.2",
97
+ "@simplybusiness/mobius": "^9.0.0",
98
+ "@simplybusiness/mobius-hooks": "^0.1.0",
101
99
  "classnames": "^2.5.1",
102
100
  "date-fns": "^4.1.0",
103
101
  "react-day-picker": "^9.13.0"
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from "@storybook/react-webpack5";
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { add, format } from "date-fns";
3
3
  import { excludeControls } from "../../utils";
4
4
  import { StoryContainer } from "../../utils/StoryContainer";
@@ -1,9 +1,13 @@
1
1
  import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
3
  import { DatePicker } from ".";
4
- import { jestMockMatchMedia } from "../../utils/jestMockMatchMedia";
4
+ import { mockMatchMedia } from "../../utils/mockMatchMedia";
5
5
  import { DEFAULT_AFTER_MAX, DEFAULT_BEFORE_MIN } from "./utils";
6
6
 
7
+ // Eagerly resolve DatePickerModal so React.lazy gets an instant cache hit
8
+ // Without this, the dynamic import can take too long on slow CI runners
9
+ vi.mock("./DatePickerModal", async importOriginal => importOriginal());
10
+
7
11
  const DISABLED_CLASS_NAME = "--is-disabled";
8
12
  const TOUCH_DEVICE_CLASS_NAME = "--is-touch-device";
9
13
  const VALID_CLASS_NAME = "--is-valid";
@@ -17,7 +21,7 @@ const DEVICES = [
17
21
  describe("DatePicker", () => {
18
22
  describe.each(DEVICES)(`given it is a %s`, (deviceLabel, isTouchDevice) => {
19
23
  beforeEach(() => {
20
- jestMockMatchMedia(isTouchDevice as boolean);
24
+ mockMatchMedia(isTouchDevice as boolean);
21
25
  });
22
26
 
23
27
  it("should render without errors", () => {
@@ -110,7 +114,7 @@ describe("DatePicker", () => {
110
114
  it("triggers onChange", async () => {
111
115
  const labelText = "Start date";
112
116
  const testId = "date-picker";
113
- const onChange = jest.fn();
117
+ const onChange = vi.fn();
114
118
 
115
119
  render(
116
120
  <DatePicker
@@ -216,26 +220,35 @@ describe("DatePicker", () => {
216
220
  });
217
221
 
218
222
  describe("when TextField is clicked", () => {
219
- it(`should ${(isTouchDevice && "not") || ""} open the date picker modal`, async () => {
220
- const labelText = "Start date";
221
- const testId = "date-picker";
222
-
223
- render(<DatePicker label={labelText} data-testid={testId} />);
224
-
225
- const inputField = screen.getByTestId(testId);
226
-
227
- await userEvent.click(inputField);
228
-
229
- await waitFor(() => {
230
- if (isTouchDevice) {
231
- expect(
232
- screen.queryByTestId("modal-container"),
233
- ).not.toBeInTheDocument();
234
- } else {
235
- expect(screen.getByTestId("modal-container")).toBeInTheDocument();
236
- }
237
- });
238
- }, 10000);
223
+ it(
224
+ `should ${(isTouchDevice && "not") || ""} open the date picker modal`,
225
+ { retry: 2, timeout: 10000 },
226
+ async () => {
227
+ const labelText = "Start date";
228
+ const testId = "date-picker";
229
+
230
+ render(<DatePicker label={labelText} data-testid={testId} />);
231
+
232
+ const inputField = screen.getByTestId(testId);
233
+
234
+ await userEvent.click(inputField);
235
+
236
+ await waitFor(
237
+ () => {
238
+ if (isTouchDevice) {
239
+ expect(
240
+ screen.queryByTestId("modal-container"),
241
+ ).not.toBeInTheDocument();
242
+ } else {
243
+ expect(
244
+ screen.getByTestId("modal-container"),
245
+ ).toBeInTheDocument();
246
+ }
247
+ },
248
+ { timeout: 10000 },
249
+ );
250
+ },
251
+ );
239
252
  });
240
253
  });
241
254
  });
@@ -73,7 +73,7 @@ describe("DatePickerModal", () => {
73
73
  });
74
74
 
75
75
  it("calls onSelected when a day is picked", async () => {
76
- const onSelected = jest.fn();
76
+ const onSelected = vi.fn();
77
77
 
78
78
  render(<DatePickerModal isOpen top={30} onSelected={onSelected} />);
79
79
 
@@ -142,7 +142,7 @@ describe("DatePickerModal", () => {
142
142
  it.todo("traps focus when opened"); // Move to e2e test with Playwright
143
143
 
144
144
  it("should disable dates before min and after max value", () => {
145
- const onSelected = jest.fn();
145
+ const onSelected = vi.fn();
146
146
  const date = "2025-01-07";
147
147
  const min = "2025-01-05";
148
148
  const max = "2025-01-10";
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
- import { VisuallyHidden, useOnClickOutside } from "@simplybusiness/mobius";
3
+ import { useOnClickOutside } from "@simplybusiness/mobius-hooks";
4
+ import { VisuallyHidden } from "@simplybusiness/mobius";
4
5
  import classNames from "classnames/dedupe";
5
6
  import { parseISO } from "date-fns";
6
7
  import { useId, useRef } from "react";
@@ -3,8 +3,8 @@ import { getStartWeekday } from "./getStartWeekday";
3
3
 
4
4
  describe("getStartWeekday", () => {
5
5
  beforeEach(() => {
6
- jest.spyOn(navigator, "languages", "get").mockReturnValue([]);
7
- jest.spyOn(navigator, "language", "get").mockImplementation(() => "en-GB");
6
+ vi.spyOn(navigator, "languages", "get").mockReturnValue([]);
7
+ vi.spyOn(navigator, "language", "get").mockImplementation(() => "en-GB");
8
8
  });
9
9
 
10
10
  it("assumes default of en-GB (Monday) when no args supplied", () => {
@@ -0,0 +1,36 @@
1
+ import { type MockedFunction } from "vitest";
2
+
3
+ export function mockMatchMedia(matches: boolean) {
4
+ const addListenerMock: MockedFunction<MediaQueryList["addListener"]> =
5
+ vi.fn();
6
+ const removeListenerMock: MockedFunction<MediaQueryList["removeListener"]> =
7
+ vi.fn();
8
+ const addEventListenerMock: MockedFunction<
9
+ MediaQueryList["addEventListener"]
10
+ > = vi.fn();
11
+ const removeEventListenerMock: MockedFunction<
12
+ MediaQueryList["removeEventListener"]
13
+ > = vi.fn();
14
+ const dispatchEventMock: MockedFunction<MediaQueryList["dispatchEvent"]> =
15
+ vi.fn();
16
+
17
+ const matchMediaMock: MockedFunction<typeof window.matchMedia> = vi.fn(
18
+ (query: string) =>
19
+ ({
20
+ matches,
21
+ media: query,
22
+ onchange: null,
23
+ addListener: addListenerMock,
24
+ removeListener: removeListenerMock,
25
+ addEventListener: addEventListenerMock,
26
+ removeEventListener: removeEventListenerMock,
27
+ dispatchEvent: dispatchEventMock,
28
+ }) satisfies MediaQueryList,
29
+ );
30
+
31
+ Object.defineProperty(window, "matchMedia", {
32
+ writable: true,
33
+ configurable: true,
34
+ value: matchMediaMock,
35
+ });
36
+ }
@@ -1 +0,0 @@
1
- export declare function jestMockMatchMedia(matches: boolean): void;
@@ -1,36 +0,0 @@
1
- export function jestMockMatchMedia(matches: boolean) {
2
- const addListenerMock: jest.MockedFunction<MediaQueryList["addListener"]> =
3
- jest.fn();
4
- const removeListenerMock: jest.MockedFunction<
5
- MediaQueryList["removeListener"]
6
- > = jest.fn();
7
- const addEventListenerMock: jest.MockedFunction<
8
- MediaQueryList["addEventListener"]
9
- > = jest.fn();
10
- const removeEventListenerMock: jest.MockedFunction<
11
- MediaQueryList["removeEventListener"]
12
- > = jest.fn();
13
- const dispatchEventMock: jest.MockedFunction<
14
- MediaQueryList["dispatchEvent"]
15
- > = jest.fn();
16
-
17
- const matchMediaMock: jest.MockedFunction<typeof window.matchMedia> = jest.fn(
18
- (query: string) =>
19
- ({
20
- matches,
21
- media: query,
22
- onchange: null,
23
- addListener: addListenerMock,
24
- removeListener: removeListenerMock,
25
- addEventListener: addEventListenerMock,
26
- removeEventListener: removeEventListenerMock,
27
- dispatchEvent: dispatchEventMock,
28
- }) satisfies MediaQueryList,
29
- );
30
-
31
- Object.defineProperty(window, "matchMedia", {
32
- writable: true,
33
- configurable: true,
34
- value: matchMediaMock,
35
- });
36
- }