@patternfly/react-core 4.198.13 → 4.198.16

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 (66) hide show
  1. package/CHANGELOG.md +27 -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/NotificationDrawer/NotificationDrawer.d.ts +3 -1
  9. package/dist/esm/components/NotificationDrawer/NotificationDrawer.d.ts.map +1 -1
  10. package/dist/esm/components/NotificationDrawer/NotificationDrawer.js +4 -3
  11. package/dist/esm/components/NotificationDrawer/NotificationDrawer.js.map +1 -1
  12. package/dist/esm/components/Tabs/Tab.d.ts +3 -1
  13. package/dist/esm/components/Tabs/Tab.d.ts.map +1 -1
  14. package/dist/esm/components/Tabs/Tab.js +4 -3
  15. package/dist/esm/components/Tabs/Tab.js.map +1 -1
  16. package/dist/esm/components/Tabs/TabButton.d.ts +2 -0
  17. package/dist/esm/components/Tabs/TabButton.d.ts.map +1 -1
  18. package/dist/esm/components/Tabs/TabButton.js +2 -2
  19. package/dist/esm/components/Tabs/TabButton.js.map +1 -1
  20. package/dist/esm/components/Tile/Tile.d.ts.map +1 -1
  21. package/dist/esm/components/Tile/Tile.js +1 -1
  22. package/dist/esm/components/Tile/Tile.js.map +1 -1
  23. package/dist/js/components/Form/FormFieldGroup.d.ts +1 -1
  24. package/dist/js/components/Form/FormFieldGroupExpandable.d.ts +2 -2
  25. package/dist/js/components/Form/FormFieldGroupHeader.d.ts +3 -1
  26. package/dist/js/components/Form/FormFieldGroupHeader.d.ts.map +1 -1
  27. package/dist/js/components/Form/FormFieldGroupHeader.js.map +1 -1
  28. package/dist/js/components/Form/InternalFormFieldGroup.d.ts +2 -2
  29. package/dist/js/components/NotificationDrawer/NotificationDrawer.d.ts +3 -1
  30. package/dist/js/components/NotificationDrawer/NotificationDrawer.d.ts.map +1 -1
  31. package/dist/js/components/NotificationDrawer/NotificationDrawer.js +4 -4
  32. package/dist/js/components/NotificationDrawer/NotificationDrawer.js.map +1 -1
  33. package/dist/js/components/Tabs/Tab.d.ts +3 -1
  34. package/dist/js/components/Tabs/Tab.d.ts.map +1 -1
  35. package/dist/js/components/Tabs/Tab.js +4 -3
  36. package/dist/js/components/Tabs/Tab.js.map +1 -1
  37. package/dist/js/components/Tabs/TabButton.d.ts +2 -0
  38. package/dist/js/components/Tabs/TabButton.d.ts.map +1 -1
  39. package/dist/js/components/Tabs/TabButton.js +2 -2
  40. package/dist/js/components/Tabs/TabButton.js.map +1 -1
  41. package/dist/js/components/Tile/Tile.d.ts.map +1 -1
  42. package/dist/js/components/Tile/Tile.js +1 -1
  43. package/dist/js/components/Tile/Tile.js.map +1 -1
  44. package/dist/umd/react-core.js +10 -9
  45. package/dist/umd/react-core.min.css +72 -72
  46. package/dist/umd/react-core.min.js +1 -1
  47. package/package.json +5 -5
  48. package/src/components/Form/FormFieldGroup.tsx +1 -1
  49. package/src/components/Form/FormFieldGroupExpandable.tsx +2 -2
  50. package/src/components/Form/FormFieldGroupHeader.tsx +5 -1
  51. package/src/components/Form/InternalFormFieldGroup.tsx +2 -2
  52. package/src/components/Form/__tests__/FormFieldGroup.test.tsx +1 -1
  53. package/src/components/Form/__tests__/__snapshots__/FormFieldGroup.test.tsx.snap +1 -1
  54. package/src/components/Form/examples/Form.md +1 -1
  55. package/src/components/NotificationDrawer/NotificationDrawer.tsx +8 -2
  56. package/src/components/NotificationDrawer/__tests__/__snapshots__/NotificationDrawer.test.tsx.snap +1 -1
  57. package/src/components/Tabs/Tab.tsx +31 -24
  58. package/src/components/Tabs/TabButton.tsx +5 -1
  59. package/src/components/Tabs/examples/Tabs.md +527 -256
  60. package/src/components/Tile/Tile.tsx +3 -0
  61. package/src/components/Tile/__tests__/__snapshots__/Tile.test.tsx.snap +9 -0
  62. package/src/components/Tile/examples/Tile.md +41 -39
  63. package/src/demos/ComposableMenu/ComposableMenu.md +4 -9
  64. package/src/demos/ComposableMenu/examples/ComposableDateSelect.tsx +113 -0
  65. package/src/demos/NotificationDrawer/NotificationDrawer.md +26 -2
  66. package/src/demos/TileDemo.md +17 -35
@@ -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
+ };
@@ -16,6 +16,11 @@ import AttentionBellIcon from '@patternfly/react-icons/dist/esm/icons/attention-
16
16
 
17
17
  ## Demos
18
18
 
19
+ - Focus must be manually managed when the NotificationDrawer component is opened:
20
+
21
+ 1. Create a React `ref` and pass it into the NotificationDrawer component's `ref` attribute
22
+ 2. Pass in a function to the `onNotificationDrawerExpand` prop of the Page component that will place focus on the first interact-able element inside the NotificationDrawer component via the previously created `ref`
23
+
19
24
  ### Basic
20
25
 
21
26
  ```js isFullscreen
@@ -87,6 +92,7 @@ import { Table, TableHeader, TableBody } from '@patternfly/react-table';
87
92
  class BasicNotificationDrawer extends React.Component {
88
93
  constructor(props) {
89
94
  super(props);
95
+ this.drawerRef = React.createRef();
90
96
  this.state = {
91
97
  isDropdownOpen: false,
92
98
  isKebabDropdownOpen: false,
@@ -179,6 +185,11 @@ class BasicNotificationDrawer extends React.Component {
179
185
  showNotifications: showNotifications
180
186
  });
181
187
  };
188
+
189
+ this.focusDrawer = () => {
190
+ const firstTabbableItem = this.drawerRef.current.querySelector('a, button');
191
+ firstTabbableItem.focus();
192
+ };
182
193
  }
183
194
 
184
195
  render() {
@@ -359,7 +370,7 @@ class BasicNotificationDrawer extends React.Component {
359
370
  ];
360
371
 
361
372
  const notificationDrawer = (
362
- <NotificationDrawer>
373
+ <NotificationDrawer ref={this.drawerRef}>
363
374
  <NotificationDrawerHeader count={this.getNumberUnread()} onClose={this.onCloseNotificationDrawer}>
364
375
  <Dropdown
365
376
  onSelect={this.onSelect}
@@ -499,6 +510,7 @@ class BasicNotificationDrawer extends React.Component {
499
510
  sidebar={Sidebar}
500
511
  isManagedSidebar
501
512
  notificationDrawer={notificationDrawer}
513
+ onNotificationDrawerExpand={this.focusDrawer}
502
514
  isNotificationDrawerExpanded={isDrawerExpanded}
503
515
  skipToContent={PageSkipToContent}
504
516
  breadcrumb={PageBreadcrumb}
@@ -525,6 +537,8 @@ class BasicNotificationDrawer extends React.Component {
525
537
 
526
538
  ### Grouped
527
539
 
540
+ When using the NotificationDrawerGroupList and related components, the function that is passed in to the `onNotificationDrawerExpand` prop on the Page component must also ensure the NotificationDrawer component only receives focus when it is initially opened. Otherwise any time a drawer group item is opened the NotificationDrawer component will receive focus, which would be unexpected behavior for users.
541
+
528
542
  ```js isFullscreen
529
543
  import React from 'react';
530
544
  import {
@@ -597,6 +611,7 @@ import { Table, TableHeader, TableBody } from '@patternfly/react-table';
597
611
  class GroupedNotificationDrawer extends React.Component {
598
612
  constructor(props) {
599
613
  super(props);
614
+ this.drawerRef = React.createRef();
600
615
  this.state = {
601
616
  isDropdownOpen: false,
602
617
  isKebabDropdownOpen: false,
@@ -736,6 +751,14 @@ class GroupedNotificationDrawer extends React.Component {
736
751
  thirdDrawerGroupExpanded: value
737
752
  });
738
753
  };
754
+
755
+ this.focusDrawer = () => {
756
+ // Prevent the NotificationDrawer from receiving focus if a drawer group item is opened
757
+ if (!document.activeElement.closest(`.${this.drawerRef.current.className}`)) {
758
+ const firstTabbableItem = this.drawerRef.current.querySelector('a, button');
759
+ firstTabbableItem.focus();
760
+ }
761
+ };
739
762
  }
740
763
 
741
764
  render() {
@@ -919,7 +942,7 @@ class GroupedNotificationDrawer extends React.Component {
919
942
  ];
920
943
 
921
944
  const notificationDrawer = (
922
- <NotificationDrawer>
945
+ <NotificationDrawer ref={this.drawerRef}>
923
946
  <NotificationDrawerHeader count={this.getNumberUnread()} onClose={this.onCloseNotificationDrawer}>
924
947
  <Dropdown
925
948
  onSelect={this.onSelect}
@@ -1214,6 +1237,7 @@ class GroupedNotificationDrawer extends React.Component {
1214
1237
  isManagedSidebar
1215
1238
  notificationDrawer={notificationDrawer}
1216
1239
  isNotificationDrawerExpanded={isDrawerExpanded}
1240
+ onNotificationDrawerExpand={this.focusDrawer}
1217
1241
  skipToContent={PageSkipToContent}
1218
1242
  breadcrumb={PageBreadcrumb}
1219
1243
  mainContainerId={pageId}
@@ -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
  ```