@patternfly/react-table 5.3.0-prerelease.2 → 5.3.0-prerelease.20

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 (103) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/Table/package.json +1 -1
  5. package/dist/dynamic/components/Table/utils/decorators/package.json +1 -1
  6. package/dist/dynamic/components/Table/utils/package.json +1 -1
  7. package/dist/dynamic/deprecated/components/Table/base/package.json +1 -1
  8. package/dist/dynamic/deprecated/components/Table/package.json +1 -1
  9. package/dist/dynamic/deprecated/components/package.json +1 -1
  10. package/dist/esm/components/Table/ActionsColumn.d.ts +2 -0
  11. package/dist/esm/components/Table/ActionsColumn.d.ts.map +1 -1
  12. package/dist/esm/components/Table/ActionsColumn.js +4 -4
  13. package/dist/esm/components/Table/ActionsColumn.js.map +1 -1
  14. package/dist/esm/components/Table/TableTypes.d.ts +2 -0
  15. package/dist/esm/components/Table/TableTypes.d.ts.map +1 -1
  16. package/dist/esm/components/Table/Th.d.ts +8 -0
  17. package/dist/esm/components/Table/Th.d.ts.map +1 -1
  18. package/dist/esm/components/Table/Th.js +7 -3
  19. package/dist/esm/components/Table/Th.js.map +1 -1
  20. package/dist/esm/components/Table/utils/decorators/selectable.js +1 -1
  21. package/dist/esm/components/Table/utils/decorators/selectable.js.map +1 -1
  22. package/dist/esm/demos/DashboardHeader.d.ts +8 -0
  23. package/dist/esm/demos/DashboardHeader.d.ts.map +1 -0
  24. package/dist/esm/demos/DashboardHeader.js +77 -0
  25. package/dist/esm/demos/DashboardHeader.js.map +1 -0
  26. package/dist/esm/demos/DashboardWrapper.d.ts +85 -0
  27. package/dist/esm/demos/DashboardWrapper.d.ts.map +1 -0
  28. package/dist/esm/demos/DashboardWrapper.js +41 -0
  29. package/dist/esm/demos/DashboardWrapper.js.map +1 -0
  30. package/dist/esm/demos/index.d.ts +2 -0
  31. package/dist/esm/demos/index.d.ts.map +1 -1
  32. package/dist/esm/demos/index.js +2 -0
  33. package/dist/esm/demos/index.js.map +1 -1
  34. package/dist/esm/index.d.ts +0 -1
  35. package/dist/esm/index.d.ts.map +1 -1
  36. package/dist/esm/index.js +0 -1
  37. package/dist/esm/index.js.map +1 -1
  38. package/dist/js/components/Table/ActionsColumn.d.ts +2 -0
  39. package/dist/js/components/Table/ActionsColumn.d.ts.map +1 -1
  40. package/dist/js/components/Table/ActionsColumn.js +4 -4
  41. package/dist/js/components/Table/ActionsColumn.js.map +1 -1
  42. package/dist/js/components/Table/TableTypes.d.ts +2 -0
  43. package/dist/js/components/Table/TableTypes.d.ts.map +1 -1
  44. package/dist/js/components/Table/Th.d.ts +8 -0
  45. package/dist/js/components/Table/Th.d.ts.map +1 -1
  46. package/dist/js/components/Table/Th.js +7 -3
  47. package/dist/js/components/Table/Th.js.map +1 -1
  48. package/dist/js/components/Table/utils/decorators/selectable.js +1 -1
  49. package/dist/js/components/Table/utils/decorators/selectable.js.map +1 -1
  50. package/dist/js/demos/DashboardHeader.d.ts +8 -0
  51. package/dist/js/demos/DashboardHeader.d.ts.map +1 -0
  52. package/dist/js/demos/DashboardHeader.js +82 -0
  53. package/dist/js/demos/DashboardHeader.js.map +1 -0
  54. package/dist/js/demos/DashboardWrapper.d.ts +85 -0
  55. package/dist/js/demos/DashboardWrapper.d.ts.map +1 -0
  56. package/dist/js/demos/DashboardWrapper.js +45 -0
  57. package/dist/js/demos/DashboardWrapper.js.map +1 -0
  58. package/dist/js/demos/index.d.ts +2 -0
  59. package/dist/js/demos/index.d.ts.map +1 -1
  60. package/dist/js/demos/index.js +2 -0
  61. package/dist/js/demos/index.js.map +1 -1
  62. package/dist/js/index.d.ts +0 -1
  63. package/dist/js/index.d.ts.map +1 -1
  64. package/dist/js/index.js +0 -1
  65. package/dist/js/index.js.map +1 -1
  66. package/dist/umd/react-table.min.js +3 -3
  67. package/package.json +6 -6
  68. package/src/components/Table/ActionsColumn.tsx +33 -28
  69. package/src/components/Table/TableTypes.tsx +2 -0
  70. package/src/components/Table/Th.tsx +20 -2
  71. package/src/components/Table/__tests__/Th.test.tsx +31 -0
  72. package/src/components/Table/examples/TableActions.tsx +2 -2
  73. package/src/components/Table/examples/TableActionsOverflow.tsx +1 -1
  74. package/src/components/Table/examples/TableCompoundExpandable.tsx +1 -1
  75. package/src/components/Table/examples/TableDraggable.tsx +2 -2
  76. package/src/components/Table/examples/TableExpandable.tsx +1 -1
  77. package/src/components/Table/examples/TableNestedExpandable.tsx +1 -1
  78. package/src/components/Table/examples/TableNestedStickyHeader.tsx +3 -0
  79. package/src/components/Table/examples/TableNestedTableExpandable.tsx +1 -1
  80. package/src/components/Table/examples/TableSelectableRadio.tsx +1 -1
  81. package/src/components/Table/examples/TableStripedExpandable.tsx +1 -1
  82. package/src/components/Table/utils/decorators/selectable.tsx +1 -1
  83. package/src/demos/DashboardHeader.tsx +198 -0
  84. package/src/demos/DashboardWrapper.tsx +199 -0
  85. package/src/demos/Table.md +29 -5
  86. package/src/demos/assets/avatarImg.svg +18 -0
  87. package/src/demos/assets/index.d.ts +4 -0
  88. package/src/demos/assets/pf-logo.svg +28 -0
  89. package/src/demos/examples/TableBulkSelect.tsx +2 -2
  90. package/src/demos/examples/TableColumnManagement.tsx +1 -1
  91. package/src/demos/examples/TableCompact.tsx +1 -1
  92. package/src/demos/examples/TableCompoundExpansion.tsx +4 -3
  93. package/src/demos/examples/TableEmptyStateDefault.tsx +1 -1
  94. package/src/demos/examples/TableEmptyStateError.tsx +1 -1
  95. package/src/demos/examples/TableEmptyStateLoading.tsx +1 -1
  96. package/src/demos/examples/TableExpandCollapseAll.tsx +1 -1
  97. package/src/demos/examples/TableSortableResponsive.tsx +2 -2
  98. package/src/demos/examples/TableStaticBottomPagination.tsx +1 -1
  99. package/src/demos/examples/TableStickyFirstColumn.tsx +1 -1
  100. package/src/demos/examples/TableStickyHeader.tsx +1 -1
  101. package/src/demos/index.ts +2 -0
  102. package/src/deprecated/components/Table/__tests__/__snapshots__/Table.test.tsx.snap +10 -4
  103. package/src/index.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-table",
3
- "version": "5.3.0-prerelease.2",
3
+ "version": "5.3.0-prerelease.20",
4
4
  "description": "This library provides a set of React table components for use with the PatternFly 4",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -39,10 +39,10 @@
39
39
  "subpaths": "node ../../scripts/exportSubpaths.js --config subpaths.config.json"
40
40
  },
41
41
  "dependencies": {
42
- "@patternfly/react-core": "^5.3.0-prerelease.2",
43
- "@patternfly/react-icons": "^5.3.0-prerelease.0",
44
- "@patternfly/react-styles": "^5.3.0-prerelease.0",
45
- "@patternfly/react-tokens": "^5.3.0-prerelease.0",
42
+ "@patternfly/react-core": "^5.3.0-prerelease.19",
43
+ "@patternfly/react-icons": "^5.3.0-prerelease.3",
44
+ "@patternfly/react-styles": "^5.3.0-prerelease.3",
45
+ "@patternfly/react-tokens": "^5.3.0-prerelease.3",
46
46
  "lodash": "^4.17.19",
47
47
  "tslib": "^2.5.0"
48
48
  },
@@ -54,5 +54,5 @@
54
54
  "react": "^17 || ^18",
55
55
  "react-dom": "^17 || ^18"
56
56
  },
57
- "gitHead": "08a6b1aaaf6769b657ed674f4bb741b530265c7d"
57
+ "gitHead": "5fc4f385c49deaa2f2835879132bd33869ff73d1"
58
58
  }
@@ -30,6 +30,8 @@ export interface ActionsColumnProps extends Omit<React.HTMLProps<HTMLElement>, '
30
30
  innerRef?: React.Ref<any>;
31
31
  /** Ref to forward to the first item in the popup menu */
32
32
  firstActionItemRef?: React.Ref<HTMLButtonElement>;
33
+ /** Flag indicating that the dropdown's onOpenChange callback should not be called. */
34
+ isOnOpenChangeDisabled?: boolean;
33
35
  }
34
36
 
35
37
  const ActionsColumnBase: React.FunctionComponent<ActionsColumnProps> = ({
@@ -44,6 +46,7 @@ const ActionsColumnBase: React.FunctionComponent<ActionsColumnProps> = ({
44
46
  },
45
47
  innerRef,
46
48
  firstActionItemRef,
49
+ isOnOpenChangeDisabled = false,
47
50
  ...props
48
51
  }: ActionsColumnProps) => {
49
52
  const [isOpen, setIsOpen] = React.useState(false);
@@ -91,7 +94,7 @@ const ActionsColumnBase: React.FunctionComponent<ActionsColumnProps> = ({
91
94
 
92
95
  <Dropdown
93
96
  isOpen={isOpen}
94
- onOpenChange={(isOpen: boolean) => setIsOpen(isOpen)}
97
+ onOpenChange={!isOnOpenChangeDisabled ? (isOpen: boolean) => setIsOpen(isOpen) : undefined}
95
98
  toggle={(toggleRef: any) =>
96
99
  actionsToggle ? (
97
100
  actionsToggle({ onToggle, isOpen, isDisabled, toggleRef })
@@ -116,35 +119,37 @@ const ActionsColumnBase: React.FunctionComponent<ActionsColumnProps> = ({
116
119
  <DropdownList>
117
120
  {items
118
121
  .filter((item) => !item.isOutsideDropdown)
119
- .map(({ title, itemKey, onClick, tooltipProps, isSeparator, ...props }, index) => {
120
- if (isSeparator) {
121
- return <Divider key={itemKey || index} data-key={itemKey || index} />;
122
- }
123
- const item = (
124
- <DropdownItem
125
- onClick={(event: any) => {
126
- onActionClick(event, onClick);
127
- onToggle();
128
- }}
129
- {...props}
130
- key={itemKey || index}
131
- data-key={itemKey || index}
132
- ref={index === 0 ? firstActionItemRef : undefined}
133
- >
134
- {title}
135
- </DropdownItem>
136
- );
137
-
138
- if (tooltipProps?.content) {
139
- return (
140
- <Tooltip key={itemKey || index} {...tooltipProps}>
141
- {item}
142
- </Tooltip>
122
+ .map(
123
+ ({ title, itemKey, onClick, tooltipProps, isSeparator, shouldCloseOnClick = true, ...props }, index) => {
124
+ if (isSeparator) {
125
+ return <Divider key={itemKey || index} data-key={itemKey || index} />;
126
+ }
127
+ const item = (
128
+ <DropdownItem
129
+ onClick={(event: any) => {
130
+ onActionClick(event, onClick);
131
+ shouldCloseOnClick && onToggle();
132
+ }}
133
+ {...props}
134
+ key={itemKey || index}
135
+ data-key={itemKey || index}
136
+ ref={index === 0 ? firstActionItemRef : undefined}
137
+ >
138
+ {title}
139
+ </DropdownItem>
143
140
  );
144
- } else {
145
- return item;
141
+
142
+ if (tooltipProps?.content) {
143
+ return (
144
+ <Tooltip key={itemKey || index} {...tooltipProps}>
145
+ {item}
146
+ </Tooltip>
147
+ );
148
+ } else {
149
+ return item;
150
+ }
146
151
  }
147
- })}
152
+ )}
148
153
  </DropdownList>
149
154
  </Dropdown>
150
155
  </React.Fragment>
@@ -166,6 +166,8 @@ export interface IAction extends Omit<DropdownItemProps, 'title' | 'onClick'>, P
166
166
  onClick?: (event: React.MouseEvent, rowIndex: number, rowData: IRowData, extraData: IExtraData) => void;
167
167
  /** Flag indicating this action should be placed outside the actions menu, beside the toggle */
168
168
  isOutsideDropdown?: boolean;
169
+ /** Flag indicating whether the actions dropdown should close after an item is clicked. */
170
+ shouldCloseOnClick?: boolean;
169
171
  }
170
172
 
171
173
  export interface ISeparator extends IAction {
@@ -56,6 +56,14 @@ export interface ThProps
56
56
  stickyRightOffset?: string;
57
57
  /** Indicates the <th> is part of a subheader of a nested header */
58
58
  isSubheader?: boolean;
59
+ /** Visually hidden text accessible only via assistive technologies. This must be passed in if the
60
+ * th is intended to be visually empty, and must be conveyed as a column header text.
61
+ */
62
+ screenReaderText?: string;
63
+ /** Provides an accessible name to the th. This should only be passed in when the th contains only non-text
64
+ * content, such as a "select all" checkbox or "expand all" toggle.
65
+ */
66
+ 'aria-label'?: string;
59
67
  }
60
68
 
61
69
  const ThBase: React.FunctionComponent<ThProps> = ({
@@ -83,8 +91,17 @@ const ThBase: React.FunctionComponent<ThProps> = ({
83
91
  stickyLeftOffset,
84
92
  stickyRightOffset,
85
93
  isSubheader = false,
94
+ screenReaderText,
95
+ 'aria-label': ariaLabel,
86
96
  ...props
87
97
  }: ThProps) => {
98
+ if (!children && !screenReaderText && !ariaLabel) {
99
+ // eslint-disable-next-line no-console
100
+ console.warn(
101
+ 'Th: Table headers must have an accessible name. If the Th is intended to be visually empty, pass in screenReaderText. If the Th contains only non-text, interactive content such as a checkbox or expand toggle, pass in an aria-label.'
102
+ );
103
+ }
104
+
88
105
  const [showTooltip, setShowTooltip] = React.useState(false);
89
106
  const [truncated, setTruncated] = React.useState(false);
90
107
  const cellRef = innerRef ? innerRef : React.createRef();
@@ -188,8 +205,9 @@ const ThBase: React.FunctionComponent<ThProps> = ({
188
205
  onBlur={() => setShowTooltip(false)}
189
206
  data-label={dataLabel}
190
207
  onMouseEnter={tooltip !== null ? onMouseEnter : onMouseEnterProp}
191
- scope={component === 'th' && children ? scope : null}
208
+ scope={component === 'th' ? scope : null}
192
209
  ref={cellRef}
210
+ aria-label={ariaLabel}
193
211
  className={css(
194
212
  styles.tableTh,
195
213
  className,
@@ -212,7 +230,7 @@ const ThBase: React.FunctionComponent<ThProps> = ({
212
230
  } as React.CSSProperties
213
231
  })}
214
232
  >
215
- {transformedChildren}
233
+ {transformedChildren || (screenReaderText && <span className="pf-v5-screen-reader">{screenReaderText}</span>)}
216
234
  </MergedComponent>
217
235
  );
218
236
 
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { Th } from '../Th';
4
+
5
+ test('Does not render with aria-label by default', () => {
6
+ render(<Th />);
7
+ expect(screen.getByRole('columnheader')).not.toHaveAccessibleName();
8
+ });
9
+
10
+ test('Renders with aria-label when passed in', () => {
11
+ render(<Th aria-label="Test" />);
12
+ expect(screen.getByRole('columnheader')).toHaveAccessibleName('Test');
13
+ });
14
+
15
+ test('Does not render with screen reader text by default', () => {
16
+ render(<Th />);
17
+
18
+ expect(screen.getByRole('columnheader')).toBeEmptyDOMElement();
19
+ });
20
+
21
+ test('Does not render with screen reader text when children are passed in', () => {
22
+ render(<Th screenReaderText="Test">Heading label</Th>);
23
+
24
+ expect(screen.getByRole('columnheader')).not.toHaveTextContent('Test');
25
+ });
26
+
27
+ test('Renders with screen reader text when screenReaderText is passed in', () => {
28
+ render(<Th screenReaderText="Test" />);
29
+
30
+ expect(screen.getByRole('columnheader')).toHaveTextContent('Test');
31
+ });
@@ -116,8 +116,8 @@ export const TableActions: React.FunctionComponent = () => {
116
116
  <Th>{columnNames.prs}</Th>
117
117
  <Th>{columnNames.workspaces}</Th>
118
118
  <Th>{columnNames.lastCommit}</Th>
119
- <Td></Td>
120
- <Td></Td>
119
+ <Th screenReaderText="Primary action" />
120
+ <Th screenReaderText="Secondary action" />
121
121
  </Tr>
122
122
  </Thead>
123
123
  <Tbody>
@@ -66,7 +66,7 @@ export const TableActions: React.FunctionComponent = () => {
66
66
  <Th>{columnNames.prs}</Th>
67
67
  <Th>{columnNames.workspaces}</Th>
68
68
  <Th>{columnNames.lastCommit}</Th>
69
- <Td></Td>
69
+ <Th screenReaderText="Actions" />
70
70
  </Tr>
71
71
  </Thead>
72
72
  <Tbody>
@@ -67,7 +67,7 @@ export const TableCompoundExpandable: React.FunctionComponent = () => {
67
67
  <Th>{columnNames.prs}</Th>
68
68
  <Th>{columnNames.workspaces}</Th>
69
69
  <Th>{columnNames.lastCommit}</Th>
70
- <Th />
70
+ <Th screenReaderText="URL" />
71
71
  </Tr>
72
72
  </Thead>
73
73
  {repositories.map((repo: Repository, rowIndex: number) => {
@@ -140,10 +140,10 @@ export const TableDraggable: React.FunctionComponent = () => {
140
140
  ];
141
141
 
142
142
  return (
143
- <Table aria-label="Draggable table" className={isDragging && styles.modifiers.dragOver}>
143
+ <Table aria-label="Draggable table" className={isDragging ? styles.modifiers.dragOver : ''}>
144
144
  <Thead>
145
145
  <Tr>
146
- <Th />
146
+ <Th screenReaderText="Drag and drop" />
147
147
  {columns.map((column, columnIndex) => (
148
148
  <Th key={columnIndex}>{column}</Th>
149
149
  ))}
@@ -120,7 +120,7 @@ export const TableExpandable: React.FunctionComponent = () => {
120
120
  <Table aria-label="Expandable table" variant={isExampleCompact ? 'compact' : undefined}>
121
121
  <Thead>
122
122
  <Tr>
123
- <Th />
123
+ <Th screenReaderText="Row expansion" />
124
124
  <Th width={25}>{columnNames.name}</Th>
125
125
  <Th width={10}>{columnNames.branches}</Th>
126
126
  <Th width={15}>{columnNames.prs}</Th>
@@ -64,7 +64,7 @@ export const TableNestedExpandable: React.FunctionComponent = () => {
64
64
  <Table aria-label="Nested column headers with expandable rows table" gridBreakPoint="">
65
65
  <Thead hasNestedHeader>
66
66
  <Tr>
67
- <Th rowSpan={2} />
67
+ <Th screenReaderText="Row expansion" rowSpan={2} />
68
68
  <Th width={35} rowSpan={2} hasRightBorder>
69
69
  {columnNames.team}
70
70
  </Th>
@@ -154,6 +154,9 @@ export const TableNestedHeaders: React.FunctionComponent = () => {
154
154
  {columnNames.destination}
155
155
  </Th>
156
156
  </Tr>
157
+ {/* TODO: Remove the following Tr once Core updates towards https://github.com/patternfly/patternfly/issues/6272
158
+ are made for the v5 branch. For v6 branch the row can be removed immediately.
159
+ */}
157
160
  <Tr isBorderRow aria-hidden="true">
158
161
  <Td colSpan={9}></Td>
159
162
  </Tr>
@@ -131,7 +131,7 @@ export const TableExpandable: React.FunctionComponent = () => {
131
131
  <Table aria-label="Simple table">
132
132
  <Thead>
133
133
  <Tr>
134
- <Td />
134
+ <Th screenReaderText="Row expansion" />
135
135
  <Th width={20}>{columnNames.name}</Th>
136
136
  <Th>{columnNames.branches}</Th>
137
137
  <Th>{columnNames.prs}</Th>
@@ -35,7 +35,7 @@ export const TableSelectableRadio: React.FunctionComponent = () => {
35
35
  <Table aria-label="Selectable table">
36
36
  <Thead>
37
37
  <Tr>
38
- <Th />
38
+ <Th screenReaderText="Row select" />
39
39
  <Th>{columnNames.name}</Th>
40
40
  <Th>{columnNames.branches}</Th>
41
41
  <Th>{columnNames.prs}</Th>
@@ -115,7 +115,7 @@ export const TableStripedExpandable: React.FunctionComponent = () => {
115
115
  <Table aria-label="Expandable table" variant={isExampleCompact ? 'compact' : undefined} isStriped isExpandable>
116
116
  <Thead>
117
117
  <Tr>
118
- <Th />
118
+ <Th screenReaderText="Row expansion" />
119
119
  <Th width={25}>{columnNames.name}</Th>
120
120
  <Th width={10}>{columnNames.branches}</Th>
121
121
  <Th width={15}>{columnNames.prs}</Th>
@@ -61,7 +61,7 @@ export const selectable: ITransform = (
61
61
 
62
62
  return {
63
63
  className: css(styles.tableCheck),
64
- component: 'td',
64
+ component: rowId !== -1 ? 'td' : 'th',
65
65
  isVisible: !rowData || !rowData.fullWidth,
66
66
  children: (
67
67
  <SelectColumn
@@ -0,0 +1,198 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ Avatar,
4
+ Brand,
5
+ Button,
6
+ ButtonVariant,
7
+ Divider,
8
+ Dropdown,
9
+ DropdownGroup,
10
+ DropdownItem,
11
+ DropdownList,
12
+ Masthead,
13
+ MastheadToggle,
14
+ MastheadMain,
15
+ MastheadBrand,
16
+ MastheadContent,
17
+ MenuToggle,
18
+ Toolbar,
19
+ ToolbarContent,
20
+ ToolbarGroup,
21
+ ToolbarItem,
22
+ PageToggleButton
23
+ } from '@patternfly/react-core';
24
+ import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
25
+ import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
26
+ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
27
+ import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
28
+ import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
29
+ import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
30
+ import imgAvatar from '@patternfly/react-table/src/demos/assets/avatarImg.svg';
31
+ import pfLogo from '@patternfly/react-table/src/demos/assets/pf-logo.svg';
32
+
33
+ interface DashboardHeaderProps {
34
+ /** Render custom notification badge */
35
+ notificationBadge?: React.ReactNode;
36
+ }
37
+
38
+ export const DashboardHeader: React.FC<DashboardHeaderProps> = ({ notificationBadge }) => {
39
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
40
+ const [isKebabDropdownOpen, setIsKebabDropdownOpen] = useState(false);
41
+ const [isFullKebabDropdownOpen, setIsFullKebabDropdownOpen] = useState(false);
42
+
43
+ const kebabDropdownItems = (
44
+ <>
45
+ <DropdownItem>
46
+ <CogIcon /> Settings
47
+ </DropdownItem>
48
+ <DropdownItem>
49
+ <HelpIcon /> Help
50
+ </DropdownItem>
51
+ </>
52
+ );
53
+
54
+ const userDropdownItems = (
55
+ <>
56
+ <DropdownItem key="group 2 profile">My profile</DropdownItem>
57
+ <DropdownItem key="group 2 user">User management</DropdownItem>
58
+ <DropdownItem key="group 2 logout">Logout</DropdownItem>
59
+ </>
60
+ );
61
+
62
+ const onDropdownToggle = () => {
63
+ setIsDropdownOpen(!isDropdownOpen);
64
+ };
65
+
66
+ const onDropdownSelect = () => {
67
+ setIsDropdownOpen(false);
68
+ };
69
+
70
+ const onKebabDropdownToggle = () => {
71
+ setIsKebabDropdownOpen(!isKebabDropdownOpen);
72
+ };
73
+
74
+ const onKebabDropdownSelect = () => {
75
+ setIsKebabDropdownOpen(false);
76
+ };
77
+
78
+ const onFullKebabToggle = () => {
79
+ setIsFullKebabDropdownOpen(!isFullKebabDropdownOpen);
80
+ };
81
+
82
+ const onFullKebabSelect = () => {
83
+ setIsFullKebabDropdownOpen(false);
84
+ };
85
+
86
+ return (
87
+ <Masthead>
88
+ <MastheadToggle>
89
+ <PageToggleButton variant="plain" aria-label="Global navigation">
90
+ <BarsIcon />
91
+ </PageToggleButton>
92
+ </MastheadToggle>
93
+ <MastheadMain>
94
+ <MastheadBrand>
95
+ <Brand src={pfLogo} alt="PatternFly" heights={{ default: '36px' }} />
96
+ </MastheadBrand>
97
+ </MastheadMain>
98
+ <MastheadContent>
99
+ <Toolbar id="toolbar" isFullHeight isStatic>
100
+ <ToolbarContent>
101
+ <ToolbarGroup
102
+ variant="icon-button-group"
103
+ align={{ default: 'alignRight' }}
104
+ spacer={{ default: 'spacerNone', md: 'spacerMd' }}
105
+ >
106
+ {notificationBadge ?? (
107
+ <ToolbarItem>
108
+ <Button
109
+ aria-label="Notifications"
110
+ variant={ButtonVariant.plain}
111
+ icon={<BellIcon />}
112
+ onClick={() => {}}
113
+ />
114
+ </ToolbarItem>
115
+ )}
116
+ <ToolbarGroup variant="icon-button-group" visibility={{ default: 'hidden', lg: 'visible' }}>
117
+ <ToolbarItem>
118
+ <Button aria-label="Settings" variant={ButtonVariant.plain} icon={<CogIcon />} />
119
+ </ToolbarItem>
120
+ <ToolbarItem>
121
+ <Button aria-label="Help" variant={ButtonVariant.plain} icon={<QuestionCircleIcon />} />
122
+ </ToolbarItem>
123
+ </ToolbarGroup>
124
+ <ToolbarItem visibility={{ default: 'hidden', md: 'visible', lg: 'hidden' }}>
125
+ <Dropdown
126
+ isOpen={isKebabDropdownOpen}
127
+ onSelect={onKebabDropdownSelect}
128
+ onOpenChange={setIsKebabDropdownOpen}
129
+ popperProps={{ position: 'right' }}
130
+ toggle={(toggleRef: React.RefObject<any>) => (
131
+ <MenuToggle
132
+ ref={toggleRef}
133
+ isExpanded={isKebabDropdownOpen}
134
+ onClick={onKebabDropdownToggle}
135
+ variant="plain"
136
+ aria-label="Settings and help"
137
+ >
138
+ <EllipsisVIcon aria-hidden="true" />
139
+ </MenuToggle>
140
+ )}
141
+ >
142
+ <DropdownList>{kebabDropdownItems}</DropdownList>
143
+ </Dropdown>
144
+ </ToolbarItem>
145
+ <ToolbarItem visibility={{ md: 'hidden' }}>
146
+ <Dropdown
147
+ isOpen={isFullKebabDropdownOpen}
148
+ onSelect={onFullKebabSelect}
149
+ onOpenChange={setIsFullKebabDropdownOpen}
150
+ popperProps={{ position: 'right' }}
151
+ toggle={(toggleRef: React.RefObject<any>) => (
152
+ <MenuToggle
153
+ ref={toggleRef}
154
+ isExpanded={isFullKebabDropdownOpen}
155
+ onClick={onFullKebabToggle}
156
+ variant="plain"
157
+ aria-label="Toolbar menu"
158
+ >
159
+ <EllipsisVIcon aria-hidden="true" />
160
+ </MenuToggle>
161
+ )}
162
+ >
163
+ <DropdownGroup key="group 2" aria-label="User actions">
164
+ <DropdownList>{userDropdownItems}</DropdownList>
165
+ </DropdownGroup>
166
+ <Divider />
167
+ <DropdownList>{kebabDropdownItems}</DropdownList>
168
+ </Dropdown>
169
+ </ToolbarItem>
170
+ </ToolbarGroup>
171
+ <ToolbarItem visibility={{ default: 'hidden', md: 'visible' }}>
172
+ <Dropdown
173
+ isOpen={isDropdownOpen}
174
+ onSelect={onDropdownSelect}
175
+ onOpenChange={setIsDropdownOpen}
176
+ popperProps={{ position: 'right' }}
177
+ toggle={(toggleRef: React.RefObject<any>) => (
178
+ <MenuToggle
179
+ ref={toggleRef}
180
+ isExpanded={isDropdownOpen}
181
+ onClick={onDropdownToggle}
182
+ icon={<Avatar src={imgAvatar} alt="" />}
183
+ isFullHeight
184
+ >
185
+ Ned Username
186
+ </MenuToggle>
187
+ )}
188
+ >
189
+ <DropdownList>{userDropdownItems}</DropdownList>
190
+ </Dropdown>
191
+ </ToolbarItem>
192
+ </ToolbarContent>
193
+ </Toolbar>
194
+ </MastheadContent>
195
+ </Masthead>
196
+ );
197
+ };
198
+ DashboardHeader.displayName = 'DashboardHeader';