From 03bdecf7ac6aa0ad863a1efd71c056aa41df62b7 Mon Sep 17 00:00:00 2001 From: xobotyi Date: Sun, 3 Nov 2019 23:13:36 +0300 Subject: [PATCH] feat(useWindowSize): A bit changed lyfecycle and added types; feat(useWindowSize): Rewritten tests - covers more cases and will work on next @testing-library/react-hooks release (been broken before); --- src/__tests__/useWindowSize.test.tsx | 93 ++++++++++++++++------------ src/createReducer.ts | 4 +- src/useWindowSize.ts | 4 +- src/util/parseTimeRanges.ts | 2 +- 4 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/__tests__/useWindowSize.test.tsx b/src/__tests__/useWindowSize.test.tsx index 015cf495c2..ebb4d8d749 100644 --- a/src/__tests__/useWindowSize.test.tsx +++ b/src/__tests__/useWindowSize.test.tsx @@ -1,73 +1,88 @@ import { act, renderHook } from '@testing-library/react-hooks'; import { replaceRaf } from 'raf-stub'; import useWindowSize from '../useWindowSize'; +import { isClient } from '../util'; -interface RequestAnimationFrame { - reset(): void; - step(): void; -} +declare var requestAnimationFrame: { + reset: () => void; + step: (steps?: number, duration?: number) => void; +}; -declare var requestAnimationFrame: RequestAnimationFrame; +describe('useWindowSize', () => { + beforeAll(() => { + replaceRaf(); + }); -replaceRaf(); + afterEach(() => { + requestAnimationFrame.reset(); + }); -beforeEach(() => { - requestAnimationFrame.reset(); -}); + it('should be defined', () => { + expect(useWindowSize).toBeDefined(); + }); -afterEach(() => { - requestAnimationFrame.reset(); -}); + function getHook(...args) { + return renderHook(() => useWindowSize(...args)); + } + + function triggerResize(dimension: 'width' | 'height', value: number) { + if (dimension === 'width') { + (window.innerWidth as number) = value; + } else if (dimension === 'height') { + (window.innerHeight as number) = value; + } -// simulate window resize -function fireResize(type, value) { - switch (type) { - case 'width': - (window.innerWidth as number) = value; // assert type of window.innerWidth as it is typed as readonly. - break; - case 'height': - (window.innerHeight as number) = value; // assert type of window.innerHeight as it is typed as readonly. - break; - default: - break; + window.dispatchEvent(new Event('resize')); } - window.dispatchEvent(new Event('resize')); -} + it('should return current window dimensions', () => { + const hook = getHook(); -describe('useWindowSize', () => { - it('should be defined', () => { - expect(useWindowSize).toBeDefined(); + expect(typeof hook.result.current).toBe('object'); + expect(typeof hook.result.current.height).toBe('number'); + expect(typeof hook.result.current.width).toBe('number'); + }); + + it('should use passed parameters as initial values in case of non-browser use', () => { + const hook = getHook(1, 1); + + expect(hook.result.current.height).toBe(isClient ? window.innerHeight : 1); + expect(hook.result.current.width).toBe(isClient ? window.innerWidth : 1); }); - const hook = renderHook(() => useWindowSize()); + it('should re-render after height change on closest RAF', () => { + const hook = getHook(); - it('should update width', () => { act(() => { - fireResize('width', 320); + triggerResize('height', 360); requestAnimationFrame.step(); }); - expect(hook.result.current.width).toBe(320); + expect(hook.result.current.height).toBe(360); act(() => { - fireResize('width', 640); + triggerResize('height', 2048); requestAnimationFrame.step(); }); - expect(hook.result.current.width).toBe(640); + + expect(hook.result.current.height).toBe(2048); }); - it('should update height', () => { + it('should re-render after width change on closest RAF', () => { + const hook = getHook(); + act(() => { - fireResize('height', 500); + triggerResize('width', 360); requestAnimationFrame.step(); }); - expect(hook.result.current.height).toBe(500); + + expect(hook.result.current.width).toBe(360); act(() => { - fireResize('height', 1000); + triggerResize('width', 2048); requestAnimationFrame.step(); }); - expect(hook.result.current.height).toBe(1000); + + expect(hook.result.current.width).toBe(2048); }); }); diff --git a/src/createReducer.ts b/src/createReducer.ts index 7cece51cbc..50b8ec06c7 100644 --- a/src/createReducer.ts +++ b/src/createReducer.ts @@ -10,7 +10,7 @@ interface Store { type Middleware = (store: Store) => (next: Dispatch) => (action: Action) => void; -function composeMiddleware(chain: Array>) { +function composeMiddleware(chain: Middleware[]) { return (context: Store, dispatch: Dispatch) => { return chain.reduceRight((res, middleware) => { return middleware(context)(res); @@ -18,7 +18,7 @@ function composeMiddleware(chain: Array }; } -const createReducer = (...middlewares: Array>) => { +const createReducer = (...middlewares: Middleware[]) => { const composedMiddleware = composeMiddleware(middlewares); return ( diff --git a/src/useWindowSize.ts b/src/useWindowSize.ts index e3021a08dd..2995796967 100644 --- a/src/useWindowSize.ts +++ b/src/useWindowSize.ts @@ -9,7 +9,7 @@ const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity) => { height: isClient ? window.innerHeight : initialHeight, }); - useEffect(() => { + useEffect((): (() => void) | void => { if (isClient) { const handler = () => { setState({ @@ -23,8 +23,6 @@ const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity) => { return () => { window.removeEventListener('resize', handler); }; - } else { - return undefined; } }, []); diff --git a/src/util/parseTimeRanges.ts b/src/util/parseTimeRanges.ts index e3bfdc3969..1348024ca2 100644 --- a/src/util/parseTimeRanges.ts +++ b/src/util/parseTimeRanges.ts @@ -1,5 +1,5 @@ const parseTimeRanges = ranges => { - const result: Array<{ start: number; end: number }> = []; + const result: { start: number; end: number }[] = []; for (let i = 0; i < ranges.length; i++) { result.push({