Skip to content

Commit 693b1b3

Browse files
fix(Web Components): don't crash when single react element is used inside a slot fragment (#1316)
1 parent 73481d8 commit 693b1b3

File tree

3 files changed

+93
-9
lines changed

3 files changed

+93
-9
lines changed

packages/main/src/internal/__snapshots__/withWebComponent.test.tsx.snap

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,56 @@ exports[`withWebComponent conditional rendering 1`] = `
1515
</DocumentFragment>
1616
`;
1717

18+
exports[`withWebComponent destruct fragments in slots 1`] = `
19+
<DocumentFragment>
20+
<ui5-bar
21+
design="Header"
22+
ui5-bar=""
23+
>
24+
<span
25+
slot="startContent"
26+
>
27+
I'm here!
28+
</span>
29+
<span
30+
slot="startContent"
31+
>
32+
I'm here nested level 1!
33+
</span>
34+
<span
35+
slot="startContent"
36+
>
37+
I'm here nested level 2!
38+
</span>
39+
<span
40+
slot="startContent"
41+
>
42+
I'm the only child of a fragment in level 3!
43+
</span>
44+
<span
45+
slot="startContent"
46+
>
47+
I'm the only child of a fragment in level 1!
48+
</span>
49+
<span
50+
slot="startContent"
51+
>
52+
I'm the only child of a fragment in level 1!
53+
</span>
54+
<span
55+
slot="startContent"
56+
>
57+
I'm in an array inside of a fragment!
58+
</span>
59+
<span
60+
slot="startContent"
61+
>
62+
Me too!
63+
</span>
64+
</ui5-bar>
65+
</DocumentFragment>
66+
`;
67+
1868
exports[`withWebComponent scoping 1`] = `
1969
<DocumentFragment>
2070
<ui5-button-ui5-wcr

packages/main/src/internal/withWebComponent.test.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,39 @@ describe('withWebComponent', () => {
8787
expect(asFragment()).toMatchSnapshot();
8888
});
8989

90+
test('destruct fragments in slots', () => {
91+
const { asFragment } = render(
92+
<Bar
93+
startContent={
94+
<>
95+
{false && <span>I'm not here</span>}
96+
<span>I'm here!</span>
97+
<>
98+
{false && <span>I'm not here</span>}
99+
<span>I'm here nested level 1!</span>
100+
<>
101+
{false && <span>I'm not here</span>}
102+
<span>I'm here nested level 2!</span>
103+
<>
104+
<span>I'm the only child of a fragment in level 3!</span>
105+
</>
106+
</>
107+
</>
108+
<>
109+
<span>I'm the only child of a fragment in level 1!</span>
110+
</>
111+
<>{false && <span>I'm an empty fragment</span>}</>
112+
<>
113+
<span>I'm the only child of a fragment in level 1!</span>
114+
{[<span>I'm in an array inside of a fragment!</span>, <span>Me too!</span>]}
115+
</>
116+
</>
117+
}
118+
/>
119+
);
120+
expect(asFragment()).toMatchSnapshot();
121+
});
122+
90123
test('scoping', () => {
91124
setCustomElementsScopingSuffix('ui5-wcr');
92125
const { asFragment, rerender } = render(<Button>Scoping Test</Button>);

packages/main/src/internal/withWebComponent.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getEffectiveScopingSuffixForTag } from '@ui5/webcomponents-base/dist/CustomElementsScope';
22
import { useConsolidatedRef } from '@ui5/webcomponents-react-base/lib/useConsolidatedRef';
33
import React, {
4+
Children,
45
cloneElement,
56
ComponentType,
67
forwardRef,
@@ -23,8 +24,6 @@ const createEventPropName = (eventName) => `on${capitalizeFirstLetter(kebabToCam
2324

2425
type EventHandler = (event: CustomEvent<unknown>) => void;
2526

26-
const staticSlotStyle = { display: 'contents' };
27-
2827
export interface WithWebComponentPropTypes extends CommonProps {
2928
ref?: Ref<any>;
3029
children?: any | void;
@@ -64,16 +63,18 @@ export const withWebComponent = <T extends Record<string, any>>(
6463

6564
if (!slotValue) return acc;
6665

67-
let children = [];
66+
const slottedChildren = [];
6867
let index = 0;
6968
const removeFragments = (element) => {
7069
if (!element) return;
71-
if (element?.type === React.Fragment) {
72-
element.props?.children?.forEach((item) => {
73-
removeFragments(item);
74-
});
70+
if (element.type === React.Fragment) {
71+
Children.toArray(element.props?.children)
72+
.filter(Boolean)
73+
.forEach((item) => {
74+
removeFragments(item);
75+
});
7576
} else {
76-
children.push(
77+
slottedChildren.push(
7778
cloneElement(element, {
7879
key: `${name}-${index}`,
7980
slot: name
@@ -90,7 +91,7 @@ export const withWebComponent = <T extends Record<string, any>>(
9091
} else {
9192
removeFragments(slotValue);
9293
}
93-
return [...acc, ...children];
94+
return [...acc, ...slottedChildren];
9495
}, []);
9596
// event binding
9697
useEffect(

0 commit comments

Comments
 (0)