@rancher/shell 0.3.26 → 0.3.28
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 -23
- package/assets/translations/zh-hans.yaml +2 -26
- package/chart/gatekeeper.vue +2 -11
- package/chart/istio.vue +1 -10
- package/chart/logging/index.vue +2 -11
- package/chart/monitoring/index.vue +1 -9
- package/chart/rancher-backup/index.vue +1 -9
- package/components/AlertTable.vue +8 -6
- package/components/Carousel.vue +2 -1
- 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/formatter/ClusterProvider.vue +1 -18
- package/components/nav/Header.vue +5 -4
- package/components/nav/TopLevelMenu.vue +38 -15
- package/components/nav/WindowManager/ContainerLogs.vue +22 -19
- 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/product/manager.js +0 -13
- package/config/settings.ts +0 -2
- package/config/types.js +0 -4
- package/core/types.ts +11 -4
- package/edit/management.cattle.io.project.vue +1 -52
- package/edit/management.cattle.io.setting.vue +31 -2
- package/edit/provisioning.cattle.io.cluster/Basics.vue +19 -107
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -3
- package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -128
- package/edit/workload/mixins/workload.js +14 -4
- package/middleware/authenticated.js +4 -2
- package/models/__tests__/management.cattle.io.cluster.test.ts +19 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +90 -0
- package/models/cluster.x-k8s.io.machine.js +1 -1
- package/models/fleet.cattle.io.cluster.js +11 -1
- package/models/management.cattle.io.cluster.js +4 -0
- package/models/management.cattle.io.project.js +0 -36
- package/models/management.cattle.io.setting.js +11 -7
- package/models/provisioning.cattle.io.cluster.js +16 -4
- package/package.json +1 -1
- package/pages/auth/setup.vue +38 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
- package/pages/c/_cluster/apps/charts/index.vue +0 -15
- package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/auth/roles/index.vue +11 -1
- package/pages/c/_cluster/explorer/index.vue +7 -49
- package/pages/c/_cluster/manager/pages/_page.vue +4 -5
- 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/BadgeState/BadgeState.vue +1 -5
- package/rancher-components/Banner/Banner.test.ts +1 -51
- package/rancher-components/Banner/Banner.vue +53 -134
- package/rancher-components/Card/Card.vue +7 -24
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
- package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
- package/rancher-components/Form/Radio/RadioButton.vue +13 -30
- package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
- package/rancher-components/StringList/StringList.test.ts +49 -453
- package/rancher-components/StringList/StringList.vue +58 -92
- package/rancher-components/components/Form/Radio/RadioGroup.test.ts +30 -0
- package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -0
- package/rancher-components/components/StringList/StringList.test.ts +270 -0
- package/rancher-components/components/StringList/StringList.vue +57 -18
- package/store/features.js +1 -0
- package/store/prefs.js +0 -3
- package/types/shell/index.d.ts +26 -17
- package/utils/__tests__/object.test.ts +67 -1
- package/utils/__tests__/version.test.ts +13 -23
- package/utils/cluster.js +1 -1
- package/utils/custom-validators.js +0 -2
- package/utils/error.js +16 -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/__tests__/index.test.ts +49 -4
- package/utils/validators/formRules/index.ts +13 -10
- package/utils/validators/role-template.js +1 -1
- package/utils/validators/setting.js +6 -10
- package/utils/version.js +0 -13
- package/components/ChartPsp.vue +0 -76
- package/components/__tests__/ChartPsp.test.ts +0 -75
- package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
- package/rancher-components/Card/Card.test.ts +0 -37
- package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
- package/yarn-error.log +0 -200
|
@@ -30,10 +30,10 @@ const CLASS = {
|
|
|
30
30
|
* Manage a list of strings
|
|
31
31
|
*/
|
|
32
32
|
export default Vue.extend({
|
|
33
|
-
|
|
34
|
-
name: 'StringList',
|
|
35
33
|
components: { LabeledInput },
|
|
36
34
|
|
|
35
|
+
name: 'StringList',
|
|
36
|
+
|
|
37
37
|
props: {
|
|
38
38
|
/**
|
|
39
39
|
* The items source
|
|
@@ -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
|
+
isEditItem: 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(
|
|
104
|
-
.map(
|
|
103
|
+
.filter(f => !!(this.errors)[f])
|
|
104
|
+
.map(k => this.errorMessages[k]);
|
|
105
105
|
},
|
|
106
106
|
},
|
|
107
107
|
|
|
@@ -113,35 +113,23 @@ 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
|
-
}
|
|
125
116
|
},
|
|
126
117
|
|
|
127
118
|
methods: {
|
|
128
119
|
onChange(value: string) {
|
|
129
120
|
this.value = value;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.value
|
|
134
|
-
];
|
|
135
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Remove duplicate error when a new value is typed
|
|
123
|
+
*/
|
|
136
124
|
this.toggleError(
|
|
137
125
|
'duplicate',
|
|
138
|
-
|
|
139
|
-
this.isCreateItem ? INPUT.create : INPUT.edit
|
|
126
|
+
false,
|
|
127
|
+
this.isCreateItem ? INPUT.create : INPUT.edit,
|
|
140
128
|
);
|
|
141
129
|
},
|
|
142
130
|
|
|
143
131
|
onSelect(item: string) {
|
|
144
|
-
if (this.
|
|
132
|
+
if (this.isCreateItem || this.isEditItem === item) {
|
|
145
133
|
return;
|
|
146
134
|
}
|
|
147
135
|
this.selected = item;
|
|
@@ -172,7 +160,7 @@ export default Vue.extend({
|
|
|
172
160
|
},
|
|
173
161
|
|
|
174
162
|
onClickEmptyBody() {
|
|
175
|
-
if (!this.isCreateItem && !this.
|
|
163
|
+
if (!this.isCreateItem && !this.isEditItem) {
|
|
176
164
|
this.toggleCreateMode(true);
|
|
177
165
|
}
|
|
178
166
|
},
|
|
@@ -188,43 +176,38 @@ export default Vue.extend({
|
|
|
188
176
|
|
|
189
177
|
return;
|
|
190
178
|
}
|
|
191
|
-
if (this.
|
|
192
|
-
this.deleteAndSelectNext(this.editedItem);
|
|
179
|
+
if (this.isEditItem) {
|
|
193
180
|
this.toggleEditMode(false);
|
|
194
181
|
|
|
195
182
|
return;
|
|
196
183
|
}
|
|
197
184
|
if (this.selected) {
|
|
198
|
-
this.
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
deleteAndSelectNext(currItem: string) {
|
|
203
|
-
const index = findStringIndex(this.items, currItem, false);
|
|
185
|
+
const index = findStringIndex(this.items, this.selected, false);
|
|
204
186
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
187
|
+
if (index !== -1) {
|
|
188
|
+
/**
|
|
189
|
+
* Select the next item in the list when an item is to be deleted.
|
|
190
|
+
*/
|
|
191
|
+
const item = (this.items[index + 1] || this.items[index - 1]);
|
|
210
192
|
|
|
211
|
-
|
|
212
|
-
|
|
193
|
+
this.onSelect(item);
|
|
194
|
+
this.setFocus(item);
|
|
213
195
|
|
|
214
|
-
|
|
196
|
+
this.deleteItem(this.items[index]);
|
|
197
|
+
}
|
|
215
198
|
}
|
|
216
199
|
},
|
|
217
200
|
|
|
218
201
|
setFocus(refId: string) {
|
|
219
|
-
this.$nextTick(() =>
|
|
202
|
+
this.$nextTick(() => this.getElemByRef(refId)?.focus());
|
|
220
203
|
},
|
|
221
204
|
|
|
222
205
|
/**
|
|
223
206
|
* Move scrollbar when the selected item is over the top or bottom side of the box
|
|
224
207
|
*/
|
|
225
208
|
moveScrollbar(arrow: Arrow, value?: number) {
|
|
226
|
-
const box = this.getElemByRef(BOX)
|
|
227
|
-
const item = this.getElemByRef(this.selected || '')
|
|
209
|
+
const box = this.getElemByRef(BOX);
|
|
210
|
+
const item = this.getElemByRef(this.selected || '');
|
|
228
211
|
|
|
229
212
|
if (box && item && item.className.includes(CLASS.item)) {
|
|
230
213
|
const boxRect = box.getClientRects()[0];
|
|
@@ -246,14 +229,13 @@ export default Vue.extend({
|
|
|
246
229
|
*/
|
|
247
230
|
toggleError(type: Error, val: boolean, refId?: string) {
|
|
248
231
|
this.errors[type] = val;
|
|
249
|
-
|
|
250
232
|
if (refId) {
|
|
251
233
|
this.toggleErrorClass(refId, val);
|
|
252
234
|
}
|
|
253
235
|
},
|
|
254
236
|
|
|
255
237
|
toggleErrorClass(refId: string, val: boolean) {
|
|
256
|
-
const input =
|
|
238
|
+
const input = this.getElemByRef(refId)?.$el;
|
|
257
239
|
|
|
258
240
|
if (input) {
|
|
259
241
|
if (val) {
|
|
@@ -268,11 +250,7 @@ export default Vue.extend({
|
|
|
268
250
|
* Show/Hide the input line to create new item
|
|
269
251
|
*/
|
|
270
252
|
toggleCreateMode(show: boolean) {
|
|
271
|
-
if (this.readonly) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
253
|
if (show) {
|
|
275
|
-
this.toggleEditMode(false);
|
|
276
254
|
this.value = '';
|
|
277
255
|
|
|
278
256
|
this.isCreateItem = true;
|
|
@@ -290,34 +268,31 @@ export default Vue.extend({
|
|
|
290
268
|
* Show/Hide the in-line editing to edit an existing item
|
|
291
269
|
*/
|
|
292
270
|
toggleEditMode(show: boolean, item?: string) {
|
|
293
|
-
if (this.readonly) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
271
|
if (show) {
|
|
297
272
|
this.toggleCreateMode(false);
|
|
298
|
-
this.value = this.
|
|
273
|
+
this.value = this.isEditItem;
|
|
299
274
|
|
|
300
|
-
this.
|
|
275
|
+
this.isEditItem = item || '';
|
|
301
276
|
this.setFocus(INPUT.edit);
|
|
302
277
|
} else {
|
|
303
278
|
this.value = null;
|
|
304
279
|
this.toggleError('duplicate', false);
|
|
305
280
|
this.onSelectLeave();
|
|
306
281
|
|
|
307
|
-
this.
|
|
282
|
+
this.isEditItem = null;
|
|
308
283
|
}
|
|
309
284
|
},
|
|
310
285
|
|
|
311
286
|
getElemByRef(id: string) {
|
|
312
287
|
const ref = this.$refs[id];
|
|
313
288
|
|
|
314
|
-
return Array.isArray(ref) ? ref[0] : ref;
|
|
289
|
+
return (Array.isArray(ref) ? ref[0] : ref) as any;
|
|
315
290
|
},
|
|
316
291
|
|
|
317
292
|
/**
|
|
318
293
|
* Create a new item and insert in the items list
|
|
319
294
|
*/
|
|
320
|
-
saveItem(
|
|
295
|
+
saveItem() {
|
|
321
296
|
const value = this.value?.trim();
|
|
322
297
|
|
|
323
298
|
if (value) {
|
|
@@ -326,20 +301,21 @@ export default Vue.extend({
|
|
|
326
301
|
value,
|
|
327
302
|
];
|
|
328
303
|
|
|
329
|
-
if (
|
|
330
|
-
this.
|
|
304
|
+
if (hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
305
|
+
this.toggleError('duplicate', true, INPUT.create);
|
|
306
|
+
|
|
307
|
+
return;
|
|
331
308
|
}
|
|
332
|
-
}
|
|
333
309
|
|
|
334
|
-
|
|
335
|
-
this.toggleCreateMode(false);
|
|
310
|
+
this.updateItems(items);
|
|
336
311
|
}
|
|
312
|
+
this.toggleCreateMode(false);
|
|
337
313
|
},
|
|
338
314
|
|
|
339
315
|
/**
|
|
340
316
|
* Update an existing item in the items list
|
|
341
317
|
*/
|
|
342
|
-
updateItem(item: string
|
|
318
|
+
updateItem(item: string) {
|
|
343
319
|
const value = this.value?.trim();
|
|
344
320
|
|
|
345
321
|
if (value) {
|
|
@@ -350,21 +326,22 @@ export default Vue.extend({
|
|
|
350
326
|
items[index] = value;
|
|
351
327
|
}
|
|
352
328
|
|
|
353
|
-
if (
|
|
354
|
-
this.
|
|
329
|
+
if (hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
330
|
+
this.toggleError('duplicate', true, INPUT.edit);
|
|
331
|
+
|
|
332
|
+
return;
|
|
355
333
|
}
|
|
356
|
-
}
|
|
357
334
|
|
|
358
|
-
|
|
359
|
-
this.toggleEditMode(false);
|
|
335
|
+
this.updateItems(items);
|
|
360
336
|
}
|
|
337
|
+
this.toggleEditMode(false);
|
|
361
338
|
},
|
|
362
339
|
|
|
363
340
|
/**
|
|
364
341
|
* Remove an item from items list
|
|
365
342
|
*/
|
|
366
343
|
deleteItem(item?: string) {
|
|
367
|
-
const items = this.items.filter(
|
|
344
|
+
const items = this.items.filter(f => f !== item);
|
|
368
345
|
|
|
369
346
|
this.updateItems(items);
|
|
370
347
|
},
|
|
@@ -410,20 +387,19 @@ export default Vue.extend({
|
|
|
410
387
|
@blur="onSelectLeave(item)"
|
|
411
388
|
>
|
|
412
389
|
<span
|
|
413
|
-
v-if="!
|
|
390
|
+
v-if="!isEditItem || isEditItem !== item"
|
|
414
391
|
class="label static"
|
|
415
392
|
>
|
|
416
393
|
{{ item }}
|
|
417
394
|
</span>
|
|
418
395
|
<LabeledInput
|
|
419
|
-
v-if="
|
|
396
|
+
v-if="isEditItem && isEditItem === item"
|
|
420
397
|
ref="item-edit"
|
|
421
|
-
:data-testid="`item-edit-${item}`"
|
|
422
398
|
class="edit-input static"
|
|
423
399
|
:value="value != null ? value : item"
|
|
424
400
|
@input="onChange($event)"
|
|
425
|
-
@blur.prevent="
|
|
426
|
-
@keydown.native.enter="updateItem(item
|
|
401
|
+
@blur.prevent="toggleEditMode(false)"
|
|
402
|
+
@keydown.native.enter="updateItem(item)"
|
|
427
403
|
/>
|
|
428
404
|
</div>
|
|
429
405
|
<div
|
|
@@ -432,14 +408,12 @@ export default Vue.extend({
|
|
|
432
408
|
>
|
|
433
409
|
<LabeledInput
|
|
434
410
|
ref="item-create"
|
|
435
|
-
data-testid="item-create"
|
|
436
411
|
class="create-input static"
|
|
437
412
|
type="text"
|
|
438
413
|
:value="value"
|
|
439
414
|
:placeholder="placeholder"
|
|
440
415
|
@input="onChange($event)"
|
|
441
|
-
@
|
|
442
|
-
@keydown.native.enter="saveItem(!errors.duplicate)"
|
|
416
|
+
@keydown.native.enter="saveItem"
|
|
443
417
|
/>
|
|
444
418
|
</div>
|
|
445
419
|
</div>
|
|
@@ -453,32 +427,25 @@ export default Vue.extend({
|
|
|
453
427
|
class="action-buttons"
|
|
454
428
|
>
|
|
455
429
|
<button
|
|
456
|
-
data-testid="button-remove"
|
|
457
430
|
class="btn btn-sm role-tertiary remove-button"
|
|
458
|
-
:disabled="!selected && !isCreateItem && !
|
|
431
|
+
:disabled="!selected && !isCreateItem && !isEditItem"
|
|
459
432
|
@mousedown.prevent="onClickMinusButton"
|
|
460
433
|
>
|
|
461
434
|
<span class="icon icon-minus icon-sm" />
|
|
462
435
|
</button>
|
|
463
436
|
<button
|
|
464
|
-
data-testid="button-add"
|
|
465
437
|
class="btn btn-sm role-tertiary add-button"
|
|
466
|
-
:disabled="isCreateItem
|
|
438
|
+
:disabled="isCreateItem"
|
|
467
439
|
@click.prevent="onClickPlusButton"
|
|
468
440
|
>
|
|
469
441
|
<span class="icon icon-plus icon-sm" />
|
|
470
442
|
</button>
|
|
471
443
|
</div>
|
|
472
444
|
<div class="messages">
|
|
473
|
-
<i
|
|
474
|
-
v-if="errorMessagesArray.length > 0"
|
|
475
|
-
data-testid="i-warning-icon"
|
|
476
|
-
class="icon icon-warning icon-lg"
|
|
477
|
-
/>
|
|
445
|
+
<i v-if="errorMessagesArray.length > 0" class="icon icon-warning icon-lg" />
|
|
478
446
|
<span
|
|
479
447
|
v-for="(msg, idx) in errorMessagesArray"
|
|
480
448
|
:key="idx"
|
|
481
|
-
:data-testid="`span-error-message-${msg}`"
|
|
482
449
|
class="error"
|
|
483
450
|
>
|
|
484
451
|
{{ idx > 0 ? '; ' : '' }}
|
|
@@ -532,7 +499,6 @@ export default Vue.extend({
|
|
|
532
499
|
width: auto;
|
|
533
500
|
user-select: none;
|
|
534
501
|
overflow: hidden;
|
|
535
|
-
white-space: no-wrap;
|
|
536
502
|
text-overflow: ellipsis;
|
|
537
503
|
padding-top: 1px;
|
|
538
504
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { RadioGroup } from './index';
|
|
3
|
+
|
|
4
|
+
describe('component: RadioGroup', () => {
|
|
5
|
+
describe('when disabled', () => {
|
|
6
|
+
it.each([true, false])('should expose disabled slot prop for indexed slots for %p', (disabled) => {
|
|
7
|
+
const wrapper = mount(RadioGroup, {
|
|
8
|
+
propsData: {
|
|
9
|
+
name: 'whatever',
|
|
10
|
+
options: [{ label: 'whatever', value: 'whatever' }],
|
|
11
|
+
disabled
|
|
12
|
+
},
|
|
13
|
+
scopedSlots: {
|
|
14
|
+
0(props: {isDisabled: boolean}) {
|
|
15
|
+
return this.$createElement('input', {
|
|
16
|
+
attrs: {
|
|
17
|
+
id: 'test',
|
|
18
|
+
disabled: props.isDisabled
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const slot = wrapper.find('#test').element as HTMLInputElement;
|
|
26
|
+
|
|
27
|
+
expect(slot.disabled).toBe(disabled);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -169,6 +169,7 @@ export default Vue.extend({
|
|
|
169
169
|
|
|
170
170
|
<template>
|
|
171
171
|
<div>
|
|
172
|
+
<!-- Label -->
|
|
172
173
|
<div
|
|
173
174
|
v-if="label || labelKey || tooltip || tooltipKey || $slots.label"
|
|
174
175
|
class="radio-group label"
|
|
@@ -195,6 +196,8 @@ export default Vue.extend({
|
|
|
195
196
|
</h3>
|
|
196
197
|
</slot>
|
|
197
198
|
</div>
|
|
199
|
+
|
|
200
|
+
<!-- Group -->
|
|
198
201
|
<div
|
|
199
202
|
class="radio-group"
|
|
200
203
|
:class="{'row':row}"
|
|
@@ -212,6 +215,7 @@ export default Vue.extend({
|
|
|
212
215
|
:is-disabled="isDisabled"
|
|
213
216
|
:name="i"
|
|
214
217
|
>
|
|
218
|
+
<!-- Default input -->
|
|
215
219
|
<RadioButton
|
|
216
220
|
:key="name+'-'+i"
|
|
217
221
|
:name="name"
|
|
@@ -398,6 +398,276 @@ describe('stringList.vue', () => {
|
|
|
398
398
|
});
|
|
399
399
|
});
|
|
400
400
|
|
|
401
|
+
describe('bulk delimiter', () => {
|
|
402
|
+
const delimiter = /;/;
|
|
403
|
+
|
|
404
|
+
describe('add', () => {
|
|
405
|
+
const items: string[] = [];
|
|
406
|
+
|
|
407
|
+
beforeEach(() => {
|
|
408
|
+
wrapper = mount(StringList, {
|
|
409
|
+
propsData: {
|
|
410
|
+
items,
|
|
411
|
+
bulkAdditionDelimiter: delimiter,
|
|
412
|
+
errorMessages: { duplicate: 'error, item is duplicate.' },
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('should split values if delimiter set', async() => {
|
|
418
|
+
const value = 'test;test1;test2';
|
|
419
|
+
const result = ['test', 'test1', 'test2'];
|
|
420
|
+
|
|
421
|
+
// activate create mode
|
|
422
|
+
await wrapper.setData({ isCreateItem: true });
|
|
423
|
+
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
424
|
+
|
|
425
|
+
await inputField.setValue(value);
|
|
426
|
+
|
|
427
|
+
// press enter
|
|
428
|
+
await inputField.trigger('keydown.enter');
|
|
429
|
+
await wrapper.vm.$nextTick();
|
|
430
|
+
|
|
431
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
432
|
+
|
|
433
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should show warning if one of the values is a duplicate', async() => {
|
|
437
|
+
const value = 'test;test1;test2';
|
|
438
|
+
|
|
439
|
+
await wrapper.setProps({ items: ['test1'] });
|
|
440
|
+
|
|
441
|
+
// activate create mode
|
|
442
|
+
await wrapper.setData({ isCreateItem: true });
|
|
443
|
+
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
444
|
+
|
|
445
|
+
await inputField.setValue(value);
|
|
446
|
+
|
|
447
|
+
// press enter
|
|
448
|
+
await inputField.trigger('keydown.enter');
|
|
449
|
+
await wrapper.vm.$nextTick();
|
|
450
|
+
|
|
451
|
+
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
|
452
|
+
|
|
453
|
+
expect(isDuplicate).toBe(true);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should show a warning if the new values are all duplicates', async() => {
|
|
457
|
+
const value = 'test;test';
|
|
458
|
+
|
|
459
|
+
// activate create mode
|
|
460
|
+
await wrapper.setData({ isCreateItem: true });
|
|
461
|
+
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
462
|
+
|
|
463
|
+
await inputField.setValue(value);
|
|
464
|
+
|
|
465
|
+
// press enter
|
|
466
|
+
await inputField.trigger('keydown.enter');
|
|
467
|
+
await wrapper.vm.$nextTick();
|
|
468
|
+
|
|
469
|
+
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
|
470
|
+
|
|
471
|
+
expect(isDuplicate).toBe(true);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('should not consider empty strings at the beginning', async() => {
|
|
475
|
+
const value = ';test;test1;test2';
|
|
476
|
+
const result = ['test', 'test1', 'test2'];
|
|
477
|
+
|
|
478
|
+
// activate create mode
|
|
479
|
+
await wrapper.setData({ isCreateItem: true });
|
|
480
|
+
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
481
|
+
|
|
482
|
+
await inputField.setValue(value);
|
|
483
|
+
|
|
484
|
+
// press enter
|
|
485
|
+
await inputField.trigger('keydown.enter');
|
|
486
|
+
await wrapper.vm.$nextTick();
|
|
487
|
+
|
|
488
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
489
|
+
|
|
490
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('should not consider empty strings in the middle', async() => {
|
|
494
|
+
const value = 'test;test1;;test2';
|
|
495
|
+
const result = ['test', 'test1', 'test2'];
|
|
496
|
+
|
|
497
|
+
// activate create mode
|
|
498
|
+
await wrapper.setData({ isCreateItem: true });
|
|
499
|
+
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
500
|
+
|
|
501
|
+
await inputField.setValue(value);
|
|
502
|
+
|
|
503
|
+
// press enter
|
|
504
|
+
await inputField.trigger('keydown.enter');
|
|
505
|
+
await wrapper.vm.$nextTick();
|
|
506
|
+
|
|
507
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
508
|
+
|
|
509
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('should not consider empty strings at the end', async() => {
|
|
513
|
+
const value = 'test;test1;test2;';
|
|
514
|
+
const result = ['test', 'test1', 'test2'];
|
|
515
|
+
|
|
516
|
+
// activate create mode
|
|
517
|
+
await wrapper.setData({ isCreateItem: true });
|
|
518
|
+
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
519
|
+
|
|
520
|
+
await inputField.setValue(value);
|
|
521
|
+
|
|
522
|
+
// press enter
|
|
523
|
+
await inputField.trigger('keydown.enter');
|
|
524
|
+
await wrapper.vm.$nextTick();
|
|
525
|
+
|
|
526
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
527
|
+
|
|
528
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
describe('edit', () => {
|
|
533
|
+
const items = ['test1', 'test2'];
|
|
534
|
+
|
|
535
|
+
beforeEach(() => {
|
|
536
|
+
wrapper = mount(StringList, {
|
|
537
|
+
propsData: {
|
|
538
|
+
items,
|
|
539
|
+
bulkAdditionDelimiter: delimiter,
|
|
540
|
+
errorMessages: { duplicate: 'error, item is duplicate.' },
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('should split values if delimiter set', async() => {
|
|
546
|
+
const newValue = 'test1;new;values';
|
|
547
|
+
const result = ['test1', 'new', 'values', 'test2'];
|
|
548
|
+
|
|
549
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
550
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
551
|
+
|
|
552
|
+
await inputField.setValue(newValue);
|
|
553
|
+
|
|
554
|
+
// press enter
|
|
555
|
+
await inputField.trigger('keydown.enter');
|
|
556
|
+
await wrapper.vm.$nextTick();
|
|
557
|
+
|
|
558
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
559
|
+
|
|
560
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('should show warning if one of the values is a duplicate', async() => {
|
|
564
|
+
const newValue = 'test1;test2';
|
|
565
|
+
|
|
566
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
567
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
568
|
+
|
|
569
|
+
await inputField.setValue(newValue);
|
|
570
|
+
|
|
571
|
+
// press enter
|
|
572
|
+
await inputField.trigger('keydown.enter');
|
|
573
|
+
await wrapper.vm.$nextTick();
|
|
574
|
+
|
|
575
|
+
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
|
576
|
+
|
|
577
|
+
expect(isDuplicate).toBe(true);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('should show a warning if the new values are all duplicates', async() => {
|
|
581
|
+
const newValue = 'test;test';
|
|
582
|
+
|
|
583
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
584
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
585
|
+
|
|
586
|
+
await inputField.setValue(newValue);
|
|
587
|
+
|
|
588
|
+
// press enter
|
|
589
|
+
await inputField.trigger('keydown.enter');
|
|
590
|
+
await wrapper.vm.$nextTick();
|
|
591
|
+
|
|
592
|
+
const isDuplicate = (wrapper.emitted('errors') || [])[0][0].duplicate;
|
|
593
|
+
|
|
594
|
+
expect(isDuplicate).toBe(true);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('should not consider empty strings at the beginning', async() => {
|
|
598
|
+
const newValue = ';test1;new;value';
|
|
599
|
+
const result = ['test1', 'new', 'value', 'test2'];
|
|
600
|
+
|
|
601
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
602
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
603
|
+
|
|
604
|
+
await inputField.setValue(newValue);
|
|
605
|
+
|
|
606
|
+
// press enter
|
|
607
|
+
await inputField.trigger('keydown.enter');
|
|
608
|
+
await wrapper.vm.$nextTick();
|
|
609
|
+
|
|
610
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
611
|
+
|
|
612
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it('should not consider empty strings in the middle 1', async() => {
|
|
616
|
+
const newValue = 'test1; ;new;value';
|
|
617
|
+
const result = ['test1', 'new', 'value', 'test2'];
|
|
618
|
+
|
|
619
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
620
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
621
|
+
|
|
622
|
+
await inputField.setValue(newValue);
|
|
623
|
+
|
|
624
|
+
// press enter
|
|
625
|
+
await inputField.trigger('keydown.enter');
|
|
626
|
+
await wrapper.vm.$nextTick();
|
|
627
|
+
|
|
628
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
629
|
+
|
|
630
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it('should not consider empty strings in the middle 2', async() => {
|
|
634
|
+
const newValue = 'test1;;new;value';
|
|
635
|
+
const result = ['test1', 'new', 'value', 'test2'];
|
|
636
|
+
|
|
637
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
638
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
639
|
+
|
|
640
|
+
await inputField.setValue(newValue);
|
|
641
|
+
|
|
642
|
+
// press enter
|
|
643
|
+
await inputField.trigger('keydown.enter');
|
|
644
|
+
await wrapper.vm.$nextTick();
|
|
645
|
+
|
|
646
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
647
|
+
|
|
648
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('should not consider empty strings at the end', async() => {
|
|
652
|
+
const newValue = 'test1;new;value;';
|
|
653
|
+
const result = ['test1', 'new', 'value', 'test2'];
|
|
654
|
+
|
|
655
|
+
await wrapper.setData({ editedItem: items[0] });
|
|
656
|
+
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
657
|
+
|
|
658
|
+
await inputField.setValue(newValue);
|
|
659
|
+
|
|
660
|
+
// press enter
|
|
661
|
+
await inputField.trigger('keydown.enter');
|
|
662
|
+
await wrapper.vm.$nextTick();
|
|
663
|
+
|
|
664
|
+
const itemsResult = (wrapper.emitted('change') || [])[0][0];
|
|
665
|
+
|
|
666
|
+
expect(JSON.stringify(itemsResult)).toBe(JSON.stringify(result));
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
|
|
401
671
|
describe('errors handling', () => {
|
|
402
672
|
it('show duplicate warning icon when errorMessages is defined', async() => {
|
|
403
673
|
const items = ['test'];
|