Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Tabs): fix error with a single Tabs.Content as children #2534

Merged
merged 5 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,27 @@ export const TabsNoBorder = () => (
</ComponentBox>
</Wrapper>
)

export const TabsSingleChildrenReactElement = () =>
!globalThis.IS_TEST ? null : (
<Wrapper>
<ComponentBox data-visual-test="tabs-single-children-react-element">
<Tabs>
<Tabs.Content title="First">
<div>hello1</div>
</Tabs.Content>
</Tabs>
</ComponentBox>
</Wrapper>
)

export const TabsSingleElementData = () =>
!globalThis.IS_TEST ? null : (
<Wrapper>
<ComponentBox data-visual-test="tabs-single-element-data">
<Tabs
data={[{ title: 'First', key: 1, content: <div>hello1</div> }]}
/>
</ComponentBox>
</Wrapper>
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
TabsExampleReachRouterNavigation,
TabsNoBorder,
TabsExamplePrerender,
TabsSingleChildrenReactElement,
TabsSingleElementData,
} from 'Docs/uilib/components/tabs/Examples'

## Demos
Expand Down Expand Up @@ -86,3 +88,6 @@ const exampleContent = {
fourth: 'Fourth as a string only',
}
```

<TabsSingleChildrenReactElement />
<TabsSingleElementData />
87 changes: 51 additions & 36 deletions packages/dnb-eufemia/src/components/tabs/Tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,56 +199,71 @@ export default class Tabs extends React.PureComponent {
}

static getData(props) {
const addReactElement = (list, reactElem, reactElemIndex) => {
if (
reactElem.props &&
reactElem.props.displayName === 'CustomContent' // we use this solution, as Component.displayName
) {
// tabs data from main prop
const dataProps =
(props.tabs &&
Array.isArray(props.tabs) &&
props.tabs[reactElemIndex]) ||
{}

// props from the "CustomContent" Component
const componentProps = { ...reactElem.props }
if (componentProps.title === null) {
delete componentProps.title
}

const {
title,
key: _key,
hash,
...rest
} = {
...dataProps,
...componentProps,
...{ children: null }, // remove children, if there is some
}

list.push({
title,
key: (!_key && hash ? hash : _key) || slugify(title),
content: reactElem, // can be a Node or a Function
...rest,
})
}
}

let res = []

// check if we have to use the children prop to prepare our data
const data =
!props.data && props.children ? props.children : props.data

// if it is a React Component - collect data from Tabs.Content component
// if it is a array of React Components - collect data from Tabs.Content component
langz marked this conversation as resolved.
Show resolved Hide resolved
if (
Array.isArray(props.children) &&
(typeof props.children[0] === 'function' ||
React.isValidElement(props.children[0]))
) {
res = props.children.reduce((acc, content, i) => {
if (
content.props &&
content.props.displayName === 'CustomContent' // we use this solution, as Component.displayName
) {
// tabs data from main prop
const dataProps =
(props.tabs && Array.isArray(props.tabs) && props.tabs[i]) ||
{}

// props from the "CustomContent" Component
const componentProps = { ...content.props }
if (componentProps.title === null) {
delete componentProps.title
}

const {
title,
key: _key,
hash,
...rest
} = {
...dataProps,
...componentProps,
...{ children: null }, // remove children, if there is some
}

acc.push({
title,
key: (!_key && hash ? hash : _key) || slugify(title),
content, // can be a Node or a Function
...rest,
})
}
return acc
res = props.children.reduce((list, reactElem, i) => {
addReactElement(list, reactElem, i)
return list
}, [])
}

// if it is a single React Component - collect data from Tabs.Content component
if (
!Array.isArray(props.children) &&
(typeof props.children === 'function' ||
React.isValidElement(props.children))
) {
addReactElement(res, props.children)
}

// continue, while the children didn't contain our data
if (!(res && res.length > 0)) {
// if data is array, it looks good!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ describe('Tabs', () => {
expect(screenshot).toMatchImageSnapshot()
})

it('have to match single single children as react element', async () => {
langz marked this conversation as resolved.
Show resolved Hide resolved
const screenshot = await makeScreenshot({
selector:
'[data-visual-test="tabs-single-children-react-element"] .dnb-tabs__tabs',
})
expect(screenshot).toMatchImageSnapshot()
})

it('have to match when used with a single element in data', async () => {
const screenshot = await makeScreenshot({
selector:
'[data-visual-test="tabs-single-element-data"] .dnb-tabs__tabs',
})
expect(screenshot).toMatchImageSnapshot()
})

it('have to match a tablist with a click handler', async () => {
const screenshot = await makeScreenshot({
selector: '[data-visual-test="tabs-clickhandler"] .dnb-tabs__tabs',
Expand Down
36 changes: 36 additions & 0 deletions packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,42 @@ describe('A single Tab component', () => {
expect(testKey).toBe('third')
})

it('has to work with "Tabs.Content" as a single children', () => {
render(
<Tabs>
<Tabs.Content title="single title">
<div>single</div>
</Tabs.Content>
</Tabs>
)

expect(
document.querySelector('div.dnb-tabs__content__inner').textContent
).toBe('single')
expect(
document.querySelector('button span.dnb-tabs__button__title')
.textContent
).toBe('single title')
})

it('has to work with a single element for data property', () => {
render(
<Tabs
data={[
{ title: 'single title', key: 1, content: <div>single</div> },
]}
/>
)

expect(
document.querySelector('div.dnb-tabs__content').textContent
).toBe('single')
expect(
document.querySelector('button span.dnb-tabs__button__title')
.textContent
).toBe('single title')
})

it('should render in StrictMode', () => {
render(
<React.StrictMode>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 12 additions & 2 deletions packages/dnb-eufemia/src/components/tabs/stories/Tabs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,20 @@ export const TabsSandbox = () => {
<Box>
<Tabs tabs_style="mint-green" content_style="black-3">
<Tabs.Content title="First">
<H2>First</H2>
<div>hello1</div>
</Tabs.Content>
</Tabs>
<Tabs
tabs_style="mint-green"
content_style="black-3"
data={[{ title: 'First', key: 1, content: <div>hello1</div> }]}
/>
<Tabs tabs_style="mint-green" content_style="black-3">
<Tabs.Content title="First">
<div>hello1</div>
</Tabs.Content>
<Tabs.Content title="Second">
<H2>Second</H2>
<div>hello2</div>
</Tabs.Content>
</Tabs>
</Box>
Expand Down