@rancher/shell 0.3.29 → 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 +31 -6
- package/assets/translations/zh-hans.yaml +2 -2
- 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/CopyCode.vue +6 -2
- package/components/CopyToClipboard.vue +2 -1
- package/components/CopyToClipboardText.vue +14 -9
- package/components/CruResource.vue +1 -0
- package/components/DraggableZone.vue +2 -2
- package/components/EtcdInfoBanner.vue +5 -5
- package/components/ExplorerProjectsNamespaces.vue +25 -1
- package/components/IconOrSvg.vue +1 -1
- package/components/LandingPagePreference.vue +1 -4
- package/components/Markdown.vue +16 -12
- package/components/PodSecurityAdmission.vue +2 -2
- package/components/Questions/index.vue +1 -1
- package/components/ResourceDetail/Masthead.vue +25 -9
- 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/StatusTable.vue +5 -1
- package/components/Tabbed/index.vue +12 -0
- package/components/__tests__/CopyCode.test.ts +5 -4
- package/components/fleet/FleetBundles.vue +5 -11
- package/components/fleet/FleetRepos.vue +62 -27
- package/components/fleet/FleetResources.vue +6 -1
- package/components/fleet/FleetSummary.vue +3 -3
- package/components/fleet/__tests__/FleetSummary.test.ts +316 -0
- 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/form/Password.vue +3 -1
- 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 +1 -2
- 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/home-links.js +1 -1
- 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-helpers.js +3 -5
- 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 +14 -0
- package/creators/app/init +19 -0
- 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/monitoring.coreos.com.prometheusrule/AlertingRule.vue +12 -3
- package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +2 -1
- 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} +6 -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/Upgrading.vue +3 -2
- package/edit/workload/mixins/workload.js +1 -1
- package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +2 -1
- package/initialize/App.js +25 -71
- package/initialize/client.js +21 -162
- package/initialize/index.js +47 -124
- 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.cluster.test.ts +4 -0
- 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.cluster.js +7 -3
- 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 +39 -4
- package/models/secret.js +117 -18
- package/models/workload.js +16 -0
- package/models/workload.service.js +18 -0
- package/package.json +11 -10
- 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/index.vue +64 -43
- 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/clean-html-directive.js +1 -19
- package/plugins/clean-html.js +53 -0
- package/plugins/clean-tooltip-directive.js +1 -1
- 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/index.js +11 -0
- 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/bundle +19 -7
- package/scripts/extension/helm/scripts/package +11 -3
- package/scripts/extension/parse-tag-name +2 -2
- package/scripts/extension/publish +20 -9
- 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 +13 -15
- 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 +433 -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/clipboard.js +5 -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/plugins/vue-clipboard2.js +0 -4
- 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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
3
|
import { _VIEW } from '@shell/config/query-params';
|
|
4
|
+
import { randomStr } from '@shell/utils/string';
|
|
4
5
|
|
|
5
|
-
export default
|
|
6
|
+
export default defineComponent({
|
|
6
7
|
props: {
|
|
7
8
|
/**
|
|
8
9
|
* The name of the input, for grouping.
|
|
@@ -71,7 +72,10 @@ export default Vue.extend({
|
|
|
71
72
|
},
|
|
72
73
|
|
|
73
74
|
data() {
|
|
74
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
isChecked: this.value === this.val,
|
|
77
|
+
randomString: `${ randomStr() }-radio`,
|
|
78
|
+
};
|
|
75
79
|
},
|
|
76
80
|
|
|
77
81
|
computed: {
|
|
@@ -115,13 +119,15 @@ export default Vue.extend({
|
|
|
115
119
|
/**
|
|
116
120
|
* Emits the input event.
|
|
117
121
|
*/
|
|
118
|
-
clicked(
|
|
119
|
-
|
|
122
|
+
clicked(event: MouseEvent | KeyboardEvent) {
|
|
123
|
+
const target = event.target;
|
|
124
|
+
|
|
125
|
+
if (this.isDisabled || (target instanceof HTMLElement && target.tagName === 'A')) {
|
|
120
126
|
return;
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
this.$emit('input', this.val);
|
|
124
|
-
}
|
|
130
|
+
},
|
|
125
131
|
}
|
|
126
132
|
});
|
|
127
133
|
</script>
|
|
@@ -134,7 +140,7 @@ export default Vue.extend({
|
|
|
134
140
|
@click.stop="clicked($event)"
|
|
135
141
|
>
|
|
136
142
|
<input
|
|
137
|
-
:id="
|
|
143
|
+
:id="randomString"
|
|
138
144
|
:disabled="isDisabled"
|
|
139
145
|
:name="name"
|
|
140
146
|
:value="''+val"
|
|
@@ -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
|
+
});
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { PropType, defineComponent } from 'vue';
|
|
3
3
|
import { _VIEW } from '@shell/config/query-params';
|
|
4
4
|
import RadioButton from '@components/Form/Radio/RadioButton.vue';
|
|
5
5
|
|
|
6
6
|
interface Option {
|
|
7
7
|
value: unknown,
|
|
8
|
-
label: string
|
|
8
|
+
label: string,
|
|
9
|
+
description?: string,
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
export default
|
|
12
|
+
export default defineComponent({
|
|
12
13
|
components: { RadioButton },
|
|
13
14
|
props: {
|
|
14
15
|
/**
|
|
@@ -169,6 +170,7 @@ export default Vue.extend({
|
|
|
169
170
|
|
|
170
171
|
<template>
|
|
171
172
|
<div>
|
|
173
|
+
<!-- Label -->
|
|
172
174
|
<div
|
|
173
175
|
v-if="label || labelKey || tooltip || tooltipKey || $slots.label"
|
|
174
176
|
class="radio-group label"
|
|
@@ -195,6 +197,8 @@ export default Vue.extend({
|
|
|
195
197
|
</h3>
|
|
196
198
|
</slot>
|
|
197
199
|
</div>
|
|
200
|
+
|
|
201
|
+
<!-- Group -->
|
|
198
202
|
<div
|
|
199
203
|
class="radio-group"
|
|
200
204
|
:class="{'row':row}"
|
|
@@ -212,6 +216,7 @@ export default Vue.extend({
|
|
|
212
216
|
:is-disabled="isDisabled"
|
|
213
217
|
:name="i"
|
|
214
218
|
>
|
|
219
|
+
<!-- Default input -->
|
|
215
220
|
<RadioButton
|
|
216
221
|
:key="name+'-'+i"
|
|
217
222
|
:name="name"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
3
|
import debounce from 'lodash/debounce';
|
|
4
4
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ declare module 'vue/types/vue' {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export default
|
|
13
|
+
export default defineComponent({
|
|
14
14
|
inheritAttrs: false,
|
|
15
15
|
|
|
16
16
|
props: {
|
|
@@ -115,7 +115,9 @@ export default Vue.extend({
|
|
|
115
115
|
/**
|
|
116
116
|
* Emits the input event and resizes the Text Area.
|
|
117
117
|
*/
|
|
118
|
-
onInput(
|
|
118
|
+
onInput(event: Event): void {
|
|
119
|
+
const val = (event?.target as HTMLInputElement)?.value;
|
|
120
|
+
|
|
119
121
|
this.$emit('input', val);
|
|
120
122
|
this.queueResize();
|
|
121
123
|
},
|
|
@@ -163,7 +165,7 @@ export default Vue.extend({
|
|
|
163
165
|
v-bind="$attrs"
|
|
164
166
|
:spellcheck="spellcheck"
|
|
165
167
|
@paste="$emit('paste', $event)"
|
|
166
|
-
@input="onInput($event
|
|
168
|
+
@input="onInput($event)"
|
|
167
169
|
@focus="$emit('focus', $event)"
|
|
168
170
|
@blur="$emit('blur', $event)"
|
|
169
171
|
/>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
|
+
|
|
4
|
+
type StateType = boolean | 'true' | 'false' | undefined;
|
|
5
|
+
|
|
6
|
+
export default defineComponent({
|
|
4
7
|
props: {
|
|
5
8
|
value: {
|
|
6
9
|
type: [Boolean, String, Number],
|
|
@@ -28,7 +31,7 @@ export default Vue.extend({
|
|
|
28
31
|
},
|
|
29
32
|
},
|
|
30
33
|
data() {
|
|
31
|
-
return { state: false as
|
|
34
|
+
return { state: false as StateType };
|
|
32
35
|
},
|
|
33
36
|
|
|
34
37
|
watch: {
|
|
@@ -41,7 +44,7 @@ export default Vue.extend({
|
|
|
41
44
|
},
|
|
42
45
|
|
|
43
46
|
methods: {
|
|
44
|
-
toggle(neu:
|
|
47
|
+
toggle(neu: StateType | null) {
|
|
45
48
|
this.state = neu === null ? !this.state : neu;
|
|
46
49
|
this.$emit('input', this.state ? this.onValue : this.offValue);
|
|
47
50
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import { defineComponent } from 'vue';
|
|
3
3
|
|
|
4
|
-
export default
|
|
4
|
+
export default defineComponent({
|
|
5
5
|
props: {
|
|
6
6
|
/**
|
|
7
7
|
* The Labeled Tooltip value.
|
|
@@ -29,9 +29,14 @@ export default Vue.extend({
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
computed: {
|
|
32
|
-
iconClass() {
|
|
32
|
+
iconClass(): string {
|
|
33
33
|
return this.status === 'error' ? 'icon-warning' : 'icon-info';
|
|
34
34
|
}
|
|
35
|
+
},
|
|
36
|
+
methods: {
|
|
37
|
+
isObject(value: string | Record<string, unknown>): value is Record<string, unknown> {
|
|
38
|
+
return typeof value === 'object' && value !== null && !!value.content;
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
});
|
|
37
42
|
</script>
|
|
@@ -44,7 +49,7 @@ export default Vue.extend({
|
|
|
44
49
|
>
|
|
45
50
|
<template v-if="hover">
|
|
46
51
|
<i
|
|
47
|
-
v-clean-tooltip="value
|
|
52
|
+
v-clean-tooltip="isObject(value) ? { ...{content: value.content, classes: [`tooltip-${status}`]}, ...value } : value"
|
|
48
53
|
:class="{'hover':!value, [iconClass]: true}"
|
|
49
54
|
class="icon status-icon"
|
|
50
55
|
/>
|
|
@@ -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" />
|