Skip to content

Commit f674dc5

Browse files
committed
frontend: ClusterTable/ResourceTable: Lift up tableSettings helpers and add localStorage to ClusterTable
1 parent 51c2b8b commit f674dc5

File tree

5 files changed

+235
-58
lines changed

5 files changed

+235
-58
lines changed

frontend/src/components/App/Home/ClusterTable.tsx

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ import Box from '@mui/material/Box';
1919
import Button from '@mui/material/Button';
2020
import { useTheme } from '@mui/material/styles';
2121
import Typography from '@mui/material/Typography';
22-
import { useMemo } from 'react';
22+
import {
23+
MRT_ColumnFiltersState,
24+
MRT_SortingState,
25+
MRT_VisibilityState,
26+
} from 'material-react-table';
27+
import { useCallback, useMemo, useState } from 'react';
2328
import { useTranslation } from 'react-i18next';
2429
import { generatePath, useHistory } from 'react-router-dom';
2530
import { getClusterAppearanceFromMeta } from '../../../helpers/clusterAppearance';
2631
import { isElectron } from '../../../helpers/isElectron';
32+
import { loadTableSettings, storeTableSettings } from '../../../helpers/tableSettings';
2733
import { formatClusterPathParam } from '../../../lib/cluster';
2834
import { useClustersConf, useClustersVersion } from '../../../lib/k8s';
2935
import { ApiError } from '../../../lib/k8s/api/v2/ApiError';
@@ -34,6 +40,7 @@ import { useTypedSelector } from '../../../redux/hooks';
3440
import { Loader } from '../../common';
3541
import Link from '../../common/Link';
3642
import Table from '../../common/Table';
43+
import { useLocalStorageState } from '../../globalSearch/useLocalStorageState';
3744
import ClusterBadge from '../../Sidebar/ClusterBadge';
3845
import ClusterContextMenu from './ClusterContextMenu';
3946
import { MULTI_HOME_ENABLED } from './config';
@@ -115,6 +122,8 @@ export interface ClusterTableProps {
115122
/**
116123
* ClusterTable component displays a table of clusters with their status, origin, and version.
117124
*/
125+
const CLUSTER_TABLE_ID = 'home-clusters';
126+
118127
export default function ClusterTable({
119128
customNameClusters,
120129
versions,
@@ -125,6 +134,54 @@ export default function ClusterTable({
125134
const history = useHistory();
126135
const { t } = useTranslation(['translation']);
127136

137+
const [columnVisibility, setColumnVisibility] = useState<MRT_VisibilityState>(() => {
138+
const visibility: Record<string, boolean> = {};
139+
const stored = loadTableSettings(CLUSTER_TABLE_ID);
140+
stored.forEach(({ id, show }) => (visibility[id] = show));
141+
return visibility;
142+
});
143+
144+
const [sorting, setSorting] = useLocalStorageState<MRT_SortingState>(
145+
`table_sorting.${CLUSTER_TABLE_ID}`,
146+
[{ id: 'name', desc: false }]
147+
);
148+
149+
const [columnFilters, setColumnFilters] = useLocalStorageState<MRT_ColumnFiltersState>(
150+
`table_filters.${CLUSTER_TABLE_ID}`,
151+
[]
152+
);
153+
154+
const handleColumnVisibilityChange = useCallback(
155+
(updater: MRT_VisibilityState | ((old: MRT_VisibilityState) => MRT_VisibilityState)) => {
156+
setColumnVisibility(oldCols => {
157+
const newCols = typeof updater === 'function' ? updater(oldCols) : updater;
158+
const colsToStore = Object.entries(newCols).map(([id, show]) => ({
159+
id,
160+
show: (show ?? true) as boolean,
161+
}));
162+
storeTableSettings(CLUSTER_TABLE_ID, colsToStore);
163+
return newCols;
164+
});
165+
},
166+
[]
167+
);
168+
169+
const handleSortingChange = useCallback(
170+
(updater: MRT_SortingState | ((old: MRT_SortingState) => MRT_SortingState)) => {
171+
setSorting(old => (typeof updater === 'function' ? updater(old) : updater));
172+
},
173+
[setSorting]
174+
);
175+
176+
const handleColumnFiltersChange = useCallback(
177+
(
178+
updater: MRT_ColumnFiltersState | ((old: MRT_ColumnFiltersState) => MRT_ColumnFiltersState)
179+
) => {
180+
setColumnFilters(old => (typeof updater === 'function' ? updater(old) : updater));
181+
},
182+
[setColumnFilters]
183+
);
184+
128185
/**
129186
* Gets the origin of a cluster.
130187
*
@@ -206,22 +263,29 @@ export default function ClusterTable({
206263
},
207264
},
208265
{
266+
id: 'origin',
209267
header: t('Origin'),
210268
accessorFn: cluster => getOrigin(cluster),
211269
Cell: ({ row: { original } }) => (
212270
<Typography variant="body2">{getOrigin((clusters || {})[original.name])}</Typography>
213271
),
214272
},
215273
{
274+
id: 'status',
216275
header: t('Status'),
217276
accessorFn: cluster =>
218277
errors[cluster?.name] === null ? 'Active' : errors[cluster?.name]?.message,
219278
Cell: ({ row: { original } }) => (
220279
<ClusterStatus error={errors[original.name]} cluster={original} />
221280
),
222281
},
223-
{ header: t('Warnings'), accessorFn: cluster => warningLabels[cluster?.name] },
224282
{
283+
id: 'warnings',
284+
header: t('Warnings'),
285+
accessorFn: cluster => warningLabels[cluster?.name],
286+
},
287+
{
288+
id: 'version',
225289
header: t('glossary|Kubernetes Version'),
226290
accessorFn: ({ name }) => versions[name]?.gitVersion || '⋯',
227291
},
@@ -250,9 +314,14 @@ export default function ClusterTable({
250314
}
251315
: false
252316
}
253-
initialState={{
254-
sorting: [{ id: 'name', desc: false }],
317+
state={{
318+
columnVisibility,
319+
sorting,
320+
columnFilters,
255321
}}
322+
onColumnVisibilityChange={handleColumnVisibilityChange}
323+
onSortingChange={handleSortingChange}
324+
onColumnFiltersChange={handleColumnFiltersChange}
256325
muiToolbarAlertBannerProps={{
257326
sx: theme => ({
258327
background: theme.palette.background.muted,

frontend/src/components/App/Home/__snapshots__/index.Base.stories.storyshot

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@
329329
</th>
330330
<th
331331
aria-sort="none"
332-
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-11ihby4-MuiTableCell-root"
332+
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-4ks04s-MuiTableCell-root"
333333
colspan="1"
334334
data-can-sort="true"
335335
data-index="-1"
@@ -384,7 +384,7 @@
384384
</th>
385385
<th
386386
aria-sort="none"
387-
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-9s5e2s-MuiTableCell-root"
387+
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-13c0hsi-MuiTableCell-root"
388388
colspan="1"
389389
data-can-sort="true"
390390
data-index="-1"
@@ -439,7 +439,7 @@
439439
</th>
440440
<th
441441
aria-sort="none"
442-
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-usbwln-MuiTableCell-root"
442+
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-rymsnu-MuiTableCell-root"
443443
colspan="1"
444444
data-can-sort="true"
445445
data-index="-1"
@@ -494,7 +494,7 @@
494494
</th>
495495
<th
496496
aria-sort="none"
497-
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-19o1a0j-MuiTableCell-root"
497+
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1bc3cd9-MuiTableCell-root"
498498
colspan="1"
499499
data-can-sort="true"
500500
data-index="-1"
@@ -629,7 +629,7 @@
629629
</a>
630630
</td>
631631
<td
632-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-2wxm0s-MuiTableCell-root"
632+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1fu0nlf-MuiTableCell-root"
633633
>
634634
<p
635635
class="MuiTypography-root MuiTypography-body2 css-ab4e19-MuiTypography-root"
@@ -638,7 +638,7 @@
638638
</p>
639639
</td>
640640
<td
641-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1qzqemr-MuiTableCell-root"
641+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1n4xkg0-MuiTableCell-root"
642642
>
643643
<div
644644
class="MuiBox-root css-1gtanqs"
@@ -656,12 +656,12 @@
656656
</div>
657657
</td>
658658
<td
659-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1yl63d8-MuiTableCell-root"
659+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-17iljsw-MuiTableCell-root"
660660
>
661661
0
662662
</td>
663663
<td
664-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-i01a7i-MuiTableCell-root"
664+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1qb9npk-MuiTableCell-root"
665665
>
666666
667667
</td>
@@ -737,7 +737,7 @@
737737
</a>
738738
</td>
739739
<td
740-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-2wxm0s-MuiTableCell-root"
740+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1fu0nlf-MuiTableCell-root"
741741
>
742742
<p
743743
class="MuiTypography-root MuiTypography-body2 css-ab4e19-MuiTypography-root"
@@ -746,7 +746,7 @@
746746
</p>
747747
</td>
748748
<td
749-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1qzqemr-MuiTableCell-root"
749+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1n4xkg0-MuiTableCell-root"
750750
>
751751
<div
752752
class="MuiBox-root css-1gtanqs"
@@ -764,12 +764,12 @@
764764
</div>
765765
</td>
766766
<td
767-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1yl63d8-MuiTableCell-root"
767+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-17iljsw-MuiTableCell-root"
768768
>
769769
0
770770
</td>
771771
<td
772-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-i01a7i-MuiTableCell-root"
772+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1qb9npk-MuiTableCell-root"
773773
>
774774
775775
</td>
@@ -845,7 +845,7 @@
845845
</a>
846846
</td>
847847
<td
848-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-2wxm0s-MuiTableCell-root"
848+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1fu0nlf-MuiTableCell-root"
849849
>
850850
<p
851851
class="MuiTypography-root MuiTypography-body2 css-ab4e19-MuiTypography-root"
@@ -854,7 +854,7 @@
854854
</p>
855855
</td>
856856
<td
857-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1qzqemr-MuiTableCell-root"
857+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1n4xkg0-MuiTableCell-root"
858858
>
859859
<div
860860
class="MuiBox-root css-1gtanqs"
@@ -872,12 +872,12 @@
872872
</div>
873873
</td>
874874
<td
875-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1yl63d8-MuiTableCell-root"
875+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-17iljsw-MuiTableCell-root"
876876
>
877877
0
878878
</td>
879879
<td
880-
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-i01a7i-MuiTableCell-root"
880+
class="MuiTableCell-root MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1qb9npk-MuiTableCell-root"
881881
>
882882
883883
</td>

frontend/src/components/common/Resource/ResourceTable.tsx

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
useState,
3737
} from 'react';
3838
import { useTranslation } from 'react-i18next';
39+
import { loadTableSettings, storeTableSettings } from '../../../helpers/tableSettings';
3940
import { useSelectedClusters } from '../../../lib/k8s';
4041
import { ApiError } from '../../../lib/k8s/api/v2/ApiError';
4142
import { KubeObject } from '../../../lib/k8s/KubeObject';
@@ -209,44 +210,6 @@ function TableFromResourceClass<KubeClass extends KubeObjectClass>(
209210
);
210211
}
211212

212-
/**
213-
* Store the table settings in local storage.
214-
*
215-
* @param tableId - The ID of the table.
216-
* @param columns - The columns to store.
217-
* @returns void
218-
*/
219-
function storeTableSettings(tableId: string, columns: { id?: string; show: boolean }[]) {
220-
if (!tableId) {
221-
console.debug('storeTableSettings: tableId is empty!', new Error().stack);
222-
return;
223-
}
224-
225-
const columnsWithIds = columns.map((c, i) => ({ id: i.toString(), ...c }));
226-
// Delete the entry if there are no settings to store.
227-
if (columnsWithIds.length === 0) {
228-
localStorage.removeItem(`table_settings.${tableId}`);
229-
return;
230-
}
231-
localStorage.setItem(`table_settings.${tableId}`, JSON.stringify(columnsWithIds));
232-
}
233-
234-
/**
235-
* Load the table settings from local storage for a given table ID.
236-
*
237-
* @param tableId - The ID of the table.
238-
* @returns The table settings for the given table ID.
239-
*/
240-
function loadTableSettings(tableId: string): { id: string; show: boolean }[] {
241-
if (!tableId) {
242-
console.debug('loadTableSettings: tableId is empty!', new Error().stack);
243-
return [];
244-
}
245-
246-
const settings = JSON.parse(localStorage.getItem(`table_settings.${tableId}`) || '[]');
247-
return settings;
248-
}
249-
250213
/**
251214
* Here we figure out which columns are visible and not visible
252215
* We can control it using show property in the columns prop {@link ResourceTableColumn}

0 commit comments

Comments
 (0)