Skip to content

Commit b67e3ea

Browse files
fix(eds-core-react): Tabs now allow 'null' value as child element 'Tabs.List' and 'Tabs.Panel' (#3878)
1 parent 23479f7 commit b67e3ea

File tree

3 files changed

+47
-12
lines changed

3 files changed

+47
-12
lines changed

packages/eds-core-react/src/components/Tabs/TabList.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
RefAttributes,
1111
cloneElement,
1212
Children as ReactChildren,
13+
isValidElement,
1314
} from 'react'
1415
import styled from 'styled-components'
1516
import { mergeRefs } from '@equinor/eds-utils'
@@ -96,31 +97,33 @@ const TabList = forwardRef<HTMLDivElement, TabListProps>(function TabsList(
9697
[arrowNavigating, tabsFocused],
9798
)
9899

99-
const Tabs = ReactChildren.map(
100-
children,
101-
(child: TabChild, $index: number) => {
102-
const childProps = child.props as TabChild
100+
const Tabs =
101+
ReactChildren.map(children, (child, $index) => {
102+
if (!isValidElement(child)) {
103+
return null
104+
}
105+
const tabChild = child as TabChild
106+
const childProps = tabChild.props as TabChild
103107
const controlledActive = childProps.value
104108
const isActive = controlledActive
105109
? controlledActive === activeTab
106110
: $index === activeTab
107111

108112
const tabRef = isActive
109-
? mergeRefs<HTMLButtonElement>(child.ref, selectedTabRef)
110-
: child.ref
113+
? mergeRefs<HTMLButtonElement>(tabChild.ref, selectedTabRef)
114+
: tabChild.ref
111115

112116
if (isActive) currentTab.current = $index
113117

114-
return cloneElement(child, {
118+
return cloneElement(tabChild, {
115119
id: `${tabsId}-tab-${$index + 1}`,
116120
'aria-controls': `${tabsId}-panel-${$index + 1}`,
117121
active: isActive,
118122
$index,
119123
onClick: () => handleChange($index),
120124
ref: tabRef,
121125
})
122-
},
123-
)
126+
}) ?? []
124127

125128
const focusableChildren: number[] = Tabs.filter((child: TabChild) => {
126129
const childProps = child.props as TabChild

packages/eds-core-react/src/components/Tabs/TabPanels.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
HTMLAttributes,
66
cloneElement,
77
Children as ReactChildren,
8+
isValidElement,
89
} from 'react'
910
import { TabsContext } from './Tabs.context'
1011

@@ -18,9 +19,10 @@ const TabPanels = forwardRef<HTMLDivElement, TabPanelsProps>(function TabPanels(
1819
) {
1920
const { activeTab, tabsId } = useContext(TabsContext)
2021

21-
const Panels = ReactChildren.map(children, (child: ReactElement, $index) => {
22-
if (conditionalRender && activeTab !== $index) return null
23-
return cloneElement(child, {
22+
const Panels = ReactChildren.map(children, (child, $index) => {
23+
if (!isValidElement(child) || (conditionalRender && activeTab !== $index))
24+
return null
25+
return cloneElement(child as ReactElement, {
2426
id: `${tabsId}-panel-${$index + 1}`,
2527
'aria-labelledby': `${tabsId}-tab-${$index + 1}`,
2628
hidden: activeTab !== $index,

packages/eds-core-react/src/components/Tabs/Tabs.test.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,34 @@ describe('Tabs', () => {
175175
expect(screen.getByTestId(tablist)).toBeDefined()
176176
expect(screen.getByTestId(tabpanels)).toBeDefined()
177177
})
178+
it("Doesn't crash if null-child is provided to Tabs.List or Tabs.Panels", () => {
179+
const tablist = 'tablist'
180+
const tabpanels = 'tabpanels'
181+
render(
182+
<Tabs>
183+
<Tabs.List data-testid={tablist}>
184+
<Tabs.Tab>First</Tabs.Tab>
185+
{null}
186+
</Tabs.List>
187+
<Tabs.Panels data-testid={tabpanels}>
188+
<Tabs.Panel>Second</Tabs.Panel>
189+
{null}
190+
</Tabs.Panels>
191+
</Tabs>,
192+
)
193+
expect(screen.getByTestId(tablist)).toBeDefined()
194+
expect(screen.getByTestId(tabpanels)).toBeDefined()
195+
})
196+
it("Doesn't crash if only null-child is provided to Tabs.List or Tabs.Panels", () => {
197+
const tablist = 'tablist'
198+
const tabpanels = 'tabpanels'
199+
render(
200+
<Tabs>
201+
<Tabs.List data-testid={tablist}>{null}</Tabs.List>
202+
<Tabs.Panels data-testid={tabpanels}>{null}</Tabs.Panels>
203+
</Tabs>,
204+
)
205+
expect(screen.getByTestId(tablist)).toBeDefined()
206+
expect(screen.getByTestId(tabpanels)).toBeDefined()
207+
})
178208
})

0 commit comments

Comments
 (0)