@rancher/shell 0.5.1 → 0.5.3
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/translations/en-us.yaml +8 -4
- package/components/ClusterIconMenu.vue +24 -9
- package/components/CodeMirror.vue +79 -18
- package/components/FixedBanner.vue +1 -0
- package/components/ResourceDetail/index.vue +1 -4
- package/components/ResourceYaml.vue +29 -5
- package/components/SideNav.vue +42 -64
- package/components/SortableTable/index.vue +1 -1
- package/components/YamlEditor.vue +1 -0
- package/components/__tests__/CodeMirror.spec.ts +99 -0
- package/components/form/BannerSettings.vue +3 -0
- package/components/form/FileSelector.vue +1 -0
- package/components/form/KeyValue.vue +1 -0
- package/components/formatter/WorkloadDetailEndpoints.vue +12 -22
- package/components/formatter/__tests__/WorkloadDetailEndpoints.test.ts +81 -0
- package/components/nav/Header.vue +1 -0
- package/components/nav/Jump.vue +19 -9
- package/components/nav/TopLevelMenu.vue +37 -15
- package/components/nav/Type.vue +15 -4
- package/components/nav/__tests__/TopLevelMenu.test.ts +1 -1
- package/components/nav/__tests__/Type.test.ts +30 -0
- package/core/types-provisioning.ts +7 -0
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +77 -0
- package/detail/fleet.cattle.io.bundle.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +19 -4
- package/edit/management.cattle.io.setting.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/opsgenie.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +1 -2
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/slack.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +23 -10
- package/edit/provisioning.cattle.io.cluster/rke2.vue +22 -50
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +9 -11
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +3 -1
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +3 -0
- package/edit/token.vue +1 -0
- package/list/catalog.cattle.io.app.vue +1 -0
- package/list/management.cattle.io.setting.vue +1 -0
- package/machine-config/amazonec2.vue +1 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +151 -0
- package/models/__tests__/secret.test.ts +37 -0
- package/models/__tests__/storage.k8s.io.storageclass.test.ts +22 -0
- package/models/management.cattle.io.kontainerdriver.js +2 -1
- package/models/provisioning.cattle.io.cluster.js +36 -1
- package/models/secret.js +9 -0
- package/models/storage.k8s.io.storageclass.js +1 -1
- package/package.json +1 -1
- package/pages/c/_cluster/settings/DefaultLinksEditor.vue +1 -0
- package/pages/c/_cluster/settings/brand.vue +3 -0
- package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +4 -4
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +5 -2
- package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +96 -0
- package/pages/c/_cluster/uiplugins/__tests__/SetupUIPlugins.test.ts +128 -0
- package/plugins/dashboard-store/__tests__/actions.test.ts +196 -111
- package/plugins/dashboard-store/actions.js +4 -6
- package/plugins/dashboard-store/getters.js +60 -2
- package/plugins/dashboard-store/resource-class.js +6 -2
- package/plugins/steve/__tests__/getters.spec.ts +10 -0
- package/plugins/steve/__tests__/resource-utils.test.ts +159 -0
- package/plugins/steve/actions.js +3 -37
- package/plugins/steve/getters.js +6 -0
- package/plugins/steve/resource-utils.ts +38 -0
- package/scripts/extension/parse-tag-name +3 -3
- package/store/__tests__/type-map.test.ts +1122 -0
- package/store/index.js +3 -2
- package/store/plugins.js +7 -6
- package/store/type-map.js +145 -75
- package/types/shell/index.d.ts +2 -0
- package/utils/__tests__/create-yaml.test.ts +10 -0
- package/utils/create-yaml.js +5 -1
- package/utils/object.js +10 -0
|
@@ -185,7 +185,7 @@ nav:
|
|
|
185
185
|
configuration: Configuration
|
|
186
186
|
hci: HCI
|
|
187
187
|
search:
|
|
188
|
-
placeholder:
|
|
188
|
+
placeholder: Filter clusters by...
|
|
189
189
|
noResults: No matching clusters
|
|
190
190
|
clusters: clusters
|
|
191
191
|
resourceSearch:
|
|
@@ -1862,6 +1862,7 @@ cluster:
|
|
|
1862
1862
|
header: System Services
|
|
1863
1863
|
cni:
|
|
1864
1864
|
label: Container Network
|
|
1865
|
+
cniNoneBanner: When CNI is set to 'none' an <a href="https://ranchermanager.docs.rancher.com/reference-guides/cluster-configuration/rancher-server-configuration/rke2-cluster-configuration#additionalmanifest" target="_blank" rel="noopener noreferrer nofollow">Additional Manifest</a> deploying an alternate CNI must be specified for successful cluster provisioning.
|
|
1865
1866
|
cilium:
|
|
1866
1867
|
BandwidthManager:
|
|
1867
1868
|
enable: Enable Bandwidth Manager
|
|
@@ -1870,7 +1871,6 @@ cluster:
|
|
|
1870
1871
|
header: Cloud Provider Config
|
|
1871
1872
|
defaultValue:
|
|
1872
1873
|
label: Default - RKE2 Embedded
|
|
1873
|
-
unsupported: The current Cloud Provider is not supported by this version of Kubernetes. The Cloud Provider has been changed to External. Please use the Cloud Provider Config to supply an out-of-tree configuration as needed.
|
|
1874
1874
|
security:
|
|
1875
1875
|
header: Security
|
|
1876
1876
|
cis:
|
|
@@ -1903,8 +1903,8 @@ cluster:
|
|
|
1903
1903
|
toolTip: "This can be either a fixed number of nodes (e.g. 1) at a time or a percentage (e.g. 10%)"
|
|
1904
1904
|
drain:
|
|
1905
1905
|
label: Drain Nodes
|
|
1906
|
-
toolTip: Draining preemptively removes the pods on each node so there are no running workloads on the nodes being upgraded. Upgrading without draining is faster and causes less shuffling around, but pods may still be restarted depending on the upgrade being performed.
|
|
1907
|
-
deleteEmptyDir:
|
|
1906
|
+
toolTip: Draining preemptively removes the pods on each node so there are no running workloads on the nodes being upgraded. Upgrading without draining is faster and causes less shuffling around, but pods may still be restarted depending on the upgrade being performed.
|
|
1907
|
+
deleteEmptyDir:
|
|
1908
1908
|
warning: "By default, pods using emptyDir volumes will be deleted on upgrade. Operations reliant on emptyDir volumes persisting through the pod's lifecycle may be impacted."
|
|
1909
1909
|
label: Delete pods using emptyDir volumes
|
|
1910
1910
|
tooltip: emptyDir volumes are often used for ephemeral data, but the data will be permanently deleted. Draining will fail if this is not set and there are pods using emptyDir.
|
|
@@ -2078,6 +2078,10 @@ resource:
|
|
|
2078
2078
|
errors:
|
|
2079
2079
|
update: "Error updating {name}"
|
|
2080
2080
|
|
|
2081
|
+
codeMirror:
|
|
2082
|
+
keymap:
|
|
2083
|
+
tooltip: Key mapping preference.
|
|
2084
|
+
|
|
2081
2085
|
cruResource:
|
|
2082
2086
|
backToForm: Back to Form
|
|
2083
2087
|
backBody: You will lose any changes made to the YAML.
|
|
@@ -15,10 +15,11 @@ export default {
|
|
|
15
15
|
showLocalIcon() {
|
|
16
16
|
return this.cluster.isLocal && !this.cluster.isHarvester && !this.cluster.badge?.iconText;
|
|
17
17
|
},
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
hasCustomColor() {
|
|
19
|
+
return this.cluster.badge?.color;
|
|
20
|
+
},
|
|
21
|
+
customColor() {
|
|
22
|
+
return this.cluster.badge?.color || '';
|
|
22
23
|
}
|
|
23
24
|
},
|
|
24
25
|
methods: {
|
|
@@ -44,14 +45,17 @@ export default {
|
|
|
44
45
|
>
|
|
45
46
|
<div
|
|
46
47
|
class="cluster-badge-logo"
|
|
47
|
-
:class="{ 'disabled': !isEnabled }"
|
|
48
|
-
:style="{ borderBottom: badgeLogoBorderBottom }"
|
|
48
|
+
:class="{ 'disabled': !isEnabled, 'custom-color': hasCustomColor }"
|
|
49
49
|
>
|
|
50
50
|
<span
|
|
51
51
|
class="cluster-badge-logo-text"
|
|
52
52
|
>
|
|
53
53
|
{{ smallIdentifier(cluster.label) }}
|
|
54
54
|
</span>
|
|
55
|
+
<span
|
|
56
|
+
class="custom-color-decoration"
|
|
57
|
+
:style="{'background': customColor}"
|
|
58
|
+
/>
|
|
55
59
|
<svg
|
|
56
60
|
v-if="showLocalIcon"
|
|
57
61
|
class="cluster-local-logo"
|
|
@@ -99,7 +103,6 @@ export default {
|
|
|
99
103
|
</template>
|
|
100
104
|
|
|
101
105
|
<style lang="scss" scoped>
|
|
102
|
-
|
|
103
106
|
.cluster-icon-menu {
|
|
104
107
|
position: relative;
|
|
105
108
|
align-items: center;
|
|
@@ -111,8 +114,8 @@ export default {
|
|
|
111
114
|
.cluster-pin-icon {
|
|
112
115
|
position: absolute;
|
|
113
116
|
top: -6px;
|
|
114
|
-
right: -
|
|
115
|
-
font-size:
|
|
117
|
+
right: -7px;
|
|
118
|
+
font-size: 14px;
|
|
116
119
|
transform: scaleX(-1);
|
|
117
120
|
color: var(--body-text);
|
|
118
121
|
}
|
|
@@ -135,6 +138,18 @@ export default {
|
|
|
135
138
|
font-size: 12px;
|
|
136
139
|
text-transform: uppercase;
|
|
137
140
|
|
|
141
|
+
&.custom-color {
|
|
142
|
+
border-bottom: 4px solid transparent;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.custom-color-decoration {
|
|
146
|
+
height: 4px;
|
|
147
|
+
width: 100%;
|
|
148
|
+
position: absolute;
|
|
149
|
+
bottom: 0;
|
|
150
|
+
border-radius: 0px 0px 5px 5px;
|
|
151
|
+
}
|
|
152
|
+
|
|
138
153
|
&.disabled {
|
|
139
154
|
filter: grayscale(1);
|
|
140
155
|
color: var(--muted);
|
|
@@ -24,18 +24,23 @@ export default {
|
|
|
24
24
|
asTextArea: {
|
|
25
25
|
type: Boolean,
|
|
26
26
|
default: false
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
|
+
showKeyMapBox: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: false
|
|
31
|
+
},
|
|
28
32
|
},
|
|
29
33
|
|
|
30
34
|
data() {
|
|
31
35
|
return {
|
|
32
|
-
codeMirrorRef:
|
|
33
|
-
loaded:
|
|
36
|
+
codeMirrorRef: null,
|
|
37
|
+
loaded: false,
|
|
38
|
+
showKeyMapCloseIcon: false,
|
|
39
|
+
removeKeyMapBox: false,
|
|
34
40
|
};
|
|
35
41
|
},
|
|
36
42
|
|
|
37
43
|
computed: {
|
|
38
|
-
|
|
39
44
|
isDisabled() {
|
|
40
45
|
return this.mode === _VIEW;
|
|
41
46
|
},
|
|
@@ -70,6 +75,10 @@ export default {
|
|
|
70
75
|
|
|
71
76
|
return out;
|
|
72
77
|
},
|
|
78
|
+
|
|
79
|
+
keyMapText() {
|
|
80
|
+
return this.combinedOptions?.keyMap ? this.t(`prefs.keymap.${ this.combinedOptions.keyMap }`) : null;
|
|
81
|
+
},
|
|
73
82
|
},
|
|
74
83
|
|
|
75
84
|
created() {
|
|
@@ -124,6 +133,14 @@ export default {
|
|
|
124
133
|
if ( this.$refs.codeMirrorRef ) {
|
|
125
134
|
this.$refs.codeMirrorRef.codemirror.doc.setValue(value);
|
|
126
135
|
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
closeKeyMapInfo() {
|
|
139
|
+
this.removeKeyMapBox = true;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
onKeyMapMouseOver(v) {
|
|
143
|
+
this.showKeyMapCloseIcon = v;
|
|
127
144
|
}
|
|
128
145
|
}
|
|
129
146
|
};
|
|
@@ -134,18 +151,40 @@ export default {
|
|
|
134
151
|
class="code-mirror"
|
|
135
152
|
:class="{['as-text-area']: asTextArea}"
|
|
136
153
|
>
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
<div v-if="loaded">
|
|
155
|
+
<div
|
|
156
|
+
v-if="showKeyMapBox && !removeKeyMapBox && keyMapText"
|
|
157
|
+
class="keymap overlay"
|
|
158
|
+
>
|
|
159
|
+
<div
|
|
160
|
+
v-clean-tooltip="t('codeMirror.keymap.tooltip')"
|
|
161
|
+
class="label"
|
|
162
|
+
data-testid="code-mirror-keymap"
|
|
163
|
+
@mouseover="onKeyMapMouseOver(true)"
|
|
164
|
+
@mouseleave="onKeyMapMouseOver(false)"
|
|
165
|
+
>
|
|
166
|
+
<span>
|
|
167
|
+
{{ keyMapText }}
|
|
168
|
+
</span>
|
|
169
|
+
<i
|
|
170
|
+
v-if="showKeyMapCloseIcon"
|
|
171
|
+
class="icon icon-close icon-sm"
|
|
172
|
+
@click="closeKeyMapInfo"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
<codemirror
|
|
177
|
+
ref="codeMirrorRef"
|
|
178
|
+
:value="value"
|
|
179
|
+
:options="combinedOptions"
|
|
180
|
+
:disabled="isDisabled"
|
|
181
|
+
@ready="onReady"
|
|
182
|
+
@input="onInput"
|
|
183
|
+
@changes="onChanges"
|
|
184
|
+
@focus="onFocus"
|
|
185
|
+
@blur="onBlur"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
149
188
|
<div v-else>
|
|
150
189
|
Loading...
|
|
151
190
|
</div>
|
|
@@ -156,6 +195,30 @@ export default {
|
|
|
156
195
|
.code-mirror {
|
|
157
196
|
z-index: 0;
|
|
158
197
|
|
|
198
|
+
.overlay {
|
|
199
|
+
position: sticky;
|
|
200
|
+
display: grid;
|
|
201
|
+
top: 0;
|
|
202
|
+
float: right;
|
|
203
|
+
height: 0;
|
|
204
|
+
z-index: 1;
|
|
205
|
+
|
|
206
|
+
.label {
|
|
207
|
+
border-radius: 2px;
|
|
208
|
+
border-style: dashed;
|
|
209
|
+
border-width: 0.1px;
|
|
210
|
+
margin: 7px 7px 0 0;
|
|
211
|
+
padding: 7px;
|
|
212
|
+
color: var(--darker);
|
|
213
|
+
background-color: var(--overlay-bg);
|
|
214
|
+
font-size: 12px;
|
|
215
|
+
|
|
216
|
+
.icon {
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
159
222
|
.vue-codemirror .CodeMirror {
|
|
160
223
|
height: initial;
|
|
161
224
|
background: none
|
|
@@ -248,7 +311,5 @@ export default {
|
|
|
248
311
|
background-color: var(--primary);
|
|
249
312
|
}
|
|
250
313
|
}
|
|
251
|
-
|
|
252
314
|
}
|
|
253
|
-
|
|
254
315
|
</style>
|
|
@@ -28,7 +28,6 @@ function modeFor(route) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async function getYaml(store, model) {
|
|
31
|
-
const inStore = store.getters['currentStore'](model.type);
|
|
32
31
|
let yaml;
|
|
33
32
|
const opt = { headers: { accept: 'application/yaml' } };
|
|
34
33
|
|
|
@@ -36,9 +35,7 @@ async function getYaml(store, model) {
|
|
|
36
35
|
yaml = (await model.followLink('view', opt)).data;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return cleanedYaml;
|
|
38
|
+
return model.cleanForDownload(yaml);
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
export default {
|
|
@@ -365,6 +365,8 @@ export default {
|
|
|
365
365
|
>
|
|
366
366
|
<Footer
|
|
367
367
|
v-if="showFooter"
|
|
368
|
+
class="footer"
|
|
369
|
+
:class="{ 'edit': !isView }"
|
|
368
370
|
:mode="mode"
|
|
369
371
|
:errors="errors"
|
|
370
372
|
@save="save"
|
|
@@ -408,11 +410,29 @@ export default {
|
|
|
408
410
|
</template>
|
|
409
411
|
|
|
410
412
|
<style lang='scss' scoped>
|
|
411
|
-
.flex-content {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
413
|
+
.flex-content {
|
|
414
|
+
display: flex;
|
|
415
|
+
flex-direction: column;
|
|
416
|
+
flex-grow: 1;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.footer {
|
|
420
|
+
margin-top: 20px;
|
|
421
|
+
right: 0;
|
|
422
|
+
position: sticky;
|
|
423
|
+
bottom: 0;
|
|
424
|
+
background-color: var(--header-bg);
|
|
425
|
+
|
|
426
|
+
// Overrides outlet padding
|
|
427
|
+
margin-left: -$space-m;
|
|
428
|
+
margin-right: -$space-m;
|
|
429
|
+
margin-bottom: -$space-m;
|
|
430
|
+
padding: $space-s $space-m;
|
|
431
|
+
|
|
432
|
+
&.edit {
|
|
433
|
+
border-top: var(--header-border-size) solid var(--header-border);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
416
436
|
</style>
|
|
417
437
|
|
|
418
438
|
<style lang="scss">
|
|
@@ -424,6 +444,10 @@ export default {
|
|
|
424
444
|
footer .actions {
|
|
425
445
|
text-align: right;
|
|
426
446
|
}
|
|
447
|
+
|
|
448
|
+
.spacer-small {
|
|
449
|
+
padding: 0;
|
|
450
|
+
}
|
|
427
451
|
}
|
|
428
452
|
|
|
429
453
|
</style>
|
package/components/SideNav.vue
CHANGED
|
@@ -7,16 +7,16 @@ import {
|
|
|
7
7
|
FAVORITE_TYPES
|
|
8
8
|
} from '@shell/store/prefs';
|
|
9
9
|
import { getVersionInfo } from '@shell/utils/version';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
addObjects, replaceWith, clear, addObject, sameContents
|
|
12
|
+
} from '@shell/utils/array';
|
|
11
13
|
import { sortBy } from '@shell/utils/sort';
|
|
12
14
|
import { ucFirst } from '@shell/utils/string';
|
|
13
15
|
|
|
14
|
-
import {
|
|
15
|
-
HCI, CATALOG, UI, SCHEMA, COUNT
|
|
16
|
-
} from '@shell/config/types';
|
|
16
|
+
import { HCI, CATALOG, UI, SCHEMA } from '@shell/config/types';
|
|
17
17
|
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
18
18
|
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
19
|
-
import {
|
|
19
|
+
import { TYPE_MODES } from '@shell/store/type-map';
|
|
20
20
|
import { NAME as NAVLINKS } from '@shell/config/product/navlinks';
|
|
21
21
|
import Group from '@shell/components/nav/Group';
|
|
22
22
|
|
|
@@ -31,6 +31,7 @@ export default {
|
|
|
31
31
|
},
|
|
32
32
|
|
|
33
33
|
created() {
|
|
34
|
+
// Ensure that changes to resource that change often don't resort to spamming redraw of the side nav
|
|
34
35
|
this.queueUpdate = debounce(this.getGroups, 500);
|
|
35
36
|
|
|
36
37
|
this.getGroups();
|
|
@@ -42,24 +43,25 @@ export default {
|
|
|
42
43
|
},
|
|
43
44
|
|
|
44
45
|
watch: {
|
|
45
|
-
counts(a, b) {
|
|
46
|
-
if ( a !== b ) {
|
|
47
|
-
this.queueUpdate();
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Keep this simple, we're only interested in new / removed schemas
|
|
49
|
+
*/
|
|
50
|
+
allSchemasIds(a, b) {
|
|
51
|
+
if ( !sameContents(a, b) ) {
|
|
53
52
|
this.queueUpdate();
|
|
54
53
|
}
|
|
55
54
|
},
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
if ( a
|
|
56
|
+
allNavLinksIds(a, b) {
|
|
57
|
+
if ( !sameContents(a, b) ) {
|
|
59
58
|
this.queueUpdate();
|
|
60
59
|
}
|
|
61
60
|
},
|
|
62
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Note - There's no watch on prefs, so this only catches in session changes
|
|
64
|
+
*/
|
|
63
65
|
favoriteTypes(a, b) {
|
|
64
66
|
if ( !isEqual(a, b) ) {
|
|
65
67
|
this.queueUpdate();
|
|
@@ -73,23 +75,24 @@ export default {
|
|
|
73
75
|
},
|
|
74
76
|
|
|
75
77
|
productId(a, b) {
|
|
76
|
-
if (
|
|
78
|
+
if ( a !== b) {
|
|
77
79
|
// Immediately update because you'll see it come in later
|
|
78
80
|
this.getGroups();
|
|
79
81
|
}
|
|
80
82
|
},
|
|
81
83
|
|
|
84
|
+
// Queue namespaceMode and namespaces
|
|
85
|
+
// Changes to namespaceMode can also change namespaces, so keep this simple and execute both in a shortened queue
|
|
86
|
+
|
|
82
87
|
namespaceMode(a, b) {
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
this.getGroups();
|
|
88
|
+
if ( a !== b ) {
|
|
89
|
+
this.queueUpdate();
|
|
86
90
|
}
|
|
87
91
|
},
|
|
88
92
|
|
|
89
93
|
namespaces(a, b) {
|
|
90
94
|
if ( !isEqual(a, b) ) {
|
|
91
|
-
|
|
92
|
-
this.getGroups();
|
|
95
|
+
this.queueUpdate();
|
|
93
96
|
}
|
|
94
97
|
},
|
|
95
98
|
|
|
@@ -100,13 +103,6 @@ export default {
|
|
|
100
103
|
}
|
|
101
104
|
},
|
|
102
105
|
|
|
103
|
-
product(a, b) {
|
|
104
|
-
if ( !isEqual(a, b) ) {
|
|
105
|
-
// Immediately update because you'll see it come in later
|
|
106
|
-
this.getGroups();
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
|
|
110
106
|
$route(a, b) {
|
|
111
107
|
this.$nextTick(() => this.syncNav());
|
|
112
108
|
},
|
|
@@ -174,7 +170,7 @@ export default {
|
|
|
174
170
|
return this.$store.getters['cluster/all'](UI.NAV_LINK);
|
|
175
171
|
},
|
|
176
172
|
|
|
177
|
-
|
|
173
|
+
allSchemasIds() {
|
|
178
174
|
const managementReady = this.managementReady;
|
|
179
175
|
const product = this.currentProduct;
|
|
180
176
|
|
|
@@ -182,33 +178,19 @@ export default {
|
|
|
182
178
|
return [];
|
|
183
179
|
}
|
|
184
180
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
counts() {
|
|
189
|
-
const managementReady = this.managementReady;
|
|
190
|
-
const product = this.currentProduct;
|
|
191
|
-
|
|
192
|
-
if ( !managementReady || !product ) {
|
|
193
|
-
return {};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const inStore = product.inStore;
|
|
197
|
-
|
|
198
|
-
// So that there's something to watch for updates
|
|
199
|
-
if ( this.$store.getters[`${ inStore }/haveAll`](COUNT) ) {
|
|
200
|
-
const counts = this.$store.getters[`${ inStore }/all`](COUNT)[0].counts;
|
|
201
|
-
|
|
202
|
-
return counts;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {};
|
|
181
|
+
// This does take some up-front time, however avoids an even more costly getGroups call
|
|
182
|
+
return this.$store.getters[`${ product.inStore }/all`](SCHEMA).map((s) => s.id).sort();
|
|
206
183
|
},
|
|
207
184
|
|
|
208
185
|
namespaces() {
|
|
209
186
|
return this.$store.getters['activeNamespaceCache'];
|
|
210
187
|
},
|
|
188
|
+
|
|
189
|
+
allNavLinksIds() {
|
|
190
|
+
return this.allNavLinks.map((a) => a.id);
|
|
191
|
+
},
|
|
211
192
|
},
|
|
193
|
+
|
|
212
194
|
methods: {
|
|
213
195
|
/**
|
|
214
196
|
* Fetch navigation by creating groups from product schemas
|
|
@@ -227,13 +209,6 @@ export default {
|
|
|
227
209
|
}
|
|
228
210
|
|
|
229
211
|
const currentProduct = this.$store.getters['productId'];
|
|
230
|
-
let namespaces = null;
|
|
231
|
-
|
|
232
|
-
if ( !this.$store.getters['isAllNamespaces'] ) {
|
|
233
|
-
const namespacesObject = this.$store.getters['namespaces']();
|
|
234
|
-
|
|
235
|
-
namespaces = Object.keys(namespacesObject);
|
|
236
|
-
}
|
|
237
212
|
|
|
238
213
|
// Always show cluster-level types, regardless of the namespace filter
|
|
239
214
|
const namespaceMode = 'both';
|
|
@@ -255,7 +230,8 @@ export default {
|
|
|
255
230
|
// This should already have come into the list from above, but in case it hasn't...
|
|
256
231
|
addObject(loadProducts, currentProduct);
|
|
257
232
|
|
|
258
|
-
this.getProductsGroups(out, loadProducts, namespaceMode,
|
|
233
|
+
this.getProductsGroups(out, loadProducts, namespaceMode, productMap);
|
|
234
|
+
|
|
259
235
|
this.getExplorerGroups(out);
|
|
260
236
|
|
|
261
237
|
replaceWith(this.groups, ...sortBy(out, ['weight:desc', 'label']));
|
|
@@ -263,12 +239,12 @@ export default {
|
|
|
263
239
|
this.gettingGroups = false;
|
|
264
240
|
},
|
|
265
241
|
|
|
266
|
-
getProductsGroups(out, loadProducts, namespaceMode,
|
|
242
|
+
getProductsGroups(out, loadProducts, namespaceMode, productMap) {
|
|
267
243
|
const clusterId = this.$store.getters['clusterId'];
|
|
268
244
|
const currentType = this.$route.params.resource || '';
|
|
269
245
|
|
|
270
246
|
for ( const productId of loadProducts ) {
|
|
271
|
-
const modes = [BASIC];
|
|
247
|
+
const modes = [TYPE_MODES.BASIC];
|
|
272
248
|
|
|
273
249
|
if ( productId === NAVLINKS ) {
|
|
274
250
|
// Navlinks produce their own top-level nav items so don't need to show it as a product.
|
|
@@ -276,14 +252,16 @@ export default {
|
|
|
276
252
|
}
|
|
277
253
|
|
|
278
254
|
if ( productId === EXPLORER ) {
|
|
279
|
-
modes.push(FAVORITE);
|
|
280
|
-
modes.push(USED);
|
|
255
|
+
modes.push(TYPE_MODES.FAVORITE);
|
|
256
|
+
modes.push(TYPE_MODES.USED);
|
|
281
257
|
}
|
|
282
258
|
|
|
283
|
-
|
|
284
|
-
|
|
259
|
+
// Get all types for all modes
|
|
260
|
+
const typesByMode = this.$store.getters['type-map/allTypes'](productId, modes);
|
|
285
261
|
|
|
286
|
-
|
|
262
|
+
for ( const mode of modes ) {
|
|
263
|
+
const types = typesByMode[mode] || {};
|
|
264
|
+
const more = this.$store.getters['type-map/getTree'](productId, mode, types, clusterId, namespaceMode, currentType);
|
|
287
265
|
|
|
288
266
|
if ( productId === EXPLORER || !this.isExplorer ) {
|
|
289
267
|
addObjects(out, more);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { shallowMount, Wrapper } from '@vue/test-utils';
|
|
2
|
+
import CodeMirror from '@shell/components/CodeMirror.vue';
|
|
3
|
+
import { _EDIT, _YAML } from '@shell/config/query-params';
|
|
4
|
+
|
|
5
|
+
describe('component: CodeMirror.vue', () => {
|
|
6
|
+
let wrapper: Wrapper<InstanceType<typeof CodeMirror>>;
|
|
7
|
+
|
|
8
|
+
const options = {
|
|
9
|
+
readOnly: false,
|
|
10
|
+
gutters: [
|
|
11
|
+
'CodeMirror-lint-markers',
|
|
12
|
+
'CodeMirror-foldgutter'
|
|
13
|
+
],
|
|
14
|
+
mode: 'yaml',
|
|
15
|
+
lint: true,
|
|
16
|
+
lineNumbers: true,
|
|
17
|
+
styleActiveLine: true,
|
|
18
|
+
tabSize: 2,
|
|
19
|
+
indentWithTabs: false,
|
|
20
|
+
cursorBlinkRate: 530,
|
|
21
|
+
extraKeys: { 'Ctrl-Space': 'autocomplete' }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const mountOptions = {
|
|
25
|
+
propsData: {
|
|
26
|
+
value: '',
|
|
27
|
+
mode: _EDIT,
|
|
28
|
+
options,
|
|
29
|
+
asTextArea: false,
|
|
30
|
+
showKeyMapBox: true,
|
|
31
|
+
},
|
|
32
|
+
mocks: {
|
|
33
|
+
$store: {
|
|
34
|
+
getters: {
|
|
35
|
+
currentStore: () => 'current_store',
|
|
36
|
+
'current_store/schemaFor': jest.fn(),
|
|
37
|
+
'current_store/all': jest.fn(),
|
|
38
|
+
'i18n/t': () => 'Vim',
|
|
39
|
+
'prefs/get': () => 'Vim',
|
|
40
|
+
'prefs/theme': jest.fn(),
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
$route: { query: { AS: _YAML } },
|
|
44
|
+
$router: { applyQuery: jest.fn() },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('keyMap info', () => {
|
|
49
|
+
(window as any).__codeMirrorLoader = () => new Promise((resolve) => {
|
|
50
|
+
resolve(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
wrapper = shallowMount(
|
|
54
|
+
CodeMirror,
|
|
55
|
+
mountOptions,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
it(`should show keyMap preference`, async() => {
|
|
59
|
+
await wrapper.vm.$nextTick();
|
|
60
|
+
|
|
61
|
+
const keyMapBox = wrapper.find('[data-testid="code-mirror-keymap"]');
|
|
62
|
+
const closeIcon = keyMapBox.find('.icon');
|
|
63
|
+
|
|
64
|
+
expect(keyMapBox.element.textContent).toContain('Vim');
|
|
65
|
+
expect(closeIcon.element).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it(`should show keyMap close icon on mouse over`, async() => {
|
|
69
|
+
await wrapper.vm.$nextTick();
|
|
70
|
+
|
|
71
|
+
const keyMapBox = wrapper.find('[data-testid="code-mirror-keymap"]');
|
|
72
|
+
|
|
73
|
+
keyMapBox.trigger('mouseover');
|
|
74
|
+
await wrapper.vm.$nextTick();
|
|
75
|
+
|
|
76
|
+
const closeIcon = keyMapBox.find('.icon');
|
|
77
|
+
|
|
78
|
+
expect(closeIcon.element).toBeDefined();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it(`should remove keyMap box`, async() => {
|
|
82
|
+
await wrapper.vm.$nextTick();
|
|
83
|
+
|
|
84
|
+
let keyMapBox = wrapper.find('[data-testid="code-mirror-keymap"]');
|
|
85
|
+
|
|
86
|
+
keyMapBox.trigger('mouseover');
|
|
87
|
+
await wrapper.vm.$nextTick();
|
|
88
|
+
|
|
89
|
+
const closeIcon = keyMapBox.find('.icon');
|
|
90
|
+
|
|
91
|
+
closeIcon.element.click();
|
|
92
|
+
await wrapper.vm.$nextTick();
|
|
93
|
+
|
|
94
|
+
keyMapBox = wrapper.find('[data-testid="code-mirror-keymap"]');
|
|
95
|
+
|
|
96
|
+
expect(keyMapBox.element).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|