@patternfly/react-core 4.198.14 → 4.198.17

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 (114) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/esm/components/Form/FormFieldGroup.d.ts +1 -1
  3. package/dist/esm/components/Form/FormFieldGroupExpandable.d.ts +2 -2
  4. package/dist/esm/components/Form/FormFieldGroupHeader.d.ts +3 -1
  5. package/dist/esm/components/Form/FormFieldGroupHeader.d.ts.map +1 -1
  6. package/dist/esm/components/Form/FormFieldGroupHeader.js.map +1 -1
  7. package/dist/esm/components/Form/InternalFormFieldGroup.d.ts +2 -2
  8. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadMain.d.ts +10 -2
  9. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadMain.d.ts.map +1 -1
  10. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadMain.js +9 -2
  11. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadMain.js.map +1 -1
  12. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadStatus.d.ts +1 -1
  13. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadStatus.d.ts.map +1 -1
  14. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadStatus.js +21 -2
  15. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadStatus.js.map +1 -1
  16. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadStatusItem.js.map +1 -1
  17. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitle.d.ts +6 -2
  18. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitle.d.ts.map +1 -1
  19. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitle.js +9 -2
  20. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitle.js.map +1 -1
  21. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitleIcon.d.ts +2 -0
  22. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitleIcon.d.ts.map +1 -1
  23. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitleIcon.js +2 -4
  24. package/dist/esm/components/MultipleFileUpload/MultipleFileUploadTitleIcon.js.map +1 -1
  25. package/dist/esm/components/MultipleFileUpload/index.d.ts +0 -6
  26. package/dist/esm/components/MultipleFileUpload/index.d.ts.map +1 -1
  27. package/dist/esm/components/MultipleFileUpload/index.js +0 -6
  28. package/dist/esm/components/MultipleFileUpload/index.js.map +1 -1
  29. package/dist/esm/components/Tabs/Tab.d.ts +3 -1
  30. package/dist/esm/components/Tabs/Tab.d.ts.map +1 -1
  31. package/dist/esm/components/Tabs/Tab.js +4 -3
  32. package/dist/esm/components/Tabs/Tab.js.map +1 -1
  33. package/dist/esm/components/Tabs/TabButton.d.ts +2 -0
  34. package/dist/esm/components/Tabs/TabButton.d.ts.map +1 -1
  35. package/dist/esm/components/Tabs/TabButton.js +2 -2
  36. package/dist/esm/components/Tabs/TabButton.js.map +1 -1
  37. package/dist/esm/components/Tile/Tile.d.ts.map +1 -1
  38. package/dist/esm/components/Tile/Tile.js +1 -1
  39. package/dist/esm/components/Tile/Tile.js.map +1 -1
  40. package/dist/js/components/Form/FormFieldGroup.d.ts +1 -1
  41. package/dist/js/components/Form/FormFieldGroupExpandable.d.ts +2 -2
  42. package/dist/js/components/Form/FormFieldGroupHeader.d.ts +3 -1
  43. package/dist/js/components/Form/FormFieldGroupHeader.d.ts.map +1 -1
  44. package/dist/js/components/Form/FormFieldGroupHeader.js.map +1 -1
  45. package/dist/js/components/Form/InternalFormFieldGroup.d.ts +2 -2
  46. package/dist/js/components/MultipleFileUpload/MultipleFileUploadMain.d.ts +10 -2
  47. package/dist/js/components/MultipleFileUpload/MultipleFileUploadMain.d.ts.map +1 -1
  48. package/dist/js/components/MultipleFileUpload/MultipleFileUploadMain.js +9 -2
  49. package/dist/js/components/MultipleFileUpload/MultipleFileUploadMain.js.map +1 -1
  50. package/dist/js/components/MultipleFileUpload/MultipleFileUploadStatus.d.ts +1 -1
  51. package/dist/js/components/MultipleFileUpload/MultipleFileUploadStatus.d.ts.map +1 -1
  52. package/dist/js/components/MultipleFileUpload/MultipleFileUploadStatus.js +21 -2
  53. package/dist/js/components/MultipleFileUpload/MultipleFileUploadStatus.js.map +1 -1
  54. package/dist/js/components/MultipleFileUpload/MultipleFileUploadStatusItem.js.map +1 -1
  55. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitle.d.ts +6 -2
  56. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitle.d.ts.map +1 -1
  57. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitle.js +9 -2
  58. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitle.js.map +1 -1
  59. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitleIcon.d.ts +2 -0
  60. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitleIcon.d.ts.map +1 -1
  61. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitleIcon.js +2 -4
  62. package/dist/js/components/MultipleFileUpload/MultipleFileUploadTitleIcon.js.map +1 -1
  63. package/dist/js/components/MultipleFileUpload/index.d.ts +0 -6
  64. package/dist/js/components/MultipleFileUpload/index.d.ts.map +1 -1
  65. package/dist/js/components/MultipleFileUpload/index.js +0 -6
  66. package/dist/js/components/MultipleFileUpload/index.js.map +1 -1
  67. package/dist/js/components/Tabs/Tab.d.ts +3 -1
  68. package/dist/js/components/Tabs/Tab.d.ts.map +1 -1
  69. package/dist/js/components/Tabs/Tab.js +4 -3
  70. package/dist/js/components/Tabs/Tab.js.map +1 -1
  71. package/dist/js/components/Tabs/TabButton.d.ts +2 -0
  72. package/dist/js/components/Tabs/TabButton.d.ts.map +1 -1
  73. package/dist/js/components/Tabs/TabButton.js +2 -2
  74. package/dist/js/components/Tabs/TabButton.js.map +1 -1
  75. package/dist/js/components/Tile/Tile.d.ts.map +1 -1
  76. package/dist/js/components/Tile/Tile.js +1 -1
  77. package/dist/js/components/Tile/Tile.js.map +1 -1
  78. package/dist/umd/react-core.css +10 -10
  79. package/dist/umd/react-core.js +65 -48
  80. package/dist/umd/react-core.min.css +10 -10
  81. package/dist/umd/react-core.min.js +4 -4
  82. package/package.json +5 -5
  83. package/src/components/Form/FormFieldGroup.tsx +1 -1
  84. package/src/components/Form/FormFieldGroupExpandable.tsx +2 -2
  85. package/src/components/Form/FormFieldGroupHeader.tsx +5 -1
  86. package/src/components/Form/InternalFormFieldGroup.tsx +2 -2
  87. package/src/components/Form/__tests__/FormFieldGroup.test.tsx +1 -1
  88. package/src/components/Form/__tests__/__snapshots__/FormFieldGroup.test.tsx.snap +1 -1
  89. package/src/components/Form/examples/Form.md +1 -1
  90. package/src/components/MultipleFileUpload/MultipleFileUploadMain.tsx +29 -8
  91. package/src/components/MultipleFileUpload/MultipleFileUploadStatus.tsx +24 -4
  92. package/src/components/MultipleFileUpload/MultipleFileUploadStatusItem.tsx +1 -1
  93. package/src/components/MultipleFileUpload/MultipleFileUploadTitle.tsx +19 -5
  94. package/src/components/MultipleFileUpload/MultipleFileUploadTitleIcon.tsx +4 -3
  95. package/src/components/MultipleFileUpload/__tests__/MultipleFileUploadMain.test.tsx +19 -2
  96. package/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitle.test.tsx +17 -2
  97. package/src/components/MultipleFileUpload/__tests__/MultipleFileUploadTitleIcon.test.tsx +1 -1
  98. package/src/components/MultipleFileUpload/__tests__/__snapshots__/MultipleFileUploadMain.test.tsx.snap +198 -2
  99. package/src/components/MultipleFileUpload/__tests__/__snapshots__/MultipleFileUploadTitle.test.tsx.snap +62 -4
  100. package/src/components/MultipleFileUpload/__tests__/__snapshots__/MultipleFileUploadTitleIcon.test.tsx.snap +2 -50
  101. package/src/components/MultipleFileUpload/examples/MultipleFileUpload.md +7 -60
  102. package/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx +48 -74
  103. package/src/components/MultipleFileUpload/index.ts +0 -6
  104. package/src/components/Tabs/Tab.tsx +31 -24
  105. package/src/components/Tabs/TabButton.tsx +5 -1
  106. package/src/components/Tabs/examples/Tabs.md +527 -256
  107. package/src/components/Tile/Tile.tsx +3 -0
  108. package/src/components/Tile/__tests__/__snapshots__/Tile.test.tsx.snap +9 -0
  109. package/src/components/Tile/examples/Tile.md +41 -39
  110. package/src/demos/ComposableMenu/ComposableMenu.md +4 -9
  111. package/src/demos/ComposableMenu/examples/ComposableDateSelect.tsx +113 -0
  112. package/src/demos/MultipleFileUploadDemos.md +16 -0
  113. package/src/demos/TileDemo.md +17 -35
  114. package/src/{components/MultipleFileUpload/examples/MultipleFileUploadHorizontal.tsx → demos/examples/MultipleFileUpload/MultipleFileUploadRejectedFile.tsx} +60 -64
@@ -33,6 +33,9 @@ export const Tile: React.FunctionComponent<TileProps> = ({
33
33
  ...props
34
34
  }: TileProps) => (
35
35
  <div
36
+ role="option"
37
+ aria-selected={isSelected}
38
+ {...(isDisabled && { 'aria-disabled': isDisabled })}
36
39
  className={css(
37
40
  styles.tile,
38
41
  isSelected && styles.modifiers.selected,
@@ -4,6 +4,7 @@ exports[`Tile basic 1`] = `
4
4
  <div>
5
5
  <div
6
6
  class="pf-c-tile"
7
+ role="option"
7
8
  tabindex="0"
8
9
  >
9
10
  <div
@@ -22,7 +23,9 @@ exports[`Tile basic 1`] = `
22
23
  exports[`Tile renders disabled 1`] = `
23
24
  <div>
24
25
  <div
26
+ aria-disabled="true"
25
27
  class="pf-c-tile pf-m-disabled"
28
+ role="option"
26
29
  tabindex="0"
27
30
  >
28
31
  <div
@@ -41,7 +44,9 @@ exports[`Tile renders disabled 1`] = `
41
44
  exports[`Tile renders selected 1`] = `
42
45
  <div>
43
46
  <div
47
+ aria-selected="true"
44
48
  class="pf-c-tile pf-m-selected"
49
+ role="option"
45
50
  tabindex="0"
46
51
  >
47
52
  <div
@@ -61,6 +66,7 @@ exports[`Tile renders with icon 1`] = `
61
66
  <div>
62
67
  <div
63
68
  class="pf-c-tile"
69
+ role="option"
64
70
  tabindex="0"
65
71
  >
66
72
  <div
@@ -97,6 +103,7 @@ exports[`Tile renders with stacked icon 1`] = `
97
103
  <div>
98
104
  <div
99
105
  class="pf-c-tile"
106
+ role="option"
100
107
  tabindex="0"
101
108
  >
102
109
  <div
@@ -133,6 +140,7 @@ exports[`Tile renders with stacked large icon 1`] = `
133
140
  <div>
134
141
  <div
135
142
  class="pf-c-tile pf-m-display-lg"
143
+ role="option"
136
144
  tabindex="0"
137
145
  >
138
146
  <div
@@ -169,6 +177,7 @@ exports[`Tile renders with subtext 1`] = `
169
177
  <div>
170
178
  <div
171
179
  class="pf-c-tile"
180
+ role="option"
172
181
  tabindex="0"
173
182
  >
174
183
  <div
@@ -9,7 +9,6 @@ beta: true
9
9
  import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon';
10
10
  import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
11
11
 
12
-
13
12
  ## Examples
14
13
 
15
14
  Keyboard interaction patterns and a11y is implemented in the Tile demos, located in the [Demo section](/documentation/react/demos/tiledemo).
@@ -20,29 +19,30 @@ Keyboard interaction patterns and a11y is implemented in the Tile demos, located
20
19
  import React from 'react';
21
20
  import { Tile } from '@patternfly/react-core';
22
21
 
23
- <React.Fragment>
24
- <Tile title="Default" />
22
+ <div role="listbox" aria-label="Basic tiles">
23
+ <Tile title="Default" isSelected={false} />
25
24
  <Tile title="Selected" isSelected />
26
- <Tile title="Disabled" isDisabled />
27
- </React.Fragment>
25
+ <Tile title="Disabled" isDisabled isSelected={false} />
26
+ </div>;
28
27
  ```
29
28
 
30
-
31
29
  ### With subtext
32
30
 
33
31
  ```js
34
32
  import React from 'react';
35
33
  import { Tile } from '@patternfly/react-core';
36
34
 
37
- <React.Fragment>
38
- <Tile title="Default">Subtext goes here</Tile>{' '}
35
+ <div role="listbox" aria-label="Tiles with subtext">
36
+ <Tile title="Default" isSelected={false}>
37
+ Subtext goes here
38
+ </Tile>{' '}
39
39
  <Tile title="Selected" isSelected>
40
40
  Subtext goes here
41
41
  </Tile>{' '}
42
- <Tile title="Disabled" isDisabled>
42
+ <Tile title="Disabled" isDisabled isSelected={false}>
43
43
  Subtext goes here
44
44
  </Tile>
45
- </React.Fragment>
45
+ </div>;
46
46
  ```
47
47
 
48
48
  ### With icon
@@ -52,17 +52,17 @@ import React from 'react';
52
52
  import { Tile } from '@patternfly/react-core';
53
53
  import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon';
54
54
 
55
- <React.Fragment>
56
- <Tile title="Default" icon={<PlusIcon />}>
55
+ <div role="listbox" aria-label="Tiles with icon">
56
+ <Tile title="Default" icon={<PlusIcon />} isSelected={false}>
57
57
  Subtext goes here
58
58
  </Tile>{' '}
59
59
  <Tile title="Selected" icon={<PlusIcon />} isSelected>
60
60
  Subtext goes here
61
61
  </Tile>{' '}
62
- <Tile title="Disabled" icon={<PlusIcon />} isDisabled>
62
+ <Tile title="Disabled" icon={<PlusIcon />} isDisabled isSelected={false}>
63
63
  Subtext goes here
64
64
  </Tile>
65
- </React.Fragment>
65
+ </div>;
66
66
  ```
67
67
 
68
68
  ### Stacked
@@ -72,17 +72,17 @@ import React from 'react';
72
72
  import { Tile } from '@patternfly/react-core';
73
73
  import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
74
74
 
75
- <React.Fragment>
76
- <Tile title="Default" icon={<BellIcon />} isStacked>
75
+ <div role="listbox" aria-label="Stacked tiles">
76
+ <Tile title="Default" icon={<BellIcon />} isStacked isSelected={false}>
77
77
  Subtext goes here
78
78
  </Tile>{' '}
79
79
  <Tile title="Selected" icon={<BellIcon />} isStacked isSelected>
80
80
  Subtext goes here
81
81
  </Tile>{' '}
82
- <Tile title="Disabled" icon={<BellIcon />} isStacked isDisabled>
82
+ <Tile title="Disabled" icon={<BellIcon />} isStacked isDisabled isSelected={false}>
83
83
  Subtext goes here
84
84
  </Tile>
85
- </React.Fragment>
85
+ </div>;
86
86
  ```
87
87
 
88
88
  ### Stacked with large icons
@@ -92,17 +92,17 @@ import React from 'react';
92
92
  import { Tile } from '@patternfly/react-core';
93
93
  import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
94
94
 
95
- <React.Fragment>
96
- <Tile title="Default" icon={<BellIcon />} isStacked isDisplayLarge>
95
+ <div role="listbox" aria-label="Stacked tiles with large icons">
96
+ <Tile title="Default" icon={<BellIcon />} isStacked isDisplayLarge isSelected={false}>
97
97
  Subtext goes here
98
98
  </Tile>{' '}
99
99
  <Tile title="Selected" icon={<BellIcon />} isStacked isDisplayLarge isSelected>
100
100
  Subtext goes here
101
101
  </Tile>{' '}
102
- <Tile title="Disabled" icon={<BellIcon />} isStacked isDisplayLarge isDisabled>
102
+ <Tile title="Disabled" icon={<BellIcon />} isStacked isDisplayLarge isDisabled isSelected={false}>
103
103
  Subtext goes here
104
104
  </Tile>
105
- </React.Fragment>
105
+ </div>;
106
106
  ```
107
107
 
108
108
  ### With extra content
@@ -112,22 +112,24 @@ import React from 'react';
112
112
  import { Tile, Flex } from '@patternfly/react-core';
113
113
  import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
114
114
 
115
- <Flex>
116
- <Flex flex={{ default: 'flex_1' }}>
117
- <Tile title="Default" icon={<BellIcon />} isStacked>
118
- This is really really long subtext that goes on for so long that it has to wrap to the next line. This is really
119
- really long subtext that goes on for so long that it has to wrap to the next line.
120
- </Tile>
121
- </Flex>
122
- <Flex flex={{ default: 'flex_1' }}>
123
- <Tile title="Selected" icon={<BellIcon />} isStacked>
124
- This is really really long subtext that goes on for so long that it has to wrap to the next line.
125
- </Tile>
126
- </Flex>
127
- <Flex flex={{ default: 'flex_1' }}>
128
- <Tile title="Disabled" icon={<BellIcon />} isStacked>
129
- Subtext goes here
130
- </Tile>
115
+ <div role="listbox" aria-label="Tiles with extra content">
116
+ <Flex>
117
+ <Flex flex={{ default: 'flex_1' }}>
118
+ <Tile title="Default" icon={<BellIcon />} isStacked isSelected={false}>
119
+ This is really really long subtext that goes on for so long that it has to wrap to the next line. This is really
120
+ really long subtext that goes on for so long that it has to wrap to the next line.
121
+ </Tile>
122
+ </Flex>
123
+ <Flex flex={{ default: 'flex_1' }}>
124
+ <Tile title="Selected" icon={<BellIcon />} isStacked isSelected>
125
+ This is really really long subtext that goes on for so long that it has to wrap to the next line.
126
+ </Tile>
127
+ </Flex>
128
+ <Flex flex={{ default: 'flex_1' }}>
129
+ <Tile title="Disabled" icon={<BellIcon />} isStacked isDisabled isSelected={false}>
130
+ Subtext goes here
131
+ </Tile>
132
+ </Flex>
131
133
  </Flex>
132
- </Flex>
134
+ </div>;
133
135
  ```
@@ -28,31 +28,26 @@ Composable menus currently require consumer keyboard handling and use of our und
28
28
  ### Composable simple dropdown
29
29
 
30
30
  ```ts file="./examples/ComposableSimpleDropdown.tsx"
31
-
32
31
  ```
33
32
 
34
33
  ### Composable actions menu
35
34
 
36
35
  ```ts file="./examples/ComposableActionsMenu.tsx"
37
-
38
36
  ```
39
37
 
40
38
  ### Composable simple select
41
39
 
42
40
  ```ts file="./examples/ComposableSimpleSelect.tsx"
43
-
44
41
  ```
45
42
 
46
43
  ### Composable drilldown menu
47
44
 
48
45
  ```ts isBeta file="./examples/ComposableDrilldownMenu.tsx"
49
-
50
46
  ```
51
47
 
52
48
  ### Composable tree view menu
53
49
 
54
50
  ```ts file="./examples/ComposableTreeViewMenu.tsx"
55
-
56
51
  ```
57
52
 
58
53
  ### Composable flyout
@@ -60,29 +55,29 @@ Composable menus currently require consumer keyboard handling and use of our und
60
55
  The flyout will automatically position to the left or top if it would otherwise go outside the window. The menu must be placed in a container outside the main content like Popper, [Popover](/components/popover) or [Tooltip](/components/tooltip) since it may go over the side nav.
61
56
 
62
57
  ```ts isBeta file="./examples/ComposableFlyout.tsx"
63
-
64
58
  ```
65
59
 
66
60
  ### Composable application launcher
67
61
 
68
62
  ```ts file="./examples/ComposableApplicationLauncher.tsx"
69
-
70
63
  ```
71
64
 
72
65
  ### Composable context selector
73
66
 
74
67
  ```ts file="./examples/ComposableContextSelector.tsx"
75
-
76
68
  ```
77
69
 
78
70
  ### Composable options menu variants
79
71
 
80
72
  ```ts file="./examples/ComposableOptionsMenuVariants.tsx"
81
-
82
73
  ```
83
74
 
84
75
  ### Composable dropdown variants
85
76
 
86
77
  ```ts file="./examples/ComposableDropdwnVariants.tsx"
78
+ ```
79
+
80
+ ### Composable date select
87
81
 
82
+ ```ts file="./examples/ComposableDateSelect.tsx"
88
83
  ```
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+ import { MenuToggle, Menu, MenuContent, MenuList, MenuItem, Popper } from '@patternfly/react-core';
3
+
4
+ export const ComposableSimpleDropdown: React.FunctionComponent = () => {
5
+ const [isOpen, setIsOpen] = React.useState(false);
6
+ const toggleRef = React.useRef<HTMLButtonElement>();
7
+ const menuRef = React.useRef<HTMLDivElement>();
8
+ const [selected, setSelected] = React.useState<number>(0);
9
+
10
+ const handleMenuKeys = (event: KeyboardEvent) => {
11
+ if (!isOpen) {
12
+ return;
13
+ }
14
+ if (menuRef.current.contains(event.target as Node) || toggleRef.current.contains(event.target as Node)) {
15
+ if (event.key === 'Escape' || event.key === 'Tab') {
16
+ setIsOpen(!isOpen);
17
+ toggleRef.current.focus();
18
+ }
19
+ }
20
+ };
21
+
22
+ const handleClickOutside = (event: MouseEvent) => {
23
+ if (isOpen && !menuRef.current.contains(event.target as Node)) {
24
+ setIsOpen(false);
25
+ }
26
+ };
27
+
28
+ React.useEffect(() => {
29
+ window.addEventListener('keydown', handleMenuKeys);
30
+ window.addEventListener('click', handleClickOutside);
31
+ return () => {
32
+ window.removeEventListener('keydown', handleMenuKeys);
33
+ window.removeEventListener('click', handleClickOutside);
34
+ };
35
+ }, [isOpen, menuRef]);
36
+
37
+ const onToggleClick = (ev: React.MouseEvent) => {
38
+ ev.stopPropagation(); // Stop handleClickOutside from handling
39
+ setTimeout(() => {
40
+ if (menuRef.current) {
41
+ const firstElement = menuRef.current.querySelector('li > button:not(:disabled)');
42
+ firstElement && (firstElement as HTMLElement).focus();
43
+ }
44
+ }, 0);
45
+ setIsOpen(!isOpen);
46
+ };
47
+
48
+ const monthStrings = [
49
+ 'January',
50
+ 'February',
51
+ 'March',
52
+ 'April',
53
+ 'May',
54
+ 'June',
55
+ 'July',
56
+ 'August',
57
+ 'September',
58
+ 'October',
59
+ 'November',
60
+ 'December'
61
+ ];
62
+
63
+ const dateString = (date: Date) => `${monthStrings[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
64
+
65
+ const date = new Date();
66
+
67
+ const toggleText = {
68
+ 0: 'Today ',
69
+ 1: 'Yesterday ',
70
+ 2: 'Last 7 days ',
71
+ 3: 'Last 14 days '
72
+ };
73
+
74
+ const dateText = {
75
+ 0: <small className="pf-u-color-200">({dateString(date)})</small>,
76
+ 1: (
77
+ <small className="pf-u-color-200">
78
+ ({dateString(new Date(new Date().setDate(date.getDate() - 1)))} - {dateString(date)})
79
+ </small>
80
+ ),
81
+ 2: (
82
+ <small className="pf-u-color-200">
83
+ ({dateString(new Date(new Date().setDate(date.getDate() - 7)))} - {dateString(date)})
84
+ </small>
85
+ ),
86
+ 3: (
87
+ <small className="pf-u-color-200">
88
+ ({dateString(new Date(new Date().setDate(date.getDate() - 14)))} - {dateString(date)})
89
+ </small>
90
+ )
91
+ };
92
+
93
+ const toggle = (
94
+ <MenuToggle ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen} style={{ minWidth: '250px' }}>
95
+ <span style={{ verticalAlign: 'middle', marginRight: '8px' }}>{toggleText[selected]}</span>
96
+ {dateText[selected]}
97
+ </MenuToggle>
98
+ );
99
+ const menu = (
100
+ // eslint-disable-next-line no-console
101
+ <Menu ref={menuRef} onSelect={(_ev, itemId) => setSelected(itemId as number)} selected={selected}>
102
+ <MenuContent>
103
+ <MenuList>
104
+ <MenuItem itemId={0}>Today</MenuItem>
105
+ <MenuItem itemId={1}>Yesterday</MenuItem>
106
+ <MenuItem itemId={2}>Last 7 days</MenuItem>
107
+ <MenuItem itemId={3}>Last 14 days</MenuItem>
108
+ </MenuList>
109
+ </MenuContent>
110
+ </Menu>
111
+ );
112
+ return <Popper trigger={toggle} popper={menu} isVisible={isOpen} popperMatchesTriggerWidth={false} />;
113
+ };
@@ -0,0 +1,16 @@
1
+ ---
2
+ id: File upload - multiple
3
+ section: components
4
+ beta: true
5
+ ---
6
+
7
+ import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';
8
+
9
+ ## Demos
10
+
11
+ ### Rejected file handling
12
+
13
+ This demo shows how to add a modal to alert users that the file(s) they attempted to drop was of a non-acceptable type.
14
+
15
+ ```ts file="./examples/MultipleFileUpload/MultipleFileUploadRejectedFile.tsx"
16
+ ```
@@ -13,11 +13,11 @@ import { Tile } from '@patternfly/react-core';
13
13
 
14
14
  const TileSingleSelect: React.FunctionComponent = () => {
15
15
  const [selectedId, setSelectedId] = React.useState<string>('');
16
-
17
- const onSelect = (event: React.MouseEvent) => {``
16
+
17
+ const onSelect = (event: React.MouseEvent) => {
18
18
  setSelectedId(event.currentTarget.id);
19
19
  };
20
-
20
+
21
21
  const onKeyDown = (event: React.KeyboardEvent) => {
22
22
  if (event.key === ' ' || event.key === 'Enter') {
23
23
  event.preventDefault();
@@ -26,32 +26,14 @@ const TileSingleSelect: React.FunctionComponent = () => {
26
26
  };
27
27
 
28
28
  return (
29
- <div>
30
- <Tile
31
- title="Tile 1"
32
- id="tile-1"
33
- onClick={onSelect}
34
- onKeyDown={onKeyDown}
35
- isSelected={selectedId === 'tile-1'}
36
- />
37
- <Tile
38
- title="Tile 2"
39
- id="tile-2"
40
- onClick={onSelect}
41
- onKeyDown={onKeyDown}
42
- isSelected={selectedId === 'tile-2'}
43
- />
44
- <Tile title="Tile 3" id="tile-3" isDisabled />
45
- <Tile
46
- title="Tile 4"
47
- id="tile-4"
48
- onClick={onSelect}
49
- onKeyDown={onKeyDown}
50
- isSelected={selectedId === 'tile-4'}
51
- />
29
+ <div role="listbox" aria-label="Single selection tiles">
30
+ <Tile title="Tile 1" id="tile-1" onClick={onSelect} onKeyDown={onKeyDown} isSelected={selectedId === 'tile-1'} />
31
+ <Tile title="Tile 2" id="tile-2" onClick={onSelect} onKeyDown={onKeyDown} isSelected={selectedId === 'tile-2'} />
32
+ <Tile title="Tile 3" id="tile-3" isDisabled isSelected={selectedId === 'tile-3'} />
33
+ <Tile title="Tile 4" id="tile-4" onClick={onSelect} onKeyDown={onKeyDown} isSelected={selectedId === 'tile-4'} />
52
34
  </div>
53
35
  );
54
- }
36
+ };
55
37
  ```
56
38
 
57
39
  ### Tiles with multiple selection
@@ -62,18 +44,18 @@ import { Tile } from '@patternfly/react-core';
62
44
 
63
45
  const TileMultiSelect: React.FunctionComponent = () => {
64
46
  const [selectedIds, setSelectedIds] = React.useState<string[]>([]);
65
-
47
+
66
48
  const onSelect = (event: React.MouseEvent | React.KeyboardEvent) => {
67
49
  const targetId = event.currentTarget.id;
68
50
  setSelectedIds(prevSelectedIds => {
69
- const newSelectedIds = prevSelectedIds.includes(targetId) ?
70
- prevSelectedIds.filter(id => id !== targetId) :
71
- [...prevSelectedIds, targetId]
51
+ const newSelectedIds = prevSelectedIds.includes(targetId)
52
+ ? prevSelectedIds.filter(id => id !== targetId)
53
+ : [...prevSelectedIds, targetId];
72
54
  console.log('tile selections: ', newSelectedIds);
73
55
  return newSelectedIds;
74
56
  });
75
57
  };
76
-
58
+
77
59
  const onKeyDown = (event: React.KeyboardEvent) => {
78
60
  console.log(event.key);
79
61
  if (event.key === ' ' || event.key === 'Enter') {
@@ -81,9 +63,9 @@ const TileMultiSelect: React.FunctionComponent = () => {
81
63
  onSelect(event);
82
64
  }
83
65
  };
84
-
66
+
85
67
  return (
86
- <div>
68
+ <div role="listbox" aria-multiselectable={true} aria-label="Multiselectable tiles">
87
69
  <Tile
88
70
  title="Tile 1"
89
71
  id="tile-1"
@@ -108,5 +90,5 @@ const TileMultiSelect: React.FunctionComponent = () => {
108
90
  />
109
91
  </div>
110
92
  );
111
- }
93
+ };
112
94
  ```
@@ -1,20 +1,13 @@
1
1
  import React from 'react';
2
2
  import {
3
3
  MultipleFileUpload,
4
- MultipleFileUploadInfo,
5
4
  MultipleFileUploadMain,
6
- MultipleFileUploadTitle,
7
- MultipleFileUploadTitleIcon,
8
- MultipleFileUploadTitleText,
9
- MultipleFileUploadTitleTextSeparator,
10
- MultipleFileUploadButton,
11
5
  MultipleFileUploadStatus,
12
6
  MultipleFileUploadStatusItem,
13
- Modal
7
+ Modal,
8
+ Checkbox
14
9
  } from '@patternfly/react-core';
15
- import InProgressIcon from '@patternfly/react-icons/dist/esm/icons/in-progress-icon';
16
- import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
17
- import TimesCircleIcon from '@patternfly/react-icons/dist/esm/icons/times-circle-icon';
10
+ import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';
18
11
 
19
12
  interface readFile {
20
13
  fileName: string;
@@ -24,9 +17,11 @@ interface readFile {
24
17
  }
25
18
 
26
19
  export const MultipleFileUploadBasic: React.FunctionComponent = () => {
20
+ const [isHorizontal, setIsHorizontal] = React.useState(false);
27
21
  const [currentFiles, setCurrentFiles] = React.useState<File[]>([]);
28
22
  const [readFileData, setReadFileData] = React.useState<readFile[]>([]);
29
23
  const [showStatus, setShowStatus] = React.useState(false);
24
+ const [statusIcon, setStatusIcon] = React.useState('inProgress');
30
25
  const [modalText, setModalText] = React.useState('');
31
26
 
32
27
  // only show the status component once a file has been uploaded, but keep the status list component itself even if all files are removed
@@ -35,17 +30,15 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
35
30
  }
36
31
 
37
32
  // determine the icon that should be shown for the overall status list
38
- const getStatusIcon = () => {
33
+ React.useEffect(() => {
39
34
  if (readFileData.length < currentFiles.length) {
40
- return <InProgressIcon />;
41
- }
42
-
43
- if (readFileData.every(file => file.loadResult === 'success')) {
44
- return <CheckCircleIcon />;
35
+ setStatusIcon('inProgress');
36
+ } else if (readFileData.every(file => file.loadResult === 'success')) {
37
+ setStatusIcon('success');
38
+ } else {
39
+ setStatusIcon('danger');
45
40
  }
46
-
47
- return <TimesCircleIcon />;
48
- };
41
+ }, [readFileData, currentFiles]);
49
42
 
50
43
  // remove files from both state arrays based on their name
51
44
  const removeFiles = (namesOfFilesToRemove: string[]) => {
@@ -101,51 +94,54 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
101
94
  const successfullyReadFileCount = readFileData.filter(fileData => fileData.loadResult === 'success').length;
102
95
 
103
96
  return (
104
- <MultipleFileUpload
105
- onFileDrop={handleFileDrop}
106
- dropzoneProps={{
107
- accept: 'image/jpeg, application/msword, application/pdf, image/png',
108
- onDropRejected: handleDropRejected
109
- }}
110
- isHorizontal
111
- >
112
- <MultipleFileUploadMain>
113
- <MultipleFileUploadTitle>
114
- <MultipleFileUploadTitleIcon />
115
- <MultipleFileUploadTitleText>
116
- Drag and drop files here
117
- <MultipleFileUploadTitleTextSeparator>or</MultipleFileUploadTitleTextSeparator>
118
- </MultipleFileUploadTitleText>
119
- </MultipleFileUploadTitle>
120
- <MultipleFileUploadButton />
121
- <MultipleFileUploadInfo>Accepted file types: JPEG, Doc, PDF, PNG</MultipleFileUploadInfo>
122
- </MultipleFileUploadMain>
123
- {showStatus && (
124
- <MultipleFileUploadStatus
125
- statusToggleText={`${successfullyReadFileCount} of ${currentFiles.length} files uploaded`}
126
- statusToggleIcon={getStatusIcon()}
127
- >
128
- {currentFiles.map(file => (
129
- <MultipleFileUploadStatusItem
130
- file={file}
131
- key={file.name}
132
- onClearClick={() => removeFiles([file.name])}
133
- onReadSuccess={handleReadSuccess}
134
- onReadFail={handleReadFail}
135
- />
136
- ))}
137
- </MultipleFileUploadStatus>
138
- )}
139
- <Modal
140
- isOpen={!!modalText}
141
- title="Unsupported file"
142
- titleIconVariant="warning"
143
- showClose
144
- aria-label="unsupported file upload attempted"
145
- onClose={() => setModalText('')}
97
+ <>
98
+ <MultipleFileUpload
99
+ onFileDrop={handleFileDrop}
100
+ dropzoneProps={{
101
+ accept: 'image/jpeg, application/msword, application/pdf, image/png',
102
+ onDropRejected: handleDropRejected
103
+ }}
104
+ isHorizontal={isHorizontal}
146
105
  >
147
- {modalText}
148
- </Modal>
149
- </MultipleFileUpload>
106
+ <MultipleFileUploadMain
107
+ titleIcon={<UploadIcon />}
108
+ titleText="Drag and drop files here"
109
+ titleTextSeparator="or"
110
+ infoText="Accepted file types: JPEG, Doc, PDF, PNG"
111
+ />
112
+ {showStatus && (
113
+ <MultipleFileUploadStatus
114
+ statusToggleText={`${successfullyReadFileCount} of ${currentFiles.length} files uploaded`}
115
+ statusToggleIcon={statusIcon}
116
+ >
117
+ {currentFiles.map(file => (
118
+ <MultipleFileUploadStatusItem
119
+ file={file}
120
+ key={file.name}
121
+ onClearClick={() => removeFiles([file.name])}
122
+ onReadSuccess={handleReadSuccess}
123
+ onReadFail={handleReadFail}
124
+ />
125
+ ))}
126
+ </MultipleFileUploadStatus>
127
+ )}
128
+ <Modal
129
+ isOpen={!!modalText}
130
+ title="Unsupported file"
131
+ titleIconVariant="warning"
132
+ showClose
133
+ aria-label="unsupported file upload attempted"
134
+ onClose={() => setModalText('')}
135
+ >
136
+ {modalText}
137
+ </Modal>
138
+ </MultipleFileUpload>
139
+ <Checkbox
140
+ id="horizontal-checkbox"
141
+ label="Show as horizontal"
142
+ isChecked={isHorizontal}
143
+ onChange={() => setIsHorizontal(!isHorizontal)}
144
+ />
145
+ </>
150
146
  );
151
147
  };