From 0788ac404e9e231b9bb10de77466457406932e4a Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Mon, 29 Sep 2025 13:06:59 -0400 Subject: [PATCH] chore(Tabs): Update separate content examples Separate content examples did not make it clear enough that the hidden attribute on TabContent is used to set the initial state of Tabs with separate content. I updated the example so it is managed programmatically and updated the docs. I also added some tests to make sure it is working as advertised. --- .../components/Tabs/__tests__/Tabs.test.tsx | 110 +++++++++++++++++- .../__snapshots__/Tabs.test.tsx.snap | 4 +- .../src/components/Tabs/examples/Tabs.md | 42 ++++++- .../Tabs/examples/TabsSeparateContent.tsx | 5 +- 4 files changed, 152 insertions(+), 9 deletions(-) diff --git a/packages/react-core/src/components/Tabs/__tests__/Tabs.test.tsx b/packages/react-core/src/components/Tabs/__tests__/Tabs.test.tsx index 790c386eb31..d668e1c77ab 100644 --- a/packages/react-core/src/components/Tabs/__tests__/Tabs.test.tsx +++ b/packages/react-core/src/components/Tabs/__tests__/Tabs.test.tsx @@ -1,15 +1,83 @@ import { render, screen, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { Tabs } from '../Tabs'; +import { Tabs, TabsProps } from '../Tabs'; import styles from '@patternfly/react-styles/css/components/Tabs/tabs'; import { Tab } from '../Tab'; import { TabTitleText } from '../TabTitleText'; import { TabTitleIcon } from '../TabTitleIcon'; import { TabContent } from '../TabContent'; import { TabContentBody } from '../TabContentBody'; +import { createRef } from 'react'; jest.mock('../../../helpers/GenerateId/GenerateId'); +const renderSeparateTabs = (props?: Pick) => { + const contentRef1 = createRef(); + const contentRef2 = createRef(); + const contentRef3 = createRef(); + + let calculatedActiveKey; + if (props?.defaultActiveKey) { + calculatedActiveKey = props?.defaultActiveKey; + } else { + calculatedActiveKey = props?.activeKey; + } + + return ( + <> + + Tab item 1} + tabContentId="refTab1Section" + tabContentRef={contentRef1} + /> + Tab item 2} + tabContentId="refTab2Section" + tabContentRef={contentRef2} + /> + Tab item 3} + tabContentId="refTab3Section" + tabContentRef={contentRef3} + /> + +
+ + + +
+ + ); +}; + test(`Renders with classes ${styles.tabs} and ${styles.modifiers.animateCurrent} by default`, () => { render( @@ -429,6 +497,46 @@ test('should render tabs with separate content', () => { expect(asFragment()).toMatchSnapshot(); }); +test('should render correct tab content for uncontrolled tabs with separate content', () => { + render(renderSeparateTabs({ defaultActiveKey: 1 })); + + expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible(); + expect(screen.getByText(/Tab 2 section/i)).toBeVisible(); + expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible(); +}); + +test('should correctly advance tab content for uncontrolled tabs with separate content', async () => { + render(renderSeparateTabs({ defaultActiveKey: 1 })); + + userEvent.setup(); + expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible(); + expect(screen.getByText(/Tab 2 section/i)).toBeVisible(); + expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible(); + await userEvent.click(screen.getByRole('tab', { name: /Tab item 1/i })); + expect(screen.getByText(/Tab 1 section/i)).toBeVisible(); + expect(screen.getByText(/Tab 2 section/i)).not.toBeVisible(); +}); + +test('should render correct tab content for controlled tabs with separate content', () => { + render(renderSeparateTabs({ activeKey: 1 })); + + expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible(); + expect(screen.getByText(/Tab 2 section/i)).toBeVisible(); + expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible(); +}); + +test('should correctly advance tab content for controlled tabs with separate content', async () => { + render(renderSeparateTabs({ activeKey: 1 })); + + userEvent.setup(); + expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible(); + expect(screen.getByText(/Tab 2 section/i)).toBeVisible(); + expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible(); + await userEvent.click(screen.getByRole('tab', { name: /Tab item 1/i })); + expect(screen.getByText(/Tab 1 section/i)).toBeVisible(); + expect(screen.getByText(/Tab 2 section/i)).not.toBeVisible(); +}); + test('should render box tabs of secondary variant', () => { const { asFragment } = render( diff --git a/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap b/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap index 26915039523..c20a358a503 100644 --- a/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +++ b/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap @@ -281,7 +281,7 @@ exports[`should render box tabs of secondary variant 1`] = `
` component to manage setting the active tab and displaying correct content itself, use uncontrolled tabs, as shown in the following example. - ```ts file="./TabsUncontrolled.tsx" + ``` ### With adjusted inset @@ -113,6 +132,7 @@ To allow the `` component to manage setting the active tab and displaying To adjust the inset of tabs and visually separate them more, use the `inset` property. You can set the inset to "insetNone", "insetSm", "insetMd", "insetLg", "insetXl", or "inset2xl" at "default", "sm", "md", "lg, "xl, and "2xl" breakpoints. ```ts file="./TabsInset.tsx" + ``` ### With page insets @@ -120,6 +140,7 @@ To adjust the inset of tabs and visually separate them more, use the `inset` pro To adjust the left padding of tabs, use the `usePageInsets` property. This property aligns the tabs padding with the default padding of the page section, which makes it easier to align tabs with page section content. ```ts file="./TabsPageInsets.tsx" + ``` ### With icons and text @@ -129,6 +150,7 @@ You can render different content in the `title` property of a tab to add icons a To add an icon to a tab, pass a `` component that contains the icon of your choice into the `title`. To use an icon alongside styled text, keep the text in the `` component. ```ts file="./TabsIconAndText.tsx" + ``` ### Subtabs @@ -138,6 +160,7 @@ Use subtabs within other components, like modals. Subtabs have less visually pro To apply subtab styling to tabs, use the `isSubtab` property. ```ts file="./TabsSubtabs.tsx" + ``` ### Filled tabs with icons @@ -145,6 +168,7 @@ To apply subtab styling to tabs, use the `isSubtab` property. To allow tabs to fill the available width of the page section, use the `isFilled` property. ```ts file="./TabsFilledWithIcons.tsx" + ``` ### Tabs linked to nav elements @@ -154,6 +178,7 @@ To let tabs link to nav elements, pass `{TabsComponent.nav}` into the `component Nav tabs should use the `href` property to link the tab to the URL of another page or page section. A tab with an `href` will render as an `` instead of a `