@patternfly/react-core 4.212.0 → 4.213.2
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.
- package/CHANGELOG.md +30 -0
- package/dist/esm/components/ApplicationLauncher/ApplicationLauncher.d.ts +2 -9
- package/dist/esm/components/ApplicationLauncher/ApplicationLauncher.d.ts.map +1 -1
- package/dist/esm/components/ApplicationLauncher/ApplicationLauncher.js.map +1 -1
- package/dist/esm/components/ContextSelector/ContextSelector.d.ts +2 -9
- package/dist/esm/components/ContextSelector/ContextSelector.d.ts.map +1 -1
- package/dist/esm/components/ContextSelector/ContextSelector.js.map +1 -1
- package/dist/esm/components/Dropdown/Dropdown.d.ts +2 -9
- package/dist/esm/components/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/esm/components/Dropdown/Dropdown.js +2 -2
- package/dist/esm/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/esm/components/OptionsMenu/OptionsMenu.d.ts +2 -9
- package/dist/esm/components/OptionsMenu/OptionsMenu.d.ts.map +1 -1
- package/dist/esm/components/OptionsMenu/OptionsMenu.js.map +1 -1
- package/dist/esm/components/Select/Select.d.ts +2 -9
- package/dist/esm/components/Select/Select.d.ts.map +1 -1
- package/dist/esm/components/Select/Select.js.map +1 -1
- package/dist/esm/components/Tabs/Tab.d.ts +5 -1
- package/dist/esm/components/Tabs/Tab.d.ts.map +1 -1
- package/dist/esm/components/Tabs/Tab.js +10 -3
- package/dist/esm/components/Tabs/Tab.js.map +1 -1
- package/dist/esm/components/Tabs/Tabs.d.ts +6 -0
- package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/esm/components/Tabs/Tabs.js +14 -4
- package/dist/esm/components/Tabs/Tabs.js.map +1 -1
- package/dist/esm/components/Tabs/TabsContext.d.ts +1 -0
- package/dist/esm/components/Tabs/TabsContext.d.ts.map +1 -1
- package/dist/esm/components/Tabs/TabsContext.js +2 -1
- package/dist/esm/components/Tabs/TabsContext.js.map +1 -1
- package/dist/esm/helpers/Popper/Popper.d.ts +10 -0
- package/dist/esm/helpers/Popper/Popper.d.ts.map +1 -1
- package/dist/esm/helpers/Popper/Popper.js.map +1 -1
- package/dist/js/components/ApplicationLauncher/ApplicationLauncher.d.ts +2 -9
- package/dist/js/components/ApplicationLauncher/ApplicationLauncher.d.ts.map +1 -1
- package/dist/js/components/ApplicationLauncher/ApplicationLauncher.js.map +1 -1
- package/dist/js/components/ContextSelector/ContextSelector.d.ts +2 -9
- package/dist/js/components/ContextSelector/ContextSelector.d.ts.map +1 -1
- package/dist/js/components/ContextSelector/ContextSelector.js.map +1 -1
- package/dist/js/components/Dropdown/Dropdown.d.ts +2 -9
- package/dist/js/components/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/js/components/Dropdown/Dropdown.js +2 -2
- package/dist/js/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/js/components/OptionsMenu/OptionsMenu.d.ts +2 -9
- package/dist/js/components/OptionsMenu/OptionsMenu.d.ts.map +1 -1
- package/dist/js/components/OptionsMenu/OptionsMenu.js.map +1 -1
- package/dist/js/components/Select/Select.d.ts +2 -9
- package/dist/js/components/Select/Select.d.ts.map +1 -1
- package/dist/js/components/Select/Select.js.map +1 -1
- package/dist/js/components/Tabs/Tab.d.ts +5 -1
- package/dist/js/components/Tabs/Tab.d.ts.map +1 -1
- package/dist/js/components/Tabs/Tab.js +10 -3
- package/dist/js/components/Tabs/Tab.js.map +1 -1
- package/dist/js/components/Tabs/Tabs.d.ts +6 -0
- package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/js/components/Tabs/Tabs.js +14 -4
- package/dist/js/components/Tabs/Tabs.js.map +1 -1
- package/dist/js/components/Tabs/TabsContext.d.ts +1 -0
- package/dist/js/components/Tabs/TabsContext.d.ts.map +1 -1
- package/dist/js/components/Tabs/TabsContext.js +2 -1
- package/dist/js/components/Tabs/TabsContext.js.map +1 -1
- package/dist/js/helpers/Popper/Popper.d.ts +10 -0
- package/dist/js/helpers/Popper/Popper.d.ts.map +1 -1
- package/dist/js/helpers/Popper/Popper.js.map +1 -1
- package/dist/umd/react-core.css +511 -511
- package/dist/umd/react-core.js +38 -23
- package/dist/umd/react-core.min.css +107 -107
- package/dist/umd/react-core.min.js +2 -2
- package/package.json +6 -6
- package/src/components/ApplicationLauncher/ApplicationLauncher.tsx +2 -9
- package/src/components/ContextSelector/ContextSelector.tsx +2 -9
- package/src/components/Dropdown/Dropdown.tsx +3 -11
- package/src/components/OptionsMenu/OptionsMenu.tsx +2 -9
- package/src/components/Select/Select.tsx +3 -10
- package/src/components/Tabs/Tab.tsx +32 -2
- package/src/components/Tabs/Tabs.tsx +28 -2
- package/src/components/Tabs/TabsContext.ts +7 -1
- package/src/components/Tabs/__tests__/Tabs.test.tsx +22 -0
- package/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +13 -13
- package/src/components/Tabs/examples/Tabs.md +7 -0
- package/src/components/Tabs/examples/TabsDynamic.tsx +64 -0
- package/src/helpers/Popper/Popper.tsx +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.213.2",
|
|
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",
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"generate": "node scripts/copyStyles.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@patternfly/react-icons": "^4.
|
|
38
|
-
"@patternfly/react-styles": "^4.
|
|
39
|
-
"@patternfly/react-tokens": "^4.
|
|
37
|
+
"@patternfly/react-icons": "^4.64.2",
|
|
38
|
+
"@patternfly/react-styles": "^4.63.2",
|
|
39
|
+
"@patternfly/react-tokens": "^4.65.2",
|
|
40
40
|
"focus-trap": "6.2.2",
|
|
41
41
|
"react-dropzone": "9.0.0",
|
|
42
42
|
"tippy.js": "5.1.2",
|
|
43
43
|
"tslib": "^2.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@patternfly/patternfly": "4.194.
|
|
46
|
+
"@patternfly/patternfly": "4.194.2",
|
|
47
47
|
"@rollup/plugin-commonjs": "^21.0.0",
|
|
48
48
|
"@rollup/plugin-node-resolve": "^13.0.0",
|
|
49
49
|
"@rollup/plugin-replace": "^3.0.0",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"react": "^16.8.0 || ^17.0.0",
|
|
61
61
|
"react-dom": "^16.8.0 || ^17.0.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "42579815df2b03fd9efa0082f41dd349fa04bfe4"
|
|
64
64
|
}
|
|
@@ -9,9 +9,10 @@ import { ApplicationLauncherGroup } from './ApplicationLauncherGroup';
|
|
|
9
9
|
import { ApplicationLauncherSeparator } from './ApplicationLauncherSeparator';
|
|
10
10
|
import { ApplicationLauncherItem } from './ApplicationLauncherItem';
|
|
11
11
|
import { ApplicationLauncherContext } from './ApplicationLauncherContext';
|
|
12
|
+
import { ToggleMenuBaseProps } from '../../helpers/Popper/Popper';
|
|
12
13
|
import { createRenderableFavorites, extendItemsWithFavorite } from '../../helpers/favorites';
|
|
13
14
|
|
|
14
|
-
export interface ApplicationLauncherProps extends React.HTMLProps<HTMLDivElement> {
|
|
15
|
+
export interface ApplicationLauncherProps extends ToggleMenuBaseProps, React.HTMLProps<HTMLDivElement> {
|
|
15
16
|
/** Additional element css classes */
|
|
16
17
|
className?: string;
|
|
17
18
|
/** Display menu above or below dropdown toggle */
|
|
@@ -34,14 +35,6 @@ export interface ApplicationLauncherProps extends React.HTMLProps<HTMLDivElement
|
|
|
34
35
|
isGrouped?: boolean;
|
|
35
36
|
/** Toggle Icon, optional to override the icon used for the toggle */
|
|
36
37
|
toggleIcon?: React.ReactNode;
|
|
37
|
-
/** The container to append the menu to. Defaults to 'inline'.
|
|
38
|
-
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
39
|
-
* Some examples:
|
|
40
|
-
* menuAppendTo="parent"
|
|
41
|
-
* menuAppendTo={() => document.body}
|
|
42
|
-
* menuAppendTo={document.getElementById('target')}
|
|
43
|
-
*/
|
|
44
|
-
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
|
|
45
38
|
/** ID list of favorited ApplicationLauncherItems */
|
|
46
39
|
favorites?: string[];
|
|
47
40
|
/** Enables favorites. Callback called when an ApplicationLauncherItem's favorite button is clicked */
|
|
@@ -10,6 +10,7 @@ import { TextInput } from '../TextInput';
|
|
|
10
10
|
import { InputGroup } from '../InputGroup';
|
|
11
11
|
import { KEY_CODES } from '../../helpers/constants';
|
|
12
12
|
import { FocusTrap } from '../../helpers';
|
|
13
|
+
import { ToggleMenuBaseProps } from '../../helpers/Popper/Popper';
|
|
13
14
|
import { Popper } from '../../helpers/Popper/Popper';
|
|
14
15
|
import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../helpers';
|
|
15
16
|
|
|
@@ -17,7 +18,7 @@ import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../helpers';
|
|
|
17
18
|
let currentId = 0;
|
|
18
19
|
const newId = currentId++;
|
|
19
20
|
|
|
20
|
-
export interface ContextSelectorProps extends OUIAProps {
|
|
21
|
+
export interface ContextSelectorProps extends ToggleMenuBaseProps, OUIAProps {
|
|
21
22
|
/** content rendered inside the Context Selector */
|
|
22
23
|
children?: React.ReactNode;
|
|
23
24
|
/** Classes applied to root element of Context Selector */
|
|
@@ -28,14 +29,6 @@ export interface ContextSelectorProps extends OUIAProps {
|
|
|
28
29
|
onToggle?: (event: any, value: boolean) => void;
|
|
29
30
|
/** Function callback called when user selects item */
|
|
30
31
|
onSelect?: (event: any, value: React.ReactNode) => void;
|
|
31
|
-
/** The container to append the menu to. Defaults to 'inline'.
|
|
32
|
-
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
33
|
-
* Some examples:
|
|
34
|
-
* menuAppendTo="parent"
|
|
35
|
-
* menuAppendTo={() => document.body}
|
|
36
|
-
* menuAppendTo={document.getElementById('target')}
|
|
37
|
-
*/
|
|
38
|
-
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
|
|
39
32
|
/** Flag indicating that the context selector should expand to full height */
|
|
40
33
|
isFullHeight?: boolean;
|
|
41
34
|
/** Labels the Context Selector for Screen Readers */
|
|
@@ -2,9 +2,10 @@ import * as React from 'react';
|
|
|
2
2
|
import styles from '@patternfly/react-styles/css/components/Dropdown/dropdown';
|
|
3
3
|
import { DropdownPosition, DropdownDirection, DropdownContext } from './dropdownConstants';
|
|
4
4
|
import { DropdownWithContext } from './DropdownWithContext';
|
|
5
|
+
import { ToggleMenuBaseProps } from '../../helpers/Popper/Popper';
|
|
5
6
|
import { OUIAProps, useOUIAId } from '../../helpers';
|
|
6
7
|
|
|
7
|
-
export interface DropdownProps extends React.HTMLProps<HTMLDivElement>, OUIAProps {
|
|
8
|
+
export interface DropdownProps extends ToggleMenuBaseProps, React.HTMLProps<HTMLDivElement>, OUIAProps {
|
|
8
9
|
/** Anything which can be rendered in a dropdown */
|
|
9
10
|
children?: React.ReactNode;
|
|
10
11
|
/** Classes applied to root element of dropdown */
|
|
@@ -31,14 +32,6 @@ export interface DropdownProps extends React.HTMLProps<HTMLDivElement>, OUIAProp
|
|
|
31
32
|
};
|
|
32
33
|
/** Display menu above or below dropdown toggle */
|
|
33
34
|
direction?: DropdownDirection | 'up' | 'down';
|
|
34
|
-
/** The container to append the menu to. Defaults to 'inline'.
|
|
35
|
-
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
36
|
-
* Some examples:
|
|
37
|
-
* menuAppendTo="parent"
|
|
38
|
-
* menuAppendTo={() => document.body}
|
|
39
|
-
* menuAppendTo={document.getElementById('target')}
|
|
40
|
-
*/
|
|
41
|
-
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
|
|
42
35
|
/** Flag to indicate if dropdown has groups */
|
|
43
36
|
isGrouped?: boolean;
|
|
44
37
|
/** Toggle for the dropdown, examples: <DropdownToggle> or <DropdownToggleCheckbox> */
|
|
@@ -61,7 +54,6 @@ export const Dropdown: React.FunctionComponent<DropdownProps> = ({
|
|
|
61
54
|
ouiaSafe,
|
|
62
55
|
alignments,
|
|
63
56
|
contextProps,
|
|
64
|
-
menuAppendTo = 'inline',
|
|
65
57
|
...props
|
|
66
58
|
}: DropdownProps) => (
|
|
67
59
|
<DropdownContext.Provider
|
|
@@ -87,7 +79,7 @@ export const Dropdown: React.FunctionComponent<DropdownProps> = ({
|
|
|
87
79
|
...contextProps
|
|
88
80
|
}}
|
|
89
81
|
>
|
|
90
|
-
<DropdownWithContext
|
|
82
|
+
<DropdownWithContext {...props} />
|
|
91
83
|
</DropdownContext.Provider>
|
|
92
84
|
);
|
|
93
85
|
Dropdown.displayName = 'Dropdown';
|
|
@@ -3,6 +3,7 @@ import styles from '@patternfly/react-styles/css/components/OptionsMenu/options-
|
|
|
3
3
|
import { DropdownContext } from '../Dropdown';
|
|
4
4
|
import { DropdownWithContext } from '../Dropdown/DropdownWithContext';
|
|
5
5
|
import { OUIAProps, useOUIAId } from '../../helpers';
|
|
6
|
+
import { ToggleMenuBaseProps } from '../../helpers/Popper/Popper';
|
|
6
7
|
|
|
7
8
|
export enum OptionsMenuPosition {
|
|
8
9
|
right = 'right',
|
|
@@ -14,7 +15,7 @@ export enum OptionsMenuDirection {
|
|
|
14
15
|
down = 'down'
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export interface OptionsMenuProps extends React.HTMLProps<HTMLDivElement>, OUIAProps {
|
|
18
|
+
export interface OptionsMenuProps extends ToggleMenuBaseProps, React.HTMLProps<HTMLDivElement>, OUIAProps {
|
|
18
19
|
/** Classes applied to root element of the options menu */
|
|
19
20
|
className?: string;
|
|
20
21
|
/** Id of the root element of the options menu */
|
|
@@ -35,14 +36,6 @@ export interface OptionsMenuProps extends React.HTMLProps<HTMLDivElement>, OUIAP
|
|
|
35
36
|
position?: 'right' | 'left';
|
|
36
37
|
/** Menu will open up or open down from the options menu toggle */
|
|
37
38
|
direction?: 'up' | 'down';
|
|
38
|
-
/** The container to append the menu to. Defaults to 'inline'.
|
|
39
|
-
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
40
|
-
* Some examples:
|
|
41
|
-
* menuAppendTo="parent"
|
|
42
|
-
* menuAppendTo={() => document.body}
|
|
43
|
-
* menuAppendTo={document.getElementById('target')}
|
|
44
|
-
*/
|
|
45
|
-
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
|
|
46
39
|
}
|
|
47
40
|
|
|
48
41
|
export const OptionsMenu: React.FunctionComponent<OptionsMenuProps> = ({
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from '../../helpers';
|
|
34
34
|
import { KeyTypes } from '../../helpers/constants';
|
|
35
35
|
import { Divider } from '../Divider';
|
|
36
|
-
import { Popper } from '../../helpers/Popper/Popper';
|
|
36
|
+
import { ToggleMenuBaseProps, Popper } from '../../helpers/Popper/Popper';
|
|
37
37
|
import { createRenderableFavorites, extendItemsWithFavorite } from '../../helpers/favorites';
|
|
38
38
|
import { ValidatedOptions } from '../../helpers/constants';
|
|
39
39
|
import { findTabbableElements } from '../../helpers/util';
|
|
@@ -48,7 +48,8 @@ export interface SelectViewMoreObject {
|
|
|
48
48
|
onClick: (event: React.MouseEvent | React.ChangeEvent) => void;
|
|
49
49
|
}
|
|
50
50
|
export interface SelectProps
|
|
51
|
-
extends
|
|
51
|
+
extends ToggleMenuBaseProps,
|
|
52
|
+
Omit<React.HTMLProps<HTMLDivElement>, 'onSelect' | 'ref' | 'checked' | 'selected'>,
|
|
52
53
|
OUIAProps {
|
|
53
54
|
/** Content rendered inside the Select. Must be React.ReactElement<SelectGroupProps>[] */
|
|
54
55
|
children?: React.ReactElement[];
|
|
@@ -164,14 +165,6 @@ export interface SelectProps
|
|
|
164
165
|
shouldResetOnSelect?: boolean;
|
|
165
166
|
/** Content rendered in the footer of the select menu */
|
|
166
167
|
footer?: React.ReactNode;
|
|
167
|
-
/** The container to append the menu to. Defaults to 'inline'.
|
|
168
|
-
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
169
|
-
* Some examples:
|
|
170
|
-
* menuAppendTo="parent"
|
|
171
|
-
* menuAppendTo={() => document.body}
|
|
172
|
-
* menuAppendTo={document.getElementById('target')}
|
|
173
|
-
*/
|
|
174
|
-
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
|
|
175
168
|
}
|
|
176
169
|
|
|
177
170
|
export interface SelectState {
|
|
@@ -5,6 +5,8 @@ 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
|
+
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
|
|
8
10
|
|
|
9
11
|
export interface TabProps extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLButtonElement>, 'title'>, OUIAProps {
|
|
10
12
|
/** content rendered inside the Tab content area. */
|
|
@@ -33,6 +35,10 @@ export interface TabProps extends Omit<React.HTMLProps<HTMLAnchorElement | HTMLB
|
|
|
33
35
|
innerRef?: React.Ref<any>;
|
|
34
36
|
/** Optional Tooltip rendered to a Tab. Should be <Tooltip> with appropriate props for proper rendering. */
|
|
35
37
|
tooltip?: React.ReactElement<any>;
|
|
38
|
+
/** @beta Aria-label for the close button added by passing the onClose property to Tabs. */
|
|
39
|
+
closeButtonAriaLabel?: string;
|
|
40
|
+
/** @beta Flag indicating the close button should be disabled */
|
|
41
|
+
isCloseDisabled?: boolean;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
const TabBase: React.FunctionComponent<TabProps> = ({
|
|
@@ -49,6 +55,8 @@ const TabBase: React.FunctionComponent<TabProps> = ({
|
|
|
49
55
|
href,
|
|
50
56
|
innerRef,
|
|
51
57
|
tooltip,
|
|
58
|
+
closeButtonAriaLabel,
|
|
59
|
+
isCloseDisabled = false,
|
|
52
60
|
...props
|
|
53
61
|
}: TabProps) => {
|
|
54
62
|
const preventedEvents = inoperableEvents.reduce(
|
|
@@ -60,7 +68,9 @@ const TabBase: React.FunctionComponent<TabProps> = ({
|
|
|
60
68
|
}),
|
|
61
69
|
{}
|
|
62
70
|
);
|
|
63
|
-
const { mountOnEnter, localActiveKey, unmountOnExit, uniqueId, handleTabClick } = React.useContext(
|
|
71
|
+
const { mountOnEnter, localActiveKey, unmountOnExit, uniqueId, handleTabClick, handleTabClose } = React.useContext(
|
|
72
|
+
TabsContext
|
|
73
|
+
);
|
|
64
74
|
let ariaControls = tabContentId ? `${tabContentId}` : `pf-tab-section-${eventKey}-${childId || uniqueId}`;
|
|
65
75
|
if ((mountOnEnter || unmountOnExit) && eventKey !== localActiveKey) {
|
|
66
76
|
ariaControls = undefined;
|
|
@@ -102,10 +112,30 @@ const TabBase: React.FunctionComponent<TabProps> = ({
|
|
|
102
112
|
|
|
103
113
|
return (
|
|
104
114
|
<li
|
|
105
|
-
className={css(
|
|
115
|
+
className={css(
|
|
116
|
+
styles.tabsItem,
|
|
117
|
+
eventKey === localActiveKey && styles.modifiers.current,
|
|
118
|
+
handleTabClose && styles.modifiers.action,
|
|
119
|
+
handleTabClose && (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
|
|
120
|
+
childClassName
|
|
121
|
+
)}
|
|
106
122
|
role="presentation"
|
|
107
123
|
>
|
|
108
124
|
{tooltip ? <Tooltip {...tooltip.props}>{tabButton}</Tooltip> : tabButton}
|
|
125
|
+
{handleTabClose !== undefined && (
|
|
126
|
+
<span className={css(styles.tabsItemClose)}>
|
|
127
|
+
<Button
|
|
128
|
+
variant="plain"
|
|
129
|
+
aria-label={closeButtonAriaLabel || 'Close tab'}
|
|
130
|
+
onClick={(event: any) => handleTabClose(event, eventKey, tabContentRef)}
|
|
131
|
+
isDisabled={isCloseDisabled}
|
|
132
|
+
>
|
|
133
|
+
<span className={css(styles.tabsItemCloseIcon)}>
|
|
134
|
+
<TimesIcon />
|
|
135
|
+
</span>
|
|
136
|
+
</Button>
|
|
137
|
+
</span>
|
|
138
|
+
)}
|
|
109
139
|
</li>
|
|
110
140
|
);
|
|
111
141
|
};
|
|
@@ -5,6 +5,7 @@ import { css } from '@patternfly/react-styles';
|
|
|
5
5
|
import { PickOptional } from '../../helpers/typeUtils';
|
|
6
6
|
import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
|
|
7
7
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
8
|
+
import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon';
|
|
8
9
|
import { getUniqueId, isElementInView, formatBreakpointMods } from '../../helpers/util';
|
|
9
10
|
import { TabContent } from './TabContent';
|
|
10
11
|
import { TabProps } from './Tab';
|
|
@@ -31,6 +32,12 @@ export interface TabsProps extends Omit<React.HTMLProps<HTMLElement | HTMLDivEle
|
|
|
31
32
|
defaultActiveKey?: number | string;
|
|
32
33
|
/** Callback to handle tab selection */
|
|
33
34
|
onSelect?: (event: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: number | string) => void;
|
|
35
|
+
/** @beta Callback to handle tab closing */
|
|
36
|
+
onClose?: (event: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: number | string) => void;
|
|
37
|
+
/** @beta Callback for the add button. Passing this property inserts the add button */
|
|
38
|
+
onAdd?: () => void;
|
|
39
|
+
/** @beta Aria-label for the add button */
|
|
40
|
+
addButtonAriaLabel?: string;
|
|
34
41
|
/** Uniquely identifies the tabs */
|
|
35
42
|
id?: string;
|
|
36
43
|
/** Enables the filled tab list layout */
|
|
@@ -273,13 +280,21 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
273
280
|
}
|
|
274
281
|
|
|
275
282
|
componentDidUpdate(prevProps: TabsProps) {
|
|
276
|
-
const { activeKey, mountOnEnter } = this.props;
|
|
283
|
+
const { activeKey, mountOnEnter, children } = this.props;
|
|
277
284
|
const { shownKeys } = this.state;
|
|
278
285
|
if (prevProps.activeKey !== activeKey && mountOnEnter && shownKeys.indexOf(activeKey) < 0) {
|
|
279
286
|
this.setState({
|
|
280
287
|
shownKeys: shownKeys.concat(activeKey)
|
|
281
288
|
});
|
|
282
289
|
}
|
|
290
|
+
|
|
291
|
+
if (
|
|
292
|
+
prevProps.children &&
|
|
293
|
+
children &&
|
|
294
|
+
React.Children.toArray(prevProps.children).length !== React.Children.toArray(children).length
|
|
295
|
+
) {
|
|
296
|
+
this.handleScrollButtons();
|
|
297
|
+
}
|
|
283
298
|
}
|
|
284
299
|
|
|
285
300
|
render() {
|
|
@@ -311,7 +326,10 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
311
326
|
defaultIsExpanded,
|
|
312
327
|
toggleText,
|
|
313
328
|
toggleAriaLabel,
|
|
329
|
+
addButtonAriaLabel,
|
|
314
330
|
onToggle,
|
|
331
|
+
onClose,
|
|
332
|
+
onAdd,
|
|
315
333
|
...props
|
|
316
334
|
} = this.props;
|
|
317
335
|
const {
|
|
@@ -348,7 +366,8 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
348
366
|
unmountOnExit,
|
|
349
367
|
localActiveKey,
|
|
350
368
|
uniqueId,
|
|
351
|
-
handleTabClick: (...args) => this.handleTabClick(...args)
|
|
369
|
+
handleTabClick: (...args) => this.handleTabClick(...args),
|
|
370
|
+
handleTabClose: onClose
|
|
352
371
|
}}
|
|
353
372
|
>
|
|
354
373
|
<Component
|
|
@@ -421,6 +440,13 @@ export class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
421
440
|
>
|
|
422
441
|
<AngleRightIcon />
|
|
423
442
|
</button>
|
|
443
|
+
{onAdd !== undefined && (
|
|
444
|
+
<span className={css(styles.tabsAdd)}>
|
|
445
|
+
<Button variant="plain" aria-label={addButtonAriaLabel || 'Add tab'} onClick={onAdd}>
|
|
446
|
+
<PlusIcon />
|
|
447
|
+
</Button>
|
|
448
|
+
</span>
|
|
449
|
+
)}
|
|
424
450
|
</Component>
|
|
425
451
|
{filteredChildren
|
|
426
452
|
.filter(
|
|
@@ -11,6 +11,11 @@ export interface TabsContextProps {
|
|
|
11
11
|
eventKey: number | string,
|
|
12
12
|
tabContentRef: React.RefObject<any>
|
|
13
13
|
) => void;
|
|
14
|
+
handleTabClose: (
|
|
15
|
+
event: React.MouseEvent<HTMLElement, MouseEvent>,
|
|
16
|
+
eventKey: number | string,
|
|
17
|
+
tabContentRef?: React.RefObject<any>
|
|
18
|
+
) => void;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export const TabsContext = React.createContext<TabsContextProps>({
|
|
@@ -19,7 +24,8 @@ export const TabsContext = React.createContext<TabsContextProps>({
|
|
|
19
24
|
unmountOnExit: false,
|
|
20
25
|
localActiveKey: '',
|
|
21
26
|
uniqueId: '',
|
|
22
|
-
handleTabClick: () => null
|
|
27
|
+
handleTabClick: () => null,
|
|
28
|
+
handleTabClose: undefined
|
|
23
29
|
});
|
|
24
30
|
|
|
25
31
|
export const TabsContextProvider = TabsContext.Provider;
|
|
@@ -38,6 +38,28 @@ test('should render simple tabs', () => {
|
|
|
38
38
|
expect(asFragment()).toMatchSnapshot();
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
test('should render closeable tabs', () => {
|
|
42
|
+
const view = render(
|
|
43
|
+
<Tabs onClose={jest.fn()}>
|
|
44
|
+
<Tab eventKey={0} title={<TabTitleText>"Tab item 1"</TabTitleText>} closeButtonAriaLabel="close-label">
|
|
45
|
+
Tab 1 section
|
|
46
|
+
</Tab>
|
|
47
|
+
</Tabs>
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByLabelText('close-label')).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should render add button', () => {
|
|
53
|
+
const view = render(
|
|
54
|
+
<Tabs onAdd={jest.fn()} addButtonAriaLabel="add-label">
|
|
55
|
+
<Tab eventKey={0} title={<TabTitleText>"Tab item 1"</TabTitleText>} closeButtonAriaLabel="close-label">
|
|
56
|
+
Tab 1 section
|
|
57
|
+
</Tab>
|
|
58
|
+
</Tabs>
|
|
59
|
+
);
|
|
60
|
+
expect(screen.getByLabelText('add-label')).toBeTruthy();
|
|
61
|
+
});
|
|
62
|
+
|
|
41
63
|
test('should render uncontrolled tabs', () => {
|
|
42
64
|
const { asFragment } = render(
|
|
43
65
|
<Tabs defaultActiveKey={0}>
|
|
@@ -5,7 +5,7 @@ exports[`should render accessible tabs 1`] = `
|
|
|
5
5
|
<nav
|
|
6
6
|
aria-label="accessible Tabs example"
|
|
7
7
|
class="pf-c-tabs"
|
|
8
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
8
|
+
data-ouia-component-id="OUIA-Generated-Tabs-9"
|
|
9
9
|
data-ouia-component-type="PF4/Tabs"
|
|
10
10
|
data-ouia-safe="true"
|
|
11
11
|
id="accessibleTabs"
|
|
@@ -161,7 +161,7 @@ exports[`should render box tabs 1`] = `
|
|
|
161
161
|
<DocumentFragment>
|
|
162
162
|
<div
|
|
163
163
|
class="pf-c-tabs pf-m-box"
|
|
164
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
164
|
+
data-ouia-component-id="OUIA-Generated-Tabs-8"
|
|
165
165
|
data-ouia-component-type="PF4/Tabs"
|
|
166
166
|
data-ouia-safe="true"
|
|
167
167
|
id="boxTabs"
|
|
@@ -355,7 +355,7 @@ exports[`should render box tabs of light variant 1`] = `
|
|
|
355
355
|
<DocumentFragment>
|
|
356
356
|
<div
|
|
357
357
|
class="pf-c-tabs pf-m-box pf-m-color-scheme--light-300"
|
|
358
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
358
|
+
data-ouia-component-id="OUIA-Generated-Tabs-15"
|
|
359
359
|
data-ouia-component-type="PF4/Tabs"
|
|
360
360
|
data-ouia-safe="true"
|
|
361
361
|
id="boxLightVariantTabs"
|
|
@@ -508,7 +508,7 @@ exports[`should render expandable vertical tabs 1`] = `
|
|
|
508
508
|
<DocumentFragment>
|
|
509
509
|
<div
|
|
510
510
|
class="pf-c-tabs pf-m-vertical pf-m-expandable"
|
|
511
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
511
|
+
data-ouia-component-id="OUIA-Generated-Tabs-6"
|
|
512
512
|
data-ouia-component-type="PF4/Tabs"
|
|
513
513
|
data-ouia-safe="true"
|
|
514
514
|
id="verticalTabs"
|
|
@@ -523,7 +523,7 @@ exports[`should render expandable vertical tabs 1`] = `
|
|
|
523
523
|
aria-disabled="false"
|
|
524
524
|
aria-labelledby="pf-random-id-0-text pf-random-id-0-button"
|
|
525
525
|
class="pf-c-button pf-m-plain"
|
|
526
|
-
data-ouia-component-id="OUIA-Generated-Button-plain-
|
|
526
|
+
data-ouia-component-id="OUIA-Generated-Button-plain-3"
|
|
527
527
|
data-ouia-component-type="PF4/Button"
|
|
528
528
|
data-ouia-safe="true"
|
|
529
529
|
id="pf-random-id-0-button"
|
|
@@ -745,7 +745,7 @@ exports[`should render filled tabs 1`] = `
|
|
|
745
745
|
<DocumentFragment>
|
|
746
746
|
<div
|
|
747
747
|
class="pf-c-tabs pf-m-fill"
|
|
748
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
748
|
+
data-ouia-component-id="OUIA-Generated-Tabs-10"
|
|
749
749
|
data-ouia-component-type="PF4/Tabs"
|
|
750
750
|
data-ouia-safe="true"
|
|
751
751
|
id="filledTabs"
|
|
@@ -898,7 +898,7 @@ exports[`should render secondary tabs 1`] = `
|
|
|
898
898
|
<DocumentFragment>
|
|
899
899
|
<div
|
|
900
900
|
class="pf-c-tabs"
|
|
901
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
901
|
+
data-ouia-component-id="OUIA-Generated-Tabs-11"
|
|
902
902
|
data-ouia-component-type="PF4/Tabs"
|
|
903
903
|
data-ouia-safe="true"
|
|
904
904
|
id="primarieTabs"
|
|
@@ -1020,7 +1020,7 @@ exports[`should render secondary tabs 1`] = `
|
|
|
1020
1020
|
>
|
|
1021
1021
|
<div
|
|
1022
1022
|
class="pf-c-tabs pf-m-secondary"
|
|
1023
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
1023
|
+
data-ouia-component-id="OUIA-Generated-Tabs-12"
|
|
1024
1024
|
data-ouia-component-type="PF4/Tabs"
|
|
1025
1025
|
data-ouia-safe="true"
|
|
1026
1026
|
id="secondaryTabs"
|
|
@@ -1393,7 +1393,7 @@ exports[`should render tabs with eventKey Strings 1`] = `
|
|
|
1393
1393
|
<DocumentFragment>
|
|
1394
1394
|
<div
|
|
1395
1395
|
class="pf-c-tabs"
|
|
1396
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
1396
|
+
data-ouia-component-id="OUIA-Generated-Tabs-13"
|
|
1397
1397
|
data-ouia-component-type="PF4/Tabs"
|
|
1398
1398
|
data-ouia-safe="true"
|
|
1399
1399
|
id="eventKeyTabs"
|
|
@@ -1547,7 +1547,7 @@ exports[`should render tabs with no bottom border 1`] = `
|
|
|
1547
1547
|
<DocumentFragment>
|
|
1548
1548
|
<div
|
|
1549
1549
|
class="pf-c-tabs pf-m-no-border-bottom"
|
|
1550
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
1550
|
+
data-ouia-component-id="OUIA-Generated-Tabs-16"
|
|
1551
1551
|
data-ouia-component-type="PF4/Tabs"
|
|
1552
1552
|
data-ouia-safe="true"
|
|
1553
1553
|
id="noBottomBorderTabs"
|
|
@@ -1700,7 +1700,7 @@ exports[`should render tabs with separate content 1`] = `
|
|
|
1700
1700
|
<DocumentFragment>
|
|
1701
1701
|
<div
|
|
1702
1702
|
class="pf-c-tabs"
|
|
1703
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
1703
|
+
data-ouia-component-id="OUIA-Generated-Tabs-14"
|
|
1704
1704
|
data-ouia-component-type="PF4/Tabs"
|
|
1705
1705
|
data-ouia-safe="true"
|
|
1706
1706
|
id="separateTabs"
|
|
@@ -1863,7 +1863,7 @@ exports[`should render uncontrolled tabs 1`] = `
|
|
|
1863
1863
|
<DocumentFragment>
|
|
1864
1864
|
<div
|
|
1865
1865
|
class="pf-c-tabs"
|
|
1866
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
1866
|
+
data-ouia-component-id="OUIA-Generated-Tabs-4"
|
|
1867
1867
|
data-ouia-component-type="PF4/Tabs"
|
|
1868
1868
|
data-ouia-safe="true"
|
|
1869
1869
|
>
|
|
@@ -2056,7 +2056,7 @@ exports[`should render vertical tabs 1`] = `
|
|
|
2056
2056
|
<DocumentFragment>
|
|
2057
2057
|
<div
|
|
2058
2058
|
class="pf-c-tabs pf-m-vertical"
|
|
2059
|
-
data-ouia-component-id="OUIA-Generated-Tabs-
|
|
2059
|
+
data-ouia-component-id="OUIA-Generated-Tabs-5"
|
|
2060
2060
|
data-ouia-component-type="PF4/Tabs"
|
|
2061
2061
|
data-ouia-safe="true"
|
|
2062
2062
|
id="verticalTabs"
|
|
@@ -1617,3 +1617,10 @@ class ToggledSeparateContent extends React.Component {
|
|
|
1617
1617
|
}
|
|
1618
1618
|
}
|
|
1619
1619
|
```
|
|
1620
|
+
|
|
1621
|
+
### Dynamic
|
|
1622
|
+
|
|
1623
|
+
To enable closeable tabs, pass the `onClose` property to `Tabs`, and to enable the add button, pass the `onAdd` property to `Tabs`. Aria labels may be controlled manually by passing `closeButtonAriaLabel` to `Tab` and `addButtonAriaLabel` to `Tabs`.
|
|
1624
|
+
|
|
1625
|
+
```ts file="./TabsDynamic.tsx" isBeta
|
|
1626
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const TabsDynamic: React.FunctionComponent = () => {
|
|
5
|
+
const [activeTabKey, setActiveTabKey] = React.useState<number>(0);
|
|
6
|
+
const [tabs, setTabs] = React.useState<string[]>(['Terminal 1', 'Terminal 2', 'Terminal 3']);
|
|
7
|
+
const [newTabNumber, setNewTabNumber] = React.useState<number>(4);
|
|
8
|
+
const tabComponentRef = React.useRef<any>();
|
|
9
|
+
const firstMount = React.useRef(true);
|
|
10
|
+
|
|
11
|
+
const onClose = (event: any, tabIndex: string | number) => {
|
|
12
|
+
const tabIndexNum = tabIndex as number;
|
|
13
|
+
let nextTabIndex = activeTabKey;
|
|
14
|
+
if (tabIndexNum < activeTabKey) {
|
|
15
|
+
// if a preceding tab is closing, keep focus on the new index of the current tab
|
|
16
|
+
nextTabIndex = activeTabKey - 1 > 0 ? activeTabKey - 1 : 0;
|
|
17
|
+
} else if (activeTabKey === tabs.length - 1) {
|
|
18
|
+
// if the closing tab is the last tab, focus the preceding tab
|
|
19
|
+
nextTabIndex = tabs.length - 2 > 0 ? tabs.length - 2 : 0;
|
|
20
|
+
}
|
|
21
|
+
setActiveTabKey(nextTabIndex);
|
|
22
|
+
setTabs(tabs.filter((tab, index) => index !== tabIndex));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const onAdd = () => {
|
|
26
|
+
setTabs([...tabs, `Terminal ${newTabNumber}`]);
|
|
27
|
+
setActiveTabKey(tabs.length);
|
|
28
|
+
setNewTabNumber(newTabNumber + 1);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
React.useEffect(() => {
|
|
32
|
+
if (firstMount.current) {
|
|
33
|
+
firstMount.current = false;
|
|
34
|
+
return;
|
|
35
|
+
} else {
|
|
36
|
+
const first = tabComponentRef.current.tabList.current.childNodes[activeTabKey];
|
|
37
|
+
first && first.firstChild.focus();
|
|
38
|
+
}
|
|
39
|
+
}, [tabs]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Tabs
|
|
43
|
+
activeKey={activeTabKey}
|
|
44
|
+
onSelect={(event: any, tabIndex: string | number) => setActiveTabKey(tabIndex as number)}
|
|
45
|
+
onClose={onClose}
|
|
46
|
+
onAdd={onAdd}
|
|
47
|
+
aria-label="Tabs in the addable/closeable example"
|
|
48
|
+
addButtonAriaLabel="Add new tab"
|
|
49
|
+
ref={tabComponentRef}
|
|
50
|
+
>
|
|
51
|
+
{tabs.map((tab, index) => (
|
|
52
|
+
<Tab
|
|
53
|
+
key={index}
|
|
54
|
+
eventKey={index}
|
|
55
|
+
title={<TabTitleText>{tab}</TabTitleText>}
|
|
56
|
+
closeButtonAriaLabel={`Close ${tab}`}
|
|
57
|
+
isCloseDisabled={tabs.length === 1}
|
|
58
|
+
>
|
|
59
|
+
{tab}
|
|
60
|
+
</Tab>
|
|
61
|
+
))}
|
|
62
|
+
</Tabs>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -14,6 +14,17 @@ const getOppositePlacement = (placement: Placement): any =>
|
|
|
14
14
|
(matched: string) => hash[matched as 'left' | 'right' | 'bottom' | 'top'] as BasePlacement
|
|
15
15
|
);
|
|
16
16
|
|
|
17
|
+
export interface ToggleMenuBaseProps {
|
|
18
|
+
/** The container to append the menu to. Defaults to 'inline'
|
|
19
|
+
* If your menu is being cut off you can append it to an element higher up the DOM tree.
|
|
20
|
+
* Some examples:
|
|
21
|
+
* menuAppendTo="parent"
|
|
22
|
+
* menuAppendTo={() => document.body}
|
|
23
|
+
* menuAppendTo={document.getElementById('target')}
|
|
24
|
+
*/
|
|
25
|
+
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'parent' | 'inline';
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
export const getOpacityTransition = (animationDuration: number) =>
|
|
18
29
|
`opacity ${animationDuration}ms cubic-bezier(.54, 1.5, .38, 1.11)`;
|
|
19
30
|
|