@patternfly/react-table 4.44.12 → 4.44.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/esm/components/Table/Table.d.ts +2 -1
- package/dist/esm/components/Table/Table.d.ts.map +1 -1
- package/dist/esm/components/Table/Table.js.map +1 -1
- package/dist/esm/components/Table/TableTypes.d.ts +2 -1
- package/dist/esm/components/Table/TableTypes.d.ts.map +1 -1
- package/dist/esm/components/Table/TableTypes.js.map +1 -1
- package/dist/esm/components/Table/base/provider.d.ts +2 -2
- package/dist/esm/components/Table/base/types.d.ts +2 -2
- package/dist/esm/components/TableComposable/TableComposable.d.ts +1 -1
- package/dist/esm/components/TableComposable/Td.d.ts +1 -1
- package/dist/js/components/Table/Table.d.ts +2 -1
- package/dist/js/components/Table/Table.d.ts.map +1 -1
- package/dist/js/components/Table/Table.js.map +1 -1
- package/dist/js/components/Table/TableTypes.d.ts +2 -1
- package/dist/js/components/Table/TableTypes.d.ts.map +1 -1
- package/dist/js/components/Table/TableTypes.js.map +1 -1
- package/dist/js/components/Table/base/provider.d.ts +2 -2
- package/dist/js/components/Table/base/types.d.ts +2 -2
- package/dist/js/components/TableComposable/TableComposable.d.ts +1 -1
- package/dist/js/components/TableComposable/Td.d.ts +1 -1
- package/package.json +6 -6
- package/src/components/Table/Table.tsx +2 -1
- package/src/components/Table/TableTypes.tsx +2 -1
- package/src/components/Table/examples/LegacyTableActions.tsx +218 -0
- package/src/components/Table/examples/LegacyTableBasic.tsx +90 -0
- package/src/components/Table/examples/LegacyTableCellWidth.tsx +66 -0
- package/src/components/Table/examples/LegacyTableCompoundExpandable.tsx +175 -0
- package/src/components/Table/examples/LegacyTableControllingText.tsx +51 -0
- package/src/components/Table/examples/LegacyTableEmptyState.tsx +48 -0
- package/src/components/Table/examples/LegacyTableExpandable.tsx +172 -0
- package/src/components/Table/examples/LegacyTableFavoritable.tsx +157 -0
- package/src/components/Table/examples/LegacyTableHoverable.tsx +125 -0
- package/src/components/Table/examples/LegacyTableMisc.tsx +56 -0
- package/src/components/Table/examples/LegacyTableSelectable.tsx +124 -0
- package/src/components/Table/examples/LegacyTableSelectableRadio.tsx +54 -0
- package/src/components/Table/examples/LegacyTableSortable.tsx +120 -0
- package/src/components/Table/examples/LegacyTableSortableCustom.tsx +189 -0
- package/src/components/Table/examples/LegacyTableTextModifiers.tsx +31 -0
- package/src/components/Table/examples/LegacyTableTree.tsx +213 -0
- package/src/components/Table/examples/Table.md +83 -1848
- package/src/components/TableComposable/examples/ComposableTable.md +31 -2805
- package/src/components/TableComposable/examples/ComposableTableActions.tsx +163 -0
- package/src/components/TableComposable/examples/ComposableTableBasic.tsx +89 -0
- package/src/components/TableComposable/examples/ComposableTableCellWidth.tsx +56 -0
- package/src/components/TableComposable/examples/ComposableTableCompoundExpandable.tsx +103 -0
- package/src/components/TableComposable/examples/ComposableTableControllingText.tsx +51 -0
- package/src/components/TableComposable/examples/ComposableTableDraggable.tsx +170 -0
- package/src/components/TableComposable/examples/ComposableTableEmptyState.tsx +45 -0
- package/src/components/TableComposable/examples/ComposableTableExpandable.tsx +195 -0
- package/src/components/TableComposable/examples/ComposableTableFavoritable.tsx +118 -0
- package/src/components/TableComposable/examples/ComposableTableHoverable.tsx +67 -0
- package/src/components/TableComposable/examples/ComposableTableMisc.tsx +92 -0
- package/src/components/TableComposable/examples/ComposableTableMultipleStickyColumns.tsx +153 -0
- package/src/components/TableComposable/examples/ComposableTableNestedExpandable.tsx +127 -0
- package/src/components/TableComposable/examples/ComposableTableNestedHeaders.tsx +202 -0
- package/src/components/TableComposable/examples/ComposableTableSelectable.tsx +125 -0
- package/src/components/TableComposable/examples/ComposableTableSelectableRadio.tsx +68 -0
- package/src/components/TableComposable/examples/ComposableTableSortable.tsx +107 -0
- package/src/components/TableComposable/examples/ComposableTableSortableCustom.tsx +175 -0
- package/src/components/TableComposable/examples/ComposableTableStickyColumn.tsx +144 -0
- package/src/components/TableComposable/examples/ComposableTableStickyColumnsAndHeader.tsx +167 -0
- package/src/components/TableComposable/examples/ComposableTableTextModifiers.tsx +33 -0
- package/src/components/TableComposable/examples/ComposableTableTree.tsx +201 -0
- package/src/demos/Table.md +7 -6
- package/src/components/Table/examples/DemoSortableTable.js +0 -65
- package/src/components/TableComposable/examples/DemoSortableTable.js +0 -65
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Table, TableHeader, TableBody, info, textCenter, TableProps, ICell } from '@patternfly/react-table';
|
|
3
|
+
import styles from '@patternfly/react-styles/css/components/Table/table';
|
|
4
|
+
|
|
5
|
+
interface Repository {
|
|
6
|
+
name: string;
|
|
7
|
+
branches: string | null;
|
|
8
|
+
prs: string | null;
|
|
9
|
+
workspaces: string;
|
|
10
|
+
lastCommit: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const LegacyTableHoverable: React.FunctionComponent = () => {
|
|
14
|
+
// In real usage, this data would come from some external source like an API via props.
|
|
15
|
+
const repositories: Repository[] = [
|
|
16
|
+
{
|
|
17
|
+
name: 'Repository one',
|
|
18
|
+
branches: 'Branch one',
|
|
19
|
+
prs: 'PR one',
|
|
20
|
+
workspaces: 'Workspace one',
|
|
21
|
+
lastCommit: 'Commit one'
|
|
22
|
+
},
|
|
23
|
+
{ name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },
|
|
24
|
+
{
|
|
25
|
+
name: 'one - 3',
|
|
26
|
+
branches: 'two - 3',
|
|
27
|
+
prs: 'three - 3',
|
|
28
|
+
workspaces: 'four - 3',
|
|
29
|
+
lastCommit: 'five - 3 (not centered)'
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.
|
|
34
|
+
// This is to prevent state from being based on row order index in case we later add sorting.
|
|
35
|
+
const [selectedRepoNames, setSelectedRepoNames] = React.useState<string[]>([]);
|
|
36
|
+
const setRepoSelected = (repo: Repository, isSelecting = true) =>
|
|
37
|
+
setSelectedRepoNames(prevSelected => {
|
|
38
|
+
const otherSelectedRepoNames = prevSelected.filter(r => r !== repo.name);
|
|
39
|
+
return isSelecting ? [...otherSelectedRepoNames, repo.name] : otherSelectedRepoNames;
|
|
40
|
+
});
|
|
41
|
+
const isRepoSelected = (repo: Repository) => selectedRepoNames.includes(repo.name);
|
|
42
|
+
|
|
43
|
+
const columns: TableProps['cells'] = [
|
|
44
|
+
{
|
|
45
|
+
title: 'Repositories',
|
|
46
|
+
transforms: [
|
|
47
|
+
info({
|
|
48
|
+
tooltip: 'More information about repositories',
|
|
49
|
+
className: 'repositories-info-tip',
|
|
50
|
+
tooltipProps: {
|
|
51
|
+
isContentLeftAligned: true
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
'Branches',
|
|
57
|
+
{
|
|
58
|
+
title: 'Pull requests',
|
|
59
|
+
transforms: [
|
|
60
|
+
info({
|
|
61
|
+
popover: (
|
|
62
|
+
<div>
|
|
63
|
+
More <strong>information</strong> on pull requests
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
ariaLabel: 'More information on pull requests',
|
|
67
|
+
popoverProps: {
|
|
68
|
+
headerContent: 'Pull requests',
|
|
69
|
+
footerContent: <a href="#">Click here for even more info</a>
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
'Workspaces',
|
|
75
|
+
{
|
|
76
|
+
title: 'Last commit',
|
|
77
|
+
transforms: [textCenter],
|
|
78
|
+
cellTransforms: [textCenter]
|
|
79
|
+
}
|
|
80
|
+
];
|
|
81
|
+
const rows: TableProps['rows'] = repositories.map(repo => {
|
|
82
|
+
let cells: (string | ICell)[] = [repo.name, repo.branches || '', repo.prs || '', repo.workspaces, repo.lastCommit];
|
|
83
|
+
// These rows have arbitrary differences for this example, but these could be based on some other conditions
|
|
84
|
+
if (repo.name === 'one - 2') {
|
|
85
|
+
cells = [
|
|
86
|
+
{
|
|
87
|
+
title: <div>{repo.name}</div>,
|
|
88
|
+
props: { title: 'hover title', colSpan: 3 }
|
|
89
|
+
},
|
|
90
|
+
repo.workspaces,
|
|
91
|
+
repo.lastCommit
|
|
92
|
+
];
|
|
93
|
+
} else if (repo.name === 'five - 3 (not centrered)') {
|
|
94
|
+
cells = [
|
|
95
|
+
repo.name,
|
|
96
|
+
repo.branches || '',
|
|
97
|
+
repo.prs || '',
|
|
98
|
+
repo.workspaces,
|
|
99
|
+
{
|
|
100
|
+
title: repo.lastCommit,
|
|
101
|
+
props: { textCenter: false }
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
cells,
|
|
107
|
+
isHoverable: true,
|
|
108
|
+
isRowSelected: isRepoSelected(repo)
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Table caption="Row click handler table" cells={columns} rows={rows}>
|
|
114
|
+
<TableHeader className={styles.modifiers.nowrap} />
|
|
115
|
+
<TableBody
|
|
116
|
+
onRowClick={(_event, row, rowProps) => {
|
|
117
|
+
if (rowProps) {
|
|
118
|
+
const repo = repositories[rowProps.rowIndex];
|
|
119
|
+
setRepoSelected(repo, !isRepoSelected(repo));
|
|
120
|
+
}
|
|
121
|
+
}}
|
|
122
|
+
/>
|
|
123
|
+
</Table>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Table, TableHeader, TableBody, TableProps } from '@patternfly/react-table';
|
|
3
|
+
import { css } from '@patternfly/react-styles';
|
|
4
|
+
|
|
5
|
+
interface Repository {
|
|
6
|
+
name: string;
|
|
7
|
+
branches: string | null;
|
|
8
|
+
prs: string | null;
|
|
9
|
+
workspaces: string;
|
|
10
|
+
lastCommit: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const LegacyTableMisc: React.FunctionComponent = () => {
|
|
14
|
+
// In real usage, this data would come from some external source like an API via props.
|
|
15
|
+
const repositories: Repository[] = [
|
|
16
|
+
{ name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },
|
|
17
|
+
{ name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },
|
|
18
|
+
{ name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const columns: TableProps['cells'] = ['Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit'];
|
|
22
|
+
const rows: TableProps['rows'] = repositories.map(repo => [
|
|
23
|
+
repo.name,
|
|
24
|
+
repo.branches || '',
|
|
25
|
+
repo.prs || '',
|
|
26
|
+
repo.workspaces,
|
|
27
|
+
repo.lastCommit
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const customRowWrapper: TableProps['rowWrapper'] = ({ trRef, className, rowProps, row: _row, ...props }) => {
|
|
31
|
+
const isOddRow = rowProps ? !!((rowProps.rowIndex + 1) % 2) : true;
|
|
32
|
+
const customStyle = {
|
|
33
|
+
borderLeft: '3px solid var(--pf-global--primary-color--100)'
|
|
34
|
+
};
|
|
35
|
+
return (
|
|
36
|
+
<tr
|
|
37
|
+
{...props}
|
|
38
|
+
ref={trRef as React.LegacyRef<HTMLTableRowElement>}
|
|
39
|
+
className={css(className, isOddRow ? 'odd-row-class' : 'even-row-class', 'custom-static-class')}
|
|
40
|
+
style={isOddRow ? customStyle : {}}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Table
|
|
47
|
+
caption="Table with custom row wrapper that styles odd rows"
|
|
48
|
+
cells={columns}
|
|
49
|
+
rows={rows}
|
|
50
|
+
rowWrapper={customRowWrapper}
|
|
51
|
+
>
|
|
52
|
+
<TableHeader />
|
|
53
|
+
<TableBody />
|
|
54
|
+
</Table>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Table, TableBody, TableHeader, headerCol, TableProps } from '@patternfly/react-table';
|
|
3
|
+
import { Checkbox } from '@patternfly/react-core';
|
|
4
|
+
|
|
5
|
+
interface Repository {
|
|
6
|
+
name: string;
|
|
7
|
+
branches: string;
|
|
8
|
+
prs: string;
|
|
9
|
+
workspaces: string;
|
|
10
|
+
lastCommit: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const LegacyTableSelectable: React.FunctionComponent = () => {
|
|
14
|
+
// In real usage, this data would come from some external source like an API via props.
|
|
15
|
+
const repositories: Repository[] = [
|
|
16
|
+
{ name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },
|
|
17
|
+
{ name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
|
|
18
|
+
{ name: 'b', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
|
|
19
|
+
{ name: 'c', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
|
|
20
|
+
{ name: 'd', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
|
|
21
|
+
{ name: 'e', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const isRepoSelectable = (repo: Repository) => repo.name !== 'a'; // Arbitrary logic for this example
|
|
25
|
+
const selectableRepos = repositories.filter(isRepoSelectable);
|
|
26
|
+
|
|
27
|
+
// In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.
|
|
28
|
+
// This is to prevent state from being based on row order index in case we later add sorting.
|
|
29
|
+
const [selectedRepoNames, setSelectedRepoNames] = React.useState<string[]>([]);
|
|
30
|
+
const setRepoSelected = (repo: Repository, isSelecting = true) =>
|
|
31
|
+
setSelectedRepoNames(prevSelected => {
|
|
32
|
+
const otherSelectedRepoNames = prevSelected.filter(r => r !== repo.name);
|
|
33
|
+
return isSelecting && isRepoSelectable(repo) ? [...otherSelectedRepoNames, repo.name] : otherSelectedRepoNames;
|
|
34
|
+
});
|
|
35
|
+
const selectAllRepos = (isSelecting = true) =>
|
|
36
|
+
setSelectedRepoNames(isSelecting ? selectableRepos.map(r => r.name) : []);
|
|
37
|
+
const isRepoSelected = (repo: Repository) => selectedRepoNames.includes(repo.name);
|
|
38
|
+
|
|
39
|
+
const [canSelectAll, setCanSelectAll] = React.useState(true);
|
|
40
|
+
|
|
41
|
+
// To allow shift+click to select/deselect multiple rows
|
|
42
|
+
const [recentSelectedRowIndex, setRecentSelectedRowIndex] = React.useState<number | null>(null);
|
|
43
|
+
const [shifting, setShifting] = React.useState(false);
|
|
44
|
+
|
|
45
|
+
const onSelectRepo = (repo: Repository, rowIndex: number, isSelecting: boolean) => {
|
|
46
|
+
// If the user is shift + selecting the checkboxes, then all intermediate checkboxes should be selected
|
|
47
|
+
if (shifting && recentSelectedRowIndex !== null) {
|
|
48
|
+
const numberSelected = rowIndex - recentSelectedRowIndex;
|
|
49
|
+
const intermediateIndexes =
|
|
50
|
+
numberSelected > 0
|
|
51
|
+
? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)
|
|
52
|
+
: Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);
|
|
53
|
+
intermediateIndexes.forEach(index => setRepoSelected(repositories[index], isSelecting));
|
|
54
|
+
} else {
|
|
55
|
+
setRepoSelected(repo, isSelecting);
|
|
56
|
+
}
|
|
57
|
+
setRecentSelectedRowIndex(rowIndex);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
62
|
+
if (e.key === 'Shift') {
|
|
63
|
+
setShifting(true);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const onKeyUp = (e: KeyboardEvent) => {
|
|
67
|
+
if (e.key === 'Shift') {
|
|
68
|
+
setShifting(false);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
document.addEventListener('keydown', onKeyDown);
|
|
73
|
+
document.addEventListener('keyup', onKeyUp);
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
document.removeEventListener('keydown', onKeyDown);
|
|
77
|
+
document.removeEventListener('keyup', onKeyUp);
|
|
78
|
+
};
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
const columns: TableProps['cells'] = [
|
|
82
|
+
{ title: 'Repositories', cellTransforms: [headerCol()] },
|
|
83
|
+
'Branches',
|
|
84
|
+
{ title: 'Pull requests' },
|
|
85
|
+
'Workspaces',
|
|
86
|
+
'Last commit'
|
|
87
|
+
];
|
|
88
|
+
const rows: TableProps['rows'] = repositories.map(repo => ({
|
|
89
|
+
cells: [repo.name, repo.branches, repo.prs, repo.workspaces, repo.lastCommit],
|
|
90
|
+
selected: isRepoSelected(repo),
|
|
91
|
+
disableSelection: !isRepoSelectable(repo)
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div>
|
|
96
|
+
<Checkbox
|
|
97
|
+
label="Can select all"
|
|
98
|
+
className="pf-u-mb-lg"
|
|
99
|
+
isChecked={canSelectAll}
|
|
100
|
+
onChange={checked => setCanSelectAll(checked)}
|
|
101
|
+
aria-label="toggle select all checkbox"
|
|
102
|
+
id="toggle-select-all"
|
|
103
|
+
name="toggle-select-all"
|
|
104
|
+
/>
|
|
105
|
+
<Table
|
|
106
|
+
onSelect={(_event, isSelecting, rowIndex) => {
|
|
107
|
+
if (rowIndex === -1) {
|
|
108
|
+
selectAllRepos(isSelecting);
|
|
109
|
+
} else {
|
|
110
|
+
const repo = repositories[rowIndex];
|
|
111
|
+
onSelectRepo(repo, rowIndex, isSelecting);
|
|
112
|
+
}
|
|
113
|
+
}}
|
|
114
|
+
canSelectAll={canSelectAll}
|
|
115
|
+
aria-label="Selectable Table"
|
|
116
|
+
cells={columns}
|
|
117
|
+
rows={rows}
|
|
118
|
+
>
|
|
119
|
+
<TableHeader />
|
|
120
|
+
<TableBody />
|
|
121
|
+
</Table>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Table, TableHeader, TableBody, headerCol, TableProps } 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 LegacyTableSelectableRadio: 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: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const isRepoSelectable = (repo: Repository) => repo.name !== 'a'; // Arbitrary logic for this example
|
|
21
|
+
|
|
22
|
+
// In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.
|
|
23
|
+
// This is to prevent state from being based on row order index in case we later add sorting.
|
|
24
|
+
const [selectedRepoName, setSelectedRepoName] = React.useState<string | null>(null);
|
|
25
|
+
|
|
26
|
+
const columns: TableProps['cells'] = [
|
|
27
|
+
{ title: 'Repositories', cellTransforms: [headerCol()] },
|
|
28
|
+
'Branches',
|
|
29
|
+
{ title: 'Pull requests' },
|
|
30
|
+
'Workspaces',
|
|
31
|
+
'Last commit'
|
|
32
|
+
];
|
|
33
|
+
const rows: TableProps['rows'] = repositories.map(repo => ({
|
|
34
|
+
cells: [repo.name, repo.branches, repo.prs, repo.workspaces, repo.lastCommit],
|
|
35
|
+
selected: selectedRepoName === repo.name,
|
|
36
|
+
disableSelection: !isRepoSelectable(repo)
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Table
|
|
41
|
+
onSelect={(_event, _isSelecting, rowIndex) => {
|
|
42
|
+
const repo = repositories[rowIndex];
|
|
43
|
+
setSelectedRepoName(repo.name);
|
|
44
|
+
}}
|
|
45
|
+
selectVariant="radio"
|
|
46
|
+
aria-label="Selectable Table with Radio Buttons"
|
|
47
|
+
cells={columns}
|
|
48
|
+
rows={rows}
|
|
49
|
+
>
|
|
50
|
+
<TableHeader />
|
|
51
|
+
<TableBody />
|
|
52
|
+
</Table>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
TableProps,
|
|
4
|
+
sortable,
|
|
5
|
+
info,
|
|
6
|
+
cellWidth,
|
|
7
|
+
wrappable,
|
|
8
|
+
Table,
|
|
9
|
+
TableBody,
|
|
10
|
+
TableHeader
|
|
11
|
+
} from '@patternfly/react-table';
|
|
12
|
+
|
|
13
|
+
interface Repository {
|
|
14
|
+
name: string;
|
|
15
|
+
branches: string;
|
|
16
|
+
prs: string;
|
|
17
|
+
workspaces: string;
|
|
18
|
+
lastCommit: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const LegacyTableSortable: React.FunctionComponent = () => {
|
|
22
|
+
// In real usage, this data would come from some external source like an API via props.
|
|
23
|
+
const repositories: Repository[] = [
|
|
24
|
+
{ name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },
|
|
25
|
+
{ name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
|
|
26
|
+
{ name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// Index of the currently sorted column
|
|
30
|
+
// Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key
|
|
31
|
+
// as the identifier of the sorted column. See the "Compound expandable" example.
|
|
32
|
+
const [activeSortIndex, setActiveSortIndex] = React.useState<number | null>(null);
|
|
33
|
+
|
|
34
|
+
// Sort direction of the currently sorted column
|
|
35
|
+
const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null);
|
|
36
|
+
|
|
37
|
+
// Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.
|
|
38
|
+
// This example is trivial since our data objects just contain strings, but if the data was more complex
|
|
39
|
+
// this would be a place to return simplified string or number versions of each column to sort by.
|
|
40
|
+
const getSortableRowValues = (repo: Repository): (string | number)[] => {
|
|
41
|
+
const { name, branches, prs, workspaces, lastCommit } = repo;
|
|
42
|
+
return [name, branches, prs, workspaces, lastCommit];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Note that we perform the sort as part of the component's render logic and not in onSort.
|
|
46
|
+
// We shouldn't store the list of data in state because we don't want to have to sync that with props.
|
|
47
|
+
let sortedRepositories = repositories;
|
|
48
|
+
if (activeSortIndex !== null) {
|
|
49
|
+
sortedRepositories = repositories.sort((a, b) => {
|
|
50
|
+
const aValue = getSortableRowValues(a)[activeSortIndex];
|
|
51
|
+
const bValue = getSortableRowValues(b)[activeSortIndex];
|
|
52
|
+
if (typeof aValue === 'number') {
|
|
53
|
+
// Numeric sort
|
|
54
|
+
if (activeSortDirection === 'asc') {
|
|
55
|
+
return (aValue as number) - (bValue as number);
|
|
56
|
+
}
|
|
57
|
+
return (bValue as number) - (aValue as number);
|
|
58
|
+
} else {
|
|
59
|
+
// String sort
|
|
60
|
+
if (activeSortDirection === 'asc') {
|
|
61
|
+
return (aValue as string).localeCompare(bValue as string);
|
|
62
|
+
}
|
|
63
|
+
return (bValue as string).localeCompare(aValue as string);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const columns: TableProps['cells'] = [
|
|
69
|
+
{ title: 'Repositories', transforms: [sortable] },
|
|
70
|
+
{
|
|
71
|
+
title: 'Branches',
|
|
72
|
+
transforms: [
|
|
73
|
+
info({
|
|
74
|
+
tooltip: 'More information about branches'
|
|
75
|
+
}),
|
|
76
|
+
sortable
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{ title: 'Pull requests', transforms: [sortable] },
|
|
80
|
+
{
|
|
81
|
+
title: 'Workspaces - Also this long header will wrap since we have applied the wrappable transform',
|
|
82
|
+
transforms: [sortable, wrappable, cellWidth(20)]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
title: 'Last commit',
|
|
86
|
+
transforms: [
|
|
87
|
+
info({
|
|
88
|
+
tooltip: 'More information about commits'
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
const rows: TableProps['rows'] = sortedRepositories.map(repo => [
|
|
94
|
+
repo.name,
|
|
95
|
+
repo.branches,
|
|
96
|
+
repo.prs,
|
|
97
|
+
repo.workspaces,
|
|
98
|
+
repo.lastCommit
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Table
|
|
103
|
+
aria-label="Sortable Table"
|
|
104
|
+
sortBy={{
|
|
105
|
+
index: activeSortIndex,
|
|
106
|
+
direction: activeSortDirection,
|
|
107
|
+
defaultDirection: 'asc' // starting sort direction when first sorting a column. Defaults to 'asc'
|
|
108
|
+
}}
|
|
109
|
+
onSort={(_event, index, direction) => {
|
|
110
|
+
setActiveSortIndex(index);
|
|
111
|
+
setActiveSortDirection(direction);
|
|
112
|
+
}}
|
|
113
|
+
cells={columns}
|
|
114
|
+
rows={rows}
|
|
115
|
+
>
|
|
116
|
+
<TableHeader />
|
|
117
|
+
<TableBody />
|
|
118
|
+
</Table>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TableProps, sortable, info, Table, TableBody, TableHeader } from '@patternfly/react-table';
|
|
3
|
+
import {
|
|
4
|
+
Toolbar,
|
|
5
|
+
ToolbarContent,
|
|
6
|
+
ToolbarItem,
|
|
7
|
+
OptionsMenu,
|
|
8
|
+
OptionsMenuItemGroup,
|
|
9
|
+
OptionsMenuItem,
|
|
10
|
+
OptionsMenuSeparator,
|
|
11
|
+
OptionsMenuToggle
|
|
12
|
+
} from '@patternfly/react-core';
|
|
13
|
+
import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amount-down-icon';
|
|
14
|
+
|
|
15
|
+
interface Repository {
|
|
16
|
+
name: string;
|
|
17
|
+
branches: string;
|
|
18
|
+
prs: string;
|
|
19
|
+
workspaces: string;
|
|
20
|
+
lastCommit: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const LegacyTableSortableCustom: React.FunctionComponent = () => {
|
|
24
|
+
// In real usage, this data would come from some external source like an API via props.
|
|
25
|
+
const repositories: Repository[] = [
|
|
26
|
+
{ name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },
|
|
27
|
+
{ name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },
|
|
28
|
+
{ name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const columnNames = {
|
|
32
|
+
name: 'Repositories',
|
|
33
|
+
branches: 'Branches',
|
|
34
|
+
prs: 'Pull requests',
|
|
35
|
+
workspaces: 'Workspaces',
|
|
36
|
+
lastCommit: 'Last commit'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Index of the currently sorted column
|
|
40
|
+
// Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key
|
|
41
|
+
// as the identifier of the sorted column. See the "Compound expandable" example.
|
|
42
|
+
const [activeSortIndex, setActiveSortIndex] = React.useState<number | null>(null);
|
|
43
|
+
|
|
44
|
+
// Sort direction of the currently sorted column
|
|
45
|
+
const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null);
|
|
46
|
+
|
|
47
|
+
// Sort dropdown expansion
|
|
48
|
+
const [isSortDropdownOpen, setIsSortDropdownOpen] = React.useState(false);
|
|
49
|
+
|
|
50
|
+
// Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.
|
|
51
|
+
// This example is trivial since our data objects just contain strings, but if the data was more complex
|
|
52
|
+
// this would be a place to return simplified string or number versions of each column to sort by.
|
|
53
|
+
const getSortableRowValues = (repo: Repository): (string | number)[] => {
|
|
54
|
+
const { name, branches, prs, workspaces, lastCommit } = repo;
|
|
55
|
+
return [name, branches, prs, workspaces, lastCommit];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Note that we perform the sort as part of the component's render logic and not in onSort.
|
|
59
|
+
// We shouldn't store the list of data in state because we don't want to have to sync that with props.
|
|
60
|
+
let sortedRepositories = repositories;
|
|
61
|
+
if (activeSortIndex !== null) {
|
|
62
|
+
sortedRepositories = repositories.sort((a, b) => {
|
|
63
|
+
const aValue = getSortableRowValues(a)[activeSortIndex];
|
|
64
|
+
const bValue = getSortableRowValues(b)[activeSortIndex];
|
|
65
|
+
if (typeof aValue === 'number') {
|
|
66
|
+
// Numeric sort
|
|
67
|
+
if (activeSortDirection === 'asc') {
|
|
68
|
+
return (aValue as number) - (bValue as number);
|
|
69
|
+
}
|
|
70
|
+
return (bValue as number) - (aValue as number);
|
|
71
|
+
} else {
|
|
72
|
+
// String sort
|
|
73
|
+
if (activeSortDirection === 'asc') {
|
|
74
|
+
return (aValue as string).localeCompare(bValue as string);
|
|
75
|
+
}
|
|
76
|
+
return (bValue as string).localeCompare(aValue as string);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const columns: TableProps['cells'] = [
|
|
82
|
+
{ title: 'Repositories', transforms: [sortable] },
|
|
83
|
+
{
|
|
84
|
+
title: 'Branches',
|
|
85
|
+
transforms: [
|
|
86
|
+
info({
|
|
87
|
+
tooltip: 'More information about branches'
|
|
88
|
+
}),
|
|
89
|
+
sortable
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
{ title: 'Pull requests', transforms: [sortable] },
|
|
93
|
+
{
|
|
94
|
+
title: 'Workspaces',
|
|
95
|
+
transforms: [sortable]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
title: 'Last commit',
|
|
99
|
+
transforms: [
|
|
100
|
+
info({
|
|
101
|
+
tooltip: 'More information about commits'
|
|
102
|
+
}),
|
|
103
|
+
sortable
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
];
|
|
107
|
+
const rows: TableProps['rows'] = sortedRepositories.map(repo => [
|
|
108
|
+
repo.name,
|
|
109
|
+
repo.branches,
|
|
110
|
+
repo.prs,
|
|
111
|
+
repo.workspaces,
|
|
112
|
+
repo.lastCommit
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<React.Fragment>
|
|
117
|
+
<Toolbar id="toolbar">
|
|
118
|
+
<ToolbarContent>
|
|
119
|
+
<ToolbarItem>
|
|
120
|
+
<OptionsMenu
|
|
121
|
+
id="options-menu-multiple-options-example"
|
|
122
|
+
menuItems={[
|
|
123
|
+
<OptionsMenuItemGroup key="first group" aria-label="Sort column">
|
|
124
|
+
{Object.values(columnNames).map((columnName, columnIndex) => (
|
|
125
|
+
<OptionsMenuItem
|
|
126
|
+
key={columnName}
|
|
127
|
+
isSelected={activeSortIndex === columnIndex}
|
|
128
|
+
onSelect={() => {
|
|
129
|
+
setActiveSortIndex(columnIndex);
|
|
130
|
+
setActiveSortDirection(activeSortDirection !== null ? activeSortDirection : 'asc');
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
{columnName}
|
|
134
|
+
</OptionsMenuItem>
|
|
135
|
+
))}
|
|
136
|
+
</OptionsMenuItemGroup>,
|
|
137
|
+
<OptionsMenuSeparator key="separator" />,
|
|
138
|
+
<OptionsMenuItemGroup key="second group" aria-label="Sort direction">
|
|
139
|
+
<OptionsMenuItem
|
|
140
|
+
onSelect={() => setActiveSortDirection('asc')}
|
|
141
|
+
isSelected={activeSortDirection === 'asc'}
|
|
142
|
+
id="ascending"
|
|
143
|
+
key="ascending"
|
|
144
|
+
>
|
|
145
|
+
Ascending
|
|
146
|
+
</OptionsMenuItem>
|
|
147
|
+
<OptionsMenuItem
|
|
148
|
+
onSelect={() => setActiveSortDirection('desc')}
|
|
149
|
+
isSelected={activeSortDirection === 'desc'}
|
|
150
|
+
id="descending"
|
|
151
|
+
key="descending"
|
|
152
|
+
>
|
|
153
|
+
Descending
|
|
154
|
+
</OptionsMenuItem>
|
|
155
|
+
</OptionsMenuItemGroup>
|
|
156
|
+
]}
|
|
157
|
+
isOpen={isSortDropdownOpen}
|
|
158
|
+
toggle={
|
|
159
|
+
<OptionsMenuToggle
|
|
160
|
+
hideCaret
|
|
161
|
+
onToggle={() => setIsSortDropdownOpen(!isSortDropdownOpen)}
|
|
162
|
+
toggleTemplate={<SortAmountDownIcon />}
|
|
163
|
+
/>
|
|
164
|
+
}
|
|
165
|
+
isPlain
|
|
166
|
+
isGrouped
|
|
167
|
+
/>
|
|
168
|
+
</ToolbarItem>
|
|
169
|
+
</ToolbarContent>
|
|
170
|
+
</Toolbar>
|
|
171
|
+
<Table
|
|
172
|
+
aria-label="Sortable Table with Custom Toolbar"
|
|
173
|
+
sortBy={{
|
|
174
|
+
index: activeSortIndex,
|
|
175
|
+
direction: activeSortDirection
|
|
176
|
+
}}
|
|
177
|
+
onSort={(_event, index, direction) => {
|
|
178
|
+
setActiveSortIndex(index);
|
|
179
|
+
setActiveSortDirection(direction);
|
|
180
|
+
}}
|
|
181
|
+
cells={columns}
|
|
182
|
+
rows={rows}
|
|
183
|
+
>
|
|
184
|
+
<TableHeader />
|
|
185
|
+
<TableBody />
|
|
186
|
+
</Table>
|
|
187
|
+
</React.Fragment>
|
|
188
|
+
);
|
|
189
|
+
};
|