From ac26cc581b6c79288d2d7d59646fb07b4a35fc04 Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Fri, 3 Apr 2026 12:08:37 -0400 Subject: [PATCH 1/3] feat: support React 19 --- easy-ui-react/package.json | 19 +- easy-ui-react/src/CodeBlock/context.tsx | 2 +- .../src/ColorPicker/ColorPicker.test.tsx | 2 +- .../ColorPickerInputField.test.tsx | 2 +- easy-ui-react/src/DataGrid/Table.tsx | 2 +- easy-ui-react/src/Drawer/DrawerContainer.tsx | 2 +- easy-ui-react/src/Drawer/DrawerTrigger.tsx | 4 +- easy-ui-react/src/Drawer/context.ts | 4 +- .../src/Drawer/useIntersectionDetection.ts | 4 +- easy-ui-react/src/FormLayout/Section.tsx | 2 +- easy-ui-react/src/Menu/MenuTrigger.tsx | 2 +- easy-ui-react/src/Modal/ModalContainer.tsx | 2 +- easy-ui-react/src/Modal/ModalTrigger.tsx | 4 +- easy-ui-react/src/Modal/context.tsx | 6 +- .../src/Modal/useIntersectionDetection.ts | 4 +- .../src/MultiSelect/MultiSelectDropdown.tsx | 6 +- easy-ui-react/src/MultiSelect/utilities.ts | 2 +- .../Notification/NotificationTransition.tsx | 2 +- .../ProductLayoutTabbedContent.tsx | 2 +- easy-ui-react/src/Provider/Provider.tsx | 2 +- easy-ui-react/src/SearchNav/CTAGroup.tsx | 4 +- .../src/SearchNav/CondensedSearchNav.tsx | 4 +- easy-ui-react/src/SearchNav/utilities.ts | 8 +- .../src/TabPanels/TabPanelsPanels.tsx | 4 +- easy-ui-react/src/Tooltip/Tooltip.tsx | 2 +- .../src/VerticalNav/SupplementaryAction.tsx | 2 +- easy-ui-react/src/types.ts | 2 +- easy-ui-react/src/utilities/Noop.tsx | 2 +- easy-ui-react/src/utilities/react.ts | 6 +- easy-ui-react/src/utilities/test.ts | 4 +- easy-ui-react/vite.config.mts | 2 +- easy-ui-react/vitest.setup.ts | 2 + package-lock.json | 335 +++++++++--------- package.json | 6 +- 34 files changed, 230 insertions(+), 228 deletions(-) diff --git a/easy-ui-react/package.json b/easy-ui-react/package.json index a86b12b15..f823b3b56 100644 --- a/easy-ui-react/package.json +++ b/easy-ui-react/package.json @@ -35,36 +35,37 @@ "@react-aria/utils": "^3.32.0", "@react-stately/toast": "^3.1.2", "@react-types/shared": "^3.32.1", - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.1", "lodash": "^4.17.21", "overlayscrollbars": "^2.3.0", "overlayscrollbars-react": "^0.5.6", "react-aria": "^3.45.0", "react-aria-components": "^1.14.0", - "react-is": "^18.3.1", "react-stately": "^3.43.0", "react-syntax-highlighter": "^15.6.1", "react-transition-group": "^4.4.5", "use-clipboard-copy": "^0.2.0" }, "peerDependencies": { - "react": "^16.14.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "devDependencies": { "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.3.0", + "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/lodash": "^4.17.18", - "@types/react-is": "^18.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/react-is": "^19.0.0", "@types/react-syntax-highlighter": "^15.5.13", "@types/react-transition-group": "^4.4.12", "@vitejs/plugin-react": "^4.6.0", "glob": "^10.2.5", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-is": "^19.0.0", "sass": "^1.89.2", "vite-plugin-react-remove-attributes": "^1.0.3", "vite-plugin-static-copy": "^3.1.2" diff --git a/easy-ui-react/src/CodeBlock/context.tsx b/easy-ui-react/src/CodeBlock/context.tsx index 7b3359541..0964aec61 100644 --- a/easy-ui-react/src/CodeBlock/context.tsx +++ b/easy-ui-react/src/CodeBlock/context.tsx @@ -3,7 +3,7 @@ import { SnippetLanguage } from "../CodeSnippet/SyntaxHighlighter"; export type CodeBlockContextType = { languages: SnippetLanguage[]; - snippet: ReactElement; + snippet: ReactElement; language: SnippetLanguage; onLanguageChange: (language: SnippetLanguage) => void; }; diff --git a/easy-ui-react/src/ColorPicker/ColorPicker.test.tsx b/easy-ui-react/src/ColorPicker/ColorPicker.test.tsx index 106835313..54547764a 100644 --- a/easy-ui-react/src/ColorPicker/ColorPicker.test.tsx +++ b/easy-ui-react/src/ColorPicker/ColorPicker.test.tsx @@ -74,6 +74,6 @@ describe("", () => { expect(screen.getAllByLabelText("Color picker").length).toBeGreaterThan(0); await userTab(user); await userKeyboard(user, "{ArrowRight}{ArrowUp}{ArrowLeft}{ArrowDown}"); - expect(screen.getByText("hsla(0, 100%, 49.5%, 1)")).toBeInTheDocument(); + expect(screen.getByText("hsla(0, 98.02%, 50%, 1)")).toBeInTheDocument(); }); }); diff --git a/easy-ui-react/src/ColorPicker/ColorPickerInputField.test.tsx b/easy-ui-react/src/ColorPicker/ColorPickerInputField.test.tsx index 55f3e8655..1a48c6a15 100644 --- a/easy-ui-react/src/ColorPicker/ColorPickerInputField.test.tsx +++ b/easy-ui-react/src/ColorPicker/ColorPickerInputField.test.tsx @@ -61,6 +61,6 @@ describe("", () => { await userTab(user); await userKeyboard(user, "{ArrowRight}{ArrowUp}{ArrowLeft}{ArrowDown}"); expect(handleChange).toHaveBeenCalled(); - expect(screen.getByLabelText("Primary color")).toHaveValue("#FC0000"); + expect(screen.getByLabelText("Primary color")).toHaveValue("#FC0303"); }); }); diff --git a/easy-ui-react/src/DataGrid/Table.tsx b/easy-ui-react/src/DataGrid/Table.tsx index 3a2563280..c4dd1070e 100644 --- a/easy-ui-react/src/DataGrid/Table.tsx +++ b/easy-ui-react/src/DataGrid/Table.tsx @@ -26,7 +26,7 @@ type TableProps = Omit< DataGridProps, "children" > & { - children?: [ReactElement, ReactElement]; + children?: [ReactElement, ReactElement]; }; export function Table( diff --git a/easy-ui-react/src/Drawer/DrawerContainer.tsx b/easy-ui-react/src/Drawer/DrawerContainer.tsx index 5bdb524e7..daa618f0a 100644 --- a/easy-ui-react/src/Drawer/DrawerContainer.tsx +++ b/easy-ui-react/src/Drawer/DrawerContainer.tsx @@ -43,7 +43,7 @@ export function DrawerContainer(props: DrawerContainerProps) { throw new Error("Only a single child can be passed to ModalContainer."); } - const [lastChild, setLastChild] = useState(null); + const [lastChild, setLastChild] = useState | null>(null); // React.Children.toArray mutates the children, and we need them to be stable // between renders so that the lastChild comparison works. diff --git a/easy-ui-react/src/Drawer/DrawerTrigger.tsx b/easy-ui-react/src/Drawer/DrawerTrigger.tsx index 268f4b311..174e1fb93 100644 --- a/easy-ui-react/src/Drawer/DrawerTrigger.tsx +++ b/easy-ui-react/src/Drawer/DrawerTrigger.tsx @@ -4,13 +4,13 @@ import { useOverlayTriggerState } from "react-stately"; import { DrawerUnderlay } from "./DrawerUnderlay"; import { DrawerTriggerContext } from "./context"; -export type CloseableDrawerElement = (close: () => void) => ReactElement; +export type CloseableDrawerElement = (close: () => void) => ReactElement; export type DrawerTriggerProps = { /** * Content of modal trigger. Must be exactly two elements. */ - children: [ReactElement, CloseableDrawerElement | ReactElement]; + children: [ReactElement, CloseableDrawerElement | ReactElement]; /** * Whether the modal is open by default (uncontrolled). diff --git a/easy-ui-react/src/Drawer/context.ts b/easy-ui-react/src/Drawer/context.ts index 124be9c0d..395881e26 100644 --- a/easy-ui-react/src/Drawer/context.ts +++ b/easy-ui-react/src/Drawer/context.ts @@ -6,8 +6,8 @@ export type DrawerContextType = { dialogProps: DOMAttributes; titleProps: DOMAttributes; isHeaderStuck: boolean; - bodyRef: RefObject; - headerInterceptorRef: RefObject; + bodyRef: RefObject; + headerInterceptorRef: RefObject; }; type DrawerTriggerContextType = { diff --git a/easy-ui-react/src/Drawer/useIntersectionDetection.ts b/easy-ui-react/src/Drawer/useIntersectionDetection.ts index b79eac267..012a9a4ee 100644 --- a/easy-ui-react/src/Drawer/useIntersectionDetection.ts +++ b/easy-ui-react/src/Drawer/useIntersectionDetection.ts @@ -8,8 +8,8 @@ import { RefObject, useEffect, useState } from "react"; * @returns when element is intersecting */ export function useIntersectionDetection( - targetRef: RefObject, - scrollRef: RefObject, + targetRef: RefObject, + scrollRef: RefObject, ) { const [isStuck, setIsStuck] = useState(false); diff --git a/easy-ui-react/src/FormLayout/Section.tsx b/easy-ui-react/src/FormLayout/Section.tsx index 5160bc388..ace0b61d0 100644 --- a/easy-ui-react/src/FormLayout/Section.tsx +++ b/easy-ui-react/src/FormLayout/Section.tsx @@ -67,6 +67,6 @@ function findSectionTitleInChildren(children: ReactNode) { const firstTitleOrSectionType = firstTitleOrSection.type as NamedExoticComponent; return firstTitleOrSectionType.displayName === "FormLayout.Title" - ? firstTitleOrSection.props.children + ? (firstTitleOrSection.props as { children?: ReactNode }).children : null; } diff --git a/easy-ui-react/src/Menu/MenuTrigger.tsx b/easy-ui-react/src/Menu/MenuTrigger.tsx index 7e473ea1d..c009dd30a 100644 --- a/easy-ui-react/src/Menu/MenuTrigger.tsx +++ b/easy-ui-react/src/Menu/MenuTrigger.tsx @@ -4,7 +4,7 @@ import { useInternalMenuContext } from "./MenuContext"; export type MenuTriggerProps = { /** The element that will activate the menu. */ - children: ReactElement; + children: ReactElement; }; export function MenuTrigger(props: MenuTriggerProps) { diff --git a/easy-ui-react/src/Modal/ModalContainer.tsx b/easy-ui-react/src/Modal/ModalContainer.tsx index 741c40321..e07e4d85a 100644 --- a/easy-ui-react/src/Modal/ModalContainer.tsx +++ b/easy-ui-react/src/Modal/ModalContainer.tsx @@ -37,7 +37,7 @@ export function ModalContainer(props: ModalContainerProps) { throw new Error("Only a single child can be passed to ModalContainer."); } - const [lastChild, setLastChild] = useState(null); + const [lastChild, setLastChild] = useState | null>(null); // React.Children.toArray mutates the children, and we need them to be stable // between renders so that the lastChild comparison works. diff --git a/easy-ui-react/src/Modal/ModalTrigger.tsx b/easy-ui-react/src/Modal/ModalTrigger.tsx index d603584fe..b4172d7c2 100644 --- a/easy-ui-react/src/Modal/ModalTrigger.tsx +++ b/easy-ui-react/src/Modal/ModalTrigger.tsx @@ -4,13 +4,13 @@ import { useOverlayTriggerState } from "react-stately"; import { ModalUnderlay } from "./ModalUnderlay"; import { ModalTriggerProvider } from "./context"; -export type CloseableModalElement = (close: () => void) => ReactElement; +export type CloseableModalElement = (close: () => void) => ReactElement; export type ModalTriggerProps = { /** * Content of modal trigger. Must be exactly two elements. */ - children: [ReactElement, CloseableModalElement | ReactElement]; + children: [ReactElement, CloseableModalElement | ReactElement]; /** * Whether the modal is open by default (uncontrolled). diff --git a/easy-ui-react/src/Modal/context.tsx b/easy-ui-react/src/Modal/context.tsx index e9889b3bf..9b97ab9b9 100644 --- a/easy-ui-react/src/Modal/context.tsx +++ b/easy-ui-react/src/Modal/context.tsx @@ -15,9 +15,9 @@ export type ModalContextType = { titleProps: DOMAttributes; isHeaderStuck: boolean; isFooterStuck: boolean; - bodyRef: RefObject; - headerInterceptorRef: RefObject; - footerInterceptorRef: RefObject; + bodyRef: RefObject; + headerInterceptorRef: RefObject; + footerInterceptorRef: RefObject; }; type ModalTriggerContextType = { diff --git a/easy-ui-react/src/Modal/useIntersectionDetection.ts b/easy-ui-react/src/Modal/useIntersectionDetection.ts index b79eac267..012a9a4ee 100644 --- a/easy-ui-react/src/Modal/useIntersectionDetection.ts +++ b/easy-ui-react/src/Modal/useIntersectionDetection.ts @@ -8,8 +8,8 @@ import { RefObject, useEffect, useState } from "react"; * @returns when element is intersecting */ export function useIntersectionDetection( - targetRef: RefObject, - scrollRef: RefObject, + targetRef: RefObject, + scrollRef: RefObject, ) { const [isStuck, setIsStuck] = useState(false); diff --git a/easy-ui-react/src/MultiSelect/MultiSelectDropdown.tsx b/easy-ui-react/src/MultiSelect/MultiSelectDropdown.tsx index dfa904d0d..3e135d256 100644 --- a/easy-ui-react/src/MultiSelect/MultiSelectDropdown.tsx +++ b/easy-ui-react/src/MultiSelect/MultiSelectDropdown.tsx @@ -25,8 +25,8 @@ type MultiSelectDropdownProps = { children: React.ReactNode | ((item: T) => React.ReactNode); isLoading?: AsyncListData["isLoading"]; maxItemsUntilScroll?: MenuOverlayProps["maxItemsUntilScroll"]; - menuRef: RefObject; - triggerRef: RefObject; + menuRef: RefObject; + triggerRef: RefObject; width: number; }; @@ -106,7 +106,7 @@ function ScrollContainer({ scrollRef, children, }: { - scrollRef: RefObject; + scrollRef: RefObject; children: ReactNode; }) { useScrollbar(scrollRef, "ezui-os-theme-overlay"); diff --git a/easy-ui-react/src/MultiSelect/utilities.ts b/easy-ui-react/src/MultiSelect/utilities.ts index 752d38681..879e1d185 100644 --- a/easy-ui-react/src/MultiSelect/utilities.ts +++ b/easy-ui-react/src/MultiSelect/utilities.ts @@ -1,7 +1,7 @@ import { useResizeObserver } from "@react-aria/utils"; import { RefObject, useState } from "react"; -export function useElementWidth(elementRef: RefObject) { +export function useElementWidth(elementRef: RefObject) { const [width, setWidth] = useState(0); useResizeObserver({ diff --git a/easy-ui-react/src/Notification/NotificationTransition.tsx b/easy-ui-react/src/Notification/NotificationTransition.tsx index 345d33e04..e16a69c3a 100644 --- a/easy-ui-react/src/Notification/NotificationTransition.tsx +++ b/easy-ui-react/src/Notification/NotificationTransition.tsx @@ -8,7 +8,7 @@ export type NotificationTransitionProps = { /** * Children function that receives a ref to the node being transitioned. */ - children: ChildrenFunction<{ nodeRef: RefObject }>; + children: ChildrenFunction<{ nodeRef: RefObject }>; /** * Unique key for the transition. diff --git a/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx b/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx index fb3730173..b72645182 100644 --- a/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx +++ b/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx @@ -14,7 +14,7 @@ export type ProductLayoutTabbedContentProps = AriaLabelingProps & { * The tabs to display for nested content. Uses a `` and should * contain an array of ``s. */ - tabs: ReactElement[]; + tabs: ReactElement[]; }; export function ProductLayoutTabbedContent( diff --git a/easy-ui-react/src/Provider/Provider.tsx b/easy-ui-react/src/Provider/Provider.tsx index 17e5d3068..4d2405f0c 100644 --- a/easy-ui-react/src/Provider/Provider.tsx +++ b/easy-ui-react/src/Provider/Provider.tsx @@ -28,7 +28,7 @@ export type EasyUIRouterProviderProps = { export type ProviderProps = { /** Component tree */ - children: ReactElement; + children: ReactElement; /** Theme to apply to Easy UI. Use `createTheme()` to build theme object. */ theme?: ThemeCreator; /** Color scheme to apply to Easy UI. */ diff --git a/easy-ui-react/src/SearchNav/CTAGroup.tsx b/easy-ui-react/src/SearchNav/CTAGroup.tsx index 4869d2f9d..1322bae0e 100644 --- a/easy-ui-react/src/SearchNav/CTAGroup.tsx +++ b/easy-ui-react/src/SearchNav/CTAGroup.tsx @@ -38,7 +38,7 @@ export function CTAGroup(_props: CTAGroupProps) {
{secondaryCTAItems?.map((item, index) => { const isLastChild = index === totalItems - 1; - const itemEle = item as ReactElement; + const itemEle = item as ReactElement; return ( {itemEle} @@ -64,7 +64,7 @@ export function CTAGroup(_props: CTAGroupProps) { {secondaryCTAItems?.map((item) => { - const itemEle = item as ReactElement; + const itemEle = item as ReactElement; return ( {selectorChildren?.map((item) => { - const itemEle = item as ReactElement; + const itemEle = item as ReactElement; return ( {itemEle.props.children} @@ -77,7 +77,7 @@ export function CondensedSearchNav() { {secondaryCTAItems?.map((item) => { - const itemEle = item as ReactElement; + const itemEle = item as ReactElement; return ( ; const logoGroupChildren = flattenChildren(logoGroupElement.props.children); const logoDisplayName = getDisplayNameFromReactNode(logoGroupChildren[0]); if (logoDisplayName !== "SearchNav.Logo") { @@ -36,7 +36,7 @@ export function getLogoGroupChildren(logoGroup: ReactNode) { ) === "SearchNav.Selector" ) { selector = logoGroupChildren[logoGroupChildren.length - 1]; - const selectorElem = selector as ReactElement; + const selectorElem = selector as ReactElement; selectorChildren = flattenChildren(selectorElem.props.children); } @@ -63,7 +63,7 @@ export function getCTAGroupChildren(ctaGroup: ReactNode) { let secondaryCTAItems; let primaryCTAItem; if (getDisplayNameFromReactNode(ctaGroup) === "SearchNav.CTAGroup") { - const ctaGroupElement = ctaGroup as ReactElement; + const ctaGroupElement = ctaGroup as ReactElement; secondaryCTAItems = filterChildrenByDisplayName( ctaGroupElement.props.children, "SearchNav.SecondaryCTAItem", @@ -92,7 +92,7 @@ export function getCTAGroupChildren(ctaGroup: ReactNode) { export function getSelectorLabel(selector: ReactNode) { let selectorLabel; if (selector) { - const selectorElem = selector as ReactElement; + const selectorElem = selector as ReactElement; const { "aria-label": label } = selectorElem.props; selectorLabel = label; } diff --git a/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx b/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx index 97897f269..c96739f59 100644 --- a/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx +++ b/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx @@ -16,7 +16,7 @@ type TabPanelsPanelsProps = { * The contents of each tab. Item keys should match the key of the * corresponding `` within the `` element. */ - children: ReactElement | ReactElement[]; + children: ReactElement | ReactElement[]; }; type TabPanelProps = AriaTabPanelProps & { @@ -77,7 +77,7 @@ function TabPanelsPanel({ state, ...props }: TabPanelProps) { React.cloneElement(children, { ref, ...tabPanelProps, - ...(children as ReactElement).props, + ...(children as ReactElement).props, }) ) : (
diff --git a/easy-ui-react/src/Tooltip/Tooltip.tsx b/easy-ui-react/src/Tooltip/Tooltip.tsx index 74e54aa78..e5ea41e92 100644 --- a/easy-ui-react/src/Tooltip/Tooltip.tsx +++ b/easy-ui-react/src/Tooltip/Tooltip.tsx @@ -28,7 +28,7 @@ const CLOSE_DELAY = 250; export type TooltipProps = { /** The element that will activate to tooltip. */ - children: ReactElement; + children: ReactElement; /** The content to display within the tooltip. */ content: ReactNode; diff --git a/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx b/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx index 5c915a50c..1ba4e2eab 100644 --- a/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx +++ b/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx @@ -46,7 +46,7 @@ export const SupplementaryAction = forwardRef< }) as { ( props: SupplementaryActionProps & { ref?: DOMRef }, - ): ReactElement; + ): ReactElement; displayName?: string; }; diff --git a/easy-ui-react/src/types.ts b/easy-ui-react/src/types.ts index dd19428b0..6bd0eb2bd 100644 --- a/easy-ui-react/src/types.ts +++ b/easy-ui-react/src/types.ts @@ -5,7 +5,7 @@ import { ResponsiveProp } from "./utilities/css"; export type DesignTokens = typeof tokens; export type DesignTokenAliases = keyof DesignTokens; -export type Falsy = boolean | undefined | null | 0; +export type Falsy = boolean | undefined | null | 0 | 0n; export type Heading = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; diff --git a/easy-ui-react/src/utilities/Noop.tsx b/easy-ui-react/src/utilities/Noop.tsx index 4ba9991c4..4f3851551 100644 --- a/easy-ui-react/src/utilities/Noop.tsx +++ b/easy-ui-react/src/utilities/Noop.tsx @@ -4,6 +4,6 @@ import { ReactElement } from "react"; * React component that simply renders its children. Useful for conditionally * swapping out an optional parent component. */ -export function Noop({ children }: { children: ReactElement }) { +export function Noop({ children }: { children: ReactElement }) { return children; } diff --git a/easy-ui-react/src/utilities/react.ts b/easy-ui-react/src/utilities/react.ts index b579f3b73..ba4f28143 100644 --- a/easy-ui-react/src/utilities/react.ts +++ b/easy-ui-react/src/utilities/react.ts @@ -32,7 +32,7 @@ export function flattenChildren( acc.push.apply( acc, flattenChildren( - node.props.children, + (node.props as { children?: ReactNode }).children, depth + 1, keys.concat(node.key || nodeIndex), ), @@ -60,7 +60,7 @@ export function filterChildrenByDisplayName( ) { const elements = flattenChildren(children).filter((n) => isElement(n), - ) as ReactElement[]; + ) as ReactElement[]; return elements.filter((e) => { const elementType = e.type as NamedExoticComponent; return elementType.displayName === displayName; @@ -68,7 +68,7 @@ export function filterChildrenByDisplayName( } export function getDisplayNameFromReactNode(component: ReactNode) { - const componentAsElement = component as ReactElement; + const componentAsElement = component as ReactElement; if (isValidElement(componentAsElement)) { const componentType = componentAsElement.type as NamedExoticComponent; return componentType.displayName; diff --git a/easy-ui-react/src/utilities/test.ts b/easy-ui-react/src/utilities/test.ts index e32b22745..859a15128 100644 --- a/easy-ui-react/src/utilities/test.ts +++ b/easy-ui-react/src/utilities/test.ts @@ -7,12 +7,14 @@ import { vi } from "vitest"; declare global { // eslint-disable-next-line no-var var jest: object; + // eslint-disable-next-line no-var + var IS_REACT_ACT_ENVIRONMENT: boolean; } /** * Render a react element for testing. Passes in vitest's timers for user-event. */ -export function render(jsx: ReactElement) { +export function render(jsx: ReactElement) { return { user: userEvent.setup({ advanceTimers: vi.advanceTimersByTime.bind(vi), diff --git a/easy-ui-react/vite.config.mts b/easy-ui-react/vite.config.mts index fec891056..881a8d6c5 100644 --- a/easy-ui-react/vite.config.mts +++ b/easy-ui-react/vite.config.mts @@ -17,7 +17,7 @@ export default defineConfig({ removeReactAttributes({ attributes: ["data-testid"], }), - react({ jsxRuntime: "classic" }), + react({ jsxRuntime: "automatic" }), viteStaticCopy({ targets: [ { src: "package.json", dest: ".", transform: cleanPkgJsonForDist }, diff --git a/easy-ui-react/vitest.setup.ts b/easy-ui-react/vitest.setup.ts index ab51fb986..810e3ec15 100644 --- a/easy-ui-react/vitest.setup.ts +++ b/easy-ui-react/vitest.setup.ts @@ -4,5 +4,7 @@ import { installScrollToMock, } from "./src/utilities/test"; +globalThis.IS_REACT_ACT_ENVIRONMENT = true; + installJestCompatibleFakeTimers(); installScrollToMock(); diff --git a/package-lock.json b/package-lock.json index 408322f1c..0c7a89bd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,14 +24,16 @@ "@storybook/addon-docs": "^9.1.13", "@storybook/react-vite": "^9.1.13", "@types/node": "^24.0.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "babel-loader": "^10.0.0", "eslint": "^9.22.0", "eslint-plugin-storybook": "9.1.13", "jsdom": "^26.1.0", "npm-run-all": "^4.1.5", "prettier": "^3.6.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "storybook": "^9.1.13", "stylelint": "^16.21.0", "turbo": "^2.4.4", @@ -45,7 +47,7 @@ }, "easy-ui-icons": { "name": "@easypost/easy-ui-icons", - "version": "1.0.0-alpha.51", + "version": "1.0.0-alpha.53", "devDependencies": { "@material-symbols/svg-300": "^0.40.2", "@material-symbols/svg-400": "^0.40.2", @@ -125,22 +127,19 @@ }, "easy-ui-react": { "name": "@easypost/easy-ui", - "version": "1.0.0-alpha.108", + "version": "1.0.0-alpha.112", "dependencies": { - "@easypost/easy-ui-icons": "1.0.0-alpha.51", + "@easypost/easy-ui-icons": "1.0.0-alpha.53", "@easypost/easy-ui-tokens": "1.0.0-alpha.17", "@react-aria/toast": "^3.0.9", "@react-aria/utils": "^3.32.0", "@react-stately/toast": "^3.1.2", "@react-types/shared": "^3.32.1", - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.1", "lodash": "^4.17.21", "overlayscrollbars": "^2.3.0", "overlayscrollbars-react": "^0.5.6", "react-aria": "^3.45.0", "react-aria-components": "^1.14.0", - "react-is": "^18.3.1", "react-stately": "^3.43.0", "react-syntax-highlighter": "^15.6.1", "react-transition-group": "^4.4.5", @@ -149,23 +148,65 @@ "devDependencies": { "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.3.0", + "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/lodash": "^4.17.18", - "@types/react-is": "^18.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/react-is": "^19.0.0", "@types/react-syntax-highlighter": "^15.5.13", "@types/react-transition-group": "^4.4.12", "@vitejs/plugin-react": "^4.6.0", "glob": "^10.2.5", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-is": "^19.0.0", "sass": "^1.89.2", "vite-plugin-react-remove-attributes": "^1.0.3", "vite-plugin-static-copy": "^3.1.2" }, "peerDependencies": { - "react": "^16.14.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "easy-ui-react/node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "easy-ui-react/node_modules/@types/react-is": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-19.2.0.tgz", + "integrity": "sha512-NP2xtcjZfORsOa4g2JwdseyEnF+wUCx25fTdG/J/HIY6yKga6+NozRBg2xR2gyh7kKYyd6DXndbq0YbQuTJ7Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" } }, "easy-ui-react/node_modules/brace-expansion": { @@ -215,6 +256,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "easy-ui-react/node_modules/react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "dev": true, + "license": "MIT" + }, "easy-ui-tokens": { "name": "@easypost/easy-ui-tokens", "version": "1.0.0-alpha.17", @@ -704,15 +752,16 @@ } }, "node_modules/@bundled-es-modules/memfs": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/memfs/-/memfs-4.9.4.tgz", - "integrity": "sha512-1XyYPUaIHwEOdF19wYVLBtHJRr42Do+3ctht17cZOHwHf67vkmRNPlYDGY2kJps4RgE5+c7nEZmEzxxvb1NZWA==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-ykdrkEmQr9BV804yd37ikXfNnvxrwYfY9Z2/EtMHFEFadEjsQXJ1zL9bVZrKNLDtm91UdUOEHso6Aweg93K6xQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "assert": "^2.0.0", + "assert": "^2.1.0", "buffer": "^6.0.3", "events": "^3.3.0", - "memfs": "^4.9.3", + "memfs": "^4.17.0", "path": "^0.12.7", "stream": "^0.0.3", "util": "^0.12.5" @@ -5790,34 +5839,6 @@ "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", "dev": true }, - "node_modules/@testing-library/react": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", - "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0 || ^19.0.0", - "@types/react-dom": "^18.0.0 || ^19.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@testing-library/user-event": { "version": "14.6.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", @@ -5955,34 +5976,24 @@ "undici-types": "~7.16.0" } }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "license": "MIT" - }, "node_modules/@types/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", - "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-is": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.3.0.tgz", - "integrity": "sha512-KZJpHUkAdzyKj/kUHJDc6N7KyidftICufJfOFpiG6haL/BDQNQt5i4n1XDUL/nDZAtGLHDSWRYpLzKTAKSvX6w==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, - "dependencies": { - "@types/react": "*" + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" } }, "node_modules/@types/react-syntax-highlighter": { @@ -7368,7 +7379,9 @@ } }, "node_modules/csstype": { - "version": "3.1.1", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/dashify": { @@ -10182,11 +10195,11 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -11487,13 +11500,11 @@ "license": "MIT" }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "dev": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11660,23 +11671,18 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "dev": true, + "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.2.4" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" - }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -12147,13 +12153,11 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "dev": true, - "dependencies": { - "loose-envify": "^1.1.0" - } + "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", @@ -14717,15 +14721,15 @@ } }, "@bundled-es-modules/memfs": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/memfs/-/memfs-4.9.4.tgz", - "integrity": "sha512-1XyYPUaIHwEOdF19wYVLBtHJRr42Do+3ctht17cZOHwHf67vkmRNPlYDGY2kJps4RgE5+c7nEZmEzxxvb1NZWA==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-ykdrkEmQr9BV804yd37ikXfNnvxrwYfY9Z2/EtMHFEFadEjsQXJ1zL9bVZrKNLDtm91UdUOEHso6Aweg93K6xQ==", "dev": true, "requires": { - "assert": "^2.0.0", + "assert": "^2.1.0", "buffer": "^6.0.3", "events": "^3.3.0", - "memfs": "^4.9.3", + "memfs": "^4.17.0", "path": "^0.12.7", "stream": "^0.0.3", "util": "^0.12.5" @@ -15302,7 +15306,7 @@ "@easypost/easy-ui": { "version": "file:easy-ui-react", "requires": { - "@easypost/easy-ui-icons": "1.0.0-alpha.51", + "@easypost/easy-ui-icons": "1.0.0-alpha.53", "@easypost/easy-ui-tokens": "1.0.0-alpha.17", "@react-aria/toast": "^3.0.9", "@react-aria/utils": "^3.32.0", @@ -15310,12 +15314,12 @@ "@react-types/shared": "^3.32.1", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.3.0", + "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/lodash": "^4.17.18", - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.1", - "@types/react-is": "^18.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/react-is": "^19.0.0", "@types/react-syntax-highlighter": "^15.5.13", "@types/react-transition-group": "^4.4.12", "@vitejs/plugin-react": "^4.6.0", @@ -15323,11 +15327,11 @@ "lodash": "^4.17.21", "overlayscrollbars": "^2.3.0", "overlayscrollbars-react": "^0.5.6", - "react": "^18.3.1", + "react": "^19.0.0", "react-aria": "^3.45.0", "react-aria-components": "^1.14.0", - "react-dom": "^18.3.1", - "react-is": "^18.3.1", + "react-dom": "^19.0.0", + "react-is": "^19.0.0", "react-stately": "^3.43.0", "react-syntax-highlighter": "^15.6.1", "react-transition-group": "^4.4.5", @@ -15337,6 +15341,24 @@ "vite-plugin-static-copy": "^3.1.2" }, "dependencies": { + "@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5" + } + }, + "@types/react-is": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-19.2.0.tgz", + "integrity": "sha512-NP2xtcjZfORsOa4g2JwdseyEnF+wUCx25fTdG/J/HIY6yKga6+NozRBg2xR2gyh7kKYyd6DXndbq0YbQuTJ7Ew==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -15368,6 +15390,12 @@ "requires": { "brace-expansion": "^2.0.1" } + }, + "react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "dev": true } } }, @@ -18168,15 +18196,6 @@ } } }, - "@testing-library/react": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", - "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5" - } - }, "@testing-library/user-event": { "version": "14.6.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", @@ -18293,34 +18312,20 @@ "undici-types": "~7.16.0" } }, - "@types/prop-types": { - "version": "15.7.5" - }, "@types/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", - "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, "requires": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "requires": { - "@types/react": "*" - } - }, - "@types/react-is": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.3.0.tgz", - "integrity": "sha512-KZJpHUkAdzyKj/kUHJDc6N7KyidftICufJfOFpiG6haL/BDQNQt5i4n1XDUL/nDZAtGLHDSWRYpLzKTAKSvX6w==", - "dev": true, - "requires": { - "@types/react": "*" - } + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true }, "@types/react-syntax-highlighter": { "version": "15.5.13", @@ -19206,7 +19211,9 @@ } }, "csstype": { - "version": "3.1.1" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" }, "dashify": { "version": "2.0.0", @@ -21094,9 +21101,9 @@ "dev": true }, "minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true }, "mri": { @@ -21934,13 +21941,10 @@ "dev": true }, "react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0" - } + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "dev": true }, "react-aria": { "version": "3.45.0", @@ -22071,20 +22075,14 @@ "dev": true }, "react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "dev": true, "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" } }, - "react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" - }, "react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -22403,13 +22401,10 @@ } }, "scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0" - } + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "dev": true }, "semver": { "version": "6.3.1", diff --git a/package.json b/package.json index 9cefa805e..f9e302339 100644 --- a/package.json +++ b/package.json @@ -55,14 +55,16 @@ "@storybook/addon-docs": "^9.1.13", "@storybook/react-vite": "^9.1.13", "@types/node": "^24.0.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "babel-loader": "^10.0.0", "eslint": "^9.22.0", "eslint-plugin-storybook": "9.1.13", "jsdom": "^26.1.0", "npm-run-all": "^4.1.5", "prettier": "^3.6.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "storybook": "^9.1.13", "stylelint": "^16.21.0", "turbo": "^2.4.4", From aa4da716ef38877f7feb1ea53dfa6a91fac69c34 Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Fri, 3 Apr 2026 12:30:35 -0400 Subject: [PATCH 2/3] fix types --- easy-ui-react/src/CodeBlock/CodeBlock.tsx | 7 +++++-- easy-ui-react/src/CodeBlock/context.tsx | 3 ++- easy-ui-react/src/DataGrid/Table.tsx | 4 ++-- easy-ui-react/src/Drawer/DrawerContainer.tsx | 2 +- easy-ui-react/src/Drawer/DrawerTrigger.tsx | 4 ++-- easy-ui-react/src/Menu/MenuTrigger.tsx | 12 +++++++++--- easy-ui-react/src/Modal/ModalContainer.tsx | 2 +- easy-ui-react/src/Modal/ModalTrigger.tsx | 4 ++-- .../ProductLayout/ProductLayoutTabbedContent.tsx | 2 +- easy-ui-react/src/Provider/Provider.tsx | 2 +- easy-ui-react/src/SearchNav/CTAGroup.tsx | 10 +++++----- .../src/SearchNav/CondensedSearchNav.tsx | 16 ++++++++++------ easy-ui-react/src/SearchNav/utilities.ts | 10 +++++----- easy-ui-react/src/TabPanels/TabPanelsPanels.tsx | 4 ++-- easy-ui-react/src/Tooltip/Tooltip.tsx | 6 +++--- .../src/VerticalNav/SupplementaryAction.tsx | 2 +- easy-ui-react/src/utilities/Noop.tsx | 2 +- easy-ui-react/src/utilities/react.ts | 4 ++-- easy-ui-react/src/utilities/test.ts | 2 +- 19 files changed, 56 insertions(+), 42 deletions(-) diff --git a/easy-ui-react/src/CodeBlock/CodeBlock.tsx b/easy-ui-react/src/CodeBlock/CodeBlock.tsx index 011b547ea..057d894b2 100644 --- a/easy-ui-react/src/CodeBlock/CodeBlock.tsx +++ b/easy-ui-react/src/CodeBlock/CodeBlock.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useMemo } from "react"; +import React, { ReactElement, ReactNode, useMemo } from "react"; import { CodeSnippet, CodeSnippetProps } from "../CodeSnippet"; import { SnippetLanguage } from "../CodeSnippet/SyntaxHighlighter"; import { HorizontalStack } from "../HorizontalStack"; @@ -120,7 +120,10 @@ export function CodeBlock(props: CodeBlockProps) { const { children, language, onLanguageChange } = props; const snippets = useMemo(() => { - return filterChildrenByDisplayName(children, "CodeBlock.Snippet"); + return filterChildrenByDisplayName( + children, + "CodeBlock.Snippet", + ) as ReactElement[]; }, [children]); const headers = useMemo(() => { diff --git a/easy-ui-react/src/CodeBlock/context.tsx b/easy-ui-react/src/CodeBlock/context.tsx index 0964aec61..f7598ac84 100644 --- a/easy-ui-react/src/CodeBlock/context.tsx +++ b/easy-ui-react/src/CodeBlock/context.tsx @@ -1,9 +1,10 @@ import { ReactElement, createContext, useContext } from "react"; +import { CodeSnippetProps } from "../CodeSnippet"; import { SnippetLanguage } from "../CodeSnippet/SyntaxHighlighter"; export type CodeBlockContextType = { languages: SnippetLanguage[]; - snippet: ReactElement; + snippet: ReactElement; language: SnippetLanguage; onLanguageChange: (language: SnippetLanguage) => void; }; diff --git a/easy-ui-react/src/DataGrid/Table.tsx b/easy-ui-react/src/DataGrid/Table.tsx index c4dd1070e..323cb96d1 100644 --- a/easy-ui-react/src/DataGrid/Table.tsx +++ b/easy-ui-react/src/DataGrid/Table.tsx @@ -26,7 +26,7 @@ type TableProps = Omit< DataGridProps, "children" > & { - children?: [ReactElement, ReactElement]; + children?: [ReactElement, ReactElement]; }; export function Table( @@ -46,7 +46,7 @@ export function Table( const innerContainerRef = useRef(null); const tableRef = useRef(null); const state = useTableState({ - ...props, + ...(props as Parameters[0]), selectionMode, selectionBehavior: "toggle", showSelectionCheckboxes: selectionMode !== "none", diff --git a/easy-ui-react/src/Drawer/DrawerContainer.tsx b/easy-ui-react/src/Drawer/DrawerContainer.tsx index daa618f0a..5bdb524e7 100644 --- a/easy-ui-react/src/Drawer/DrawerContainer.tsx +++ b/easy-ui-react/src/Drawer/DrawerContainer.tsx @@ -43,7 +43,7 @@ export function DrawerContainer(props: DrawerContainerProps) { throw new Error("Only a single child can be passed to ModalContainer."); } - const [lastChild, setLastChild] = useState | null>(null); + const [lastChild, setLastChild] = useState(null); // React.Children.toArray mutates the children, and we need them to be stable // between renders so that the lastChild comparison works. diff --git a/easy-ui-react/src/Drawer/DrawerTrigger.tsx b/easy-ui-react/src/Drawer/DrawerTrigger.tsx index 174e1fb93..268f4b311 100644 --- a/easy-ui-react/src/Drawer/DrawerTrigger.tsx +++ b/easy-ui-react/src/Drawer/DrawerTrigger.tsx @@ -4,13 +4,13 @@ import { useOverlayTriggerState } from "react-stately"; import { DrawerUnderlay } from "./DrawerUnderlay"; import { DrawerTriggerContext } from "./context"; -export type CloseableDrawerElement = (close: () => void) => ReactElement; +export type CloseableDrawerElement = (close: () => void) => ReactElement; export type DrawerTriggerProps = { /** * Content of modal trigger. Must be exactly two elements. */ - children: [ReactElement, CloseableDrawerElement | ReactElement]; + children: [ReactElement, CloseableDrawerElement | ReactElement]; /** * Whether the modal is open by default (uncontrolled). diff --git a/easy-ui-react/src/Menu/MenuTrigger.tsx b/easy-ui-react/src/Menu/MenuTrigger.tsx index c009dd30a..2838fe317 100644 --- a/easy-ui-react/src/Menu/MenuTrigger.tsx +++ b/easy-ui-react/src/Menu/MenuTrigger.tsx @@ -4,12 +4,18 @@ import { useInternalMenuContext } from "./MenuContext"; export type MenuTriggerProps = { /** The element that will activate the menu. */ - children: ReactElement; + children: ReactElement; }; export function MenuTrigger(props: MenuTriggerProps) { const { children } = props; const { triggerRef, menuTriggerProps } = useInternalMenuContext(); - const clonedProps = mergeProps(menuTriggerProps, children.props); - return React.cloneElement(children, { ...clonedProps, ref: triggerRef }); + const clonedProps = mergeProps( + menuTriggerProps, + children.props as Record, + ); + return React.cloneElement(children as ReactElement>, { + ...clonedProps, + ref: triggerRef, + }); } diff --git a/easy-ui-react/src/Modal/ModalContainer.tsx b/easy-ui-react/src/Modal/ModalContainer.tsx index e07e4d85a..741c40321 100644 --- a/easy-ui-react/src/Modal/ModalContainer.tsx +++ b/easy-ui-react/src/Modal/ModalContainer.tsx @@ -37,7 +37,7 @@ export function ModalContainer(props: ModalContainerProps) { throw new Error("Only a single child can be passed to ModalContainer."); } - const [lastChild, setLastChild] = useState | null>(null); + const [lastChild, setLastChild] = useState(null); // React.Children.toArray mutates the children, and we need them to be stable // between renders so that the lastChild comparison works. diff --git a/easy-ui-react/src/Modal/ModalTrigger.tsx b/easy-ui-react/src/Modal/ModalTrigger.tsx index b4172d7c2..d603584fe 100644 --- a/easy-ui-react/src/Modal/ModalTrigger.tsx +++ b/easy-ui-react/src/Modal/ModalTrigger.tsx @@ -4,13 +4,13 @@ import { useOverlayTriggerState } from "react-stately"; import { ModalUnderlay } from "./ModalUnderlay"; import { ModalTriggerProvider } from "./context"; -export type CloseableModalElement = (close: () => void) => ReactElement; +export type CloseableModalElement = (close: () => void) => ReactElement; export type ModalTriggerProps = { /** * Content of modal trigger. Must be exactly two elements. */ - children: [ReactElement, CloseableModalElement | ReactElement]; + children: [ReactElement, CloseableModalElement | ReactElement]; /** * Whether the modal is open by default (uncontrolled). diff --git a/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx b/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx index b72645182..fb3730173 100644 --- a/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx +++ b/easy-ui-react/src/ProductLayout/ProductLayoutTabbedContent.tsx @@ -14,7 +14,7 @@ export type ProductLayoutTabbedContentProps = AriaLabelingProps & { * The tabs to display for nested content. Uses a `` and should * contain an array of ``s. */ - tabs: ReactElement[]; + tabs: ReactElement[]; }; export function ProductLayoutTabbedContent( diff --git a/easy-ui-react/src/Provider/Provider.tsx b/easy-ui-react/src/Provider/Provider.tsx index 4d2405f0c..17e5d3068 100644 --- a/easy-ui-react/src/Provider/Provider.tsx +++ b/easy-ui-react/src/Provider/Provider.tsx @@ -28,7 +28,7 @@ export type EasyUIRouterProviderProps = { export type ProviderProps = { /** Component tree */ - children: ReactElement; + children: ReactElement; /** Theme to apply to Easy UI. Use `createTheme()` to build theme object. */ theme?: ThemeCreator; /** Color scheme to apply to Easy UI. */ diff --git a/easy-ui-react/src/SearchNav/CTAGroup.tsx b/easy-ui-react/src/SearchNav/CTAGroup.tsx index 1322bae0e..66daa1437 100644 --- a/easy-ui-react/src/SearchNav/CTAGroup.tsx +++ b/easy-ui-react/src/SearchNav/CTAGroup.tsx @@ -38,7 +38,7 @@ export function CTAGroup(_props: CTAGroupProps) {
{secondaryCTAItems?.map((item, index) => { const isLastChild = index === totalItems - 1; - const itemEle = item as ReactElement; + const itemEle = item as ReactElement; return ( {itemEle} @@ -64,14 +64,14 @@ export function CTAGroup(_props: CTAGroupProps) { {secondaryCTAItems?.map((item) => { - const itemEle = item as ReactElement; + const itemEle = item as ReactElement>; return ( - {itemEle.props.label} + {itemEle.props.label as ReactNode} ); })} diff --git a/easy-ui-react/src/SearchNav/CondensedSearchNav.tsx b/easy-ui-react/src/SearchNav/CondensedSearchNav.tsx index 4ab312c85..c4d65675b 100644 --- a/easy-ui-react/src/SearchNav/CondensedSearchNav.tsx +++ b/easy-ui-react/src/SearchNav/CondensedSearchNav.tsx @@ -1,4 +1,4 @@ -import React, { useState, ReactElement } from "react"; +import React, { useState, ReactElement, ReactNode } from "react"; import Close from "@easypost/easy-ui-icons/Close"; import MenuSymbol from "@easypost/easy-ui-icons/Menu"; import Search from "@easypost/easy-ui-icons/Search"; @@ -67,7 +67,9 @@ export function CondensedSearchNav() { {selectorChildren?.map((item) => { - const itemEle = item as ReactElement; + const itemEle = item as ReactElement< + Record + >; return ( {itemEle.props.children} @@ -77,14 +79,16 @@ export function CondensedSearchNav() { {secondaryCTAItems?.map((item) => { - const itemEle = item as ReactElement; + const itemEle = item as ReactElement< + Record + >; return ( - {itemEle.props.label} + {itemEle.props.label as ReactNode} ); })} diff --git a/easy-ui-react/src/SearchNav/utilities.ts b/easy-ui-react/src/SearchNav/utilities.ts index 74612d885..fe9e3d342 100644 --- a/easy-ui-react/src/SearchNav/utilities.ts +++ b/easy-ui-react/src/SearchNav/utilities.ts @@ -11,7 +11,7 @@ export function getLogoGroupChildren(logoGroup: ReactNode) { if (logoGroupDisplayName !== "SearchNav.LogoGroup") { throw new Error("SearchNav must contain SearchNav.LogoGroup."); } - const logoGroupElement = logoGroup as ReactElement; + const logoGroupElement = logoGroup as ReactElement>; const logoGroupChildren = flattenChildren(logoGroupElement.props.children); const logoDisplayName = getDisplayNameFromReactNode(logoGroupChildren[0]); if (logoDisplayName !== "SearchNav.Logo") { @@ -36,7 +36,7 @@ export function getLogoGroupChildren(logoGroup: ReactNode) { ) === "SearchNav.Selector" ) { selector = logoGroupChildren[logoGroupChildren.length - 1]; - const selectorElem = selector as ReactElement; + const selectorElem = selector as ReactElement>; selectorChildren = flattenChildren(selectorElem.props.children); } @@ -63,7 +63,7 @@ export function getCTAGroupChildren(ctaGroup: ReactNode) { let secondaryCTAItems; let primaryCTAItem; if (getDisplayNameFromReactNode(ctaGroup) === "SearchNav.CTAGroup") { - const ctaGroupElement = ctaGroup as ReactElement; + const ctaGroupElement = ctaGroup as ReactElement>; secondaryCTAItems = filterChildrenByDisplayName( ctaGroupElement.props.children, "SearchNav.SecondaryCTAItem", @@ -92,9 +92,9 @@ export function getCTAGroupChildren(ctaGroup: ReactNode) { export function getSelectorLabel(selector: ReactNode) { let selectorLabel; if (selector) { - const selectorElem = selector as ReactElement; + const selectorElem = selector as ReactElement>; const { "aria-label": label } = selectorElem.props; - selectorLabel = label; + selectorLabel = label as string | undefined; } return selectorLabel; } diff --git a/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx b/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx index c96739f59..73e91a895 100644 --- a/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx +++ b/easy-ui-react/src/TabPanels/TabPanelsPanels.tsx @@ -16,7 +16,7 @@ type TabPanelsPanelsProps = { * The contents of each tab. Item keys should match the key of the * corresponding `` within the `` element. */ - children: ReactElement | ReactElement[]; + children: ReactElement | ReactElement[]; }; type TabPanelProps = AriaTabPanelProps & { @@ -77,7 +77,7 @@ function TabPanelsPanel({ state, ...props }: TabPanelProps) { React.cloneElement(children, { ref, ...tabPanelProps, - ...(children as ReactElement).props, + ...((children as ReactElement).props as Record), }) ) : (
diff --git a/easy-ui-react/src/Tooltip/Tooltip.tsx b/easy-ui-react/src/Tooltip/Tooltip.tsx index e5ea41e92..17684b2e1 100644 --- a/easy-ui-react/src/Tooltip/Tooltip.tsx +++ b/easy-ui-react/src/Tooltip/Tooltip.tsx @@ -28,7 +28,7 @@ const CLOSE_DELAY = 250; export type TooltipProps = { /** The element that will activate to tooltip. */ - children: ReactElement; + children: ReactElement; /** The content to display within the tooltip. */ content: ReactNode; @@ -124,8 +124,8 @@ export function Tooltip(props: TooltipProps) { return ( <> - {React.cloneElement(children, { - ...mergeProps(triggerProps, children.props), + {React.cloneElement(children as ReactElement>, { + ...mergeProps(triggerProps, children.props as Record), ref: triggerRef, })} {tooltipTriggerState.isOpen && ( diff --git a/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx b/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx index 1ba4e2eab..5c915a50c 100644 --- a/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx +++ b/easy-ui-react/src/VerticalNav/SupplementaryAction.tsx @@ -46,7 +46,7 @@ export const SupplementaryAction = forwardRef< }) as { ( props: SupplementaryActionProps & { ref?: DOMRef }, - ): ReactElement; + ): ReactElement; displayName?: string; }; diff --git a/easy-ui-react/src/utilities/Noop.tsx b/easy-ui-react/src/utilities/Noop.tsx index 4f3851551..4ba9991c4 100644 --- a/easy-ui-react/src/utilities/Noop.tsx +++ b/easy-ui-react/src/utilities/Noop.tsx @@ -4,6 +4,6 @@ import { ReactElement } from "react"; * React component that simply renders its children. Useful for conditionally * swapping out an optional parent component. */ -export function Noop({ children }: { children: ReactElement }) { +export function Noop({ children }: { children: ReactElement }) { return children; } diff --git a/easy-ui-react/src/utilities/react.ts b/easy-ui-react/src/utilities/react.ts index ba4f28143..a44f2f029 100644 --- a/easy-ui-react/src/utilities/react.ts +++ b/easy-ui-react/src/utilities/react.ts @@ -60,7 +60,7 @@ export function filterChildrenByDisplayName( ) { const elements = flattenChildren(children).filter((n) => isElement(n), - ) as ReactElement[]; + ) as ReactElement[]; return elements.filter((e) => { const elementType = e.type as NamedExoticComponent; return elementType.displayName === displayName; @@ -68,7 +68,7 @@ export function filterChildrenByDisplayName( } export function getDisplayNameFromReactNode(component: ReactNode) { - const componentAsElement = component as ReactElement; + const componentAsElement = component as ReactElement; if (isValidElement(componentAsElement)) { const componentType = componentAsElement.type as NamedExoticComponent; return componentType.displayName; diff --git a/easy-ui-react/src/utilities/test.ts b/easy-ui-react/src/utilities/test.ts index 859a15128..2ecd164f8 100644 --- a/easy-ui-react/src/utilities/test.ts +++ b/easy-ui-react/src/utilities/test.ts @@ -14,7 +14,7 @@ declare global { /** * Render a react element for testing. Passes in vitest's timers for user-event. */ -export function render(jsx: ReactElement) { +export function render(jsx: ReactElement) { return { user: userEvent.setup({ advanceTimers: vi.advanceTimersByTime.bind(vi), From 165577e95413350e99872de1ad49f4929ed53616 Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Mon, 6 Apr 2026 16:08:17 -0400 Subject: [PATCH 3/3] changeset --- .changeset/fancy-mangos-eat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fancy-mangos-eat.md diff --git a/.changeset/fancy-mangos-eat.md b/.changeset/fancy-mangos-eat.md new file mode 100644 index 000000000..ca11c8afa --- /dev/null +++ b/.changeset/fancy-mangos-eat.md @@ -0,0 +1,5 @@ +--- +"@easypost/easy-ui": minor +--- + +feat: support React 19