@patternfly/react-core 4.232.13 → 4.234.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.
- package/CHANGELOG.md +33 -0
- package/dist/esm/components/ExpandableSection/ExpandableSection.d.ts +17 -3
- package/dist/esm/components/ExpandableSection/ExpandableSection.d.ts.map +1 -1
- package/dist/esm/components/ExpandableSection/ExpandableSection.js +39 -8
- package/dist/esm/components/ExpandableSection/ExpandableSection.js.map +1 -1
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.d.ts +2 -0
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.d.ts.map +1 -1
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.js +4 -4
- package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.js.map +1 -1
- package/dist/esm/components/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/esm/components/InputGroup/InputGroup.js +5 -3
- package/dist/esm/components/InputGroup/InputGroup.js.map +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.js +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
- package/dist/esm/components/NumberInput/NumberInput.d.ts +5 -0
- package/dist/esm/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/esm/components/NumberInput/NumberInput.js +5 -4
- package/dist/esm/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/esm/components/TimePicker/TimePicker.d.ts +1 -0
- package/dist/esm/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/esm/components/TimePicker/TimePicker.js +14 -2
- package/dist/esm/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/esm/components/Timestamp/Timestamp.d.ts +72 -0
- package/dist/esm/components/Timestamp/Timestamp.d.ts.map +1 -0
- package/dist/esm/components/Timestamp/Timestamp.js +33 -0
- package/dist/esm/components/Timestamp/Timestamp.js.map +1 -0
- package/dist/esm/components/Timestamp/index.d.ts +2 -0
- package/dist/esm/components/Timestamp/index.d.ts.map +1 -0
- package/dist/esm/components/Timestamp/index.js +2 -0
- package/dist/esm/components/Timestamp/index.js.map +1 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +1 -0
- package/dist/esm/components/index.js.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSection.d.ts +17 -3
- package/dist/js/components/ExpandableSection/ExpandableSection.d.ts.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSection.js +40 -9
- package/dist/js/components/ExpandableSection/ExpandableSection.js.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.d.ts +2 -0
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.d.ts.map +1 -1
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.js +4 -4
- package/dist/js/components/ExpandableSection/ExpandableSectionToggle.js.map +1 -1
- package/dist/js/components/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/js/components/InputGroup/InputGroup.js +5 -3
- package/dist/js/components/InputGroup/InputGroup.js.map +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.js +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
- package/dist/js/components/NumberInput/NumberInput.d.ts +5 -0
- package/dist/js/components/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/js/components/NumberInput/NumberInput.js +4 -3
- package/dist/js/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/js/components/TimePicker/TimePicker.d.ts +1 -0
- package/dist/js/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/js/components/TimePicker/TimePicker.js +14 -2
- package/dist/js/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/js/components/Timestamp/Timestamp.d.ts +72 -0
- package/dist/js/components/Timestamp/Timestamp.d.ts.map +1 -0
- package/dist/js/components/Timestamp/Timestamp.js +37 -0
- package/dist/js/components/Timestamp/Timestamp.js.map +1 -0
- package/dist/js/components/Timestamp/index.d.ts +2 -0
- package/dist/js/components/Timestamp/index.d.ts.map +1 -0
- package/dist/js/components/Timestamp/index.js +5 -0
- package/dist/js/components/Timestamp/index.js.map +1 -0
- package/dist/js/components/index.d.ts +1 -0
- package/dist/js/components/index.d.ts.map +1 -1
- package/dist/js/components/index.js +1 -0
- package/dist/js/components/index.js.map +1 -1
- package/dist/styles/base-no-reset.css +2 -2
- package/dist/styles/base.css +2 -2
- package/dist/umd/react-core.css +58 -12
- package/dist/umd/react-core.js +861 -770
- package/dist/umd/react-core.min.css +312 -266
- package/dist/umd/react-core.min.js +5 -5
- package/package.json +6 -6
- package/src/components/Button/examples/Button.md +55 -24
- package/src/components/ContextSelector/examples/ContextSelectorBasic.tsx +4 -4
- package/src/components/ContextSelector/examples/ContextSelectorPlainText.tsx +3 -3
- package/src/components/ContextSelector/examples/ContextSelectorWithFooter.tsx +4 -4
- package/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +8 -2
- package/src/components/DualListSelector/examples/DualListSelectorComposableDragDrop.tsx +3 -9
- package/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +3 -3
- package/src/components/DualListSelector/examples/DualListSelectorTreeExample.tsx +8 -5
- package/src/components/ExpandableSection/ExpandableSection.tsx +73 -19
- package/src/components/ExpandableSection/ExpandableSectionToggle.tsx +15 -9
- package/src/components/ExpandableSection/__tests__/ExpandableSection.test.tsx +27 -1
- package/src/components/ExpandableSection/examples/ExpandableSection.md +7 -0
- package/src/components/ExpandableSection/examples/ExpandableSectionTruncateExpansion.tsx +27 -0
- package/src/components/InputGroup/InputGroup.tsx +5 -3
- package/src/components/InputGroup/__tests__/InputGroup.test.tsx +30 -0
- package/src/components/MenuToggle/MenuToggle.tsx +2 -2
- package/src/components/NumberInput/NumberInput.tsx +14 -8
- package/src/components/NumberInput/__tests__/NumberInput.test.tsx +16 -0
- package/src/components/NumberInput/__tests__/__snapshots__/NumberInput.test.tsx.snap +292 -18
- package/src/components/NumberInput/examples/NumberInput.md +5 -0
- package/src/components/NumberInput/examples/WithStatus.tsx +59 -0
- package/src/components/TimePicker/TimePicker.tsx +19 -11
- package/src/components/Timestamp/Timestamp.tsx +134 -0
- package/src/components/Timestamp/__tests__/Timestamp.test.tsx +177 -0
- package/src/components/Timestamp/__tests__/__snapshots__/Timestamp.test.tsx.snap +17 -0
- package/src/components/Timestamp/examples/Timestamp.md +57 -0
- package/src/components/Timestamp/examples/TimestampBasicFormats.tsx +26 -0
- package/src/components/Timestamp/examples/TimestampCustomContent.tsx +20 -0
- package/src/components/Timestamp/examples/TimestampCustomFormat.tsx +6 -0
- package/src/components/Timestamp/examples/TimestampCustomTooltip.tsx +19 -0
- package/src/components/Timestamp/examples/TimestampDefault.tsx +4 -0
- package/src/components/Timestamp/examples/TimestampDefaultTooltip.tsx +18 -0
- package/src/components/Timestamp/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/demos/Button.md +5 -1
- package/src/demos/ComposableMenu/examples/ComposableActionsMenu.tsx +12 -8
- package/src/demos/ComposableMenu/examples/ComposableApplicationLauncher.tsx +12 -11
- package/src/demos/ComposableMenu/examples/ComposableContextSelector.tsx +10 -12
- package/src/demos/ComposableMenu/examples/ComposableDrilldownMenu.tsx +7 -7
- package/src/demos/ComposableMenu/examples/ComposableFlyout.tsx +8 -8
- package/src/demos/ComposableMenu/examples/ComposableSimpleCheckboxSelect.tsx +11 -8
- package/src/demos/ComposableMenu/examples/ComposableSimpleDropdown.tsx +7 -7
- package/src/demos/ComposableMenu/examples/ComposableSimpleSelect.tsx +17 -8
- package/src/demos/ComposableMenu/examples/ComposableTreeViewMenu.tsx +11 -10
- package/src/demos/ComposableMenu/examples/ComposableTypeaheadSelect.tsx +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.234.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",
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
"generate": "node scripts/copyStyles.js"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@patternfly/react-icons": "^4.
|
|
54
|
-
"@patternfly/react-styles": "^4.
|
|
55
|
-
"@patternfly/react-tokens": "^4.
|
|
53
|
+
"@patternfly/react-icons": "^4.85.0",
|
|
54
|
+
"@patternfly/react-styles": "^4.84.0",
|
|
55
|
+
"@patternfly/react-tokens": "^4.86.0",
|
|
56
56
|
"focus-trap": "6.9.2",
|
|
57
57
|
"react-dropzone": "9.0.0",
|
|
58
58
|
"tippy.js": "5.1.2",
|
|
59
59
|
"tslib": "^2.0.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@patternfly/patternfly": "4.
|
|
62
|
+
"@patternfly/patternfly": "4.210.1",
|
|
63
63
|
"@rollup/plugin-commonjs": "^22.0.2",
|
|
64
64
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
|
65
65
|
"@rollup/plugin-replace": "^4.0.0",
|
|
@@ -76,5 +76,5 @@
|
|
|
76
76
|
"react": "^16.8.0 || ^17.0.0",
|
|
77
77
|
"react-dom": "^16.8.0 || ^17.0.0"
|
|
78
78
|
},
|
|
79
|
-
"gitHead": "
|
|
79
|
+
"gitHead": "fa6869951efc44e52696e065322c40478aa3fad3"
|
|
80
80
|
}
|
|
@@ -16,68 +16,99 @@ import { Link } from '@reach/router';
|
|
|
16
16
|
|
|
17
17
|
## Examples
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Variant examples
|
|
20
|
+
|
|
21
|
+
PatternFly supports several button styling variants to be used in different scenarios as needed. The following button variants can be assigned using the `variant` property.
|
|
20
22
|
|
|
21
23
|
```ts file="./ButtonVariations.tsx"
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
| Variant | Description|
|
|
27
|
+
| --- | ---|
|
|
28
|
+
| Primary | Primary buttons are the most visually prominent variant. Use for the most important call to action.|
|
|
29
|
+
| Secondary | Secondary buttons are less visually prominant than primary buttons. Use for general actions on a page that require less emphasis than primary buttons. |
|
|
30
|
+
| Tertiary | Tertiary buttons are the least visually prominent variant. Use to retain a classic button format with less emphasis than primary or secondary buttons. |
|
|
31
|
+
| Danger | Danger buttons may be used for actions that are potentially destructive or difficult/impossible to undo. Danger is an independent variant, but all button variants may use the the `isDanger` property to apply similar styling. |
|
|
32
|
+
| Warning | Warning buttons may be used for actions that change an important setting or behavior, but not in a destructive or irreversible way. |
|
|
33
|
+
| Link | Links are labeled, but have no background or border. Use for actions that require less emphasis, actions that navigate users to another page within the same window, and/or actions that navigate to external pages in a new window. Links may be placed inline with text using the `isInline` property.|
|
|
34
|
+
| Plain | Plain buttons have no styling and are intended to be labeled with icons. |
|
|
35
|
+
| Control | Control buttons can be labeled with text or icons. Primarily intended to be paired with other controls in an [input group](https://www.patternfly.org/v4/components/input-group). |
|
|
36
|
+
|
|
37
|
+
### Disabled buttons
|
|
38
|
+
|
|
39
|
+
To indicate that an action is currently unavailable, all button variations can be disabled using the `isDisabled` property.
|
|
25
40
|
|
|
26
41
|
```ts file="./ButtonDisabled.tsx"
|
|
27
42
|
```
|
|
28
43
|
|
|
29
|
-
###
|
|
44
|
+
### Small buttons
|
|
30
45
|
|
|
31
|
-
|
|
46
|
+
To fit into tight spaces, primary, secondary, tertiary, danger, and warning button variations can be made smaller using the `isSmall` property.
|
|
47
|
+
|
|
48
|
+
```ts file="./ButtonSmall.tsx"
|
|
32
49
|
```
|
|
33
50
|
|
|
34
|
-
###
|
|
51
|
+
### Call to action (CTA) buttons
|
|
35
52
|
|
|
36
|
-
|
|
53
|
+
CTA buttons and links direct users to complete an action. Primary, secondary, tertiary, and link button variants can be styled as CTAs using the `isLarge` property.
|
|
54
|
+
|
|
55
|
+
```ts file="./ButtonCallToAction.tsx"
|
|
37
56
|
```
|
|
38
57
|
|
|
39
|
-
###
|
|
58
|
+
### Block level buttons
|
|
40
59
|
|
|
41
|
-
|
|
60
|
+
Block level buttons span the full width of the parent element and can be enabled using the `isBlock` property.
|
|
61
|
+
|
|
62
|
+
```ts file="./ButtonBlock.tsx"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Progress indicators
|
|
66
|
+
|
|
67
|
+
Progress indicators can be added to buttons to identify that an action is in progress after a click.
|
|
68
|
+
|
|
69
|
+
```ts file="./ButtonProgress.tsx"
|
|
42
70
|
```
|
|
43
71
|
|
|
44
72
|
### Links as buttons
|
|
45
73
|
|
|
74
|
+
Buttons that link to another resource may take the form of primary, secondary, tertiary, or link variants. Use `component="a"` and an `href` property to designate the button's target link.
|
|
75
|
+
|
|
46
76
|
```ts file="./ButtonLinks.tsx"
|
|
47
77
|
```
|
|
48
78
|
|
|
49
79
|
### Inline link as span
|
|
50
80
|
|
|
81
|
+
Inline links should use `component="span"` and the `isInline` property to wrap inline with surrounding text.
|
|
82
|
+
|
|
51
83
|
```ts file="./ButtonInlineSpanLink.tsx"
|
|
52
84
|
```
|
|
53
85
|
|
|
54
|
-
###
|
|
55
|
-
|
|
56
|
-
```ts file="./ButtonBlock.tsx"
|
|
57
|
-
```
|
|
86
|
+
### Router link
|
|
58
87
|
|
|
59
|
-
|
|
88
|
+
Router links can be used for in-app linking in React environments to prevent page reloading.
|
|
60
89
|
|
|
61
|
-
```ts file="./
|
|
90
|
+
```ts file="./ButtonRouterLink.tsx"
|
|
62
91
|
```
|
|
63
92
|
|
|
64
|
-
###
|
|
93
|
+
### Aria-disabled examples
|
|
65
94
|
|
|
66
|
-
|
|
67
|
-
```
|
|
95
|
+
[Accessible Rich Internet Applications (ARIA)](https://www.w3.org/WAI/standards-guidelines/aria/) is a set of roles and attributes specified by the World Wide Web Consortium. ARIA defines ways to make web content and web applications more accessible to people with disabilities.
|
|
68
96
|
|
|
69
|
-
|
|
97
|
+
Buttons that are aria-disabled are similar to normal disabled buttons, except they can receive focus. Every button variant can be aria-disabled using the `isAriaDisabled` property.
|
|
70
98
|
|
|
71
|
-
```ts file="./
|
|
99
|
+
```ts file="./ButtonAriaDisabled.tsx"
|
|
72
100
|
```
|
|
73
101
|
|
|
74
|
-
###
|
|
102
|
+
### Aria-disabled with tooltip
|
|
75
103
|
|
|
76
|
-
|
|
104
|
+
Unlike normal disabled buttons, aria-disabled buttons can support tooltips.
|
|
105
|
+
|
|
106
|
+
```ts file="./ButtonAriaDisabledTooltip.tsx"
|
|
77
107
|
```
|
|
78
108
|
|
|
79
|
-
###
|
|
109
|
+
### Aria-disabled link as button with tooltip
|
|
80
110
|
|
|
81
|
-
|
|
82
|
-
```
|
|
111
|
+
Aria-disabled buttons can operate as links, which also support tooltips.
|
|
83
112
|
|
|
113
|
+
```ts file="./ButtonAriaDisabledLinkTooltip.tsx"
|
|
114
|
+
```
|
|
@@ -48,7 +48,7 @@ export const ContextSelectorBasic: React.FunctionComponent = () => {
|
|
|
48
48
|
setSearchValue(value);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const onSearchButtonClick = (
|
|
51
|
+
const onSearchButtonClick = () => {
|
|
52
52
|
const filtered =
|
|
53
53
|
searchValue === ''
|
|
54
54
|
? items
|
|
@@ -71,11 +71,11 @@ export const ContextSelectorBasic: React.FunctionComponent = () => {
|
|
|
71
71
|
screenReaderLabel="Selected Project:"
|
|
72
72
|
>
|
|
73
73
|
{filteredItems.map((item, index) => {
|
|
74
|
-
const [text = null, href =
|
|
75
|
-
typeof item === 'string' ? [item,
|
|
74
|
+
const [text = null, href = undefined, isDisabled] =
|
|
75
|
+
typeof item === 'string' ? [item, undefined, false] : [item.text, item.href, item.isDisabled];
|
|
76
76
|
return (
|
|
77
77
|
<ContextSelectorItem key={index} href={href} isDisabled={isDisabled}>
|
|
78
|
-
{text
|
|
78
|
+
{text}
|
|
79
79
|
</ContextSelectorItem>
|
|
80
80
|
);
|
|
81
81
|
})}
|
|
@@ -47,7 +47,7 @@ export const ContextSelectorPlainText: React.FunctionComponent = () => {
|
|
|
47
47
|
setSearchValue(value);
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const onSearchButtonClick = (
|
|
50
|
+
const onSearchButtonClick = () => {
|
|
51
51
|
const filtered =
|
|
52
52
|
searchValue === ''
|
|
53
53
|
? items
|
|
@@ -72,8 +72,8 @@ export const ContextSelectorPlainText: React.FunctionComponent = () => {
|
|
|
72
72
|
isText
|
|
73
73
|
>
|
|
74
74
|
{filteredItems.map((item, index) => {
|
|
75
|
-
const [text, href =
|
|
76
|
-
typeof item === 'string' ? [item,
|
|
75
|
+
const [text, href = undefined, isDisabled = false] =
|
|
76
|
+
typeof item === 'string' ? [item, undefined, false] : [item.text, item.href, item.isDisabled];
|
|
77
77
|
return (
|
|
78
78
|
<ContextSelectorItem key={index} href={href} isDisabled={isDisabled}>
|
|
79
79
|
{text}
|
|
@@ -47,7 +47,7 @@ export const ContextSelectorWithFooter: React.FunctionComponent = () => {
|
|
|
47
47
|
setSearchValue(value);
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const onSearchButtonClick = (
|
|
50
|
+
const onSearchButtonClick = () => {
|
|
51
51
|
const filtered =
|
|
52
52
|
searchValue === ''
|
|
53
53
|
? items
|
|
@@ -78,11 +78,11 @@ export const ContextSelectorWithFooter: React.FunctionComponent = () => {
|
|
|
78
78
|
}
|
|
79
79
|
>
|
|
80
80
|
{filteredItems.map((item, index) => {
|
|
81
|
-
const [text = null, href =
|
|
82
|
-
typeof item === 'string' ? [item,
|
|
81
|
+
const [text = null, href = undefined, isDisabled] =
|
|
82
|
+
typeof item === 'string' ? [item, undefined, false] : [item.text, item.href, item.isDisabled];
|
|
83
83
|
return (
|
|
84
84
|
<ContextSelectorItem key={index} href={href} isDisabled={isDisabled}>
|
|
85
|
-
{text
|
|
85
|
+
{text}
|
|
86
86
|
</ContextSelectorItem>
|
|
87
87
|
);
|
|
88
88
|
})}
|
|
@@ -16,8 +16,14 @@ import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-d
|
|
|
16
16
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
17
17
|
import PficonSortCommonAscIcon from '@patternfly/react-icons/dist/esm/icons/pficon-sort-common-asc-icon';
|
|
18
18
|
|
|
19
|
+
interface Option {
|
|
20
|
+
text: string;
|
|
21
|
+
selected: boolean;
|
|
22
|
+
isVisible: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
export const DualListSelectorComposable: React.FunctionComponent = () => {
|
|
20
|
-
const [availableOptions, setAvailableOptions] = React.useState([
|
|
26
|
+
const [availableOptions, setAvailableOptions] = React.useState<Option[]>([
|
|
21
27
|
{ text: 'Apple', selected: false, isVisible: true },
|
|
22
28
|
{ text: 'Banana', selected: false, isVisible: true },
|
|
23
29
|
{ text: 'Pineapple', selected: false, isVisible: true },
|
|
@@ -26,7 +32,7 @@ export const DualListSelectorComposable: React.FunctionComponent = () => {
|
|
|
26
32
|
{ text: 'Peach', selected: false, isVisible: true },
|
|
27
33
|
{ text: 'Strawberry', selected: false, isVisible: true }
|
|
28
34
|
]);
|
|
29
|
-
const [chosenOptions, setChosenOptions] = React.useState([]);
|
|
35
|
+
const [chosenOptions, setChosenOptions] = React.useState<Option[]>([]);
|
|
30
36
|
const [availableFilter, setAvailableFilter] = React.useState('');
|
|
31
37
|
const [chosenFilter, setChosenFilter] = React.useState('');
|
|
32
38
|
|
|
@@ -8,20 +8,14 @@ import {
|
|
|
8
8
|
DualListSelectorList,
|
|
9
9
|
DualListSelectorListItem,
|
|
10
10
|
DualListSelectorControlsWrapper,
|
|
11
|
-
DualListSelectorControl
|
|
11
|
+
DualListSelectorControl,
|
|
12
|
+
DraggableItemPosition
|
|
12
13
|
} from '@patternfly/react-core';
|
|
13
14
|
import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon';
|
|
14
15
|
import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
|
|
15
16
|
import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
|
|
16
17
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
17
18
|
|
|
18
|
-
interface SourceType {
|
|
19
|
-
droppableId: string;
|
|
20
|
-
index: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface DestinationType extends SourceType {}
|
|
24
|
-
|
|
25
19
|
export const DualListSelectorComposableDragDrop: React.FunctionComponent = () => {
|
|
26
20
|
const [ignoreNextOptionSelect, setIgnoreNextOptionSelect] = React.useState(false);
|
|
27
21
|
const [availableOptions, setAvailableOptions] = React.useState([
|
|
@@ -87,7 +81,7 @@ export const DualListSelectorComposableDragDrop: React.FunctionComponent = () =>
|
|
|
87
81
|
}
|
|
88
82
|
};
|
|
89
83
|
|
|
90
|
-
const onDrop = (source:
|
|
84
|
+
const onDrop = (source: DraggableItemPosition, dest?: DraggableItemPosition) => {
|
|
91
85
|
if (dest) {
|
|
92
86
|
const newList = [...chosenOptions];
|
|
93
87
|
const [removed] = newList.splice(source.index, 1);
|
|
@@ -52,7 +52,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
|
|
|
52
52
|
if (!node.children || !node.children.length) {
|
|
53
53
|
return [node.id];
|
|
54
54
|
} else {
|
|
55
|
-
let childrenIds = [];
|
|
55
|
+
let childrenIds: string[] = [];
|
|
56
56
|
node.children.forEach(child => {
|
|
57
57
|
childrenIds = [...childrenIds, ...getDescendantLeafIds(child)];
|
|
58
58
|
});
|
|
@@ -77,7 +77,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
|
|
|
77
77
|
// Builds a map of child leaf nodes by node id - memoized so that it only rebuilds the list if the data changes.
|
|
78
78
|
const { memoizedLeavesById, memoizedAllLeaves, memoizedNodeText } = React.useMemo(() => {
|
|
79
79
|
let leavesById = {};
|
|
80
|
-
let allLeaves = [];
|
|
80
|
+
let allLeaves: string[] = [];
|
|
81
81
|
let nodeTexts = {};
|
|
82
82
|
data.forEach(foodNode => {
|
|
83
83
|
nodeTexts = { ...nodeTexts, ...buildTextById(foodNode) };
|
|
@@ -129,7 +129,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
|
|
|
129
129
|
return true;
|
|
130
130
|
}
|
|
131
131
|
if (areSomeDescendantsSelected(node, isChosen)) {
|
|
132
|
-
return
|
|
132
|
+
return false;
|
|
133
133
|
}
|
|
134
134
|
return false;
|
|
135
135
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { DualListSelector } from '@patternfly/react-core';
|
|
2
|
+
import { DualListSelector, DualListSelectorTreeItemData } from '@patternfly/react-core';
|
|
3
3
|
|
|
4
4
|
export const DualListSelectorTreeExample: React.FunctionComponent = () => {
|
|
5
|
-
const [availableOptions, setAvailableOptions] = React.useState<
|
|
5
|
+
const [availableOptions, setAvailableOptions] = React.useState<DualListSelectorTreeItemData[]>([
|
|
6
6
|
{
|
|
7
7
|
id: 'F1',
|
|
8
8
|
text: 'Folder 1',
|
|
@@ -38,7 +38,7 @@ export const DualListSelectorTreeExample: React.FunctionComponent = () => {
|
|
|
38
38
|
}
|
|
39
39
|
]);
|
|
40
40
|
|
|
41
|
-
const [chosenOptions, setChosenOptions] = React.useState<
|
|
41
|
+
const [chosenOptions, setChosenOptions] = React.useState<DualListSelectorTreeItemData[]>([
|
|
42
42
|
{
|
|
43
43
|
id: 'CF1',
|
|
44
44
|
text: 'Chosen Folder 1',
|
|
@@ -73,7 +73,10 @@ export const DualListSelectorTreeExample: React.FunctionComponent = () => {
|
|
|
73
73
|
}
|
|
74
74
|
]);
|
|
75
75
|
|
|
76
|
-
const onListChange = (
|
|
76
|
+
const onListChange = (
|
|
77
|
+
newAvailableOptions: DualListSelectorTreeItemData[],
|
|
78
|
+
newChosenOptions: DualListSelectorTreeItemData[]
|
|
79
|
+
) => {
|
|
77
80
|
setAvailableOptions(newAvailableOptions.sort());
|
|
78
81
|
setChosenOptions(newChosenOptions.sort());
|
|
79
82
|
};
|
|
@@ -84,7 +87,7 @@ export const DualListSelectorTreeExample: React.FunctionComponent = () => {
|
|
|
84
87
|
isTree
|
|
85
88
|
availableOptions={availableOptions}
|
|
86
89
|
chosenOptions={chosenOptions}
|
|
87
|
-
onListChange={onListChange}
|
|
90
|
+
onListChange={onListChange as any}
|
|
88
91
|
id="dual-list-selector-tree"
|
|
89
92
|
/>
|
|
90
93
|
);
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import styles from '@patternfly/react-styles/css/components/ExpandableSection/expandable-section';
|
|
3
3
|
import { css } from '@patternfly/react-styles';
|
|
4
|
+
import lineClamp from '@patternfly/react-tokens/dist/esm/c_expandable_section_m_truncate__content_LineClamp';
|
|
4
5
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
5
6
|
import { PickOptional } from '../../helpers/typeUtils';
|
|
6
7
|
|
|
8
|
+
export enum ExpandableSectionVariant {
|
|
9
|
+
default = 'default',
|
|
10
|
+
truncate = 'truncate'
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement> {
|
|
8
|
-
/** Content rendered inside the
|
|
14
|
+
/** Content rendered inside the expandable section. */
|
|
9
15
|
children?: React.ReactNode;
|
|
10
|
-
/** Additional classes added to the
|
|
16
|
+
/** Additional classes added to the expandable section. */
|
|
11
17
|
className?: string;
|
|
12
18
|
/** Flag to indicate if the content is expanded */
|
|
13
19
|
isExpanded?: boolean;
|
|
@@ -19,7 +25,7 @@ export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement>
|
|
|
19
25
|
toggleTextCollapsed?: string;
|
|
20
26
|
/** React node that appears in the attached toggle in place of toggle text */
|
|
21
27
|
toggleContent?: React.ReactNode;
|
|
22
|
-
/** Callback function to toggle the expandable
|
|
28
|
+
/** Callback function to toggle the expandable section. Detached expandable sections should use the onToggle property of the expandable section toggle sub-component. */
|
|
23
29
|
onToggle?: (isExpanded: boolean) => void;
|
|
24
30
|
/** Forces active state */
|
|
25
31
|
isActive?: boolean;
|
|
@@ -33,12 +39,27 @@ export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement>
|
|
|
33
39
|
isWidthLimited?: boolean;
|
|
34
40
|
/** Flag to indicate if the content is indented */
|
|
35
41
|
isIndented?: boolean;
|
|
42
|
+
/** @beta Determines the variant of the expandable section. When passing in "truncate" as the
|
|
43
|
+
* variant, the expandable content will be truncated after 3 lines by default.
|
|
44
|
+
*/
|
|
45
|
+
variant?: 'default' | 'truncate';
|
|
46
|
+
/** @beta Truncates the expandable content to the specified number of lines when using the
|
|
47
|
+
* "truncate" variant. */
|
|
48
|
+
truncateMaxLines?: number;
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
interface ExpandableSectionState {
|
|
39
52
|
isExpanded: boolean;
|
|
40
53
|
}
|
|
41
54
|
|
|
55
|
+
const setLineClamp = (lines: number, element: HTMLDivElement) => {
|
|
56
|
+
if (!element || lines < 1) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
element.style.setProperty(lineClamp.name, lines.toString());
|
|
61
|
+
};
|
|
62
|
+
|
|
42
63
|
export class ExpandableSection extends React.Component<ExpandableSectionProps, ExpandableSectionState> {
|
|
43
64
|
static displayName = 'ExpandableSection';
|
|
44
65
|
constructor(props: ExpandableSectionProps) {
|
|
@@ -49,6 +70,7 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
49
70
|
};
|
|
50
71
|
}
|
|
51
72
|
|
|
73
|
+
expandableContentRef = React.createRef<HTMLDivElement>();
|
|
52
74
|
static defaultProps: PickOptional<ExpandableSectionProps> = {
|
|
53
75
|
className: '',
|
|
54
76
|
toggleText: '',
|
|
@@ -61,7 +83,8 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
61
83
|
displaySize: 'default',
|
|
62
84
|
isWidthLimited: false,
|
|
63
85
|
isIndented: false,
|
|
64
|
-
contentId: ''
|
|
86
|
+
contentId: '',
|
|
87
|
+
variant: 'default'
|
|
65
88
|
};
|
|
66
89
|
|
|
67
90
|
private calculateToggleText(
|
|
@@ -79,6 +102,23 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
79
102
|
return toggleText;
|
|
80
103
|
}
|
|
81
104
|
|
|
105
|
+
componentDidMount() {
|
|
106
|
+
if (this.props.variant === ExpandableSectionVariant.truncate && this.props.truncateMaxLines) {
|
|
107
|
+
const expandableContent = this.expandableContentRef.current;
|
|
108
|
+
setLineClamp(this.props.truncateMaxLines, expandableContent);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
componentDidUpdate(prevProps: ExpandableSectionProps) {
|
|
113
|
+
if (
|
|
114
|
+
this.props.variant === ExpandableSectionVariant.truncate &&
|
|
115
|
+
prevProps.truncateMaxLines !== this.props.truncateMaxLines
|
|
116
|
+
) {
|
|
117
|
+
const expandableContent = this.expandableContentRef.current;
|
|
118
|
+
setLineClamp(this.props.truncateMaxLines, expandableContent);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
82
122
|
render() {
|
|
83
123
|
const {
|
|
84
124
|
onToggle: onToggleProp,
|
|
@@ -96,6 +136,9 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
96
136
|
isWidthLimited,
|
|
97
137
|
isIndented,
|
|
98
138
|
contentId,
|
|
139
|
+
variant,
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
141
|
+
truncateMaxLines,
|
|
99
142
|
...props
|
|
100
143
|
} = this.props;
|
|
101
144
|
let onToggle = onToggleProp;
|
|
@@ -116,9 +159,24 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
116
159
|
propOrStateIsExpanded
|
|
117
160
|
);
|
|
118
161
|
|
|
162
|
+
const expandableToggle = !isDetached && (
|
|
163
|
+
<button
|
|
164
|
+
className={css(styles.expandableSectionToggle)}
|
|
165
|
+
type="button"
|
|
166
|
+
aria-expanded={propOrStateIsExpanded}
|
|
167
|
+
onClick={() => onToggle(!propOrStateIsExpanded)}
|
|
168
|
+
>
|
|
169
|
+
{variant !== ExpandableSectionVariant.truncate && (
|
|
170
|
+
<span className={css(styles.expandableSectionToggleIcon)}>
|
|
171
|
+
<AngleRightIcon aria-hidden />
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
<span className={css(styles.expandableSectionToggleText)}>{toggleContent || computedToggleText}</span>
|
|
175
|
+
</button>
|
|
176
|
+
);
|
|
177
|
+
|
|
119
178
|
return (
|
|
120
179
|
<div
|
|
121
|
-
{...props}
|
|
122
180
|
className={css(
|
|
123
181
|
styles.expandableSection,
|
|
124
182
|
propOrStateIsExpanded && styles.modifiers.expanded,
|
|
@@ -127,25 +185,21 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
|
|
|
127
185
|
displaySize === 'large' && styles.modifiers.displayLg,
|
|
128
186
|
isWidthLimited && styles.modifiers.limitWidth,
|
|
129
187
|
isIndented && styles.modifiers.indented,
|
|
188
|
+
variant === ExpandableSectionVariant.truncate && styles.modifiers.truncate,
|
|
130
189
|
className
|
|
131
190
|
)}
|
|
191
|
+
{...props}
|
|
132
192
|
>
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
<span className={css(styles.expandableSectionToggleIcon)}>
|
|
141
|
-
<AngleRightIcon aria-hidden />
|
|
142
|
-
</span>
|
|
143
|
-
<span className={css(styles.expandableSectionToggleText)}>{toggleContent || computedToggleText}</span>
|
|
144
|
-
</button>
|
|
145
|
-
)}
|
|
146
|
-
<div className={css(styles.expandableSectionContent)} hidden={!propOrStateIsExpanded} id={contentId}>
|
|
193
|
+
{variant === ExpandableSectionVariant.default && expandableToggle}
|
|
194
|
+
<div
|
|
195
|
+
ref={this.expandableContentRef}
|
|
196
|
+
className={css(styles.expandableSectionContent)}
|
|
197
|
+
hidden={variant !== ExpandableSectionVariant.truncate && !propOrStateIsExpanded}
|
|
198
|
+
id={contentId}
|
|
199
|
+
>
|
|
147
200
|
{children}
|
|
148
201
|
</div>
|
|
202
|
+
{variant === ExpandableSectionVariant.truncate && expandableToggle}
|
|
149
203
|
</div>
|
|
150
204
|
);
|
|
151
205
|
}
|
|
@@ -16,6 +16,8 @@ export interface ExpandableSectionToggleProps extends React.HTMLProps<HTMLDivEle
|
|
|
16
16
|
contentId?: string;
|
|
17
17
|
/** Direction the toggle arrow should point when the expandable section is expanded. */
|
|
18
18
|
direction?: 'up' | 'down';
|
|
19
|
+
/** @beta Flag to determine toggle styling when the expandable content is truncated. */
|
|
20
|
+
hasTruncatedContent?: boolean;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionToggleProps> = ({
|
|
@@ -25,16 +27,18 @@ export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionT
|
|
|
25
27
|
onToggle,
|
|
26
28
|
contentId,
|
|
27
29
|
direction = 'down',
|
|
30
|
+
hasTruncatedContent = false,
|
|
28
31
|
...props
|
|
29
32
|
}: ExpandableSectionToggleProps) => (
|
|
30
33
|
<div
|
|
31
|
-
{...props}
|
|
32
34
|
className={css(
|
|
33
35
|
styles.expandableSection,
|
|
34
36
|
isExpanded && styles.modifiers.expanded,
|
|
35
37
|
styles.modifiers.detached,
|
|
38
|
+
hasTruncatedContent && styles.modifiers.truncate,
|
|
36
39
|
className
|
|
37
40
|
)}
|
|
41
|
+
{...props}
|
|
38
42
|
>
|
|
39
43
|
<button
|
|
40
44
|
className={css(styles.expandableSectionToggle)}
|
|
@@ -43,14 +47,16 @@ export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionT
|
|
|
43
47
|
aria-controls={contentId}
|
|
44
48
|
onClick={() => onToggle(!isExpanded)}
|
|
45
49
|
>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
{!hasTruncatedContent && (
|
|
51
|
+
<span
|
|
52
|
+
className={css(
|
|
53
|
+
styles.expandableSectionToggleIcon,
|
|
54
|
+
isExpanded && direction === 'up' && styles.modifiers.expandTop
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
<AngleRightIcon aria-hidden />
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
54
60
|
<span className={css(styles.expandableSectionToggleText)}>{children}</span>
|
|
55
61
|
</button>
|
|
56
62
|
</div>
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
|
|
6
|
-
import { ExpandableSection } from '../ExpandableSection';
|
|
6
|
+
import { ExpandableSection, ExpandableSectionVariant } from '../ExpandableSection';
|
|
7
7
|
import { ExpandableSectionToggle } from '../ExpandableSectionToggle';
|
|
8
8
|
|
|
9
9
|
const props = {};
|
|
@@ -64,3 +64,29 @@ test('Renders ExpandableSection indented', () => {
|
|
|
64
64
|
);
|
|
65
65
|
expect(asFragment()).toMatchSnapshot();
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
test('Does not render with pf-m-truncate class when variant is not passed', () => {
|
|
69
|
+
render(<ExpandableSection {...props}>test</ExpandableSection>);
|
|
70
|
+
|
|
71
|
+
expect(screen.getByText('test').parentElement).not.toHaveClass('pf-m-truncate');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('Does not render with pf-m-truncate class when variant is not truncate', () => {
|
|
75
|
+
render(
|
|
76
|
+
<ExpandableSection variant={ExpandableSectionVariant.default} {...props}>
|
|
77
|
+
test
|
|
78
|
+
</ExpandableSection>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(screen.getByText('test').parentElement).not.toHaveClass('pf-m-truncate');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('Renders with pf-m-truncate class when variant is truncate', () => {
|
|
85
|
+
render(
|
|
86
|
+
<ExpandableSection variant={ExpandableSectionVariant.truncate} {...props}>
|
|
87
|
+
test
|
|
88
|
+
</ExpandableSection>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(screen.getByText('test').parentElement).toHaveClass('pf-m-truncate');
|
|
92
|
+
});
|
|
@@ -45,3 +45,10 @@ By using the `toggleContent` prop, you can pass in content other than a simple s
|
|
|
45
45
|
|
|
46
46
|
```ts file="ExpandableSectionCustomToggle.tsx"
|
|
47
47
|
```
|
|
48
|
+
|
|
49
|
+
### Truncate expansion
|
|
50
|
+
|
|
51
|
+
By passing in `variant="truncate"`, the expandable content will be visible up to a maximum number of lines before being truncated, with the toggle revealing or hiding the truncated content. By default the expandable content will truncate after 3 lines, and this can be customized by also passing in the `truncateMaxLines` prop.
|
|
52
|
+
|
|
53
|
+
```ts file="ExpandableSectionTruncateExpansion.tsx" isBeta
|
|
54
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ExpandableSection, ExpandableSectionVariant } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const ExpandableSectionTruncateExpansion: React.FunctionComponent = () => {
|
|
5
|
+
const [isExpanded, setIsExpanded] = React.useState(false);
|
|
6
|
+
|
|
7
|
+
const onToggle = (isExpanded: boolean) => {
|
|
8
|
+
setIsExpanded(isExpanded);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<ExpandableSection
|
|
13
|
+
variant={ExpandableSectionVariant.truncate}
|
|
14
|
+
toggleText={isExpanded ? 'Show less' : 'Show more'}
|
|
15
|
+
onToggle={onToggle}
|
|
16
|
+
isExpanded={isExpanded}
|
|
17
|
+
>
|
|
18
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque nec dignissim turpis, et tristique purus.
|
|
19
|
+
Phasellus efficitur ante quis dolor viverra imperdiet. Orci varius natoque penatibus et magnis dis parturient
|
|
20
|
+
montes, nascetur ridiculus mus. Pellentesque laoreet, sem ac elementum semper, lectus mauris vestibulum nulla,
|
|
21
|
+
eget volutpat massa neque vel turpis. Donec finibus enim eu leo accumsan consectetur. Praesent massa diam,
|
|
22
|
+
tincidunt eu dui ac, ullamcorper elementum est. Phasellus metus felis, venenatis vitae semper nec, porta a metus.
|
|
23
|
+
Vestibulum justo nisi, imperdiet id eleifend at, varius nec lorem. Fusce porttitor mollis nibh, ut elementum ante
|
|
24
|
+
commodo tincidunt. Integer tincidunt at ipsum non aliquet.
|
|
25
|
+
</ExpandableSection>
|
|
26
|
+
);
|
|
27
|
+
};
|