@rancher/shell 3.0.1-rc.4 → 3.0.1
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/assets/data/aws-regions.json +1 -0
- package/assets/styles/base/_basic.scss +5 -0
- package/assets/styles/base/_mixins.scss +8 -0
- package/assets/styles/global/_button.scss +5 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/translations/en-us.yaml +27 -11
- package/assets/translations/zh-hans.yaml +1 -1
- package/chart/monitoring/StorageClassSelector.vue +1 -1
- package/components/AssignTo.vue +1 -0
- package/components/AsyncButton.vue +1 -0
- package/components/BackLink.vue +8 -2
- package/components/PaginatedResourceTable.vue +135 -0
- package/components/ResourceList/index.vue +0 -1
- package/components/ResourceTable.vue +6 -1
- package/components/SortableTable/index.vue +8 -6
- package/components/Tabbed/index.vue +35 -2
- package/components/form/ResourceLabeledSelect.vue +2 -2
- package/components/form/ResourceTabs/index.vue +0 -23
- package/components/form/Taints.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +546 -0
- package/components/nav/TopLevelMenu.vue +124 -159
- package/components/nav/__tests__/TopLevelMenu.test.ts +338 -326
- package/config/pagination-table-headers.js +4 -4
- package/config/product/explorer.js +2 -0
- package/config/router/routes.js +1 -1
- package/config/settings.ts +13 -1
- package/core/plugin.ts +8 -1
- package/core/types-provisioning.ts +5 -0
- package/core/types.ts +26 -1
- package/dialog/DrainNode.vue +6 -6
- package/edit/catalog.cattle.io.clusterrepo.vue +95 -52
- package/edit/provisioning.cattle.io.cluster/index.vue +8 -3
- package/list/node.vue +8 -5
- package/mixins/resource-fetch-api-pagination.js +40 -5
- package/mixins/resource-fetch.js +48 -5
- package/models/management.cattle.io.nodepool.js +5 -4
- package/models/provisioning.cattle.io.cluster.js +2 -10
- package/package.json +6 -6
- package/pages/about.vue +22 -0
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +36 -24
- package/pages/c/_cluster/explorer/index.vue +100 -59
- package/pages/home.vue +308 -123
- package/plugins/dashboard-store/__tests__/mutations.test.ts +2 -0
- package/plugins/dashboard-store/actions.js +29 -19
- package/plugins/dashboard-store/getters.js +5 -2
- package/plugins/dashboard-store/mutations.js +4 -2
- package/plugins/steve/__tests__/mutations.test.ts +2 -1
- package/plugins/steve/steve-pagination-utils.ts +25 -2
- package/plugins/steve/subscribe.js +22 -8
- package/scripts/extension/parse-tag-name +2 -0
- package/scripts/test-plugins-build.sh +1 -0
- package/store/index.js +31 -9
- package/tsconfig.json +7 -1
- package/types/resources/settings.d.ts +1 -1
- package/types/shell/index.d.ts +1107 -1276
- package/types/store/dashboard-store.types.ts +4 -0
- package/types/store/pagination.types.ts +13 -0
- package/types/store/vuex.d.ts +8 -0
- package/types/vue-shim.d.ts +6 -31
- package/utils/cluster.js +92 -1
- package/utils/pagination-utils.ts +17 -8
- package/utils/pagination-wrapper.ts +70 -0
- package/utils/uiplugins.ts +18 -4
|
@@ -4,18 +4,20 @@ import ClusterIconMenu from '@shell/components/ClusterIconMenu';
|
|
|
4
4
|
import IconOrSvg from '../IconOrSvg';
|
|
5
5
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
6
6
|
import { mapGetters } from 'vuex';
|
|
7
|
-
import { CAPI, MANAGEMENT } from '@shell/config/types';
|
|
8
|
-
import { MENU_MAX_CLUSTERS } from '@shell/store/prefs';
|
|
7
|
+
import { CAPI, COUNT, MANAGEMENT } from '@shell/config/types';
|
|
8
|
+
import { MENU_MAX_CLUSTERS, PINNED_CLUSTERS } from '@shell/store/prefs';
|
|
9
9
|
import { sortBy } from '@shell/utils/sort';
|
|
10
10
|
import { ucFirst } from '@shell/utils/string';
|
|
11
11
|
import { KEY } from '@shell/utils/platform';
|
|
12
12
|
import { getVersionInfo } from '@shell/utils/version';
|
|
13
13
|
import { SETTING } from '@shell/config/settings';
|
|
14
|
-
import { filterOnlyKubernetesClusters, filterHiddenLocalCluster } from '@shell/utils/cluster';
|
|
15
14
|
import { getProductFromRoute } from '@shell/utils/router';
|
|
16
15
|
import { isRancherPrime } from '@shell/config/version';
|
|
17
16
|
import Pinned from '@shell/components/nav/Pinned';
|
|
18
17
|
import { getGlobalBannerFontSizes } from '@shell/utils/banners';
|
|
18
|
+
import { TopLevelMenuHelperPagination, TopLevelMenuHelperLegacy } from '@shell/components/nav/TopLevelMenu.helper';
|
|
19
|
+
import { debounce } from 'lodash';
|
|
20
|
+
import { sameContents } from '@shell/utils/array';
|
|
19
21
|
|
|
20
22
|
export default {
|
|
21
23
|
components: {
|
|
@@ -29,6 +31,17 @@ export default {
|
|
|
29
31
|
const { displayVersion, fullVersion } = getVersionInfo(this.$store);
|
|
30
32
|
const hasProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
|
|
31
33
|
|
|
34
|
+
const canPagination = this.$store.getters[`management/paginationEnabled`]({
|
|
35
|
+
id: MANAGEMENT.CLUSTER,
|
|
36
|
+
context: 'side-bar',
|
|
37
|
+
}) && this.$store.getters[`management/paginationEnabled`]({
|
|
38
|
+
id: CAPI.RANCHER_CLUSTER,
|
|
39
|
+
context: 'side-bar',
|
|
40
|
+
});
|
|
41
|
+
const helper = canPagination ? new TopLevelMenuHelperPagination({ $store: this.$store }) : new TopLevelMenuHelperLegacy({ $store: this.$store });
|
|
42
|
+
const provClusters = !canPagination && hasProvCluster ? this.$store.getters[`management/all`](CAPI.RANCHER_CLUSTER) : [];
|
|
43
|
+
const mgmtClusters = !canPagination ? this.$store.getters[`management/all`](MANAGEMENT.CLUSTER) : [];
|
|
44
|
+
|
|
32
45
|
return {
|
|
33
46
|
shown: false,
|
|
34
47
|
displayVersion,
|
|
@@ -37,27 +50,26 @@ export default {
|
|
|
37
50
|
hasProvCluster,
|
|
38
51
|
maxClustersToShow: MENU_MAX_CLUSTERS,
|
|
39
52
|
emptyCluster: BLANK_CLUSTER,
|
|
40
|
-
showPinClusters: false,
|
|
41
|
-
searchActive: false,
|
|
42
53
|
routeCombo: false,
|
|
43
|
-
};
|
|
44
|
-
},
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this
|
|
49
|
-
|
|
55
|
+
canPagination,
|
|
56
|
+
helper,
|
|
57
|
+
debouncedHelperUpdateSlow: debounce((...args) => this.helper.update(...args), 750),
|
|
58
|
+
debouncedHelperUpdateQuick: debounce((...args) => this.helper.update(...args), 200),
|
|
59
|
+
provClusters,
|
|
60
|
+
mgmtClusters,
|
|
61
|
+
};
|
|
50
62
|
},
|
|
51
63
|
|
|
52
64
|
computed: {
|
|
53
65
|
...mapGetters(['clusterId']),
|
|
54
66
|
...mapGetters(['clusterReady', 'isRancher', 'currentCluster', 'currentProduct', 'isRancherInHarvester']),
|
|
55
67
|
...mapGetters({ features: 'features/get' }),
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
68
|
+
|
|
69
|
+
pinnedIds() {
|
|
70
|
+
return this.$store.getters['prefs/get'](PINNED_CLUSTERS);
|
|
60
71
|
},
|
|
72
|
+
|
|
61
73
|
sideMenuStyle() {
|
|
62
74
|
const globalBannerSettings = getGlobalBannerFontSizes(this.$store);
|
|
63
75
|
|
|
@@ -68,161 +80,47 @@ export default {
|
|
|
68
80
|
},
|
|
69
81
|
|
|
70
82
|
showClusterSearch() {
|
|
71
|
-
return this.
|
|
83
|
+
return this.allClustersCount > this.maxClustersToShow;
|
|
72
84
|
},
|
|
73
85
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
* 2. Harvester type 2 (filterHiddenLocalCluster)
|
|
78
|
-
* 3. There's a matching prov cluster
|
|
79
|
-
*
|
|
80
|
-
* Convert remaining clusters to special format
|
|
81
|
-
*/
|
|
82
|
-
clusters() {
|
|
83
|
-
if (!this.hasProvCluster) {
|
|
84
|
-
// We're filtering out mgmt clusters without prov clusters, so if the user can't see any prov clusters at all
|
|
85
|
-
// exit early
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
|
|
90
|
-
const mgmtClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all, this.$store), this.$store);
|
|
91
|
-
const provClusters = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
|
|
92
|
-
const provClustersByMgmtId = provClusters.reduce((res, provCluster) => {
|
|
93
|
-
if (provCluster.mgmt?.id) {
|
|
94
|
-
res[provCluster.mgmt.id] = provCluster;
|
|
95
|
-
}
|
|
86
|
+
allClustersCount() {
|
|
87
|
+
const counts = this.$store.getters[`management/all`](COUNT)?.[0]?.counts || {};
|
|
88
|
+
const count = counts[MANAGEMENT.CLUSTER] || {};
|
|
96
89
|
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
return count?.summary.count;
|
|
91
|
+
},
|
|
99
92
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (!provClustersByMgmtId[mgmtCluster.id]) {
|
|
105
|
-
return res;
|
|
106
|
-
}
|
|
93
|
+
// New
|
|
94
|
+
search() {
|
|
95
|
+
return (this.clusterFilter || '').toLowerCase();
|
|
96
|
+
},
|
|
107
97
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
label: mgmtCluster.nameDisplay,
|
|
113
|
-
ready: mgmtCluster.isReady && !pCluster?.hasError,
|
|
114
|
-
osLogo: mgmtCluster.providerOsLogo,
|
|
115
|
-
providerNavLogo: mgmtCluster.providerMenuLogo,
|
|
116
|
-
badge: mgmtCluster.badge,
|
|
117
|
-
isLocal: mgmtCluster.isLocal,
|
|
118
|
-
isHarvester: mgmtCluster.isHarvester,
|
|
119
|
-
pinned: mgmtCluster.pinned,
|
|
120
|
-
description: pCluster?.description || mgmtCluster.description,
|
|
121
|
-
pin: () => mgmtCluster.pin(),
|
|
122
|
-
unpin: () => mgmtCluster.unpin(),
|
|
123
|
-
clusterRoute: { name: 'c-cluster-explorer', params: { cluster: mgmtCluster.id } }
|
|
124
|
-
});
|
|
98
|
+
// New
|
|
99
|
+
showPinClusters() {
|
|
100
|
+
return !this.clusterFilter;
|
|
101
|
+
},
|
|
125
102
|
|
|
126
|
-
|
|
127
|
-
|
|
103
|
+
// New
|
|
104
|
+
searchActive() {
|
|
105
|
+
return !!this.search;
|
|
128
106
|
},
|
|
129
107
|
|
|
130
108
|
/**
|
|
131
|
-
*
|
|
132
|
-
* 1. Not pinned
|
|
133
|
-
* 2. Includes search term
|
|
134
|
-
*
|
|
135
|
-
* Sort remaining clusters
|
|
109
|
+
* Only Clusters that are pinned
|
|
136
110
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* Important! This is used to show unpinned clusters OR results of search
|
|
111
|
+
* (see description of helper.clustersPinned for more details)
|
|
140
112
|
*/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
let localCluster = null;
|
|
144
|
-
|
|
145
|
-
const filtered = this.clusters.filter((c) => {
|
|
146
|
-
// If we're searching we don't care if pinned or not
|
|
147
|
-
if (search) {
|
|
148
|
-
if (!c.label?.toLowerCase().includes(search)) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
} else if (c.pinned) {
|
|
152
|
-
// Not searching, not pinned, don't care
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!localCluster && c.id === 'local') {
|
|
157
|
-
// Local cluster is a special case, we're inserting it at top so don't include in the middle
|
|
158
|
-
localCluster = c;
|
|
159
|
-
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return true;
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const sorted = sortBy(filtered, ['ready:desc', 'label']);
|
|
167
|
-
|
|
168
|
-
// put local cluster on top of list always - https://github.com/rancher/dashboard/issues/10975
|
|
169
|
-
if (localCluster) {
|
|
170
|
-
sorted.unshift(localCluster);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (search) {
|
|
174
|
-
this.showPinClusters = false;
|
|
175
|
-
this.searchActive = !sorted.length > 0;
|
|
176
|
-
|
|
177
|
-
return sorted;
|
|
178
|
-
}
|
|
179
|
-
this.showPinClusters = true;
|
|
180
|
-
this.searchActive = false;
|
|
181
|
-
|
|
182
|
-
if (sorted.length >= this.maxClustersToShow) {
|
|
183
|
-
return sorted.slice(0, this.maxClustersToShow);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return sorted;
|
|
113
|
+
pinFiltered() {
|
|
114
|
+
return this.hasProvCluster ? this.helper.clustersPinned : [];
|
|
187
115
|
},
|
|
188
116
|
|
|
189
117
|
/**
|
|
190
|
-
*
|
|
191
|
-
* 1. Not pinned
|
|
192
|
-
* 2. Includes search term
|
|
193
|
-
*
|
|
194
|
-
* Sort remaining clusters
|
|
118
|
+
* Used to shown unpinned clusters OR results of text search
|
|
195
119
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* Important! This is hidden if there's a filter (user searching)
|
|
120
|
+
* (see description of helper.clustersOthers for more details)
|
|
199
121
|
*/
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const filtered = this.clusters.filter((c) => {
|
|
203
|
-
if (!c.pinned) {
|
|
204
|
-
// We only care about pinned clusters
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (c.id === 'local') {
|
|
209
|
-
// Special case, we're going to add this at the start so filter out
|
|
210
|
-
localCluster = c;
|
|
211
|
-
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return true;
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const sorted = sortBy(filtered, ['ready:desc', 'label']);
|
|
219
|
-
|
|
220
|
-
// put local cluster on top of list always - https://github.com/rancher/dashboard/issues/10975
|
|
221
|
-
if (localCluster) {
|
|
222
|
-
sorted.unshift(localCluster);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return sorted;
|
|
122
|
+
clustersFiltered() {
|
|
123
|
+
return this.hasProvCluster ? this.helper.clustersOthers : [];
|
|
226
124
|
},
|
|
227
125
|
|
|
228
126
|
pinnedClustersHeight() {
|
|
@@ -232,7 +130,7 @@ export default {
|
|
|
232
130
|
return `min-height: ${ height }px`;
|
|
233
131
|
},
|
|
234
132
|
clusterFilterCount() {
|
|
235
|
-
return this.clusterFilter ? this.clustersFiltered.length : this.
|
|
133
|
+
return this.clusterFilter ? this.clustersFiltered.length : this.allClustersCount;
|
|
236
134
|
},
|
|
237
135
|
|
|
238
136
|
multiClusterApps() {
|
|
@@ -360,10 +258,45 @@ export default {
|
|
|
360
258
|
}
|
|
361
259
|
},
|
|
362
260
|
|
|
261
|
+
// See https://github.com/rancher/dashboard/issues/12831 for outstanding performance related work
|
|
363
262
|
watch: {
|
|
364
263
|
$route() {
|
|
365
264
|
this.shown = false;
|
|
366
|
-
}
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
pinnedIds: {
|
|
268
|
+
immediate: true,
|
|
269
|
+
handler(neu, old) {
|
|
270
|
+
if (sameContents(neu, old)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this.updateClusters(neu, 'quick');
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
search() {
|
|
279
|
+
this.updateClusters(this.pinnedIds, 'slow');
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
provClusters: {
|
|
283
|
+
handler() {
|
|
284
|
+
// Shouldn't get here if SSP
|
|
285
|
+
this.updateClusters(this.pinnedIds, 'slow');
|
|
286
|
+
},
|
|
287
|
+
deep: true,
|
|
288
|
+
immediate: true,
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
mgmtClusters: {
|
|
292
|
+
handler() {
|
|
293
|
+
// Shouldn't get here if SSP
|
|
294
|
+
this.updateClusters(this.pinnedIds, 'slow');
|
|
295
|
+
},
|
|
296
|
+
deep: true,
|
|
297
|
+
immediate: true,
|
|
298
|
+
},
|
|
299
|
+
|
|
367
300
|
},
|
|
368
301
|
|
|
369
302
|
mounted() {
|
|
@@ -490,9 +423,27 @@ export default {
|
|
|
490
423
|
popperClass
|
|
491
424
|
};
|
|
492
425
|
},
|
|
426
|
+
|
|
427
|
+
updateClusters(pinnedIds, speed = 'slow' | 'quick') {
|
|
428
|
+
const args = {
|
|
429
|
+
pinnedIds,
|
|
430
|
+
searchTerm: this.search,
|
|
431
|
+
unPinnedMax: this.maxClustersToShow
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
switch (speed) {
|
|
435
|
+
case 'slow':
|
|
436
|
+
this.debouncedHelperUpdateSlow(args);
|
|
437
|
+
break;
|
|
438
|
+
case 'quick':
|
|
439
|
+
this.debouncedHelperUpdateQuick(args);
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
493
443
|
}
|
|
494
444
|
};
|
|
495
445
|
</script>
|
|
446
|
+
|
|
496
447
|
<template>
|
|
497
448
|
<div>
|
|
498
449
|
<!-- Overlay -->
|
|
@@ -514,7 +465,12 @@ export default {
|
|
|
514
465
|
<div class="title">
|
|
515
466
|
<div
|
|
516
467
|
data-testid="top-level-menu"
|
|
468
|
+
:aria-label="t('nav.expandCollapseAppBar')"
|
|
469
|
+
role="button"
|
|
470
|
+
tabindex="0"
|
|
517
471
|
class="menu"
|
|
472
|
+
@keyup.enter="toggle()"
|
|
473
|
+
@keyup.space="toggle()"
|
|
518
474
|
@click="toggle()"
|
|
519
475
|
>
|
|
520
476
|
<svg
|
|
@@ -632,7 +588,7 @@ export default {
|
|
|
632
588
|
</template>
|
|
633
589
|
|
|
634
590
|
<!-- Cluster menu -->
|
|
635
|
-
<template v-if="
|
|
591
|
+
<template v-if="!!allClustersCount">
|
|
636
592
|
<div
|
|
637
593
|
ref="clusterList"
|
|
638
594
|
class="clusters"
|
|
@@ -790,7 +746,7 @@ export default {
|
|
|
790
746
|
|
|
791
747
|
<!-- No clusters message -->
|
|
792
748
|
<div
|
|
793
|
-
v-if="
|
|
749
|
+
v-if="clustersFiltered.length === 0 && searchActive"
|
|
794
750
|
data-testid="top-level-menu-no-results"
|
|
795
751
|
class="none-matching"
|
|
796
752
|
>
|
|
@@ -800,7 +756,7 @@ export default {
|
|
|
800
756
|
|
|
801
757
|
<!-- See all clusters -->
|
|
802
758
|
<router-link
|
|
803
|
-
v-if="
|
|
759
|
+
v-if="allClustersCount > maxClustersToShow"
|
|
804
760
|
class="clusters-all"
|
|
805
761
|
:to="{name: 'c-cluster-product-resource', params: {
|
|
806
762
|
cluster: emptyCluster,
|
|
@@ -972,6 +928,15 @@ export default {
|
|
|
972
928
|
align-items: center;
|
|
973
929
|
justify-content: center;
|
|
974
930
|
|
|
931
|
+
&:focus-visible {
|
|
932
|
+
outline: none;
|
|
933
|
+
|
|
934
|
+
.menu-icon {
|
|
935
|
+
@include focus-outline;
|
|
936
|
+
outline-offset: 4px; // Ensure there is space around the menu icon for the focus indication
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
975
940
|
.menu-icon {
|
|
976
941
|
width: 25px;
|
|
977
942
|
height: 25px;
|