Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
110 changes: 109 additions & 1 deletion packages/react-core/src/components/Tabs/__tests__/Tabs.test.tsx
Original file line number Diff line number Diff line change
@@ -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<TabsProps, 'activeKey' | 'defaultActiveKey'>) => {
const contentRef1 = createRef<HTMLElement>();
const contentRef2 = createRef<HTMLElement>();
const contentRef3 = createRef<HTMLElement>();

let calculatedActiveKey;
if (props?.defaultActiveKey) {
calculatedActiveKey = props?.defaultActiveKey;
} else {
calculatedActiveKey = props?.activeKey;
}

return (
<>
<Tabs id="separateTabs" {...props}>
<Tab
eventKey={0}
title={<TabTitleText>Tab item 1</TabTitleText>}
tabContentId="refTab1Section"
tabContentRef={contentRef1}
/>
<Tab
eventKey={1}
title={<TabTitleText>Tab item 2</TabTitleText>}
tabContentId="refTab2Section"
tabContentRef={contentRef2}
/>
<Tab
eventKey={2}
title={<TabTitleText>Tab item 3</TabTitleText>}
tabContentId="refTab3Section"
tabContentRef={contentRef3}
/>
</Tabs>
<div>
<TabContent
eventKey={0}
id="refTab1Section"
ref={contentRef1}
aria-label="Tab item 1"
hidden={calculatedActiveKey !== 0}
>
Tab 1 section
</TabContent>
<TabContent
eventKey={1}
id="refTab2Section"
ref={contentRef2}
aria-label="Tab item 2"
hidden={calculatedActiveKey !== 1}
>
<TabContentBody>Tab 2 section</TabContentBody>
</TabContent>
<TabContent
eventKey={2}
id="refTab3Section"
ref={contentRef3}
aria-label="Tab item 3"
hidden={calculatedActiveKey !== 2}
>
<TabContentBody hasPadding>Tab 3 section with padding </TabContentBody>
</TabContent>
</div>
</>
);
};

test(`Renders with classes ${styles.tabs} and ${styles.modifiers.animateCurrent} by default`, () => {
render(
<Tabs role="region">
Expand Down Expand Up @@ -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(
<Tabs id="boxSecondaryVariantTabs" isBox variant="secondary">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ exports[`should render box tabs of secondary variant 1`] = `
<DocumentFragment>
<div
class="pf-v6-c-tabs pf-m-animate-current pf-m-box pf-m-secondary pf-m-initializing-accent"
data-ouia-component-id="OUIA-Generated-Tabs-20"
data-ouia-component-id="OUIA-Generated-Tabs-24"
data-ouia-component-type="PF6/Tabs"
data-ouia-safe="true"
id="boxSecondaryVariantTabs"
Expand Down Expand Up @@ -1228,7 +1228,7 @@ exports[`should render tabs with no bottom border 1`] = `
<DocumentFragment>
<div
class="pf-v6-c-tabs pf-m-animate-current pf-m-no-border-bottom pf-m-initializing-accent"
data-ouia-component-id="OUIA-Generated-Tabs-21"
data-ouia-component-id="OUIA-Generated-Tabs-25"
data-ouia-component-type="PF6/Tabs"
data-ouia-safe="true"
id="noBottomBorderTabs"
Expand Down
42 changes: 38 additions & 4 deletions packages/react-core/src/components/Tabs/examples/Tabs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@
id: Tabs
section: components
cssPrefix: pf-v6-c-tabs
propComponents: ['Tabs', 'Tab', 'TabContent', 'TabContentBody', 'TabTitleText', 'TabTitleIcon', 'HorizontalOverflowObject', 'TabAction', 'PopperOptions']
propComponents:
[
'Tabs',
'Tab',
'TabContent',
'TabContentBody',
'TabTitleText',
'TabTitleIcon',
'HorizontalOverflowObject',
'TabAction',
'PopperOptions'
]
ouia: true
---

Expand Down Expand Up @@ -35,6 +46,7 @@ Tabs can be styled as 'default' or 'boxed':
- Boxed tabs are outlined to emphasize the area that a tab spans. To preview boxed tabs in the following examples, select the 'isBox' checkbox, which sets the `isBox` property to true.

```ts file="./TabsDefault.tsx"

```

### Boxed secondary tabs
Expand All @@ -44,6 +56,7 @@ To change the background color of boxed tabs or the tab content, use the `varian
Toggle the tab color by selecting the 'Tabs secondary variant' checkbox in the following example.

```ts file="./TabsBoxSecondary.tsx"

```

### Vertical tabs
Expand All @@ -53,6 +66,7 @@ Vertical tabs are placed on the left-hand side of a page or container and may ap
To style tabs vertically, use the `isVertical` property.

```ts file="./TabsVertical.tsx"

```

### Vertical expandable tabs
Expand All @@ -64,20 +78,23 @@ Expandable tabs can be enabled at different breakpoints. The following example p
To flag vertical tabs when they're expanded, use the `isExpanded` property.

```ts file="./TabsVerticalExpandable.tsx"

```

### Vertical expandable uncontrolled

To flag the default expanded state for uncontrolled tabs, use the `defaultIsExpanded` property.

```ts file="./TabsVerticalExpandableUncontrolled.tsx"

```

### Default overflow tabs

By default, overflow is applied when there are too many tabs for the width of the container they are in. This overflow can be navigated by side-scrolling within the tabs section, or by selecting the left and right arrows.

```ts file="./TabsDefaultOverflow.tsx"

```

### Horizontal overflow tabs
Expand All @@ -89,6 +106,7 @@ To enable horizontal overflow, use the `isOverflowHorizontal` property.
In the following example, select the 'Show overflowing tab count' checkbox to add a count of overflow items to the final “more” tab.

```ts file="./TabsHorizontalOverflow.tsx"

```

### With tooltip react ref
Expand All @@ -98,28 +116,31 @@ When using a React ref to link a tooltip to a tab component via the `reference`
The tooltip should also have the `id` property passed in. The value of `id` should be passed into the tab's `aria-describedby` property. This ensures a tooltip used with a React ref will be announced by the JAWS and NVDA screen readers.

```ts file="./TabsTooltipReactRef.tsx"

```

### Uncontrolled tabs

To allow the `<Tabs>` 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

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

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
Expand All @@ -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 `<TabTitleIcon>` component that contains the icon of your choice into the `title`. To use an icon alongside styled text, keep the text in the `<TabTitleText>` component.

```ts file="./TabsIconAndText.tsx"

```

### Subtabs
Expand All @@ -138,13 +160,15 @@ 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

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
Expand All @@ -154,27 +178,31 @@ 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 `<a>` instead of a `<button>`.

```ts file="./TabsNav.tsx"

```

### Subtabs linked to nav elements

Subtabs can also link to nav elements.

```ts file="./TabsNavSubtab.tsx"

```

### With separate content

If a `<TabContent>` component is defined outside of a `<Tabs>` component, use the `tabContentRef` and `tabContentId` properties
If a `<TabContent>` component is defined outside of a `<Tabs>` component, use the `tabContentRef` and `tabContentId` properties. The `hidden` property is used on `TabContent` to set the initial visible content.

```ts file="./TabsSeparateContent.tsx"

```

### With tab content with body and padding

To add a content body to a `<TabContent>` component, pass a `<TabContentBody>`. To add padding to the body section, use the `hasPadding` property.

```ts file="./TabsContentWithBodyPadding.tsx"

```

### Children mounting on click
Expand All @@ -184,29 +212,33 @@ To mount tab children (add to the DOM) when a tab is clicked, use the `mountOnEn
Note that this property does not create the tab children until the tab is clicked, so they are not preloaded into the DOM.

```ts file="./TabsChildrenMounting.tsx"

```

### Unmounting invisible children

To unmount tab children (remove from the DOM) when they are no longer visible, use the `unmountOnExit` property.

```ts file="./TabsUnmountingInvisibleChildren.tsx"

```

### Toggled tab content

You may control tabs from outside of the tabs component. For example, select the "Hide tab 2" button below to make "Tab item 2" invisible.
You may control tabs from outside of the tabs component. For example, select the "Hide tab 2" button below to make "Tab item 2" invisible.

The tab its content should only be mounted when the tab is visible.

```ts file="./TabsToggledSeparateContent.tsx"

```

### Dynamic tabs

To enable closeable tabs, pass the `onClose` property to the `<Tabs>` component. To enable a button that adds new tabs, pass the `onAdd` property to `<Tabs>`.

```ts file="./TabsDynamic.tsx"

```

### With help action popover
Expand All @@ -216,6 +248,7 @@ You may add a help action to a tab to provide users with additional context in a
To render an action beside the tab content, use the `actions` property of a `<Tab>`. Pass a popover and a `<TabsAction>` component into the `actions` property.

```ts file="./TabsHelp.tsx"

```

### With help and close actions
Expand All @@ -225,4 +258,5 @@ To add multiple actions to a tab, create a `<TabAction>` component for each acti
The following example passes in both help popover and close actions.

```ts file="./TabsHelpAndClose.tsx"

```
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const TabsSeparateContent: React.FunctionComponent = () => {
id="refTab1Section"
ref={contentRef1}
aria-label="This is content for the first separate content tab"
hidden={activeTabKey !== 0}
>
Tab 1 section
</TabContent>
Expand All @@ -56,7 +57,7 @@ export const TabsSeparateContent: React.FunctionComponent = () => {
id="refTab2Section"
ref={contentRef2}
aria-label="This is content for the second separate content tab"
hidden
hidden={activeTabKey !== 1}
>
Tab 2 section
</TabContent>
Expand All @@ -65,7 +66,7 @@ export const TabsSeparateContent: React.FunctionComponent = () => {
id="refTab3Section"
ref={contentRef3}
aria-label="This is content for the third separate content tab"
hidden
hidden={activeTabKey !== 2}
>
Tab 3 section
</TabContent>
Expand Down
Loading