Skip to content

Commit d457fb6

Browse files
committed
main 🧊 add use keyboard test
1 parent 0cf1595 commit d457fb6

10 files changed

Lines changed: 76 additions & 113 deletions

File tree

‎packages/core/src/bundle/hooks/useKeyboard/useKeyboard.js‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef, useState } from 'react';
1+
import { useEffect, useRef } from 'react';
22
import { isTarget } from '@/utils/helpers';
33
import { useRefState } from '../useRefState/useRefState';
44
/**
@@ -29,15 +29,15 @@ import { useRefState } from '../useRefState/useRefState';
2929
* @returns {{ ref: StateRef<Target> }} An object containing the ref
3030
*
3131
* @example
32-
* const ref = useKeyboard((event) => console.log('key down'));
32+
* const keyboard = useKeyboard((event) => console.log('key down'));
3333
*
3434
* @overload
3535
* @template Target The target element type
3636
* @param {UseKeyboardEventOptions} [options] The keyboard event options
3737
* @returns {{ ref: StateRef<Target> }} An object containing the ref
3838
*
3939
* @example
40-
* const ref = useKeyboard({ onKeyDown: (event) => console.log('key down'), onKeyUp: (event) => console.log('key up') });
40+
* const keyboard = useKeyboard({ onKeyDown: (event) => console.log('key down'), onKeyUp: (event) => console.log('key up') });
4141
*/
4242
export const useKeyboard = (...params) => {
4343
const target = isTarget(params[0]) ? params[0] : undefined;
@@ -48,14 +48,11 @@ export const useKeyboard = (...params) => {
4848
: typeof params[0] === 'object'
4949
? params[0]
5050
: { onKeyDown: params[0] };
51-
const [initialValue] = useState(() => (typeof window !== 'undefined' ? window : undefined));
52-
const internalRef = useRefState(initialValue);
51+
const internalRef = useRefState();
5352
const internalOptionsRef = useRef(options);
5453
internalOptionsRef.current = options;
5554
useEffect(() => {
56-
if (!target && !internalRef.state) return;
57-
const element = target ? isTarget.getElement(target) : internalRef.current;
58-
if (!element) return;
55+
const element = (target ? isTarget.getElement(target) : internalRef.current) ?? window;
5956
const onKeyDown = (event) => internalOptionsRef.current?.onKeyDown?.(event);
6057
const onKeyUp = (event) => internalOptionsRef.current?.onKeyUp?.(event);
6158
element.addEventListener('keydown', onKeyDown);
@@ -66,5 +63,5 @@ export const useKeyboard = (...params) => {
6663
};
6764
}, [target && isTarget.getRawElement(target), internalRef.state]);
6865
if (target) return;
69-
return internalRef;
66+
return { ref: internalRef };
7067
};

‎packages/core/src/bundle/hooks/useScroll/useScroll.js‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ export const useScroll = (...params) => {
9797
if (watchingRef.current) rerender();
9898
};
9999
useEffect(() => {
100-
if (!target && !internalRef.state) return;
101100
const element = (target ? isTarget.getElement(target) : internalRef.current) ?? window;
102101
elementRef.current = element;
103102
const onScrollEnd = (event) => {

‎packages/core/src/bundle/hooks/useScrollIntoView/useScrollIntoView.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useRefState } from '../useRefState/useRefState';
99
* @usage low
1010
*
1111
* @overload
12-
* @param {HookTarget} [target=window] The target element to scroll into view
12+
* @param {HookTarget} target The target element to scroll into view
1313
* @param {boolean} [options.immediately=true] Whether to scroll immediately
1414
* @param {ScrollBehavior} [options.behavior='smooth'] The scrolling behavior
1515
* @param {ScrollLogicalPosition} [options.block='start'] The vertical alignment
@@ -44,7 +44,7 @@ export const useScrollIntoView = (...params) => {
4444
useIsomorphicLayoutEffect(() => {
4545
if (!immediately) return;
4646
if (!target && !internalRef.state) return;
47-
const element = (target ? isTarget.getElement(target) : internalRef.current) ?? window;
47+
const element = target ? isTarget.getElement(target) : internalRef.current;
4848
elementRef.current = element;
4949
element.scrollIntoView({
5050
behavior,

‎packages/core/src/bundle/hooks/useScrollTo/useScrollTo.js‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export const useScrollTo = (...params) => {
3838
const elementRef = useRef(null);
3939
useIsomorphicLayoutEffect(() => {
4040
if (!immediately) return;
41-
if (!target && !internalRef.state) return;
4241
const element = (target ? isTarget.getElement(target) : internalRef.current) ?? window;
4342
elementRef.current = element;
4443
element.scrollTo({ top: y, left: x, behavior });
Lines changed: 55 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { act, renderHook } from '@testing-library/react';
2-
import { describe, expect, it, vi } from 'vitest';
32

3+
import { renderHookServer } from '@/tests';
44
import { target } from '@/utils/helpers';
55

66
import type { StateRef } from '../useRefState/useRefState';
77

8-
import { renderHookServer } from '../../../tests/renderHookServer';
98
import { useKeyboard } from './useKeyboard';
109

1110
type UseKeyboardReturn = StateRef<HTMLDivElement>;
@@ -28,33 +27,42 @@ targets.forEach((target) => {
2827
describe(`${target}`, () => {
2928
it('Should use keyboard', () => {
3029
const { result } = renderHook(() => {
31-
if (target) return useKeyboard(target, () => {}) as unknown as UseKeyboardReturn;
30+
if (target)
31+
return useKeyboard(target, () => {}) as unknown as UseKeyboardReturn & {
32+
ref: StateRef<HTMLDivElement>;
33+
};
3234
return useKeyboard<HTMLDivElement>(() => {});
3335
});
3436

3537
if (target) expect(result.current).toBeUndefined();
36-
if (!target) expect(result.current).toBeTypeOf('function');
38+
if (!target) expect(result.current.ref).toBeTypeOf('function');
3739
});
3840

3941
it('Should use keyboard on server side', () => {
4042
const { result } = renderHookServer(() => {
41-
if (target) return useKeyboard(target, () => {}) as unknown as UseKeyboardReturn;
43+
if (target)
44+
return useKeyboard(target, () => {}) as unknown as UseKeyboardReturn & {
45+
ref: StateRef<HTMLDivElement>;
46+
};
4247
return useKeyboard<HTMLDivElement>(() => {});
4348
});
4449

4550
if (target) expect(result.current).toBeUndefined();
46-
if (!target) expect(result.current).toBeTypeOf('function');
51+
if (!target) expect(result.current.ref).toBeTypeOf('function');
4752
});
4853

4954
it('Should call callback on key down', () => {
5055
const callback = vi.fn();
5156

5257
const { result } = renderHook(() => {
53-
if (target) return useKeyboard(target, callback) as unknown as UseKeyboardReturn;
58+
if (target)
59+
return useKeyboard(target, callback) as unknown as UseKeyboardReturn & {
60+
ref: StateRef<HTMLDivElement>;
61+
};
5462
return useKeyboard<HTMLDivElement>(callback);
5563
});
5664

57-
if (!target) act(() => (result.current as UseKeyboardReturn)(element));
65+
if (!target) act(() => result.current.ref(element));
5866

5967
act(() => {
6068
element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' }));
@@ -68,11 +76,16 @@ targets.forEach((target) => {
6876
const onKeyDown = vi.fn();
6977

7078
const { result } = renderHook(() => {
71-
if (target) return useKeyboard(target, { onKeyDown }) as unknown as UseKeyboardReturn;
79+
if (target)
80+
return useKeyboard(target, {
81+
onKeyDown
82+
}) as unknown as UseKeyboardReturn & {
83+
ref: StateRef<HTMLDivElement>;
84+
};
7285
return useKeyboard<HTMLDivElement>({ onKeyDown });
7386
});
7487

75-
if (!target) act(() => (result.current as UseKeyboardReturn)(element));
88+
if (!target) act(() => result.current.ref(element));
7689

7790
act(() => {
7891
element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'a' }));
@@ -86,11 +99,16 @@ targets.forEach((target) => {
8699
const onKeyUp = vi.fn();
87100

88101
const { result } = renderHook(() => {
89-
if (target) return useKeyboard(target, { onKeyUp }) as unknown as UseKeyboardReturn;
102+
if (target)
103+
return useKeyboard(target, {
104+
onKeyUp
105+
}) as unknown as UseKeyboardReturn & {
106+
ref: StateRef<HTMLDivElement>;
107+
};
90108
return useKeyboard<HTMLDivElement>({ onKeyUp });
91109
});
92110

93-
if (!target) act(() => (result.current as UseKeyboardReturn)(element));
111+
if (!target) act(() => result.current.ref(element));
94112

95113
act(() => {
96114
element.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, key: 'Escape' }));
@@ -106,11 +124,16 @@ targets.forEach((target) => {
106124

107125
const { result } = renderHook(() => {
108126
if (target)
109-
return useKeyboard(target, { onKeyDown, onKeyUp }) as unknown as UseKeyboardReturn;
127+
return useKeyboard(target, {
128+
onKeyDown,
129+
onKeyUp
130+
}) as unknown as UseKeyboardReturn & {
131+
ref: StateRef<HTMLDivElement>;
132+
};
110133
return useKeyboard<HTMLDivElement>({ onKeyDown, onKeyUp });
111134
});
112135

113-
if (!target) act(() => (result.current as UseKeyboardReturn)(element));
136+
if (!target) act(() => result.current.ref(element));
114137

115138
act(() => {
116139
element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Tab' }));
@@ -121,31 +144,18 @@ targets.forEach((target) => {
121144
expect(onKeyUp).toHaveBeenCalledOnce();
122145
});
123146

124-
it('Should cleanup on unmount', () => {
125-
const removeEventListenerSpy = vi.spyOn(element, 'removeEventListener');
126-
127-
const { result, unmount } = renderHook(() => {
128-
if (target) return useKeyboard(target, () => {}) as unknown as UseKeyboardReturn;
129-
return useKeyboard<HTMLDivElement>(() => {});
130-
});
131-
132-
if (!target) act(() => (result.current as UseKeyboardReturn)(element));
133-
134-
unmount();
135-
136-
expect(removeEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function));
137-
expect(removeEventListenerSpy).toHaveBeenCalledWith('keyup', expect.any(Function));
138-
});
139-
140147
it('Should pass correct key in keyboard event', () => {
141148
const callback = vi.fn();
142149

143150
const { result } = renderHook(() => {
144-
if (target) return useKeyboard(target, callback) as unknown as UseKeyboardReturn;
151+
if (target)
152+
return useKeyboard(target, callback) as unknown as UseKeyboardReturn & {
153+
ref: StateRef<HTMLDivElement>;
154+
};
145155
return useKeyboard<HTMLDivElement>(callback);
146156
});
147157

148-
if (!target) act(() => (result.current as UseKeyboardReturn)(element));
158+
if (!target) act(() => result.current.ref(element));
149159

150160
act(() => {
151161
element.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' }));
@@ -154,45 +164,24 @@ targets.forEach((target) => {
154164
expect(callback).toHaveBeenCalledOnce();
155165
expect(callback.mock.calls[0][0].key).toBe('Enter');
156166
});
167+
});
157168

158-
it('Should attach listener to window when no target provided', () => {
159-
const callback = vi.fn();
160-
161-
renderHook(() => useKeyboard(callback));
162-
163-
act(() => {
164-
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
165-
});
169+
it('Should cleanup on unmount', () => {
170+
const removeEventListenerSpy = vi.spyOn(element, 'removeEventListener');
166171

167-
expect(callback).toHaveBeenCalledOnce();
172+
const { result, unmount } = renderHook(() => {
173+
if (target)
174+
return useKeyboard(target, () => {}) as unknown as UseKeyboardReturn & {
175+
ref: StateRef<HTMLDivElement>;
176+
};
177+
return useKeyboard<HTMLDivElement>(() => {});
168178
});
169179

170-
it('Should update options after rerender', () => {
171-
if (target) return;
172-
173-
const onKeyDown1 = vi.fn();
174-
const onKeyDown2 = vi.fn();
175-
176-
const { result, rerender } = renderHook(
177-
({ handler }) => useKeyboard<HTMLDivElement>({ onKeyDown: handler }),
178-
{ initialProps: { handler: onKeyDown1 } }
179-
);
180-
181-
act(() => (result.current as unknown as UseKeyboardReturn)(element));
180+
if (!target) act(() => result.current.ref(element));
182181

183-
act(() => {
184-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
185-
});
186-
187-
expect(onKeyDown1).toHaveBeenCalledOnce();
188-
189-
rerender({ handler: onKeyDown2 });
182+
unmount();
190183

191-
act(() => {
192-
element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
193-
});
194-
195-
expect(onKeyDown2).toHaveBeenCalledOnce();
196-
});
184+
expect(removeEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function));
185+
expect(removeEventListenerSpy).toHaveBeenCalledWith('keyup', expect.any(Function));
197186
});
198187
});

‎packages/core/src/hooks/useKeyboard/useKeyboard.ts‎

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef, useState } from 'react';
1+
import { useEffect, useRef } from 'react';
22

33
import type { HookTarget } from '@/utils/helpers';
44

@@ -68,15 +68,15 @@ export interface UseKeyboard {
6868
* @returns {{ ref: StateRef<Target> }} An object containing the ref
6969
*
7070
* @example
71-
* const ref = useKeyboard((event) => console.log('key down'));
71+
* const keyboard = useKeyboard((event) => console.log('key down'));
7272
*
7373
* @overload
7474
* @template Target The target element type
7575
* @param {UseKeyboardEventOptions} [options] The keyboard event options
7676
* @returns {{ ref: StateRef<Target> }} An object containing the ref
7777
*
7878
* @example
79-
* const ref = useKeyboard({ onKeyDown: (event) => console.log('key down'), onKeyUp: (event) => console.log('key up') });
79+
* const keyboard = useKeyboard({ onKeyDown: (event) => console.log('key down'), onKeyUp: (event) => console.log('key up') });
8080
*/
8181
export const useKeyboard = ((...params: any[]) => {
8282
const target = isTarget(params[0]) ? params[0] : undefined;
@@ -90,19 +90,13 @@ export const useKeyboard = ((...params: any[]) => {
9090
: { onKeyDown: params[0] }
9191
) as UseKeyboardEventOptions;
9292

93-
const [initialValue] = useState<Window | undefined>(() =>
94-
typeof window !== 'undefined' ? window : undefined
95-
);
96-
97-
const internalRef = useRefState<HTMLElement | Window>(initialValue as Window);
93+
const internalRef = useRefState<HTMLElement | Window>();
9894
const internalOptionsRef = useRef(options);
9995
internalOptionsRef.current = options;
10096

10197
useEffect(() => {
102-
if (!target && !internalRef.state) return;
103-
104-
const element = (target ? isTarget.getElement(target) : internalRef.current) as HTMLElement;
105-
if (!element) return;
98+
const element =
99+
((target ? isTarget.getElement(target) : internalRef.current) as HTMLElement) ?? window;
106100

107101
const onKeyDown = (event: Event) =>
108102
internalOptionsRef.current?.onKeyDown?.(event as KeyboardEvent);
@@ -118,5 +112,5 @@ export const useKeyboard = ((...params: any[]) => {
118112
}, [target && isTarget.getRawElement(target), internalRef.state]);
119113

120114
if (target) return;
121-
return internalRef;
115+
return { ref: internalRef };
122116
}) as UseKeyboard;

‎packages/core/src/hooks/useScroll/useScroll.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,6 @@ export const useScroll = ((...params: any[]) => {
193193
};
194194

195195
useEffect(() => {
196-
if (!target && !internalRef.state) return;
197196
const element =
198197
((target ? isTarget.getElement(target) : internalRef.current) as Element) ?? window;
199198

‎packages/core/src/hooks/useScrollIntoView/useScrollIntoView.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface UseScrollIntoView {
4141
* @usage low
4242
*
4343
* @overload
44-
* @param {HookTarget} [target=window] The target element to scroll into view
44+
* @param {HookTarget} target The target element to scroll into view
4545
* @param {boolean} [options.immediately=true] Whether to scroll immediately
4646
* @param {ScrollBehavior} [options.behavior='smooth'] The scrolling behavior
4747
* @param {ScrollLogicalPosition} [options.block='start'] The vertical alignment
@@ -79,8 +79,7 @@ export const useScrollIntoView = ((...params: any[]) => {
7979
if (!immediately) return;
8080
if (!target && !internalRef.state) return;
8181

82-
const element =
83-
((target ? isTarget.getElement(target) : internalRef.current) as Element) ?? window;
82+
const element = (target ? isTarget.getElement(target) : internalRef.current) as Element;
8483

8584
elementRef.current = element;
8685

0 commit comments

Comments
 (0)