Skip to content

Commit 4b35fd4

Browse files
committed
frontend: RecentClusters: Add multi cluster selection feature
If there are multiple clusters then this allows selecting up to three of them from the recent clusters list. If there is a single cluster it acts as before. This feature can be enabled by using: - `REACT_APP_MULTI_ENABLED=true make run-frontend` Signed-off-by: René Dudfield <[email protected]>
1 parent 2596766 commit 4b35fd4

7 files changed

+362
-421
lines changed

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

+73-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import Grid from '@mui/material/Grid';
1+
import { Button, Grid, ToggleButton as MuiToggledButton, ToggleButtonGroup } from '@mui/material';
2+
import { styled } from '@mui/system';
23
import React from 'react';
34
import { useTranslation } from 'react-i18next';
45
import { generatePath, useHistory } from 'react-router-dom';
@@ -8,6 +9,13 @@ import { createRouteURL } from '../../../lib/router';
89
import { getClusterPrefixedPath } from '../../../lib/util';
910
import SquareButton from './SquareButton';
1011

12+
const ToggleButton = styled(MuiToggledButton)({
13+
textTransform: 'none',
14+
});
15+
16+
/** Allow selecting multiple recent clusters. */
17+
const MULTI_ENABLED = import.meta.env.REACT_APP_MULTI_ENABLED === 'true' || true;
18+
1119
interface ClusterButtonProps extends React.PropsWithChildren<{}> {
1220
/** The cluster to display this button for. */
1321
cluster: Cluster;
@@ -49,6 +57,8 @@ export default function RecentClusters(props: RecentClustersProps) {
4957
}
5058
}, []);
5159
const { t } = useTranslation();
60+
const [selectedClusters, setSelectedClusters] = React.useState<Cluster[]>([]);
61+
5262
const recentClustersLabelId = 'recent-clusters-label';
5363
const maxRecentClusters = 3;
5464
// We slice it here for the maximum recent clusters just for extra safety, since this
@@ -85,6 +95,33 @@ export default function RecentClusters(props: RecentClustersProps) {
8595
});
8696
}
8797

98+
/**
99+
* Callback for when different clusters are toggled.
100+
*
101+
* @param event - The event that triggered the toggle.
102+
* @param clusters - The clusters that are toggled.
103+
*/
104+
function onClustersToggled(event: React.MouseEvent<HTMLElement>, clusters: Cluster[]) {
105+
setSelectedClusters(clusters);
106+
}
107+
108+
/**
109+
* Callback for when the "View" button is clicked. It will navigate to the selected clusters.
110+
*/
111+
function onViewClusters() {
112+
selectedClusters.forEach(cluster => {
113+
helpers.setRecentCluster(cluster);
114+
});
115+
116+
history.push({
117+
pathname: generatePath(getClusterPrefixedPath(), {
118+
cluster: selectedClusters.map(cluster => cluster.name).join('+'),
119+
}),
120+
});
121+
}
122+
123+
const doMulti = recentClusters.length > 1 && MULTI_ENABLED;
124+
88125
return (
89126
<Grid
90127
aria-labelledby={`#${recentClustersLabelId}`}
@@ -93,15 +130,42 @@ export default function RecentClusters(props: RecentClustersProps) {
93130
alignItems="flex-start"
94131
spacing={2}
95132
>
96-
{recentClusters.map((cluster, i) => (
97-
<Grid item key={cluster.name}>
98-
<ClusterButton
99-
focusedRef={i === 0 ? focusedRef : undefined}
100-
cluster={cluster}
101-
onClick={() => onClusterButtonClicked(cluster)}
102-
/>
133+
{!doMulti &&
134+
recentClusters.map((cluster, i) => (
135+
<Grid item key={cluster.name}>
136+
<ClusterButton
137+
focusedRef={i === 0 ? focusedRef : undefined}
138+
cluster={cluster}
139+
onClick={() => onClusterButtonClicked(cluster)}
140+
/>
141+
</Grid>
142+
))}
143+
{doMulti && (
144+
<Grid container item alignItems="center">
145+
<ToggleButtonGroup
146+
value={selectedClusters}
147+
onChange={onClustersToggled}
148+
aria-label="selected clusters"
149+
exclusive={false}
150+
>
151+
{recentClusters.map(cluster => (
152+
<ToggleButton key={cluster.name} value={cluster}>
153+
{cluster.name}
154+
</ToggleButton>
155+
))}
156+
</ToggleButtonGroup>
157+
<Grid item pl={2}>
158+
<Button
159+
variant="contained"
160+
disabled={selectedClusters.length < 1}
161+
color="primary"
162+
onClick={onViewClusters}
163+
>
164+
View
165+
</Button>
166+
</Grid>
103167
</Grid>
104-
))}
168+
)}
105169
{helpers.isElectron() && (
106170
<Grid item>
107171
<SquareButton

frontend/src/components/App/Home/__snapshots__/RecentClusters.MoreThanThreeClusters.stories.storyshot

+50-73
Original file line numberDiff line numberDiff line change
@@ -5,85 +5,62 @@
55
class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-spacing-xs-2 css-1vj4bu4-MuiGrid-root"
66
>
77
<div
8-
class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root"
8+
class="MuiGrid-root MuiGrid-container MuiGrid-item css-lx31tv-MuiGrid-root"
99
>
10-
<button
11-
class="MuiButtonBase-root css-10d1a0h-MuiButtonBase-root"
12-
tabindex="0"
13-
type="button"
10+
<div
11+
aria-label="selected clusters"
12+
class="MuiToggleButtonGroup-root css-bghv3y-MuiToggleButtonGroup-root"
13+
role="group"
1414
>
15-
<div
16-
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-1o0qjyj-MuiPaper-root-MuiCard-root"
15+
<button
16+
aria-pressed="false"
17+
class="MuiButtonBase-root MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal MuiToggleButton-root MuiToggleButton-sizeMedium MuiToggleButton-standard MuiToggleButtonGroup-firstButton css-1tck97h-MuiButtonBase-root-MuiToggleButton-root"
18+
tabindex="0"
19+
type="button"
20+
value="[object Object]"
1721
>
18-
<div
19-
class="MuiCardContent-root css-d64700-MuiCardContent-root"
20-
>
21-
<p
22-
class="MuiTypography-root MuiTypography-body1 MuiTypography-gutterBottom css-qscjxq-MuiTypography-root"
23-
title="cluster0"
24-
>
25-
cluster0
26-
</p>
27-
</div>
28-
</div>
29-
<span
30-
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
31-
/>
32-
</button>
33-
</div>
34-
<div
35-
class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root"
36-
>
37-
<button
38-
class="MuiButtonBase-root css-10d1a0h-MuiButtonBase-root"
39-
tabindex="0"
40-
type="button"
41-
>
42-
<div
43-
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-1o0qjyj-MuiPaper-root-MuiCard-root"
22+
cluster0
23+
<span
24+
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
25+
/>
26+
</button>
27+
<button
28+
aria-pressed="false"
29+
class="MuiButtonBase-root MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal MuiToggleButton-root MuiToggleButton-sizeMedium MuiToggleButton-standard MuiToggleButtonGroup-middleButton css-1tck97h-MuiButtonBase-root-MuiToggleButton-root"
30+
tabindex="0"
31+
type="button"
32+
value="[object Object]"
4433
>
45-
<div
46-
class="MuiCardContent-root css-d64700-MuiCardContent-root"
47-
>
48-
<p
49-
class="MuiTypography-root MuiTypography-body1 MuiTypography-gutterBottom css-qscjxq-MuiTypography-root"
50-
title="cluster1"
51-
>
52-
cluster1
53-
</p>
54-
</div>
55-
</div>
56-
<span
57-
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
58-
/>
59-
</button>
60-
</div>
61-
<div
62-
class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root"
63-
>
64-
<button
65-
class="MuiButtonBase-root css-10d1a0h-MuiButtonBase-root"
66-
tabindex="0"
67-
type="button"
34+
cluster1
35+
<span
36+
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
37+
/>
38+
</button>
39+
<button
40+
aria-pressed="false"
41+
class="MuiButtonBase-root MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal MuiToggleButton-root MuiToggleButton-sizeMedium MuiToggleButton-standard MuiToggleButtonGroup-lastButton css-1tck97h-MuiButtonBase-root-MuiToggleButton-root"
42+
tabindex="0"
43+
type="button"
44+
value="[object Object]"
45+
>
46+
cluster2
47+
<span
48+
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
49+
/>
50+
</button>
51+
</div>
52+
<div
53+
class="MuiGrid-root MuiGrid-item css-1oluiat-MuiGrid-root"
6854
>
69-
<div
70-
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-1o0qjyj-MuiPaper-root-MuiCard-root"
55+
<button
56+
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary Mui-disabled MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary css-19g3xkx-MuiButtonBase-root-MuiButton-root"
57+
disabled=""
58+
tabindex="-1"
59+
type="button"
7160
>
72-
<div
73-
class="MuiCardContent-root css-d64700-MuiCardContent-root"
74-
>
75-
<p
76-
class="MuiTypography-root MuiTypography-body1 MuiTypography-gutterBottom css-qscjxq-MuiTypography-root"
77-
title="cluster2"
78-
>
79-
cluster2
80-
</p>
81-
</div>
82-
</div>
83-
<span
84-
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
85-
/>
86-
</button>
61+
View
62+
</button>
63+
</div>
8764
</div>
8865
</div>
8966
</div>

frontend/src/components/App/Home/__snapshots__/RecentClusters.OneRecentCluster.stories.storyshot

+50-73
Original file line numberDiff line numberDiff line change
@@ -5,85 +5,62 @@
55
class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-spacing-xs-2 css-1vj4bu4-MuiGrid-root"
66
>
77
<div
8-
class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root"
8+
class="MuiGrid-root MuiGrid-container MuiGrid-item css-lx31tv-MuiGrid-root"
99
>
10-
<button
11-
class="MuiButtonBase-root css-10d1a0h-MuiButtonBase-root"
12-
tabindex="0"
13-
type="button"
10+
<div
11+
aria-label="selected clusters"
12+
class="MuiToggleButtonGroup-root css-bghv3y-MuiToggleButtonGroup-root"
13+
role="group"
1414
>
15-
<div
16-
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-1o0qjyj-MuiPaper-root-MuiCard-root"
15+
<button
16+
aria-pressed="false"
17+
class="MuiButtonBase-root MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal MuiToggleButton-root MuiToggleButton-sizeMedium MuiToggleButton-standard MuiToggleButtonGroup-firstButton css-1tck97h-MuiButtonBase-root-MuiToggleButton-root"
18+
tabindex="0"
19+
type="button"
20+
value="[object Object]"
1721
>
18-
<div
19-
class="MuiCardContent-root css-d64700-MuiCardContent-root"
20-
>
21-
<p
22-
class="MuiTypography-root MuiTypography-body1 MuiTypography-gutterBottom css-qscjxq-MuiTypography-root"
23-
title="cluster0"
24-
>
25-
cluster0
26-
</p>
27-
</div>
28-
</div>
29-
<span
30-
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
31-
/>
32-
</button>
33-
</div>
34-
<div
35-
class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root"
36-
>
37-
<button
38-
class="MuiButtonBase-root css-10d1a0h-MuiButtonBase-root"
39-
tabindex="0"
40-
type="button"
41-
>
42-
<div
43-
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-1o0qjyj-MuiPaper-root-MuiCard-root"
22+
cluster0
23+
<span
24+
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
25+
/>
26+
</button>
27+
<button
28+
aria-pressed="false"
29+
class="MuiButtonBase-root MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal MuiToggleButton-root MuiToggleButton-sizeMedium MuiToggleButton-standard MuiToggleButtonGroup-middleButton css-1tck97h-MuiButtonBase-root-MuiToggleButton-root"
30+
tabindex="0"
31+
type="button"
32+
value="[object Object]"
4433
>
45-
<div
46-
class="MuiCardContent-root css-d64700-MuiCardContent-root"
47-
>
48-
<p
49-
class="MuiTypography-root MuiTypography-body1 MuiTypography-gutterBottom css-qscjxq-MuiTypography-root"
50-
title="cluster1"
51-
>
52-
cluster1
53-
</p>
54-
</div>
55-
</div>
56-
<span
57-
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
58-
/>
59-
</button>
60-
</div>
61-
<div
62-
class="MuiGrid-root MuiGrid-item css-13i4rnv-MuiGrid-root"
63-
>
64-
<button
65-
class="MuiButtonBase-root css-10d1a0h-MuiButtonBase-root"
66-
tabindex="0"
67-
type="button"
34+
cluster1
35+
<span
36+
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
37+
/>
38+
</button>
39+
<button
40+
aria-pressed="false"
41+
class="MuiButtonBase-root MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal MuiToggleButton-root MuiToggleButton-sizeMedium MuiToggleButton-standard MuiToggleButtonGroup-lastButton css-1tck97h-MuiButtonBase-root-MuiToggleButton-root"
42+
tabindex="0"
43+
type="button"
44+
value="[object Object]"
45+
>
46+
cluster2
47+
<span
48+
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
49+
/>
50+
</button>
51+
</div>
52+
<div
53+
class="MuiGrid-root MuiGrid-item css-1oluiat-MuiGrid-root"
6854
>
69-
<div
70-
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiCard-root css-1o0qjyj-MuiPaper-root-MuiCard-root"
55+
<button
56+
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary Mui-disabled MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary css-19g3xkx-MuiButtonBase-root-MuiButton-root"
57+
disabled=""
58+
tabindex="-1"
59+
type="button"
7160
>
72-
<div
73-
class="MuiCardContent-root css-d64700-MuiCardContent-root"
74-
>
75-
<p
76-
class="MuiTypography-root MuiTypography-body1 MuiTypography-gutterBottom css-qscjxq-MuiTypography-root"
77-
title="cluster2"
78-
>
79-
cluster2
80-
</p>
81-
</div>
82-
</div>
83-
<span
84-
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
85-
/>
86-
</button>
61+
View
62+
</button>
63+
</div>
8764
</div>
8865
</div>
8966
</div>

0 commit comments

Comments
 (0)