From 0684533f3dc8a74fd5a9451865457cc1aa0b3787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommy=20Jos=C3=A9povic?= <44372776+tjosepo@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:53:59 -0500 Subject: [PATCH 1/4] Replace element.ref with element.props.ref --- .../components/src/accordion/src/useAccordionItems.ts | 7 ++++--- .../components/src/collection/src/getElementRef.tsx | 10 ++++++++++ packages/components/src/collection/src/index.ts | 1 + .../components/src/collection/src/useCollection.ts | 10 +++++----- packages/components/src/shared/src/augmentElement.tsx | 3 ++- packages/components/src/tabs/src/useTabsItems.ts | 5 +++-- 6 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 packages/components/src/collection/src/getElementRef.tsx diff --git a/packages/components/src/accordion/src/useAccordionItems.ts b/packages/components/src/accordion/src/useAccordionItems.ts index bbcbc1997..79387ab38 100644 --- a/packages/components/src/accordion/src/useAccordionItems.ts +++ b/packages/components/src/accordion/src/useAccordionItems.ts @@ -1,6 +1,7 @@ -import { Children, ReactElement, ReactNode, Ref, RefAttributes, useMemo } from "react"; +import { Children, ReactElement, ReactNode, Ref, useMemo } from "react"; import { Content, Header } from "../../placeholders/index.ts"; import { isNil, mergeProps } from "../../shared/index.ts"; +import { getElementRef } from "@components/collection/index.ts"; export interface AccordionBuilderItem { header: AccordionBuilderHeader; @@ -47,14 +48,14 @@ export class AccordionBuilder { // elementType: isHeading(header.type) ? undefined : header.type, elementType: header.type, props: mergeProps(header.props, element.props), - ref: (header as RefAttributes).ref as Ref + ref: getElementRef(header), }; const panelProps: AccordionBuilderItem["header"] = { // Use a custom type if available otherwise let the AccordionPanel component choose his default type. elementType: content.type !== Content ? content.type : undefined, props: content.props, - ref: (content as RefAttributes).ref as Ref + ref: getElementRef(content), }; return { diff --git a/packages/components/src/collection/src/getElementRef.tsx b/packages/components/src/collection/src/getElementRef.tsx new file mode 100644 index 000000000..67dde2516 --- /dev/null +++ b/packages/components/src/collection/src/getElementRef.tsx @@ -0,0 +1,10 @@ +import { ReactElement, Ref } from "react"; + +/** + * `ref` is passed as prop in React 19, whereas `ref` is directly attached to + * children in React 18 below check is to ensure `ref` is accessible in both + * cases + */ +export function getElementRef(element: ReactElement): Ref { + return element.props.propertyIsEnumerable("ref") ? element.props.ref : (element as any).ref; +} \ No newline at end of file diff --git a/packages/components/src/collection/src/index.ts b/packages/components/src/collection/src/index.ts index 525caf9fe..126d131ff 100644 --- a/packages/components/src/collection/src/index.ts +++ b/packages/components/src/collection/src/index.ts @@ -7,3 +7,4 @@ export * from "./getItemText.tsx"; export * from "./reduceCollection.ts"; export * from "./Item.tsx"; export * from "./Section.tsx"; +export * from "./getElementRef.tsx"; diff --git a/packages/components/src/collection/src/useCollection.ts b/packages/components/src/collection/src/useCollection.ts index 1eec9e832..24fa26b41 100644 --- a/packages/components/src/collection/src/useCollection.ts +++ b/packages/components/src/collection/src/useCollection.ts @@ -1,6 +1,6 @@ -import { Children, ElementType, ReactElement, ReactNode, Ref, RefAttributes, useMemo } from "react"; +import { Children, ElementType, ReactElement, ReactNode, Ref, useMemo } from "react"; import { Divider } from "../../divider/index.ts"; -import { Item, Section } from "../../collection/index.ts"; +import { getElementRef, Item, Section } from "../../collection/index.ts"; import { TooltipTrigger, parseTooltipTrigger } from "../../tooltip/index.ts"; import { isNil, resolveChildren } from "../../shared/index.ts"; @@ -75,7 +75,7 @@ export class CollectionBuilder { index, key: !isNil(element.key) ? element.key.toString().replace(".", "").replace("$", "") : index.toString(), props, - ref: (element as RefAttributes).ref as Ref, + ref: getElementRef(element), type: NodeType.item }; } @@ -97,7 +97,7 @@ export class CollectionBuilder { items, key: index.toString(), props, - ref: (element as RefAttributes).ref as Ref, + ref: getElementRef(element), type: NodeType.section }; } @@ -113,7 +113,7 @@ export class CollectionBuilder { index, key: index.toString(), props, - ref: (element as RefAttributes).ref as Ref, + ref: getElementRef(element), type: NodeType.divider }; } diff --git a/packages/components/src/shared/src/augmentElement.tsx b/packages/components/src/shared/src/augmentElement.tsx index 0c8bf7725..0d1a41a49 100644 --- a/packages/components/src/shared/src/augmentElement.tsx +++ b/packages/components/src/shared/src/augmentElement.tsx @@ -1,9 +1,10 @@ import { ReactElement, RefAttributes, cloneElement } from "react"; import { Size, SizeAdapter, normalizeSize } from "./size.ts"; import { mergeProps } from "./mergeProps.ts"; +import { getElementRef } from "@components/collection/index.ts"; export function augmentElement(element: ReactElement & RefAttributes, newProps: Record) { - const augmentedProps = mergeProps({ ...element.props, ref: element.ref }, newProps); + const augmentedProps = mergeProps({ ...element.props, ref: getElementRef(element) }, newProps); return cloneElement(element, augmentedProps); } diff --git a/packages/components/src/tabs/src/useTabsItems.ts b/packages/components/src/tabs/src/useTabsItems.ts index f391009ac..9d508a0a8 100644 --- a/packages/components/src/tabs/src/useTabsItems.ts +++ b/packages/components/src/tabs/src/useTabsItems.ts @@ -1,6 +1,7 @@ import { Children, ReactElement, ReactNode, Ref, RefAttributes, useMemo } from "react"; import { Content, Header } from "../../placeholders/index.ts"; import { isNil, mergeProps, resolveChildren } from "../../shared/index.ts"; +import { getElementRef } from "@components/collection/index.ts"; export interface PanelType { disabled?: boolean; @@ -56,7 +57,7 @@ export class TabsBuilder { key, panelId, props: mergeProps(header.props, element.props), - ref: header.ref as Ref, + ref: getElementRef(header), tabId }); @@ -69,7 +70,7 @@ export class TabsBuilder { key, panelId, props: content.props, - ref: content.ref as Ref, + ref: getElementRef(content), tabId }); }); From 98511b741a8505cb7ae5319e0ca1c80421866fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommy=20Jos=C3=A9povic?= <44372776+tjosepo@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:55:21 -0500 Subject: [PATCH 2/4] changeset --- .changeset/empty-years-type.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/empty-years-type.md diff --git a/.changeset/empty-years-type.md b/.changeset/empty-years-type.md new file mode 100644 index 000000000..84356426a --- /dev/null +++ b/.changeset/empty-years-type.md @@ -0,0 +1,5 @@ +--- +"@workleap/orbiter-ui": patch +--- + +Replace element.ref with element.props.ref for React 19 compatibility From 1f25189efd21c65bcdabab060e234673ec0c7cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommy=20Jos=C3=A9povic?= <44372776+tjosepo@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:58:07 -0500 Subject: [PATCH 3/4] lint --- packages/components/src/accordion/src/useAccordionItems.ts | 4 ++-- packages/components/src/collection/src/getElementRef.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/accordion/src/useAccordionItems.ts b/packages/components/src/accordion/src/useAccordionItems.ts index 79387ab38..5d83c5025 100644 --- a/packages/components/src/accordion/src/useAccordionItems.ts +++ b/packages/components/src/accordion/src/useAccordionItems.ts @@ -48,14 +48,14 @@ export class AccordionBuilder { // elementType: isHeading(header.type) ? undefined : header.type, elementType: header.type, props: mergeProps(header.props, element.props), - ref: getElementRef(header), + ref: getElementRef(header) }; const panelProps: AccordionBuilderItem["header"] = { // Use a custom type if available otherwise let the AccordionPanel component choose his default type. elementType: content.type !== Content ? content.type : undefined, props: content.props, - ref: getElementRef(content), + ref: getElementRef(content) }; return { diff --git a/packages/components/src/collection/src/getElementRef.tsx b/packages/components/src/collection/src/getElementRef.tsx index 67dde2516..7f80435b8 100644 --- a/packages/components/src/collection/src/getElementRef.tsx +++ b/packages/components/src/collection/src/getElementRef.tsx @@ -6,5 +6,5 @@ import { ReactElement, Ref } from "react"; * cases */ export function getElementRef(element: ReactElement): Ref { - return element.props.propertyIsEnumerable("ref") ? element.props.ref : (element as any).ref; + return element.props.propertyIsEnumerable("ref") ? element.props.ref : (element as any).ref; } \ No newline at end of file From e424e1edc111058521a9da1fa2440effca4ac673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommy=20Jos=C3=A9povic?= <44372776+tjosepo@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:19:00 -0500 Subject: [PATCH 4/4] remove cicular import --- packages/components/src/accordion/src/useAccordionItems.ts | 2 +- packages/components/src/collection/src/index.ts | 1 - packages/components/src/collection/src/useCollection.ts | 3 ++- packages/components/src/shared/src/augmentElement.tsx | 2 +- .../src/{collection => shared}/src/getElementRef.tsx | 0 packages/components/src/tabs/src/useTabsItems.ts | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename packages/components/src/{collection => shared}/src/getElementRef.tsx (100%) diff --git a/packages/components/src/accordion/src/useAccordionItems.ts b/packages/components/src/accordion/src/useAccordionItems.ts index 5d83c5025..36ded84eb 100644 --- a/packages/components/src/accordion/src/useAccordionItems.ts +++ b/packages/components/src/accordion/src/useAccordionItems.ts @@ -1,7 +1,7 @@ import { Children, ReactElement, ReactNode, Ref, useMemo } from "react"; import { Content, Header } from "../../placeholders/index.ts"; import { isNil, mergeProps } from "../../shared/index.ts"; -import { getElementRef } from "@components/collection/index.ts"; +import { getElementRef } from "../../shared/src/getElementRef.tsx"; export interface AccordionBuilderItem { header: AccordionBuilderHeader; diff --git a/packages/components/src/collection/src/index.ts b/packages/components/src/collection/src/index.ts index 126d131ff..525caf9fe 100644 --- a/packages/components/src/collection/src/index.ts +++ b/packages/components/src/collection/src/index.ts @@ -7,4 +7,3 @@ export * from "./getItemText.tsx"; export * from "./reduceCollection.ts"; export * from "./Item.tsx"; export * from "./Section.tsx"; -export * from "./getElementRef.tsx"; diff --git a/packages/components/src/collection/src/useCollection.ts b/packages/components/src/collection/src/useCollection.ts index 24fa26b41..8bbda3934 100644 --- a/packages/components/src/collection/src/useCollection.ts +++ b/packages/components/src/collection/src/useCollection.ts @@ -1,6 +1,7 @@ import { Children, ElementType, ReactElement, ReactNode, Ref, useMemo } from "react"; import { Divider } from "../../divider/index.ts"; -import { getElementRef, Item, Section } from "../../collection/index.ts"; +import { Item, Section } from "../../collection/index.ts"; +import { getElementRef } from "../../shared/src/getElementRef.tsx"; import { TooltipTrigger, parseTooltipTrigger } from "../../tooltip/index.ts"; import { isNil, resolveChildren } from "../../shared/index.ts"; diff --git a/packages/components/src/shared/src/augmentElement.tsx b/packages/components/src/shared/src/augmentElement.tsx index 0d1a41a49..6389635cf 100644 --- a/packages/components/src/shared/src/augmentElement.tsx +++ b/packages/components/src/shared/src/augmentElement.tsx @@ -1,7 +1,7 @@ import { ReactElement, RefAttributes, cloneElement } from "react"; import { Size, SizeAdapter, normalizeSize } from "./size.ts"; import { mergeProps } from "./mergeProps.ts"; -import { getElementRef } from "@components/collection/index.ts"; +import { getElementRef } from "./getElementRef.tsx"; export function augmentElement(element: ReactElement & RefAttributes, newProps: Record) { const augmentedProps = mergeProps({ ...element.props, ref: getElementRef(element) }, newProps); diff --git a/packages/components/src/collection/src/getElementRef.tsx b/packages/components/src/shared/src/getElementRef.tsx similarity index 100% rename from packages/components/src/collection/src/getElementRef.tsx rename to packages/components/src/shared/src/getElementRef.tsx diff --git a/packages/components/src/tabs/src/useTabsItems.ts b/packages/components/src/tabs/src/useTabsItems.ts index 9d508a0a8..5e0d7d201 100644 --- a/packages/components/src/tabs/src/useTabsItems.ts +++ b/packages/components/src/tabs/src/useTabsItems.ts @@ -1,7 +1,7 @@ import { Children, ReactElement, ReactNode, Ref, RefAttributes, useMemo } from "react"; import { Content, Header } from "../../placeholders/index.ts"; import { isNil, mergeProps, resolveChildren } from "../../shared/index.ts"; -import { getElementRef } from "@components/collection/index.ts"; +import { getElementRef } from "../../shared/src/getElementRef.tsx"; export interface PanelType { disabled?: boolean;