@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 { 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 { useColorScheme } from
|
|
15
|
+
import { useColorScheme } from '../useColorScheme'
|
|
16
16
|
|
|
17
|
-
describe(
|
|
17
|
+
describe('useColorScheme', () => {
|
|
18
18
|
let changeListeners: Map<string, (e: MediaQueryListEvent) => void>
|
|
19
19
|
|
|
20
20
|
beforeEach(() => {
|
|
@@ -22,35 +22,35 @@ describe("useColorScheme", () => {
|
|
|
22
22
|
unmountCallbacks = []
|
|
23
23
|
changeListeners = new Map()
|
|
24
24
|
|
|
25
|
-
Object.defineProperty(window,
|
|
25
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
26
26
|
writable: true,
|
|
27
27
|
value: vi.fn((query: string) => ({
|
|
28
28
|
matches: false,
|
|
29
29
|
media: query,
|
|
30
30
|
addEventListener: vi.fn((event: string, cb: (e: MediaQueryListEvent) => void) => {
|
|
31
|
-
if (event ===
|
|
31
|
+
if (event === 'change') changeListeners.set(query, cb)
|
|
32
32
|
}),
|
|
33
33
|
removeEventListener: vi.fn(),
|
|
34
34
|
})),
|
|
35
35
|
})
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
it(
|
|
38
|
+
it('returns light by default', () => {
|
|
39
39
|
const scheme = useColorScheme()
|
|
40
40
|
mountCallbacks.forEach((cb) => {
|
|
41
41
|
cb()
|
|
42
42
|
})
|
|
43
|
-
expect(scheme()).toBe(
|
|
43
|
+
expect(scheme()).toBe('light')
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
it(
|
|
47
|
-
Object.defineProperty(window,
|
|
46
|
+
it('returns dark when prefers-color-scheme is dark', () => {
|
|
47
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
48
48
|
writable: true,
|
|
49
49
|
value: vi.fn((query: string) => ({
|
|
50
50
|
matches: true,
|
|
51
51
|
media: query,
|
|
52
52
|
addEventListener: vi.fn((event: string, cb: (e: MediaQueryListEvent) => void) => {
|
|
53
|
-
if (event ===
|
|
53
|
+
if (event === 'change') changeListeners.set(query, cb)
|
|
54
54
|
}),
|
|
55
55
|
removeEventListener: vi.fn(),
|
|
56
56
|
})),
|
|
@@ -60,29 +60,29 @@ describe("useColorScheme", () => {
|
|
|
60
60
|
mountCallbacks.forEach((cb) => {
|
|
61
61
|
cb()
|
|
62
62
|
})
|
|
63
|
-
expect(scheme()).toBe(
|
|
63
|
+
expect(scheme()).toBe('dark')
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
it(
|
|
66
|
+
it('updates when color scheme changes from light to dark', () => {
|
|
67
67
|
const scheme = useColorScheme()
|
|
68
68
|
mountCallbacks.forEach((cb) => {
|
|
69
69
|
cb()
|
|
70
70
|
})
|
|
71
|
-
expect(scheme()).toBe(
|
|
71
|
+
expect(scheme()).toBe('light')
|
|
72
72
|
|
|
73
|
-
const listener = changeListeners.get(
|
|
73
|
+
const listener = changeListeners.get('(prefers-color-scheme: dark)')
|
|
74
74
|
listener?.({ matches: true } as MediaQueryListEvent)
|
|
75
|
-
expect(scheme()).toBe(
|
|
75
|
+
expect(scheme()).toBe('dark')
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it(
|
|
79
|
-
Object.defineProperty(window,
|
|
78
|
+
it('updates when color scheme changes from dark to light', () => {
|
|
79
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
80
80
|
writable: true,
|
|
81
81
|
value: vi.fn((query: string) => ({
|
|
82
82
|
matches: true,
|
|
83
83
|
media: query,
|
|
84
84
|
addEventListener: vi.fn((event: string, cb: (e: MediaQueryListEvent) => void) => {
|
|
85
|
-
if (event ===
|
|
85
|
+
if (event === 'change') changeListeners.set(query, cb)
|
|
86
86
|
}),
|
|
87
87
|
removeEventListener: vi.fn(),
|
|
88
88
|
})),
|
|
@@ -92,10 +92,10 @@ describe("useColorScheme", () => {
|
|
|
92
92
|
mountCallbacks.forEach((cb) => {
|
|
93
93
|
cb()
|
|
94
94
|
})
|
|
95
|
-
expect(scheme()).toBe(
|
|
95
|
+
expect(scheme()).toBe('dark')
|
|
96
96
|
|
|
97
|
-
const listener = changeListeners.get(
|
|
97
|
+
const listener = changeListeners.get('(prefers-color-scheme: dark)')
|
|
98
98
|
listener?.({ matches: false } as MediaQueryListEvent)
|
|
99
|
-
expect(scheme()).toBe(
|
|
99
|
+
expect(scheme()).toBe('light')
|
|
100
100
|
})
|
|
101
101
|
})
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from
|
|
2
|
-
import { useControllableState } from
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { useControllableState } from '../useControllableState'
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
6
|
-
const [value] = useControllableState({ defaultValue:
|
|
7
|
-
expect(value()).toBe(
|
|
4
|
+
describe('useControllableState', () => {
|
|
5
|
+
it('uses defaultValue when uncontrolled', () => {
|
|
6
|
+
const [value] = useControllableState({ defaultValue: 'hello' })
|
|
7
|
+
expect(value()).toBe('hello')
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
it(
|
|
10
|
+
it('updates internal state when uncontrolled', () => {
|
|
11
11
|
const [value, setValue] = useControllableState({ defaultValue: 0 })
|
|
12
12
|
setValue(5)
|
|
13
13
|
expect(value()).toBe(5)
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
it(
|
|
17
|
-
const [value] = useControllableState({ value:
|
|
18
|
-
expect(value()).toBe(
|
|
16
|
+
it('uses value when controlled', () => {
|
|
17
|
+
const [value] = useControllableState({ value: 'controlled', defaultValue: 'default' })
|
|
18
|
+
expect(value()).toBe('controlled')
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
it(
|
|
21
|
+
it('does not update internal state when controlled', () => {
|
|
22
22
|
const onChange = vi.fn()
|
|
23
23
|
const [value, setValue] = useControllableState({
|
|
24
|
-
value:
|
|
25
|
-
defaultValue:
|
|
24
|
+
value: 'controlled',
|
|
25
|
+
defaultValue: 'default',
|
|
26
26
|
onChange,
|
|
27
27
|
})
|
|
28
|
-
setValue(
|
|
29
|
-
expect(value()).toBe(
|
|
30
|
-
expect(onChange).toHaveBeenCalledWith(
|
|
28
|
+
setValue('new')
|
|
29
|
+
expect(value()).toBe('controlled')
|
|
30
|
+
expect(onChange).toHaveBeenCalledWith('new')
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
it(
|
|
33
|
+
it('calls onChange in uncontrolled mode', () => {
|
|
34
34
|
const onChange = vi.fn()
|
|
35
35
|
const [, setValue] = useControllableState({ defaultValue: 0, onChange })
|
|
36
36
|
setValue(10)
|
|
37
37
|
expect(onChange).toHaveBeenCalledWith(10)
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
it(
|
|
40
|
+
it('supports updater function', () => {
|
|
41
41
|
const [value, setValue] = useControllableState({ defaultValue: 1 })
|
|
42
42
|
setValue((prev: number) => prev + 1)
|
|
43
43
|
expect(value()).toBe(2)
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from
|
|
2
|
-
import { useDebouncedCallback } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { useDebouncedCallback } from '../useDebouncedCallback'
|
|
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('useDebouncedCallback', () => {
|
|
13
13
|
beforeEach(() => vi.useFakeTimers())
|
|
14
14
|
afterEach(() => vi.useRealTimers())
|
|
15
15
|
|
|
16
|
-
it(
|
|
16
|
+
it('debounces the callback', () => {
|
|
17
17
|
const fn = vi.fn()
|
|
18
18
|
const debounced = useDebouncedCallback(fn, 100)
|
|
19
19
|
|
|
20
|
-
debounced(
|
|
21
|
-
debounced(
|
|
22
|
-
debounced(
|
|
20
|
+
debounced('a')
|
|
21
|
+
debounced('b')
|
|
22
|
+
debounced('c')
|
|
23
23
|
|
|
24
24
|
expect(fn).not.toHaveBeenCalled()
|
|
25
25
|
vi.advanceTimersByTime(100)
|
|
26
26
|
expect(fn).toHaveBeenCalledTimes(1)
|
|
27
|
-
expect(fn).toHaveBeenCalledWith(
|
|
27
|
+
expect(fn).toHaveBeenCalledWith('c')
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
it(
|
|
30
|
+
it('cancel prevents the callback', () => {
|
|
31
31
|
const fn = vi.fn()
|
|
32
32
|
const debounced = useDebouncedCallback(fn, 100)
|
|
33
33
|
|
|
34
|
-
debounced(
|
|
34
|
+
debounced('a')
|
|
35
35
|
debounced.cancel()
|
|
36
36
|
vi.advanceTimersByTime(200)
|
|
37
37
|
|
|
38
38
|
expect(fn).not.toHaveBeenCalled()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
it(
|
|
41
|
+
it('flush invokes immediately', () => {
|
|
42
42
|
const fn = vi.fn()
|
|
43
43
|
const debounced = useDebouncedCallback(fn, 100)
|
|
44
44
|
|
|
45
|
-
debounced(
|
|
45
|
+
debounced('x')
|
|
46
46
|
debounced.flush()
|
|
47
47
|
|
|
48
|
-
expect(fn).toHaveBeenCalledWith(
|
|
48
|
+
expect(fn).toHaveBeenCalledWith('x')
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
it(
|
|
51
|
+
it('flush is a no-op when no pending timer', () => {
|
|
52
52
|
const fn = vi.fn()
|
|
53
53
|
const debounced = useDebouncedCallback(fn, 100)
|
|
54
54
|
|
|
@@ -56,11 +56,11 @@ describe("useDebouncedCallback", () => {
|
|
|
56
56
|
expect(fn).not.toHaveBeenCalled()
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
it(
|
|
59
|
+
it('flush is a no-op after timer already fired', () => {
|
|
60
60
|
const fn = vi.fn()
|
|
61
61
|
const debounced = useDebouncedCallback(fn, 100)
|
|
62
62
|
|
|
63
|
-
debounced(
|
|
63
|
+
debounced('a')
|
|
64
64
|
vi.advanceTimersByTime(100)
|
|
65
65
|
expect(fn).toHaveBeenCalledTimes(1)
|
|
66
66
|
|
|
@@ -68,11 +68,11 @@ describe("useDebouncedCallback", () => {
|
|
|
68
68
|
expect(fn).toHaveBeenCalledTimes(1)
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
it(
|
|
71
|
+
it('flush is a no-op after cancel', () => {
|
|
72
72
|
const fn = vi.fn()
|
|
73
73
|
const debounced = useDebouncedCallback(fn, 100)
|
|
74
74
|
|
|
75
|
-
debounced(
|
|
75
|
+
debounced('a')
|
|
76
76
|
debounced.cancel()
|
|
77
77
|
debounced.flush()
|
|
78
78
|
expect(fn).not.toHaveBeenCalled()
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { signal } from
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from
|
|
3
|
-
import { useDebouncedValue } from
|
|
1
|
+
import { signal } from '@pyreon/reactivity'
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { useDebouncedValue } from '../useDebouncedValue'
|
|
4
4
|
|
|
5
5
|
// Mock onUnmount since it requires component lifecycle context
|
|
6
|
-
vi.mock(
|
|
6
|
+
vi.mock('@pyreon/core', () => ({
|
|
7
7
|
onMount: (fn: () => void) => fn(),
|
|
8
8
|
onUnmount: (_fn: () => void) => {
|
|
9
9
|
/* no-op */
|
|
10
10
|
},
|
|
11
11
|
}))
|
|
12
12
|
|
|
13
|
-
describe(
|
|
13
|
+
describe('useDebouncedValue', () => {
|
|
14
14
|
beforeEach(() => {
|
|
15
15
|
vi.useFakeTimers()
|
|
16
16
|
})
|
|
@@ -19,33 +19,33 @@ describe("useDebouncedValue", () => {
|
|
|
19
19
|
vi.useRealTimers()
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
it(
|
|
22
|
+
it('returns the initial value immediately', () => {
|
|
23
23
|
const source = signal(42)
|
|
24
24
|
const debounced = useDebouncedValue(source, 300)
|
|
25
25
|
expect(debounced()).toBe(42)
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
-
it(
|
|
29
|
-
const source = signal(
|
|
28
|
+
it('does not update immediately when source changes', () => {
|
|
29
|
+
const source = signal('hello')
|
|
30
30
|
const debounced = useDebouncedValue(source, 300)
|
|
31
|
-
expect(debounced()).toBe(
|
|
31
|
+
expect(debounced()).toBe('hello')
|
|
32
32
|
|
|
33
|
-
source.set(
|
|
34
|
-
expect(debounced()).toBe(
|
|
33
|
+
source.set('world')
|
|
34
|
+
expect(debounced()).toBe('hello')
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
it(
|
|
38
|
-
const source = signal(
|
|
37
|
+
it('updates after the delay elapses', () => {
|
|
38
|
+
const source = signal('hello')
|
|
39
39
|
const debounced = useDebouncedValue(source, 300)
|
|
40
40
|
|
|
41
|
-
source.set(
|
|
42
|
-
expect(debounced()).toBe(
|
|
41
|
+
source.set('world')
|
|
42
|
+
expect(debounced()).toBe('hello')
|
|
43
43
|
|
|
44
44
|
vi.advanceTimersByTime(300)
|
|
45
|
-
expect(debounced()).toBe(
|
|
45
|
+
expect(debounced()).toBe('world')
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
it(
|
|
48
|
+
it('resets the timer on rapid changes', () => {
|
|
49
49
|
const source = signal(1)
|
|
50
50
|
const debounced = useDebouncedValue(source, 300)
|
|
51
51
|
|
|
@@ -62,7 +62,7 @@ describe("useDebouncedValue", () => {
|
|
|
62
62
|
expect(debounced()).toBe(4)
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
-
it(
|
|
65
|
+
it('only applies the last value after debounce', () => {
|
|
66
66
|
const source = signal(0)
|
|
67
67
|
const debounced = useDebouncedValue(source, 200)
|
|
68
68
|
|
|
@@ -76,20 +76,20 @@ describe("useDebouncedValue", () => {
|
|
|
76
76
|
expect(debounced()).toBe(5)
|
|
77
77
|
})
|
|
78
78
|
|
|
79
|
-
it(
|
|
80
|
-
const source = signal(
|
|
79
|
+
it('handles multiple debounce cycles', () => {
|
|
80
|
+
const source = signal('a')
|
|
81
81
|
const debounced = useDebouncedValue(source, 100)
|
|
82
82
|
|
|
83
|
-
source.set(
|
|
83
|
+
source.set('b')
|
|
84
84
|
vi.advanceTimersByTime(100)
|
|
85
|
-
expect(debounced()).toBe(
|
|
85
|
+
expect(debounced()).toBe('b')
|
|
86
86
|
|
|
87
|
-
source.set(
|
|
87
|
+
source.set('c')
|
|
88
88
|
vi.advanceTimersByTime(100)
|
|
89
|
-
expect(debounced()).toBe(
|
|
89
|
+
expect(debounced()).toBe('c')
|
|
90
90
|
})
|
|
91
91
|
|
|
92
|
-
it(
|
|
92
|
+
it('works with zero delay', () => {
|
|
93
93
|
const source = signal(1)
|
|
94
94
|
const debounced = useDebouncedValue(source, 0)
|
|
95
95
|
|
|
@@ -98,9 +98,9 @@ describe("useDebouncedValue", () => {
|
|
|
98
98
|
expect(debounced()).toBe(2)
|
|
99
99
|
})
|
|
100
100
|
|
|
101
|
-
it(
|
|
102
|
-
const obj1 = { name:
|
|
103
|
-
const obj2 = { name:
|
|
101
|
+
it('works with object values', () => {
|
|
102
|
+
const obj1 = { name: 'Alice' }
|
|
103
|
+
const obj2 = { name: 'Bob' }
|
|
104
104
|
const source = signal(obj1)
|
|
105
105
|
const debounced = useDebouncedValue(source, 100)
|
|
106
106
|
expect(debounced()).toBe(obj1)
|
|
@@ -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 { useElementSize } from
|
|
15
|
+
import { useElementSize } from '../useElementSize'
|
|
16
16
|
|
|
17
|
-
describe(
|
|
17
|
+
describe('useElementSize', () => {
|
|
18
18
|
let resizeCallback: ((entries: ResizeObserverEntry[]) => void) | undefined
|
|
19
19
|
let observeSpy: ReturnType<typeof vi.fn>
|
|
20
20
|
let disconnectSpy: ReturnType<typeof vi.fn>
|
|
@@ -36,15 +36,15 @@ describe("useElementSize", () => {
|
|
|
36
36
|
}) as unknown as typeof ResizeObserver
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
it(
|
|
39
|
+
it('returns initial size of 0x0', () => {
|
|
40
40
|
const size = useElementSize(() => null)
|
|
41
41
|
expect(size().width).toBe(0)
|
|
42
42
|
expect(size().height).toBe(0)
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
it(
|
|
46
|
-
const el = document.createElement(
|
|
47
|
-
vi.spyOn(el,
|
|
45
|
+
it('measures initial element size on mount', () => {
|
|
46
|
+
const el = document.createElement('div')
|
|
47
|
+
vi.spyOn(el, 'getBoundingClientRect').mockReturnValue({
|
|
48
48
|
width: 200,
|
|
49
49
|
height: 100,
|
|
50
50
|
x: 0,
|
|
@@ -67,9 +67,9 @@ describe("useElementSize", () => {
|
|
|
67
67
|
expect(size().height).toBe(100)
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
it(
|
|
71
|
-
const el = document.createElement(
|
|
72
|
-
vi.spyOn(el,
|
|
70
|
+
it('observes the element with ResizeObserver', () => {
|
|
71
|
+
const el = document.createElement('div')
|
|
72
|
+
vi.spyOn(el, 'getBoundingClientRect').mockReturnValue({
|
|
73
73
|
width: 0,
|
|
74
74
|
height: 0,
|
|
75
75
|
x: 0,
|
|
@@ -91,9 +91,9 @@ describe("useElementSize", () => {
|
|
|
91
91
|
expect(observeSpy).toHaveBeenCalledWith(el)
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
it(
|
|
95
|
-
const el = document.createElement(
|
|
96
|
-
vi.spyOn(el,
|
|
94
|
+
it('updates size when ResizeObserver fires', () => {
|
|
95
|
+
const el = document.createElement('div')
|
|
96
|
+
vi.spyOn(el, 'getBoundingClientRect').mockReturnValue({
|
|
97
97
|
width: 100,
|
|
98
98
|
height: 50,
|
|
99
99
|
x: 0,
|
|
@@ -124,7 +124,7 @@ describe("useElementSize", () => {
|
|
|
124
124
|
expect(size().height).toBe(150)
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
it(
|
|
127
|
+
it('does nothing on mount when element is null', () => {
|
|
128
128
|
useElementSize(() => null)
|
|
129
129
|
mountCallbacks.forEach((cb) => {
|
|
130
130
|
cb()
|
|
@@ -133,9 +133,9 @@ describe("useElementSize", () => {
|
|
|
133
133
|
expect(observeSpy).not.toHaveBeenCalled()
|
|
134
134
|
})
|
|
135
135
|
|
|
136
|
-
it(
|
|
137
|
-
const el = document.createElement(
|
|
138
|
-
vi.spyOn(el,
|
|
136
|
+
it('disconnects ResizeObserver on unmount', () => {
|
|
137
|
+
const el = document.createElement('div')
|
|
138
|
+
vi.spyOn(el, 'getBoundingClientRect').mockReturnValue({
|
|
139
139
|
width: 0,
|
|
140
140
|
height: 0,
|
|
141
141
|
x: 0,
|
|
@@ -160,9 +160,9 @@ describe("useElementSize", () => {
|
|
|
160
160
|
expect(disconnectSpy).toHaveBeenCalled()
|
|
161
161
|
})
|
|
162
162
|
|
|
163
|
-
it(
|
|
164
|
-
const el = document.createElement(
|
|
165
|
-
vi.spyOn(el,
|
|
163
|
+
it('handles ResizeObserver callback with no entry', () => {
|
|
164
|
+
const el = document.createElement('div')
|
|
165
|
+
vi.spyOn(el, 'getBoundingClientRect').mockReturnValue({
|
|
166
166
|
width: 50,
|
|
167
167
|
height: 25,
|
|
168
168
|
x: 0,
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { useFocus } from
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { useFocus } from '../useFocus'
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
4
|
+
describe('useFocus', () => {
|
|
5
|
+
it('initializes with focused=false', () => {
|
|
6
6
|
const { focused } = useFocus()
|
|
7
7
|
expect(focused()).toBe(false)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
it(
|
|
10
|
+
it('sets focused to true on focus', () => {
|
|
11
11
|
const { focused, props } = useFocus()
|
|
12
12
|
props.onFocus()
|
|
13
13
|
expect(focused()).toBe(true)
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
it(
|
|
16
|
+
it('sets focused to false on blur', () => {
|
|
17
17
|
const { focused, props } = useFocus()
|
|
18
18
|
props.onFocus()
|
|
19
19
|
expect(focused()).toBe(true)
|
|
@@ -21,7 +21,7 @@ describe("useFocus", () => {
|
|
|
21
21
|
expect(focused()).toBe(false)
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
it(
|
|
24
|
+
it('toggles focus state through multiple cycles', () => {
|
|
25
25
|
const { focused, props } = useFocus()
|
|
26
26
|
props.onFocus()
|
|
27
27
|
expect(focused()).toBe(true)
|
|
@@ -31,22 +31,22 @@ describe("useFocus", () => {
|
|
|
31
31
|
expect(focused()).toBe(true)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
it(
|
|
34
|
+
it('calling blur when not focused is safe', () => {
|
|
35
35
|
const { focused, props } = useFocus()
|
|
36
36
|
props.onBlur()
|
|
37
37
|
expect(focused()).toBe(false)
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
it(
|
|
40
|
+
it('calling focus multiple times stays true', () => {
|
|
41
41
|
const { focused, props } = useFocus()
|
|
42
42
|
props.onFocus()
|
|
43
43
|
props.onFocus()
|
|
44
44
|
expect(focused()).toBe(true)
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
it(
|
|
47
|
+
it('returns props object with onFocus and onBlur', () => {
|
|
48
48
|
const { props } = useFocus()
|
|
49
|
-
expect(typeof props.onFocus).toBe(
|
|
50
|
-
expect(typeof props.onBlur).toBe(
|
|
49
|
+
expect(typeof props.onFocus).toBe('function')
|
|
50
|
+
expect(typeof props.onBlur).toBe('function')
|
|
51
51
|
})
|
|
52
52
|
})
|