@rancher/shell 0.3.23 → 0.3.24
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/styles/base/_variables.scss +1 -0
- package/assets/styles/themes/_dark.scss +1 -0
- package/assets/styles/themes/_light.scss +6 -5
- package/assets/translations/en-us.yaml +15 -10
- package/assets/translations/zh-hans.yaml +1 -1
- package/components/ClusterProviderIconMenu.vue +161 -0
- package/components/Loading.vue +1 -1
- package/components/SideNav.vue +1 -1
- package/components/form/SelectOrCreateAuthSecret.vue +7 -0
- package/components/nav/Group.vue +54 -24
- package/components/nav/Header.vue +1 -1
- package/components/nav/TopLevelMenu.vue +469 -294
- package/components/nav/Type.vue +31 -5
- package/creators/pkg/init +2 -2
- package/edit/fleet.cattle.io.gitrepo.vue +43 -15
- package/edit/logging.banzaicloud.io.output/index.vue +7 -0
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +3 -8
- package/edit/provisioning.cattle.io.cluster/rke2.vue +108 -33
- package/edit/workload/storage/ContainerMountPaths.vue +7 -5
- package/initialize/App.js +2 -0
- package/initialize/client.js +63 -51
- package/initialize/index.js +2 -0
- package/layouts/default.vue +8 -0
- package/machine-config/amazonec2.vue +1 -0
- package/mixins/fetch.client.js +3 -3
- package/package.json +1 -1
- package/pages/__tests__/prefs.test.ts +1 -1
- package/pages/c/_cluster/explorer/ConfigBadge.vue +1 -0
- package/pages/prefs.vue +3 -13
- package/plugins/dashboard-store/resource-class.js +1 -1
- package/public/index.html +4 -2
- package/rancher-components/BadgeState/BadgeState.vue +5 -1
- package/rancher-components/Banner/Banner.test.ts +51 -1
- package/rancher-components/Banner/Banner.vue +134 -53
- package/rancher-components/Card/Card.test.ts +37 -0
- package/rancher-components/Card/Card.vue +24 -7
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
- package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -10
- package/rancher-components/Form/Radio/RadioButton.test.ts +35 -0
- package/rancher-components/Form/Radio/RadioButton.vue +30 -13
- package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
- package/rancher-components/StringList/StringList.test.ts +453 -49
- package/rancher-components/StringList/StringList.vue +92 -58
- package/scripts/extension/parse-tag-name +0 -0
- package/store/prefs.js +3 -4
- package/store/type-map.js +2 -16
- package/types/shell/index.d.ts +13 -10
- package/utils/__tests__/sort.test.ts +61 -0
- package/utils/string.js +12 -0
- package/vue.config.js +1 -4
- package/yarn-error.log +200 -0
|
@@ -30,9 +30,9 @@ const CLASS = {
|
|
|
30
30
|
* Manage a list of strings
|
|
31
31
|
*/
|
|
32
32
|
export default Vue.extend({
|
|
33
|
-
components: { LabeledInput },
|
|
34
33
|
|
|
35
|
-
name:
|
|
34
|
+
name: 'StringList',
|
|
35
|
+
components: { LabeledInput },
|
|
36
36
|
|
|
37
37
|
props: {
|
|
38
38
|
/**
|
|
@@ -85,11 +85,11 @@ export default Vue.extend({
|
|
|
85
85
|
},
|
|
86
86
|
data() {
|
|
87
87
|
return {
|
|
88
|
-
value:
|
|
89
|
-
selected:
|
|
90
|
-
|
|
91
|
-
isCreateItem:
|
|
92
|
-
errors:
|
|
88
|
+
value: null as string | null,
|
|
89
|
+
selected: null as string | null,
|
|
90
|
+
editedItem: null as string | null,
|
|
91
|
+
isCreateItem: false,
|
|
92
|
+
errors: { duplicate: false } as Record<Error, boolean>
|
|
93
93
|
};
|
|
94
94
|
},
|
|
95
95
|
|
|
@@ -100,8 +100,8 @@ export default Vue.extend({
|
|
|
100
100
|
*/
|
|
101
101
|
errorMessagesArray(): string[] {
|
|
102
102
|
return (Object.keys(this.errors) as Error[])
|
|
103
|
-
.filter(f =>
|
|
104
|
-
.map(k => this.errorMessages[k]);
|
|
103
|
+
.filter((f) => this.errors[f] && this.errorMessages[f])
|
|
104
|
+
.map((k) => this.errorMessages[k]);
|
|
105
105
|
},
|
|
106
106
|
},
|
|
107
107
|
|
|
@@ -113,23 +113,35 @@ export default Vue.extend({
|
|
|
113
113
|
this.toggleEditMode(false);
|
|
114
114
|
this.toggleCreateMode(false);
|
|
115
115
|
},
|
|
116
|
+
value(val) {
|
|
117
|
+
this.$emit('type:item', val);
|
|
118
|
+
},
|
|
119
|
+
errors: {
|
|
120
|
+
handler(val) {
|
|
121
|
+
this.$emit('errors', val);
|
|
122
|
+
},
|
|
123
|
+
deep: true
|
|
124
|
+
}
|
|
116
125
|
},
|
|
117
126
|
|
|
118
127
|
methods: {
|
|
119
128
|
onChange(value: string) {
|
|
120
129
|
this.value = value;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
130
|
+
|
|
131
|
+
const items = [
|
|
132
|
+
...this.items,
|
|
133
|
+
this.value
|
|
134
|
+
];
|
|
135
|
+
|
|
124
136
|
this.toggleError(
|
|
125
137
|
'duplicate',
|
|
126
|
-
|
|
127
|
-
this.isCreateItem ? INPUT.create : INPUT.edit
|
|
138
|
+
hasDuplicatedStrings(items, this.caseSensitive),
|
|
139
|
+
this.isCreateItem ? INPUT.create : INPUT.edit
|
|
128
140
|
);
|
|
129
141
|
},
|
|
130
142
|
|
|
131
143
|
onSelect(item: string) {
|
|
132
|
-
if (this.isCreateItem || this.
|
|
144
|
+
if (this.readonly || this.isCreateItem || this.editedItem === item) {
|
|
133
145
|
return;
|
|
134
146
|
}
|
|
135
147
|
this.selected = item;
|
|
@@ -160,7 +172,7 @@ export default Vue.extend({
|
|
|
160
172
|
},
|
|
161
173
|
|
|
162
174
|
onClickEmptyBody() {
|
|
163
|
-
if (!this.isCreateItem && !this.
|
|
175
|
+
if (!this.isCreateItem && !this.editedItem) {
|
|
164
176
|
this.toggleCreateMode(true);
|
|
165
177
|
}
|
|
166
178
|
},
|
|
@@ -176,38 +188,43 @@ export default Vue.extend({
|
|
|
176
188
|
|
|
177
189
|
return;
|
|
178
190
|
}
|
|
179
|
-
if (this.
|
|
191
|
+
if (this.editedItem) {
|
|
192
|
+
this.deleteAndSelectNext(this.editedItem);
|
|
180
193
|
this.toggleEditMode(false);
|
|
181
194
|
|
|
182
195
|
return;
|
|
183
196
|
}
|
|
184
197
|
if (this.selected) {
|
|
185
|
-
|
|
198
|
+
this.deleteAndSelectNext(this.selected);
|
|
199
|
+
}
|
|
200
|
+
},
|
|
186
201
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
202
|
+
deleteAndSelectNext(currItem: string) {
|
|
203
|
+
const index = findStringIndex(this.items, currItem, false);
|
|
204
|
+
|
|
205
|
+
if (index !== -1) {
|
|
206
|
+
/**
|
|
207
|
+
* Select the next item in the list.
|
|
208
|
+
*/
|
|
209
|
+
const item = (this.items[index + 1] || this.items[index - 1]);
|
|
192
210
|
|
|
193
|
-
|
|
194
|
-
|
|
211
|
+
this.onSelect(item);
|
|
212
|
+
this.setFocus(item);
|
|
195
213
|
|
|
196
|
-
|
|
197
|
-
}
|
|
214
|
+
this.deleteItem(this.items[index]);
|
|
198
215
|
}
|
|
199
216
|
},
|
|
200
217
|
|
|
201
218
|
setFocus(refId: string) {
|
|
202
|
-
this.$nextTick(() => this.getElemByRef(refId)?.focus());
|
|
219
|
+
this.$nextTick(() => (this.getElemByRef(refId) as Vue & HTMLElement)?.focus());
|
|
203
220
|
},
|
|
204
221
|
|
|
205
222
|
/**
|
|
206
223
|
* Move scrollbar when the selected item is over the top or bottom side of the box
|
|
207
224
|
*/
|
|
208
225
|
moveScrollbar(arrow: Arrow, value?: number) {
|
|
209
|
-
const box = this.getElemByRef(BOX);
|
|
210
|
-
const item = this.getElemByRef(this.selected || '');
|
|
226
|
+
const box = this.getElemByRef(BOX) as HTMLElement;
|
|
227
|
+
const item = this.getElemByRef(this.selected || '') as HTMLElement;
|
|
211
228
|
|
|
212
229
|
if (box && item && item.className.includes(CLASS.item)) {
|
|
213
230
|
const boxRect = box.getClientRects()[0];
|
|
@@ -229,13 +246,14 @@ export default Vue.extend({
|
|
|
229
246
|
*/
|
|
230
247
|
toggleError(type: Error, val: boolean, refId?: string) {
|
|
231
248
|
this.errors[type] = val;
|
|
249
|
+
|
|
232
250
|
if (refId) {
|
|
233
251
|
this.toggleErrorClass(refId, val);
|
|
234
252
|
}
|
|
235
253
|
},
|
|
236
254
|
|
|
237
255
|
toggleErrorClass(refId: string, val: boolean) {
|
|
238
|
-
const input = this.getElemByRef(refId)?.$el;
|
|
256
|
+
const input = (this.getElemByRef(refId) as Vue)?.$el;
|
|
239
257
|
|
|
240
258
|
if (input) {
|
|
241
259
|
if (val) {
|
|
@@ -250,7 +268,11 @@ export default Vue.extend({
|
|
|
250
268
|
* Show/Hide the input line to create new item
|
|
251
269
|
*/
|
|
252
270
|
toggleCreateMode(show: boolean) {
|
|
271
|
+
if (this.readonly) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
253
274
|
if (show) {
|
|
275
|
+
this.toggleEditMode(false);
|
|
254
276
|
this.value = '';
|
|
255
277
|
|
|
256
278
|
this.isCreateItem = true;
|
|
@@ -268,31 +290,34 @@ export default Vue.extend({
|
|
|
268
290
|
* Show/Hide the in-line editing to edit an existing item
|
|
269
291
|
*/
|
|
270
292
|
toggleEditMode(show: boolean, item?: string) {
|
|
293
|
+
if (this.readonly) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
271
296
|
if (show) {
|
|
272
297
|
this.toggleCreateMode(false);
|
|
273
|
-
this.value = this.
|
|
298
|
+
this.value = this.editedItem;
|
|
274
299
|
|
|
275
|
-
this.
|
|
300
|
+
this.editedItem = item || '';
|
|
276
301
|
this.setFocus(INPUT.edit);
|
|
277
302
|
} else {
|
|
278
303
|
this.value = null;
|
|
279
304
|
this.toggleError('duplicate', false);
|
|
280
305
|
this.onSelectLeave();
|
|
281
306
|
|
|
282
|
-
this.
|
|
307
|
+
this.editedItem = null;
|
|
283
308
|
}
|
|
284
309
|
},
|
|
285
310
|
|
|
286
311
|
getElemByRef(id: string) {
|
|
287
312
|
const ref = this.$refs[id];
|
|
288
313
|
|
|
289
|
-
return
|
|
314
|
+
return Array.isArray(ref) ? ref[0] : ref;
|
|
290
315
|
},
|
|
291
316
|
|
|
292
317
|
/**
|
|
293
318
|
* Create a new item and insert in the items list
|
|
294
319
|
*/
|
|
295
|
-
saveItem() {
|
|
320
|
+
saveItem(closeInput = true) {
|
|
296
321
|
const value = this.value?.trim();
|
|
297
322
|
|
|
298
323
|
if (value) {
|
|
@@ -301,21 +326,20 @@ export default Vue.extend({
|
|
|
301
326
|
value,
|
|
302
327
|
];
|
|
303
328
|
|
|
304
|
-
if (hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
305
|
-
this.
|
|
306
|
-
|
|
307
|
-
return;
|
|
329
|
+
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
330
|
+
this.updateItems(items);
|
|
308
331
|
}
|
|
332
|
+
}
|
|
309
333
|
|
|
310
|
-
|
|
334
|
+
if (closeInput) {
|
|
335
|
+
this.toggleCreateMode(false);
|
|
311
336
|
}
|
|
312
|
-
this.toggleCreateMode(false);
|
|
313
337
|
},
|
|
314
338
|
|
|
315
339
|
/**
|
|
316
340
|
* Update an existing item in the items list
|
|
317
341
|
*/
|
|
318
|
-
updateItem(item: string) {
|
|
342
|
+
updateItem(item: string, closeInput = true) {
|
|
319
343
|
const value = this.value?.trim();
|
|
320
344
|
|
|
321
345
|
if (value) {
|
|
@@ -326,22 +350,21 @@ export default Vue.extend({
|
|
|
326
350
|
items[index] = value;
|
|
327
351
|
}
|
|
328
352
|
|
|
329
|
-
if (hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
330
|
-
this.
|
|
331
|
-
|
|
332
|
-
return;
|
|
353
|
+
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
354
|
+
this.updateItems(items);
|
|
333
355
|
}
|
|
356
|
+
}
|
|
334
357
|
|
|
335
|
-
|
|
358
|
+
if (closeInput) {
|
|
359
|
+
this.toggleEditMode(false);
|
|
336
360
|
}
|
|
337
|
-
this.toggleEditMode(false);
|
|
338
361
|
},
|
|
339
362
|
|
|
340
363
|
/**
|
|
341
364
|
* Remove an item from items list
|
|
342
365
|
*/
|
|
343
366
|
deleteItem(item?: string) {
|
|
344
|
-
const items = this.items.filter(f => f !== item);
|
|
367
|
+
const items = this.items.filter((f) => f !== item);
|
|
345
368
|
|
|
346
369
|
this.updateItems(items);
|
|
347
370
|
},
|
|
@@ -387,19 +410,20 @@ export default Vue.extend({
|
|
|
387
410
|
@blur="onSelectLeave(item)"
|
|
388
411
|
>
|
|
389
412
|
<span
|
|
390
|
-
v-if="!
|
|
413
|
+
v-if="!editedItem || editedItem !== item"
|
|
391
414
|
class="label static"
|
|
392
415
|
>
|
|
393
416
|
{{ item }}
|
|
394
417
|
</span>
|
|
395
418
|
<LabeledInput
|
|
396
|
-
v-if="
|
|
419
|
+
v-if="editedItem && editedItem === item"
|
|
397
420
|
ref="item-edit"
|
|
421
|
+
:data-testid="`item-edit-${item}`"
|
|
398
422
|
class="edit-input static"
|
|
399
423
|
:value="value != null ? value : item"
|
|
400
424
|
@input="onChange($event)"
|
|
401
|
-
@blur.prevent="
|
|
402
|
-
@keydown.native.enter="updateItem(item)"
|
|
425
|
+
@blur.prevent="updateItem(item)"
|
|
426
|
+
@keydown.native.enter="updateItem(item, !errors.duplicate)"
|
|
403
427
|
/>
|
|
404
428
|
</div>
|
|
405
429
|
<div
|
|
@@ -408,12 +432,14 @@ export default Vue.extend({
|
|
|
408
432
|
>
|
|
409
433
|
<LabeledInput
|
|
410
434
|
ref="item-create"
|
|
435
|
+
data-testid="item-create"
|
|
411
436
|
class="create-input static"
|
|
412
437
|
type="text"
|
|
413
438
|
:value="value"
|
|
414
439
|
:placeholder="placeholder"
|
|
415
440
|
@input="onChange($event)"
|
|
416
|
-
@
|
|
441
|
+
@blur.prevent="saveItem"
|
|
442
|
+
@keydown.native.enter="saveItem(!errors.duplicate)"
|
|
417
443
|
/>
|
|
418
444
|
</div>
|
|
419
445
|
</div>
|
|
@@ -427,25 +453,32 @@ export default Vue.extend({
|
|
|
427
453
|
class="action-buttons"
|
|
428
454
|
>
|
|
429
455
|
<button
|
|
456
|
+
data-testid="button-remove"
|
|
430
457
|
class="btn btn-sm role-tertiary remove-button"
|
|
431
|
-
:disabled="!selected && !isCreateItem && !
|
|
458
|
+
:disabled="!selected && !isCreateItem && !editedItem"
|
|
432
459
|
@mousedown.prevent="onClickMinusButton"
|
|
433
460
|
>
|
|
434
461
|
<span class="icon icon-minus icon-sm" />
|
|
435
462
|
</button>
|
|
436
463
|
<button
|
|
464
|
+
data-testid="button-add"
|
|
437
465
|
class="btn btn-sm role-tertiary add-button"
|
|
438
|
-
:disabled="isCreateItem"
|
|
466
|
+
:disabled="isCreateItem || editedItem"
|
|
439
467
|
@click.prevent="onClickPlusButton"
|
|
440
468
|
>
|
|
441
469
|
<span class="icon icon-plus icon-sm" />
|
|
442
470
|
</button>
|
|
443
471
|
</div>
|
|
444
472
|
<div class="messages">
|
|
445
|
-
<i
|
|
473
|
+
<i
|
|
474
|
+
v-if="errorMessagesArray.length > 0"
|
|
475
|
+
data-testid="i-warning-icon"
|
|
476
|
+
class="icon icon-warning icon-lg"
|
|
477
|
+
/>
|
|
446
478
|
<span
|
|
447
479
|
v-for="(msg, idx) in errorMessagesArray"
|
|
448
480
|
:key="idx"
|
|
481
|
+
:data-testid="`span-error-message-${msg}`"
|
|
449
482
|
class="error"
|
|
450
483
|
>
|
|
451
484
|
{{ idx > 0 ? '; ' : '' }}
|
|
@@ -499,6 +532,7 @@ export default Vue.extend({
|
|
|
499
532
|
width: auto;
|
|
500
533
|
user-select: none;
|
|
501
534
|
overflow: hidden;
|
|
535
|
+
white-space: no-wrap;
|
|
502
536
|
text-overflow: ellipsis;
|
|
503
537
|
padding-top: 1px;
|
|
504
538
|
}
|
|
File without changes
|
package/store/prefs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { SETTING } from '@shell/config/settings';
|
|
2
2
|
import { MANAGEMENT, STEVE } from '@shell/config/types';
|
|
3
3
|
import { clone } from '@shell/utils/object';
|
|
4
|
-
import
|
|
4
|
+
import Vue from 'vue';
|
|
5
5
|
|
|
6
6
|
const definitions = {};
|
|
7
7
|
/**
|
|
@@ -115,8 +115,7 @@ export const PROVISIONER = create('provisioner', _RKE2, { options: [_RKE1, _RKE2
|
|
|
115
115
|
export const PSP_DEPRECATION_BANNER = create('hide-psp-deprecation-banner', false, { parseJSON });
|
|
116
116
|
|
|
117
117
|
// Maximum number of clusters to show in the slide-in menu
|
|
118
|
-
export const MENU_MAX_CLUSTERS =
|
|
119
|
-
|
|
118
|
+
export const MENU_MAX_CLUSTERS = 10;
|
|
120
119
|
// Prompt for confirm when scaling down node pool in GUI and save the pref
|
|
121
120
|
export const SCALE_POOL_PROMPT = create('scale-pool-prompt', null, { parseJSON });
|
|
122
121
|
// --------------------
|
package/store/type-map.js
CHANGED
|
@@ -593,18 +593,8 @@ export const getters = {
|
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
const label = typeObj.labelKey ? rootGetters['i18n/t'](typeObj.labelKey) || typeObj.label : typeObj.label;
|
|
596
|
-
const virtual = !!typeObj.virtual;
|
|
597
|
-
let icon = typeObj.icon;
|
|
598
596
|
|
|
599
|
-
|
|
600
|
-
if ( namespaced ) {
|
|
601
|
-
icon = 'folder';
|
|
602
|
-
} else {
|
|
603
|
-
icon = 'globe';
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const labelDisplay = highlightLabel(label, icon, typeObj.count, typeObj.schema);
|
|
597
|
+
const labelDisplay = highlightLabel(label, typeObj.count, typeObj.schema);
|
|
608
598
|
|
|
609
599
|
if ( !labelDisplay ) {
|
|
610
600
|
// Search happens in highlight and returns null if not found
|
|
@@ -711,7 +701,7 @@ export const getters = {
|
|
|
711
701
|
return group;
|
|
712
702
|
}
|
|
713
703
|
|
|
714
|
-
function highlightLabel(original,
|
|
704
|
+
function highlightLabel(original, count, schema) {
|
|
715
705
|
let label = escapeHtml(original);
|
|
716
706
|
|
|
717
707
|
if ( searchRegex ) {
|
|
@@ -735,10 +725,6 @@ export const getters = {
|
|
|
735
725
|
}
|
|
736
726
|
}
|
|
737
727
|
|
|
738
|
-
if ( icon ) {
|
|
739
|
-
label = `<i class="icon icon-fw icon-${ icon }"></i>${ label }`;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
728
|
return label;
|
|
743
729
|
}
|
|
744
730
|
};
|
package/types/shell/index.d.ts
CHANGED
|
@@ -2868,7 +2868,7 @@ export const _RKE1: "rke1";
|
|
|
2868
2868
|
export const _RKE2: "rke2";
|
|
2869
2869
|
export const PROVISIONER: any;
|
|
2870
2870
|
export const PSP_DEPRECATION_BANNER: any;
|
|
2871
|
-
export const MENU_MAX_CLUSTERS:
|
|
2871
|
+
export const MENU_MAX_CLUSTERS: 10;
|
|
2872
2872
|
export const SCALE_POOL_PROMPT: any;
|
|
2873
2873
|
export function state(): {
|
|
2874
2874
|
cookiesLoaded: boolean;
|
|
@@ -3605,35 +3605,35 @@ export namespace KEY {
|
|
|
3605
3605
|
}
|
|
3606
3606
|
}
|
|
3607
3607
|
|
|
3608
|
-
// @shell/utils/poller
|
|
3608
|
+
// @shell/utils/poller-sequential
|
|
3609
3609
|
|
|
3610
|
-
declare module '@shell/utils/poller' {
|
|
3611
|
-
export default class
|
|
3610
|
+
declare module '@shell/utils/poller-sequential' {
|
|
3611
|
+
export default class PollerSequential {
|
|
3612
3612
|
constructor(fn: any, pollRateMs: any, maxRetries?: number);
|
|
3613
3613
|
fn: any;
|
|
3614
3614
|
pollRateMs: any;
|
|
3615
3615
|
maxRetries: number;
|
|
3616
|
-
|
|
3616
|
+
timeoutId: any;
|
|
3617
3617
|
tryCount: number;
|
|
3618
3618
|
start(): void;
|
|
3619
3619
|
stop(): void;
|
|
3620
|
+
_poll(): void;
|
|
3620
3621
|
_intervalMethod(): Promise<void>;
|
|
3621
3622
|
}
|
|
3622
3623
|
}
|
|
3623
3624
|
|
|
3624
|
-
// @shell/utils/poller
|
|
3625
|
+
// @shell/utils/poller
|
|
3625
3626
|
|
|
3626
|
-
declare module '@shell/utils/poller
|
|
3627
|
-
export default class
|
|
3627
|
+
declare module '@shell/utils/poller' {
|
|
3628
|
+
export default class Poller {
|
|
3628
3629
|
constructor(fn: any, pollRateMs: any, maxRetries?: number);
|
|
3629
3630
|
fn: any;
|
|
3630
3631
|
pollRateMs: any;
|
|
3631
3632
|
maxRetries: number;
|
|
3632
|
-
|
|
3633
|
+
intervalId: any;
|
|
3633
3634
|
tryCount: number;
|
|
3634
3635
|
start(): void;
|
|
3635
3636
|
stop(): void;
|
|
3636
|
-
_poll(): void;
|
|
3637
3637
|
_intervalMethod(): Promise<void>;
|
|
3638
3638
|
}
|
|
3639
3639
|
}
|
|
@@ -3908,6 +3908,9 @@ export function splitObjectPath(path: any): any;
|
|
|
3908
3908
|
export function joinObjectPath(ary: any): string;
|
|
3909
3909
|
export function shortenedImage(image: any): any;
|
|
3910
3910
|
export function isIpv4(ip: any): boolean;
|
|
3911
|
+
export function sanitizeKey(k: any): any;
|
|
3912
|
+
export function sanitizeValue(v: any): any;
|
|
3913
|
+
export function sanitizeIP(v: any): any;
|
|
3911
3914
|
export namespace CHARSET {
|
|
3912
3915
|
export { num as NUMERIC };
|
|
3913
3916
|
export const NO_VOWELS: string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { sortBy } from '@shell/utils/sort';
|
|
2
|
+
|
|
3
|
+
describe('fx: sort', () => {
|
|
4
|
+
describe('sortBy', () => {
|
|
5
|
+
const testSortBy = <T = object[]>(ary: T[], key: string[], expected: T[], desc?: boolean) => {
|
|
6
|
+
const result = sortBy(ary, key, desc);
|
|
7
|
+
|
|
8
|
+
expect(result).toStrictEqual(expected);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
it.each([
|
|
12
|
+
[[{ a: 1 }, { a: 9 }], ['a'], [{ a: 1 }, { a: 9 }]],
|
|
13
|
+
[[{ a: 2 }, { a: 1 }], ['a'], [{ a: 1 }, { a: 2 }]],
|
|
14
|
+
])('should sort by single property', (ary, key, expected) => {
|
|
15
|
+
testSortBy(ary, key, expected);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it.each([
|
|
19
|
+
[[{ a: 1, b: 1 }, { a: 9, b: 9 }], ['a', 'b'], [{ a: 1, b: 1 }, { a: 9, b: 9 }]],
|
|
20
|
+
[[{ a: 2, b: 2 }, { a: 1, b: 1 }], ['a', 'b'], [{ a: 1, b: 1 }, { a: 2, b: 2 }]],
|
|
21
|
+
[[{ a: 2, b: 1 }, { a: 1, b: 9 }], ['a', 'b'], [{ a: 1, b: 9 }, { a: 2, b: 1 }]],
|
|
22
|
+
[[{ a: 1, b: 2 }, { a: 9, b: 1 }], ['a', 'b'], [{ a: 1, b: 2 }, { a: 9, b: 1 }]],
|
|
23
|
+
[[{ a: 1, b: 1 }, { a: 9, b: 9 }], ['a', 'b'], [{ a: 1, b: 1 }, { a: 9, b: 9 }]],
|
|
24
|
+
])('should sort by two properties (primary property always first)', (ary, key, expected) => {
|
|
25
|
+
testSortBy(ary, key, expected);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it.each([
|
|
29
|
+
[[{ a: 1, b: 1 }, { a: 1, b: 9 }], ['a', 'b'], [{ a: 1, b: 1 }, { a: 1, b: 9 }]],
|
|
30
|
+
[[{ a: 1, b: 2 }, { a: 1, b: 1 }], ['a', 'b'], [{ a: 1, b: 1 }, { a: 1, b: 2 }]],
|
|
31
|
+
])('should sort by two properties (primary property the same)', (ary, key, expected) => {
|
|
32
|
+
testSortBy(ary, key, expected);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('descending', () => {
|
|
36
|
+
it.each([
|
|
37
|
+
[[{ a: 1 }, { a: 9 }], ['a'], [{ a: 9 }, { a: 1 }]],
|
|
38
|
+
[[{ a: 2 }, { a: 1 }], ['a'], [{ a: 2 }, { a: 1 }]],
|
|
39
|
+
])('should sort by single property', (ary, key, expected) => {
|
|
40
|
+
testSortBy(ary, key, expected, true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it.each([
|
|
44
|
+
[[{ a: 1, b: 1 }, { a: 9, b: 9 }], ['a', 'b'], [{ a: 9, b: 9 }, { a: 1, b: 1 }]],
|
|
45
|
+
[[{ a: 2, b: 2 }, { a: 1, b: 1 }], ['a', 'b'], [{ a: 2, b: 2 }, { a: 1, b: 1 }]],
|
|
46
|
+
[[{ a: 2, b: 1 }, { a: 1, b: 9 }], ['a', 'b'], [{ a: 2, b: 1 }, { a: 1, b: 9 }]],
|
|
47
|
+
[[{ a: 1, b: 2 }, { a: 9, b: 1 }], ['a', 'b'], [{ a: 9, b: 1 }, { a: 1, b: 2 }]],
|
|
48
|
+
[[{ a: 1, b: 1 }, { a: 9, b: 9 }], ['a', 'b'], [{ a: 9, b: 9 }, { a: 1, b: 1 }]],
|
|
49
|
+
])('should sort by two properties', (ary, key, expected) => {
|
|
50
|
+
testSortBy(ary, key, expected, true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it.each([
|
|
54
|
+
[[{ a: 1, b: 1 }, { a: 1, b: 9 }], ['a', 'b'], [{ a: 1, b: 9 }, { a: 1, b: 1 }]],
|
|
55
|
+
[[{ a: 1, b: 2 }, { a: 1, b: 1 }], ['a', 'b'], [{ a: 1, b: 2 }, { a: 1, b: 1 }]],
|
|
56
|
+
])('should sort by two properties (primary property the same)', (ary, key, expected) => {
|
|
57
|
+
testSortBy(ary, key, expected, true);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
package/utils/string.js
CHANGED
|
@@ -309,3 +309,15 @@ export function isIpv4(ip) {
|
|
|
309
309
|
|
|
310
310
|
return reg.test(ip);
|
|
311
311
|
}
|
|
312
|
+
|
|
313
|
+
export function sanitizeKey(k) {
|
|
314
|
+
return (k || '').replace(/[^a-z0-9./_-]/ig, '');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function sanitizeValue(v) {
|
|
318
|
+
return (v || '').replace(/[^a-z0-9._-]/ig, '');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function sanitizeIP(v) {
|
|
322
|
+
return (v || '').replace(/[^a-z0-9.:_-]/ig, '');
|
|
323
|
+
}
|
package/vue.config.js
CHANGED
|
@@ -250,10 +250,6 @@ module.exports = function(dir, _appConfig) {
|
|
|
250
250
|
console.log(`Version: ${ dashboardVersion }`); // eslint-disable-line no-console
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
if ( !dev ) {
|
|
254
|
-
console.log(`Version: ${ dashboardVersion }`); // eslint-disable-line no-console
|
|
255
|
-
}
|
|
256
|
-
|
|
257
253
|
if ( resourceBase ) {
|
|
258
254
|
console.log(`Resource Base URL: ${ resourceBase }`); // eslint-disable-line no-console
|
|
259
255
|
}
|
|
@@ -410,6 +406,7 @@ module.exports = function(dir, _appConfig) {
|
|
|
410
406
|
rancherEnv,
|
|
411
407
|
dashboardVersion
|
|
412
408
|
}),
|
|
409
|
+
|
|
413
410
|
}));
|
|
414
411
|
|
|
415
412
|
// The static assets need to be in the built assets directory in order to get served (primarily the favicon)
|