@rancher/shell 3.0.3 → 3.0.5-rc.1

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.
Files changed (139) hide show
  1. package/assets/styles/base/_basic.scss +6 -0
  2. package/assets/styles/global/_button.scss +1 -0
  3. package/assets/translations/en-us.yaml +38 -3
  4. package/cloud-credential/aws.vue +2 -0
  5. package/components/AssignTo.vue +25 -11
  6. package/components/AsyncButton.vue +24 -7
  7. package/components/BannerGraphic.vue +1 -0
  8. package/components/CommunityLinks.vue +3 -3
  9. package/components/CopyToClipboardText.vue +2 -1
  10. package/components/DetailText.vue +5 -0
  11. package/components/DisableAuthProviderModal.vue +1 -0
  12. package/components/ExplorerMembers.vue +1 -1
  13. package/components/ExplorerProjectsNamespaces.vue +56 -14
  14. package/components/LandingPagePreference.vue +5 -3
  15. package/components/LocaleSelector.vue +38 -94
  16. package/components/ModalWithCard.vue +1 -0
  17. package/components/MoveModal.vue +1 -0
  18. package/components/PromptRemove.vue +2 -1
  19. package/components/PromptRestore.vue +1 -0
  20. package/components/ResourceCancelModal.vue +1 -0
  21. package/components/SortableTable/index.vue +35 -10
  22. package/components/StatusBadge.vue +10 -4
  23. package/components/__tests__/AsyncButton.test.ts +2 -2
  24. package/components/auth/Principal.vue +9 -3
  25. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  26. package/components/form/ArrayList.vue +75 -54
  27. package/components/form/Command.vue +6 -15
  28. package/components/form/EnvVars.vue +15 -8
  29. package/components/form/HealthCheck.vue +3 -3
  30. package/components/form/HookOption.vue +11 -16
  31. package/components/form/KeyValue.vue +1 -1
  32. package/components/form/LabeledSelect.vue +2 -1
  33. package/components/form/LifecycleHooks.vue +3 -3
  34. package/components/form/MatchExpressions.vue +10 -7
  35. package/components/form/NameNsDescription.vue +123 -103
  36. package/components/form/Networking.vue +20 -12
  37. package/components/form/NodeAffinity.vue +31 -23
  38. package/components/form/NodeScheduling.vue +13 -3
  39. package/components/form/PodAffinity.vue +43 -43
  40. package/components/form/Probe.vue +67 -66
  41. package/components/form/ResourceQuota/Project.vue +5 -1
  42. package/components/form/ResourceSelector.vue +7 -9
  43. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +6 -3
  44. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +12 -1
  45. package/components/form/SSHKnownHosts/index.vue +16 -2
  46. package/components/form/Security.vue +54 -56
  47. package/components/form/Select.vue +31 -6
  48. package/components/form/ShellInput.vue +5 -1
  49. package/components/form/Tolerations.vue +5 -1
  50. package/components/form/ValueFromResource.vue +134 -121
  51. package/components/form/WorkloadPorts.vue +18 -18
  52. package/components/form/__tests__/ArrayList.test.ts +3 -0
  53. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  54. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  55. package/components/form/__tests__/Probe.test.ts +12 -8
  56. package/components/form/__tests__/SSHKnownHosts.test.ts +11 -0
  57. package/components/form/__tests__/Select.test.ts +37 -0
  58. package/components/formatter/InternalExternalIP.vue +2 -0
  59. package/components/formatter/SecretData.vue +20 -7
  60. package/components/nav/Group.vue +15 -1
  61. package/components/nav/Header.vue +1 -0
  62. package/components/nav/Type.vue +12 -1
  63. package/components/templates/blank.vue +4 -1
  64. package/components/templates/default.vue +2 -0
  65. package/components/templates/home.vue +4 -1
  66. package/components/templates/plain.vue +4 -1
  67. package/composables/useRuntimeFlag.ts +29 -0
  68. package/config/router/routes.js +20 -13
  69. package/core/types.ts +5 -0
  70. package/dialog/AddCustomBadgeDialog.vue +1 -0
  71. package/dialog/DeactivateDriverDialog.vue +5 -4
  72. package/dialog/ForceMachineRemoveDialog.vue +4 -1
  73. package/dialog/GitRepoForceUpdateDialog.vue +1 -1
  74. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
  75. package/edit/auth/__tests__/oidc.test.ts +152 -109
  76. package/edit/auth/azuread.vue +1 -0
  77. package/edit/auth/googleoauth.vue +4 -0
  78. package/edit/auth/oidc.vue +37 -4
  79. package/edit/cloudcredential.vue +1 -0
  80. package/edit/fleet.cattle.io.gitrepo.vue +1 -0
  81. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
  82. package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
  83. package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
  84. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
  85. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +1 -0
  86. package/edit/provisioning.cattle.io.cluster/rke2.vue +25 -34
  87. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
  88. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +29 -1
  89. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
  90. package/edit/token.vue +2 -0
  91. package/edit/workload/index.vue +1 -0
  92. package/edit/workload/mixins/workload.js +0 -2
  93. package/list/management.cattle.io.feature.vue +1 -0
  94. package/list/provisioning.cattle.io.cluster.vue +20 -12
  95. package/machine-config/__tests__/vmwarevsphere.test.ts +48 -3
  96. package/machine-config/vmwarevsphere.vue +16 -0
  97. package/models/__tests__/namespace.test.ts +25 -1
  98. package/models/cloudcredential.js +5 -0
  99. package/models/kontainerdriver.js +6 -3
  100. package/models/management.cattle.io.node.js +3 -3
  101. package/models/namespace.js +4 -5
  102. package/models/nodedriver.js +6 -3
  103. package/models/workload.js +4 -1
  104. package/package.json +4 -4
  105. package/pages/about.vue +16 -8
  106. package/pages/account/index.vue +4 -1
  107. package/pages/auth/login.vue +11 -3
  108. package/pages/auth/logout.vue +4 -1
  109. package/pages/auth/setup.vue +1 -0
  110. package/pages/auth/verify.vue +4 -1
  111. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  112. package/pages/diagnostic.vue +47 -2
  113. package/pages/fail-whale.vue +6 -3
  114. package/pages/home.vue +24 -18
  115. package/pages/support/index.vue +4 -1
  116. package/promptRemove/management.cattle.io.fleetworkspace.vue +1 -1
  117. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  118. package/promptRemove/management.cattle.io.project.vue +2 -2
  119. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  120. package/promptRemove/pod.vue +1 -1
  121. package/rancher-components/Form/Radio/RadioGroup.vue +25 -23
  122. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +3 -3
  123. package/rancher-components/RcDropdown/RcDropdown.vue +6 -5
  124. package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -2
  125. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +12 -2
  126. package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
  127. package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
  128. package/scripts/extension/publish +1 -0
  129. package/server/har-file.js +25 -3
  130. package/store/features.js +2 -1
  131. package/store/type-map.js +4 -0
  132. package/types/shell/index.d.ts +9 -2
  133. package/utils/__tests__/string.test.ts +2 -2
  134. package/utils/cluster.js +35 -0
  135. package/utils/string.js +1 -3
  136. package/utils/validators/machine-pool.ts +20 -0
  137. package/components/formatter/ExtensionCache.vue +0 -74
  138. package/components/formatter/Port.vue +0 -24
  139. package/components/formatter/SecretType.vue +0 -41
@@ -1,11 +1,17 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
3
  import Select from '@shell/components/form/Select.vue';
4
+ import { RcDropdown, RcDropdownTrigger, RcDropdownItem } from '@components/RcDropdown';
4
5
 
5
6
  export default {
6
7
  name: 'LocalSelector',
7
8
 
8
- components: { Select },
9
+ components: {
10
+ Select,
11
+ RcDropdown,
12
+ RcDropdownItem,
13
+ RcDropdownTrigger,
14
+ },
9
15
 
10
16
  props: {
11
17
  mode: {
@@ -65,71 +71,37 @@ export default {
65
71
  <template>
66
72
  <div>
67
73
  <div v-if="mode === 'login'">
68
- <div
69
- v-if="showLocale"
70
- role="menu"
71
- :aria-label="t('locale.menu')"
72
- class="locale-login-container"
73
- tabindex="0"
74
- @click="openLocaleSelector"
75
- @blur.capture="closeLocaleSelector"
76
- @keyup.enter="openLocaleSelector"
77
- @keyup.space="openLocaleSelector"
78
- >
79
- <v-dropdown
80
- popperClass="localeSelector"
81
- :shown="isLocaleSelectorOpen"
82
- placement="top"
83
- distance="8"
84
- skidding="12"
85
- :triggers="[]"
86
- :autoHide="false"
87
- :flip="false"
88
- :container="false"
89
- @focus.capture="openLocaleSelector"
74
+ <rc-dropdown v-if="showLocale">
75
+ <rc-dropdown-trigger
76
+ data-testid="locale-selector"
77
+ link
78
+ class="baseline"
79
+ :aria-label="t('locale.menu')"
90
80
  >
91
- <a
92
- data-testid="locale-selector"
93
- class="locale-chooser"
81
+ {{ selectedLocaleLabel }}
82
+ <template
83
+ v-if="showIcon"
84
+ #after
94
85
  >
95
- {{ selectedLocaleLabel }}
96
- <i
97
- v-if="showIcon"
98
- class="icon icon-fw icon-sort-down"
99
- />
100
- </a>
101
- <template #popper>
102
- <ul
103
- class="list-unstyled dropdown"
104
- style="margin: -1px;"
105
- >
106
- <li
107
- v-if="showNone"
108
- v-t="'locale.none'"
109
- class="hand"
110
- tabindex="0"
111
- role="menuitem"
112
- @click.stop="switchLocale('none')"
113
- @keyup.enter.stop="switchLocale('none')"
114
- @keyup.space.stop="switchLocale('none')"
115
- />
116
- <li
117
- v-for="(label, name) in availableLocales"
118
- :key="name"
119
- tabindex="0"
120
- role="menuitem"
121
- class="hand"
122
- :lang="name"
123
- @click.stop="switchLocale(name)"
124
- @keyup.enter.stop="switchLocale(name)"
125
- @keyup.space.stop="switchLocale(name)"
126
- >
127
- {{ label }}
128
- </li>
129
- </ul>
86
+ <i class="icon icon-fw icon-sort-down" />
130
87
  </template>
131
- </v-dropdown>
132
- </div>
88
+ </rc-dropdown-trigger>
89
+ <template #dropdownCollection>
90
+ <rc-dropdown-item
91
+ v-if="showNone"
92
+ v-t="'locale.none'"
93
+ @click="switchLocale('none')"
94
+ />
95
+ <rc-dropdown-item
96
+ v-for="(label, name) in availableLocales"
97
+ :key="name"
98
+ :lang="name"
99
+ @click.stop="switchLocale(name)"
100
+ >
101
+ {{ label }}
102
+ </rc-dropdown-item>
103
+ </template>
104
+ </rc-dropdown>
133
105
  </div>
134
106
  <div v-else>
135
107
  <Select
@@ -142,36 +114,8 @@ export default {
142
114
  </div>
143
115
  </template>
144
116
 
145
- <style lang="scss" scoped>
146
- .advanced {
147
- user-select: none;
148
- padding: 0 5px;
149
- line-height: 40px;
150
- font-size: 15px;
151
- font-weight: 500;
152
- }
153
- .content {
154
- background: var(--nav-active);
155
- padding: 10px;
156
- margin-top: 6px;
157
- border-radius: 4px;
158
- }
159
-
160
- .hand:focus-visible {
161
- @include focus-outline;
162
- outline-offset: 4px;
163
- }
164
-
165
- .locale-chooser {
166
- cursor: pointer;
167
-
168
- &:hover {
169
- text-decoration: none;
117
+ <style lang="scss">
118
+ .baseline {
119
+ align-items: baseline;
170
120
  }
171
- }
172
-
173
- .locale-login-container:focus-visible {
174
- @include focus-outline;
175
- outline-offset: 2px;
176
- }
177
121
  </style>
@@ -65,6 +65,7 @@ export default {
65
65
  v-bind="$attrs"
66
66
  class="modal"
67
67
  data-testid="mvc__card"
68
+ :trigger-focus-trap="true"
68
69
  @close="$emit('finish', $event)"
69
70
  >
70
71
  <Card
@@ -93,6 +93,7 @@ export default {
93
93
  :name="modalName"
94
94
  :width="440"
95
95
  height="auto"
96
+ :trigger-focus-trap="true"
96
97
  @close="close"
97
98
  >
98
99
  <Loading v-if="$fetchState.pending" />
@@ -339,6 +339,7 @@ export default {
339
339
  :width="400"
340
340
  height="auto"
341
341
  styles="max-height: 100vh;"
342
+ :trigger-focus-trap="true"
342
343
  @close="close"
343
344
  >
344
345
  <Card
@@ -354,7 +355,7 @@ export default {
354
355
  <div class="mb-10">
355
356
  <template v-if="!hasCustomRemove">
356
357
  {{ t('promptRemove.attemptingToRemove', { type }) }} <span
357
- v-clean-html="resourceNames(names, t)"
358
+ v-clean-html="resourceNames(names, null, t)"
358
359
  />
359
360
  </template>
360
361
 
@@ -213,6 +213,7 @@ export default {
213
213
  styles="background-color: var(--nav-bg); border-radius: var(--border-radius); max-height: 100vh;"
214
214
  height="auto"
215
215
  :scrollable="true"
216
+ :trigger-focus-trap="true"
216
217
  @close="close"
217
218
  >
218
219
  <Card
@@ -56,6 +56,7 @@ export default {
56
56
  name="cancel-modal"
57
57
  :width="440"
58
58
  height="auto"
59
+ :trigger-focus-trap="true"
59
60
  @close="cancelCancel"
60
61
  >
61
62
  <div class="header">
@@ -1,6 +1,6 @@
1
1
  <script>
2
- import { mapGetters } from 'vuex';
3
- import { defineAsyncComponent, useTemplateRef, onMounted, onBeforeUnmount } from 'vue';
2
+ import { mapGetters, useStore } from 'vuex';
3
+ import { defineAsyncComponent, ref, onMounted, onBeforeUnmount } from 'vue';
4
4
  import day from 'dayjs';
5
5
  import isEmpty from 'lodash/isEmpty';
6
6
  import { dasherize, ucFirst } from '@shell/utils/string';
@@ -24,6 +24,7 @@ import { getParent } from '@shell/utils/dom';
24
24
  import { FORMATTERS } from '@shell/components/SortableTable/sortable-config';
25
25
  import ButtonMultiAction from '@shell/components/ButtonMultiAction.vue';
26
26
  import ActionMenu from '@shell/components/ActionMenuShell.vue';
27
+ import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
27
28
 
28
29
  // Uncomment for table performance debugging
29
30
  // import tableDebug from './debug';
@@ -528,7 +529,7 @@ export default {
528
529
  },
529
530
  },
530
531
  setup(_props, { emit }) {
531
- const table = useTemplateRef('table');
532
+ const table = ref(null);
532
533
 
533
534
  const handleEnterKey = (event) => {
534
535
  if (event.key === 'Enter' && !event.target?.classList?.contains('checkbox-custom')) {
@@ -543,6 +544,14 @@ export default {
543
544
  onBeforeUnmount(() => {
544
545
  table.value.removeEventListener('keyup', handleEnterKey);
545
546
  });
547
+
548
+ const store = useStore();
549
+ const { featureDropdownMenu } = useRuntimeFlag(store);
550
+
551
+ return {
552
+ table,
553
+ featureDropdownMenu,
554
+ };
546
555
  },
547
556
 
548
557
  created() {
@@ -763,7 +772,7 @@ export default {
763
772
  });
764
773
 
765
774
  return rows;
766
- }
775
+ },
767
776
  },
768
777
 
769
778
  methods: {
@@ -1447,9 +1456,9 @@ export default {
1447
1456
  :value="col.value"
1448
1457
  :row="row.row"
1449
1458
  :col="col.col"
1459
+ :get-custom-detail-link="getCustomDetailLink"
1450
1460
  v-bind="col.col.formatterOpts"
1451
1461
  :row-key="row.key"
1452
- :get-custom-detail-link="getCustomDetailLink"
1453
1462
  />
1454
1463
  <component
1455
1464
  :is="col.component"
@@ -1487,11 +1496,27 @@ export default {
1487
1496
  :row="row.row"
1488
1497
  :index="i"
1489
1498
  >
1490
- <ActionMenu
1491
- :resource="row.row"
1492
- :data-testid="componentTestid + '-' + i + '-action-button'"
1493
- :button-aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1494
- />
1499
+ <template v-if="featureDropdownMenu">
1500
+ <ActionMenu
1501
+ :resource="row.row"
1502
+ :data-testid="componentTestid + '-' + i + '-action-button'"
1503
+ :button-aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1504
+ />
1505
+ </template>
1506
+ <template v-else>
1507
+ <ButtonMultiAction
1508
+ :id="`actionButton+${i}+${(row.row && row.row.name) ? row.row.name : ''}`"
1509
+ :ref="`actionButton${i}`"
1510
+ aria-haspopup="true"
1511
+ aria-expanded="false"
1512
+ :aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1513
+ :data-testid="componentTestid + '-' + i + '-action-button'"
1514
+ :borderless="true"
1515
+ @click="handleActionButtonClick(i, $event)"
1516
+ @keyup.enter="handleActionButtonClick(i, $event)"
1517
+ @keyup.space="handleActionButtonClick(i, $event)"
1518
+ />
1519
+ </template>
1495
1520
  </slot>
1496
1521
  </td>
1497
1522
  </tr>
@@ -19,10 +19,16 @@ const STATUS = {
19
19
  }
20
20
  };
21
21
 
22
- const { status = 'success', label } = defineProps<{
23
- status?: 'success' | 'warning' | 'info' | 'error',
24
- label?: string
25
- }>();
22
+ withDefaults(
23
+ defineProps<{
24
+ status?: 'success' | 'warning' | 'info' | 'error',
25
+ label?: string
26
+ }>(),
27
+ {
28
+ status: 'success',
29
+ label: '',
30
+ }
31
+ );
26
32
 
27
33
  </script>
28
34
  <template>
@@ -42,7 +42,7 @@ describe('component: AsyncButton', () => {
42
42
  expect(span.text()).toBe('some-string');
43
43
  });
44
44
 
45
- it('click on async button should emit click with a proper state of waiting, disabled and spinning ::: CB true', () => {
45
+ it('click on async button should emit click with a proper state of waiting, appear disabled and spinning ::: CB true', () => {
46
46
  jest.useFakeTimers();
47
47
 
48
48
  const wrapper: VueWrapper<InstanceType<typeof AsyncButton>> = mount(AsyncButton, {
@@ -65,7 +65,7 @@ describe('component: AsyncButton', () => {
65
65
  expect(wrapper.emitted('click')).toHaveLength(1);
66
66
  expect(wrapper.vm.phase).toBe(ASYNC_BUTTON_STATES.WAITING);
67
67
  expect(wrapper.vm.isSpinning).toBe(true);
68
- expect(wrapper.vm.isDisabled).toBe(true);
68
+ expect(wrapper.vm.appearsDisabled).toBe(true);
69
69
  // testing cb function has been emitted
70
70
  expect(typeof wrapper.emitted('click')![0][0]).toBe('function');
71
71
 
@@ -98,9 +98,9 @@ export default {
98
98
  >
99
99
  <table>
100
100
  <tbody>
101
- <tr><td>{{ t('principal.name') }}: </td><td>{{ principal.name || principal.loginName }}</td></tr>
102
- <tr><td>{{ t('principal.loginName') }}: </td><td>{{ principal.loginName }}</td></tr>
103
- <tr><td>{{ t('principal.type') }}: </td><td>{{ principal.displayType }}</td></tr>
101
+ <tr><th>{{ t('principal.name') }}: </th><td>{{ principal.name || principal.loginName }}</td></tr>
102
+ <tr><th>{{ t('principal.loginName') }}: </th><td>{{ principal.loginName }}</td></tr>
103
+ <tr><th>{{ t('principal.type') }}: </th><td>{{ principal.displayType }}</td></tr>
104
104
  </tbody>
105
105
  </table>
106
106
  </div>
@@ -164,6 +164,12 @@ export default {
164
164
  grid-template-rows: auto math.div($size, 2);
165
165
  column-gap: 10px;
166
166
 
167
+ th {
168
+ text-align: left;
169
+ font-weight: normal;
170
+ padding-right: 10px;
171
+ }
172
+
167
173
  &.showLabels {
168
174
  grid-template-areas:
169
175
  "avatar name";
@@ -51,8 +51,9 @@ describe('component: RoleDetailEdit', () => {
51
51
  const wrapper = mount(RoleDetailEdit, {
52
52
  props: {
53
53
  value: {
54
- rules: [{ verbs }],
55
- subtype: 'GLOBAL'
54
+ rules: [{ verbs }],
55
+ subtype: 'GLOBAL',
56
+ metadata: { name: 'global-role-with-inherited' },
56
57
  },
57
58
  },
58
59
 
@@ -1,4 +1,5 @@
1
1
  <script>
2
+ import { ref, watch, computed } from 'vue';
2
3
  import debounce from 'lodash/debounce';
3
4
  import { _EDIT, _VIEW } from '@shell/config/query-params';
4
5
  import { removeAt } from '@shell/utils/array';
@@ -94,22 +95,83 @@ export default {
94
95
  // we only want functions in the rules array
95
96
  validator: (rules) => rules.every((rule) => ['function'].includes(typeof rule))
96
97
  },
98
+ a11yLabel: {
99
+ type: String,
100
+ default: '',
101
+ },
97
102
  },
98
- data() {
99
- const input = (Array.isArray(this.value) ? this.value : []).slice();
100
- const rows = [];
103
+
104
+ setup(props, { emit }) {
105
+ const input = (Array.isArray(props.value) ? props.value : []).slice();
106
+ const rows = ref([]);
101
107
 
102
108
  for ( const value of input ) {
103
- rows.push({ value });
109
+ rows.value.push({ value });
104
110
  }
105
- if ( !rows.length && this.initialEmptyRow ) {
106
- const value = this.defaultAddValue ? clone(this.defaultAddValue) : '';
111
+ if ( !rows.value.length && props.initialEmptyRow ) {
112
+ const value = props.defaultAddValue ? clone(props.defaultAddValue) : '';
107
113
 
108
- rows.push({ value });
114
+ rows.value.push({ value });
109
115
  }
110
116
 
111
- return { rows, lastUpdateWasFromValue: false };
117
+ const isView = computed(() => {
118
+ return props.mode === _VIEW;
119
+ });
120
+
121
+ /**
122
+ * Cleanup rows and emit input
123
+ */
124
+ const update = () => {
125
+ if ( isView.value ) {
126
+ return;
127
+ }
128
+ const out = [];
129
+
130
+ for ( const row of rows.value ) {
131
+ const trim = !props.valueMultiline && (typeof row.value === 'string');
132
+ const value = trim ? row.value.trim() : row.value;
133
+
134
+ if ( typeof value !== 'undefined' ) {
135
+ out.push(value);
136
+ }
137
+ }
138
+ emit('update:value', out);
139
+ };
140
+
141
+ const lastUpdateWasFromValue = ref(false);
142
+ const queueUpdate = debounce(update, 50);
143
+
144
+ watch(
145
+ rows,
146
+ () => {
147
+ // lastUpdateWasFromValue is used to break a cycle where when rows are updated
148
+ // this was called which then forced rows to updated again
149
+ if (!lastUpdateWasFromValue.value) {
150
+ queueUpdate();
151
+ }
152
+ lastUpdateWasFromValue.value = false;
153
+ },
154
+ { deep: true }
155
+ );
156
+
157
+ watch(
158
+ () => props.value,
159
+ () => {
160
+ lastUpdateWasFromValue.value = true;
161
+ rows.value = (props.value || []).map((v) => ({ value: v }));
162
+ },
163
+ { deep: true }
164
+ );
165
+
166
+ return {
167
+ rows,
168
+ lastUpdateWasFromValue,
169
+ queueUpdate,
170
+ isView,
171
+ update,
172
+ };
112
173
  },
174
+
113
175
  computed: {
114
176
  _addLabel() {
115
177
  return this.addLabel || this.t('generic.add');
@@ -117,10 +179,6 @@ export default {
117
179
  _removeLabel() {
118
180
  return this.removeLabel || this.t('generic.remove');
119
181
  },
120
-
121
- isView() {
122
- return this.mode === _VIEW;
123
- },
124
182
  showAdd() {
125
183
  return this.addAllowed;
126
184
  },
@@ -141,29 +199,7 @@ export default {
141
199
  return !this.valueMultiline && this.protip;
142
200
  }
143
201
  },
144
- watch: {
145
- value: {
146
- deep: true,
147
- handler() {
148
- this.lastUpdateWasFromValue = true;
149
- this.rows = (this.value || []).map((v) => ({ value: v }));
150
- }
151
- },
152
-
153
- rows: {
154
- deep: true,
155
- handler(newValue, oldValue) {
156
- // lastUpdateWasFromValue is used to break a cycle where when rows are updated
157
- // this was called which then forced rows to updated again
158
- if (!this.lastUpdateWasFromValue) {
159
- this.queueUpdate();
160
- }
161
- this.lastUpdateWasFromValue = false;
162
- }
163
- }
164
- },
165
202
  created() {
166
- this.queueUpdate = debounce(this.update, 50);
167
203
  },
168
204
  methods: {
169
205
  add() {
@@ -189,26 +225,6 @@ export default {
189
225
  this.queueUpdate();
190
226
  },
191
227
 
192
- /**
193
- * Cleanup rows and emit input
194
- */
195
- update() {
196
- if ( this.isView ) {
197
- return;
198
- }
199
- const out = [];
200
-
201
- for ( const row of this.rows ) {
202
- const trim = !this.valueMultiline && (typeof row.value === 'string');
203
- const value = trim ? row.value.trim() : row.value;
204
-
205
- if ( typeof value !== 'undefined' ) {
206
- out.push(value);
207
- }
208
- }
209
- this.$emit('update:value', out);
210
- },
211
-
212
228
  /**
213
229
  * Handle paste event, e.g. split multiple lines in rows
214
230
  */
@@ -316,6 +332,7 @@ export default {
316
332
  :data-testid="`input-${idx}`"
317
333
  :placeholder="valuePlaceholder"
318
334
  :disabled="isView || disabled"
335
+ :aria-label="a11yLabel ? a11yLabel : undefined"
319
336
  @paste="onPaste(idx, $event)"
320
337
  >
321
338
  </slot>
@@ -336,6 +353,8 @@ export default {
336
353
  :disabled="isView"
337
354
  class="btn role-link"
338
355
  :data-testid="`remove-item-${idx}`"
356
+ :aria-label="`${_removeLabel} ${idx + 1}`"
357
+ role="button"
339
358
  @click="remove(row, idx)"
340
359
  >
341
360
  {{ _removeLabel }}
@@ -368,6 +387,8 @@ export default {
368
387
  class="btn role-tertiary add"
369
388
  :disabled="loading || disableAdd"
370
389
  data-testid="array-list-button"
390
+ :aria-label="_addLabel"
391
+ role="button"
371
392
  @click="add()"
372
393
  >
373
394
  <i
@@ -43,23 +43,14 @@ export default {
43
43
  },
44
44
 
45
45
  data() {
46
- const {
47
- command,
48
- args,
49
- workingDir,
50
- stdin = false,
51
- stdinOnce = false,
52
- tty = false,
53
- } = this.value;
54
-
55
46
  return {
56
- args,
57
- command,
47
+ args: this.value.args,
48
+ command: this.value.command,
58
49
  commandOptions: ['No', 'Once', 'Yes'],
59
- stdin,
60
- stdinOnce,
61
- tty,
62
- workingDir,
50
+ stdin: this.value.stdin || false,
51
+ stdinOnce: this.value.stdin || false,
52
+ tty: this.value.tty || false,
53
+ workingDir: this.value.workingDir,
63
54
  };
64
55
  },
65
56
 
@@ -39,14 +39,10 @@ export default {
39
39
  },
40
40
 
41
41
  data() {
42
- const { env = [], envFrom = [] } = this.value;
43
-
44
- const allEnv = [...env, ...envFrom].map((row) => {
45
- return { value: row, id: randomStr(4) };
46
- });
47
-
48
42
  return {
49
- env, envFrom, allEnv
43
+ env: [],
44
+ envFrom: [],
45
+ allEnv: [],
50
46
  };
51
47
  },
52
48
 
@@ -63,7 +59,18 @@ export default {
63
59
  }
64
60
  }
65
61
  },
62
+
66
63
  created() {
64
+ const { env = [], envFrom = [] } = this.value;
65
+
66
+ const allEnv = [...env, ...envFrom].map((row) => {
67
+ return { value: row, id: randomStr(4) };
68
+ });
69
+
70
+ this.env = env;
71
+ this.envFrom = envFrom;
72
+ this.allEnv = allEnv;
73
+
67
74
  this.queueUpdate = debounce(this.update, 500);
68
75
  },
69
76
 
@@ -107,7 +114,7 @@ export default {
107
114
  <div :style="{'width':'100%'}">
108
115
  <div
109
116
  v-for="(row, i) in allEnv"
110
- :key="i"
117
+ :key="row.id"
111
118
  >
112
119
  <ValueFromResource
113
120
  v-model:value="row.value"
@@ -18,10 +18,10 @@ export default {
18
18
  },
19
19
 
20
20
  data() {
21
- const { readinessProbe, livenessProbe, startupProbe } = this.value;
22
-
23
21
  return {
24
- readinessProbe, livenessProbe, startupProbe
22
+ readinessProbe: this.value.readinessProbe,
23
+ livenessProbe: this.value.livenessProbe,
24
+ startupProbe: this.value.startupProbe,
25
25
  };
26
26
  },
27
27