@rancher/shell 0.3.26 → 0.3.27
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/.DS_Store +0 -0
- package/assets/translations/en-us.yaml +4 -3
- package/assets/translations/zh-hans.yaml +2 -3
- package/components/AlertTable.vue +8 -6
- package/components/EmberPage.vue +2 -2
- package/components/EtcdInfoBanner.vue +12 -2
- package/components/GlobalRoleBindings.vue +10 -0
- package/components/GrafanaDashboard.vue +8 -3
- package/components/Wizard.vue +17 -1
- package/components/auth/RoleDetailEdit.vue +17 -1
- package/components/form/ArrayList.vue +20 -11
- package/components/form/__tests__/ArrayList.test.ts +44 -0
- package/components/nav/Header.vue +5 -4
- package/components/nav/TopLevelMenu.vue +38 -15
- package/components/nav/__tests__/TopLevelMenu.test.ts +120 -0
- package/components/nav/__tests__/Type.test.ts +139 -0
- package/config/private-label.js +1 -1
- package/config/settings.ts +0 -2
- package/core/types.ts +11 -4
- package/edit/provisioning.cattle.io.cluster/Basics.vue +13 -0
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
- package/edit/workload/mixins/workload.js +14 -4
- package/models/fleet.cattle.io.cluster.js +11 -1
- package/package.json +1 -1
- package/pages/c/_cluster/auth/roles/index.vue +11 -1
- package/pages/c/_cluster/explorer/index.vue +7 -2
- package/pages/c/_cluster/monitoring/index.vue +26 -39
- package/pages/support/index.vue +1 -8
- package/promptRemove/management.cattle.io.project.vue +6 -9
- package/rancher-components/components/Form/Radio/RadioGroup.test.ts +30 -0
- package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -0
- package/store/features.js +1 -0
- package/types/shell/index.d.ts +4 -1
- package/utils/__tests__/object.test.ts +67 -1
- package/utils/__tests__/version.test.ts +13 -23
- package/utils/cluster.js +1 -1
- package/utils/grafana.js +1 -2
- package/utils/monitoring.js +25 -1
- package/utils/object.js +4 -3
- package/utils/sort.js +1 -1
- package/utils/validators/formRules/index.ts +1 -1
- package/utils/validators/role-template.js +1 -1
- package/utils/version.js +0 -13
package/.DS_Store
ADDED
|
Binary file
|
|
@@ -1677,6 +1677,7 @@ cluster:
|
|
|
1677
1677
|
haveArgInfo: Configuration information is not available for the selected Kubernetes version. The options available on this screen will be limited; you may want to use the YAML editor.
|
|
1678
1678
|
deprecatedPsp: Pod Security Policies are deprecated as of Kubernetes v1.21, and have been removed in Kubernetes v1.25.
|
|
1679
1679
|
removedPsp: Pod Security Policies have been removed in Kubernetes v1.25. Use Pod Security Admission instead.
|
|
1680
|
+
cloudProviderAddConfig: 'On Kubernetes 1.27 or greater, the Amazon Cloud Provider requires additional configuration. See <a href="https://ranchermanager.docs.rancher.com/how-to-guides/new-user-guides/kubernetes-clusters-in-rancher-setup/set-up-cloud-providers/amazon" target="_blank" rel="noopener noreferrer nofollow">the documentation</a> for more information.'
|
|
1680
1681
|
machinePoolError: |-
|
|
1681
1682
|
{count, plural,
|
|
1682
1683
|
=1 { {pool_name}: The provided value for {fields} was not found in the list of expected values. This can happen with clusters provisioned outside of Rancher or when options for the provider have changed. }
|
|
@@ -2763,7 +2764,7 @@ landing:
|
|
|
2763
2764
|
cpuUsed: CPU Used
|
|
2764
2765
|
memoryUsed: Memory Used
|
|
2765
2766
|
seeWhatsNew: Learn more about the improvements and new capabilities in this version.
|
|
2766
|
-
whatsNewLink: "What's new in 2.
|
|
2767
|
+
whatsNewLink: "What's new in 2.8"
|
|
2767
2768
|
learnMore: Learn More
|
|
2768
2769
|
support: Support
|
|
2769
2770
|
psps: PSPs
|
|
@@ -2833,7 +2834,7 @@ logging:
|
|
|
2833
2834
|
dockerRootDirectory: Docker Root Directory
|
|
2834
2835
|
systemdLogPath: systemd Log Path
|
|
2835
2836
|
tooltip: 'Some kubernetes distributions log to <code>journald</code>. In order to collect these logs the <code>systemdLogPath</code> needs to be defined. While the <code>/run/log/journal</code> directory is used by default, some Linux distributions do not default to this path.'
|
|
2836
|
-
url: '<a href="https://rancher.com/
|
|
2837
|
+
url: '<a href="https://ranchermanager.docs.rancher.com/v2.8/integrations-in-rancher/logging/logging-helm-chart-options" target="_blank" rel="noopener nofollow noreferrer">Learn more</a>'
|
|
2837
2838
|
default: /run/log/journal
|
|
2838
2839
|
elasticsearch:
|
|
2839
2840
|
host: Host
|
|
@@ -4613,6 +4614,7 @@ rbac:
|
|
|
4613
4614
|
restricted-admin:
|
|
4614
4615
|
label: Restricted Administrator
|
|
4615
4616
|
description: Restricted Admins have full control over all resources in all downstream clusters but no access to the local cluster.
|
|
4617
|
+
deprecation: 'Warning: The Restricted Administrator role has been deprecated as of Rancher 2.8.0 and will be removed in a future release - Check out the <a href="{releaseNotesUrl}" target="_blank" rel="noopener noreferrer nofollow">Release Notes</a>'
|
|
4616
4618
|
user:
|
|
4617
4619
|
label: Standard User
|
|
4618
4620
|
description: Standard Users can create new clusters and manage clusters and projects they have been granted access to.
|
|
@@ -7079,7 +7081,6 @@ advancedSettings:
|
|
|
7079
7081
|
'auth-user-session-ttl-minutes': 'Custom TTL (in minutes) on a user auth session.'
|
|
7080
7082
|
'auth-token-max-ttl-minutes': 'Max TTL (in minutes) for all authentication tokens. When set to 0, the token never expires.'
|
|
7081
7083
|
'kubeconfig-generate-token': 'Automatically generate tokens for users when a kubeconfig is requested.'
|
|
7082
|
-
'kubeconfig-token-ttl-minutes': 'TTL used for tokens generated via the CLI. Deprecated: This setting will be removed, and kubeconfig-default-token-ttl-minutes will be used for all kubeconfig tokens.'
|
|
7083
7084
|
'kubeconfig-default-token-ttl-minutes': 'TTL (in minutes) applied on all kubeconfig tokens. When set to 0, the token never expires.'
|
|
7084
7085
|
'rke-metadata-config': 'Configure RKE metadata refresh parameters.'
|
|
7085
7086
|
'ui-banners': 'Classification banner is used to display a custom fixed banner in the header, footer, or both.'
|
|
@@ -2735,7 +2735,7 @@ landing:
|
|
|
2735
2735
|
cpuUsed: 已用 CPU
|
|
2736
2736
|
memoryUsed: 已用内存
|
|
2737
2737
|
seeWhatsNew: 点击右侧链接,了解此版本的新功能和优化。
|
|
2738
|
-
whatsNewLink: "2.
|
|
2738
|
+
whatsNewLink: "2.8 的新功能"
|
|
2739
2739
|
learnMore: 了解更多
|
|
2740
2740
|
support: 支持
|
|
2741
2741
|
psps: PSP
|
|
@@ -2805,7 +2805,7 @@ logging:
|
|
|
2805
2805
|
dockerRootDirectory: Docker 根目录
|
|
2806
2806
|
systemdLogPath: systemd 日志路径
|
|
2807
2807
|
tooltip: '某些 Kubernetes 发行版在 <code>journald</code>中记录日志。你需要定义<code>systemdLogPath</code> 以收集日志。默认路径是<code>/run/log/journal</code>,但某些 Linux 发行版不默认使用该路径。'
|
|
2808
|
-
url: '<a href="https://rancher.com/
|
|
2808
|
+
url: '<a href="https://ranchermanager.docs.rancher.com/v2.8/integrations-in-rancher/logging/logging-helm-chart-options" target="_blank" rel="noopener nofollow noreferrer">了解更多</a>'
|
|
2809
2809
|
default: /run/log/journal
|
|
2810
2810
|
elasticsearch:
|
|
2811
2811
|
host: 主机
|
|
@@ -7051,7 +7051,6 @@ advancedSettings:
|
|
|
7051
7051
|
'auth-user-session-ttl-minutes': '用户认证会话的自定义 TTL(单位:分钟)。'
|
|
7052
7052
|
'auth-token-max-ttl-minutes': '所有身份认证 Token 的最大 TTL(单位:分钟)。如果设置为 0,则 Token 永不过期。'
|
|
7053
7053
|
'kubeconfig-generate-token': '请求 kubeconfig 时自动为用户生成 Token。'
|
|
7054
|
-
'kubeconfig-token-ttl-minutes': '在 CLI 中生成的 Token TTL。已弃用:此设置将被删除,kubeconfig-default-token-ttl-minutes 将用于所有 kubeconfig Token。'
|
|
7055
7054
|
'kubeconfig-default-token-ttl-minutes': '应用于所有 kubeconfig Token 的 TTL(单位:分钟)。如果设置为 0,则 Token 永不过期。'
|
|
7056
7055
|
'rke-metadata-config': '配置 RKE 元数据刷新参数。'
|
|
7057
7056
|
'ui-banners': '分类横幅用于在页眉、页脚或两者中显示自定义的固定横幅。'
|
|
@@ -82,14 +82,16 @@ export default {
|
|
|
82
82
|
},
|
|
83
83
|
|
|
84
84
|
async fetchDeps() {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
if (this.$store.getters['cluster/canList'](ENDPOINTS)) {
|
|
86
|
+
try {
|
|
87
|
+
const am = await this.$store.dispatch('cluster/find', { type: ENDPOINTS, id: `${ this.monitoringNamespace }/${ this.alertServiceEndpoint }` });
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (!isEmpty(am) && !isEmpty(am.subsets)) {
|
|
90
|
+
this.alertManagerPoller.start();
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
92
93
|
|
|
94
|
+
}
|
|
93
95
|
}
|
|
94
96
|
},
|
|
95
97
|
}
|
package/components/EmberPage.vue
CHANGED
|
@@ -597,11 +597,11 @@ export default {
|
|
|
597
597
|
|
|
598
598
|
.ember-iframe {
|
|
599
599
|
border: 0;
|
|
600
|
-
left: var(--nav-width);
|
|
600
|
+
left: calc(var(--nav-width) + $app-bar-collapsed-width);
|
|
601
601
|
height: calc(100vh - var(--header-height));
|
|
602
602
|
position: absolute;
|
|
603
603
|
top: var(--header-height);
|
|
604
|
-
width: calc(100vw - var(--nav-width));
|
|
604
|
+
width: calc(100vw - var(--nav-width) - $app-bar-collapsed-width);
|
|
605
605
|
visibility: show;
|
|
606
606
|
}
|
|
607
607
|
|
|
@@ -9,8 +9,18 @@ export default {
|
|
|
9
9
|
components: { Banner, Loading },
|
|
10
10
|
async fetch() {
|
|
11
11
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
let monitoringVersion = '';
|
|
13
|
+
|
|
14
|
+
if (this.$store.getters[`${ inStore }/canList}`](CATALOG.APP)) {
|
|
15
|
+
try {
|
|
16
|
+
const res = await this.$store.dispatch(`${ inStore }/find`, { type: CATALOG.APP, id: 'cattle-monitoring-system/rancher-monitoring' });
|
|
17
|
+
|
|
18
|
+
monitoringVersion = res?.currentVersion;
|
|
19
|
+
} catch (err) {
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
const leader = await hasLeader(monitoringVersion, this.$store.dispatch, this.currentCluster.id);
|
|
15
25
|
|
|
16
26
|
this.hasLeader = leader ? this.t('generic.yes') : this.t('generic.no');
|
|
@@ -94,6 +94,7 @@ export default {
|
|
|
94
94
|
};
|
|
95
95
|
},
|
|
96
96
|
computed: {
|
|
97
|
+
...mapGetters(['releaseNotesUrl']),
|
|
97
98
|
...mapGetters({ t: 'i18n/t' }),
|
|
98
99
|
|
|
99
100
|
isCreate() {
|
|
@@ -347,6 +348,11 @@ export default {
|
|
|
347
348
|
</div>
|
|
348
349
|
</template>
|
|
349
350
|
</Checkbox>
|
|
351
|
+
<p
|
|
352
|
+
v-if="role.id === 'restricted-admin'"
|
|
353
|
+
v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)"
|
|
354
|
+
class="deprecation-notice"
|
|
355
|
+
/>
|
|
350
356
|
</div>
|
|
351
357
|
</div>
|
|
352
358
|
</template>
|
|
@@ -364,6 +370,10 @@ export default {
|
|
|
364
370
|
</style>
|
|
365
371
|
<style lang='scss' scoped>
|
|
366
372
|
$detailSize: 11px;
|
|
373
|
+
|
|
374
|
+
.deprecation-notice {
|
|
375
|
+
margin: 8px 0 8px 20px;
|
|
376
|
+
}
|
|
367
377
|
.role-group {
|
|
368
378
|
.type-title {
|
|
369
379
|
display: flex;
|
|
@@ -40,13 +40,18 @@ export default {
|
|
|
40
40
|
},
|
|
41
41
|
async fetch() {
|
|
42
42
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
43
|
-
const res = await this.$store.dispatch(`${ inStore }/find`, { type: CATALOG.APP, id: 'cattle-monitoring-system/rancher-monitoring' });
|
|
44
43
|
|
|
45
|
-
this.
|
|
44
|
+
if (this.$store.getters[`${ inStore }/canList`](CATALOG.APP)) {
|
|
45
|
+
try {
|
|
46
|
+
const res = await this.$store.dispatch(`${ inStore }/find`, { type: CATALOG.APP, id: 'cattle-monitoring-system/rancher-monitoring' });
|
|
47
|
+
|
|
48
|
+
this.monitoringVersion = res?.currentVersion;
|
|
49
|
+
} catch (err) {}
|
|
50
|
+
}
|
|
46
51
|
},
|
|
47
52
|
data() {
|
|
48
53
|
return {
|
|
49
|
-
loading: false, error: false, interval: null, errorTimer: null, monitoringVersion:
|
|
54
|
+
loading: false, error: false, interval: null, errorTimer: null, monitoringVersion: ''
|
|
50
55
|
};
|
|
51
56
|
},
|
|
52
57
|
computed: {
|
package/components/Wizard.vue
CHANGED
|
@@ -182,6 +182,12 @@ export default {
|
|
|
182
182
|
this.activeStep = this.visibleSteps[this.initStepIndex];
|
|
183
183
|
this.goToStep(this.activeStepIndex + 1);
|
|
184
184
|
}
|
|
185
|
+
},
|
|
186
|
+
errors() {
|
|
187
|
+
// Ensurce we scroll the errors into view
|
|
188
|
+
this.$nextTick(() => {
|
|
189
|
+
this.$refs.wizard.scrollTop = this.$refs.wizard.scrollHeight;
|
|
190
|
+
});
|
|
185
191
|
}
|
|
186
192
|
},
|
|
187
193
|
|
|
@@ -253,7 +259,10 @@ export default {
|
|
|
253
259
|
</script>
|
|
254
260
|
|
|
255
261
|
<template>
|
|
256
|
-
<div
|
|
262
|
+
<div
|
|
263
|
+
ref="wizard"
|
|
264
|
+
class="outer-container"
|
|
265
|
+
>
|
|
257
266
|
<Loading
|
|
258
267
|
v-if="!stepsLoaded"
|
|
259
268
|
mode="relative"
|
|
@@ -397,6 +406,7 @@ export default {
|
|
|
397
406
|
color="error"
|
|
398
407
|
:label="err"
|
|
399
408
|
:closable="true"
|
|
409
|
+
class="footer-error"
|
|
400
410
|
@close="errors.splice(idx, 1)"
|
|
401
411
|
/>
|
|
402
412
|
</div>
|
|
@@ -647,6 +657,12 @@ $spacer: 10px;
|
|
|
647
657
|
}
|
|
648
658
|
}
|
|
649
659
|
|
|
660
|
+
// We have to account for the absolute position of the .controls-row
|
|
661
|
+
.footer-error {
|
|
662
|
+
margin-top: -40px;
|
|
663
|
+
margin-bottom: 70px;
|
|
664
|
+
}
|
|
665
|
+
|
|
650
666
|
.controls-row {
|
|
651
667
|
|
|
652
668
|
// Overrides outlet padding
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { mapGetters } from 'vuex';
|
|
2
3
|
import { MANAGEMENT, RBAC } from '@shell/config/types';
|
|
3
4
|
import CruResource from '@shell/components/CruResource';
|
|
4
5
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
@@ -14,6 +15,7 @@ import { ucFirst } from '@shell/utils/string';
|
|
|
14
15
|
import SortableTable from '@shell/components/SortableTable';
|
|
15
16
|
import { _CLONE, _DETAIL } from '@shell/config/query-params';
|
|
16
17
|
import { SCOPED_RESOURCES } from '@shell/config/roles';
|
|
18
|
+
import { Banner } from '@components/Banner';
|
|
17
19
|
|
|
18
20
|
import { SUBTYPE_MAPPING, VERBS } from '@shell/models/management.cattle.io.roletemplate';
|
|
19
21
|
import Loading from '@shell/components/Loading';
|
|
@@ -60,7 +62,8 @@ export default {
|
|
|
60
62
|
Tabbed,
|
|
61
63
|
SortableTable,
|
|
62
64
|
Loading,
|
|
63
|
-
Error
|
|
65
|
+
Error,
|
|
66
|
+
Banner
|
|
64
67
|
},
|
|
65
68
|
|
|
66
69
|
mixins: [CreateEditView, FormValidation],
|
|
@@ -162,6 +165,12 @@ export default {
|
|
|
162
165
|
},
|
|
163
166
|
|
|
164
167
|
computed: {
|
|
168
|
+
...mapGetters(['releaseNotesUrl']),
|
|
169
|
+
|
|
170
|
+
showRestrictedAdminDeprecationBanner() {
|
|
171
|
+
return this.value.subtype === GLOBAL && this.value.id === 'restricted-admin';
|
|
172
|
+
},
|
|
173
|
+
|
|
165
174
|
label() {
|
|
166
175
|
return this.t(`rbac.roletemplate.subtypes.${ this.value.subtype }.label`);
|
|
167
176
|
},
|
|
@@ -541,6 +550,13 @@ export default {
|
|
|
541
550
|
@finish="save"
|
|
542
551
|
@cancel="cancel"
|
|
543
552
|
>
|
|
553
|
+
<Banner
|
|
554
|
+
v-if="showRestrictedAdminDeprecationBanner"
|
|
555
|
+
color="warning"
|
|
556
|
+
class="mb-20"
|
|
557
|
+
>
|
|
558
|
+
<span v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)" />
|
|
559
|
+
</Banner>
|
|
544
560
|
<template v-if="isDetail">
|
|
545
561
|
<SortableTable
|
|
546
562
|
key-field="index"
|
|
@@ -164,6 +164,10 @@ export default {
|
|
|
164
164
|
removeAt(this.rows, index);
|
|
165
165
|
this.queueUpdate();
|
|
166
166
|
},
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Cleanup rows and emit input
|
|
170
|
+
*/
|
|
167
171
|
update() {
|
|
168
172
|
if ( this.isView ) {
|
|
169
173
|
return;
|
|
@@ -180,22 +184,24 @@ export default {
|
|
|
180
184
|
}
|
|
181
185
|
this.$emit('input', out);
|
|
182
186
|
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Handle paste event, e.g. split multiple lines in rows
|
|
190
|
+
*/
|
|
183
191
|
onPaste(index, event) {
|
|
184
|
-
if (this.valueMultiline) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
192
|
event.preventDefault();
|
|
188
193
|
const text = event.clipboardData.getData('text/plain');
|
|
189
|
-
const split = text.split('\n').map((value) => ({ value }));
|
|
190
|
-
|
|
191
|
-
if (split.length === 1) {
|
|
192
|
-
// It's not multi-line, so don't treat it as such
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
194
|
|
|
196
|
-
|
|
195
|
+
if (this.valueMultiline) {
|
|
196
|
+
// Allow to paste multiple lines
|
|
197
|
+
this.rows[index].value = text;
|
|
198
|
+
} else {
|
|
199
|
+
// Prevent to paste the value and emit text in multiple rows
|
|
200
|
+
const split = text.split('\n').map((value) => ({ value }));
|
|
197
201
|
|
|
198
|
-
|
|
202
|
+
event.preventDefault();
|
|
203
|
+
this.rows.splice(index, 1, ...split);
|
|
204
|
+
}
|
|
199
205
|
|
|
200
206
|
this.update();
|
|
201
207
|
}
|
|
@@ -256,6 +262,7 @@ export default {
|
|
|
256
262
|
v-if="valueMultiline"
|
|
257
263
|
ref="value"
|
|
258
264
|
v-model="row.value"
|
|
265
|
+
:data-testid="`textarea-${idx}`"
|
|
259
266
|
:placeholder="valuePlaceholder"
|
|
260
267
|
:mode="mode"
|
|
261
268
|
:disabled="disabled"
|
|
@@ -266,6 +273,7 @@ export default {
|
|
|
266
273
|
v-else-if="rules.length > 0"
|
|
267
274
|
ref="value"
|
|
268
275
|
v-model="row.value"
|
|
276
|
+
:data-testid="`labeled-input-${idx}`"
|
|
269
277
|
:placeholder="valuePlaceholder"
|
|
270
278
|
:disabled="isView || disabled"
|
|
271
279
|
:rules="rules"
|
|
@@ -277,6 +285,7 @@ export default {
|
|
|
277
285
|
v-else
|
|
278
286
|
ref="value"
|
|
279
287
|
v-model="row.value"
|
|
288
|
+
:data-testid="`input-${idx}`"
|
|
280
289
|
:placeholder="valuePlaceholder"
|
|
281
290
|
:disabled="isView || disabled"
|
|
282
291
|
@paste="onPaste(idx, $event)"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import ArrayList from '@shell/components/form/ArrayList.vue';
|
|
3
3
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
4
|
+
import { ExtendedVue, Vue } from 'vue/types/vue';
|
|
5
|
+
import { DefaultProps } from 'vue/types/options';
|
|
4
6
|
|
|
5
7
|
describe('the ArrayList', () => {
|
|
6
8
|
it('is empty', () => {
|
|
@@ -71,4 +73,46 @@ describe('the ArrayList', () => {
|
|
|
71
73
|
|
|
72
74
|
expect(arrayListButtons).toHaveLength(0);
|
|
73
75
|
});
|
|
76
|
+
|
|
77
|
+
describe('onPaste', () => {
|
|
78
|
+
it('should emit value with updated row text', () => {
|
|
79
|
+
const text = 'test';
|
|
80
|
+
const expectation = [text];
|
|
81
|
+
const wrapper = mount(ArrayList as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, { propsData: { value: [''] } });
|
|
82
|
+
const event = { preventDefault: jest.fn(), clipboardData: { getData: jest.fn().mockReturnValue(text) } } as any;
|
|
83
|
+
|
|
84
|
+
wrapper.vm.onPaste(0, event);
|
|
85
|
+
|
|
86
|
+
expect(wrapper.emitted().input?.[0][0]).toStrictEqual(expectation);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should emit value with multiple rows', () => {
|
|
90
|
+
const wrapper = mount(ArrayList as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, { propsData: { value: [''] } });
|
|
91
|
+
const text = `multiline
|
|
92
|
+
rows`;
|
|
93
|
+
const expectation = ['multiline', 'rows'];
|
|
94
|
+
const event = { preventDefault: jest.fn(), clipboardData: { getData: jest.fn().mockReturnValue(text) } } as any;
|
|
95
|
+
|
|
96
|
+
wrapper.vm.onPaste(0, event);
|
|
97
|
+
|
|
98
|
+
expect(wrapper.emitted().input?.[0][0]).toStrictEqual(expectation);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should allow emit multiline pasted values if enabled', () => {
|
|
102
|
+
const wrapper = mount(ArrayList as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
|
|
103
|
+
propsData: {
|
|
104
|
+
value: [''],
|
|
105
|
+
valueMultiline: true,
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const text = `multiline
|
|
109
|
+
text`;
|
|
110
|
+
const expectation = [text];
|
|
111
|
+
const event = { preventDefault: jest.fn(), clipboardData: { getData: jest.fn().mockReturnValue(text) } } as any;
|
|
112
|
+
|
|
113
|
+
wrapper.vm.onPaste(0, event);
|
|
114
|
+
|
|
115
|
+
expect(wrapper.emitted().input?.[0][0]).toStrictEqual(expectation);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
74
118
|
});
|
|
@@ -706,7 +706,6 @@ export default {
|
|
|
706
706
|
</template>
|
|
707
707
|
|
|
708
708
|
<style lang="scss" scoped>
|
|
709
|
-
$side-menu-logo-margin-left: 5px;
|
|
710
709
|
// It would be nice to grab this from `Group.vue`, but there's margin, padding and border, which is overkill to var
|
|
711
710
|
$side-menu-group-padding-left: 16px;
|
|
712
711
|
|
|
@@ -724,9 +723,11 @@ export default {
|
|
|
724
723
|
&.isSingleProduct {
|
|
725
724
|
display: flex;
|
|
726
725
|
justify-content: center;
|
|
726
|
+
|
|
727
727
|
// Align the icon with the side nav menu items ($side-menu-group-padding-left)
|
|
728
|
-
|
|
729
|
-
|
|
728
|
+
.side-menu-logo {
|
|
729
|
+
margin-left: $side-menu-group-padding-left;
|
|
730
|
+
}
|
|
730
731
|
}
|
|
731
732
|
}
|
|
732
733
|
|
|
@@ -815,7 +816,7 @@ export default {
|
|
|
815
816
|
display: flex;
|
|
816
817
|
margin-right: 8px;
|
|
817
818
|
height: 55px;
|
|
818
|
-
margin-left:
|
|
819
|
+
margin-left: 5px;
|
|
819
820
|
max-width: 200px;
|
|
820
821
|
padding: 12px 0;
|
|
821
822
|
}
|
|
@@ -50,7 +50,6 @@ export default {
|
|
|
50
50
|
computed: {
|
|
51
51
|
...mapGetters(['clusterId']),
|
|
52
52
|
...mapGetters(['clusterReady', 'isRancher', 'currentCluster', 'currentProduct', 'isRancherInHarvester']),
|
|
53
|
-
...mapGetters('type-map', ['activeProducts']),
|
|
54
53
|
...mapGetters({ features: 'features/get' }),
|
|
55
54
|
|
|
56
55
|
value: {
|
|
@@ -59,9 +58,16 @@ export default {
|
|
|
59
58
|
},
|
|
60
59
|
},
|
|
61
60
|
|
|
61
|
+
sideMenuStyle() {
|
|
62
|
+
return {
|
|
63
|
+
marginBottom: this.globalBannerSettings?.footerFont,
|
|
64
|
+
marginTop: this.globalBannerSettings?.headerFont
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
|
|
62
68
|
globalBannerSettings() {
|
|
63
69
|
const settings = this.$store.getters['management/all'](MANAGEMENT.SETTING);
|
|
64
|
-
const bannerSettings = settings
|
|
70
|
+
const bannerSettings = settings?.find((s) => s.id === SETTING.BANNERS);
|
|
65
71
|
|
|
66
72
|
if (bannerSettings) {
|
|
67
73
|
const parsed = JSON.parse(bannerSettings.value);
|
|
@@ -104,8 +110,8 @@ export default {
|
|
|
104
110
|
kubeClusters = kubeClusters.filter((c) => !!available[c]);
|
|
105
111
|
}
|
|
106
112
|
|
|
107
|
-
return kubeClusters
|
|
108
|
-
const pCluster = pClusters?.find((c) => c.mgmt
|
|
113
|
+
return kubeClusters?.map((x) => {
|
|
114
|
+
const pCluster = pClusters?.find((c) => c.mgmt?.id === x.id);
|
|
109
115
|
|
|
110
116
|
return {
|
|
111
117
|
id: x.id,
|
|
@@ -120,14 +126,12 @@ export default {
|
|
|
120
126
|
pin: () => x.pin(),
|
|
121
127
|
unpin: () => x.unpin()
|
|
122
128
|
};
|
|
123
|
-
});
|
|
129
|
+
}) || [];
|
|
124
130
|
},
|
|
125
131
|
|
|
126
132
|
clustersFiltered() {
|
|
127
133
|
const search = (this.clusterFilter || '').toLowerCase();
|
|
128
|
-
|
|
129
|
-
const out = search ? this.clusters.filter((item) => item.label.toLowerCase().includes(search)) : this.clusters;
|
|
130
|
-
|
|
134
|
+
const out = search ? this.clusters.filter((item) => item.label?.toLowerCase().includes(search)) : this.clusters;
|
|
131
135
|
const sorted = sortBy(out, ['ready:desc', 'label']);
|
|
132
136
|
|
|
133
137
|
if (search) {
|
|
@@ -203,7 +207,7 @@ export default {
|
|
|
203
207
|
const cluster = this.clusterId || this.$store.getters['defaultClusterId'];
|
|
204
208
|
|
|
205
209
|
// TODO plugin routes
|
|
206
|
-
const entries = this.activeProducts
|
|
210
|
+
const entries = this.$store.getters['type-map/activeProducts']?.map((p) => {
|
|
207
211
|
// Try product-specific index first
|
|
208
212
|
const to = p.to || {
|
|
209
213
|
name: `c-cluster-${ p.name }`,
|
|
@@ -314,22 +318,22 @@ export default {
|
|
|
314
318
|
</script>
|
|
315
319
|
<template>
|
|
316
320
|
<div>
|
|
321
|
+
<!-- Overlay -->
|
|
317
322
|
<div
|
|
318
323
|
v-if="shown"
|
|
319
324
|
class="side-menu-glass"
|
|
320
325
|
@click="hide()"
|
|
321
326
|
/>
|
|
322
327
|
<transition name="fade">
|
|
328
|
+
<!-- Side menu -->
|
|
323
329
|
<div
|
|
324
330
|
data-testid="side-menu"
|
|
325
331
|
class="side-menu"
|
|
326
332
|
:class="{'menu-open': shown, 'menu-close':!shown}"
|
|
327
|
-
:style="
|
|
328
|
-
globalBannerSettings?.footerFont,
|
|
329
|
-
'marginTop':
|
|
330
|
-
globalBannerSettings?.headerFont}"
|
|
333
|
+
:style="sideMenuStyle"
|
|
331
334
|
tabindex="-1"
|
|
332
335
|
>
|
|
336
|
+
<!-- Logo and name -->
|
|
333
337
|
<div class="title">
|
|
334
338
|
<div
|
|
335
339
|
data-testid="top-level-menu"
|
|
@@ -351,8 +355,11 @@ export default {
|
|
|
351
355
|
<BrandImage file-name="rancher-logo.svg" />
|
|
352
356
|
</div>
|
|
353
357
|
</div>
|
|
358
|
+
|
|
359
|
+
<!-- Menu body -->
|
|
354
360
|
<div class="body">
|
|
355
361
|
<div>
|
|
362
|
+
<!-- Home button -->
|
|
356
363
|
<nuxt-link
|
|
357
364
|
class="option cluster selector home"
|
|
358
365
|
:to="{ name: 'home' }"
|
|
@@ -371,6 +378,8 @@ export default {
|
|
|
371
378
|
{{ t('nav.home') }}
|
|
372
379
|
</div>
|
|
373
380
|
</nuxt-link>
|
|
381
|
+
|
|
382
|
+
<!-- Search bar -->
|
|
374
383
|
<div
|
|
375
384
|
v-if="showClusterSearch"
|
|
376
385
|
class="clusters-search"
|
|
@@ -400,6 +409,8 @@ export default {
|
|
|
400
409
|
</div>
|
|
401
410
|
</div>
|
|
402
411
|
</div>
|
|
412
|
+
|
|
413
|
+
<!-- Harvester extras -->
|
|
403
414
|
<template v-if="hciApps.length">
|
|
404
415
|
<div class="category" />
|
|
405
416
|
<div>
|
|
@@ -416,7 +427,6 @@ export default {
|
|
|
416
427
|
</div>
|
|
417
428
|
</a>
|
|
418
429
|
</div>
|
|
419
|
-
|
|
420
430
|
<div
|
|
421
431
|
v-for="a in hciApps"
|
|
422
432
|
:key="a.label"
|
|
@@ -435,12 +445,14 @@ export default {
|
|
|
435
445
|
</div>
|
|
436
446
|
</template>
|
|
437
447
|
|
|
448
|
+
<!-- Cluster menu -->
|
|
438
449
|
<template v-if="clusters && !!clusters.length">
|
|
439
450
|
<div
|
|
440
451
|
ref="clusterList"
|
|
441
452
|
class="clusters"
|
|
442
453
|
:style="pinnedClustersHeight"
|
|
443
454
|
>
|
|
455
|
+
<!-- Pinned Clusters -->
|
|
444
456
|
<div
|
|
445
457
|
v-if="showPinClusters && pinFiltered.length"
|
|
446
458
|
class="clustersPinned"
|
|
@@ -490,10 +502,13 @@ export default {
|
|
|
490
502
|
<hr>
|
|
491
503
|
</div>
|
|
492
504
|
</div>
|
|
505
|
+
|
|
506
|
+
<!-- Clusters Search result -->
|
|
493
507
|
<div class="clustersList">
|
|
494
508
|
<div
|
|
495
|
-
v-for="c in clustersFiltered"
|
|
509
|
+
v-for="(c, index) in clustersFiltered"
|
|
496
510
|
:key="c.id"
|
|
511
|
+
:data-testid="`top-level-menu-cluster-${index}`"
|
|
497
512
|
@click="hide()"
|
|
498
513
|
>
|
|
499
514
|
<nuxt-link
|
|
@@ -532,14 +547,18 @@ export default {
|
|
|
532
547
|
</span>
|
|
533
548
|
</div>
|
|
534
549
|
</div>
|
|
550
|
+
|
|
551
|
+
<!-- No clusters message -->
|
|
535
552
|
<div
|
|
536
553
|
v-if="(clustersFiltered.length === 0 || pinFiltered.length === 0) && searchActive"
|
|
554
|
+
data-testid="top-level-menu-no-results"
|
|
537
555
|
class="none-matching"
|
|
538
556
|
>
|
|
539
557
|
{{ t('nav.search.noResults') }}
|
|
540
558
|
</div>
|
|
541
559
|
</div>
|
|
542
560
|
|
|
561
|
+
<!-- See all clusters -->
|
|
543
562
|
<nuxt-link
|
|
544
563
|
v-if="clusters.length > maxClustersToShow"
|
|
545
564
|
class="clusters-all"
|
|
@@ -611,6 +630,8 @@ export default {
|
|
|
611
630
|
</nuxt-link>
|
|
612
631
|
</div>
|
|
613
632
|
</template>
|
|
633
|
+
|
|
634
|
+
<!-- App menu -->
|
|
614
635
|
<template v-if="configurationApps.length">
|
|
615
636
|
<div
|
|
616
637
|
class="category-title"
|
|
@@ -640,6 +661,8 @@ export default {
|
|
|
640
661
|
</template>
|
|
641
662
|
</div>
|
|
642
663
|
</div>
|
|
664
|
+
|
|
665
|
+
<!-- Footer -->
|
|
643
666
|
<div
|
|
644
667
|
class="footer"
|
|
645
668
|
>
|