diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/Examples.tsx index c9d6b39ce41..625c3d4bd3a 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/Examples.tsx @@ -338,3 +338,27 @@ export const TabsNoBorder = () => ( ) + +export const TabsSingleChildrenReactElement = () => + !globalThis.IS_TEST ? null : ( + + + + +
hello1
+
+
+
+
+ ) + +export const TabsSingleElementData = () => + !globalThis.IS_TEST ? null : ( + + + hello1 }]} + /> + + + ) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/demos.mdx index d45e4babf5a..37e395dbbff 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/demos.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/tabs/demos.mdx @@ -13,6 +13,8 @@ import { TabsExampleReachRouterNavigation, TabsNoBorder, TabsExamplePrerender, + TabsSingleChildrenReactElement, + TabsSingleElementData, } from 'Docs/uilib/components/tabs/Examples' ## Demos @@ -86,3 +88,6 @@ const exampleContent = { fourth: 'Fourth as a string only', } ``` + + + diff --git a/packages/dnb-eufemia/src/components/tabs/Tabs.js b/packages/dnb-eufemia/src/components/tabs/Tabs.js index 54e54cba228..51885f1ab3c 100644 --- a/packages/dnb-eufemia/src/components/tabs/Tabs.js +++ b/packages/dnb-eufemia/src/components/tabs/Tabs.js @@ -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 an array of React Components - collect data from Tabs.Content component 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! diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.screenshot.test.ts b/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.screenshot.test.ts index 7d426cd55b0..02279bfba18 100644 --- a/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.screenshot.test.ts +++ b/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.screenshot.test.ts @@ -21,6 +21,20 @@ describe('Tabs', () => { expect(screenshot).toMatchImageSnapshot() }) + it('have to match when used with a single children as react element', async () => { + const screenshot = await makeScreenshot({ + selector: '[data-visual-test="tabs-single-children-react-element"]', + }) + 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"]', + }) + 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', diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.test.tsx b/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.test.tsx index d4cd92d57db..662e8fa329c 100644 --- a/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.test.tsx +++ b/packages/dnb-eufemia/src/components/tabs/__tests__/Tabs.test.tsx @@ -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( + + +
single
+
+
+ ) + + 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( + single }, + ]} + /> + ) + + 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( diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-single-single-children-as-react-element.snap.png b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-single-single-children-as-react-element.snap.png new file mode 100644 index 00000000000..2c78fa1c383 Binary files /dev/null and b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-single-single-children-as-react-element.snap.png differ diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-single-single-element-in-data.snap.png b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-single-single-element-in-data.snap.png new file mode 100644 index 00000000000..2c78fa1c383 Binary files /dev/null and b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-single-single-element-in-data.snap.png differ diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-when-used-with-a-single-children-as-react-element.snap.png b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-when-used-with-a-single-children-as-react-element.snap.png new file mode 100644 index 00000000000..1736a3df94d Binary files /dev/null and b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-when-used-with-a-single-children-as-react-element.snap.png differ diff --git a/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-when-used-with-a-single-element-in-data.snap.png b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-when-used-with-a-single-element-in-data.snap.png new file mode 100644 index 00000000000..1736a3df94d Binary files /dev/null and b/packages/dnb-eufemia/src/components/tabs/__tests__/__image_snapshots__/tabs-have-to-match-when-used-with-a-single-element-in-data.snap.png differ diff --git a/packages/dnb-eufemia/src/components/tabs/stories/Tabs.stories.tsx b/packages/dnb-eufemia/src/components/tabs/stories/Tabs.stories.tsx index 1e3d381caf2..fc2e944ba22 100644 --- a/packages/dnb-eufemia/src/components/tabs/stories/Tabs.stories.tsx +++ b/packages/dnb-eufemia/src/components/tabs/stories/Tabs.stories.tsx @@ -52,10 +52,20 @@ export const TabsSandbox = () => { -

First

+
hello1
+
+
+ hello1 }]} + /> + + +
hello1
-

Second

+
hello2