Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fuselage-hooks): React 18 compatibility #1483

Merged
merged 5 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/witty-dodos-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/fuselage-hooks': minor
'@rocket.chat/fuselage': patch
---

Enables hooks' compatibility with React 18
7 changes: 0 additions & 7 deletions packages/fuselage-hooks/jest.config.js

This file was deleted.

68 changes: 68 additions & 0 deletions packages/fuselage-hooks/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { Config } from 'jest';

export default {
projects: [
{
displayName: 'React 17',
preset: 'ts-jest',
errorOnDeprecated: true,
testMatch: [
'<rootDir>/src/**/*.spec.{ts,tsx}',
'!**/*.server.spec.{ts,tsx}',
],
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['testing-utils/setup/noErrorsLogged'],
moduleNameMapper: {
'^react($|/.+)': 'react$1',
'^react-dom/client$': 'react-dom$1',
'^react-dom($|/.+)': 'react-dom$1',
},
},
{
displayName: 'React 17 SSR',
preset: 'ts-jest',
errorOnDeprecated: true,
testMatch: ['<rootDir>/src/**/*.server.spec.{ts,tsx}'],
testEnvironment: 'node',
setupFilesAfterEnv: ['testing-utils/setup/noErrorsLogged'],
moduleNameMapper: {
'^react($|/.+)': 'react$1',
'^react-dom/client$': 'react-dom$1',
'^react-dom($|/.+)': 'react-dom$1',
},
},
{
displayName: 'React 18',
preset: 'ts-jest',
errorOnDeprecated: true,
testMatch: [
'<rootDir>/src/**/*.spec.{ts,tsx}',
'!**/*.server.spec.{ts,tsx}',
],
testEnvironment: 'jsdom',
setupFilesAfterEnv: [
'testing-utils/setup/noErrorsLogged',
'<rootDir>/src/jest-setup.ts',
],
moduleNameMapper: {
'^react($|/.+)': 'react18$1',
'^react-dom($|/.+)': 'react-dom18$1',
},
},
{
displayName: 'React 18 SSR',
preset: 'ts-jest',
errorOnDeprecated: true,
testMatch: ['<rootDir>/src/**/*.server.spec.{ts,tsx}'],
testEnvironment: 'node',
setupFilesAfterEnv: [
'testing-utils/setup/noErrorsLogged',
'<rootDir>/src/jest-setup.ts',
],
moduleNameMapper: {
'^react($|/.+)': 'react18$1',
'^react-dom($|/.+)': 'react-dom18$1',
},
},
],
} satisfies Config;
5 changes: 4 additions & 1 deletion packages/fuselage-hooks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@rollup/plugin-json": "~4.1.0",
"@rollup/plugin-node-resolve": "~13.1.3",
"@rollup/plugin-typescript": "~8.3.4",
"@testing-library/react-hooks": "~8.0.1",
"@testing-library/react": "~16.0.1",
"@testing-library/user-event": "~14.5.2",
"@types/jest": "~29.5.12",
"@types/react": "~17.0.80",
Expand All @@ -63,6 +63,9 @@
"npm-run-all": "^4.1.5",
"prettier": "~3.3.3",
"react": "^17.0.2",
"react-dom": "~17.0.2",
"react-dom18": "npm:react-dom@18",
"react18": "npm:react@18",
"rimraf": "~5.0.0",
"rollup": "~2.79.2",
"rollup-plugin-terser": "~7.0.2",
Expand Down
3 changes: 3 additions & 0 deletions packages/fuselage-hooks/src/jest-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { configure } from '@testing-library/react';

configure({ reactStrictMode: true });
45 changes: 45 additions & 0 deletions packages/fuselage-hooks/src/testing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
queries,
Queries,
RenderHookOptions,
RenderHookResult,
renderHook as _renderHook,
} from '@testing-library/react';
import { createElement } from 'react';
import * as ReactDOMClient from 'react-dom';
import { renderToString } from 'react-dom/server';

type RendererableContainer = Element | Document | DocumentFragment;

export function renderHook<
Result,
Props,
Q extends Queries = typeof queries,
Container extends RendererableContainer = HTMLElement,
BaseElement extends RendererableContainer = Container,
>(
render: (initialProps: Props) => Result,
options?: RenderHookOptions<Props, Q, Container, BaseElement> | undefined,
): RenderHookResult<Result, Props> {
if (typeof document === 'undefined') {
let current: Result;
const TestComponent = () => {
current = render(options?.initialProps as any);
return null;
};

renderToString(createElement(TestComponent));

return {
result: { current: current! },
rerender: () => undefined,
unmount: () => undefined,
};
}

if ('createRoot' in ReactDOMClient) return _renderHook(render, options);

return _renderHook(render, { ...options, legacyRoot: true });
}

export { act } from '@testing-library/react';
10 changes: 5 additions & 5 deletions packages/fuselage-hooks/src/useAutoFocus.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useImperativeHandle, useState } from 'react';

import { renderHook, act } from './testing';
import { useAutoFocus } from './useAutoFocus';

const focus = jest.fn();
Expand All @@ -17,7 +17,7 @@ it('invokes focus', async () => {

act(() => undefined);

expect(focus).toHaveBeenCalledTimes(1);
expect(focus).toHaveBeenCalled();
});

it('does not invoke focus if isFocused is false', () => {
Expand All @@ -28,7 +28,7 @@ it('does not invoke focus if isFocused is false', () => {

act(() => undefined);

expect(focus).toHaveBeenCalledTimes(0);
expect(focus).not.toHaveBeenCalled();
});

it('invokes focus if isFocused is toggled', () => {
Expand All @@ -41,11 +41,11 @@ it('invokes focus if isFocused is toggled', () => {

act(() => undefined);

expect(focus).toHaveBeenCalledTimes(0);
expect(focus).not.toHaveBeenCalled();

act(() => {
result.current.setIsFocused(true);
});

expect(focus).toHaveBeenCalledTimes(1);
expect(focus).toHaveBeenCalled();
});
12 changes: 5 additions & 7 deletions packages/fuselage-hooks/src/useAutoFocus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useEffect, useRef } from 'react';
* @param options - options of the focus request
* @returns the ref which holds the element
* @public
* @deprecated in favor of focus provided by react-hook-form
* @deprecated in favor of focus provided by react-hook-form or the `autoFocus` attribute
*/
export const useAutoFocus = <
T extends { focus: (options?: FocusOptions) => void },
Expand All @@ -17,16 +17,14 @@ export const useAutoFocus = <
options?: FocusOptions,
): Ref<T> => {
const elementRef = useRef<T>(null);

const { preventScroll } = options || {};
const optionsRef = useRef(options);
optionsRef.current = options;

useEffect(() => {
if (isFocused && elementRef.current) {
elementRef.current.focus({
preventScroll,
});
elementRef.current.focus(optionsRef.current);
}
}, [elementRef, isFocused, preventScroll]);
}, [elementRef, isFocused]);

return elementRef;
};
6 changes: 1 addition & 5 deletions packages/fuselage-hooks/src/useBorderBoxSize.server.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
/**
* @jest-environment node
*/

import { renderHook } from '@testing-library/react-hooks/server';
import { useRef } from 'react';

import { renderHook } from './testing';
import { useBorderBoxSize } from './useBorderBoxSize';

it('immediately returns zero size', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-hooks/src/useBorderBoxSize.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { renderHook, act } from '@testing-library/react-hooks';
import type { RefObject } from 'react';
import { useRef } from 'react';
import { withResizeObserverMock } from 'testing-utils/mocks/withResizeObserverMock';

import { renderHook, act } from './testing';
import { useBorderBoxSize } from './useBorderBoxSize';

withResizeObserverMock();
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-hooks/src/useBreakpoints.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import breakpoints from '@rocket.chat/fuselage-tokens/breakpoints.json';
import { renderHook, act } from '@testing-library/react-hooks';
import { withMatchMediaMock } from 'testing-utils/mocks/withMatchMediaMock';

import { renderHook, act } from './testing';
import { useBreakpoints } from './useBreakpoints';

const setViewport = withMatchMediaMock();
Expand Down
7 changes: 1 addition & 6 deletions packages/fuselage-hooks/src/useClipboard.server.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
/**
* @jest-environment node
*/

import { renderHook } from '@testing-library/react-hooks/server';

import { renderHook } from './testing';
import { useClipboard } from './useClipboard';

it('has hasCopied and copy properties', () => {
Expand Down
10 changes: 5 additions & 5 deletions packages/fuselage-hooks/src/useClipboard.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { withClipboardMock } from 'testing-utils/mocks/withClipboardMock';

import { renderHook, act } from './testing';
import { useClipboard } from './useClipboard';

let container: Element | undefined;
Expand Down Expand Up @@ -87,8 +87,8 @@ it('runs only success function receiving event object', async () => {
await copy(event);
});

expect(onCopySuccess).toBeCalledWith(event);
expect(onCopyError).toBeCalledTimes(0);
expect(onCopySuccess).toHaveBeenCalledWith(event);
expect(onCopyError).toHaveBeenCalledTimes(0);
});

it('runs only error function receiving error object', async () => {
Expand All @@ -112,6 +112,6 @@ it('runs only error function receiving error object', async () => {
await copy(event);
});

expect(onCopySuccess).toBeCalledTimes(0);
expect(onCopyError).toBeCalledWith(rejection);
expect(onCopySuccess).toHaveBeenCalledTimes(0);
expect(onCopyError).toHaveBeenCalledWith(rejection);
});
6 changes: 1 addition & 5 deletions packages/fuselage-hooks/src/useContentBoxSize.server.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
/**
* @jest-environment node
*/

import { renderHook } from '@testing-library/react-hooks/server';
import { useRef } from 'react';

import { renderHook } from './testing';
import { useContentBoxSize } from './useContentBoxSize';

it('immediately returns zero size', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-hooks/src/useContentBoxSize.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { renderHook, act } from '@testing-library/react-hooks';
import type { RefObject } from 'react';
import { useRef } from 'react';
import { withResizeObserverMock } from 'testing-utils/mocks/withResizeObserverMock';

import { renderHook, act } from './testing';
import { useContentBoxSize } from './useContentBoxSize';

withResizeObserverMock();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
/**
* @jest-environment node
*/

import { renderHook } from '@testing-library/react-hooks/server';

import { renderHook } from './testing';
import { useDebouncedCallback } from './useDebouncedCallback';

beforeAll(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-hooks/src/useDebouncedCallback.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useState } from 'react';

import { renderHook, act } from './testing';
import { useDebouncedCallback } from './useDebouncedCallback';

beforeAll(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/fuselage-hooks/src/useDebouncedCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export const useDebouncedCallback = <P extends unknown[]>(
flush: () => void;
cancel: () => void;
} => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const effectiveCallback = useMemo(() => callback, deps);
// eslint-disable-next-line react-hooks/exhaustive-deps, react-hooks/rules-of-hooks
const effectiveCallback = deps ? useMemo(() => callback, deps) : callback;

const timerCallbackRef = useRef<() => void>();
const timerRef = useRef<ReturnType<typeof setTimeout>>();
Expand Down
3 changes: 1 addition & 2 deletions packages/fuselage-hooks/src/useDebouncedReducer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { renderHook, act } from '@testing-library/react-hooks';

import { renderHook, act } from './testing';
import { useDebouncedReducer } from './useDebouncedReducer';

beforeAll(() => {
Expand Down
3 changes: 1 addition & 2 deletions packages/fuselage-hooks/src/useDebouncedState.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { renderHook, act } from '@testing-library/react-hooks';

import { renderHook, act } from './testing';
import { useDebouncedState } from './useDebouncedState';

beforeAll(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-hooks/src/useDebouncedUpdates.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useState } from 'react';

import { renderHook, act } from './testing';
import { useDebouncedUpdates } from './useDebouncedUpdates';

beforeAll(() => {
Expand Down
7 changes: 1 addition & 6 deletions packages/fuselage-hooks/src/useDebouncedValue.server.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
/**
* @jest-environment node
*/

import { renderHook } from '@testing-library/react-hooks/server';

import { renderHook } from './testing';
import { useDebouncedValue } from './useDebouncedValue';

const delay = 100;
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage-hooks/src/useDebouncedValue.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useReducer } from 'react';

import { renderHook, act } from './testing';
import { useDebouncedValue } from './useDebouncedValue';

beforeAll(() => {
Expand Down
7 changes: 1 addition & 6 deletions packages/fuselage-hooks/src/useEffectEvent.server.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
/**
* @jest-environment node
*/

import { renderHook } from '@testing-library/react-hooks/server';

import { renderHook } from './testing';
import { useEffectEvent } from './useEffectEvent';

it('returns a callback that invokes the mutable one', () => {
Expand Down
Loading
Loading