From ce92ea5de13b31721bf8ac040fea35fdb5dd829f Mon Sep 17 00:00:00 2001 From: Marcus Notheis Date: Wed, 17 Jul 2024 10:48:26 +0200 Subject: [PATCH 1/5] refactor: drop support for React 16 & 17 BREAKING CHANGE: the minimum required `react` and `react-dom` version is now 18.0.0 --- README.md | 2 +- docs/Welcome.mdx | 2 +- packages/base/package.json | 2 +- packages/base/src/hooks/index.ts | 2 -- packages/base/src/hooks/useIsomorphicId.ts | 24 ------------------- packages/charts/package.json | 2 +- .../ColumnChartWithTrend.tsx | 6 ++--- packages/compat/package.json | 4 ++-- packages/main/package.json | 4 ++-- .../components/AnalyticalCardHeader/index.tsx | 6 ++--- .../src/components/AnalyticalTable/index.tsx | 5 ++-- .../src/components/ExpandableText/index.tsx | 6 ++--- .../src/components/FilterBar/FilterDialog.tsx | 6 ++--- .../main/src/components/MessageBox/index.tsx | 6 ++--- .../src/components/ThemeProvider/index.tsx | 11 +++------ .../VariantManagement/ManageViewsDialog.tsx | 6 ++--- .../VariantManagement/SaveViewDialog.tsx | 6 ++--- 17 files changed, 34 insertions(+), 66 deletions(-) delete mode 100644 packages/base/src/hooks/useIsomorphicId.ts diff --git a/README.md b/README.md index 0eebd22dd31..628b37892e9 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ You can find our documentation under the following links: ## Requirements -- [React](https://www.npmjs.com/package/react) and [React-DOM](https://www.npmjs.com/package/react-dom) (**16.14.0 or higher**) +- [React](https://www.npmjs.com/package/react) and [React-DOM](https://www.npmjs.com/package/react-dom) (**18.0.0 or higher**) - [Node.js](https://nodejs.org/) (**LTS version**) - If you're using [TypeScript](https://www.typescriptlang.org/) we recommend version **4.7** or later. diff --git a/docs/Welcome.mdx b/docs/Welcome.mdx index cbbc628f8cd..2186064397f 100644 --- a/docs/Welcome.mdx +++ b/docs/Welcome.mdx @@ -25,7 +25,7 @@ In addition to that, UI5 Web Components for React is providing complex component ## Requirements -- [React](https://www.npmjs.com/package/react) and [React-DOM](https://www.npmjs.com/package/react-dom) (**16.14.0 or higher**) +- [React](https://www.npmjs.com/package/react) and [React-DOM](https://www.npmjs.com/package/react-dom) (**18.0.0 or higher**) - [Node.js](https://nodejs.org/) (**LTS version**) - If you're using [TypeScript](https://www.typescriptlang.org/) we recommend version **4.7** or later. diff --git a/packages/base/package.json b/packages/base/package.json index 556447d8e53..015617629e5 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -31,7 +31,7 @@ "peerDependencies": { "@types/react": "*", "@ui5/webcomponents-base": "~2.0.1", - "react": "^16.14.0 || ^17 || ^18 || ^19" + "react": "^18 || ^19" }, "peerDependenciesMeta": { "@types/react": { diff --git a/packages/base/src/hooks/index.ts b/packages/base/src/hooks/index.ts index 100987600e8..2c59d6d9fb9 100644 --- a/packages/base/src/hooks/index.ts +++ b/packages/base/src/hooks/index.ts @@ -1,6 +1,5 @@ import { useCurrentTheme } from './useCurrentTheme.js'; import { useI18nBundle } from './useI18nBundle.js'; -import { useIsomorphicId } from './useIsomorphicId.js'; import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect.js'; import { useIsRTL } from './useIsRTL.js'; import { useStylesheet } from './useStylesheet.js'; @@ -13,7 +12,6 @@ export { useIsRTL, useSyncRef, useViewportRange, - useIsomorphicId, useStylesheet, useCurrentTheme }; diff --git a/packages/base/src/hooks/useIsomorphicId.ts b/packages/base/src/hooks/useIsomorphicId.ts deleted file mode 100644 index 679ff38ecbd..00000000000 --- a/packages/base/src/hooks/useIsomorphicId.ts +++ /dev/null @@ -1,24 +0,0 @@ -'use client'; - -import * as React from 'react'; - -function getRandomId() { - if ('randomUUID' in crypto) { - return crypto.randomUUID(); - } - const uint32 = window.crypto.getRandomValues(new Uint32Array(1))[0]; - return uint32.toString(16); -} - -const canUseUseId = 'useId' in React; - -export function useIsomorphicId(): string { - if (canUseUseId) { - // TODO might be fixed by https://github.com/webpack/webpack/issues/14814 - return Reflect.get(React, 'useId')(); - } - - // eslint-disable-next-line react-hooks/rules-of-hooks - const localId = React.useRef(getRandomId()); // React version never changes at runtime - return localId.current; -} diff --git a/packages/charts/package.json b/packages/charts/package.json index d84e85919bc..9078b4e2a91 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -40,7 +40,7 @@ "peerDependencies": { "@ui5/webcomponents-react": "~2.0.0", "@ui5/webcomponents-react-base": "~2.0.0", - "react": "^16.14.0 || ^17 || ^18" + "react": "^18" }, "publishConfig": { "access": "public" diff --git a/packages/charts/src/components/ColumnChartWithTrend/ColumnChartWithTrend.tsx b/packages/charts/src/components/ColumnChartWithTrend/ColumnChartWithTrend.tsx index c900c06f9dc..4be22eed35b 100644 --- a/packages/charts/src/components/ColumnChartWithTrend/ColumnChartWithTrend.tsx +++ b/packages/charts/src/components/ColumnChartWithTrend/ColumnChartWithTrend.tsx @@ -1,8 +1,8 @@ 'use client'; -import { ThemingParameters, useIsomorphicId } from '@ui5/webcomponents-react-base'; +import { ThemingParameters } from '@ui5/webcomponents-react-base'; import type { CSSProperties } from 'react'; -import { forwardRef } from 'react'; +import { forwardRef, useId } from 'react'; import type { TooltipProps, YAxisProps } from 'recharts'; import { useLongestYAxisLabel } from '../../hooks/useLongestYAxisLabel.js'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures.js'; @@ -120,7 +120,7 @@ const ColumnChartWithTrend = forwardRef[]; diff --git a/packages/main/src/components/AnalyticalTable/index.tsx b/packages/main/src/components/AnalyticalTable/index.tsx index 15977649585..bddd5056894 100644 --- a/packages/main/src/components/AnalyticalTable/index.tsx +++ b/packages/main/src/components/AnalyticalTable/index.tsx @@ -5,7 +5,6 @@ import { debounce, enrichEventWithDetails, useI18nBundle, - useIsomorphicId, useIsomorphicLayoutEffect, useIsRTL, useStylesheet, @@ -13,7 +12,7 @@ import { } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; import type { CSSProperties, MutableRefObject } from 'react'; -import { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react'; +import { forwardRef, useCallback, useEffect, useId, useMemo, useRef } from 'react'; import { useColumnOrder, useExpanded, @@ -179,7 +178,7 @@ const AnalyticalTable = forwardRef((pro const [collapsed, setCollapsed] = useState(true); const [popoverOpen, setPopoverOpen] = useState(false); - const uniqueId = useIsomorphicId(); + const uniqueId = useId(); const i18nBundle = useI18nBundle('@ui5/webcomponents-react'); const trimmedChildren = renderWhitespace ? children : children?.replace(/\s+/g, ' ').trim(); const isOverflow = trimmedChildren?.length >= maxCharacters; diff --git a/packages/main/src/components/FilterBar/FilterDialog.tsx b/packages/main/src/components/FilterBar/FilterDialog.tsx index 94bd64f1b89..5159dc0df50 100644 --- a/packages/main/src/components/FilterBar/FilterDialog.tsx +++ b/packages/main/src/components/FilterBar/FilterDialog.tsx @@ -5,9 +5,9 @@ import TitleLevel from '@ui5/webcomponents/dist/types/TitleLevel.js'; import group2Icon from '@ui5/webcomponents-icons/dist/group-2.js'; import listIcon from '@ui5/webcomponents-icons/dist/list.js'; import searchIcon from '@ui5/webcomponents-icons/dist/search.js'; -import { enrichEventWithDetails, useI18nBundle, useIsomorphicId, useStylesheet } from '@ui5/webcomponents-react-base'; +import { enrichEventWithDetails, useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base'; import type { Dispatch, ReactElement, RefObject, SetStateAction } from 'react'; -import { Children, cloneElement, useEffect, useReducer, useRef, useState } from 'react'; +import { Children, cloneElement, useEffect, useId, useReducer, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { FlexBoxDirection, FlexBoxJustifyContent, MessageBoxAction, MessageBoxType } from '../../enums/index.js'; import { @@ -145,7 +145,7 @@ export const FilterDialog = (props: FilterDialogPropTypes) => { isPhone } = props; useStylesheet(styleData, 'FilterBarDialog'); - const uniqueId = useIsomorphicId(); + const uniqueId = useId(); const [searchString, setSearchString] = useState(''); const [toggledFilters, setToggledFilters] = useState({}); const dialogRefs = useRef({}); diff --git a/packages/main/src/components/MessageBox/index.tsx b/packages/main/src/components/MessageBox/index.tsx index 9288e734216..752946f9887 100644 --- a/packages/main/src/components/MessageBox/index.tsx +++ b/packages/main/src/components/MessageBox/index.tsx @@ -6,10 +6,10 @@ import PopupAccessibleRole from '@ui5/webcomponents/dist/types/PopupAccessibleRo import TitleLevel from '@ui5/webcomponents/dist/types/TitleLevel.js'; import ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js'; import iconSysHelp from '@ui5/webcomponents-icons/dist/sys-help-2.js'; -import { useI18nBundle, useIsomorphicId, useStylesheet } from '@ui5/webcomponents-react-base'; +import { useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; import type { ReactElement, ReactNode } from 'react'; -import { cloneElement, forwardRef, isValidElement } from 'react'; +import { cloneElement, forwardRef, isValidElement, useId } from 'react'; import { MessageBoxAction, MessageBoxType } from '../../enums/index.js'; import { ABORT, @@ -200,7 +200,7 @@ const MessageBox = forwardRef((props, ref) => onClose(action); }; - const messageBoxId = useIsomorphicId(); + const messageBoxId = useId(); const internalActions = getActions(actions, type); const getInitialFocus = () => { diff --git a/packages/main/src/components/ThemeProvider/index.tsx b/packages/main/src/components/ThemeProvider/index.tsx index 454f29724f6..9023028d9a5 100644 --- a/packages/main/src/components/ThemeProvider/index.tsx +++ b/packages/main/src/components/ThemeProvider/index.tsx @@ -5,18 +5,13 @@ import { getScopedVarName } from '@ui5/webcomponents-base/dist/CustomElementsSco import { attachLanguageChange, detachLanguageChange } from '@ui5/webcomponents-base/dist/locale/languageChange.js'; import type { StyleDataCSP } from '@ui5/webcomponents-base/dist/ManagedStyles.js'; import { attachThemeLoaded, detachThemeLoaded } from '@ui5/webcomponents-base/dist/theming/ThemeLoaded.js'; -import { - I18nStore, - StyleStore, - useIsomorphicId, - useIsomorphicLayoutEffect, - useStylesheet -} from '@ui5/webcomponents-react-base'; +import { I18nStore, StyleStore, useIsomorphicLayoutEffect, useStylesheet } from '@ui5/webcomponents-react-base'; import type { FC, ReactNode } from 'react'; +import { useId } from 'react'; import { styleData } from './ThemeProvider.css.js'; function ThemeProviderStyles() { - const uniqueId = useIsomorphicId(); + const uniqueId = useId(); useStylesheet(styleData, `${ThemeProvider.displayName}-${uniqueId}`); useStylesheet(ui5WcVariablesStyleData, `${ThemeProvider.displayName}-css-vars-${uniqueId}`); return null; diff --git a/packages/main/src/components/VariantManagement/ManageViewsDialog.tsx b/packages/main/src/components/VariantManagement/ManageViewsDialog.tsx index a868235f4eb..b730427c0e2 100644 --- a/packages/main/src/components/VariantManagement/ManageViewsDialog.tsx +++ b/packages/main/src/components/VariantManagement/ManageViewsDialog.tsx @@ -2,9 +2,9 @@ import BarDesign from '@ui5/webcomponents/dist/types/BarDesign.js'; import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; import TableOverflowMode from '@ui5/webcomponents/dist/types/TableOverflowMode.js'; import searchIcon from '@ui5/webcomponents-icons/dist/search.js'; -import { enrichEventWithDetails, useI18nBundle, useIsomorphicId, useStylesheet } from '@ui5/webcomponents-react-base'; +import { enrichEventWithDetails, useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base'; import type { MouseEventHandler, ReactElement } from 'react'; -import { Children, isValidElement, useEffect, useRef, useState } from 'react'; +import { Children, isValidElement, useEffect, useId, useRef, useState } from 'react'; import { FlexBoxAlignItems, FlexBoxDirection } from '../../enums/index.js'; import { APPLY_AUTOMATICALLY, @@ -67,7 +67,7 @@ export const ManageViewsDialog = (props: ManageViewsDialogPropTypes) => { showOnlyFavorites, onManageViewsCancel } = props; - const uniqueId = useIsomorphicId(); + const uniqueId = useId(); const i18nBundle = useI18nBundle('@ui5/webcomponents-react'); const cancelText = i18nBundle.getText(CANCEL); const saveText = i18nBundle.getText(SAVE); diff --git a/packages/main/src/components/VariantManagement/SaveViewDialog.tsx b/packages/main/src/components/VariantManagement/SaveViewDialog.tsx index dbf208baa7c..4287d8e840b 100644 --- a/packages/main/src/components/VariantManagement/SaveViewDialog.tsx +++ b/packages/main/src/components/VariantManagement/SaveViewDialog.tsx @@ -1,8 +1,8 @@ import BarDesign from '@ui5/webcomponents/dist/types/BarDesign.js'; import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; -import { enrichEventWithDetails, useI18nBundle, useIsomorphicId, useStylesheet } from '@ui5/webcomponents-react-base'; +import { enrichEventWithDetails, useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; -import { useRef, useState } from 'react'; +import { useId, useRef, useState } from 'react'; import { FlexBoxAlignItems, FlexBoxDirection } from '../../enums/index.js'; import { APPLY_AUTOMATICALLY, @@ -52,7 +52,7 @@ export const SaveViewDialog = (props: SaveViewDialogPropTypes) => { const inputRef = useRef(undefined); const i18nBundle = useI18nBundle('@ui5/webcomponents-react'); useStylesheet(styleData, 'SaveViewDialog'); - const uniqueId = useIsomorphicId(); + const uniqueId = useId(); const cancelText = i18nBundle.getText(CANCEL); const saveText = i18nBundle.getText(SAVE); From 07c1ba0e78718e704decccda0dd37f651f6944af Mon Sep 17 00:00:00 2001 From: Marcus Notheis Date: Wed, 17 Jul 2024 10:51:55 +0200 Subject: [PATCH 2/5] Update yarn.lock --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9790787f7c5..3cc737e4b6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6904,7 +6904,7 @@ __metadata: peerDependencies: "@types/react": "*" "@ui5/webcomponents-base": ~2.0.1 - react: ^16.14.0 || ^17 || ^18 || ^19 + react: ^18 || ^19 peerDependenciesMeta: "@types/react": optional: true @@ -6925,7 +6925,7 @@ __metadata: peerDependencies: "@ui5/webcomponents-react": ~2.0.0 "@ui5/webcomponents-react-base": ~2.0.0 - react: ^16.14.0 || ^17 || ^18 + react: ^18 languageName: unknown linkType: soft @@ -6950,8 +6950,8 @@ __metadata: "@types/react-dom": "*" "@ui5/webcomponents-compat": ~2.0.1 "@ui5/webcomponents-react": "workspace:~" - react: ^16.14.0 || ^17 || ^18 || ^19 - react-dom: ^16.14.0 || ^17 || ^18 || ^19 + react: ^18 || ^19 + react-dom: ^18 || ^19 peerDependenciesMeta: "@types/react": optional: true @@ -6979,8 +6979,8 @@ __metadata: "@ui5/webcomponents-base": ~2.0.1 "@ui5/webcomponents-fiori": ~2.0.1 "@ui5/webcomponents-icons": ~2.0.1 - react: ^16.14.0 || ^17 || ^18 || ^19 - react-dom: ^16.14.0 || ^17 || ^18 || ^19 + react: ^18 || ^19 + react-dom: ^18 || ^19 peerDependenciesMeta: "@types/react": optional: true From bb4b71a5a74e90915eaca48952b86719e6f76c3e Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Thu, 18 Jul 2024 17:54:18 +0200 Subject: [PATCH 3/5] enrichEventWithDetails: remove `persist` check --- packages/base/src/utils/index.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/base/src/utils/index.ts b/packages/base/src/utils/index.ts index 956f427e0d4..1ad90d4739d 100644 --- a/packages/base/src/utils/index.ts +++ b/packages/base/src/utils/index.ts @@ -1,5 +1,3 @@ -import type { SyntheticEvent } from 'react'; - export const deprecationNotice = (component: string, message: string) => { if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') { const value = `*** ui5-webcomponents-react Deprecation Notice - ${component} ***\n`; @@ -23,14 +21,6 @@ export const enrichEventWithDetails = < event: Event, payload: Detail ): EnrichedEventType => { - // todo: once we drop React 16 support, remove this - // the helper accepts both SyntheticEvents and browser events - const syntheticEventCast = event as unknown as SyntheticEvent; - if (typeof syntheticEventCast.persist === 'function') { - // if there is a persist method, it's a SyntheticEvent so we need to persist it - syntheticEventCast.persist(); - } - // Determine if we need to create a new details object const shouldCreateNewDetails = event.detail === null || event.detail === undefined || typeof event.detail !== 'object'; From 1e21348ead4a63bd14f1f10eeba9169b0eb9f64c Mon Sep 17 00:00:00 2001 From: Marcus Notheis Date: Fri, 19 Jul 2024 11:05:05 +0200 Subject: [PATCH 4/5] Update index.ts --- packages/base/src/utils/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/base/src/utils/index.ts b/packages/base/src/utils/index.ts index 1ad90d4739d..b85c93c87d0 100644 --- a/packages/base/src/utils/index.ts +++ b/packages/base/src/utils/index.ts @@ -21,6 +21,10 @@ export const enrichEventWithDetails = < event: Event, payload: Detail ): EnrichedEventType => { + if (!event) { + return event; + } + // Determine if we need to create a new details object const shouldCreateNewDetails = event.detail === null || event.detail === undefined || typeof event.detail !== 'object'; From e6db463255dbb76034134b0abe00872f460a402f Mon Sep 17 00:00:00 2001 From: Marcus Notheis Date: Fri, 19 Jul 2024 11:22:50 +0200 Subject: [PATCH 5/5] Update index.ts --- packages/base/src/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/base/src/utils/index.ts b/packages/base/src/utils/index.ts index b85c93c87d0..e2531643135 100644 --- a/packages/base/src/utils/index.ts +++ b/packages/base/src/utils/index.ts @@ -24,7 +24,7 @@ export const enrichEventWithDetails = < if (!event) { return event; } - + // Determine if we need to create a new details object const shouldCreateNewDetails = event.detail === null || event.detail === undefined || typeof event.detail !== 'object';