Skip to content

Commit 27e7e54

Browse files
chore(Tabs): Update separate content examples (#12021)
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.
1 parent 757457f commit 27e7e54

File tree

4 files changed

+152
-9
lines changed

4 files changed

+152
-9
lines changed

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

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,83 @@
11
import { render, screen, act } from '@testing-library/react';
22
import userEvent from '@testing-library/user-event';
3-
import { Tabs } from '../Tabs';
3+
import { Tabs, TabsProps } from '../Tabs';
44
import styles from '@patternfly/react-styles/css/components/Tabs/tabs';
55
import { Tab } from '../Tab';
66
import { TabTitleText } from '../TabTitleText';
77
import { TabTitleIcon } from '../TabTitleIcon';
88
import { TabContent } from '../TabContent';
99
import { TabContentBody } from '../TabContentBody';
10+
import { createRef } from 'react';
1011

1112
jest.mock('../../../helpers/GenerateId/GenerateId');
1213

14+
const renderSeparateTabs = (props?: Pick<TabsProps, 'activeKey' | 'defaultActiveKey'>) => {
15+
const contentRef1 = createRef<HTMLElement>();
16+
const contentRef2 = createRef<HTMLElement>();
17+
const contentRef3 = createRef<HTMLElement>();
18+
19+
let calculatedActiveKey;
20+
if (props?.defaultActiveKey) {
21+
calculatedActiveKey = props?.defaultActiveKey;
22+
} else {
23+
calculatedActiveKey = props?.activeKey;
24+
}
25+
26+
return (
27+
<>
28+
<Tabs id="separateTabs" {...props}>
29+
<Tab
30+
eventKey={0}
31+
title={<TabTitleText>Tab item 1</TabTitleText>}
32+
tabContentId="refTab1Section"
33+
tabContentRef={contentRef1}
34+
/>
35+
<Tab
36+
eventKey={1}
37+
title={<TabTitleText>Tab item 2</TabTitleText>}
38+
tabContentId="refTab2Section"
39+
tabContentRef={contentRef2}
40+
/>
41+
<Tab
42+
eventKey={2}
43+
title={<TabTitleText>Tab item 3</TabTitleText>}
44+
tabContentId="refTab3Section"
45+
tabContentRef={contentRef3}
46+
/>
47+
</Tabs>
48+
<div>
49+
<TabContent
50+
eventKey={0}
51+
id="refTab1Section"
52+
ref={contentRef1}
53+
aria-label="Tab item 1"
54+
hidden={calculatedActiveKey !== 0}
55+
>
56+
Tab 1 section
57+
</TabContent>
58+
<TabContent
59+
eventKey={1}
60+
id="refTab2Section"
61+
ref={contentRef2}
62+
aria-label="Tab item 2"
63+
hidden={calculatedActiveKey !== 1}
64+
>
65+
<TabContentBody>Tab 2 section</TabContentBody>
66+
</TabContent>
67+
<TabContent
68+
eventKey={2}
69+
id="refTab3Section"
70+
ref={contentRef3}
71+
aria-label="Tab item 3"
72+
hidden={calculatedActiveKey !== 2}
73+
>
74+
<TabContentBody hasPadding>Tab 3 section with padding </TabContentBody>
75+
</TabContent>
76+
</div>
77+
</>
78+
);
79+
};
80+
1381
test(`Renders with classes ${styles.tabs} and ${styles.modifiers.animateCurrent} by default`, () => {
1482
render(
1583
<Tabs role="region">
@@ -429,6 +497,46 @@ test('should render tabs with separate content', () => {
429497
expect(asFragment()).toMatchSnapshot();
430498
});
431499

500+
test('should render correct tab content for uncontrolled tabs with separate content', () => {
501+
render(renderSeparateTabs({ defaultActiveKey: 1 }));
502+
503+
expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible();
504+
expect(screen.getByText(/Tab 2 section/i)).toBeVisible();
505+
expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible();
506+
});
507+
508+
test('should correctly advance tab content for uncontrolled tabs with separate content', async () => {
509+
render(renderSeparateTabs({ defaultActiveKey: 1 }));
510+
511+
userEvent.setup();
512+
expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible();
513+
expect(screen.getByText(/Tab 2 section/i)).toBeVisible();
514+
expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible();
515+
await userEvent.click(screen.getByRole('tab', { name: /Tab item 1/i }));
516+
expect(screen.getByText(/Tab 1 section/i)).toBeVisible();
517+
expect(screen.getByText(/Tab 2 section/i)).not.toBeVisible();
518+
});
519+
520+
test('should render correct tab content for controlled tabs with separate content', () => {
521+
render(renderSeparateTabs({ activeKey: 1 }));
522+
523+
expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible();
524+
expect(screen.getByText(/Tab 2 section/i)).toBeVisible();
525+
expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible();
526+
});
527+
528+
test('should correctly advance tab content for controlled tabs with separate content', async () => {
529+
render(renderSeparateTabs({ activeKey: 1 }));
530+
531+
userEvent.setup();
532+
expect(screen.getByText(/Tab 1 section/i)).not.toBeVisible();
533+
expect(screen.getByText(/Tab 2 section/i)).toBeVisible();
534+
expect(screen.getByText(/Tab 3 section with padding/i)).not.toBeVisible();
535+
await userEvent.click(screen.getByRole('tab', { name: /Tab item 1/i }));
536+
expect(screen.getByText(/Tab 1 section/i)).toBeVisible();
537+
expect(screen.getByText(/Tab 2 section/i)).not.toBeVisible();
538+
});
539+
432540
test('should render box tabs of secondary variant', () => {
433541
const { asFragment } = render(
434542
<Tabs id="boxSecondaryVariantTabs" isBox variant="secondary">

packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ exports[`should render box tabs of secondary variant 1`] = `
281281
<DocumentFragment>
282282
<div
283283
class="pf-v6-c-tabs pf-m-animate-current pf-m-box pf-m-secondary pf-m-initializing-accent"
284-
data-ouia-component-id="OUIA-Generated-Tabs-20"
284+
data-ouia-component-id="OUIA-Generated-Tabs-24"
285285
data-ouia-component-type="PF6/Tabs"
286286
data-ouia-safe="true"
287287
id="boxSecondaryVariantTabs"
@@ -1228,7 +1228,7 @@ exports[`should render tabs with no bottom border 1`] = `
12281228
<DocumentFragment>
12291229
<div
12301230
class="pf-v6-c-tabs pf-m-animate-current pf-m-no-border-bottom pf-m-initializing-accent"
1231-
data-ouia-component-id="OUIA-Generated-Tabs-21"
1231+
data-ouia-component-id="OUIA-Generated-Tabs-25"
12321232
data-ouia-component-type="PF6/Tabs"
12331233
data-ouia-safe="true"
12341234
id="noBottomBorderTabs"

packages/react-core/src/components/Tabs/examples/Tabs.md

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22
id: Tabs
33
section: components
44
cssPrefix: pf-v6-c-tabs
5-
propComponents: ['Tabs', 'Tab', 'TabContent', 'TabContentBody', 'TabTitleText', 'TabTitleIcon', 'HorizontalOverflowObject', 'TabAction', 'PopperOptions']
5+
propComponents:
6+
[
7+
'Tabs',
8+
'Tab',
9+
'TabContent',
10+
'TabContentBody',
11+
'TabTitleText',
12+
'TabTitleIcon',
13+
'HorizontalOverflowObject',
14+
'TabAction',
15+
'PopperOptions'
16+
]
617
ouia: true
718
---
819

@@ -35,6 +46,7 @@ Tabs can be styled as 'default' or 'boxed':
3546
- 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.
3647

3748
```ts file="./TabsDefault.tsx"
49+
3850
```
3951

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

4658
```ts file="./TabsBoxSecondary.tsx"
59+
4760
```
4861

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

5568
```ts file="./TabsVertical.tsx"
69+
5670
```
5771

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

6680
```ts file="./TabsVerticalExpandable.tsx"
81+
6782
```
6883

6984
### Vertical expandable uncontrolled
7085

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

7388
```ts file="./TabsVerticalExpandableUncontrolled.tsx"
89+
7490
```
7591

7692
### Default overflow tabs
7793

7894
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.
7995

8096
```ts file="./TabsDefaultOverflow.tsx"
97+
8198
```
8299

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

91108
```ts file="./TabsHorizontalOverflow.tsx"
109+
92110
```
93111

94112
### With tooltip react ref
@@ -98,28 +116,31 @@ When using a React ref to link a tooltip to a tab component via the `reference`
98116
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.
99117

100118
```ts file="./TabsTooltipReactRef.tsx"
119+
101120
```
102121

103122
### Uncontrolled tabs
104123

105124
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.
106125

107-
108126
```ts file="./TabsUncontrolled.tsx"
127+
109128
```
110129

111130
### With adjusted inset
112131

113132
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.
114133

115134
```ts file="./TabsInset.tsx"
135+
116136
```
117137

118138
### With page insets
119139

120140
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.
121141

122142
```ts file="./TabsPageInsets.tsx"
143+
123144
```
124145

125146
### With icons and text
@@ -129,6 +150,7 @@ You can render different content in the `title` property of a tab to add icons a
129150
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.
130151

131152
```ts file="./TabsIconAndText.tsx"
153+
132154
```
133155

134156
### Subtabs
@@ -138,13 +160,15 @@ Use subtabs within other components, like modals. Subtabs have less visually pro
138160
To apply subtab styling to tabs, use the `isSubtab` property.
139161

140162
```ts file="./TabsSubtabs.tsx"
163+
141164
```
142165

143166
### Filled tabs with icons
144167

145168
To allow tabs to fill the available width of the page section, use the `isFilled` property.
146169

147170
```ts file="./TabsFilledWithIcons.tsx"
171+
148172
```
149173

150174
### Tabs linked to nav elements
@@ -154,27 +178,31 @@ To let tabs link to nav elements, pass `{TabsComponent.nav}` into the `component
154178
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>`.
155179

156180
```ts file="./TabsNav.tsx"
181+
157182
```
158183

159184
### Subtabs linked to nav elements
160185

161186
Subtabs can also link to nav elements.
162187

163188
```ts file="./TabsNavSubtab.tsx"
189+
164190
```
165191

166192
### With separate content
167193

168-
If a `<TabContent>` component is defined outside of a `<Tabs>` component, use the `tabContentRef` and `tabContentId` properties
194+
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.
169195

170196
```ts file="./TabsSeparateContent.tsx"
197+
171198
```
172199

173200
### With tab content with body and padding
174201

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

177204
```ts file="./TabsContentWithBodyPadding.tsx"
205+
178206
```
179207

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

186214
```ts file="./TabsChildrenMounting.tsx"
215+
187216
```
188217

189218
### Unmounting invisible children
190219

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

193222
```ts file="./TabsUnmountingInvisibleChildren.tsx"
223+
194224
```
195225

196226
### Toggled tab content
197227

198-
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.
228+
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.
199229

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

202232
```ts file="./TabsToggledSeparateContent.tsx"
233+
203234
```
204235

205236
### Dynamic tabs
206237

207238
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>`.
208239

209240
```ts file="./TabsDynamic.tsx"
241+
210242
```
211243

212244
### With help action popover
@@ -216,6 +248,7 @@ You may add a help action to a tab to provide users with additional context in a
216248
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.
217249

218250
```ts file="./TabsHelp.tsx"
251+
219252
```
220253

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

227260
```ts file="./TabsHelpAndClose.tsx"
261+
228262
```

packages/react-core/src/components/Tabs/examples/TabsSeparateContent.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const TabsSeparateContent: React.FunctionComponent = () => {
4848
id="refTab1Section"
4949
ref={contentRef1}
5050
aria-label="This is content for the first separate content tab"
51+
hidden={activeTabKey !== 0}
5152
>
5253
Tab 1 section
5354
</TabContent>
@@ -56,7 +57,7 @@ export const TabsSeparateContent: React.FunctionComponent = () => {
5657
id="refTab2Section"
5758
ref={contentRef2}
5859
aria-label="This is content for the second separate content tab"
59-
hidden
60+
hidden={activeTabKey !== 1}
6061
>
6162
Tab 2 section
6263
</TabContent>
@@ -65,7 +66,7 @@ export const TabsSeparateContent: React.FunctionComponent = () => {
6566
id="refTab3Section"
6667
ref={contentRef3}
6768
aria-label="This is content for the third separate content tab"
68-
hidden
69+
hidden={activeTabKey !== 2}
6970
>
7071
Tab 3 section
7172
</TabContent>

0 commit comments

Comments
 (0)