dashboard-shell-shell 1.0.113 → 1.0.115

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 (119) hide show
  1. package/components/ActionDropdown.vue +1 -1
  2. package/components/ActionMenu.vue +2 -2
  3. package/components/ActionMenuShell.vue +0 -1
  4. package/components/AppModal.vue +6 -78
  5. package/components/AssignTo.vue +11 -25
  6. package/components/AsyncButton.vue +7 -24
  7. package/components/BannerGraphic.vue +0 -1
  8. package/components/ButtonDropdown.vue +4 -26
  9. package/components/ButtonGroup.vue +0 -4
  10. package/components/ButtonMultiAction.vue +0 -1
  11. package/components/CommunityLinks.vue +3 -3
  12. package/components/ConsumptionGauge.vue +5 -24
  13. package/components/CopyToClipboardText.vue +1 -2
  14. package/components/CruResource.vue +7 -12
  15. package/components/CruResourceFooter.vue +2 -2
  16. package/components/DashboardOptions.vue +15 -21
  17. package/components/DetailText.vue +0 -5
  18. package/components/DisableAuthProviderModal.vue +0 -1
  19. package/components/ExplorerMembers.vue +1 -1
  20. package/components/ExplorerProjectsNamespaces.vue +14 -56
  21. package/components/FixedBanner.vue +12 -19
  22. package/components/GlobalRoleBindings.vue +1 -5
  23. package/components/GrafanaDashboard.vue +4 -4
  24. package/components/GrowlManager.vue +1 -4
  25. package/components/HardwareResourceGauge.vue +3 -39
  26. package/components/InfoBox.vue +3 -3
  27. package/components/InputOrDisplay.vue +2 -28
  28. package/components/LabelValue.vue +1 -16
  29. package/components/LandingPagePreference.vue +3 -5
  30. package/components/LocaleSelector.vue +93 -39
  31. package/components/ModalWithCard.vue +0 -2
  32. package/components/MoveModal.vue +0 -1
  33. package/components/PromptChangePassword.vue +1 -1
  34. package/components/PromptModal.vue +2 -15
  35. package/components/PromptRemove.vue +8 -28
  36. package/components/PromptRestore.vue +0 -1
  37. package/components/ResourceCancelModal.vue +0 -1
  38. package/components/ResourceDetail/Masthead.vue +43 -188
  39. package/components/ResourceDetail/__tests__/Masthead.test.ts +1 -5
  40. package/components/ResourceDetail/index.vue +14 -49
  41. package/components/ResourceList/Masthead.vue +18 -80
  42. package/components/ResourceTable.vue +8 -3
  43. package/components/SideNav.vue +3 -2
  44. package/components/SortableTable/THead.vue +4 -10
  45. package/components/SortableTable/actions.js +1 -1
  46. package/components/SortableTable/index.vue +537 -637
  47. package/components/SortableTable/selection.js +11 -0
  48. package/components/Tabbed/Tab.vue +3 -3
  49. package/components/Tabbed/index.vue +26 -44
  50. package/components/Wizard.vue +2 -2
  51. package/components/__tests__/AsyncButton.test.ts +2 -2
  52. package/components/__tests__/FixedBanner.test.ts +3 -3
  53. package/components/auth/Principal.vue +3 -10
  54. package/components/auth/__tests__/RoleDetailEdit.test.ts +2 -3
  55. package/components/form/ArrayList.vue +85 -123
  56. package/components/form/ArrayListGrouped.vue +2 -10
  57. package/components/form/Command.vue +15 -6
  58. package/components/form/EnvVars.vue +8 -16
  59. package/components/form/Footer.vue +5 -8
  60. package/components/form/HealthCheck.vue +3 -3
  61. package/components/form/HookOption.vue +16 -11
  62. package/components/form/KeyValue.vue +7 -16
  63. package/components/form/LabeledSelect.vue +76 -59
  64. package/components/form/LifecycleHooks.vue +3 -3
  65. package/components/form/MatchExpressions.vue +12 -35
  66. package/components/form/NameNsDescription.vue +115 -147
  67. package/components/form/Networking.vue +12 -20
  68. package/components/form/NodeAffinity.vue +23 -31
  69. package/components/form/NodeScheduling.vue +3 -13
  70. package/components/form/Password.vue +5 -11
  71. package/components/form/PodAffinity.vue +44 -43
  72. package/components/form/Probe.vue +66 -68
  73. package/components/form/ResourceQuota/Project.vue +1 -5
  74. package/components/form/ResourceSelector.vue +9 -7
  75. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +3 -6
  76. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +1 -12
  77. package/components/form/SSHKnownHosts/index.vue +2 -16
  78. package/components/form/Security.vue +56 -54
  79. package/components/form/Select.vue +7 -41
  80. package/components/form/ShellInput.vue +1 -5
  81. package/components/form/Tolerations.vue +1 -5
  82. package/components/form/UnitInput.vue +2 -2
  83. package/components/form/ValueFromResource.vue +121 -134
  84. package/components/form/WorkloadPorts.vue +18 -18
  85. package/components/form/__tests__/ArrayList.test.ts +2 -5
  86. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  87. package/components/form/__tests__/NameNsDescription.test.ts +14 -115
  88. package/components/form/__tests__/Probe.test.ts +8 -12
  89. package/components/form/__tests__/SSHKnownHosts.test.ts +0 -11
  90. package/components/form/__tests__/Select.test.ts +0 -37
  91. package/components/form/__tests__/UnitInput.test.ts +5 -4
  92. package/components/formatter/BadgeStateFormatter.vue +5 -8
  93. package/components/formatter/ExtensionCache.vue +74 -0
  94. package/components/formatter/InternalExternalIP.vue +0 -2
  95. package/components/formatter/Port.vue +24 -0
  96. package/components/formatter/SecretData.vue +7 -20
  97. package/components/formatter/SecretType.vue +41 -0
  98. package/components/nav/Favorite.vue +1 -5
  99. package/components/nav/Group.vue +3 -16
  100. package/components/nav/Header.vue +13 -39
  101. package/components/nav/Jump.vue +0 -7
  102. package/components/nav/NamespaceFilter.vue +8 -14
  103. package/components/nav/Pinned.vue +1 -1
  104. package/components/nav/TopLevelMenu.vue +17 -5
  105. package/components/nav/Type.vue +1 -14
  106. package/components/nav/__tests__/TopLevelMenu.test.ts +40 -0
  107. package/components/templates/blank.vue +1 -4
  108. package/components/templates/default.vue +0 -8
  109. package/components/templates/home.vue +1 -10
  110. package/components/templates/plain.vue +1 -10
  111. package/package.json +1 -1
  112. package/public/index.html +3 -3
  113. package/components/ActionDropdownShell.vue +0 -71
  114. package/components/DotState.vue +0 -84
  115. package/components/ModalManager.vue +0 -55
  116. package/components/SlideInPanelManager.vue +0 -126
  117. package/components/StatusBadge.vue +0 -77
  118. package/components/__tests__/ModalManager.spec.ts +0 -176
  119. package/components/__tests__/SlideInPanelManager.spec.ts +0 -166
@@ -340,6 +340,17 @@ export default {
340
340
  if ( !isSelected ) {
341
341
  this.update([node], this.selectedRows.slice());
342
342
  }
343
+
344
+ let resources = this.selectedRows;
345
+
346
+ if ( this.mangleActionResources ) {
347
+ resources = await this.mangleActionResources(resources);
348
+ }
349
+
350
+ this.$store.commit(`action-menu/show`, {
351
+ resources,
352
+ event: e,
353
+ });
343
354
  },
344
355
 
345
356
  keySelectRow(row, more = false) {
@@ -100,14 +100,14 @@ export default {
100
100
  v-if="shouldShowHeader"
101
101
  class="tab-header"
102
102
  >
103
- <!-- <h2>
103
+ <h2>
104
104
  {{ labelDisplay }}
105
105
  <i
106
106
  v-if="tooltip"
107
107
  v-clean-tooltip="tooltip"
108
108
  class="icon icon-info icon-lg"
109
109
  />
110
- </h2> -->
110
+ </h2>
111
111
  <slot name="tab-header-right" />
112
112
  </div>
113
113
  <slot v-bind="{active}" />
@@ -118,7 +118,7 @@ export default {
118
118
  .tab-header {
119
119
  display: flex;
120
120
  justify-content: space-between;
121
- /* margin-bottom: 15px; */
121
+ margin-bottom: 15px;
122
122
  align-items: center;
123
123
 
124
124
  h2 {
@@ -367,6 +367,15 @@ export default {
367
367
  margin: 0;
368
368
  padding: 0;
369
369
 
370
+ &:focus-visible {
371
+ outline: none;
372
+
373
+ .tab.active {
374
+ @include focus-outline;
375
+ outline-offset: -2px;
376
+ }
377
+ }
378
+
370
379
  &.horizontal {
371
380
  border: solid thin var(--border);
372
381
  border-bottom: 0;
@@ -382,12 +391,8 @@ export default {
382
391
  }
383
392
  }
384
393
 
385
- &:focus {
386
- outline: none;
387
-
388
- & .tab.active a span {
389
- text-decoration: none;
390
- }
394
+ &:focus .tab.active a span {
395
+ text-decoration: underline;
391
396
  }
392
397
 
393
398
  .tab {
@@ -404,7 +409,7 @@ export default {
404
409
  &:hover {
405
410
  text-decoration: none;
406
411
  span {
407
- text-decoration: none;
412
+ text-decoration: underline;
408
413
  }
409
414
  }
410
415
  }
@@ -474,68 +479,45 @@ export default {
474
479
 
475
480
  .side-tabs {
476
481
  display: flex;
477
- flex-direction: column;
478
- /* box-shadow: 0 0 20px var(--shadow);
482
+ box-shadow: 0 0 20px var(--shadow);
479
483
  border-radius: calc(var(--border-radius) * 2);
480
- background-color: var(--tabbed-sidebar-bg); */
481
- margin: 0px -20px;
484
+ background-color: var(--tabbed-sidebar-bg);
485
+
482
486
  .tab-container {
483
487
  padding: 20px;
484
488
  }
485
489
 
486
490
  & .tabs {
487
- /* width: $sideways-tabs-width; */
488
- /* min-width: $sideways-tabs-width; */
491
+ width: $sideways-tabs-width;
492
+ min-width: $sideways-tabs-width;
489
493
  display: flex;
490
- border-bottom:1px solid #d7d7d7;
491
- padding: 0 0 0 20px;
492
- /* flex: 1 0; */
493
- /* flex-direction: column; */
494
+ flex: 1 0;
495
+ flex-direction: column;
494
496
 
495
497
  // &.vertical {
496
498
  // .tab.active {
497
499
  // background-color: var(--tabbed-container-bg);
498
500
  // }
499
501
  // }
502
+
500
503
  & .tab {
501
- /* width: 100%; */
502
- width: 120px;
503
- height: 36px;
504
- /* border-top: solid 5px transparent; */
505
- display: flex;
506
- justify-content: center;
507
- padding: 8px 16px;
508
- box-sizing: border-box;
509
- border: 1px solid #d7d7d7;
510
- border-bottom: 0px;
511
- margin-right: 5px;
512
- border-radius: 2px;
504
+ width: 100%;
505
+ border-left: solid 5px transparent;
513
506
 
514
507
  &.toggle A {
515
508
  color: var(--primary);
516
- padding: 0px;
517
509
  }
518
510
 
519
511
  A {
520
- color: var(--input-label);
521
- padding: 0px;
512
+ color: var(--primary);
522
513
  }
523
514
 
524
515
  &.active {
525
516
  background-color: var(--body-bg);
526
- border-top: solid 2px var(--primary);
527
- position: relative;
528
- &.active::before{
529
- position: absolute;
530
- right: 0;
531
- left: 0;
532
- top: 34px;
533
- border-bottom: 1px solid #fff;
534
- content: '';
517
+ border-left: solid 5px var(--primary);
535
518
 
536
- }
537
519
  & A {
538
- color: var(--primary);
520
+ color: var(--input-label);
539
521
  }
540
522
  }
541
523
 
@@ -579,7 +561,7 @@ margin: 0px -20px;
579
561
  &
580
562
 
581
563
  .tab-container {
582
- /* width: calc(100% - #{$sideways-tabs-width}); */
564
+ width: calc(100% - #{$sideways-tabs-width});
583
565
  flex-grow: 1;
584
566
  background-color: var(--body-bg);
585
567
  }
@@ -426,7 +426,7 @@ export default {
426
426
  </div>
427
427
  <div
428
428
  id="wizard-footer-controls"
429
- class="controls-row"
429
+ class="controls-row pt-20"
430
430
  >
431
431
  <slot
432
432
  name="cancel"
@@ -674,7 +674,7 @@ $spacer: 10px;
674
674
  // We have to account for the absolute position of the .controls-row
675
675
  .footer-error {
676
676
  margin-top: -40px;
677
- margin-bottom: calc($footer-height + 10px);
677
+ margin-bottom: 70px;
678
678
  }
679
679
 
680
680
  .controls-row {
@@ -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, appear disabled and spinning ::: CB true', () => {
45
+ it('click on async button should emit click with a proper state of waiting, 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.appearsDisabled).toBe(true);
68
+ expect(wrapper.vm.isDisabled).toBe(true);
69
69
  // testing cb function has been emitted
70
70
  expect(typeof wrapper.emitted('click')![0][0]).toBe('function');
71
71
 
@@ -40,7 +40,7 @@ describe('component: FixedBanner', () => {
40
40
  expect(wrapper.vm.showHeader).toStrictEqual(true);
41
41
 
42
42
  const bannerElem = wrapper.find('.banner');
43
- const noArrayTextElem = wrapper.find('.banner .single-row');
43
+ const noArrayTextElem = wrapper.find('.banner > p');
44
44
 
45
45
  expect(bannerElem.exists()).toBe(true);
46
46
  expect(bannerElem.classes()).not.toContain('banner-consent');
@@ -60,7 +60,7 @@ describe('component: FixedBanner', () => {
60
60
  expect(wrapper.vm.showFooter).toStrictEqual(true);
61
61
 
62
62
  const bannerElem = wrapper.find('.banner');
63
- const noArrayTextElem = wrapper.find('.banner .single-row');
63
+ const noArrayTextElem = wrapper.find('.banner > p');
64
64
 
65
65
  expect(bannerElem.exists()).toBe(true);
66
66
  expect(bannerElem.classes()).not.toContain('banner-consent');
@@ -84,7 +84,7 @@ describe('component: FixedBanner', () => {
84
84
  const bannerDialogElem = wrapper.find('.banner-dialog');
85
85
  const bannerDialogFrameElem = wrapper.find('.banner-dialog-frame');
86
86
  const buttonDialog = wrapper.find('.banner-dialog button');
87
- const noArrayTextElem = wrapper.find('.banner .single-row');
87
+ const noArrayTextElem = wrapper.find('.banner > p');
88
88
 
89
89
  expect(bannerElem.exists()).toBe(true);
90
90
  expect(bannerDialogGlassElem.exists()).toBe(true);
@@ -90,7 +90,6 @@ export default {
90
90
  <img
91
91
  :src="principal.avatarSrc"
92
92
  :class="{'round': principal.roundAvatar}"
93
- :alt="t('principal.alt.avatar')"
94
93
  >
95
94
  </div>
96
95
  <div
@@ -99,9 +98,9 @@ export default {
99
98
  >
100
99
  <table>
101
100
  <tbody>
102
- <tr><th>{{ t('principal.name') }}: </th><td>{{ principal.name || principal.loginName }}</td></tr>
103
- <tr><th>{{ t('principal.loginName') }}: </th><td>{{ principal.loginName }}</td></tr>
104
- <tr><th>{{ t('principal.type') }}: </th><td>{{ principal.displayType }}</td></tr>
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>
105
104
  </tbody>
106
105
  </table>
107
106
  </div>
@@ -165,12 +164,6 @@ export default {
165
164
  grid-template-rows: auto math.div($size, 2);
166
165
  column-gap: 10px;
167
166
 
168
- th {
169
- text-align: left;
170
- font-weight: normal;
171
- padding-right: 10px;
172
- }
173
-
174
167
  &.showLabels {
175
168
  grid-template-areas:
176
169
  "avatar name";
@@ -51,9 +51,8 @@ describe('component: RoleDetailEdit', () => {
51
51
  const wrapper = mount(RoleDetailEdit, {
52
52
  props: {
53
53
  value: {
54
- rules: [{ verbs }],
55
- subtype: 'GLOBAL',
56
- metadata: { name: 'global-role-with-inherited' },
54
+ rules: [{ verbs }],
55
+ subtype: 'GLOBAL'
57
56
  },
58
57
  },
59
58
 
@@ -1,5 +1,4 @@
1
1
  <script>
2
- import { ref, watch, computed } from 'vue';
3
2
  import debounce from 'lodash/debounce';
4
3
  import { _EDIT, _VIEW } from '@shell/config/query-params';
5
4
  import { removeAt } from '@shell/utils/array';
@@ -95,87 +94,22 @@ export default {
95
94
  // we only want functions in the rules array
96
95
  validator: (rules) => rules.every((rule) => ['function'].includes(typeof rule))
97
96
  },
98
- a11yLabel: {
99
- type: String,
100
- default: '',
101
- },
102
- componentTestid: {
103
- type: String,
104
- default: 'array-list',
105
- }
106
97
  },
107
-
108
- setup(props, { emit }) {
109
- const input = (Array.isArray(props.value) ? props.value : []).slice();
110
- const rows = ref([]);
98
+ data() {
99
+ const input = (Array.isArray(this.value) ? this.value : []).slice();
100
+ const rows = [];
111
101
 
112
102
  for ( const value of input ) {
113
- rows.value.push({ value });
103
+ rows.push({ value });
114
104
  }
115
- if ( !rows.value.length && props.initialEmptyRow ) {
116
- const value = props.defaultAddValue ? clone(props.defaultAddValue) : '';
105
+ if ( !rows.length && this.initialEmptyRow ) {
106
+ const value = this.defaultAddValue ? clone(this.defaultAddValue) : '';
117
107
 
118
- rows.value.push({ value });
108
+ rows.push({ value });
119
109
  }
120
110
 
121
- const isView = computed(() => {
122
- return props.mode === _VIEW;
123
- });
124
-
125
- /**
126
- * Cleanup rows and emit input
127
- */
128
- const update = () => {
129
- if ( isView.value ) {
130
- return;
131
- }
132
- const out = [];
133
-
134
- for ( const row of rows.value ) {
135
- const trim = !props.valueMultiline && (typeof row.value === 'string');
136
- const value = trim ? row.value.trim() : row.value;
137
-
138
- if ( typeof value !== 'undefined' ) {
139
- out.push(value);
140
- }
141
- }
142
- emit('update:value', out);
143
- };
144
-
145
- const lastUpdateWasFromValue = ref(false);
146
- const queueUpdate = debounce(update, 50);
147
-
148
- watch(
149
- rows,
150
- () => {
151
- // lastUpdateWasFromValue is used to break a cycle where when rows are updated
152
- // this was called which then forced rows to updated again
153
- if (!lastUpdateWasFromValue.value) {
154
- queueUpdate();
155
- }
156
- lastUpdateWasFromValue.value = false;
157
- },
158
- { deep: true }
159
- );
160
-
161
- watch(
162
- () => props.value,
163
- () => {
164
- lastUpdateWasFromValue.value = true;
165
- rows.value = (props.value || []).map((v) => ({ value: v }));
166
- },
167
- { deep: true }
168
- );
169
-
170
- return {
171
- rows,
172
- lastUpdateWasFromValue,
173
- queueUpdate,
174
- isView,
175
- update,
176
- };
111
+ return { rows, lastUpdateWasFromValue: false };
177
112
  },
178
-
179
113
  computed: {
180
114
  _addLabel() {
181
115
  return this.addLabel || this.t('generic.add');
@@ -183,6 +117,10 @@ export default {
183
117
  _removeLabel() {
184
118
  return this.removeLabel || this.t('generic.remove');
185
119
  },
120
+
121
+ isView() {
122
+ return this.mode === _VIEW;
123
+ },
186
124
  showAdd() {
187
125
  return this.addAllowed;
188
126
  },
@@ -203,7 +141,29 @@ export default {
203
141
  return !this.valueMultiline && this.protip;
204
142
  }
205
143
  },
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
+ },
206
165
  created() {
166
+ this.queueUpdate = debounce(this.update, 50);
207
167
  },
208
168
  methods: {
209
169
  add() {
@@ -229,6 +189,26 @@ export default {
229
189
  this.queueUpdate();
230
190
  },
231
191
 
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
+
232
212
  /**
233
213
  * Handle paste event, e.g. split multiple lines in rows
234
214
  */
@@ -274,32 +254,7 @@ export default {
274
254
  </h3>
275
255
  </slot>
276
256
  </div>
277
- <div
278
- v-if="showAdd && !isView"
279
- class="footer"
280
- >
281
- <slot
282
- v-if="showAdd"
283
- name="add"
284
- :add="add"
285
- >
286
- <button
287
- type="button"
288
- class="btn role-primary add"
289
- :disabled="loading || disableAdd"
290
- :data-testid="`${componentTestid}-button`"
291
- :aria-label="_addLabel"
292
- role="button"
293
- @click="add()"
294
- >
295
- <i
296
- class="mr-5 icon"
297
- :class="loading ? ['icon-lg', 'icon-spinner','icon-spin']: [addIcon]"
298
- />
299
- {{ _addLabel }}
300
- </button>
301
- </slot>
302
- </div>
257
+
303
258
  <template v-if="rows.length">
304
259
  <div v-if="showHeader">
305
260
  <slot name="column-headers">
@@ -311,7 +266,7 @@ export default {
311
266
  <div
312
267
  v-for="(row, idx) in rows"
313
268
  :key="idx"
314
- :data-testid="`${componentTestid}-box${ idx }`"
269
+ :data-testid="`array-list-box${ idx }`"
315
270
  class="box"
316
271
  >
317
272
  <slot
@@ -335,7 +290,7 @@ export default {
335
290
  v-if="valueMultiline"
336
291
  ref="value"
337
292
  v-model:value="row.value"
338
- :data-testid="`${componentTestid}-textarea-${idx}`"
293
+ :data-testid="`textarea-${idx}`"
339
294
  :placeholder="valuePlaceholder"
340
295
  :mode="mode"
341
296
  :disabled="disabled"
@@ -346,7 +301,7 @@ export default {
346
301
  v-else-if="rules.length > 0"
347
302
  ref="value"
348
303
  v-model:value="row.value"
349
- :data-testid="`${componentTestid}-labeled-input-${idx}`"
304
+ :data-testid="`labeled-input-${idx}`"
350
305
  :placeholder="valuePlaceholder"
351
306
  :disabled="isView || disabled"
352
307
  :rules="rules"
@@ -358,10 +313,9 @@ export default {
358
313
  v-else
359
314
  ref="value"
360
315
  v-model="row.value"
361
- :data-testid="`${componentTestid}-input-${idx}`"
316
+ :data-testid="`input-${idx}`"
362
317
  :placeholder="valuePlaceholder"
363
318
  :disabled="isView || disabled"
364
- :aria-label="a11yLabel ? a11yLabel : undefined"
365
319
  @paste="onPaste(idx, $event)"
366
320
  >
367
321
  </slot>
@@ -381,9 +335,7 @@ export default {
381
335
  type="button"
382
336
  :disabled="isView"
383
337
  class="btn role-link"
384
- :data-testid="`${componentTestid}-remove-item-${idx}`"
385
- :aria-label="`${_removeLabel} ${idx + 1}`"
386
- role="button"
338
+ :data-testid="`remove-item-${idx}`"
387
339
  @click="remove(row, idx)"
388
340
  >
389
341
  {{ _removeLabel }}
@@ -402,7 +354,30 @@ export default {
402
354
  </div>
403
355
  </slot>
404
356
  </div>
405
-
357
+ <div
358
+ v-if="showAdd && !isView"
359
+ class="footer mt-20"
360
+ >
361
+ <slot
362
+ v-if="showAdd"
363
+ name="add"
364
+ :add="add"
365
+ >
366
+ <button
367
+ type="button"
368
+ class="btn role-tertiary add"
369
+ :disabled="loading || disableAdd"
370
+ data-testid="array-list-button"
371
+ @click="add()"
372
+ >
373
+ <i
374
+ class="mr-5 icon"
375
+ :class="loading ? ['icon-lg', 'icon-spinner','icon-spin']: [addIcon]"
376
+ />
377
+ {{ _addLabel }}
378
+ </button>
379
+ </slot>
380
+ </div>
406
381
  </div>
407
382
  </template>
408
383
 
@@ -423,12 +398,7 @@ export default {
423
398
  .value {
424
399
  flex: 1;
425
400
  INPUT {
426
- height: $input-height;
427
- border: solid var(--border-width) var(--input-border);
428
- padding: 4px 11px;
429
- &:hover{
430
- box-shadow: 0 4px 6px 0 var(--input-border-box-shadow);
431
- }
401
+ height: $unlabeled-input-height;
432
402
  }
433
403
  }
434
404
  }
@@ -436,7 +406,6 @@ export default {
436
406
  text-align: right;
437
407
  }
438
408
  .footer {
439
- margin-bottom: 24px;
440
409
  .protip {
441
410
  float: right;
442
411
  padding: 5px 0;
@@ -446,11 +415,4 @@ export default {
446
415
  .required {
447
416
  color: var(--error);
448
417
  }
449
- :deep() .labeled-input.compact-input{
450
- padding: 0px;
451
- }
452
- :deep() .box{
453
- margin-bottom: 10px;
454
- }
455
-
456
418
  </style>
@@ -127,16 +127,8 @@ export default {
127
127
  & > .remove {
128
128
  position: absolute;
129
129
 
130
- top: 10px;
131
- right: 10px;
132
- & > .close{
133
- width: 32px;
134
- min-width: 32px !important;
135
- color: var(--body-text);
136
- &:hover{
137
- color: var(--link);
138
- }
139
- }
130
+ top: 0;
131
+ right: 0;
140
132
  }
141
133
 
142
134
  & > .info-box {
@@ -43,14 +43,23 @@ 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
+
46
55
  return {
47
- args: this.value.args,
48
- command: this.value.command,
56
+ args,
57
+ command,
49
58
  commandOptions: ['No', 'Once', 'Yes'],
50
- stdin: this.value.stdin || false,
51
- stdinOnce: this.value.stdin || false,
52
- tty: this.value.tty || false,
53
- workingDir: this.value.workingDir,
59
+ stdin,
60
+ stdinOnce,
61
+ tty,
62
+ workingDir,
54
63
  };
55
64
  },
56
65
 
@@ -39,10 +39,14 @@ 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
+
42
48
  return {
43
- env: [],
44
- envFrom: [],
45
- allEnv: [],
49
+ env, envFrom, allEnv
46
50
  };
47
51
  },
48
52
 
@@ -59,18 +63,7 @@ export default {
59
63
  }
60
64
  }
61
65
  },
62
-
63
66
  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
-
74
67
  this.queueUpdate = debounce(this.update, 500);
75
68
  },
76
69
 
@@ -114,8 +107,7 @@ export default {
114
107
  <div :style="{'width':'100%'}">
115
108
  <div
116
109
  v-for="(row, i) in allEnv"
117
- :key="row.id"
118
- :data-testid="`env-var-row-${i}`"
110
+ :key="i"
119
111
  >
120
112
  <ValueFromResource
121
113
  v-model:value="row.value"