@simplybusiness/mobius 8.0.2 → 9.0.1

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/cjs/index.js +577 -733
  3. package/dist/cjs/index.js.map +4 -4
  4. package/dist/cjs/meta.json +295 -520
  5. package/dist/esm/index.js +542 -694
  6. package/dist/esm/index.js.map +4 -4
  7. package/dist/esm/meta.json +300 -532
  8. package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
  9. package/dist/types/src/hooks/index.d.ts +0 -7
  10. package/dist/types/src/utils/htmlDialogPolyfill.d.ts +1 -0
  11. package/dist/types/src/utils/index.d.ts +0 -1
  12. package/dist/types/src/utils/mockMatchMedia.d.ts +1 -0
  13. package/dist/types/vitest.config.d.ts +2 -0
  14. package/package.json +18 -24
  15. package/src/components/Accordion/Accordion.stories.tsx +1 -1
  16. package/src/components/Accordion/Accordion.test.tsx +12 -12
  17. package/src/components/Accordion/Accordion.tsx +1 -1
  18. package/src/components/Accordion/AccordionList.stories.tsx +1 -1
  19. package/src/components/Accordion/AccordionList.test.tsx +6 -6
  20. package/src/components/AddressLookup/AddressLookup.stories.tsx +1 -1
  21. package/src/components/AddressLookup/AddressLookup.test.tsx +19 -20
  22. package/src/components/AddressLookup/LoqateAddressLookupService.test.tsx +7 -6
  23. package/src/components/Alert/Alert.stories.tsx +1 -1
  24. package/src/components/Box/Box.stories.tsx +1 -1
  25. package/src/components/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  26. package/src/components/Button/Button.stories.tsx +3 -4
  27. package/src/components/Button/Button.test.tsx +4 -4
  28. package/src/components/Checkbox/Checkbox.stories.tsx +1 -1
  29. package/src/components/Checkbox/Checkbox.test.tsx +2 -2
  30. package/src/components/Checkbox/CheckboxGroup.stories.tsx +1 -1
  31. package/src/components/Checkbox/CheckboxGroup.test.tsx +5 -5
  32. package/src/components/Combobox/Combobox.stories.tsx +1 -1
  33. package/src/components/Combobox/Combobox.test.tsx +67 -78
  34. package/src/components/Combobox/Combobox.tsx +2 -1
  35. package/src/components/Combobox/useComboboxOptions.test.ts +30 -30
  36. package/src/components/Combobox/useComboboxOptions.ts +1 -1
  37. package/src/components/Container/Container.stories.tsx +1 -1
  38. package/src/components/DateField/DateField.stories.tsx +1 -1
  39. package/src/components/DateField/DateField.test.tsx +1 -1
  40. package/src/components/Divider/Divider.stories.tsx +1 -1
  41. package/src/components/Drawer/Drawer.stories.tsx +1 -1
  42. package/src/components/Drawer/Drawer.test.tsx +6 -6
  43. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +8 -10
  44. package/src/components/DropdownMenu/DropdownMenu.test.tsx +1 -1
  45. package/src/components/ErrorMessage/ErrorMessage.stories.tsx +1 -1
  46. package/src/components/ExpandableText/ExpandableText.test.tsx +14 -14
  47. package/src/components/Fieldset/Fieldset.stories.tsx +1 -1
  48. package/src/components/Flex/Flex.stories.tsx +1 -1
  49. package/src/components/Grid/Grid.stories.tsx +4 -7
  50. package/src/components/Icon/Icon.stories.tsx +1 -1
  51. package/src/components/Image/Image.stories.tsx +1 -1
  52. package/src/components/Label/Label.stories.tsx +1 -1
  53. package/src/components/Link/Link.stories.tsx +1 -1
  54. package/src/components/Link/Link.test.tsx +1 -1
  55. package/src/components/LinkButton/LinkButton.stories.tsx +1 -1
  56. package/src/components/LinkButton/LinkButton.test.tsx +2 -2
  57. package/src/components/List/List.stories.tsx +1 -1
  58. package/src/components/LoadingIndicator/LoadingIndicator.stories.tsx +1 -1
  59. package/src/components/Logo/Logo.stories.tsx +1 -1
  60. package/src/components/Modal/Modal.stories.tsx +1 -1
  61. package/src/components/Modal/Modal.test.tsx +6 -6
  62. package/src/components/NumberField/NumberField.stories.tsx +1 -1
  63. package/src/components/NumberField/NumberField.test.tsx +5 -5
  64. package/src/components/PasswordField/PasswordField.stories.tsx +1 -1
  65. package/src/components/Popover/Popover.stories.tsx +4 -8
  66. package/src/components/Popover/Popover.test.tsx +4 -4
  67. package/src/components/Popover/Popover.tsx +1 -1
  68. package/src/components/Progress/Progress.stories.tsx +1 -1
  69. package/src/components/Radio/Radio.stories.tsx +1 -1
  70. package/src/components/Radio/Radio.test.tsx +9 -9
  71. package/src/components/SVG/SVG.stories.tsx +1 -1
  72. package/src/components/Segment/Segment.stories.tsx +1 -1
  73. package/src/components/Select/Select.stories.tsx +1 -1
  74. package/src/components/Select/Select.test.tsx +1 -1
  75. package/src/components/Slider/Slider.stories.tsx +1 -1
  76. package/src/components/Slider/Slider.test.tsx +6 -6
  77. package/src/components/Slider/helpers.test.ts +1 -1
  78. package/src/components/Stack/Stack.stories.tsx +1 -1
  79. package/src/components/Switch/Switch.stories.tsx +1 -1
  80. package/src/components/Switch/Switch.test.tsx +1 -1
  81. package/src/components/Table/Table.stories.tsx +1 -1
  82. package/src/components/Text/Text.stories.tsx +1 -1
  83. package/src/components/TextArea/TextArea.stories.tsx +1 -1
  84. package/src/components/TextArea/TextArea.test.tsx +3 -3
  85. package/src/components/TextField/TextField.stories.tsx +1 -1
  86. package/src/components/TextOrHTML/TextOrHTML.stories.tsx +1 -1
  87. package/src/components/Title/Title.stories.tsx +1 -1
  88. package/src/components/Toast/Toast.stories.tsx +1 -1
  89. package/src/components/Toast/Toast.test.tsx +6 -6
  90. package/src/components/Trust/Trust.stories.tsx +1 -1
  91. package/src/components/VisuallyHidden/VisuallyHidden.stories.tsx +1 -1
  92. package/src/hooks/index.tsx +0 -7
  93. package/src/hooks/useBreakpoint/useBreakpoint.ssr.test.tsx +18 -0
  94. package/src/hooks/useBreakpoint/useBreakpoint.stories.tsx +1 -1
  95. package/src/hooks/useBreakpoint/useBreakpoint.test.tsx +57 -5
  96. package/src/hooks/useBreakpoint/useBreakpoint.tsx +25 -39
  97. package/src/hooks/useButton/useButton.test.tsx +4 -4
  98. package/src/hooks/useDialog/useDialog.ts +1 -1
  99. package/src/hooks/useLabel/useLabel.test.tsx +1 -1
  100. package/src/hooks/useTextField/useTextField.test.tsx +4 -4
  101. package/src/public-whitelist.test.ts +1 -0
  102. package/src/utils/delay.test.ts +4 -4
  103. package/src/utils/{jestHTMLDialogPolyfill.ts → htmlDialogPolyfill.ts} +5 -5
  104. package/src/utils/index.ts +0 -1
  105. package/src/utils/mockMatchMedia.ts +16 -0
  106. package/dist/types/src/hooks/useBodyScrollLock/index.d.ts +0 -1
  107. package/dist/types/src/hooks/useBodyScrollLock/useBodyScrollLock.d.ts +0 -3
  108. package/dist/types/src/hooks/useDebouncedValue/index.d.ts +0 -1
  109. package/dist/types/src/hooks/useDebouncedValue/useDebouncedValue.d.ts +0 -1
  110. package/dist/types/src/hooks/useOnClickOutside/index.d.ts +0 -1
  111. package/dist/types/src/hooks/useOnClickOutside/useOnClickOutside.d.ts +0 -2
  112. package/dist/types/src/hooks/useOnUnmount/index.d.ts +0 -1
  113. package/dist/types/src/hooks/useOnUnmount/useOnUnmount.d.ts +0 -1
  114. package/dist/types/src/hooks/usePrefersReducedMotion/index.d.ts +0 -1
  115. package/dist/types/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.d.ts +0 -1
  116. package/dist/types/src/hooks/useRenderCount/index.d.ts +0 -1
  117. package/dist/types/src/hooks/useRenderCount/useRenderCount.d.ts +0 -1
  118. package/dist/types/src/hooks/useWindowEvent/index.d.ts +0 -1
  119. package/dist/types/src/hooks/useWindowEvent/useWindowEvent.d.ts +0 -1
  120. package/dist/types/src/utils/jestHTMLDialogPolyfill.d.ts +0 -1
  121. package/dist/types/src/utils/jestMockMatchMedia.d.ts +0 -1
  122. package/src/hooks/useBodyScrollLock/index.ts +0 -1
  123. package/src/hooks/useBodyScrollLock/useBodyScrollLock.test.ts +0 -34
  124. package/src/hooks/useBodyScrollLock/useBodyScrollLock.ts +0 -30
  125. package/src/hooks/useDebouncedValue/index.tsx +0 -1
  126. package/src/hooks/useDebouncedValue/useDebouncedValue.test.tsx +0 -62
  127. package/src/hooks/useDebouncedValue/useDebouncedValue.tsx +0 -25
  128. package/src/hooks/useOnClickOutside/index.tsx +0 -1
  129. package/src/hooks/useOnClickOutside/useOnClickOutside.test.tsx +0 -189
  130. package/src/hooks/useOnClickOutside/useOnClickOutside.tsx +0 -44
  131. package/src/hooks/useOnUnmount/index.tsx +0 -1
  132. package/src/hooks/useOnUnmount/useOnUnmount.test.tsx +0 -37
  133. package/src/hooks/useOnUnmount/useOnUnmount.tsx +0 -8
  134. package/src/hooks/usePrefersReducedMotion/index.tsx +0 -1
  135. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.test.tsx +0 -48
  136. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.tsx +0 -22
  137. package/src/hooks/useRenderCount/index.ts +0 -1
  138. package/src/hooks/useRenderCount/useRenderCount.test.ts +0 -26
  139. package/src/hooks/useRenderCount/useRenderCount.ts +0 -9
  140. package/src/hooks/useWindowEvent/index.tsx +0 -1
  141. package/src/hooks/useWindowEvent/useWindowEvent.test.tsx +0 -188
  142. package/src/hooks/useWindowEvent/useWindowEvent.tsx +0 -41
  143. package/src/utils/jestMockMatchMedia.ts +0 -16
@@ -1 +0,0 @@
1
- export * from "./useWindowEvent";
@@ -1 +0,0 @@
1
- export declare function useWindowEvent<EventType extends keyof WindowEventMap>(type: EventType, listener: (this: Window, ev: WindowEventMap[EventType]) => unknown, options?: AddEventListenerOptions): void;
@@ -1 +0,0 @@
1
- export declare const jestHTMLDialogPolyfill: () => void;
@@ -1 +0,0 @@
1
- export declare const jestMockMatchMedia: (matches: boolean) => void;
@@ -1 +0,0 @@
1
- export * from "./useBodyScrollLock";
@@ -1,34 +0,0 @@
1
- import { renderHook } from "@testing-library/react";
2
- import { useBodyScrollLock } from "./useBodyScrollLock";
3
-
4
- describe("useBodyScrollLock", () => {
5
- it("should be defined", () => {
6
- expect(useBodyScrollLock).toBeDefined();
7
- });
8
-
9
- it("should not throw", () => {
10
- expect(() => renderHook(() => useBodyScrollLock())).not.toThrow();
11
- });
12
-
13
- it("should disable scroll by default", () => {
14
- renderHook(() => useBodyScrollLock());
15
- expect(document.body).toHaveStyle({ overflow: "hidden" });
16
- });
17
-
18
- it("should disable scroll when enabled is true", () => {
19
- renderHook(() => useBodyScrollLock({ enabled: true }));
20
- expect(document.body).toHaveStyle({ overflow: "hidden" });
21
- });
22
-
23
- it("should enable scroll when enabled is false", () => {
24
- renderHook(() => useBodyScrollLock({ enabled: false }));
25
- expect(document.body).toHaveStyle({ overflow: "" });
26
- });
27
-
28
- it("should enable scroll on unmount", () => {
29
- const { unmount } = renderHook(() => useBodyScrollLock());
30
- expect(document.body).toHaveStyle({ overflow: "hidden" });
31
- unmount();
32
- expect(document.body).toHaveStyle({ overflow: "" });
33
- });
34
- });
@@ -1,30 +0,0 @@
1
- import { useEffect } from "react";
2
-
3
- export function useBodyScrollLock({
4
- enabled = true,
5
- }: { enabled?: boolean } = {}) {
6
- function disableScrollLock() {
7
- document.body.style.removeProperty("overflow");
8
- document.body.style.removeProperty("scrollbar-gutter");
9
- document.documentElement.style.removeProperty("scrollbar-gutter");
10
- }
11
-
12
- function enableScrollLock() {
13
- document.body.style.overflow = "hidden";
14
- // Prevent content jumping due to scrollbar disappearing
15
- document.body.style.scrollbarGutter = "stable";
16
- document.documentElement.style.scrollbarGutter = "stable";
17
- }
18
-
19
- useEffect(() => {
20
- if (enabled) {
21
- enableScrollLock();
22
- } else {
23
- disableScrollLock();
24
- }
25
-
26
- return () => {
27
- disableScrollLock();
28
- };
29
- }, [enabled]);
30
- }
@@ -1 +0,0 @@
1
- export * from "./useDebouncedValue";
@@ -1,62 +0,0 @@
1
- import { renderHook, act } from "@testing-library/react";
2
- import { useDebouncedValue } from "./useDebouncedValue";
3
-
4
- describe("useDebouncedValue", () => {
5
- beforeEach(() => {
6
- jest.useFakeTimers();
7
- });
8
-
9
- afterEach(() => {
10
- jest.useRealTimers();
11
- });
12
-
13
- it("returns the initial value immediately when delay is 0", () => {
14
- const { result } = renderHook(() => useDebouncedValue("initial", 0));
15
- expect(result.current).toBe("initial");
16
- });
17
-
18
- it("returns the previous value until delay has elapsed", () => {
19
- const { result, rerender } = renderHook(
20
- ({ value }) => useDebouncedValue(value, 1000),
21
- { initialProps: { value: "initial" } },
22
- );
23
-
24
- expect(result.current).toBe("initial");
25
-
26
- rerender({ value: "updated" });
27
- expect(result.current).toBe("initial");
28
-
29
- act(() => {
30
- jest.advanceTimersByTime(999);
31
- });
32
- expect(result.current).toBe("initial");
33
-
34
- act(() => {
35
- jest.advanceTimersByTime(1);
36
- });
37
- expect(result.current).toBe("updated");
38
- });
39
-
40
- it("cancels previous timeout when value changes", () => {
41
- const { result, rerender } = renderHook(
42
- ({ value }) => useDebouncedValue(value, 1000),
43
- { initialProps: { value: "initial" } },
44
- );
45
-
46
- rerender({ value: "updated1" });
47
- act(() => {
48
- jest.advanceTimersByTime(500);
49
- });
50
-
51
- rerender({ value: "updated2" });
52
- act(() => {
53
- jest.advanceTimersByTime(500);
54
- });
55
- expect(result.current).toBe("initial");
56
-
57
- act(() => {
58
- jest.advanceTimersByTime(500);
59
- });
60
- expect(result.current).toBe("updated2");
61
- });
62
- });
@@ -1,25 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export function useDebouncedValue<T>(value: T, delay: number = 0): T {
4
- const [debouncedValue, setDebouncedValue] = useState(value);
5
-
6
- useEffect(() => {
7
- let timeout: NodeJS.Timeout | null = null;
8
- if (delay === 0) {
9
- setDebouncedValue(value);
10
- } else {
11
- timeout = setTimeout(() => {
12
- setDebouncedValue(value);
13
- }, delay);
14
- }
15
-
16
- return () => {
17
- if (timeout) {
18
- clearTimeout(timeout);
19
- timeout = null;
20
- }
21
- };
22
- }, [value, delay]);
23
-
24
- return debouncedValue;
25
- }
@@ -1 +0,0 @@
1
- export * from "./useOnClickOutside";
@@ -1,189 +0,0 @@
1
- import { renderHook } from "@testing-library/react";
2
- import { useRef } from "react";
3
- import { useOnClickOutside } from "./useOnClickOutside";
4
-
5
- describe("useOnClickOutside", () => {
6
- const addEventListenerSpy = jest.spyOn(document, "addEventListener");
7
- const removeEventListenerSpy = jest.spyOn(document, "removeEventListener");
8
-
9
- beforeEach(() => {
10
- addEventListenerSpy.mockClear();
11
- removeEventListenerSpy.mockClear();
12
- });
13
-
14
- afterAll(() => {
15
- addEventListenerSpy.mockRestore();
16
- removeEventListenerSpy.mockRestore();
17
- });
18
-
19
- it("adds event listeners on mount", () => {
20
- const handler = jest.fn();
21
- const { result } = renderHook(() => {
22
- const ref = useRef<HTMLDivElement>(null);
23
- useOnClickOutside(ref, handler);
24
- return ref;
25
- });
26
-
27
- expect(result.current).toBeDefined();
28
- expect(addEventListenerSpy).toHaveBeenCalledWith(
29
- "mousedown",
30
- expect.any(Function),
31
- );
32
- expect(addEventListenerSpy).toHaveBeenCalledWith(
33
- "touchstart",
34
- expect.any(Function),
35
- );
36
- expect(addEventListenerSpy).toHaveBeenCalledWith(
37
- "keydown",
38
- expect.any(Function),
39
- );
40
- });
41
-
42
- it("removes event listeners on unmount", () => {
43
- const handler = jest.fn();
44
- const { unmount } = renderHook(() => {
45
- const ref = useRef<HTMLDivElement>(null);
46
- useOnClickOutside(ref, handler);
47
- return ref;
48
- });
49
-
50
- removeEventListenerSpy.mockClear();
51
- unmount();
52
-
53
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
54
- "mousedown",
55
- expect.any(Function),
56
- );
57
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
58
- "touchstart",
59
- expect.any(Function),
60
- );
61
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
62
- "keydown",
63
- expect.any(Function),
64
- );
65
- });
66
-
67
- it("calls handler when clicking outside the ref element", () => {
68
- const handler = jest.fn();
69
- const div = document.createElement("div");
70
- document.body.appendChild(div);
71
-
72
- renderHook(() => {
73
- const ref = useRef<HTMLDivElement>(div);
74
- useOnClickOutside(ref, handler);
75
- return ref;
76
- });
77
-
78
- // Click outside the element
79
- const outsideElement = document.createElement("span");
80
- document.body.appendChild(outsideElement);
81
-
82
- const event = new MouseEvent("mousedown", { bubbles: true });
83
- outsideElement.dispatchEvent(event);
84
-
85
- expect(handler).toHaveBeenCalledTimes(1);
86
-
87
- document.body.removeChild(div);
88
- document.body.removeChild(outsideElement);
89
- });
90
-
91
- it("does not call handler when clicking inside the ref element", () => {
92
- const handler = jest.fn();
93
- const div = document.createElement("div");
94
- const child = document.createElement("span");
95
- div.appendChild(child);
96
- document.body.appendChild(div);
97
-
98
- renderHook(() => {
99
- const ref = useRef<HTMLDivElement>(div);
100
- useOnClickOutside(ref, handler);
101
- return ref;
102
- });
103
-
104
- const event = new MouseEvent("mousedown", { bubbles: true });
105
- child.dispatchEvent(event);
106
-
107
- expect(handler).not.toHaveBeenCalled();
108
-
109
- document.body.removeChild(div);
110
- });
111
-
112
- it("calls handler when Escape key is pressed", () => {
113
- const handler = jest.fn();
114
- const div = document.createElement("div");
115
- document.body.appendChild(div);
116
-
117
- renderHook(() => {
118
- const ref = useRef<HTMLDivElement>(div);
119
- useOnClickOutside(ref, handler);
120
- return ref;
121
- });
122
-
123
- const event = new KeyboardEvent("keydown", {
124
- key: "Escape",
125
- bubbles: true,
126
- });
127
- document.dispatchEvent(event);
128
-
129
- expect(handler).toHaveBeenCalledTimes(1);
130
-
131
- document.body.removeChild(div);
132
- });
133
-
134
- it("does not call handler for non-Escape key presses", () => {
135
- const handler = jest.fn();
136
- const div = document.createElement("div");
137
- document.body.appendChild(div);
138
-
139
- renderHook(() => {
140
- const ref = useRef<HTMLDivElement>(div);
141
- useOnClickOutside(ref, handler);
142
- return ref;
143
- });
144
-
145
- const event = new KeyboardEvent("keydown", { key: "Enter", bubbles: true });
146
- document.dispatchEvent(event);
147
-
148
- expect(handler).not.toHaveBeenCalled();
149
-
150
- document.body.removeChild(div);
151
- });
152
-
153
- it("uses the latest handler without re-subscribing", () => {
154
- const handler1 = jest.fn();
155
- const handler2 = jest.fn();
156
- const div = document.createElement("div");
157
- document.body.appendChild(div);
158
-
159
- const { rerender } = renderHook(
160
- ({ handler }) => {
161
- const ref = useRef<HTMLDivElement>(div);
162
- useOnClickOutside(ref, handler);
163
- return ref;
164
- },
165
- { initialProps: { handler: handler1 } },
166
- );
167
-
168
- // Clear the spy to only count new subscriptions
169
- addEventListenerSpy.mockClear();
170
-
171
- // Rerender with new handler
172
- rerender({ handler: handler2 });
173
-
174
- // Should not re-subscribe (useEffectEvent handles this)
175
- expect(addEventListenerSpy).not.toHaveBeenCalled();
176
-
177
- // Trigger event - should call the new handler
178
- const event = new KeyboardEvent("keydown", {
179
- key: "Escape",
180
- bubbles: true,
181
- });
182
- document.dispatchEvent(event);
183
-
184
- expect(handler1).not.toHaveBeenCalled();
185
- expect(handler2).toHaveBeenCalledTimes(1);
186
-
187
- document.body.removeChild(div);
188
- });
189
- });
@@ -1,44 +0,0 @@
1
- "use client";
2
-
3
- import type { RefObject } from "react";
4
- import { useEffect, useEffectEvent } from "react";
5
-
6
- export const useOnClickOutside = (
7
- ref: RefObject<HTMLElement | null>,
8
- handler: (event: MouseEvent | TouchEvent | KeyboardEvent) => void,
9
- ) => {
10
- // useEffectEvent creates a stable reference to the handler
11
- // that always calls the latest version
12
- const stableHandler = useEffectEvent(handler);
13
-
14
- useEffect(() => {
15
- const listener = (event: MouseEvent | TouchEvent) => {
16
- // Do nothing if clicking ref's element or descendent elements
17
- if (!ref.current || ref.current.contains(event.target as HTMLElement)) {
18
- return;
19
- }
20
-
21
- event.preventDefault();
22
- event.stopPropagation();
23
- stableHandler(event);
24
- };
25
-
26
- const handleEscapeKeyPress = (event: KeyboardEvent) => {
27
- if (event.key === "Escape") {
28
- event.preventDefault();
29
- event.stopPropagation();
30
- stableHandler(event);
31
- }
32
- };
33
-
34
- document.addEventListener("mousedown", listener);
35
- document.addEventListener("touchstart", listener);
36
- document.addEventListener("keydown", handleEscapeKeyPress);
37
-
38
- return () => {
39
- document.removeEventListener("mousedown", listener);
40
- document.removeEventListener("touchstart", listener);
41
- document.removeEventListener("keydown", handleEscapeKeyPress);
42
- };
43
- }, [ref]);
44
- };
@@ -1 +0,0 @@
1
- export * from "./useOnUnmount";
@@ -1,37 +0,0 @@
1
- import { renderHook } from "@testing-library/react";
2
- import { useOnUnmount } from "./useOnUnmount";
3
-
4
- describe("useOnUnmount", () => {
5
- it("should call the callback on unmount", () => {
6
- const callback = jest.fn();
7
- const { unmount } = renderHook(() => useOnUnmount(callback));
8
-
9
- expect(callback).not.toHaveBeenCalled();
10
-
11
- unmount();
12
-
13
- expect(callback).toHaveBeenCalled();
14
- });
15
-
16
- it("should only use the latest callback", () => {
17
- const firstCallback = jest.fn();
18
- const secondCallback = jest.fn();
19
-
20
- const { rerender, unmount } = renderHook(
21
- ({ callback }) => useOnUnmount(callback),
22
- {
23
- initialProps: { callback: firstCallback },
24
- },
25
- );
26
-
27
- // Change the callback
28
- rerender({ callback: secondCallback });
29
-
30
- // Unmount the component
31
- unmount();
32
-
33
- // Ensure the second callback is called, not the first one
34
- expect(firstCallback).not.toHaveBeenCalled();
35
- expect(secondCallback).toHaveBeenCalled();
36
- });
37
- });
@@ -1,8 +0,0 @@
1
- import { useEffect, useEffectEvent } from "react";
2
-
3
- export function useOnUnmount(callback: () => void) {
4
- // useEffectEvent creates a stable reference that always calls the latest callback
5
- const stableCallback = useEffectEvent(callback);
6
-
7
- useEffect(() => () => stableCallback(), []);
8
- }
@@ -1 +0,0 @@
1
- export * from "./usePrefersReducedMotion";
@@ -1,48 +0,0 @@
1
- import { renderHook } from "@testing-library/react";
2
- import { usePrefersReducedMotion } from "./usePrefersReducedMotion";
3
-
4
- function mockMatchMedia(matches: boolean) {
5
- window.matchMedia = jest.fn().mockReturnValue({
6
- matches,
7
- addEventListener: jest.fn(),
8
- removeEventListener: jest.fn(),
9
- });
10
- }
11
-
12
- describe("usePrefersReducedMotion", () => {
13
- it("should return a boolean", () => {
14
- mockMatchMedia(false);
15
-
16
- const { result } = renderHook(() => usePrefersReducedMotion());
17
- expect(typeof result.current).toBe("boolean");
18
- });
19
-
20
- it("should return false by default", () => {
21
- mockMatchMedia(false);
22
-
23
- const { result } = renderHook(() => usePrefersReducedMotion());
24
- expect(result.current).toBe(false);
25
- });
26
-
27
- it("should return true when the user prefers reduced motion", () => {
28
- mockMatchMedia(true);
29
-
30
- const { result } = renderHook(() => usePrefersReducedMotion());
31
- expect(result.current).toBe(true);
32
- });
33
-
34
- it("should return false when the user does not prefer reduced motion", () => {
35
- mockMatchMedia(false);
36
-
37
- const { result } = renderHook(() => usePrefersReducedMotion());
38
- expect(result.current).toBe(false);
39
- });
40
-
41
- // TODO: These tests need better mocking to work
42
- it.todo(
43
- "should return true when the user prefers reduced motion after changing",
44
- );
45
- it.todo(
46
- "should return false when the user does not prefer reduced motion after changing",
47
- );
48
- });
@@ -1,22 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export function usePrefersReducedMotion(): boolean {
4
- const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
5
-
6
- useEffect(() => {
7
- const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
8
- setPrefersReducedMotion(mediaQuery.matches);
9
-
10
- const listener = (event: MediaQueryListEvent) => {
11
- setPrefersReducedMotion(event.matches);
12
- };
13
-
14
- mediaQuery.addEventListener("change", listener);
15
-
16
- return () => {
17
- mediaQuery.removeEventListener("change", listener);
18
- };
19
- }, []);
20
-
21
- return prefersReducedMotion;
22
- }
@@ -1 +0,0 @@
1
- export * from "./useRenderCount";
@@ -1,26 +0,0 @@
1
- import { renderHook } from "@testing-library/react";
2
- import { useRenderCount } from "./useRenderCount";
3
-
4
- describe("useRenderCount", () => {
5
- it("should return 1 on initial render", () => {
6
- const { result } = renderHook(() => useRenderCount());
7
- expect(result.current).toBe(1);
8
- });
9
-
10
- it("should increment the count on re-render", () => {
11
- const { result, rerender } = renderHook(() => useRenderCount());
12
- expect(result.current).toBe(1);
13
- rerender();
14
- expect(result.current).toBe(2);
15
- rerender();
16
- expect(result.current).toBe(3);
17
- });
18
-
19
- it("should maintain the count across multiple renders", () => {
20
- const { result, rerender } = renderHook(() => useRenderCount());
21
- rerender();
22
- rerender();
23
- rerender();
24
- expect(result.current).toBe(4);
25
- });
26
- });
@@ -1,9 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- export function useRenderCount() {
4
- const count = useRef(1);
5
- useEffect(() => {
6
- count.current += 1;
7
- });
8
- return count.current;
9
- }
@@ -1 +0,0 @@
1
- export * from "./useWindowEvent";