@patternfly/react-core 4.263.0 → 4.264.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/esm/components/Tabs/Tab.d.ts +4 -2
  3. package/dist/esm/components/Tabs/Tab.d.ts.map +1 -1
  4. package/dist/esm/components/Tabs/Tab.js +6 -7
  5. package/dist/esm/components/Tabs/Tab.js.map +1 -1
  6. package/dist/esm/components/Tabs/TabAction.d.ts +20 -0
  7. package/dist/esm/components/Tabs/TabAction.d.ts.map +1 -0
  8. package/dist/esm/components/Tabs/TabAction.js +17 -0
  9. package/dist/esm/components/Tabs/TabAction.js.map +1 -0
  10. package/dist/esm/components/Tabs/Tabs.d.ts +1 -1
  11. package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
  12. package/dist/esm/components/Tabs/index.d.ts +1 -0
  13. package/dist/esm/components/Tabs/index.d.ts.map +1 -1
  14. package/dist/esm/components/Tabs/index.js +1 -0
  15. package/dist/esm/components/Tabs/index.js.map +1 -1
  16. package/dist/js/components/Tabs/Tab.d.ts +4 -2
  17. package/dist/js/components/Tabs/Tab.d.ts.map +1 -1
  18. package/dist/js/components/Tabs/Tab.js +6 -7
  19. package/dist/js/components/Tabs/Tab.js.map +1 -1
  20. package/dist/js/components/Tabs/TabAction.d.ts +20 -0
  21. package/dist/js/components/Tabs/TabAction.d.ts.map +1 -0
  22. package/dist/js/components/Tabs/TabAction.js +20 -0
  23. package/dist/js/components/Tabs/TabAction.js.map +1 -0
  24. package/dist/js/components/Tabs/Tabs.d.ts +1 -1
  25. package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
  26. package/dist/js/components/Tabs/index.d.ts +1 -0
  27. package/dist/js/components/Tabs/index.d.ts.map +1 -1
  28. package/dist/js/components/Tabs/index.js +1 -0
  29. package/dist/js/components/Tabs/index.js.map +1 -1
  30. package/dist/styles/assets/pficon/pficon.scss +12 -0
  31. package/dist/styles/assets/pficon/pficon.woff +0 -0
  32. package/dist/styles/assets/pficon/pficon.woff2 +0 -0
  33. package/dist/styles/base-no-reset.css +10 -0
  34. package/dist/styles/base.css +10 -0
  35. package/dist/umd/react-core.min.css +947 -778
  36. package/dist/umd/react-core.min.js +1 -1
  37. package/package.json +6 -6
  38. package/src/components/Tabs/Tab.tsx +17 -16
  39. package/src/components/Tabs/TabAction.tsx +57 -0
  40. package/src/components/Tabs/Tabs.tsx +1 -1
  41. package/src/components/Tabs/__tests__/Tab.test.tsx +10 -0
  42. package/src/components/Tabs/__tests__/__snapshots__/Tab.test.tsx.snap +45 -0
  43. package/src/components/Tabs/examples/Tabs.md +64 -20
  44. package/src/components/Tabs/examples/TabsHelp.tsx +61 -0
  45. package/src/components/Tabs/examples/TabsHelpAndClose.tsx +84 -0
  46. package/src/components/Tabs/index.ts +1 -0
  47. package/src/demos/Tabs.md +0 -10
  48. package/src/demos/examples/DashboardWrapper.js +1 -0
  49. package/src/demos/examples/Tabs/ModalTabs.tsx +3 -3
  50. package/src/demos/examples/Tabs/NestedTabs.tsx +11 -3
  51. package/src/demos/examples/Tabs/NestedUnindentedTabs.tsx +25 -40
  52. package/src/demos/examples/Tabs/TabsAndTable.tsx +9 -52
  53. package/src/demos/examples/Tabs/GrayTabs.tsx +0 -152
  54. package/src/demos/examples/Tabs/TabsAndTablesAutoWidthTabs.tsx +0 -442
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.263.0",
3
+ "version": "4.264.0",
4
4
  "description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -44,16 +44,16 @@
44
44
  "generate": "node scripts/copyStyles.js"
45
45
  },
46
46
  "dependencies": {
47
- "@patternfly/react-icons": "^4.92.10",
48
- "@patternfly/react-styles": "^4.91.10",
49
- "@patternfly/react-tokens": "^4.93.10",
47
+ "@patternfly/react-icons": "^4.93.0",
48
+ "@patternfly/react-styles": "^4.92.0",
49
+ "@patternfly/react-tokens": "^4.94.0",
50
50
  "focus-trap": "6.9.2",
51
51
  "react-dropzone": "9.0.0",
52
52
  "tippy.js": "5.1.2",
53
53
  "tslib": "^2.0.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@patternfly/patternfly": "4.219.2",
56
+ "@patternfly/patternfly": "4.221.2",
57
57
  "@rollup/plugin-commonjs": "^21.0.0",
58
58
  "@rollup/plugin-node-resolve": "^13.0.0",
59
59
  "@rollup/plugin-replace": "^3.0.0",
@@ -70,5 +70,5 @@
70
70
  "react": "^16.8 || ^17 || ^18",
71
71
  "react-dom": "^16.8 || ^17 || ^18"
72
72
  },
73
- "gitHead": "a6cd98641632354c9e9765f4cff1303942123fb7"
73
+ "gitHead": "e1f95ce96852165dd4d102cd87c6b49288f01eff"
74
74
  }
@@ -5,10 +5,12 @@ import { TabButton } from './TabButton';
5
5
  import { TabsContext } from './TabsContext';
6
6
  import { css } from '@patternfly/react-styles';
7
7
  import { Tooltip } from '../Tooltip';
8
- import { Button } from '../Button';
9
8
  import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
9
+ import { TabAction } from './TabAction';
10
10
 
11
- export interface TabProps extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLButtonElement>, 'title'>, OUIAProps {
11
+ export interface TabProps
12
+ extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLButtonElement>, 'title' | 'action'>,
13
+ OUIAProps {
12
14
  /** content rendered inside the Tab content area. */
13
15
  children?: React.ReactNode;
14
16
  /** additional classes added to the Tab */
@@ -39,6 +41,8 @@ export interface TabProps extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLB
39
41
  closeButtonAriaLabel?: string;
40
42
  /** @beta Flag indicating the close button should be disabled */
41
43
  isCloseDisabled?: boolean;
44
+ /** @beta Actions rendered beside the tab content */
45
+ actions?: React.ReactNode;
42
46
  /** Value to set the data-ouia-component-id for the tab button.*/
43
47
  ouiaId?: number | string;
44
48
  }
@@ -59,6 +63,7 @@ const TabBase: React.FunctionComponent<TabProps> = ({
59
63
  tooltip,
60
64
  closeButtonAriaLabel,
61
65
  isCloseDisabled = false,
66
+ actions,
62
67
  ...props
63
68
  }: TabProps) => {
64
69
  const preventedEvents = inoperableEvents.reduce(
@@ -117,26 +122,22 @@ const TabBase: React.FunctionComponent<TabProps> = ({
117
122
  className={css(
118
123
  styles.tabsItem,
119
124
  eventKey === localActiveKey && styles.modifiers.current,
120
- handleTabClose && styles.modifiers.action,
121
- handleTabClose && (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
125
+ (handleTabClose || actions) && styles.modifiers.action,
126
+ (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
122
127
  childClassName
123
128
  )}
124
129
  role="presentation"
125
130
  >
126
131
  {tooltip ? <Tooltip {...tooltip.props}>{tabButton}</Tooltip> : tabButton}
132
+ {actions && actions}
127
133
  {handleTabClose !== undefined && (
128
- <span className={css(styles.tabsItemClose)}>
129
- <Button
130
- variant="plain"
131
- aria-label={closeButtonAriaLabel || 'Close tab'}
132
- onClick={(event: any) => handleTabClose(event, eventKey, tabContentRef)}
133
- isDisabled={isCloseDisabled}
134
- >
135
- <span className={css(styles.tabsItemCloseIcon)}>
136
- <TimesIcon />
137
- </span>
138
- </Button>
139
- </span>
134
+ <TabAction
135
+ aria-label={closeButtonAriaLabel || 'Close tab'}
136
+ onClick={(event: any) => handleTabClose(event, eventKey, tabContentRef)}
137
+ isDisabled={isCloseDisabled}
138
+ >
139
+ <TimesIcon />
140
+ </TabAction>
140
141
  )}
141
142
  </li>
142
143
  );
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+ import styles from '@patternfly/react-styles/css/components/Tabs/tabs';
4
+ import { Button } from '../Button';
5
+ import { getOUIAProps, OUIAProps } from '../../helpers';
6
+
7
+ export interface TabActionProps extends Omit<React.HTMLProps<HTMLButtonElement>, 'ref' | 'type'>, OUIAProps {
8
+ /** Content rendered in the tab action */
9
+ children?: React.ReactNode;
10
+ /** Additional classes added to the tab action span */
11
+ className?: string;
12
+ /** Click callback for tab action button */
13
+ onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
14
+ /** Flag indicating if the tab action is a help action */
15
+ isHelpAction?: boolean;
16
+ /** Flag indicating if the tab action is disabled */
17
+ isDisabled?: boolean;
18
+ /** Accessible label for the tab action */
19
+ 'aria-label'?: string;
20
+ /** @hide Callback for the section ref */
21
+ innerRef?: React.Ref<any>;
22
+ }
23
+
24
+ const TabActionBase: React.FunctionComponent<TabActionProps> = ({
25
+ children,
26
+ className,
27
+ onClick,
28
+ isHelpAction,
29
+ isDisabled,
30
+ 'aria-label': ariaLabel = 'Tab action',
31
+ innerRef,
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
+ ouiaId,
34
+ ouiaSafe,
35
+ ...props
36
+ }: TabActionProps) => (
37
+ <span className={css(styles.tabsItemAction, isHelpAction && styles.modifiers.help, className)}>
38
+ <Button
39
+ ref={innerRef}
40
+ type="button"
41
+ variant="plain"
42
+ aria-label={ariaLabel}
43
+ onClick={onClick}
44
+ isDisabled={isDisabled}
45
+ {...getOUIAProps(TabAction.displayName, ouiaId, ouiaSafe)}
46
+ {...props}
47
+ >
48
+ <span className={css(styles.tabsItemActionIcon)}>{children}</span>
49
+ </Button>
50
+ </span>
51
+ );
52
+
53
+ export const TabAction = React.forwardRef((props: TabActionProps, ref: React.Ref<HTMLElement>) => (
54
+ <TabActionBase {...props} innerRef={ref} />
55
+ ));
56
+
57
+ TabAction.displayName = 'TabAction';
@@ -42,7 +42,7 @@ export interface TabsProps extends Omit<React.HTMLProps<HTMLElement | HTMLDivEle
42
42
  defaultActiveKey?: number | string;
43
43
  /** Callback to handle tab selection */
44
44
  onSelect?: (event: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: number | string) => void;
45
- /** @beta Callback to handle tab closing */
45
+ /** @beta Callback to handle tab closing and adds a basic close button to all tabs. This is overridden by the tab actions property. */
46
46
  onClose?: (event: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: number | string) => void;
47
47
  /** @beta Callback for the add button. Passing this property inserts the add button */
48
48
  onAdd?: () => void;
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import { Tab } from '../Tab';
4
+ import { TabAction } from '../TabAction';
4
5
  import { TabTitleText } from '../TabTitleText';
5
6
 
6
7
  test('should not render anything', () => {
@@ -11,3 +12,12 @@ test('should not render anything', () => {
11
12
  );
12
13
  expect(asFragment()).toMatchSnapshot();
13
14
  });
15
+
16
+ test('renders tab action', () => {
17
+ const { asFragment } = render(
18
+ <Tab eventKey={1} title={<TabTitleText>"Tab item 2"</TabTitleText>} actions={<TabAction>test</TabAction>}>
19
+ Tab 2 section
20
+ </Tab>
21
+ );
22
+ expect(asFragment()).toMatchSnapshot();
23
+ });
@@ -1,5 +1,50 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`renders tab action 1`] = `
4
+ <DocumentFragment>
5
+ <li
6
+ class="pf-c-tabs__item pf-m-action"
7
+ role="presentation"
8
+ >
9
+ <button
10
+ aria-controls="pf-tab-section-1-"
11
+ aria-selected="false"
12
+ class="pf-c-tabs__link"
13
+ data-ouia-component-type="PF4/TabButton"
14
+ data-ouia-safe="true"
15
+ id="pf-tab-1-"
16
+ role="tab"
17
+ type="button"
18
+ >
19
+ <span
20
+ class="pf-c-tabs__item-text"
21
+ >
22
+ "Tab item 2"
23
+ </span>
24
+ </button>
25
+ <span
26
+ class="pf-c-tabs__item-action"
27
+ >
28
+ <button
29
+ aria-disabled="false"
30
+ aria-label="Tab action"
31
+ class="pf-c-button pf-m-plain"
32
+ data-ouia-component-id="OUIA-Generated-Button-plain-1"
33
+ data-ouia-component-type="PF4/Button"
34
+ data-ouia-safe="true"
35
+ type="button"
36
+ >
37
+ <span
38
+ class="pf-c-tabs__item-action-icon"
39
+ >
40
+ test
41
+ </span>
42
+ </button>
43
+ </span>
44
+ </li>
45
+ </DocumentFragment>
46
+ `;
47
+
3
48
  exports[`should not render anything 1`] = `
4
49
  <DocumentFragment>
5
50
  <li
@@ -12,6 +12,8 @@ import DatabaseIcon from '@patternfly/react-icons/dist/esm/icons/database-icon';
12
12
  import ServerIcon from '@patternfly/react-icons/dist/esm/icons/server-icon';
13
13
  import LaptopIcon from '@patternfly/react-icons/dist/esm/icons/laptop-icon';
14
14
  import ProjectDiagramIcon from '@patternfly/react-icons/dist/esm/icons/project-diagram-icon';
15
+ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
16
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
15
17
 
16
18
  Most tab variations are available as open (default) or box style tabs. Select the 'isBox' checkbox to preview an example with box styled tabs.
17
19
  The Tabs items background can be also toggled with 'Tab light variation' checkbox.
@@ -458,7 +460,15 @@ class VerticalTabs extends React.Component {
458
460
  aria-label="Tabs in the vertical example"
459
461
  role="region"
460
462
  >
461
- <Tab eventKey={0} title={<TabTitleText aria-label="vertical" role="region">Users</TabTitleText>} aria-label="Vertical example content users">
463
+ <Tab
464
+ eventKey={0}
465
+ title={
466
+ <TabTitleText aria-label="vertical" role="region">
467
+ Users
468
+ </TabTitleText>
469
+ }
470
+ aria-label="Vertical example content users"
471
+ >
462
472
  Users
463
473
  </Tab>
464
474
  <Tab eventKey={1} title={<TabTitleText>Containers</TabTitleText>}>
@@ -591,7 +601,11 @@ class VerticalExpandableUncontrolledTabs extends React.Component {
591
601
  aria-label="Tabs in the vertical expandable uncontrolled example"
592
602
  role="region"
593
603
  >
594
- <Tab eventKey={0} title={<TabTitleText>Users</TabTitleText>} aria-label="Vertical expandable uncontrolled content users">
604
+ <Tab
605
+ eventKey={0}
606
+ title={<TabTitleText>Users</TabTitleText>}
607
+ aria-label="Vertical expandable uncontrolled content users"
608
+ >
595
609
  Users
596
610
  </Tab>
597
611
  <Tab eventKey={1} title={<TabTitleText>Containers</TabTitleText>}>
@@ -1206,7 +1220,12 @@ class TabsNav extends React.Component {
1206
1220
  component={TabsComponent.nav}
1207
1221
  aria-label="Tabs in the nav element example"
1208
1222
  >
1209
- <Tab eventKey={0} title={<TabTitleText>Users</TabTitleText>} href="#users" aria-label="Nav element content users">
1223
+ <Tab
1224
+ eventKey={0}
1225
+ title={<TabTitleText>Users</TabTitleText>}
1226
+ href="#users"
1227
+ aria-label="Nav element content users"
1228
+ >
1210
1229
  Users
1211
1230
  </Tab>
1212
1231
  <Tab eventKey={1} title={<TabTitleText>Containers</TabTitleText>} href="#containers">
@@ -1267,7 +1286,12 @@ class SecondaryTabsNav extends React.Component {
1267
1286
  component={TabsComponent.nav}
1268
1287
  aria-label="Tabs in the sub tabs with nav element example"
1269
1288
  >
1270
- <Tab eventKey={0} title={<TabTitleText>Users</TabTitleText>} href="#" aria-label="Subtabs with nav content users">
1289
+ <Tab
1290
+ eventKey={0}
1291
+ title={<TabTitleText>Users</TabTitleText>}
1292
+ href="#"
1293
+ aria-label="Subtabs with nav content users"
1294
+ >
1271
1295
  <Tabs
1272
1296
  activeKey={this.state.activeTabKey2}
1273
1297
  isSecondary
@@ -1423,7 +1447,12 @@ const TabContentWithBody = () => {
1423
1447
 
1424
1448
  return (
1425
1449
  <React.Fragment>
1426
- <Tabs activeKey={activeTabKey} onSelect={handleTabClick} aria-label="Tabs in the body and padding example" role="region">
1450
+ <Tabs
1451
+ activeKey={activeTabKey}
1452
+ onSelect={handleTabClick}
1453
+ aria-label="Tabs in the body and padding example"
1454
+ role="region"
1455
+ >
1427
1456
  <Tab
1428
1457
  eventKey={0}
1429
1458
  title={<TabTitleText>Tab item 1</TabTitleText>}
@@ -1447,20 +1476,10 @@ const TabContentWithBody = () => {
1447
1476
  <TabContent eventKey={0} id="tab1SectionBodyPadding" ref={contentRef1}>
1448
1477
  <TabContentBody hasPadding> Tab 1 section with body and padding </TabContentBody>
1449
1478
  </TabContent>
1450
- <TabContent
1451
- eventKey={1}
1452
- id="tab2SectionBodyPadding"
1453
- ref={contentRef2}
1454
- hidden
1455
- >
1479
+ <TabContent eventKey={1} id="tab2SectionBodyPadding" ref={contentRef2} hidden>
1456
1480
  <TabContentBody hasPadding> Tab 2 section with body and padding </TabContentBody>
1457
1481
  </TabContent>
1458
- <TabContent
1459
- eventKey={2}
1460
- id="tab3SectionBodyPadding"
1461
- ref={contentRef3}
1462
- hidden
1463
- >
1482
+ <TabContent eventKey={2} id="tab3SectionBodyPadding" ref={contentRef3} hidden>
1464
1483
  <TabContentBody hasPadding> Tab 3 section with body and padding </TabContentBody>
1465
1484
  </TabContent>
1466
1485
  </div>
@@ -1597,11 +1616,26 @@ class ToggledSeparateContent extends React.Component {
1597
1616
  aria-label="Tabs in the toggled separate content example"
1598
1617
  role="region"
1599
1618
  >
1600
- <Tab eventKey={0} title="Tab item 1" tabContentId="tab1SectionSeparateContent" tabContentRef={this.contentRef1} />
1619
+ <Tab
1620
+ eventKey={0}
1621
+ title="Tab item 1"
1622
+ tabContentId="tab1SectionSeparateContent"
1623
+ tabContentRef={this.contentRef1}
1624
+ />
1601
1625
  {!isTab2Hidden && (
1602
- <Tab eventKey={1} title="Tab item 2" tabContentId="tab2SectionSeparateContent" tabContentRef={this.contentRef2} />
1626
+ <Tab
1627
+ eventKey={1}
1628
+ title="Tab item 2"
1629
+ tabContentId="tab2SectionSeparateContent"
1630
+ tabContentRef={this.contentRef2}
1631
+ />
1603
1632
  )}
1604
- <Tab eventKey={2} title="Tab item 3" tabContentId="tab3SectionSeparateContent" tabContentRef={this.contentRef3} />
1633
+ <Tab
1634
+ eventKey={2}
1635
+ title="Tab item 3"
1636
+ tabContentId="tab3SectionSeparateContent"
1637
+ tabContentRef={this.contentRef3}
1638
+ />
1605
1639
  </Tabs>
1606
1640
  <div>
1607
1641
  <TabContent
@@ -1645,3 +1679,13 @@ To enable closeable tabs, pass the `onClose` property to `Tabs`, and to enable t
1645
1679
 
1646
1680
  ```ts file="./TabsDynamic.tsx" isBeta
1647
1681
  ```
1682
+
1683
+ ### Help action
1684
+
1685
+ ```ts file="./TabsHelp.tsx" isBeta
1686
+ ```
1687
+
1688
+ ### Help and close actions
1689
+
1690
+ ```ts file="./TabsHelpAndClose.tsx" isBeta
1691
+ ```
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { Tabs, Tab, TabTitleText, TabAction, Popover } from '@patternfly/react-core';
3
+ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
4
+
5
+ export const TabsHelp: React.FunctionComponent = () => {
6
+ const [activeTabKey, setActiveTabKey] = React.useState<number>(0);
7
+
8
+ const tabs = ['Users', 'Containers', 'Database', 'Disabled', 'ARIA disabled', 'Help disabled'];
9
+
10
+ const helpPopover = (header: string, popoverRef: React.RefObject<any>) => (
11
+ <Popover
12
+ headerContent={<div>{header}</div>}
13
+ bodyContent={
14
+ <div>
15
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
16
+ </div>
17
+ }
18
+ footerContent="Popover footer"
19
+ reference={popoverRef}
20
+ />
21
+ );
22
+
23
+ return (
24
+ <Tabs
25
+ activeKey={activeTabKey}
26
+ onSelect={(event: any, tabIndex: string | number) => setActiveTabKey(tabIndex as number)}
27
+ aria-label="Tabs in the help action example"
28
+ role="region"
29
+ >
30
+ {tabs.map((tab, index) => {
31
+ const ref = React.createRef<HTMLElement>();
32
+
33
+ return (
34
+ <Tab
35
+ key={index}
36
+ eventKey={index}
37
+ aria-label={`Help action content - ${tab}`}
38
+ title={<TabTitleText>{tab}</TabTitleText>}
39
+ {...(tab === 'Disabled' && { isDisabled: true })}
40
+ {...(tab === 'ARIA disabled' && { isAriaDisabled: true })}
41
+ actions={
42
+ <>
43
+ <TabAction
44
+ isHelpAction
45
+ aria-label={`Help action for ${tab}`}
46
+ ref={ref}
47
+ {...(tab === 'Help disabled' && { isDisabled: true })}
48
+ >
49
+ <HelpIcon />
50
+ </TabAction>
51
+ {helpPopover(`Help popover for ${tab}`, ref)}
52
+ </>
53
+ }
54
+ >
55
+ {tab}
56
+ </Tab>
57
+ );
58
+ })}
59
+ </Tabs>
60
+ );
61
+ };
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { Tabs, Tab, TabTitleText, Popover, TabAction } from '@patternfly/react-core';
3
+ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
4
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
5
+
6
+ export const TabsHelpAndClose: React.FunctionComponent = () => {
7
+ const [activeTabKey, setActiveTabKey] = React.useState<number>(0);
8
+ const [tabs, setTabs] = React.useState<string[]>(['Terminal 1', 'Terminal 2', 'Terminal 3']);
9
+ const tabComponentRef = React.useRef<any>();
10
+ const firstMount = React.useRef(true);
11
+
12
+ const onClose = (event: any, tabIndex: string | number) => {
13
+ const tabIndexNum = tabIndex as number;
14
+ let nextTabIndex = activeTabKey;
15
+ if (tabIndexNum < activeTabKey) {
16
+ // if a preceding tab is closing, keep focus on the new index of the current tab
17
+ nextTabIndex = activeTabKey - 1 > 0 ? activeTabKey - 1 : 0;
18
+ } else if (activeTabKey === tabs.length - 1) {
19
+ // if the closing tab is the last tab, focus the preceding tab
20
+ nextTabIndex = tabs.length - 2 > 0 ? tabs.length - 2 : 0;
21
+ }
22
+ setActiveTabKey(nextTabIndex);
23
+ setTabs(tabs.filter((tab, index) => index !== tabIndex));
24
+ };
25
+
26
+ const helpPopover = (header: string, popoverRef: React.RefObject<any>) => (
27
+ <Popover
28
+ headerContent={<div>{header}</div>}
29
+ bodyContent={
30
+ <div>
31
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
32
+ </div>
33
+ }
34
+ footerContent="Popover footer"
35
+ reference={popoverRef}
36
+ />
37
+ );
38
+
39
+ React.useEffect(() => {
40
+ if (firstMount.current) {
41
+ firstMount.current = false;
42
+ return;
43
+ } else {
44
+ const first = tabComponentRef.current.tabList.current.childNodes[activeTabKey];
45
+ first && first.firstChild.focus();
46
+ }
47
+ }, [tabs]);
48
+
49
+ return (
50
+ <Tabs
51
+ activeKey={activeTabKey}
52
+ onSelect={(event: any, tabIndex: string | number) => setActiveTabKey(tabIndex as number)}
53
+ aria-label="Tabs in the help enabled and closeable example"
54
+ role="region"
55
+ ref={tabComponentRef}
56
+ >
57
+ {tabs.map((tab, index) => {
58
+ const ref = React.createRef<HTMLElement>();
59
+
60
+ return (
61
+ <Tab
62
+ key={index}
63
+ eventKey={index}
64
+ aria-label={`Help action and closable content - ${tab}`}
65
+ title={<TabTitleText>{tab}</TabTitleText>}
66
+ actions={
67
+ <>
68
+ <TabAction isHelpAction aria-label={`Help for ${tab}`} ref={ref}>
69
+ <HelpIcon />
70
+ </TabAction>
71
+ <TabAction aria-label={`Close ${tab}`} onClick={e => onClose(e, index)} isDisabled={tabs.length === 1}>
72
+ <TimesIcon />
73
+ </TabAction>
74
+ {helpPopover(`Help popover for ${tab}`, ref)}
75
+ </>
76
+ }
77
+ >
78
+ {tab}
79
+ </Tab>
80
+ );
81
+ })}
82
+ </Tabs>
83
+ );
84
+ };
@@ -1,4 +1,5 @@
1
1
  export * from './Tab';
2
+ export * from './TabAction';
2
3
  export * from './Tabs';
3
4
  export * from './TabContent';
4
5
  export * from './TabContentBody';
package/src/demos/Tabs.md CHANGED
@@ -453,17 +453,7 @@ TabsOpenWithSecondaryTabsDemo = () => {
453
453
  ```js isFullscreen file="./examples/Tabs/TabsAndTable.tsx"
454
454
  ```
455
455
 
456
- ### Tables and tabs, auto width tabs
457
-
458
- ```js isFullscreen file="./examples/Tabs/TabsAndTablesAutoWidthTabs.tsx"
459
- ```
460
-
461
456
  ### Modal tabs
462
457
 
463
458
  ```js isFullscreen file="./examples/Tabs/ModalTabs.tsx"
464
459
  ```
465
-
466
- ### Gray tabs
467
-
468
- ```js isFullscreen file="./examples/Tabs/GrayTabs.tsx"
469
- ```
@@ -33,6 +33,7 @@ export const PageTemplateTitle = (
33
33
  </TextContent>
34
34
  </PageSection>
35
35
  );
36
+
36
37
  export default class DashboardWrapper extends React.Component {
37
38
  constructor(props) {
38
39
  super(props);
@@ -47,8 +47,8 @@ const products: Product[] = [
47
47
  ];
48
48
 
49
49
  export const ModalTabs: React.FunctionComponent = () => {
50
- const [isModalOpen, setIsModalOpen] = React.useState(false);
51
- const [selectedProduct, setSelectedProduct] = React.useState<Product>();
50
+ const [isModalOpen, setIsModalOpen] = React.useState(true);
51
+ const [selectedProduct, setSelectedProduct] = React.useState<Product>(products[0]);
52
52
  const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0);
53
53
 
54
54
  const onCardClick = React.useCallback(
@@ -114,7 +114,7 @@ export const ModalTabs: React.FunctionComponent = () => {
114
114
  <Modal variant={ModalVariant.small} title={selectedProduct.name} isOpen={isModalOpen} onClose={closeModal}>
115
115
  <Grid hasGutter>
116
116
  <GridItem>
117
- <Tabs activeKey={activeTabKey} onSelect={onTabSelect} isSecondary>
117
+ <Tabs activeKey={activeTabKey} onSelect={onTabSelect} isSecondary hasSecondaryBorderBottom>
118
118
  <Tab eventKey={0} tabContentId="details-tab" title={<TabTitleText>Details</TabTitleText>} />
119
119
  <Tab eventKey={1} tabContentId="doc-tab" title={<TabTitleText>Documentation</TabTitleText>} />
120
120
  </Tabs>
@@ -12,6 +12,7 @@ import {
12
12
  TabContentBody,
13
13
  TabTitleText,
14
14
  Title,
15
+ TitleSizes,
15
16
  Flex,
16
17
  FlexItem
17
18
  } from '@patternfly/react-core';
@@ -32,7 +33,9 @@ export const NestedTabs: React.FunctionComponent = () => {
32
33
  <GridItem xl={8} md={6}>
33
34
  <Card>
34
35
  <CardHeader>
35
- <Title headingLevel="h2">Status</Title>
36
+ <Title headingLevel="h2" size={TitleSizes.lg}>
37
+ Status
38
+ </Title>
36
39
  </CardHeader>
37
40
  <CardBody>
38
41
  <Flex direction={{ default: 'column' }}>
@@ -40,6 +43,7 @@ export const NestedTabs: React.FunctionComponent = () => {
40
43
  <Tabs
41
44
  activeKey={activeNestedTabKey}
42
45
  isSecondary
46
+ hasSecondaryBorderBottom
43
47
  onSelect={(_event, tabIndex) => handleNestedTabClick(Number(tabIndex))}
44
48
  id="nested-tabs-example-nested-tabs-list"
45
49
  >
@@ -108,14 +112,18 @@ export const NestedTabs: React.FunctionComponent = () => {
108
112
  <FlexItem flex={{ default: 'flex_1' }}>
109
113
  <Card isFullHeight>
110
114
  <CardHeader>
111
- <Title headingLevel="h2">Title of Card</Title>
115
+ <Title headingLevel="h2" size={TitleSizes.lg}>
116
+ Title of card
117
+ </Title>
112
118
  </CardHeader>
113
119
  </Card>
114
120
  </FlexItem>
115
121
  <FlexItem flex={{ default: 'flex_1' }}>
116
122
  <Card isFullHeight>
117
123
  <CardHeader>
118
- <Title headingLevel="h2">Title of Card</Title>
124
+ <Title headingLevel="h2" size={TitleSizes.lg}>
125
+ Title of card
126
+ </Title>
119
127
  </CardHeader>
120
128
  </Card>
121
129
  </FlexItem>