@patternfly/react-table 6.5.0-prerelease.76 → 6.5.0-prerelease.78

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 (50) hide show
  1. package/CHANGELOG.md +10 -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/SelectColumn.d.ts +2 -0
  11. package/dist/esm/components/Table/SelectColumn.d.ts.map +1 -1
  12. package/dist/esm/components/Table/SelectColumn.js +3 -2
  13. package/dist/esm/components/Table/SelectColumn.js.map +1 -1
  14. package/dist/esm/components/Table/TableTypes.d.ts +1 -0
  15. package/dist/esm/components/Table/TableTypes.d.ts.map +1 -1
  16. package/dist/esm/components/Table/Th.d.ts.map +1 -1
  17. package/dist/esm/components/Table/Th.js +2 -1
  18. package/dist/esm/components/Table/Th.js.map +1 -1
  19. package/dist/esm/components/Table/base/types.d.ts +2 -0
  20. package/dist/esm/components/Table/base/types.d.ts.map +1 -1
  21. package/dist/esm/components/Table/base/types.js.map +1 -1
  22. package/dist/esm/components/Table/utils/decorators/selectable.d.ts.map +1 -1
  23. package/dist/esm/components/Table/utils/decorators/selectable.js +2 -2
  24. package/dist/esm/components/Table/utils/decorators/selectable.js.map +1 -1
  25. package/dist/js/components/Table/SelectColumn.d.ts +2 -0
  26. package/dist/js/components/Table/SelectColumn.d.ts.map +1 -1
  27. package/dist/js/components/Table/SelectColumn.js +3 -2
  28. package/dist/js/components/Table/SelectColumn.js.map +1 -1
  29. package/dist/js/components/Table/TableTypes.d.ts +1 -0
  30. package/dist/js/components/Table/TableTypes.d.ts.map +1 -1
  31. package/dist/js/components/Table/Th.d.ts.map +1 -1
  32. package/dist/js/components/Table/Th.js +2 -1
  33. package/dist/js/components/Table/Th.js.map +1 -1
  34. package/dist/js/components/Table/base/types.d.ts +2 -0
  35. package/dist/js/components/Table/base/types.d.ts.map +1 -1
  36. package/dist/js/components/Table/base/types.js.map +1 -1
  37. package/dist/js/components/Table/utils/decorators/selectable.d.ts.map +1 -1
  38. package/dist/js/components/Table/utils/decorators/selectable.js +2 -2
  39. package/dist/js/components/Table/utils/decorators/selectable.js.map +1 -1
  40. package/dist/umd/assets/{output-CIRSItJH.css → output-B2MhvcX2.css} +6332 -6332
  41. package/dist/umd/react-table.min.js +3 -3
  42. package/package.json +3 -3
  43. package/src/components/Table/SelectColumn.tsx +9 -1
  44. package/src/components/Table/TableTypes.tsx +1 -0
  45. package/src/components/Table/Th.tsx +2 -1
  46. package/src/components/Table/__tests__/Th.test.tsx +23 -0
  47. package/src/components/Table/base/types.tsx +2 -0
  48. package/src/components/Table/examples/Table.md +8 -0
  49. package/src/components/Table/examples/TableSelectableIndeterminate.tsx +128 -0
  50. package/src/components/Table/utils/decorators/selectable.tsx +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-table",
3
- "version": "6.5.0-prerelease.76",
3
+ "version": "6.5.0-prerelease.78",
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",
@@ -40,7 +40,7 @@
40
40
  "subpaths": "node ../../scripts/exportSubpaths.mjs --config subpaths.config.json"
41
41
  },
42
42
  "dependencies": {
43
- "@patternfly/react-core": "^6.5.0-prerelease.73",
43
+ "@patternfly/react-core": "^6.5.0-prerelease.74",
44
44
  "@patternfly/react-icons": "^6.5.0-prerelease.34",
45
45
  "@patternfly/react-styles": "^6.5.0-prerelease.24",
46
46
  "@patternfly/react-tokens": "^6.5.0-prerelease.23",
@@ -51,5 +51,5 @@
51
51
  "react": "^17 || ^18 || ^19",
52
52
  "react-dom": "^17 || ^18 || ^19"
53
53
  },
54
- "gitHead": "49cdd4af567d0e44031932738edb6d05ffe68c99"
54
+ "gitHead": "57fe8ee7cc6c44b7af797d6a52a30d19ef59785c"
55
55
  }
@@ -21,6 +21,8 @@ export interface SelectColumnProps {
21
21
  id?: string;
22
22
  /** name for the input element - required by Radio component */
23
23
  name?: string;
24
+ /** Whether the checkbox should be in an indeterminate state */
25
+ isIndeterminate?: boolean;
24
26
  }
25
27
 
26
28
  export const SelectColumn: React.FunctionComponent<SelectColumnProps> = ({
@@ -33,6 +35,7 @@ export const SelectColumn: React.FunctionComponent<SelectColumnProps> = ({
33
35
  tooltipProps,
34
36
  id,
35
37
  name,
38
+ isIndeterminate,
36
39
  ...props
37
40
  }: SelectColumnProps) => {
38
41
  const inputRef = createRef<any>();
@@ -48,10 +51,15 @@ export const SelectColumn: React.FunctionComponent<SelectColumnProps> = ({
48
51
  onChange: handleChange
49
52
  };
50
53
 
54
+ const checkboxProps = {
55
+ ...commonProps,
56
+ ...(isIndeterminate && { isChecked: null })
57
+ };
58
+
51
59
  const content = (
52
60
  <Fragment>
53
61
  {selectVariant === RowSelectVariant.checkbox ? (
54
- <Checkbox {...commonProps} />
62
+ <Checkbox {...checkboxProps} />
55
63
  ) : (
56
64
  <Radio {...commonProps} name={name} />
57
65
  )}
@@ -107,6 +107,7 @@ export interface IColumn {
107
107
  allRowsSelected?: boolean;
108
108
  allRowsExpanded?: boolean;
109
109
  isHeaderSelectDisabled?: boolean;
110
+ isIndeterminate?: boolean;
110
111
  onFavorite?: OnFavorite;
111
112
  favoriteButtonProps?: ButtonProps;
112
113
  variant?: 'compact';
@@ -158,7 +158,8 @@ const ThBase: React.FunctionComponent<ThProps> = ({
158
158
  onSelect: select?.onSelect,
159
159
  selectVariant: 'checkbox',
160
160
  allRowsSelected: select.isSelected,
161
- isHeaderSelectDisabled: !!select.isHeaderSelectDisabled
161
+ isHeaderSelectDisabled: !!select.isHeaderSelectDisabled,
162
+ isIndeterminate: select?.isIndeterminate
162
163
  }
163
164
  },
164
165
  tooltip: tooltip as string,
@@ -54,3 +54,26 @@ test('Additional content renders after children when additionalContent is passed
54
54
  expect(thChildren.item(0)?.textContent).toEqual('Test');
55
55
  expect(thChildren.item(1)?.textContent).toEqual('Extra');
56
56
  });
57
+
58
+ test('Renders checkbox without indeterminate state by default', () => {
59
+ render(<Th select={{ onSelect: jest.fn(), isSelected: false }} aria-label="Select all" />);
60
+
61
+ const checkbox = screen.getByRole('checkbox') as HTMLInputElement;
62
+ expect(checkbox).not.toBeChecked();
63
+ expect(checkbox.indeterminate).toBe(false);
64
+ });
65
+
66
+ test('Renders checkbox with indeterminate state when isIndeterminate is true', () => {
67
+ render(<Th select={{ onSelect: jest.fn(), isSelected: false, isIndeterminate: true }} aria-label="Select all" />);
68
+
69
+ const checkbox = screen.getByRole('checkbox') as HTMLInputElement;
70
+ expect(checkbox.indeterminate).toBe(true);
71
+ });
72
+
73
+ test('Renders checked checkbox when isSelected is true and isIndeterminate is false', () => {
74
+ render(<Th select={{ onSelect: jest.fn(), isSelected: true, isIndeterminate: false }} aria-label="Select all" />);
75
+
76
+ const checkbox = screen.getByRole('checkbox') as HTMLInputElement;
77
+ expect(checkbox).toBeChecked();
78
+ expect(checkbox.indeterminate).toBe(false);
79
+ });
@@ -180,6 +180,8 @@ export interface ThSelectType {
180
180
  onSelect?: OnSelect;
181
181
  /** Whether the cell is selected */
182
182
  isSelected: boolean;
183
+ /** Whether the select checkbox should be in an indeterminate state (some items selected) */
184
+ isIndeterminate?: boolean;
183
185
  /** Flag indicating the select checkbox in the th is disabled */
184
186
  isHeaderSelectDisabled?: boolean;
185
187
  /** Whether to disable the selection */
@@ -158,6 +158,14 @@ checking checkboxes will check intermediate rows' checkboxes.
158
158
 
159
159
  ```
160
160
 
161
+ ### Selectable with indeterminate state
162
+
163
+ To indicate partial selection, use `isIndeterminate` on the header's `select` prop. When some (but not all) selectable rows are selected, the header checkbox will convey this information to assistive technologies and also display a dash instead of a checkmark.
164
+
165
+ ```ts file="TableSelectableIndeterminate.tsx"
166
+
167
+ ```
168
+
161
169
  ### Selectable radio input
162
170
 
163
171
  Similarly to the selectable example above, the radio buttons use the first column. The first header cell is empty, and each body row's first cell has radio button props.
@@ -0,0 +1,128 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';
3
+
4
+ interface Repository {
5
+ name: string;
6
+ branches: string;
7
+ prs: string;
8
+ workspaces: string;
9
+ lastCommit: string;
10
+ }
11
+
12
+ export const TableSelectableIndeterminate: React.FunctionComponent = () => {
13
+ // In real usage, this data would come from some external source like an API via props.
14
+ const repositories: Repository[] = [
15
+ { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },
16
+ { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
17
+ { name: 'b', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
18
+ { name: 'c', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
19
+ { name: 'd', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
20
+ { name: 'e', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }
21
+ ];
22
+
23
+ const columnNames = {
24
+ name: 'Repositories',
25
+ branches: 'Branches',
26
+ prs: 'Pull requests',
27
+ workspaces: 'Workspaces',
28
+ lastCommit: 'Last commit'
29
+ };
30
+
31
+ const isRepoSelectable = (repo: Repository) => repo.name !== 'a'; // Arbitrary logic for this example
32
+ const selectableRepos = repositories.filter(isRepoSelectable);
33
+
34
+ // In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.
35
+ // This is to prevent state from being based on row order index in case we later add sorting.
36
+ const [selectedRepoNames, setSelectedRepoNames] = useState<string[]>([]);
37
+ const setRepoSelected = (repo: Repository, isSelecting = true) =>
38
+ setSelectedRepoNames((prevSelected) => {
39
+ const otherSelectedRepoNames = prevSelected.filter((r) => r !== repo.name);
40
+ return isSelecting && isRepoSelectable(repo) ? [...otherSelectedRepoNames, repo.name] : otherSelectedRepoNames;
41
+ });
42
+ const selectAllRepos = (isSelecting = true) =>
43
+ setSelectedRepoNames(isSelecting ? selectableRepos.map((r) => r.name) : []);
44
+ const areAllReposSelected = selectedRepoNames.length === selectableRepos.length;
45
+ const areSomeReposSelected = selectedRepoNames.length > 0 && selectedRepoNames.length < selectableRepos.length;
46
+ const isRepoSelected = (repo: Repository) => selectedRepoNames.includes(repo.name);
47
+
48
+ // To allow shift+click to select/deselect multiple rows
49
+ const [recentSelectedRowIndex, setRecentSelectedRowIndex] = useState<number | null>(null);
50
+ const [shifting, setShifting] = useState(false);
51
+
52
+ const onSelectRepo = (repo: Repository, rowIndex: number, isSelecting: boolean) => {
53
+ // If the user is shift + selecting the checkboxes, then all intermediate checkboxes should be selected
54
+ if (shifting && recentSelectedRowIndex !== null) {
55
+ const numberSelected = rowIndex - recentSelectedRowIndex;
56
+ const intermediateIndexes =
57
+ numberSelected > 0
58
+ ? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)
59
+ : Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);
60
+ intermediateIndexes.forEach((index) => setRepoSelected(repositories[index], isSelecting));
61
+ } else {
62
+ setRepoSelected(repo, isSelecting);
63
+ }
64
+ setRecentSelectedRowIndex(rowIndex);
65
+ };
66
+
67
+ useEffect(() => {
68
+ const onKeyDown = (e: KeyboardEvent) => {
69
+ if (e.key === 'Shift') {
70
+ setShifting(true);
71
+ }
72
+ };
73
+ const onKeyUp = (e: KeyboardEvent) => {
74
+ if (e.key === 'Shift') {
75
+ setShifting(false);
76
+ }
77
+ };
78
+
79
+ document.addEventListener('keydown', onKeyDown);
80
+ document.addEventListener('keyup', onKeyUp);
81
+
82
+ return () => {
83
+ document.removeEventListener('keydown', onKeyDown);
84
+ document.removeEventListener('keyup', onKeyUp);
85
+ };
86
+ }, []);
87
+
88
+ return (
89
+ <Table aria-label="Selectable table with indeterminate state">
90
+ <Thead>
91
+ <Tr>
92
+ <Th
93
+ select={{
94
+ onSelect: (_event, isSelecting) => selectAllRepos(isSelecting),
95
+ isSelected: areAllReposSelected,
96
+ isIndeterminate: areSomeReposSelected
97
+ }}
98
+ aria-label="Row select"
99
+ />
100
+ <Th>{columnNames.name}</Th>
101
+ <Th>{columnNames.branches}</Th>
102
+ <Th>{columnNames.prs}</Th>
103
+ <Th>{columnNames.workspaces}</Th>
104
+ <Th>{columnNames.lastCommit}</Th>
105
+ </Tr>
106
+ </Thead>
107
+ <Tbody>
108
+ {repositories.map((repo, rowIndex) => (
109
+ <Tr key={repo.name}>
110
+ <Td
111
+ select={{
112
+ rowIndex,
113
+ onSelect: (_event, isSelecting) => onSelectRepo(repo, rowIndex, isSelecting),
114
+ isSelected: isRepoSelected(repo),
115
+ isDisabled: !isRepoSelectable(repo)
116
+ }}
117
+ />
118
+ <Td dataLabel={columnNames.name}>{repo.name}</Td>
119
+ <Td dataLabel={columnNames.branches}>{repo.branches}</Td>
120
+ <Td dataLabel={columnNames.prs}>{repo.prs}</Td>
121
+ <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>
122
+ <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>
123
+ </Tr>
124
+ ))}
125
+ </Tbody>
126
+ </Table>
127
+ );
128
+ };
@@ -9,7 +9,7 @@ export const selectable: ITransform = (
9
9
  { rowIndex, columnIndex, rowData, column, property, tooltip }: IExtra
10
10
  ) => {
11
11
  const {
12
- extraParams: { onSelect, selectVariant, allRowsSelected, isHeaderSelectDisabled }
12
+ extraParams: { onSelect, selectVariant, allRowsSelected, isHeaderSelectDisabled, isIndeterminate }
13
13
  } = column;
14
14
  const extraData = {
15
15
  rowIndex,
@@ -70,6 +70,7 @@ export const selectable: ITransform = (
70
70
  onSelect={selectClick}
71
71
  name={selectName}
72
72
  tooltip={tooltip}
73
+ isIndeterminate={rowId === -1 ? isIndeterminate : undefined}
73
74
  >
74
75
  {label as React.ReactNode}
75
76
  </SelectColumn>