@rancher/shell 0.4.0 → 0.5.0
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/images/providers/ovhcloudmks.svg +122 -0
- package/assets/images/providers/ovhcloudpubliccloud.svg +122 -0
- package/assets/styles/global/_layout.scss +99 -0
- package/assets/translations/en-us.yaml +30 -5
- package/assets/translations/zh-hans.yaml +1 -1
- package/babel.config.js +7 -1
- package/chart/monitoring/alerting/index.vue +7 -21
- package/chart/monitoring/grafana/index.vue +55 -0
- package/chart/monitoring/index.vue +51 -17
- package/chart/monitoring/prometheus/index.vue +37 -43
- package/chart/rancher-backup/index.vue +2 -1
- package/cloud-credential/azure.vue +4 -17
- package/components/AsyncButton.vue +17 -5
- package/components/Certificates.vue +164 -0
- package/components/CodeMirror.vue +19 -21
- package/components/CruResource.vue +1 -0
- package/components/DraggableZone.vue +2 -2
- package/components/EtcdInfoBanner.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +25 -1
- package/components/IconOrSvg.vue +1 -1
- package/components/LandingPagePreference.vue +1 -4
- package/components/PodSecurityAdmission.vue +2 -2
- package/components/Questions/index.vue +1 -1
- package/components/ResourceDetail/Masthead.vue +16 -3
- package/components/ResourceTable.vue +14 -2
- package/components/ResourceYaml.vue +5 -0
- package/components/SideNav.vue +1 -1
- package/components/SingleClusterInfo.vue +1 -4
- package/components/Tabbed/index.vue +12 -0
- package/components/fleet/FleetRepos.vue +62 -27
- package/components/fleet/FleetResources.vue +6 -1
- package/components/form/ArrayListSelect.vue +10 -0
- package/components/form/Error.vue +3 -3
- package/components/form/Footer.vue +2 -2
- package/components/form/GitPicker.vue +83 -38
- package/components/form/KeyValue.vue +4 -0
- package/components/form/LabeledSelect.vue +4 -0
- package/components/formatter/Checked.vue +11 -3
- package/components/formatter/FleetClusterSummaryGraph.vue +27 -0
- package/components/formatter/FleetSummaryGraph.vue +23 -11
- package/components/formatter/LiveDuration.vue +1 -1
- package/components/formatter/PercentageBar.vue +1 -1
- package/components/formatter/__tests__/Checked.test.ts +19 -0
- package/components/nav/Group.vue +2 -2
- package/components/nav/Header.vue +0 -1
- package/components/nav/TopLevelMenu.vue +36 -6
- package/components/nav/Type.vue +1 -3
- package/components/nav/WindowManager/ContainerLogs.vue +101 -3
- package/components/nav/WindowManager/ContainerShell.vue +6 -1
- package/components/nav/WindowManager/__tests__/ContainerLogs.test.ts +186 -0
- package/components/nav/WindowManager/index.vue +11 -10
- package/components/nav/__tests__/TopLevelMenu.test.ts +33 -0
- package/components/nav/__tests__/Type.test.ts +1 -1
- package/components/nuxt/nuxt-child.js +14 -78
- package/components/nuxt/nuxt.js +1 -1
- package/{layouts → components/templates}/blank.vue +1 -1
- package/{layouts → components/templates}/default.vue +8 -98
- package/{layouts → components/templates}/error.vue +10 -19
- package/{layouts → components/templates}/home.vue +4 -1
- package/{layouts → components/templates}/plain.vue +4 -1
- package/{layouts → components/templates}/standalone.vue +1 -1
- package/{layouts → components/templates}/unauthenticated.vue +1 -1
- package/composables/useCompactInput.test.ts +36 -0
- package/composables/useCompactInput.ts +20 -0
- package/composables/useLabeledFormElement.test.ts +135 -0
- package/composables/useLabeledFormElement.ts +138 -0
- package/config/harvester-manager-types.js +2 -0
- package/config/private-label.js +22 -0
- package/config/product/explorer.js +3 -0
- package/config/product/fleet.js +6 -1
- package/config/product/manager.js +8 -2
- package/config/query-params.js +1 -0
- package/config/router.js +385 -364
- package/config/settings.ts +1 -0
- package/config/store.js +1 -1
- package/config/system-namespaces.js +3 -0
- package/config/table-headers.js +47 -0
- package/core/plugin-routes.ts +56 -114
- package/core/plugin.ts +16 -10
- package/core/plugins-loader.js +7 -9
- package/core/plugins.js +0 -3
- package/creators/app/files/.gitlab-ci.yml +1 -1
- package/detail/fleet.cattle.io.cluster.vue +11 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -3
- package/dialog/ScaleMachineDownDialog.vue +34 -17
- package/edit/__tests__/service.test.ts +89 -0
- package/edit/auth/googleoauth.vue +1 -5
- package/edit/cloudcredential.vue +2 -0
- package/edit/configmap.vue +2 -1
- package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +2 -2
- package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +1 -1
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +15 -7
- package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +112 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +473 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/{CustomCommand.tests.ts → CustomCommand.test.ts} +4 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +1 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +73 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +7 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster.ts +386 -0
- package/edit/provisioning.cattle.io.cluster/import.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/index.vue +92 -36
- package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -583
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +137 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +157 -0
- package/edit/provisioning.cattle.io.cluster/{Basics.vue → tabs/Basics.vue} +94 -19
- package/edit/provisioning.cattle.io.cluster/{MachinePool.vue → tabs/MachinePool.vue} +1 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +135 -0
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +189 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +144 -0
- package/edit/provisioning.cattle.io.cluster/tabs/upgrade/index.vue +76 -0
- package/edit/service.vue +12 -0
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.js +25 -71
- package/initialize/client.js +21 -162
- package/initialize/index.js +27 -123
- package/list/management.cattle.io.feature.vue +1 -7
- package/list/node.vue +1 -0
- package/machine-config/__tests__/vmwarevsphere.test.ts +100 -21
- package/machine-config/vmwarevsphere.vue +73 -51
- package/middleware/authenticated.js +10 -17
- package/mixins/auth-config.js +2 -7
- package/mixins/brand.js +29 -41
- package/mixins/create-edit-view/index.js +2 -2
- package/mixins/labeled-form-element.ts +6 -1
- package/models/__tests__/management.cattle.io.node.ts +85 -0
- package/models/__tests__/management.cattle.io.nodepool.ts +83 -0
- package/models/__tests__/namespace.test.ts +49 -9
- package/models/__tests__/workload.test.ts +91 -0
- package/models/cluster/node.js +4 -4
- package/models/cluster.x-k8s.io.machinedeployment.js +14 -0
- package/models/fleet.cattle.io.cluster.js +4 -0
- package/models/fleet.cattle.io.gitrepo.js +56 -13
- package/models/management.cattle.io.kontainerdriver.js +1 -1
- package/models/management.cattle.io.node.js +18 -14
- package/models/management.cattle.io.nodepool.js +17 -0
- package/models/namespace.js +1 -1
- package/models/pod.js +20 -0
- package/models/provisioning.cattle.io.cluster.js +20 -3
- package/models/secret.js +117 -18
- package/models/workload.js +16 -0
- package/models/workload.service.js +18 -0
- package/package.json +10 -9
- package/pages/about.vue +0 -1
- package/pages/account/create-key.vue +0 -1
- package/pages/account/index.vue +0 -1
- package/pages/auth/login.vue +0 -1
- package/pages/auth/logout.vue +0 -2
- package/pages/auth/setup.vue +0 -4
- package/pages/auth/verify.vue +14 -8
- package/pages/c/_cluster/apps/charts/install.vue +4 -4
- package/pages/c/_cluster/apps/index.vue +0 -2
- package/pages/c/_cluster/auth/index.vue +0 -2
- package/pages/c/_cluster/ecm/index.vue +0 -2
- package/pages/c/_cluster/explorer/index.vue +28 -2
- package/pages/c/_cluster/fleet/index.vue +1 -1
- package/pages/c/_cluster/index.vue +0 -2
- package/pages/c/_cluster/settings/banners.vue +0 -2
- package/pages/c/_cluster/settings/brand.vue +0 -2
- package/pages/c/_cluster/settings/index.vue +0 -2
- package/pages/c/_cluster/settings/links.vue +0 -1
- package/pages/c/_cluster/settings/performance.vue +0 -1
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +2 -1
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +10 -46
- package/pages/c/_cluster/uiplugins/index.vue +0 -2
- package/pages/diagnostic.vue +1 -2
- package/pages/fail-whale.vue +0 -1
- package/pages/prefs.vue +0 -1
- package/pages/support/index.vue +2 -8
- package/pkg/auto-import.js +1 -1
- package/plugins/axios.js +0 -36
- package/plugins/back-button.js +3 -5
- package/plugins/codemirror-loader.js +1 -1
- package/plugins/codemirror.js +41 -0
- package/plugins/dashboard-store/__tests__/{mutations.spec.ts → mutations.test.ts} +1 -1
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +49 -0
- package/plugins/dashboard-store/__tests__/utils/store-mocks.ts +7 -0
- package/plugins/dashboard-store/actions.js +30 -4
- package/plugins/dashboard-store/classify.js +1 -18
- package/plugins/dashboard-store/getters.js +10 -5
- package/plugins/dashboard-store/index.js +0 -12
- package/plugins/dashboard-store/mutations.js +0 -4
- package/plugins/dashboard-store/resource-class.js +59 -18
- package/plugins/steve/__tests__/steve-class.spec.ts +59 -0
- package/plugins/steve/__tests__/utils/steve-mocks.ts +31 -0
- package/plugins/steve/getters.js +4 -1
- package/plugins/steve/norman-class.js +19 -0
- package/plugins/steve/steve-class.js +22 -0
- package/plugins/steve/subscribe.js +4 -10
- package/rancher-components/Accordion/Accordion.test.ts +45 -0
- package/rancher-components/Accordion/Accordion.vue +86 -0
- package/rancher-components/Accordion/index.ts +1 -0
- package/rancher-components/BadgeState/BadgeState.vue +3 -3
- package/rancher-components/Banner/Banner.vue +2 -2
- package/rancher-components/Card/Card.vue +3 -3
- package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +65 -24
- package/rancher-components/Form/Radio/RadioButton.test.ts +7 -3
- package/rancher-components/Form/Radio/RadioButton.vue +13 -7
- package/rancher-components/Form/Radio/RadioGroup.test.ts +30 -0
- package/rancher-components/Form/Radio/RadioGroup.vue +8 -3
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +9 -4
- package/rancher-components/StringList/StringList.test.ts +270 -0
- package/rancher-components/StringList/StringList.vue +65 -26
- package/rancher-components/components/Accordion/Accordion.test.ts +45 -0
- package/rancher-components/components/Accordion/Accordion.vue +86 -0
- package/rancher-components/components/Accordion/index.ts +1 -0
- package/rancher-components/components/BadgeState/BadgeState.vue +3 -3
- package/rancher-components/components/Banner/Banner.vue +2 -2
- package/rancher-components/components/Card/Card.vue +3 -3
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +3 -3
- package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +18 -1
- package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +57 -24
- package/rancher-components/components/Form/Radio/RadioButton.vue +13 -7
- package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -3
- package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +9 -4
- package/rancher-components/components/StringList/StringList.vue +8 -8
- package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +50 -0
- package/scripts/extension/parse-tag-name +2 -2
- package/scripts/publish-shell.sh +10 -0
- package/scripts/test-plugins-build.sh +85 -9
- package/server/har-file.js +183 -0
- package/store/catalog.js +1 -1
- package/store/features.js +1 -0
- package/store/i18n.js +11 -0
- package/store/index.js +10 -11
- package/store/prefs.js +33 -35
- package/store/type-map.js +8 -7
- package/tsconfig.json +35 -9
- package/tsconfig.paths.json +21 -0
- package/types/shell/index.d.ts +427 -234
- package/types/vue-shim.d.ts +42 -0
- package/utils/__tests__/create-yaml.test.ts +60 -0
- package/utils/axios.js +0 -19
- package/utils/azure.js +24 -0
- package/utils/create-yaml.js +17 -10
- package/utils/git.ts +1 -1
- package/utils/monitoring.js +1 -1
- package/utils/nuxt.js +18 -39
- package/utils/object.js +14 -0
- package/utils/router.scrollBehavior.js +12 -14
- package/utils/time.js +1 -1
- package/utils/url.ts +1 -1
- package/vue.config.js +23 -2
- package/.DS_Store +0 -0
- package/assets/images/providers/aks-black.svg +0 -28
- package/assets/images/providers/aks.svg +0 -31
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -234
- package/initialize/layouts.ts +0 -26
- package/mixins/fetch.server.js +0 -73
- package/pages/c/index.vue +0 -9
- package/pages/rio/mesh.vue +0 -508
- package/plugins/transitions.js +0 -4
- package/scripts/.DS_Store +0 -0
- package/scripts/verdaccio.log +0 -205
- package/tsconfig.default.json +0 -46
- package/yarn-error.log +0 -200
- /package/components/form/__tests__/{NameNsDescription.ts → NameNsDescription.test.ts} +0 -0
- /package/edit/networking.k8s.io.networkpolicy/__tests__/utils/{selectors.ts → selectors.test.ts} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{AgentConfiguration.vue → tabs/AgentConfiguration.vue} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{MemberRoles.vue → tabs/MemberRoles.vue} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{S3Config.vue → tabs/etcd/S3Config.vue} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{ACE.vue → tabs/networking/ACE.vue} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{RegistryConfigs.vue → tabs/registries/RegistryConfigs.vue} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{RegistryMirrors.vue → tabs/registries/RegistryMirrors.vue} +0 -0
- /package/edit/provisioning.cattle.io.cluster/{DrainOptions.vue → tabs/upgrade/DrainOptions.vue} +0 -0
- /package/plugins/dashboard-store/__tests__/{actions.spec.ts → actions.test.ts} +0 -0
- /package/plugins/dashboard-store/__tests__/{getters.spec.ts → getters.test.ts} +0 -0
- /package/rancher-components/BadgeState/{BadgeState.spec.ts → BadgeState.test.ts} +0 -0
- /package/rancher-components/components/BadgeState/{BadgeState.spec.ts → BadgeState.test.ts} +0 -0
|
@@ -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'];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import Vue, { PropType } from 'vue';
|
|
2
|
+
import Vue, { PropType, defineComponent } from 'vue';
|
|
3
3
|
|
|
4
4
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
|
5
5
|
import { findStringIndex, hasDuplicatedStrings } from '@shell/utils/array';
|
|
@@ -29,7 +29,7 @@ const CLASS = {
|
|
|
29
29
|
/**
|
|
30
30
|
* Manage a list of strings
|
|
31
31
|
*/
|
|
32
|
-
export default
|
|
32
|
+
export default defineComponent({
|
|
33
33
|
|
|
34
34
|
name: 'StringList',
|
|
35
35
|
components: { LabeledInput },
|
|
@@ -82,12 +82,19 @@ export default Vue.extend({
|
|
|
82
82
|
return {} as ErrorMessages;
|
|
83
83
|
},
|
|
84
84
|
},
|
|
85
|
+
/**
|
|
86
|
+
* Enables bulk addition and defines the delimiter to split the input string.
|
|
87
|
+
*/
|
|
88
|
+
bulkAdditionDelimiter: {
|
|
89
|
+
type: RegExp,
|
|
90
|
+
default: null,
|
|
91
|
+
}
|
|
85
92
|
},
|
|
86
93
|
data() {
|
|
87
94
|
return {
|
|
88
|
-
value:
|
|
95
|
+
value: undefined as string | undefined,
|
|
89
96
|
selected: null as string | null,
|
|
90
|
-
editedItem:
|
|
97
|
+
editedItem: undefined as string | undefined,
|
|
91
98
|
isCreateItem: false,
|
|
92
99
|
errors: { duplicate: false } as Record<Error, boolean>
|
|
93
100
|
};
|
|
@@ -125,13 +132,9 @@ export default Vue.extend({
|
|
|
125
132
|
},
|
|
126
133
|
|
|
127
134
|
methods: {
|
|
128
|
-
onChange(value: string) {
|
|
135
|
+
onChange(value: string, index?: number) {
|
|
129
136
|
this.value = value;
|
|
130
|
-
|
|
131
|
-
const items = [
|
|
132
|
-
...this.items,
|
|
133
|
-
this.value
|
|
134
|
-
];
|
|
137
|
+
const items = this.addValueToItems(this.items, value, index);
|
|
135
138
|
|
|
136
139
|
this.toggleError(
|
|
137
140
|
'duplicate',
|
|
@@ -278,7 +281,7 @@ export default Vue.extend({
|
|
|
278
281
|
this.isCreateItem = true;
|
|
279
282
|
this.setFocus(INPUT.create);
|
|
280
283
|
} else {
|
|
281
|
-
this.value =
|
|
284
|
+
this.value = undefined;
|
|
282
285
|
this.toggleError('duplicate', false);
|
|
283
286
|
this.onSelectLeave();
|
|
284
287
|
|
|
@@ -300,11 +303,11 @@ export default Vue.extend({
|
|
|
300
303
|
this.editedItem = item || '';
|
|
301
304
|
this.setFocus(INPUT.edit);
|
|
302
305
|
} else {
|
|
303
|
-
this.value =
|
|
306
|
+
this.value = undefined;
|
|
304
307
|
this.toggleError('duplicate', false);
|
|
305
308
|
this.onSelectLeave();
|
|
306
309
|
|
|
307
|
-
this.editedItem =
|
|
310
|
+
this.editedItem = undefined;
|
|
308
311
|
}
|
|
309
312
|
},
|
|
310
313
|
|
|
@@ -321,10 +324,7 @@ export default Vue.extend({
|
|
|
321
324
|
const value = this.value?.trim();
|
|
322
325
|
|
|
323
326
|
if (value) {
|
|
324
|
-
const items =
|
|
325
|
-
...this.items,
|
|
326
|
-
value,
|
|
327
|
-
];
|
|
327
|
+
const items = this.addValueToItems(this.items, value);
|
|
328
328
|
|
|
329
329
|
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
330
330
|
this.updateItems(items);
|
|
@@ -343,12 +343,8 @@ export default Vue.extend({
|
|
|
343
343
|
const value = this.value?.trim();
|
|
344
344
|
|
|
345
345
|
if (value) {
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
if (index !== -1) {
|
|
350
|
-
items[index] = value;
|
|
351
|
-
}
|
|
346
|
+
const index = findStringIndex(this.items, item, false);
|
|
347
|
+
const items = index !== -1 ? this.addValueToItems(this.items, value, index) : this.items;
|
|
352
348
|
|
|
353
349
|
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
354
350
|
this.updateItems(items);
|
|
@@ -360,6 +356,49 @@ export default Vue.extend({
|
|
|
360
356
|
}
|
|
361
357
|
},
|
|
362
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Add a new or update an exiting item in the items list.
|
|
361
|
+
*
|
|
362
|
+
* @param items The current items list.
|
|
363
|
+
* @param value The new value to be added.
|
|
364
|
+
* @param index The list index of the item to be updated (optional).
|
|
365
|
+
* @returns Updated items list.
|
|
366
|
+
*/
|
|
367
|
+
addValueToItems(items: string[], value: string, index?: number): string[] {
|
|
368
|
+
const updatedItems = [...items];
|
|
369
|
+
|
|
370
|
+
// Add new item
|
|
371
|
+
if (index === undefined) {
|
|
372
|
+
if (this.bulkAdditionDelimiter && value.search(this.bulkAdditionDelimiter) >= 0) {
|
|
373
|
+
updatedItems.push(...this.splitBulkValue(value));
|
|
374
|
+
} else {
|
|
375
|
+
updatedItems.push(value);
|
|
376
|
+
}
|
|
377
|
+
} else { // Update existing item
|
|
378
|
+
if (this.bulkAdditionDelimiter && value.search(this.bulkAdditionDelimiter) >= 0) {
|
|
379
|
+
updatedItems.splice(index, 1, ...this.splitBulkValue(value));
|
|
380
|
+
} else {
|
|
381
|
+
updatedItems[index] = value;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return updatedItems;
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Split the value by the defined delimiter and remove empty strings.
|
|
390
|
+
*
|
|
391
|
+
* @param value The value to be split.
|
|
392
|
+
* @returns Array containing split values.
|
|
393
|
+
*/
|
|
394
|
+
splitBulkValue(value: string): string[] {
|
|
395
|
+
return value
|
|
396
|
+
.split(this.bulkAdditionDelimiter)
|
|
397
|
+
.filter((item) => {
|
|
398
|
+
return item.trim().length > 0;
|
|
399
|
+
});
|
|
400
|
+
},
|
|
401
|
+
|
|
363
402
|
/**
|
|
364
403
|
* Remove an item from items list
|
|
365
404
|
*/
|
|
@@ -393,7 +432,7 @@ export default Vue.extend({
|
|
|
393
432
|
@dblclick="onClickEmptyBody()"
|
|
394
433
|
>
|
|
395
434
|
<div
|
|
396
|
-
v-for="item in items"
|
|
435
|
+
v-for="(item, index) in items"
|
|
397
436
|
:key="item"
|
|
398
437
|
:ref="item"
|
|
399
438
|
:class="{
|
|
@@ -421,7 +460,7 @@ export default Vue.extend({
|
|
|
421
460
|
:data-testid="`item-edit-${item}`"
|
|
422
461
|
class="edit-input static"
|
|
423
462
|
:value="value != null ? value : item"
|
|
424
|
-
@input="onChange($event)"
|
|
463
|
+
@input="onChange($event, index)"
|
|
425
464
|
@blur.prevent="updateItem(item)"
|
|
426
465
|
@keydown.native.enter="updateItem(item, !errors.duplicate)"
|
|
427
466
|
/>
|
|
@@ -463,7 +502,7 @@ export default Vue.extend({
|
|
|
463
502
|
<button
|
|
464
503
|
data-testid="button-add"
|
|
465
504
|
class="btn btn-sm role-tertiary add-button"
|
|
466
|
-
:disabled="isCreateItem || editedItem"
|
|
505
|
+
:disabled="!!isCreateItem || !!editedItem"
|
|
467
506
|
@click.prevent="onClickPlusButton"
|
|
468
507
|
>
|
|
469
508
|
<span class="icon icon-plus icon-sm" />
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { Accordion } from './index';
|
|
3
|
+
|
|
4
|
+
describe('component: Accordion', () => {
|
|
5
|
+
it('is closed initially by default', () => {
|
|
6
|
+
const title = 'Test Title';
|
|
7
|
+
|
|
8
|
+
const wrapper = shallowMount(Accordion, { propsData: { title } });
|
|
9
|
+
|
|
10
|
+
expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(false);
|
|
11
|
+
expect(wrapper.find('[data-testid="accordion-header"]').text()).toBe(title);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('is opened initially when openInitially prop is true', () => {
|
|
15
|
+
const wrapper = shallowMount(Accordion, { propsData: { openInitially: true } });
|
|
16
|
+
|
|
17
|
+
expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('when closed, opens when the header is clicked', async() => {
|
|
21
|
+
const wrapper = shallowMount(Accordion, { });
|
|
22
|
+
|
|
23
|
+
await wrapper.find('[data-testid="accordion-header"]').trigger('click');
|
|
24
|
+
expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('when open, closes when the header is clicked', async() => {
|
|
28
|
+
const wrapper = shallowMount(Accordion, { propsData: { openInitially: true } });
|
|
29
|
+
|
|
30
|
+
await wrapper.find('[data-testid="accordion-header"]').trigger('click');
|
|
31
|
+
expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('displays a chevron when closed', async() => {
|
|
35
|
+
const wrapper = shallowMount(Accordion, { propsData: { } });
|
|
36
|
+
|
|
37
|
+
expect(wrapper.find('[data-testid="accordion-header"] .icon-chevron-up').exists()).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('displays an inverted chevron when open', async() => {
|
|
41
|
+
const wrapper = shallowMount(Accordion, { propsData: { openInitially: true } });
|
|
42
|
+
|
|
43
|
+
expect(wrapper.find('[data-testid="accordion-header"] .icon-chevron-down').exists()).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
|
+
import { mapGetters } from 'vuex';
|
|
4
|
+
|
|
5
|
+
export default defineComponent({
|
|
6
|
+
props: {
|
|
7
|
+
title: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: ''
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
titleKey: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: null
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
openInitially: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
data() {
|
|
24
|
+
return { isOpen: this.openInitially };
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
computed: { ...mapGetters({ t: 'i18n/t' }) },
|
|
28
|
+
|
|
29
|
+
methods: {
|
|
30
|
+
toggle() {
|
|
31
|
+
this.isOpen = !this.isOpen;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<div class="accordion-container">
|
|
39
|
+
<div
|
|
40
|
+
class="accordion-header"
|
|
41
|
+
data-testid="accordion-header"
|
|
42
|
+
@click="toggle"
|
|
43
|
+
>
|
|
44
|
+
<i
|
|
45
|
+
class="icon text-primary"
|
|
46
|
+
:class="{'icon-chevron-down':isOpen, 'icon-chevron-up':!isOpen}"
|
|
47
|
+
data-testid="accordion-chevron"
|
|
48
|
+
/>
|
|
49
|
+
<slot name="header">
|
|
50
|
+
<h4
|
|
51
|
+
data-testid="accordion-title-slot-content"
|
|
52
|
+
class="mb-0"
|
|
53
|
+
>
|
|
54
|
+
{{ titleKey ? t(titleKey) : title }}
|
|
55
|
+
</h4>
|
|
56
|
+
</slot>
|
|
57
|
+
</div>
|
|
58
|
+
<div
|
|
59
|
+
v-show="isOpen"
|
|
60
|
+
class="accordion-body"
|
|
61
|
+
data-testid="accordion-body"
|
|
62
|
+
>
|
|
63
|
+
<slot />
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<style lang="scss" scoped>
|
|
69
|
+
.accordion-container {
|
|
70
|
+
border: 1px solid var(--border)
|
|
71
|
+
}
|
|
72
|
+
.accordion-header {
|
|
73
|
+
padding: 5px;
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
&>*{
|
|
77
|
+
padding: 5px 0px 5px 0px;
|
|
78
|
+
}
|
|
79
|
+
I {
|
|
80
|
+
margin: 0px 10px 0px 10px;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
.accordion-body {
|
|
84
|
+
padding: 10px;
|
|
85
|
+
}
|
|
86
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Accordion } from './Accordion.vue';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { PropType, defineComponent } from 'vue';
|
|
3
3
|
|
|
4
4
|
interface Badge {
|
|
5
5
|
stateBackground: string;
|
|
@@ -11,7 +11,7 @@ interface Badge {
|
|
|
11
11
|
* <p>Represents a badge whose label and color is either taken from the value property or
|
|
12
12
|
* from the label and color properties. The state property takes precedence.</p>
|
|
13
13
|
*/
|
|
14
|
-
export default
|
|
14
|
+
export default defineComponent({
|
|
15
15
|
props: {
|
|
16
16
|
/**
|
|
17
17
|
* A value having the properties `stateBackground` and `stateDisplay`
|
|
@@ -59,7 +59,7 @@ export default Vue.extend({
|
|
|
59
59
|
</script>
|
|
60
60
|
|
|
61
61
|
<template>
|
|
62
|
-
<span :class="
|
|
62
|
+
<span :class="['badge-state', bg]">
|
|
63
63
|
<i
|
|
64
64
|
v-if="icon"
|
|
65
65
|
class="icon"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
3
|
import { nlToBr } from '@shell/utils/string';
|
|
4
4
|
import { stringify } from '@shell/utils/error';
|
|
5
5
|
|
|
6
|
-
export default
|
|
6
|
+
export default defineComponent({
|
|
7
7
|
props: {
|
|
8
8
|
/**
|
|
9
9
|
* A color class that represents the color of the banner.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { defineComponent, PropType } from 'vue';
|
|
3
3
|
|
|
4
|
-
export default
|
|
4
|
+
export default defineComponent({
|
|
5
5
|
name: 'Card',
|
|
6
6
|
props: {
|
|
7
7
|
/**
|
|
@@ -22,7 +22,7 @@ export default Vue.extend({
|
|
|
22
22
|
* The function to invoke when the default action button is clicked.
|
|
23
23
|
*/
|
|
24
24
|
buttonAction: {
|
|
25
|
-
type: Function
|
|
25
|
+
type: Function as PropType<(event: MouseEvent) => void>,
|
|
26
26
|
default: (): void => { }
|
|
27
27
|
},
|
|
28
28
|
/**
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { PropType, defineComponent } from 'vue';
|
|
3
3
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
4
4
|
import { addObject, removeObject } from '@shell/utils/array';
|
|
5
5
|
import cloneDeep from 'lodash/cloneDeep';
|
|
6
6
|
|
|
7
|
-
export default
|
|
7
|
+
export default defineComponent({
|
|
8
8
|
name: 'Checkbox',
|
|
9
9
|
|
|
10
10
|
props: {
|
|
@@ -140,7 +140,7 @@ export default Vue.extend({
|
|
|
140
140
|
/**
|
|
141
141
|
* Toggles the checked state for the checkbox and emits an 'input' event.
|
|
142
142
|
*/
|
|
143
|
-
clicked(event: MouseEvent): boolean | void {
|
|
143
|
+
clicked(event: MouseEvent | KeyboardEvent): boolean | void {
|
|
144
144
|
if ((event.target as HTMLLinkElement).tagName === 'A' && (event.target as HTMLLinkElement).href) {
|
|
145
145
|
// Ignore links inside the checkbox label so you can click them
|
|
146
146
|
return true;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { mount } from '@vue/test-utils';
|
|
3
2
|
import { LabeledInput } from './index';
|
|
4
3
|
|
|
@@ -20,4 +19,22 @@ describe('component: LabeledInput', () => {
|
|
|
20
19
|
expect(wrapper.emitted('input')).toHaveLength(1);
|
|
21
20
|
expect(wrapper.emitted('input')![0][0]).toBe(value);
|
|
22
21
|
});
|
|
22
|
+
|
|
23
|
+
it('using mode "multiline" should emit input value correctly', () => {
|
|
24
|
+
const value = 'any-string';
|
|
25
|
+
const delay = 1;
|
|
26
|
+
const wrapper = mount(LabeledInput as any, {
|
|
27
|
+
propsData: { delay, multiline: true },
|
|
28
|
+
mocks: { $store: { getters: { 'i18n/t': jest.fn() } } }
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
jest.useFakeTimers();
|
|
32
|
+
wrapper.find('input').setValue('1');
|
|
33
|
+
wrapper.find('input').setValue(value);
|
|
34
|
+
jest.advanceTimersByTime(delay);
|
|
35
|
+
jest.useRealTimers();
|
|
36
|
+
|
|
37
|
+
expect(wrapper.emitted('input')).toHaveLength(1);
|
|
38
|
+
expect(wrapper.emitted('input')![0][0]).toBe(value);
|
|
39
|
+
});
|
|
23
40
|
});
|