@pyreon/hooks 0.11.4 → 0.11.6
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.
- package/README.md +5 -5
- package/lib/index.d.ts +6 -6
- package/lib/index.js +4 -6
- package/package.json +24 -24
- package/src/__tests__/useBreakpoint.test.ts +48 -48
- package/src/__tests__/useClickOutside.test.ts +31 -31
- package/src/__tests__/useColorScheme.test.ts +22 -22
- package/src/__tests__/useControllableState.test.ts +18 -18
- package/src/__tests__/useDebouncedCallback.test.ts +19 -19
- package/src/__tests__/useDebouncedValue.test.ts +28 -28
- package/src/__tests__/useElementSize.test.ts +21 -21
- package/src/__tests__/useFocus.test.ts +12 -12
- package/src/__tests__/useFocusTrap.test.ts +36 -36
- package/src/__tests__/useHover.test.ts +13 -13
- package/src/__tests__/useIntersection.test.ts +20 -20
- package/src/__tests__/useInterval.test.ts +7 -7
- package/src/__tests__/useIsomorphicLayoutEffect.test.ts +5 -5
- package/src/__tests__/useKeyboard.test.ts +38 -38
- package/src/__tests__/useLatest.test.ts +11 -11
- package/src/__tests__/useMediaQuery.test.ts +29 -29
- package/src/__tests__/useMergedRef.test.ts +10 -10
- package/src/__tests__/usePrevious.test.ts +20 -20
- package/src/__tests__/useReducedMotion.test.ts +15 -15
- package/src/__tests__/useRootSize.test.ts +9 -9
- package/src/__tests__/useScrollLock.test.ts +33 -33
- package/src/__tests__/useSpacing.test.ts +11 -11
- package/src/__tests__/useThemeValue.test.ts +5 -5
- package/src/__tests__/useThrottledCallback.test.ts +16 -16
- package/src/__tests__/useTimeout.test.ts +8 -8
- package/src/__tests__/useToggle.test.ts +14 -14
- package/src/__tests__/useUpdateEffect.test.ts +8 -8
- package/src/__tests__/useWindowResize.test.ts +34 -34
- package/src/index.ts +56 -56
- package/src/useBreakpoint.ts +6 -6
- package/src/useClickOutside.ts +5 -5
- package/src/useClipboard.ts +2 -2
- package/src/useColorScheme.ts +5 -5
- package/src/useControllableState.ts +2 -2
- package/src/useDebouncedCallback.ts +1 -1
- package/src/useDebouncedValue.ts +2 -2
- package/src/useDialog.ts +4 -4
- package/src/useElementSize.ts +2 -2
- package/src/useEventListener.ts +2 -2
- package/src/useFocus.ts +1 -1
- package/src/useFocusTrap.ts +4 -4
- package/src/useHover.ts +1 -1
- package/src/useInfiniteScroll.ts +10 -10
- package/src/useIntersection.ts +2 -2
- package/src/useInterval.ts +3 -4
- package/src/useIsomorphicLayoutEffect.ts +2 -2
- package/src/useKeyboard.ts +3 -3
- package/src/useMediaQuery.ts +4 -4
- package/src/useMergedRef.ts +1 -1
- package/src/useOnline.ts +6 -6
- package/src/usePrevious.ts +1 -1
- package/src/useReducedMotion.ts +2 -2
- package/src/useRootSize.ts +1 -1
- package/src/useScrollLock.ts +3 -3
- package/src/useSpacing.ts +1 -1
- package/src/useThemeValue.ts +2 -2
- package/src/useThrottledCallback.ts +2 -2
- package/src/useTimeAgo.ts +15 -15
- package/src/useTimeout.ts +3 -4
- package/src/useToggle.ts +1 -1
- package/src/useUpdateEffect.ts +2 -2
- package/src/useWindowResize.ts +6 -6
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
let mountCallbacks: Array<() => void> = []
|
|
4
4
|
let unmountCallbacks: Array<() => void> = []
|
|
5
5
|
|
|
6
|
-
vi.mock(
|
|
6
|
+
vi.mock('@pyreon/core', () => ({
|
|
7
7
|
onMount: (fn: () => unknown) => {
|
|
8
8
|
mountCallbacks.push(fn as () => void)
|
|
9
9
|
},
|
|
@@ -12,9 +12,9 @@ vi.mock("@pyreon/core", () => ({
|
|
|
12
12
|
},
|
|
13
13
|
}))
|
|
14
14
|
|
|
15
|
-
import { useFocusTrap } from
|
|
15
|
+
import { useFocusTrap } from '../useFocusTrap'
|
|
16
16
|
|
|
17
|
-
describe(
|
|
17
|
+
describe('useFocusTrap', () => {
|
|
18
18
|
let container: HTMLDivElement
|
|
19
19
|
let btn1: HTMLButtonElement
|
|
20
20
|
let btn2: HTMLButtonElement
|
|
@@ -23,10 +23,10 @@ describe("useFocusTrap", () => {
|
|
|
23
23
|
beforeEach(() => {
|
|
24
24
|
mountCallbacks = []
|
|
25
25
|
unmountCallbacks = []
|
|
26
|
-
container = document.createElement(
|
|
27
|
-
btn1 = document.createElement(
|
|
28
|
-
btn2 = document.createElement(
|
|
29
|
-
btn3 = document.createElement(
|
|
26
|
+
container = document.createElement('div')
|
|
27
|
+
btn1 = document.createElement('button')
|
|
28
|
+
btn2 = document.createElement('button')
|
|
29
|
+
btn3 = document.createElement('button')
|
|
30
30
|
container.append(btn1, btn2, btn3)
|
|
31
31
|
document.body.appendChild(container)
|
|
32
32
|
})
|
|
@@ -35,84 +35,84 @@ describe("useFocusTrap", () => {
|
|
|
35
35
|
document.body.removeChild(container)
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
it(
|
|
38
|
+
it('wraps focus from last to first on Tab', () => {
|
|
39
39
|
useFocusTrap(() => container)
|
|
40
40
|
mountCallbacks.forEach((cb) => {
|
|
41
41
|
cb()
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
btn3.focus()
|
|
45
|
-
const event = new KeyboardEvent(
|
|
46
|
-
const prevented = vi.spyOn(event,
|
|
45
|
+
const event = new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
|
|
46
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
47
47
|
document.dispatchEvent(event)
|
|
48
48
|
|
|
49
49
|
expect(prevented).toHaveBeenCalled()
|
|
50
50
|
expect(document.activeElement).toBe(btn1)
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
it(
|
|
53
|
+
it('wraps focus from first to last on Shift+Tab', () => {
|
|
54
54
|
useFocusTrap(() => container)
|
|
55
55
|
mountCallbacks.forEach((cb) => {
|
|
56
56
|
cb()
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
btn1.focus()
|
|
60
|
-
const event = new KeyboardEvent(
|
|
61
|
-
key:
|
|
60
|
+
const event = new KeyboardEvent('keydown', {
|
|
61
|
+
key: 'Tab',
|
|
62
62
|
shiftKey: true,
|
|
63
63
|
bubbles: true,
|
|
64
64
|
})
|
|
65
|
-
const prevented = vi.spyOn(event,
|
|
65
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
66
66
|
document.dispatchEvent(event)
|
|
67
67
|
|
|
68
68
|
expect(prevented).toHaveBeenCalled()
|
|
69
69
|
expect(document.activeElement).toBe(btn3)
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
it(
|
|
72
|
+
it('does not wrap when Tab on middle element', () => {
|
|
73
73
|
useFocusTrap(() => container)
|
|
74
74
|
mountCallbacks.forEach((cb) => {
|
|
75
75
|
cb()
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
btn2.focus()
|
|
79
|
-
const event = new KeyboardEvent(
|
|
80
|
-
const prevented = vi.spyOn(event,
|
|
79
|
+
const event = new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
|
|
80
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
81
81
|
document.dispatchEvent(event)
|
|
82
82
|
|
|
83
83
|
expect(prevented).not.toHaveBeenCalled()
|
|
84
84
|
})
|
|
85
85
|
|
|
86
|
-
it(
|
|
86
|
+
it('ignores non-Tab keys', () => {
|
|
87
87
|
useFocusTrap(() => container)
|
|
88
88
|
mountCallbacks.forEach((cb) => {
|
|
89
89
|
cb()
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
btn3.focus()
|
|
93
|
-
const event = new KeyboardEvent(
|
|
94
|
-
const prevented = vi.spyOn(event,
|
|
93
|
+
const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })
|
|
94
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
95
95
|
document.dispatchEvent(event)
|
|
96
96
|
|
|
97
97
|
expect(prevented).not.toHaveBeenCalled()
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
-
it(
|
|
100
|
+
it('does nothing when element is null', () => {
|
|
101
101
|
useFocusTrap(() => null)
|
|
102
102
|
mountCallbacks.forEach((cb) => {
|
|
103
103
|
cb()
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
-
const event = new KeyboardEvent(
|
|
107
|
-
const prevented = vi.spyOn(event,
|
|
106
|
+
const event = new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
|
|
107
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
108
108
|
document.dispatchEvent(event)
|
|
109
109
|
|
|
110
110
|
expect(prevented).not.toHaveBeenCalled()
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
-
it(
|
|
114
|
-
const emptyContainer = document.createElement(
|
|
115
|
-
emptyContainer.appendChild(document.createElement(
|
|
113
|
+
it('does nothing when container has no focusable children', () => {
|
|
114
|
+
const emptyContainer = document.createElement('div')
|
|
115
|
+
emptyContainer.appendChild(document.createElement('div'))
|
|
116
116
|
document.body.appendChild(emptyContainer)
|
|
117
117
|
|
|
118
118
|
useFocusTrap(() => emptyContainer)
|
|
@@ -120,16 +120,16 @@ describe("useFocusTrap", () => {
|
|
|
120
120
|
cb()
|
|
121
121
|
})
|
|
122
122
|
|
|
123
|
-
const event = new KeyboardEvent(
|
|
124
|
-
const prevented = vi.spyOn(event,
|
|
123
|
+
const event = new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
|
|
124
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
125
125
|
document.dispatchEvent(event)
|
|
126
126
|
|
|
127
127
|
expect(prevented).not.toHaveBeenCalled()
|
|
128
128
|
document.body.removeChild(emptyContainer)
|
|
129
129
|
})
|
|
130
130
|
|
|
131
|
-
it(
|
|
132
|
-
const removeSpy = vi.spyOn(document,
|
|
131
|
+
it('cleans up event listener on unmount', () => {
|
|
132
|
+
const removeSpy = vi.spyOn(document, 'removeEventListener')
|
|
133
133
|
useFocusTrap(() => container)
|
|
134
134
|
mountCallbacks.forEach((cb) => {
|
|
135
135
|
cb()
|
|
@@ -138,23 +138,23 @@ describe("useFocusTrap", () => {
|
|
|
138
138
|
cb()
|
|
139
139
|
})
|
|
140
140
|
|
|
141
|
-
expect(removeSpy).toHaveBeenCalledWith(
|
|
141
|
+
expect(removeSpy).toHaveBeenCalledWith('keydown', expect.any(Function))
|
|
142
142
|
removeSpy.mockRestore()
|
|
143
143
|
})
|
|
144
144
|
|
|
145
|
-
it(
|
|
145
|
+
it('does not prevent default when Shift+Tab on non-first element', () => {
|
|
146
146
|
useFocusTrap(() => container)
|
|
147
147
|
mountCallbacks.forEach((cb) => {
|
|
148
148
|
cb()
|
|
149
149
|
})
|
|
150
150
|
|
|
151
151
|
btn2.focus()
|
|
152
|
-
const event = new KeyboardEvent(
|
|
153
|
-
key:
|
|
152
|
+
const event = new KeyboardEvent('keydown', {
|
|
153
|
+
key: 'Tab',
|
|
154
154
|
shiftKey: true,
|
|
155
155
|
bubbles: true,
|
|
156
156
|
})
|
|
157
|
-
const prevented = vi.spyOn(event,
|
|
157
|
+
const prevented = vi.spyOn(event, 'preventDefault')
|
|
158
158
|
document.dispatchEvent(event)
|
|
159
159
|
|
|
160
160
|
expect(prevented).not.toHaveBeenCalled()
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { useHover } from
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { useHover } from '../useHover'
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
4
|
+
describe('useHover', () => {
|
|
5
|
+
it('initializes with hovered=false', () => {
|
|
6
6
|
const { hovered } = useHover()
|
|
7
7
|
expect(hovered()).toBe(false)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
it(
|
|
10
|
+
it('sets hovered to true on mouseEnter', () => {
|
|
11
11
|
const { hovered, props } = useHover()
|
|
12
12
|
props.onMouseEnter()
|
|
13
13
|
expect(hovered()).toBe(true)
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
it(
|
|
16
|
+
it('sets hovered to false on mouseLeave', () => {
|
|
17
17
|
const { hovered, props } = useHover()
|
|
18
18
|
props.onMouseEnter()
|
|
19
19
|
expect(hovered()).toBe(true)
|
|
@@ -21,7 +21,7 @@ describe("useHover", () => {
|
|
|
21
21
|
expect(hovered()).toBe(false)
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
it(
|
|
24
|
+
it('toggles hover state correctly through multiple cycles', () => {
|
|
25
25
|
const { hovered, props } = useHover()
|
|
26
26
|
props.onMouseEnter()
|
|
27
27
|
expect(hovered()).toBe(true)
|
|
@@ -33,26 +33,26 @@ describe("useHover", () => {
|
|
|
33
33
|
expect(hovered()).toBe(false)
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
it(
|
|
36
|
+
it('calling mouseLeave when already not hovered is safe', () => {
|
|
37
37
|
const { hovered, props } = useHover()
|
|
38
38
|
props.onMouseLeave()
|
|
39
39
|
expect(hovered()).toBe(false)
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
it(
|
|
42
|
+
it('calling mouseEnter multiple times stays true', () => {
|
|
43
43
|
const { hovered, props } = useHover()
|
|
44
44
|
props.onMouseEnter()
|
|
45
45
|
props.onMouseEnter()
|
|
46
46
|
expect(hovered()).toBe(true)
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
it(
|
|
49
|
+
it('returns props object with onMouseEnter and onMouseLeave', () => {
|
|
50
50
|
const { props } = useHover()
|
|
51
|
-
expect(typeof props.onMouseEnter).toBe(
|
|
52
|
-
expect(typeof props.onMouseLeave).toBe(
|
|
51
|
+
expect(typeof props.onMouseEnter).toBe('function')
|
|
52
|
+
expect(typeof props.onMouseLeave).toBe('function')
|
|
53
53
|
})
|
|
54
54
|
|
|
55
|
-
it(
|
|
55
|
+
it('returns stable handler references', () => {
|
|
56
56
|
const { props } = useHover()
|
|
57
57
|
const enter = props.onMouseEnter
|
|
58
58
|
const leave = props.onMouseLeave
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
let mountCallbacks: Array<() => unknown> = []
|
|
4
4
|
let unmountCallbacks: Array<() => void> = []
|
|
5
5
|
|
|
6
|
-
vi.mock(
|
|
6
|
+
vi.mock('@pyreon/core', () => ({
|
|
7
7
|
onMount: (fn: () => unknown) => {
|
|
8
8
|
mountCallbacks.push(fn)
|
|
9
9
|
},
|
|
@@ -12,9 +12,9 @@ vi.mock("@pyreon/core", () => ({
|
|
|
12
12
|
},
|
|
13
13
|
}))
|
|
14
14
|
|
|
15
|
-
import { useIntersection } from
|
|
15
|
+
import { useIntersection } from '../useIntersection'
|
|
16
16
|
|
|
17
|
-
describe(
|
|
17
|
+
describe('useIntersection', () => {
|
|
18
18
|
let intersectionCallback: ((entries: IntersectionObserverEntry[]) => void) | undefined
|
|
19
19
|
let observeSpy: ReturnType<typeof vi.fn>
|
|
20
20
|
let disconnectSpy: ReturnType<typeof vi.fn>
|
|
@@ -37,7 +37,7 @@ describe("useIntersection", () => {
|
|
|
37
37
|
unobserve: vi.fn(),
|
|
38
38
|
disconnect: disconnectSpy,
|
|
39
39
|
root: options?.root ?? null,
|
|
40
|
-
rootMargin: options?.rootMargin ??
|
|
40
|
+
rootMargin: options?.rootMargin ?? '0px',
|
|
41
41
|
thresholds: Array.isArray(options?.threshold)
|
|
42
42
|
? options.threshold
|
|
43
43
|
: [options?.threshold ?? 0],
|
|
@@ -46,13 +46,13 @@ describe("useIntersection", () => {
|
|
|
46
46
|
}) as unknown as typeof IntersectionObserver
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
it(
|
|
49
|
+
it('returns null initially', () => {
|
|
50
50
|
const entry = useIntersection(() => null)
|
|
51
51
|
expect(entry()).toBeNull()
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
it(
|
|
55
|
-
const el = document.createElement(
|
|
54
|
+
it('observes the element on mount', () => {
|
|
55
|
+
const el = document.createElement('div')
|
|
56
56
|
useIntersection(() => el)
|
|
57
57
|
mountCallbacks.forEach((cb) => {
|
|
58
58
|
cb()
|
|
@@ -61,7 +61,7 @@ describe("useIntersection", () => {
|
|
|
61
61
|
expect(observeSpy).toHaveBeenCalledWith(el)
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
it(
|
|
64
|
+
it('does not observe when element is null', () => {
|
|
65
65
|
useIntersection(() => null)
|
|
66
66
|
mountCallbacks.forEach((cb) => {
|
|
67
67
|
cb()
|
|
@@ -70,8 +70,8 @@ describe("useIntersection", () => {
|
|
|
70
70
|
expect(observeSpy).not.toHaveBeenCalled()
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
it(
|
|
74
|
-
const el = document.createElement(
|
|
73
|
+
it('updates entry when intersection changes', () => {
|
|
74
|
+
const el = document.createElement('div')
|
|
75
75
|
const entrySignal = useIntersection(() => el)
|
|
76
76
|
mountCallbacks.forEach((cb) => {
|
|
77
77
|
cb()
|
|
@@ -92,9 +92,9 @@ describe("useIntersection", () => {
|
|
|
92
92
|
expect(entrySignal()?.isIntersecting).toBe(true)
|
|
93
93
|
})
|
|
94
94
|
|
|
95
|
-
it(
|
|
96
|
-
const el = document.createElement(
|
|
97
|
-
const options = { threshold: 0.5, rootMargin:
|
|
95
|
+
it('passes options to IntersectionObserver', () => {
|
|
96
|
+
const el = document.createElement('div')
|
|
97
|
+
const options = { threshold: 0.5, rootMargin: '10px' }
|
|
98
98
|
useIntersection(() => el, options)
|
|
99
99
|
mountCallbacks.forEach((cb) => {
|
|
100
100
|
cb()
|
|
@@ -103,8 +103,8 @@ describe("useIntersection", () => {
|
|
|
103
103
|
expect(IntersectionObserver).toHaveBeenCalledWith(expect.any(Function), options)
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
-
it(
|
|
107
|
-
const el = document.createElement(
|
|
106
|
+
it('disconnects observer on unmount', () => {
|
|
107
|
+
const el = document.createElement('div')
|
|
108
108
|
useIntersection(() => el)
|
|
109
109
|
mountCallbacks.forEach((cb) => {
|
|
110
110
|
cb()
|
|
@@ -116,8 +116,8 @@ describe("useIntersection", () => {
|
|
|
116
116
|
expect(disconnectSpy).toHaveBeenCalled()
|
|
117
117
|
})
|
|
118
118
|
|
|
119
|
-
it(
|
|
120
|
-
const el = document.createElement(
|
|
119
|
+
it('does not crash when callback has empty entries', () => {
|
|
120
|
+
const el = document.createElement('div')
|
|
121
121
|
const entrySignal = useIntersection(() => el)
|
|
122
122
|
mountCallbacks.forEach((cb) => {
|
|
123
123
|
cb()
|
|
@@ -128,8 +128,8 @@ describe("useIntersection", () => {
|
|
|
128
128
|
expect(entrySignal()).toBeNull()
|
|
129
129
|
})
|
|
130
130
|
|
|
131
|
-
it(
|
|
132
|
-
const el = document.createElement(
|
|
131
|
+
it('updates to latest entry on subsequent intersections', () => {
|
|
132
|
+
const el = document.createElement('div')
|
|
133
133
|
const entrySignal = useIntersection(() => el)
|
|
134
134
|
mountCallbacks.forEach((cb) => {
|
|
135
135
|
cb()
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from
|
|
2
|
-
import { useInterval } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { useInterval } from '../useInterval'
|
|
3
3
|
|
|
4
4
|
// Mock onUnmount since it requires component lifecycle context
|
|
5
|
-
vi.mock(
|
|
5
|
+
vi.mock('@pyreon/core', () => ({
|
|
6
6
|
onMount: (fn: () => void) => fn(),
|
|
7
7
|
onUnmount: (_fn: () => void) => {
|
|
8
8
|
/* no-op */
|
|
9
9
|
},
|
|
10
10
|
}))
|
|
11
11
|
|
|
12
|
-
describe(
|
|
12
|
+
describe('useInterval', () => {
|
|
13
13
|
beforeEach(() => vi.useFakeTimers())
|
|
14
14
|
afterEach(() => vi.useRealTimers())
|
|
15
15
|
|
|
16
|
-
it(
|
|
16
|
+
it('calls callback at the specified interval', () => {
|
|
17
17
|
const fn = vi.fn()
|
|
18
18
|
useInterval(fn, 100)
|
|
19
19
|
|
|
@@ -24,7 +24,7 @@ describe("useInterval", () => {
|
|
|
24
24
|
expect(fn).toHaveBeenCalledTimes(2)
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
it(
|
|
27
|
+
it('does not call callback when delay is null', () => {
|
|
28
28
|
const fn = vi.fn()
|
|
29
29
|
useInterval(fn, null)
|
|
30
30
|
|
|
@@ -32,7 +32,7 @@ describe("useInterval", () => {
|
|
|
32
32
|
expect(fn).not.toHaveBeenCalled()
|
|
33
33
|
})
|
|
34
34
|
|
|
35
|
-
it(
|
|
35
|
+
it('calls the latest callback', () => {
|
|
36
36
|
let value = 0
|
|
37
37
|
let currentCb = () => {
|
|
38
38
|
value = 1
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { onMount } from
|
|
2
|
-
import { describe, expect, it } from
|
|
3
|
-
import useIsomorphicLayoutEffect from
|
|
1
|
+
import { onMount } from '@pyreon/core'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import useIsomorphicLayoutEffect from '../useIsomorphicLayoutEffect'
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
it(
|
|
5
|
+
describe('useIsomorphicLayoutEffect', () => {
|
|
6
|
+
it('is onMount in a browser environment', () => {
|
|
7
7
|
expect(useIsomorphicLayoutEffect).toBe(onMount)
|
|
8
8
|
})
|
|
9
9
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
let mountCallbacks: Array<() => void> = []
|
|
4
4
|
let unmountCallbacks: Array<() => void> = []
|
|
5
5
|
|
|
6
|
-
vi.mock(
|
|
6
|
+
vi.mock('@pyreon/core', () => ({
|
|
7
7
|
onMount: (fn: () => unknown) => {
|
|
8
8
|
mountCallbacks.push(fn as () => void)
|
|
9
9
|
},
|
|
@@ -12,83 +12,83 @@ vi.mock("@pyreon/core", () => ({
|
|
|
12
12
|
},
|
|
13
13
|
}))
|
|
14
14
|
|
|
15
|
-
import { useKeyboard } from
|
|
15
|
+
import { useKeyboard } from '../useKeyboard'
|
|
16
16
|
|
|
17
|
-
describe(
|
|
17
|
+
describe('useKeyboard', () => {
|
|
18
18
|
beforeEach(() => {
|
|
19
19
|
mountCallbacks = []
|
|
20
20
|
unmountCallbacks = []
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
it(
|
|
23
|
+
it('calls handler when the target key is pressed', () => {
|
|
24
24
|
const handler = vi.fn()
|
|
25
|
-
useKeyboard(
|
|
25
|
+
useKeyboard('Enter', handler)
|
|
26
26
|
mountCallbacks.forEach((cb) => {
|
|
27
27
|
cb()
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
const event = new KeyboardEvent(
|
|
30
|
+
const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })
|
|
31
31
|
document.dispatchEvent(event)
|
|
32
32
|
|
|
33
33
|
expect(handler).toHaveBeenCalledTimes(1)
|
|
34
34
|
expect(handler).toHaveBeenCalledWith(event)
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
it(
|
|
37
|
+
it('does not call handler for different keys', () => {
|
|
38
38
|
const handler = vi.fn()
|
|
39
|
-
useKeyboard(
|
|
39
|
+
useKeyboard('Enter', handler)
|
|
40
40
|
mountCallbacks.forEach((cb) => {
|
|
41
41
|
cb()
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
const event = new KeyboardEvent(
|
|
44
|
+
const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })
|
|
45
45
|
document.dispatchEvent(event)
|
|
46
46
|
|
|
47
47
|
expect(handler).not.toHaveBeenCalled()
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
it(
|
|
50
|
+
it('listens for keydown by default', () => {
|
|
51
51
|
const handler = vi.fn()
|
|
52
|
-
const addSpy = vi.spyOn(document,
|
|
53
|
-
useKeyboard(
|
|
52
|
+
const addSpy = vi.spyOn(document, 'addEventListener')
|
|
53
|
+
useKeyboard('Escape', handler)
|
|
54
54
|
mountCallbacks.forEach((cb) => {
|
|
55
55
|
cb()
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
expect(addSpy).toHaveBeenCalledWith(
|
|
58
|
+
expect(addSpy).toHaveBeenCalledWith('keydown', expect.any(Function))
|
|
59
59
|
addSpy.mockRestore()
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
it(
|
|
62
|
+
it('supports keyup event option', () => {
|
|
63
63
|
const handler = vi.fn()
|
|
64
|
-
useKeyboard(
|
|
64
|
+
useKeyboard('Space', handler, { event: 'keyup' })
|
|
65
65
|
mountCallbacks.forEach((cb) => {
|
|
66
66
|
cb()
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
const keydownEvent = new KeyboardEvent(
|
|
69
|
+
const keydownEvent = new KeyboardEvent('keydown', { key: 'Space', bubbles: true })
|
|
70
70
|
document.dispatchEvent(keydownEvent)
|
|
71
71
|
expect(handler).not.toHaveBeenCalled()
|
|
72
72
|
|
|
73
|
-
const keyupEvent = new KeyboardEvent(
|
|
73
|
+
const keyupEvent = new KeyboardEvent('keyup', { key: 'Space', bubbles: true })
|
|
74
74
|
document.dispatchEvent(keyupEvent)
|
|
75
75
|
expect(handler).toHaveBeenCalledTimes(1)
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it(
|
|
78
|
+
it('supports custom target', () => {
|
|
79
79
|
const handler = vi.fn()
|
|
80
|
-
const customTarget = document.createElement(
|
|
80
|
+
const customTarget = document.createElement('div')
|
|
81
81
|
document.body.appendChild(customTarget)
|
|
82
|
-
const addSpy = vi.spyOn(customTarget,
|
|
82
|
+
const addSpy = vi.spyOn(customTarget, 'addEventListener')
|
|
83
83
|
|
|
84
|
-
useKeyboard(
|
|
84
|
+
useKeyboard('Enter', handler, { target: customTarget })
|
|
85
85
|
mountCallbacks.forEach((cb) => {
|
|
86
86
|
cb()
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
-
expect(addSpy).toHaveBeenCalledWith(
|
|
89
|
+
expect(addSpy).toHaveBeenCalledWith('keydown', expect.any(Function))
|
|
90
90
|
|
|
91
|
-
const event = new KeyboardEvent(
|
|
91
|
+
const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })
|
|
92
92
|
customTarget.dispatchEvent(event)
|
|
93
93
|
expect(handler).toHaveBeenCalledTimes(1)
|
|
94
94
|
|
|
@@ -96,10 +96,10 @@ describe("useKeyboard", () => {
|
|
|
96
96
|
document.body.removeChild(customTarget)
|
|
97
97
|
})
|
|
98
98
|
|
|
99
|
-
it(
|
|
99
|
+
it('removes listeners on unmount', () => {
|
|
100
100
|
const handler = vi.fn()
|
|
101
|
-
const removeSpy = vi.spyOn(document,
|
|
102
|
-
useKeyboard(
|
|
101
|
+
const removeSpy = vi.spyOn(document, 'removeEventListener')
|
|
102
|
+
useKeyboard('Enter', handler)
|
|
103
103
|
mountCallbacks.forEach((cb) => {
|
|
104
104
|
cb()
|
|
105
105
|
})
|
|
@@ -107,16 +107,16 @@ describe("useKeyboard", () => {
|
|
|
107
107
|
cb()
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
expect(removeSpy).toHaveBeenCalledWith(
|
|
110
|
+
expect(removeSpy).toHaveBeenCalledWith('keydown', expect.any(Function))
|
|
111
111
|
removeSpy.mockRestore()
|
|
112
112
|
})
|
|
113
113
|
|
|
114
|
-
it(
|
|
114
|
+
it('removes listeners from custom target on unmount', () => {
|
|
115
115
|
const handler = vi.fn()
|
|
116
|
-
const customTarget = document.createElement(
|
|
117
|
-
const removeSpy = vi.spyOn(customTarget,
|
|
116
|
+
const customTarget = document.createElement('div')
|
|
117
|
+
const removeSpy = vi.spyOn(customTarget, 'removeEventListener')
|
|
118
118
|
|
|
119
|
-
useKeyboard(
|
|
119
|
+
useKeyboard('Enter', handler, { target: customTarget })
|
|
120
120
|
mountCallbacks.forEach((cb) => {
|
|
121
121
|
cb()
|
|
122
122
|
})
|
|
@@ -124,20 +124,20 @@ describe("useKeyboard", () => {
|
|
|
124
124
|
cb()
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
expect(removeSpy).toHaveBeenCalledWith(
|
|
127
|
+
expect(removeSpy).toHaveBeenCalledWith('keydown', expect.any(Function))
|
|
128
128
|
removeSpy.mockRestore()
|
|
129
129
|
})
|
|
130
130
|
|
|
131
|
-
it(
|
|
131
|
+
it('calls handler multiple times for repeated key presses', () => {
|
|
132
132
|
const handler = vi.fn()
|
|
133
|
-
useKeyboard(
|
|
133
|
+
useKeyboard('a', handler)
|
|
134
134
|
mountCallbacks.forEach((cb) => {
|
|
135
135
|
cb()
|
|
136
136
|
})
|
|
137
137
|
|
|
138
|
-
document.dispatchEvent(new KeyboardEvent(
|
|
139
|
-
document.dispatchEvent(new KeyboardEvent(
|
|
140
|
-
document.dispatchEvent(new KeyboardEvent(
|
|
138
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' }))
|
|
139
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' }))
|
|
140
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' }))
|
|
141
141
|
|
|
142
142
|
expect(handler).toHaveBeenCalledTimes(3)
|
|
143
143
|
})
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { useLatest } from
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { useLatest } from '../useLatest'
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
4
|
+
describe('useLatest', () => {
|
|
5
|
+
it('returns a ref with the current value', () => {
|
|
6
6
|
const ref = useLatest(42)
|
|
7
7
|
expect(ref.current).toBe(42)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
it(
|
|
11
|
-
const ref = useLatest(
|
|
12
|
-
expect(ref.current).toBe(
|
|
10
|
+
it('ref object can be manually updated', () => {
|
|
11
|
+
const ref = useLatest('a')
|
|
12
|
+
expect(ref.current).toBe('a')
|
|
13
13
|
|
|
14
14
|
// In Pyreon, since component runs once, the caller updates .current manually
|
|
15
|
-
;(ref as { current: string }).current =
|
|
16
|
-
expect(ref.current).toBe(
|
|
15
|
+
;(ref as { current: string }).current = 'b'
|
|
16
|
+
expect(ref.current).toBe('b')
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
it(
|
|
20
|
-
const ref = useLatest(
|
|
19
|
+
it('returns a stable ref identity', () => {
|
|
20
|
+
const ref = useLatest('hello')
|
|
21
21
|
const same = ref
|
|
22
22
|
expect(same).toBe(ref)
|
|
23
23
|
})
|