From 2397363e99c6f4cb2bcfb788c552861c0c3df587 Mon Sep 17 00:00:00 2001 From: Amanuel Sisay Date: Thu, 12 Feb 2026 22:37:23 +0100 Subject: [PATCH 1/3] fix: use chilren.toArray() for the react 19+ --- .../flexible-column-layout/index.tsx | 2 +- src/column-layout/grid-column-layout.tsx | 2 +- src/grid/internal.tsx | 2 +- src/internal/utils/flatten-children.ts | 17 +++++++++++++++++ src/space-between/internal.tsx | 7 ++++--- 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/internal/utils/flatten-children.ts diff --git a/src/column-layout/flexible-column-layout/index.tsx b/src/column-layout/flexible-column-layout/index.tsx index c80c38f2d0..9c0fa1c725 100644 --- a/src/column-layout/flexible-column-layout/index.tsx +++ b/src/column-layout/flexible-column-layout/index.tsx @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import flattenChildren from 'react-keyed-flatten-children'; import clsx from 'clsx'; import { useContainerQuery } from '@cloudscape-design/component-toolkit'; +import { flattenChildren } from '../../internal/utils/flatten-children'; import { InternalColumnLayoutProps } from '../interfaces'; import styles from './styles.css.js'; diff --git a/src/column-layout/grid-column-layout.tsx b/src/column-layout/grid-column-layout.tsx index 067c725df8..c303e4181d 100644 --- a/src/column-layout/grid-column-layout.tsx +++ b/src/column-layout/grid-column-layout.tsx @@ -1,12 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import flattenChildren from 'react-keyed-flatten-children'; import clsx from 'clsx'; import { GridProps } from '../grid/interfaces'; import InternalGrid from '../grid/internal'; import { useContainerBreakpoints } from '../internal/hooks/container-queries'; +import { flattenChildren } from '../internal/utils/flatten-children'; import { InternalColumnLayoutProps } from './interfaces'; import { COLUMN_TRIGGERS, ColumnLayoutBreakpoint } from './internal'; import { repeat } from './util'; diff --git a/src/grid/internal.tsx b/src/grid/internal.tsx index cc7c05881c..f5893669ec 100644 --- a/src/grid/internal.tsx +++ b/src/grid/internal.tsx @@ -1,7 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import flattenChildren from 'react-keyed-flatten-children'; import clsx, { ClassValue } from 'clsx'; import { useMergeRefs, warnOnce } from '@cloudscape-design/component-toolkit/internal'; @@ -11,6 +10,7 @@ import { Breakpoint, matchBreakpointMapping } from '../internal/breakpoints'; import { useContainerBreakpoints } from '../internal/hooks/container-queries'; import { InternalBaseComponentProps } from '../internal/hooks/use-base-component'; import { isDevelopment } from '../internal/is-development'; +import { flattenChildren } from '../internal/utils/flatten-children'; import { GridProps } from './interfaces'; import styles from './styles.css.js'; diff --git a/src/internal/utils/flatten-children.ts b/src/internal/utils/flatten-children.ts new file mode 100644 index 0000000000..d34d6e86fc --- /dev/null +++ b/src/internal/utils/flatten-children.ts @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { ReactNode } from 'react'; +import flattenChildrenLegacy from 'react-keyed-flatten-children'; + +const majorVersion = parseInt(React.version.split('.')[0], 10); + +export function flattenChildren(children: ReactNode): ReactNode[] { + if (majorVersion >= 19) { + // React 19+: Uses Children.toArray() which doesn't flatten fragments. + // This also supports bigint which is not available in earlier React versions. + return React.Children.toArray(children); + } else { + // React 16-18: Use react-keyed-flatten-children for backward compatibility + return flattenChildrenLegacy(children); + } +} diff --git a/src/space-between/internal.tsx b/src/space-between/internal.tsx index f15e362fb2..8019045f74 100644 --- a/src/space-between/internal.tsx +++ b/src/space-between/internal.tsx @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { forwardRef } from 'react'; -import flattenChildren from 'react-keyed-flatten-children'; import clsx from 'clsx'; import { useMergeRefs } from '@cloudscape-design/component-toolkit/internal'; import { getBaseProps } from '../internal/base-component'; import { InternalBaseComponentProps } from '../internal/hooks/use-base-component'; +import { flattenChildren } from '../internal/utils/flatten-children'; import WithNativeAttributes from '../internal/utils/with-native-attributes'; import { SpaceBetweenProps } from './interfaces'; @@ -52,10 +52,11 @@ const InternalSpaceBetween = forwardRef( ref={mergedRef} > {flattenedChildren.map(child => { - const key = typeof child === 'object' ? child.key : undefined; + // If this react child is a primitive value, the key will be undefined + const key = (child as Record<'key', unknown>).key; return ( -
+
{child}
); From 642f4234ed7bb91f825f102f9310ed63eca8b37b Mon Sep 17 00:00:00 2001 From: Amanuel Sisay Date: Fri, 13 Feb 2026 13:42:28 +0100 Subject: [PATCH 2/3] test: add tests for flattenChildren --- .../utils/__tests__/flatten-children.test.tsx | 66 +++++++++++++++++++ src/internal/utils/flatten-children.ts | 4 +- 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/internal/utils/__tests__/flatten-children.test.tsx diff --git a/src/internal/utils/__tests__/flatten-children.test.tsx b/src/internal/utils/__tests__/flatten-children.test.tsx new file mode 100644 index 0000000000..80c1a34883 --- /dev/null +++ b/src/internal/utils/__tests__/flatten-children.test.tsx @@ -0,0 +1,66 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { Fragment } from 'react'; + +import { flattenChildren } from '../flatten-children'; + +describe('flattenChildren', () => { + const nestedArrayChildren = [ +
Item 1
, + [
Item 2
,
Item 3
], +
Item 4
, + ]; + + const fragmentChildren = [ + + A + B + , + C, + ]; + + const singleFragment = ( + + A + B + + ); + + describe('React 19+', () => { + beforeEach(() => { + Object.defineProperty(React, 'version', { + value: '19.0.0', + writable: true, + configurable: true, + }); + }); + + it('flattens nested arrays', () => { + expect(flattenChildren(nestedArrayChildren)).toHaveLength(4); + }); + + it('does NOT flatten fragments', () => { + expect(flattenChildren(fragmentChildren)).toHaveLength(2); + expect(flattenChildren(singleFragment)).toHaveLength(1); + }); + }); + + describe('React 16-18', () => { + beforeEach(() => { + Object.defineProperty(React, 'version', { + value: '18.2.0', + writable: true, + configurable: true, + }); + }); + + it('flattens nested arrays', () => { + expect(flattenChildren(nestedArrayChildren)).toHaveLength(4); + }); + + it('flattens fragments', () => { + expect(flattenChildren(fragmentChildren)).toHaveLength(3); + expect(flattenChildren(singleFragment)).toHaveLength(2); + }); + }); +}); diff --git a/src/internal/utils/flatten-children.ts b/src/internal/utils/flatten-children.ts index d34d6e86fc..d97279c2ec 100644 --- a/src/internal/utils/flatten-children.ts +++ b/src/internal/utils/flatten-children.ts @@ -3,9 +3,9 @@ import React, { ReactNode } from 'react'; import flattenChildrenLegacy from 'react-keyed-flatten-children'; -const majorVersion = parseInt(React.version.split('.')[0], 10); - export function flattenChildren(children: ReactNode): ReactNode[] { + const majorVersion = parseInt(React.version.split('.')[0], 10); + if (majorVersion >= 19) { // React 19+: Uses Children.toArray() which doesn't flatten fragments. // This also supports bigint which is not available in earlier React versions. From 6f566171eab6f78ea43b88585a171018d0fd96f6 Mon Sep 17 00:00:00 2001 From: Amanuel Sisay Date: Fri, 13 Feb 2026 14:12:01 +0100 Subject: [PATCH 3/3] chore: add comments to mocking --- src/internal/utils/__tests__/flatten-children.test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/internal/utils/__tests__/flatten-children.test.tsx b/src/internal/utils/__tests__/flatten-children.test.tsx index 80c1a34883..8e4a13a173 100644 --- a/src/internal/utils/__tests__/flatten-children.test.tsx +++ b/src/internal/utils/__tests__/flatten-children.test.tsx @@ -26,8 +26,10 @@ describe('flattenChildren', () => { ); + // Tests React 19+ behavior: uses Children.toArray() which does NOT flatten fragments describe('React 19+', () => { beforeEach(() => { + // Mock React.version to trigger Children.toArray() code path Object.defineProperty(React, 'version', { value: '19.0.0', writable: true, @@ -45,8 +47,10 @@ describe('flattenChildren', () => { }); }); + // Tests React 16-18 behavior: uses react-keyed-flatten-children which DOES flatten fragments describe('React 16-18', () => { beforeEach(() => { + // Mock React.version to trigger react-keyed-flatten-children code path Object.defineProperty(React, 'version', { value: '18.2.0', writable: true,