@patternfly/react-table 5.2.0-prerelease.30 → 5.2.0-prerelease.32
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/components/package.json +1 -1
- package/deprecated/package.json +1 -1
- package/dist/dynamic/components/Table/package.json +1 -1
- package/dist/dynamic/components/Table/utils/decorators/package.json +1 -1
- package/dist/dynamic/components/Table/utils/package.json +1 -1
- package/dist/dynamic/deprecated/components/Table/base/package.json +1 -1
- package/dist/dynamic/deprecated/components/Table/package.json +1 -1
- package/dist/dynamic/deprecated/components/package.json +1 -1
- package/dist/esm/demos/index.d.ts +2 -0
- package/dist/esm/demos/index.d.ts.map +1 -0
- package/dist/esm/demos/index.js +2 -0
- package/dist/esm/demos/index.js.map +1 -0
- package/dist/esm/demos/sampleData.d.ts +13 -0
- package/dist/esm/demos/sampleData.d.ts.map +1 -0
- package/dist/esm/demos/sampleData.js +764 -0
- package/dist/esm/demos/sampleData.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/js/demos/index.d.ts +2 -0
- package/dist/js/demos/index.d.ts.map +1 -0
- package/dist/js/demos/index.js +5 -0
- package/dist/js/demos/index.js.map +1 -0
- package/dist/js/demos/sampleData.d.ts +13 -0
- package/dist/js/demos/sampleData.d.ts.map +1 -0
- package/dist/js/demos/sampleData.js +767 -0
- package/dist/js/demos/sampleData.js.map +1 -0
- package/dist/js/index.d.ts +1 -0
- package/dist/js/index.d.ts.map +1 -1
- package/dist/js/index.js +1 -0
- package/dist/js/index.js.map +1 -1
- package/dist/umd/react-table.min.js +3 -3
- package/package.json +3 -3
- package/src/components/Table/examples/TableStickyColumnsAndHeader.tsx +5 -4
- package/src/demos/Table.md +159 -0
- package/src/demos/examples/TableAutomaticPagination.tsx +101 -0
- package/src/{docs/demos/table-demos/BulkSelect.jsx → demos/examples/TableBulkSelect.tsx} +41 -20
- package/src/demos/examples/TableColumnManagement.tsx +486 -0
- package/src/demos/examples/TableColumnManagementWithDraggable.tsx +641 -0
- package/src/{docs/demos/table-demos/Compact.jsx → demos/examples/TableCompact.tsx} +32 -16
- package/src/{docs/demos/table-demos/CompoundExpansion.jsx → demos/examples/TableCompoundExpansion.tsx} +25 -12
- package/src/demos/examples/TableEmptyStateDefault.tsx +60 -0
- package/src/demos/examples/TableEmptyStateError.tsx +53 -0
- package/src/demos/examples/TableEmptyStateLoading.tsx +43 -0
- package/src/{docs/demos/table-demos/ExpandCollapseAll.jsx → demos/examples/TableExpandCollapseAll.tsx} +17 -10
- package/src/demos/examples/TableFilterable.tsx +394 -0
- package/src/{docs/demos/table-demos/SortableResponsive.jsx → demos/examples/TableSortableResponsive.tsx} +30 -28
- package/src/{docs/demos/table-demos/StaticBottomPagination.jsx → demos/examples/TableStaticBottomPagination.tsx} +22 -9
- package/src/{docs/demos/table-demos/StickyFirstColumn.jsx → demos/examples/TableStickyFirstColumn.tsx} +31 -9
- package/src/{docs/demos/table-demos/StickyHeader.jsx → demos/examples/TableStickyHeader.tsx} +3 -3
- package/src/demos/index.ts +1 -0
- package/src/{docs/demos/table-demos/sampleData.js → demos/sampleData.tsx} +88 -77
- package/src/index.ts +1 -0
- package/src/docs/demos/Table.md +0 -1621
- package/src/docs/demos/table-demos/ColumnManagement.jsx +0 -471
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Badge,
|
|
4
|
+
Button,
|
|
5
|
+
Bullseye,
|
|
6
|
+
EmptyState,
|
|
7
|
+
EmptyStateActions,
|
|
8
|
+
EmptyStateBody,
|
|
9
|
+
EmptyStateIcon,
|
|
10
|
+
EmptyStateHeader,
|
|
11
|
+
EmptyStateFooter,
|
|
12
|
+
Label,
|
|
13
|
+
MenuToggle,
|
|
14
|
+
MenuToggleElement,
|
|
15
|
+
Toolbar,
|
|
16
|
+
ToolbarItem,
|
|
17
|
+
ToolbarContent,
|
|
18
|
+
ToolbarFilter,
|
|
19
|
+
ToolbarToggleGroup,
|
|
20
|
+
ToolbarGroup,
|
|
21
|
+
Select,
|
|
22
|
+
SelectOption,
|
|
23
|
+
SearchInput,
|
|
24
|
+
ToolbarChipGroup
|
|
25
|
+
} from '@patternfly/react-core';
|
|
26
|
+
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
|
|
27
|
+
import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon';
|
|
28
|
+
import { Table, TableText, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';
|
|
29
|
+
import { rows, columns } from '@patternfly/react-table/dist/esm/demos/sampleData';
|
|
30
|
+
|
|
31
|
+
export const TableFilterable: React.FunctionComponent = () => {
|
|
32
|
+
const [filters, setFilters] = React.useState<Record<string, string[]>>({ location: [], name: [], status: [] });
|
|
33
|
+
const [currentCategory, setCurrentCategory] = React.useState('Status');
|
|
34
|
+
const [isFilterDropdownOpen, setIsFilterDropdownOpen] = React.useState(false);
|
|
35
|
+
const [isCategoryDropdownOpen, setIsCategoryDropdownOpen] = React.useState(false);
|
|
36
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
37
|
+
|
|
38
|
+
const rowData = rows.slice(0, 10);
|
|
39
|
+
|
|
40
|
+
const onDelete = (type: string | ToolbarChipGroup, id: string) => {
|
|
41
|
+
if (type === 'Location') {
|
|
42
|
+
setFilters({
|
|
43
|
+
...filters,
|
|
44
|
+
location: filters.location.filter((fil: string) => fil !== id)
|
|
45
|
+
});
|
|
46
|
+
} else if (type === 'Name') {
|
|
47
|
+
setFilters({
|
|
48
|
+
...filters,
|
|
49
|
+
name: filters.name.filter((fil: string) => fil !== id)
|
|
50
|
+
});
|
|
51
|
+
} else if (type === 'Status') {
|
|
52
|
+
setFilters({
|
|
53
|
+
...filters,
|
|
54
|
+
status: filters.status.filter((fil: string) => fil !== id)
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
setFilters({ location: [], name: [], status: [] });
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const onCategoryToggle = () => {
|
|
62
|
+
setIsCategoryDropdownOpen(!isCategoryDropdownOpen);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const onCategorySelect = (event: React.MouseEvent<Element, MouseEvent>, value: string) => {
|
|
66
|
+
setCurrentCategory(value);
|
|
67
|
+
setIsCategoryDropdownOpen(false);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const onFilterToggle = () => {
|
|
71
|
+
setIsFilterDropdownOpen(!isFilterDropdownOpen);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const onFilterSelect = () => {
|
|
75
|
+
setIsFilterDropdownOpen(!isFilterDropdownOpen);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const onInputChange = (newValue: string) => {
|
|
79
|
+
setInputValue(newValue);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onStatusSelect = (event: React.MouseEvent<Element, MouseEvent>, selection: string) => {
|
|
83
|
+
const checked = (event.target as HTMLInputElement).checked;
|
|
84
|
+
setFilters({
|
|
85
|
+
...filters,
|
|
86
|
+
status: checked ? [...filters.status, selection] : filters.status.filter((value) => value !== selection)
|
|
87
|
+
});
|
|
88
|
+
setIsFilterDropdownOpen(false);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const onNameInput = (event: React.KeyboardEvent) => {
|
|
92
|
+
if (event.key && event.key !== 'Enter') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const prevFilters = filters.name;
|
|
97
|
+
setFilters({ ...filters, name: prevFilters.includes(inputValue) ? prevFilters : [...prevFilters, inputValue] });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const onLocationSelect = (event: React.MouseEvent<Element, MouseEvent>, selection: string) => {
|
|
101
|
+
setFilters({ ...filters, location: [selection] });
|
|
102
|
+
|
|
103
|
+
setIsFilterDropdownOpen(false);
|
|
104
|
+
onFilterSelect();
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const buildCategoryDropdown = () => {
|
|
108
|
+
const categoryMenuItems = [
|
|
109
|
+
<SelectOption key="cat1" value="Location">
|
|
110
|
+
Location
|
|
111
|
+
</SelectOption>,
|
|
112
|
+
<SelectOption key="cat2" value="Name">
|
|
113
|
+
Name
|
|
114
|
+
</SelectOption>,
|
|
115
|
+
<SelectOption key="cat3" value="Status">
|
|
116
|
+
Status
|
|
117
|
+
</SelectOption>
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<ToolbarItem>
|
|
122
|
+
<Select
|
|
123
|
+
onSelect={onCategorySelect}
|
|
124
|
+
selected={currentCategory}
|
|
125
|
+
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
|
|
126
|
+
<MenuToggle
|
|
127
|
+
ref={toggleRef}
|
|
128
|
+
onClick={onCategoryToggle}
|
|
129
|
+
isExpanded={isCategoryDropdownOpen}
|
|
130
|
+
icon={<FilterIcon />}
|
|
131
|
+
style={
|
|
132
|
+
{
|
|
133
|
+
width: '100%',
|
|
134
|
+
verticalAlign: 'text-bottom'
|
|
135
|
+
} as React.CSSProperties
|
|
136
|
+
}
|
|
137
|
+
>
|
|
138
|
+
{currentCategory}
|
|
139
|
+
</MenuToggle>
|
|
140
|
+
)}
|
|
141
|
+
isOpen={isCategoryDropdownOpen}
|
|
142
|
+
>
|
|
143
|
+
{categoryMenuItems}
|
|
144
|
+
</Select>
|
|
145
|
+
</ToolbarItem>
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const buildFilterDropdown = () => {
|
|
150
|
+
const locationMenuItems = [
|
|
151
|
+
<SelectOption key="raleigh" value="Raleigh">
|
|
152
|
+
Raleigh
|
|
153
|
+
</SelectOption>,
|
|
154
|
+
<SelectOption key="san francisco" value="San Francisco">
|
|
155
|
+
San Francisco
|
|
156
|
+
</SelectOption>,
|
|
157
|
+
<SelectOption key="boston" value="Boston">
|
|
158
|
+
Boston
|
|
159
|
+
</SelectOption>,
|
|
160
|
+
<SelectOption key="atlanta" value="Atlanta">
|
|
161
|
+
Atlanta
|
|
162
|
+
</SelectOption>
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const statusMenuItems = [
|
|
166
|
+
<SelectOption hasCheckbox key="statusRunning" value="Running" isSelected={filters.status.includes('Running')}>
|
|
167
|
+
Running
|
|
168
|
+
</SelectOption>,
|
|
169
|
+
<SelectOption hasCheckbox key="statusStopped" value="Stopped" isSelected={filters.status.includes('Stopped')}>
|
|
170
|
+
Stopped
|
|
171
|
+
</SelectOption>,
|
|
172
|
+
<SelectOption hasCheckbox key="statusDown" value="Down" isSelected={filters.status.includes('Down')}>
|
|
173
|
+
Down
|
|
174
|
+
</SelectOption>,
|
|
175
|
+
<SelectOption hasCheckbox key="statusDegraded" value="Degraded" isSelected={filters.status.includes('Degraded')}>
|
|
176
|
+
Degraded
|
|
177
|
+
</SelectOption>,
|
|
178
|
+
<SelectOption
|
|
179
|
+
hasCheckbox
|
|
180
|
+
key="statusMaint"
|
|
181
|
+
value="Needs maintenance"
|
|
182
|
+
isSelected={filters.status.includes('Needs maintenance')}
|
|
183
|
+
>
|
|
184
|
+
Needs maintenance
|
|
185
|
+
</SelectOption>
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<React.Fragment>
|
|
190
|
+
<ToolbarFilter
|
|
191
|
+
chips={filters.location}
|
|
192
|
+
deleteChip={(category, chip) => onDelete(category, chip as string)}
|
|
193
|
+
categoryName="Location"
|
|
194
|
+
showToolbarItem={currentCategory === 'Location'}
|
|
195
|
+
>
|
|
196
|
+
<Select
|
|
197
|
+
aria-label="Location"
|
|
198
|
+
onSelect={onLocationSelect}
|
|
199
|
+
selected={filters.location[0]}
|
|
200
|
+
isOpen={isFilterDropdownOpen}
|
|
201
|
+
popperProps={{ minWidth: '100px' }}
|
|
202
|
+
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
|
|
203
|
+
<MenuToggle
|
|
204
|
+
ref={toggleRef}
|
|
205
|
+
onClick={onFilterToggle}
|
|
206
|
+
isExpanded={isFilterDropdownOpen}
|
|
207
|
+
style={
|
|
208
|
+
{
|
|
209
|
+
width: '100%',
|
|
210
|
+
verticalAlign: 'text-bottom'
|
|
211
|
+
} as React.CSSProperties
|
|
212
|
+
}
|
|
213
|
+
>
|
|
214
|
+
{filters.location[0] || `Any`}
|
|
215
|
+
</MenuToggle>
|
|
216
|
+
)}
|
|
217
|
+
>
|
|
218
|
+
{locationMenuItems}
|
|
219
|
+
</Select>
|
|
220
|
+
</ToolbarFilter>
|
|
221
|
+
<ToolbarFilter
|
|
222
|
+
chips={filters.name}
|
|
223
|
+
deleteChip={(category, chip) => onDelete(category, chip as string)}
|
|
224
|
+
categoryName="Name"
|
|
225
|
+
showToolbarItem={currentCategory === 'Name'}
|
|
226
|
+
>
|
|
227
|
+
<SearchInput
|
|
228
|
+
aria-label="name filter"
|
|
229
|
+
placeholder="Filter by name..."
|
|
230
|
+
onChange={(_event, value) => onInputChange(value)}
|
|
231
|
+
value={inputValue}
|
|
232
|
+
onClear={() => {
|
|
233
|
+
onInputChange('');
|
|
234
|
+
}}
|
|
235
|
+
onSearch={onNameInput}
|
|
236
|
+
/>
|
|
237
|
+
</ToolbarFilter>
|
|
238
|
+
<ToolbarFilter
|
|
239
|
+
chips={filters.status}
|
|
240
|
+
deleteChip={(category, chip) => onDelete(category, chip as string)}
|
|
241
|
+
categoryName="Status"
|
|
242
|
+
showToolbarItem={currentCategory === 'Status'}
|
|
243
|
+
>
|
|
244
|
+
<Select
|
|
245
|
+
aria-label="Status"
|
|
246
|
+
isOpen={isFilterDropdownOpen}
|
|
247
|
+
popperProps={{ minWidth: '100px' }}
|
|
248
|
+
onSelect={onStatusSelect}
|
|
249
|
+
selected={filters.status}
|
|
250
|
+
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
|
|
251
|
+
<MenuToggle
|
|
252
|
+
ref={toggleRef}
|
|
253
|
+
onClick={onFilterToggle}
|
|
254
|
+
isExpanded={isFilterDropdownOpen}
|
|
255
|
+
style={
|
|
256
|
+
{
|
|
257
|
+
width: '100%',
|
|
258
|
+
verticalAlign: 'text-bottom'
|
|
259
|
+
} as React.CSSProperties
|
|
260
|
+
}
|
|
261
|
+
>
|
|
262
|
+
Filter by status
|
|
263
|
+
{filters.status.length > 0 && <Badge isRead>{filters.status.length}</Badge>}
|
|
264
|
+
</MenuToggle>
|
|
265
|
+
)}
|
|
266
|
+
>
|
|
267
|
+
{statusMenuItems}
|
|
268
|
+
</Select>
|
|
269
|
+
</ToolbarFilter>
|
|
270
|
+
</React.Fragment>
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const renderToolbar = () => (
|
|
275
|
+
<Toolbar
|
|
276
|
+
id="toolbar-with-chip-groups"
|
|
277
|
+
clearAllFilters={() => setFilters({ location: [], name: [], status: [] })}
|
|
278
|
+
collapseListedFiltersBreakpoint="xl"
|
|
279
|
+
>
|
|
280
|
+
<ToolbarContent>
|
|
281
|
+
<ToolbarToggleGroup toggleIcon={<FilterIcon />} breakpoint="xl">
|
|
282
|
+
<ToolbarGroup
|
|
283
|
+
variant="filter-group"
|
|
284
|
+
style={
|
|
285
|
+
{
|
|
286
|
+
lineHeight: '22px',
|
|
287
|
+
alignItems: 'center'
|
|
288
|
+
} as React.CSSProperties
|
|
289
|
+
}
|
|
290
|
+
>
|
|
291
|
+
{buildCategoryDropdown()}
|
|
292
|
+
{buildFilterDropdown()}
|
|
293
|
+
</ToolbarGroup>
|
|
294
|
+
</ToolbarToggleGroup>
|
|
295
|
+
</ToolbarContent>
|
|
296
|
+
</Toolbar>
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const filteredRows =
|
|
300
|
+
filters.name.length > 0 || filters.location.length > 0 || filters.status.length > 0
|
|
301
|
+
? rowData.filter(
|
|
302
|
+
(row) =>
|
|
303
|
+
(filters.name.length === 0 ||
|
|
304
|
+
filters.name.some((name) => row.name.toLowerCase().includes(name.toLowerCase()))) &&
|
|
305
|
+
(filters.location.length === 0 || filters.location.includes(row.location)) &&
|
|
306
|
+
(filters.status.length === 0 || filters.status.includes(row.status))
|
|
307
|
+
)
|
|
308
|
+
: rowData;
|
|
309
|
+
|
|
310
|
+
const emptyState = (
|
|
311
|
+
<EmptyState>
|
|
312
|
+
<EmptyStateHeader
|
|
313
|
+
titleText="Clear all filters and try again."
|
|
314
|
+
headingLevel="h5"
|
|
315
|
+
icon={<EmptyStateIcon icon={SearchIcon} />}
|
|
316
|
+
/>
|
|
317
|
+
<EmptyStateBody>No results match the filter criteria. Clear all filters and try again.</EmptyStateBody>
|
|
318
|
+
<EmptyStateFooter>
|
|
319
|
+
<EmptyStateActions>
|
|
320
|
+
<Button
|
|
321
|
+
variant="link"
|
|
322
|
+
onClick={() => {
|
|
323
|
+
setFilters({ location: [], name: [], status: [] });
|
|
324
|
+
}}
|
|
325
|
+
>
|
|
326
|
+
Clear all filters
|
|
327
|
+
</Button>
|
|
328
|
+
</EmptyStateActions>
|
|
329
|
+
</EmptyStateFooter>
|
|
330
|
+
</EmptyState>
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const renderLabel = (labelText: string) => {
|
|
334
|
+
switch (labelText) {
|
|
335
|
+
case 'Running':
|
|
336
|
+
return <Label color="green">{labelText}</Label>;
|
|
337
|
+
case 'Stopped':
|
|
338
|
+
return <Label color="orange">{labelText}</Label>;
|
|
339
|
+
case 'Needs maintenance':
|
|
340
|
+
return <Label color="blue">{labelText}</Label>;
|
|
341
|
+
case 'Down':
|
|
342
|
+
return <Label color="red">{labelText}</Label>;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<React.Fragment>
|
|
348
|
+
{renderToolbar()}
|
|
349
|
+
<Table aria-label="Filterable Table Demo">
|
|
350
|
+
<Thead>
|
|
351
|
+
<Tr>
|
|
352
|
+
<Th key={0}>{columns[0]}</Th>
|
|
353
|
+
<Th key={1}>{columns[1]}</Th>
|
|
354
|
+
<Th key={2}>{columns[2]}</Th>
|
|
355
|
+
<Th key={3}>{columns[3]}</Th>
|
|
356
|
+
<Th key={4}>{columns[4]}</Th>
|
|
357
|
+
<Th key={5}>{columns[5]}</Th>
|
|
358
|
+
<Th key={6}>{columns[6]}</Th>
|
|
359
|
+
<Th key={7} width={10}>
|
|
360
|
+
{columns[7]}
|
|
361
|
+
</Th>
|
|
362
|
+
</Tr>
|
|
363
|
+
</Thead>
|
|
364
|
+
<Tbody>
|
|
365
|
+
{filteredRows.slice(0, 10).map((row, rowIndex) => (
|
|
366
|
+
<Tr key={rowIndex}>
|
|
367
|
+
<>
|
|
368
|
+
<Td dataLabel={columns[0]}>{row.name}</Td>
|
|
369
|
+
<Td dataLabel={columns[1]}>{row.threads}</Td>
|
|
370
|
+
<Td dataLabel={columns[2]}>{row.applications}</Td>
|
|
371
|
+
<Td dataLabel={columns[3]}>{row.workspaces}</Td>
|
|
372
|
+
<Td dataLabel={columns[4]}>{renderLabel(row.status)}</Td>
|
|
373
|
+
<Td dataLabel={columns[5]}>{row.location}</Td>
|
|
374
|
+
<Td dataLabel={columns[6]}>{row.lastModified}</Td>
|
|
375
|
+
<Td dataLabel={columns[7]} modifier="truncate">
|
|
376
|
+
<TableText>
|
|
377
|
+
<a href="#">{row.url}</a>
|
|
378
|
+
</TableText>
|
|
379
|
+
</Td>
|
|
380
|
+
</>
|
|
381
|
+
</Tr>
|
|
382
|
+
))}
|
|
383
|
+
{filteredRows.length === 0 && (
|
|
384
|
+
<Tr>
|
|
385
|
+
<Td colSpan={8}>
|
|
386
|
+
<Bullseye>{emptyState}</Bullseye>
|
|
387
|
+
</Td>
|
|
388
|
+
</Tr>
|
|
389
|
+
)}
|
|
390
|
+
</Tbody>
|
|
391
|
+
</Table>
|
|
392
|
+
</React.Fragment>
|
|
393
|
+
);
|
|
394
|
+
};
|
|
@@ -7,19 +7,10 @@ import {
|
|
|
7
7
|
DropdownList,
|
|
8
8
|
Flex,
|
|
9
9
|
FlexItem,
|
|
10
|
-
Label,
|
|
11
10
|
MenuToggle,
|
|
12
11
|
MenuToggleElement,
|
|
13
|
-
OverflowMenu,
|
|
14
|
-
OverflowMenuContent,
|
|
15
|
-
OverflowMenuControl,
|
|
16
|
-
OverflowMenuDropdownItem,
|
|
17
|
-
OverflowMenuGroup,
|
|
18
|
-
OverflowMenuItem,
|
|
19
12
|
PageSection,
|
|
20
|
-
PageSectionVariants,
|
|
21
13
|
Pagination,
|
|
22
|
-
Select,
|
|
23
14
|
SelectOption,
|
|
24
15
|
SelectList,
|
|
25
16
|
SelectGroup,
|
|
@@ -28,7 +19,17 @@ import {
|
|
|
28
19
|
Toolbar,
|
|
29
20
|
ToolbarContent,
|
|
30
21
|
ToolbarGroup,
|
|
31
|
-
ToolbarItem
|
|
22
|
+
ToolbarItem,
|
|
23
|
+
OverflowMenuDropdownItem,
|
|
24
|
+
PaginationVariant,
|
|
25
|
+
Label,
|
|
26
|
+
Select,
|
|
27
|
+
OverflowMenu,
|
|
28
|
+
OverflowMenuContent,
|
|
29
|
+
OverflowMenuControl,
|
|
30
|
+
OverflowMenuGroup,
|
|
31
|
+
OverflowMenuItem,
|
|
32
|
+
PageSectionVariants
|
|
32
33
|
} from '@patternfly/react-core';
|
|
33
34
|
import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';
|
|
34
35
|
import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon';
|
|
@@ -38,15 +39,17 @@ import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon';
|
|
|
38
39
|
import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
|
|
39
40
|
import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amount-down-icon';
|
|
40
41
|
import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
|
|
41
|
-
import { DashboardWrapper } from '@patternfly/react-core/
|
|
42
|
+
import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper';
|
|
42
43
|
import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
|
|
43
|
-
import { rows, columns } from '@patternfly/react-table/
|
|
44
|
+
import { rows, columns, SampleDataRow } from '@patternfly/react-table/dist/esm/demos/sampleData';
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
type Direction = 'asc' | 'desc' | undefined;
|
|
47
|
+
|
|
48
|
+
export const TableSortableResponsive: React.FunctionComponent = () => {
|
|
46
49
|
const [isKebabDropdownOpen, setIsKebabDropdownOpen] = React.useState(false);
|
|
47
50
|
|
|
48
|
-
const sortRows = (
|
|
49
|
-
|
|
51
|
+
const sortRows = (rows: SampleDataRow[], sortIndex: number, sortDirection: Direction) =>
|
|
52
|
+
[...rows].sort((a, b) => {
|
|
50
53
|
let returnValue = 0;
|
|
51
54
|
if (sortIndex === 0 || sortIndex === 7) {
|
|
52
55
|
returnValue = 1;
|
|
@@ -62,7 +65,6 @@ export const ComposableTableSortable = () => {
|
|
|
62
65
|
}
|
|
63
66
|
return returnValue;
|
|
64
67
|
});
|
|
65
|
-
};
|
|
66
68
|
|
|
67
69
|
const [sortedData, setSortedData] = React.useState([...sortRows(rows, 0, 'asc')]);
|
|
68
70
|
const [sortedRows, setSortedRows] = React.useState([...sortedData]);
|
|
@@ -72,11 +74,11 @@ export const ComposableTableSortable = () => {
|
|
|
72
74
|
// index of the currently active column
|
|
73
75
|
const [activeSortIndex, setActiveSortIndex] = React.useState(0);
|
|
74
76
|
// sort direction of the currently active column
|
|
75
|
-
const [activeSortDirection, setActiveSortDirection] = React.useState('asc');
|
|
77
|
+
const [activeSortDirection, setActiveSortDirection] = React.useState<Direction>('asc');
|
|
76
78
|
// sort dropdown expansion
|
|
77
79
|
const [isSortDropdownOpen, setIsSortDropdownOpen] = React.useState(false);
|
|
78
80
|
|
|
79
|
-
const onSort = (
|
|
81
|
+
const onSort = (_event: any, index: number, direction: Direction) => {
|
|
80
82
|
setActiveSortIndex(index);
|
|
81
83
|
setActiveSortDirection(direction);
|
|
82
84
|
|
|
@@ -89,17 +91,17 @@ export const ComposableTableSortable = () => {
|
|
|
89
91
|
setSortedRows(sortedData.slice((page - 1) * perPage, page * perPage - 1));
|
|
90
92
|
}, [sortedData, page, perPage]);
|
|
91
93
|
|
|
92
|
-
const handleSetPage = (_evt, newPage) => {
|
|
94
|
+
const handleSetPage = (_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => {
|
|
93
95
|
setPage(newPage);
|
|
94
96
|
};
|
|
95
97
|
|
|
96
|
-
const handlePerPageSelect = (_evt, newPerPage) => {
|
|
98
|
+
const handlePerPageSelect = (_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPerPage: number) => {
|
|
97
99
|
setPerPage(newPerPage);
|
|
98
100
|
};
|
|
99
101
|
|
|
100
|
-
const renderPagination = (variant) => (
|
|
102
|
+
const renderPagination = (variant: 'top' | 'bottom' | PaginationVariant, isCompact: boolean) => (
|
|
101
103
|
<Pagination
|
|
102
|
-
isCompact
|
|
104
|
+
isCompact={isCompact}
|
|
103
105
|
itemCount={rows.length}
|
|
104
106
|
page={page}
|
|
105
107
|
perPage={perPage}
|
|
@@ -118,7 +120,7 @@ export const ComposableTableSortable = () => {
|
|
|
118
120
|
/>
|
|
119
121
|
);
|
|
120
122
|
|
|
121
|
-
const renderLabel = (labelText) => {
|
|
123
|
+
const renderLabel = (labelText: string) => {
|
|
122
124
|
switch (labelText) {
|
|
123
125
|
case 'Running':
|
|
124
126
|
return <Label color="green">{labelText}</Label>;
|
|
@@ -138,12 +140,12 @@ export const ComposableTableSortable = () => {
|
|
|
138
140
|
<Select
|
|
139
141
|
isOpen={isSortDropdownOpen}
|
|
140
142
|
selected={[activeSortDirection, activeSortIndex]}
|
|
141
|
-
onOpenChange={(isOpen) => setIsSortDropdownOpen(isOpen)}
|
|
142
|
-
onSelect={(event, value) => {
|
|
143
|
+
onOpenChange={(isOpen: boolean) => setIsSortDropdownOpen(isOpen)}
|
|
144
|
+
onSelect={(event: React.MouseEvent<Element, MouseEvent>, value: string | number) => {
|
|
143
145
|
if (value === 'asc' || value === 'desc') {
|
|
144
146
|
onSort(event, activeSortIndex, value);
|
|
145
147
|
} else {
|
|
146
|
-
onSort(event, value
|
|
148
|
+
onSort(event, value as number, activeSortDirection ?? 'asc');
|
|
147
149
|
}
|
|
148
150
|
}}
|
|
149
151
|
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
|
|
@@ -193,8 +195,8 @@ export const ComposableTableSortable = () => {
|
|
|
193
195
|
<OverflowMenuControl hasAdditionalOptions>
|
|
194
196
|
<Dropdown
|
|
195
197
|
onSelect={() => setIsKebabDropdownOpen(!isKebabDropdownOpen)}
|
|
196
|
-
onOpenChange={(isKebabDropdownOpen) => setIsKebabDropdownOpen(isKebabDropdownOpen)}
|
|
197
|
-
toggle={(toggleRef) => (
|
|
198
|
+
onOpenChange={(isKebabDropdownOpen: boolean) => setIsKebabDropdownOpen(isKebabDropdownOpen)}
|
|
199
|
+
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
|
|
198
200
|
<MenuToggle
|
|
199
201
|
ref={toggleRef}
|
|
200
202
|
aria-label="overflow menu"
|
|
@@ -13,29 +13,42 @@ import {
|
|
|
13
13
|
MenuToggleElement,
|
|
14
14
|
Label,
|
|
15
15
|
Select,
|
|
16
|
-
SelectOption
|
|
16
|
+
SelectOption,
|
|
17
|
+
PaginationVariant
|
|
17
18
|
} from '@patternfly/react-core';
|
|
18
19
|
import { Table, TableText, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';
|
|
19
20
|
import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon';
|
|
20
|
-
import { DashboardWrapper } from '@patternfly/react-core/
|
|
21
|
-
import { rows, columns } from '@patternfly/react-table/
|
|
21
|
+
import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper';
|
|
22
|
+
import { rows, columns } from '@patternfly/react-table/dist/esm/demos/sampleData';
|
|
22
23
|
|
|
23
|
-
export const
|
|
24
|
+
export const TableStaticBottomPagination: React.FunctionComponent = () => {
|
|
24
25
|
const [isSelectOpen, setIsSelectOpen] = React.useState(false);
|
|
25
26
|
const [page, setPage] = React.useState(1);
|
|
26
27
|
const [perPage, setPerPage] = React.useState(10);
|
|
27
28
|
const [paginatedRows, setPaginatedRows] = React.useState(rows.slice(0, 10));
|
|
28
|
-
const handleSetPage = (
|
|
29
|
+
const handleSetPage = (
|
|
30
|
+
_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
|
31
|
+
newPage: number,
|
|
32
|
+
_perPage: number,
|
|
33
|
+
startIdx: number,
|
|
34
|
+
endIdx: number
|
|
35
|
+
) => {
|
|
29
36
|
setPaginatedRows(rows.slice(startIdx, endIdx));
|
|
30
37
|
setPage(newPage);
|
|
31
38
|
};
|
|
32
|
-
handlePerPageSelect = (
|
|
39
|
+
const handlePerPageSelect = (
|
|
40
|
+
_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
|
41
|
+
newPerPage: number,
|
|
42
|
+
newPage: number,
|
|
43
|
+
startIdx: number,
|
|
44
|
+
endIdx: number
|
|
45
|
+
) => {
|
|
33
46
|
setPaginatedRows(rows.slice(startIdx, endIdx));
|
|
34
47
|
setPage(newPage);
|
|
35
48
|
setPerPage(newPerPage);
|
|
36
49
|
};
|
|
37
50
|
|
|
38
|
-
const renderPagination = (variant, isCompact) => (
|
|
51
|
+
const renderPagination = (variant: PaginationVariant | 'bottom' | 'top', isCompact: boolean) => (
|
|
39
52
|
<Pagination
|
|
40
53
|
isCompact={isCompact}
|
|
41
54
|
itemCount={rows.length}
|
|
@@ -51,7 +64,7 @@ export const StaticBottomPagination = () => {
|
|
|
51
64
|
/>
|
|
52
65
|
);
|
|
53
66
|
|
|
54
|
-
const renderLabel = (labelText) => {
|
|
67
|
+
const renderLabel = (labelText: string) => {
|
|
55
68
|
switch (labelText) {
|
|
56
69
|
case 'Running':
|
|
57
70
|
return (
|
|
@@ -93,7 +106,7 @@ export const StaticBottomPagination = () => {
|
|
|
93
106
|
</MenuToggle>
|
|
94
107
|
)}
|
|
95
108
|
isOpen={isSelectOpen}
|
|
96
|
-
onOpenChange={(isOpen) => setIsSelectOpen(isOpen)}
|
|
109
|
+
onOpenChange={(isOpen: boolean) => setIsSelectOpen(isOpen)}
|
|
97
110
|
onSelect={() => setIsSelectOpen(!isSelectOpen)}
|
|
98
111
|
>
|
|
99
112
|
{[
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer } from '@patternfly/react-table';
|
|
3
3
|
import { Card, PageSection } from '@patternfly/react-core';
|
|
4
|
-
import { DashboardWrapper } from '@patternfly/react-core/
|
|
4
|
+
import { DashboardWrapper } from '@patternfly/react-core/dist/esm/demos/DashboardWrapper';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type Direction = 'asc' | 'desc' | undefined;
|
|
7
|
+
|
|
8
|
+
interface Fact {
|
|
9
|
+
name: string;
|
|
10
|
+
state: string;
|
|
11
|
+
detail1: string;
|
|
12
|
+
detail2: string;
|
|
13
|
+
detail3: string;
|
|
14
|
+
detail4: string;
|
|
15
|
+
detail5: string;
|
|
16
|
+
detail6: string;
|
|
17
|
+
detail7: string;
|
|
18
|
+
detail8: string;
|
|
19
|
+
detail9: string;
|
|
20
|
+
detail10: string;
|
|
21
|
+
detail11: string;
|
|
22
|
+
detail12: string;
|
|
23
|
+
detail13: string;
|
|
24
|
+
detail14: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const TableStickyFirstColumn = () => {
|
|
7
28
|
const facts = Array.from(
|
|
8
29
|
{
|
|
9
30
|
length: 9
|
|
@@ -45,9 +66,10 @@ export const StickyFirstColumn = () => {
|
|
|
45
66
|
header15: 'Header 15',
|
|
46
67
|
header16: 'Header 16'
|
|
47
68
|
};
|
|
48
|
-
|
|
49
|
-
const [
|
|
50
|
-
const
|
|
69
|
+
|
|
70
|
+
const [activeSortIndex, setActiveSortIndex] = React.useState(-1);
|
|
71
|
+
const [activeSortDirection, setActiveSortDirection] = React.useState<Direction>('asc');
|
|
72
|
+
const getSortableRowValues = (fact: Fact) => {
|
|
51
73
|
const {
|
|
52
74
|
name,
|
|
53
75
|
state,
|
|
@@ -86,7 +108,7 @@ export const StickyFirstColumn = () => {
|
|
|
86
108
|
];
|
|
87
109
|
};
|
|
88
110
|
let sortedFacts = facts;
|
|
89
|
-
if (activeSortIndex
|
|
111
|
+
if (activeSortIndex > -1) {
|
|
90
112
|
sortedFacts = facts.sort((a, b) => {
|
|
91
113
|
const aValue = getSortableRowValues(a)[activeSortIndex];
|
|
92
114
|
const bValue = getSortableRowValues(b)[activeSortIndex];
|
|
@@ -100,12 +122,12 @@ export const StickyFirstColumn = () => {
|
|
|
100
122
|
}
|
|
101
123
|
});
|
|
102
124
|
}
|
|
103
|
-
const getSortParams = columnIndex => ({
|
|
125
|
+
const getSortParams = (columnIndex: number) => ({
|
|
104
126
|
sortBy: {
|
|
105
127
|
index: activeSortIndex,
|
|
106
128
|
direction: activeSortDirection
|
|
107
129
|
},
|
|
108
|
-
onSort: (_event, index, direction) => {
|
|
130
|
+
onSort: (_event: React.MouseEvent, index: number, direction: Direction) => {
|
|
109
131
|
setActiveSortIndex(index);
|
|
110
132
|
setActiveSortDirection(direction);
|
|
111
133
|
},
|
|
@@ -148,7 +170,7 @@ export const StickyFirstColumn = () => {
|
|
|
148
170
|
</Tr>
|
|
149
171
|
</Thead>
|
|
150
172
|
<Tbody>
|
|
151
|
-
{sortedFacts.map(fact => (
|
|
173
|
+
{sortedFacts.map((fact) => (
|
|
152
174
|
<Tr key={fact.name}>
|
|
153
175
|
<Th isStickyColumn stickyMinWidth="100px" hasRightBorder modifier="truncate">
|
|
154
176
|
{fact.name}
|