Skip to content

Commit

Permalink
feat!: migrate to react@19
Browse files Browse the repository at this point in the history
- using testing-library/react instead of removed ReactTestUtils.Simulate
- adjusted all types to upstream changes
- turned on skipLibCheck until zeejs is fixed up for react@19
  • Loading branch information
AviVahl committed Dec 7, 2024
1 parent 137736c commit 7f21061
Show file tree
Hide file tree
Showing 27 changed files with 413 additions and 182 deletions.
425 changes: 320 additions & 105 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
"@playwright/browser-chromium": "^1.49.0",
"@stylable/cli": "^6.1.1",
"@stylable/esbuild": "^6.1.1",
"@testing-library/react": "^16.1.0",
"@types/chai": "^4.3.20",
"@types/mocha": "^10.0.10",
"@types/node": "20",
"@types/react": "^18.3.13",
"@types/react-dom": "^18.3.1",
"@wixc3/board-core": "^4.6.0",
"@wixc3/react-board": "^4.6.0",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.1",
"@wixc3/board-core": "^4.6.2",
"@wixc3/react-board": "^4.6.2",
"chai": "^4.5.0",
"esbuild": "^0.24.0",
"eslint": "^9.16.0",
Expand All @@ -38,8 +39,8 @@
"mocha-web": "^2.0.0",
"prettier": "^3.4.2",
"promise-assist": "^2.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rimraf": "^6.0.1",
"typescript": "~5.7.2",
"typescript-eslint": "^8.17.0"
Expand Down
12 changes: 8 additions & 4 deletions packages/components/src/auto-complete/auto-complete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ export interface AutoCompleteProps<T, EL extends HTMLElement = HTMLDivElement> e
searchControl?: StateControls<string | undefined>;
}

export type AutoComplete<T, EL extends HTMLElement = HTMLDivElement> = (props: AutoCompleteProps<T, EL>) => JSX.Element;
export type AutoComplete<T, EL extends HTMLElement = HTMLDivElement> = (
props: AutoCompleteProps<T, EL>,
) => React.ReactElement;

export function AutoComplete<T, EL extends HTMLElement = HTMLDivElement>(props: AutoCompleteProps<T, EL>): JSX.Element {
export function AutoComplete<T, EL extends HTMLElement = HTMLDivElement>(
props: AutoCompleteProps<T, EL>,
): React.ReactElement {
const { searchControl, getTextContent, items, focusControl, selectionControl, getId, ...listProps } = props;
const [focused, setFocused] = useStateControls(focusControl, undefined);
const [selected, setSelected] = useStateControls(selectionControl, undefined);
Expand Down Expand Up @@ -54,7 +58,7 @@ export function AutoComplete<T, EL extends HTMLElement = HTMLDivElement>(props:
setSelected(selectedId);
close();
},
[close, getId, getTextContent, items, setSelected, updateSearchText]
[close, getId, getTextContent, items, setSelected, updateSearchText],
);
const scrollListRoot = createListRoot(Area, {
className: classes.scrollListRoot,
Expand All @@ -74,7 +78,7 @@ export function AutoComplete<T, EL extends HTMLElement = HTMLDivElement>(props:
onKeyPress(ev);
}
},
[close, onKeyPress, open]
[close, onKeyPress, open],
);
useEffect(() => {
if (filteredData[0]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const getPluginControls = () => {
return existing;
};

export const renderInPluginControls = (board: JSX.Element, pluginControls: JSX.Element, key: string) => {
export const renderInPluginControls = (board: React.ReactElement, pluginControls: React.ReactElement, key: string) => {
const el = getPluginControls();

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ReactTestUtils from 'react-dom/test-utils';
import { fireEvent } from '@testing-library/react';
import { Action, waitForElement } from '../scenario-plugin';

export const selectItemInput = 'input';
Expand All @@ -12,10 +12,7 @@ export const selectItemByIndex = (index: string): Action => {
execute: async () => {
const input = await waitForElement(`#${selectItemInput}`, title);
if (input && input instanceof HTMLInputElement) {
input.value = index;
ReactTestUtils.Simulate.change(input, {
target: input,
});
fireEvent.change(input, { target: { value: index } });
}
const button = await waitForElement(`#${selectItemButton}`, title);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createPlugin } from '@wixc3/board-core';
import type { IReactBoard } from '@wixc3/react-board';
import { fireEvent } from '@testing-library/react';
import { expect } from 'chai';
import { sleep, waitFor } from 'promise-assist';
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import { renderInPluginControls } from '../plugin-controls/plugin-controls';
import { classes, st } from './scenario-plugin.st.css';

Expand Down Expand Up @@ -161,7 +161,7 @@ export const ScenarioRenderer = (props: ScenarioProps) => {
);
};

export const RenderWrapper = (props: ScenarioParams & { board: JSX.Element }) => {
export const RenderWrapper = (props: ScenarioParams & { board: React.ReactElement }) => {
const [boardKey, rerenderBoard] = useReducer((n: number) => n + 1, 0);

const resetBoard = () => {
Expand Down Expand Up @@ -338,10 +338,7 @@ export const writeAction = (selector: string, text: string, timeout = 2_000): Ac
execute: async () => {
const el = await waitForElement(selector, title, timeout);
if (el && el instanceof HTMLInputElement) {
el.value = text;
ReactTestUtils.Simulate.change(el, {
target: el,
});
fireEvent.change(el, { target: { value: text } });
}
},
highlightSelector: selector,
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/common/slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import type { OptionalFields } from './types';
*/
export interface ElementSlot<
MinimalProps,
El extends React.ComponentType<Props> | keyof React.ReactHTML = React.ComponentType<any> | keyof React.ReactHTML,
Props extends MinimalProps = any
El extends React.ComponentType<Props> | React.HTMLElementType = React.ComponentType<any> | React.HTMLElementType,
Props extends MinimalProps = any,
> {
el: El;
props: OptionalFields<Props, keyof MinimalProps>;
Expand Down
6 changes: 3 additions & 3 deletions packages/components/src/data-grid/data-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ScrollList, ScrollListProps, forwardScrollListRoot } from '../scroll-li
import { classes, vars } from './data-grid.st.css';
export interface Column<T> {
id: string;
header: JSX.Element;
header: React.ReactElement;
cellRenderer: React.ComponentType<ListItemProps<T>>;
}

Expand Down Expand Up @@ -54,7 +54,7 @@ export function DataGrid<T, EL extends HTMLElement>({
columns,
sizes: columnSizes,
}),
[columnSizes, columns]
[columnSizes, columns],
);

const resizers = useMemo(
Expand All @@ -76,7 +76,7 @@ export function DataGrid<T, EL extends HTMLElement>({
window.addEventListener('mousemove', listener);
window.addEventListener('mouseup', endListener);
}),
[columns, columnSizes, updateColumnSizes]
[columns, columnSizes, updateColumnSizes],
);
useEffect(() => {
return () => {
Expand Down
22 changes: 11 additions & 11 deletions packages/components/src/hooks/use-element-dimensions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { MutableRefObject, useCallback, useLayoutEffect, useMemo, useRef } from 'react';
import React, { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
import {
childrenById,
DimensionsById,
Expand All @@ -15,14 +15,14 @@ const getElementDimensions = (element?: Element): ElementDimensions => {
};

const calculateDimensions = <T, EL extends HTMLElement>(
ref: React.RefObject<EL>,
ref: React.RefObject<EL | null>,
items: T[],
size: ElementDimensions | ((t: T) => ElementDimensions) | undefined,
getId: (t: T) => string,
measure: boolean,
sizeCache: Map<string, ElementDimensions>,
oldRes: Record<string, ElementDimensions>,
setObserveTargets?: (targets: Element[]) => void
setObserveTargets?: (targets: Element[]) => void,
): { changed: boolean; res: Record<string, ElementDimensions> } => {
const getDimensions = (item: T) => {
if (size !== undefined) {
Expand Down Expand Up @@ -87,7 +87,7 @@ const calculateDimensions = <T, EL extends HTMLElement>(

export const useSetableObserver = (shouldMeasure: boolean) => {
const listener = useRef<ResizeObserverCallback>(() => undefined);
const observerRef = useRef<ResizeObserver>();
const observerRef = useRef<ResizeObserver>(undefined);
const observed = useRef(new Set<Element>());

useLayoutEffect(() => {
Expand Down Expand Up @@ -135,18 +135,18 @@ export const createSetableMutationObserver = () => {
};

export const useElementDimensions = <T, EL extends HTMLElement>(
ref: React.RefObject<EL>,
ref: React.RefObject<EL | null>,
items: T[],
getId: (item: T) => string,
getItemDimensions: false | ElementDimensions | ((item: T) => ElementDimensions),
observeSubtree = false
): MutableRefObject<DimensionsById> => {
observeSubtree = false,
): React.RefObject<DimensionsById> => {
const shouldMeasure = getItemDimensions === false;
const preMeasured = typeof getItemDimensions === 'boolean' ? undefined : getItemDimensions;
const cache = useRef(new Map<string, ElementDimensions>());
const calculatedSize = useMemo(
() => calculateDimensions(ref, items, preMeasured, getId, false, cache.current, {}).res,
[getId, items, preMeasured, ref]
[getId, items, preMeasured, ref],
);
const unMeasuredDimensions = useRef(calculatedSize);
const dimensions = useRef(calculatedSize);
Expand All @@ -170,7 +170,7 @@ export const useElementDimensions = <T, EL extends HTMLElement>(
true,
cache.current,
dimensions.current,
setTargets
setTargets,
);

if (!changed) {
Expand All @@ -179,7 +179,7 @@ export const useElementDimensions = <T, EL extends HTMLElement>(

return res;
}),
[ref, getId, items, delayedUpdateSizes, preMeasured, setTargets, dimensions]
[ref, getId, items, delayedUpdateSizes, preMeasured, setTargets, dimensions],
);

mutationListener(() => {
Expand Down Expand Up @@ -228,7 +228,7 @@ export const useElementDimensions = <T, EL extends HTMLElement>(
true,
cache.current,
dimensions.current,
setTargets
setTargets,
);

if (changed) {
Expand Down
7 changes: 5 additions & 2 deletions packages/components/src/hooks/use-element-size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import type React from 'react';
import { useEffect, useState } from 'react';
import { getElementSize, getSizeFromDimensions, observeElementDimensions, observeWindowDimensions } from '../common';

export const useElementSize = (element: React.RefObject<HTMLElement> | undefined, sizeAsHeight: boolean): number => {
export const useElementSize = (
element: React.RefObject<HTMLElement | null> | undefined,
sizeAsHeight: boolean,
): number => {
const [size, updateSize] = useState(getElementSize(element?.current, sizeAsHeight));

useEffect(() => {
const cleanup = element?.current
? observeElementDimensions(element.current, (dimensions) =>
updateSize(getSizeFromDimensions(dimensions, sizeAsHeight))
updateSize(getSizeFromDimensions(dimensions, sizeAsHeight)),
)
: observeWindowDimensions((dimensions) => updateSize(getSizeFromDimensions(dimensions, sizeAsHeight)));

Expand Down
6 changes: 3 additions & 3 deletions packages/components/src/hooks/use-element-slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, { useMemo } from 'react';
import type { ElementSlot, PropMapping } from '../common';

// here because in issue with ts transformers in WCS
export const a = (): JSX.Element => <div></div>;
export const a = (): React.ReactElement => <div></div>;

export const defaultRoot: ElementSlot<React.ComponentPropsWithRef<'div'>, 'div'> = {
el: 'div',
Expand Down Expand Up @@ -68,9 +68,9 @@ export const defineElementSlot = <
);
},
create: function <Props extends MinimalProps>(
el: React.ComponentType<Props> | keyof React.ReactHTML,
el: React.ComponentType<Props> | React.HTMLElementType,
props: Partial<Props>,
): ElementSlot<MinimalProps, React.ComponentType<Props> | keyof React.ReactHTML, Props> {
): ElementSlot<MinimalProps, React.ComponentType<Props> | React.HTMLElementType, Props> {
return {
el,
props: props as any,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/hooks/use-keyboard-nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { childrenById, KeyCodes } from '../common';

export const getHandleKeyboardNav = (
elementsParent: React.RefObject<HTMLElement>,
elementsParent: React.RefObject<HTMLElement | null>,
focusedId: string | undefined,
setFocusedId: (id: string) => void,
setSelectedId: (id: string) => void,
Expand Down
10 changes: 5 additions & 5 deletions packages/components/src/hooks/use-position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ const getItemPositionInParent = (el: HTMLElement) => {
const isSamePosition = (p1: Position, p2: Position) => p1.x === p2.x && p1.y === p2.y;

const useAfterRenderEffect = <T, U = null>(
element: React.RefObject<HTMLElement>,
element: React.RefObject<HTMLElement | null>,
onElementUpdate: (el: HTMLElement) => T | typeof unchanged,
watch?: Watch,
def: T | U = null as unknown as U
def: T | U = null as unknown as U,
): T | U => {
const [state, update] = useState(def);
const delayedUpdate = useDelayedUpdateState(update);
Expand All @@ -39,11 +39,11 @@ const useAfterRenderEffect = <T, U = null>(
};

export const usePositionInParent = (
element: React.RefObject<HTMLElement>,
watchPosition: Position | boolean = false
element: React.RefObject<HTMLElement | null>,
watchPosition: Position | boolean = false,
): Position => {
const watch: Watch = typeof watchPosition === 'object' ? 'ignore' : watchPosition ? 'timer' : 'measure-once';
const lastValRef = useRef<Position>();
const lastValRef = useRef<Position>(undefined);
const onTimer = useCallback((el: HTMLElement) => {
if (!el || !el.parentElement) {
return unchanged;
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/hooks/use-scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const useScroll = ({
}: {
isHorizontal?: boolean;
disabled?: boolean;
ref?: React.RefObject<HTMLElement>;
ref?: React.RefObject<HTMLElement | null>;
}): number => {
const trigger = useDelayedUpdate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ProcessedControlledState } from './use-state-controls';

export type KeyboardSelectMeta = 'keyboard';
export interface TreeViewKeyboardInteractionsParams {
eventRoots?: React.RefObject<HTMLElement>[];
eventRoots?: React.RefObject<HTMLElement | null>[];
focusedItemId: string | undefined;
open: (itemId: string) => void;
close: (itemId: string) => void;
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/icons/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { memo } from 'react';
import type { IconProps } from './types';
import { classes, st } from './icon.st.css';

export const IconFactory = (icon: JSX.Element, displayName: string, width = 18, height = 18) => {
export const IconFactory = (icon: React.ReactElement, displayName: string, width = 18, height = 18) => {
const Comp = memo<IconProps>((props) => (
<svg viewBox={`0 0 ${width} ${height}`} {...props} className={st(classes.root, props.className)}>
{icon}
Expand Down
15 changes: 10 additions & 5 deletions packages/components/src/list/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export interface ListProps<T> {
disableKeyboard?: boolean;
}

export type List<T> = (props: ListProps<T>) => JSX.Element;
export type List<T> = (props: ListProps<T>) => React.ReactElement;

export function List<T, EL extends HTMLElement = HTMLDivElement>({
listRoot,
Expand All @@ -71,30 +71,35 @@ export function List<T, EL extends HTMLElement = HTMLDivElement>({
onItemMount,
onItemUnmount,
disableKeyboard,
}: ListProps<T>): JSX.Element {
}: ListProps<T>): React.ReactElement {
const [selectedId, setSelectedId] = useStateControls(selectionControl, undefined);
const [focusedId, setFocusedId] = useStateControls(focusControl, undefined);
const [prevSelectedId, setPrevSelectedId] = useState(selectedId);
if (selectedId !== prevSelectedId) {
setFocusedId(selectedId);
setPrevSelectedId(selectedId);
}
const defaultRef = useRef<EL>();
const defaultRef = useRef<EL>(null);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const actualRef = listRoot?.props?.ref || defaultRef;

const onClick = useIdListener(setSelectedId);
const onKeyPress = disableKeyboard
? () => {}
: getHandleKeyboardNav(actualRef as React.RefObject<HTMLElement>, focusedId, setFocusedId, setSelectedId);
: getHandleKeyboardNav(
actualRef as React.RefObject<HTMLElement | null>,
focusedId,
setFocusedId,
setSelectedId,
);
if (transmitKeyPress) {
transmitKeyPress(callInternalFirst(onKeyPress, listRoot?.props?.onKeyPress));
}
return (
<ListRootSlot
slot={listRoot}
props={{
ref: actualRef as React.RefObject<HTMLDivElement>,
ref: actualRef as React.RefObject<HTMLDivElement | null>,
onClick,
onKeyPress,
onKeyDown: onKeyPress,
Expand Down
Loading

0 comments on commit 7f21061

Please sign in to comment.