@rancher/shell 3.0.2-rc.4 → 3.0.2-rc.6

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 (212) hide show
  1. package/assets/images/providers/nutanix.svg +12 -1
  2. package/assets/styles/base/_basic.scss +2 -1
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +2 -0
  5. package/assets/styles/global/_labeled-input.scss +5 -13
  6. package/assets/styles/global/_layout.scss +1 -0
  7. package/assets/styles/global/_select.scss +5 -0
  8. package/assets/styles/themes/_dark.scss +1 -3
  9. package/assets/styles/themes/_light.scss +5 -1
  10. package/assets/translations/en-us.yaml +142 -22
  11. package/assets/translations/zh-hans.yaml +0 -3
  12. package/cloud-credential/azure.vue +1 -1
  13. package/components/ActionMenuShell.vue +105 -0
  14. package/components/AppModal.vue +2 -2
  15. package/components/AsyncButton.vue +2 -0
  16. package/components/ButtonGroup.vue +15 -1
  17. package/components/ButtonMultiAction.vue +5 -1
  18. package/components/ClusterBadge.vue +1 -0
  19. package/components/ClusterIconMenu.vue +3 -0
  20. package/components/ClusterProviderIcon.vue +14 -1
  21. package/components/CodeMirror.vue +96 -5
  22. package/components/Collapse.vue +16 -3
  23. package/components/CopyToClipboardText.vue +3 -1
  24. package/components/CruResource.vue +9 -0
  25. package/components/CruResourceFooter.vue +1 -1
  26. package/components/ExplorerMembers.vue +2 -1
  27. package/components/ExplorerProjectsNamespaces.vue +7 -0
  28. package/components/Import.vue +14 -1
  29. package/components/LandingPagePreference.vue +4 -2
  30. package/components/PodSecurityAdmission.vue +8 -6
  31. package/components/PromptChangePassword.vue +1 -0
  32. package/components/PromptRemove.vue +23 -21
  33. package/components/ResourceDetail/Masthead.vue +30 -11
  34. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  35. package/components/ResourceDetail/index.vue +6 -0
  36. package/components/ResourceTable.vue +6 -14
  37. package/components/ResourceYaml.vue +1 -0
  38. package/components/SelectIconGrid.vue +2 -0
  39. package/components/Setting.vue +115 -0
  40. package/components/SortableTable/THead.vue +2 -0
  41. package/components/SortableTable/index.vue +38 -14
  42. package/components/Tabbed/index.vue +16 -15
  43. package/components/Wizard.vue +108 -104
  44. package/components/YamlEditor.vue +12 -2
  45. package/components/__tests__/Collapse.test.ts +2 -2
  46. package/components/auth/Principal.vue +29 -17
  47. package/components/auth/__tests__/Principal.test.ts +40 -0
  48. package/components/auth/login/ldap.vue +7 -0
  49. package/components/fleet/FleetBundles.vue +1 -1
  50. package/components/fleet/FleetRepos.vue +1 -1
  51. package/components/fleet/FleetResources.vue +0 -2
  52. package/components/fleet/FleetSummary.vue +60 -65
  53. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  54. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  55. package/components/form/ArrayList.vue +6 -2
  56. package/components/form/ColorInput.vue +1 -0
  57. package/components/form/KeyValue.vue +11 -12
  58. package/components/form/LabeledSelect.vue +16 -3
  59. package/components/form/Labels.vue +8 -1
  60. package/components/form/Members/MembershipEditor.vue +230 -222
  61. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  62. package/components/form/Password.vue +3 -0
  63. package/components/form/ProjectMemberEditor.vue +6 -3
  64. package/components/form/ResourceTabs/index.vue +15 -13
  65. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  66. package/components/form/SchedulingCustomization.vue +85 -0
  67. package/components/form/Select.vue +4 -2
  68. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  69. package/components/form/UnitInput.vue +1 -2
  70. package/components/form/__tests__/ArrayList.test.ts +9 -6
  71. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  72. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  73. package/components/formatter/LiveDate.vue +3 -1
  74. package/components/formatter/ServiceType.vue +12 -4
  75. package/components/formatter/WorkloadHealthScale.vue +2 -1
  76. package/components/nav/Header.vue +35 -2
  77. package/components/nav/HeaderPageActionMenu.vue +11 -40
  78. package/components/nav/Jump.vue +8 -2
  79. package/components/nav/NamespaceFilter.vue +5 -4
  80. package/components/nav/Pinned.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +5 -5
  82. package/components/nav/WindowManager/ContainerLogs.vue +97 -49
  83. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  84. package/components/nav/WindowManager/index.vue +85 -6
  85. package/components/templates/default.vue +2 -47
  86. package/config/features.js +1 -0
  87. package/config/home-links.js +1 -1
  88. package/config/labels-annotations.js +11 -1
  89. package/config/router/navigation-guards/index.js +2 -1
  90. package/config/router/navigation-guards/record-last-route.js +24 -0
  91. package/config/settings.ts +66 -98
  92. package/config/version.js +1 -1
  93. package/core/types-provisioning.ts +7 -0
  94. package/detail/fleet.cattle.io.bundle.vue +7 -0
  95. package/detail/fleet.cattle.io.cluster.vue +0 -3
  96. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  97. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  98. package/dialog/DeactivateDriverDialog.vue +5 -5
  99. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  100. package/directives/strip-html-aria-label.js +19 -0
  101. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  102. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  103. package/edit/auth/__tests__/oidc.test.ts +26 -9
  104. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  105. package/edit/auth/ldap/config.vue +67 -89
  106. package/edit/auth/oidc.vue +15 -1
  107. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  108. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  109. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  110. package/edit/logging-flow/Match.vue +0 -21
  111. package/edit/management.cattle.io.project.vue +1 -1
  112. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  113. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  114. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  115. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  116. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  117. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  118. package/edit/provisioning.cattle.io.cluster/index.vue +39 -39
  119. package/edit/provisioning.cattle.io.cluster/rke2.vue +63 -12
  120. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  121. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  122. package/edit/resources.cattle.io.backup.vue +150 -15
  123. package/edit/secret/__tests__/ssh.test.ts +79 -0
  124. package/edit/secret/ssh.vue +7 -1
  125. package/edit/workload/Job.vue +2 -2
  126. package/edit/workload/index.vue +3 -1
  127. package/initialize/install-directives.js +2 -0
  128. package/initialize/install-plugins.js +6 -1
  129. package/list/catalog.cattle.io.app.vue +21 -4
  130. package/list/fleet.cattle.io.bundle.vue +1 -1
  131. package/list/management.cattle.io.setting.vue +34 -129
  132. package/list/provisioning.cattle.io.cluster.vue +11 -3
  133. package/machine-config/vmwarevsphere.vue +15 -8
  134. package/mixins/__tests__/auth-config.test.ts +74 -0
  135. package/mixins/__tests__/chart.test.ts +5 -4
  136. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  137. package/mixins/auth-config.js +9 -1
  138. package/mixins/chart.js +2 -2
  139. package/mixins/create-edit-view/impl.js +4 -1
  140. package/mixins/vue-select-overrides.js +10 -0
  141. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  142. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  143. package/models/__tests__/secret.test.ts +56 -13
  144. package/models/catalog.cattle.io.app.js +112 -37
  145. package/models/cluster.js +11 -0
  146. package/models/fleet.cattle.io.bundle.js +40 -2
  147. package/models/fleet.cattle.io.gitrepo.js +169 -109
  148. package/models/management.cattle.io.fleetworkspace.js +4 -0
  149. package/models/management.cattle.io.kontainerdriver.js +7 -0
  150. package/models/nodedriver.js +4 -1
  151. package/models/provisioning.cattle.io.cluster.js +24 -0
  152. package/models/secret.js +1 -1
  153. package/package.json +4 -4
  154. package/pages/auth/login.vue +4 -2
  155. package/pages/auth/verify.vue +11 -1
  156. package/pages/c/_cluster/apps/charts/chart.vue +1 -0
  157. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  158. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  159. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  160. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  161. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  162. package/pages/c/_cluster/explorer/index.vue +33 -35
  163. package/pages/c/_cluster/explorer/tools/index.vue +17 -4
  164. package/pages/c/_cluster/fleet/index.vue +0 -5
  165. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  166. package/pages/c/_cluster/settings/performance.vue +52 -53
  167. package/pages/c/_cluster/uiplugins/index.vue +21 -22
  168. package/pages/home.vue +17 -12
  169. package/pages/prefs.vue +5 -1
  170. package/plugins/shortkey.js +10 -1
  171. package/plugins/steve/steve-pagination-utils.ts +58 -8
  172. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  173. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  174. package/promptRemove/management.cattle.io.project.vue +2 -8
  175. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  176. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  177. package/promptRemove/pod.vue +7 -28
  178. package/rancher-components/Card/Card.vue +9 -1
  179. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  180. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  181. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  182. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  183. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  184. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  185. package/rancher-components/RcButton/RcButton.vue +14 -9
  186. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  187. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -14
  188. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  189. package/rancher-components/RcDropdown/index.ts +1 -0
  190. package/rancher-components/RcDropdown/types.ts +27 -0
  191. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  192. package/scripts/typegen.sh +1 -0
  193. package/store/__tests__/auth.test.ts +120 -0
  194. package/store/action-menu.js +13 -3
  195. package/store/auth.js +14 -9
  196. package/store/catalog.js +14 -7
  197. package/store/features.js +1 -0
  198. package/store/prefs.js +9 -28
  199. package/store/type-map.utils.ts +4 -0
  200. package/types/resources/settings.d.ts +27 -20
  201. package/types/shell/index.d.ts +18 -2
  202. package/utils/__tests__/array.test.ts +13 -1
  203. package/utils/__tests__/string.test.ts +80 -1
  204. package/utils/array.ts +13 -0
  205. package/utils/auth.js +4 -0
  206. package/utils/cluster.js +1 -1
  207. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  208. package/utils/pagination-utils.ts +15 -2
  209. package/utils/string.js +31 -7
  210. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  211. package/utils/validators/formRules/index.ts +16 -0
  212. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -229,17 +229,17 @@ export default defineComponent({
229
229
  @update:value="updateLabels()"
230
230
  />
231
231
  <p v-else>
232
- <t :k="`podSecurityAdmission.labels.${level}`" />
232
+ <t
233
+ :id="`psa-label-for-level-${ level }`"
234
+ :k="`podSecurityAdmission.labels.${level}`"
235
+ />
233
236
  </p>
234
237
  </span>
235
238
 
236
- <span
237
- class="
238
- col
239
- span-4"
240
- >
239
+ <span class="col span-4">
241
240
  <LabeledSelect
242
241
  v-model:value="psaControl.level"
242
+ :aria-labelledby="`psa-label-for-level-${ level }`"
243
243
  :data-testid="componentTestid + '--psaControl-' + i + '-level'"
244
244
  :disabled="isPsaControlDisabled(psaControl.active)"
245
245
  :options="options"
@@ -256,6 +256,7 @@ export default defineComponent({
256
256
  :options="options"
257
257
  :placeholder="t('podSecurityAdmission.version.placeholder', { psaControl: mode })"
258
258
  :mode="mode"
259
+ :aria-label="`${t(`podSecurityAdmission.labels.${level}`)} - ${t('podSecurityAdmission.version.placeholder', { psaControl: mode })}`"
259
260
  @update:value="updateLabels()"
260
261
  />
261
262
  </span>
@@ -295,6 +296,7 @@ export default defineComponent({
295
296
  :options="options"
296
297
  :placeholder="t('podSecurityAdmission.exemptions.placeholder', { psaExemptionsControl: dimension })"
297
298
  :mode="mode"
299
+ :aria-label="`${t(`podSecurityAdmission.labels.${ dimension }`)} - ${t('podSecurityAdmission.exemptions.placeholder', { psaExemptionsControl: dimension })}`"
298
300
  @update:value="updateExemptions()"
299
301
  />
300
302
  </span>
@@ -40,6 +40,7 @@ export default {
40
40
  <app-modal
41
41
  v-if="showModal"
42
42
  custom-class="change-password-modal"
43
+ data-testid="change-password__modal"
43
44
  name="password-modal"
44
45
  :width="500"
45
46
  :height="465"
@@ -1,4 +1,5 @@
1
1
  <script>
2
+ import { shallowRef } from 'vue';
2
3
  import { mapState, mapGetters } from 'vuex';
3
4
  import { get, isEmpty } from '@shell/utils/object';
4
5
  import { escapeHtml, resourceNames } from '@shell/utils/string';
@@ -38,7 +39,7 @@ export default {
38
39
  error: '',
39
40
  warning: '',
40
41
  preventDelete: false,
41
- removeComponent: this.$store.getters['type-map/importCustomPromptRemove'](resource),
42
+ removeComponent: shallowRef(this.$store.getters['type-map/importCustomPromptRemove'](resource)),
42
43
  chartsToRemoveIsApp: false,
43
44
  chartsDeleteCrd: false,
44
45
  showModal: false,
@@ -46,7 +47,7 @@ export default {
46
47
  },
47
48
  computed: {
48
49
  names() {
49
- return this.toRemove.map((obj) => obj.nameDisplay).slice(0, 5);
50
+ return this.toRemove.map((obj) => obj.nameDisplay);
50
51
  },
51
52
 
52
53
  nameToMatchPosition() {
@@ -92,12 +93,6 @@ export default {
92
93
  return first?.confirmRemove;
93
94
  },
94
95
 
95
- plusMore() {
96
- const remaining = this.toRemove.length - this.names.length;
97
-
98
- return this.t('promptRemove.andOthers', { count: remaining });
99
- },
100
-
101
96
  // if the current route ends with the ID of the resource being deleted, whatever page this is wont be valid after successful deletion: navigate away
102
97
  doneLocation() {
103
98
  // if deleting more than one resource, this is happening in list view and shouldn't redirect anywhere
@@ -167,7 +162,7 @@ export default {
167
162
  if (show) {
168
163
  const selected = this.toRemove[0];
169
164
 
170
- if (this.currentRouter?.currentRoute?.name === 'c-cluster-explorer-tools' &&
165
+ if (this.currentRouter?.currentRoute?.value?.name === 'c-cluster-explorer-tools' &&
171
166
  selected.type === CATALOG.APP &&
172
167
  selected.spec?.chart?.metadata?.annotations[CATALOG_ANNOTATIONS.AUTO_INSTALL]) {
173
168
  this.chartsToRemoveIsApp = true;
@@ -183,7 +178,7 @@ export default {
183
178
 
184
179
  this.hasCustomRemove = this.$store.getters['type-map/hasCustomPromptRemove'](resource);
185
180
 
186
- this.removeComponent = this.$store.getters['type-map/importCustomPromptRemove'](resource);
181
+ this.removeComponent = shallowRef(this.$store.getters['type-map/importCustomPromptRemove'](resource));
187
182
  } else {
188
183
  this.showModal = false;
189
184
  }
@@ -359,7 +354,7 @@ export default {
359
354
  <div class="mb-10">
360
355
  <template v-if="!hasCustomRemove">
361
356
  {{ t('promptRemove.attemptingToRemove', { type }) }} <span
362
- v-clean-html="resourceNames(names, plusMore, t)"
357
+ v-clean-html="resourceNames(names, t)"
363
358
  />
364
359
  </template>
365
360
 
@@ -368,7 +363,7 @@ export default {
368
363
  v-if="hasCustomRemove"
369
364
  ref="customPrompt"
370
365
  v-model:value="toRemove"
371
- v-bind="_data"
366
+ v-bind="$data"
372
367
  :close="close"
373
368
  :needs-confirm="needsConfirm"
374
369
  :value="toRemove"
@@ -394,6 +389,7 @@ export default {
394
389
  v-focus
395
390
  :data-testid="componentTestid + '-input'"
396
391
  type="text"
392
+ :aria-label="t('promptRemove.confirmName', { nameToMatch: escapeHtml(nameToMatch) })"
397
393
  >
398
394
  <div class="text-warning mb-10 mt-10">
399
395
  {{ warning }}
@@ -407,22 +403,28 @@ export default {
407
403
  >
408
404
  {{ protip }}
409
405
  </div>
410
- <Checkbox
411
- v-if="chartsToRemoveIsApp"
412
- v-model:value="chartsDeleteCrd"
413
- label-key="promptRemoveApp.removeCrd"
414
- class="mt-10 type"
415
- @update:value="chartAddCrdToRemove"
416
- />
417
406
  </LabeledInput>
418
407
  <div v-else-if="!hasCustomRemove">
419
- <div class="text-warning mb-10 mt-10">
408
+ <div
409
+ v-if="warning"
410
+ class="text-warning mb-10 mt-10"
411
+ >
420
412
  {{ warning }}
421
413
  </div>
422
- <div class="text-error mb-10 mt-10">
414
+ <div
415
+ v-if="error"
416
+ class="text-error mb-10 mt-10"
417
+ >
423
418
  {{ error }}
424
419
  </div>
425
420
  </div>
421
+ <Checkbox
422
+ v-if="chartsToRemoveIsApp"
423
+ v-model:value="chartsDeleteCrd"
424
+ label-key="promptRemoveApp.removeCrd"
425
+ class="mt-10 type"
426
+ @update:value="chartAddCrdToRemove"
427
+ />
426
428
  </template>
427
429
  <template #actions>
428
430
  <button
@@ -504,10 +504,32 @@ export default {
504
504
  {{ namespace }}
505
505
  </span>
506
506
  </span>
507
- <span v-if="parent.showAge">{{ t("resourceDetail.masthead.age") }}: <LiveDate
508
- class="live-date"
509
- :value="value.creationTimestamp"
510
- /></span>
507
+ <span v-if="parent.showAge">
508
+ {{ t("resourceDetail.masthead.age") }}:
509
+ <LiveDate
510
+ class="live-date"
511
+ :value="value.creationTimestamp"
512
+ />
513
+ </span>
514
+ <span
515
+ v-if="value.showCreatedBy"
516
+ data-testid="masthead-subheader-createdBy"
517
+ >
518
+ {{ t("resourceDetail.masthead.createdBy") }}:
519
+ <router-link
520
+ v-if="value.createdBy.location"
521
+ :to="value.createdBy.location"
522
+ data-testid="masthead-subheader-createdBy-link"
523
+ >
524
+ {{ value.createdBy.displayName }}
525
+ </router-link>
526
+ <span
527
+ v-else
528
+ data-testid="masthead-subheader-createdBy_plain-text"
529
+ >
530
+ {{ value.createdBy.displayName }}
531
+ </span>
532
+ </span>
511
533
  <span v-if="value.showPodRestarts">{{ t("resourceDetail.masthead.restartCount") }}:<span class="live-data"> {{ value.restartCount }}</span></span>
512
534
  </div>
513
535
  </div>
@@ -587,11 +609,8 @@ export default {
587
609
  }
588
610
 
589
611
  HEADER {
590
- margin: 0 0 0 -5px;
591
-
592
- .title {
593
- overflow-x: hidden;
594
- }
612
+ margin: 0;
613
+ grid-template-columns: minmax(0, 1fr) auto;
595
614
  }
596
615
 
597
616
  .primaryheader {
@@ -600,14 +619,13 @@ export default {
600
619
  align-items: center;
601
620
 
602
621
  h1 {
603
- margin: 0;
622
+ margin: 0 0 0 -5px;
604
623
  overflow-x: hidden;
605
624
  display: flex;
606
625
  flex-direction: row;
607
626
  align-items: center;
608
627
 
609
628
  .masthead-resource-title {
610
- padding: 0 8px;
611
629
  text-overflow: ellipsis;
612
630
  overflow: hidden;
613
631
  white-space: nowrap;
@@ -638,6 +656,7 @@ export default {
638
656
  }
639
657
 
640
658
  .masthead-state {
659
+ margin-left: 8px;
641
660
  font-size: initial;
642
661
  }
643
662
 
@@ -0,0 +1,61 @@
1
+ import { mount, RouterLinkStub } from '@vue/test-utils';
2
+ import { _VIEW } from '@shell/config/query-params';
3
+ import Masthead from '@shell/components/ResourceDetail/Masthead.vue';
4
+
5
+ const mockedStore = () => {
6
+ return {
7
+ getters: {
8
+ currentStore: () => 'current_store',
9
+ currentProduct: { inStore: 'cluster' },
10
+ isExplorer: false,
11
+ currentCluster: {},
12
+ 'type-map/labelFor': jest.fn(),
13
+ 'type-map/optionsFor': jest.fn(),
14
+ 'current_store/schemaFor': jest.fn(),
15
+ },
16
+ };
17
+ };
18
+
19
+ const requiredSetup = () => {
20
+ return {
21
+ stubs: {
22
+ 'router-link': RouterLinkStub,
23
+ LiveDate: true
24
+ },
25
+ mocks: { $store: mockedStore() }
26
+ };
27
+ };
28
+
29
+ describe('component: Masthead', () => {
30
+ it.each([
31
+ ['hidden', '', false, { displayName: 'admin', location: { id: 'resource-id' } }, false, false],
32
+ ['plain-text', 'admin', true, { displayName: 'admin', location: null }, false, true],
33
+ ['link', 'foo', true, { displayName: 'foo', location: { id: 'resource-id' } }, true, false],
34
+ ])('"Created By" should be %p, with text: %p', (
35
+ _,
36
+ text,
37
+ showCreatedBy,
38
+ createdBy,
39
+ showLink,
40
+ showPlainText,
41
+ ) => {
42
+ const wrapper = mount(Masthead, {
43
+ props: {
44
+ mode: _VIEW,
45
+ value: {
46
+ showCreatedBy,
47
+ createdBy,
48
+ },
49
+ },
50
+ global: { ...requiredSetup() }
51
+ });
52
+
53
+ const container = wrapper.find('[data-testid="masthead-subheader-createdBy"]');
54
+ const link = wrapper.find('[data-testid="masthead-subheader-createdBy-link"]');
55
+ const plainText = wrapper.find('[data-testid="masthead-subheader-createdBy_plain-text"]');
56
+
57
+ expect(link.exists()).toBe(showLink);
58
+ expect(plainText.exists()).toBe(showPlainText);
59
+ expect(showLink || showPlainText ? container.element.textContent : '').toContain(text);
60
+ });
61
+ });
@@ -71,6 +71,11 @@ export default {
71
71
  default: null,
72
72
  },
73
73
 
74
+ flexContent: {
75
+ type: Boolean,
76
+ default: false,
77
+ },
78
+
74
79
  /**
75
80
  * Inherited global identifier prefix for tests
76
81
  * Define a term based on the parent component to avoid conflicts on multiple components
@@ -484,6 +489,7 @@ export default {
484
489
  :initial-value="initialModel"
485
490
  :live-value="liveModel"
486
491
  :real-mode="realMode"
492
+ :class="{'flex-content': flexContent}"
487
493
  @update:value="$emit('input', $event)"
488
494
  @set-subtype="setSubtype"
489
495
  />
@@ -188,20 +188,11 @@ export default {
188
188
  type: Number,
189
189
  default: null, // Default comes from the user preference
190
190
  },
191
- },
192
-
193
- mounted() {
194
- /**
195
- * v-shortkey prevents the event's propagation:
196
- * https://github.com/fgr-araujo/vue-shortkey/blob/55d802ea305cadcc2ea970b55a3b8b86c7b44c05/src/index.js#L156-L157
197
- *
198
- * 'Enter' key press is handled via event listener in order to allow the event propagation
199
- */
200
- window.addEventListener('keyup', this.handleEnterKeyPress);
201
- },
202
191
 
203
- beforeUnmount() {
204
- window.removeEventListener('keyup', this.handleEnterKeyPress);
192
+ hideGroupingControls: {
193
+ type: Boolean,
194
+ default: false
195
+ }
205
196
  },
206
197
 
207
198
  data() {
@@ -602,9 +593,10 @@ export default {
602
593
  :mandatory-sort="_mandatorySort"
603
594
  @clickedActionButton="handleActionButtonClick"
604
595
  @group-value-change="group = $event"
596
+ @enter="handleEnterKeyPress"
605
597
  >
606
598
  <template
607
- v-if="showGrouping"
599
+ v-if="!hideGroupingControls && showGrouping"
608
600
  #header-middle
609
601
  >
610
602
  <slot name="more-header-middle" />
@@ -298,6 +298,7 @@ export default {
298
298
  <YamlEditor
299
299
  ref="yamleditor"
300
300
  v-model:value="currentYaml"
301
+ :mode="mode"
301
302
  :initial-yaml-values="initialYaml"
302
303
  class="yaml-editor flex-content"
303
304
  :editor-mode="editorMode"
@@ -148,10 +148,12 @@ export default {
148
148
  <i
149
149
  v-if="r.iconClass"
150
150
  :class="r.iconClass"
151
+ :alt="t('catalog.charts.iconAlt', { app: get(r, nameField) })"
151
152
  />
152
153
  <LazyImage
153
154
  v-else
154
155
  :src="get(r, iconField)"
156
+ :alt="t('catalog.charts.iconAlt', { app: get(r, nameField) })"
155
157
  />
156
158
  </div>
157
159
  <h4 class="name">
@@ -0,0 +1,115 @@
1
+ <script>
2
+ import ActionMenu from '@shell/components/ActionMenuShell.vue';
3
+ import { mapGetters } from 'vuex';
4
+ export default {
5
+ name: 'Setting',
6
+ components: { ActionMenu },
7
+ props: {
8
+ value: {
9
+ type: Object,
10
+ required: true,
11
+ },
12
+ },
13
+ computed: {
14
+ ...mapGetters({ t: 'i18n/t' }),
15
+ ...mapGetters({ options: 'action-menu/optionsArray' }),
16
+ },
17
+ };
18
+ </script>
19
+
20
+ <template>
21
+ <div
22
+ class="advanced-setting mb-20"
23
+ :data-testid="`advanced-setting__option-${value.id}`"
24
+ >
25
+ <div class="header">
26
+ <div class="title">
27
+ <h1>
28
+ {{ value.id }}
29
+ <span
30
+ v-if="value.fromEnv"
31
+ class="modified"
32
+ >{{ t('advancedSettings.setEnv') }}</span>
33
+ <span
34
+ v-else-if="value.customized"
35
+ class="modified"
36
+ >{{ t('advancedSettings.modified') }}</span>
37
+ </h1>
38
+ <h2>{{ t(`advancedSettings.descriptions.${value.id}`) }}</h2>
39
+ </div>
40
+ <div
41
+ v-if="value.hasActions"
42
+ class="action"
43
+ >
44
+ <action-menu
45
+ :resource="value.data"
46
+ :button-aria-label="t('advancedSettings.edit.label')"
47
+ data-testid="action-button"
48
+ button-role="tertiary"
49
+ />
50
+ </div>
51
+ </div>
52
+ <div value>
53
+ <div v-if="value.canHide">
54
+ <button
55
+ class="btn btn-sm role-primary"
56
+ role="button"
57
+ :aria-label="t('advancedSettings.hideShow')"
58
+ @click="value.hide = !value.hide"
59
+ >
60
+ {{ value.hide ? t('advancedSettings.show') : t('advancedSettings.hide') }} {{ value.id }}
61
+ </button>
62
+ </div>
63
+ <div
64
+ v-show="!value.canHide || (value.canHide && !value.hide)"
65
+ class="settings-value"
66
+ >
67
+ <pre v-if="value.kind === 'json'">{{ value.json }}</pre>
68
+ <pre v-else-if="value.kind === 'multiline'">{{ value.data.value || value.data.default }}</pre>
69
+ <pre v-else-if="value.kind === 'enum'">{{ t(value.enum) }}</pre>
70
+ <pre v-else-if="value.data.value || value.data.default">{{ value.data.value || value.data.default }}</pre>
71
+ <pre
72
+ v-else
73
+ class="text-muted"
74
+ >&lt;{{ t('advancedSettings.none') }}&gt;</pre>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </template>
79
+
80
+ <style lang='scss' scoped>
81
+ .settings-value pre {
82
+ margin: 0;
83
+ }
84
+ .advanced-setting {
85
+ border: 1px solid var(--border);
86
+ padding: 20px;
87
+ border-radius: var(--border-radius);
88
+
89
+ h1 {
90
+ font-size: 14px;
91
+ }
92
+ h2 {
93
+ font-size: 12px;
94
+ margin-bottom: 0;
95
+ opacity: 0.8;
96
+ }
97
+ }
98
+
99
+ .header {
100
+ display: flex;
101
+ margin-bottom: 20px;
102
+ }
103
+
104
+ .title {
105
+ flex: 1;
106
+ }
107
+
108
+ .modified {
109
+ margin-left: 10px;
110
+ border: 1px solid var(--primary);
111
+ border-radius: 5px;
112
+ padding: 2px 10px;
113
+ font-size: 12px;
114
+ }
115
+ </style>
@@ -230,6 +230,7 @@ export default {
230
230
  data-testid="sortable-table_check_select_all"
231
231
  :indeterminate="isIndeterminate"
232
232
  :disabled="noRows || noResults"
233
+ :alternate-label="t('sortableTable.genericGroupCheckbox')"
233
234
  />
234
235
  </th>
235
236
  <th
@@ -269,6 +270,7 @@ export default {
269
270
  <div
270
271
  v-if="col.sort"
271
272
  class="sort"
273
+ aria-hidden="true"
272
274
  >
273
275
  <i
274
276
  v-show="hasAdvancedFiltering && !col.isFilter"
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
- import { defineAsyncComponent } from 'vue';
3
+ import { defineAsyncComponent, useTemplateRef, 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';
@@ -23,6 +23,7 @@ import LabeledSelect from '@shell/components/form/LabeledSelect';
23
23
  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
+ import ActionMenu from '@shell/components/ActionMenuShell.vue';
26
27
 
27
28
  // Uncomment for table performance debugging
28
29
  // import tableDebug from './debug';
@@ -42,7 +43,14 @@ import ButtonMultiAction from '@shell/components/ButtonMultiAction.vue';
42
43
  export default {
43
44
  name: 'SortableTable',
44
45
 
45
- emits: ['clickedActionButton', 'pagination-changed', 'group-value-change', 'selection', 'rowClick'],
46
+ emits: [
47
+ 'clickedActionButton',
48
+ 'pagination-changed',
49
+ 'group-value-change',
50
+ 'selection',
51
+ 'rowClick',
52
+ 'enter',
53
+ ],
46
54
 
47
55
  components: {
48
56
  THead,
@@ -51,6 +59,7 @@ export default {
51
59
  ActionDropdown,
52
60
  LabeledSelect,
53
61
  ButtonMultiAction,
62
+ ActionMenu,
54
63
  },
55
64
  mixins: [
56
65
  filtering,
@@ -518,6 +527,23 @@ export default {
518
527
  immediate: true
519
528
  },
520
529
  },
530
+ setup(_props, { emit }) {
531
+ const table = useTemplateRef('table');
532
+
533
+ const handleEnterKey = (event) => {
534
+ if (event.key === 'Enter' && !event.target?.classList?.contains('checkbox-custom')) {
535
+ emit('enter', event);
536
+ }
537
+ };
538
+
539
+ onMounted(() => {
540
+ table.value.addEventListener('keyup', handleEnterKey);
541
+ });
542
+
543
+ onBeforeUnmount(() => {
544
+ table.value.removeEventListener('keyup', handleEnterKey);
545
+ });
546
+ },
521
547
 
522
548
  created() {
523
549
  this.debouncedRefreshTableData = debounce(this.refreshTableData, 500);
@@ -1056,6 +1082,7 @@ export default {
1056
1082
  :disabled="!act.enabled"
1057
1083
  :data-testid="componentTestid + '-' + act.action"
1058
1084
  @click="applyTableAction(act, null, $event)"
1085
+ @keydown.enter.stop
1059
1086
  @mouseover="setBulkActionOfInterest(act)"
1060
1087
  @mouseleave="setBulkActionOfInterest(null)"
1061
1088
  >
@@ -1221,6 +1248,7 @@ export default {
1221
1248
  </div>
1222
1249
  </div>
1223
1250
  <table
1251
+ ref="table"
1224
1252
  class="sortable-table"
1225
1253
  :class="classObject"
1226
1254
  width="100%"
@@ -1299,6 +1327,7 @@ export default {
1299
1327
  v-for="(groupedRows) in displayRows"
1300
1328
  v-else
1301
1329
  :key="groupedRows.key"
1330
+ tabindex="-1"
1302
1331
  :class="{ group: groupBy }"
1303
1332
  >
1304
1333
  <slot
@@ -1350,11 +1379,13 @@ export default {
1350
1379
  class="row-check"
1351
1380
  align="middle"
1352
1381
  >
1353
- {{ row.mainRowKey }}<Checkbox
1382
+ {{ row.mainRowKey }}
1383
+ <Checkbox
1354
1384
  class="selection-checkbox"
1355
1385
  :data-node-id="row.key"
1356
1386
  :data-testid="componentTestid + '-' + i + '-checkbox'"
1357
1387
  :value="selectedRows.includes(row.row)"
1388
+ :alternate-label="t('sortableTable.genericRowCheckbox', { item: row && row.row ? row.row.id : '' })"
1358
1389
  />
1359
1390
  </td>
1360
1391
  <td
@@ -1440,22 +1471,16 @@ export default {
1440
1471
  </template>
1441
1472
  <td
1442
1473
  v-if="rowActions"
1443
- align="middle"
1444
1474
  >
1445
1475
  <slot
1446
1476
  name="row-actions"
1447
1477
  :row="row.row"
1478
+ :index="i"
1448
1479
  >
1449
- <ButtonMultiAction
1450
- :id="`actionButton+${i}+${(row.row && row.row.name) ? row.row.name : ''}`"
1451
- :ref="`actionButton${i}`"
1452
- aria-haspopup="true"
1453
- aria-expanded="false"
1480
+ <ActionMenu
1481
+ :resource="row.row"
1454
1482
  :data-testid="componentTestid + '-' + i + '-action-button'"
1455
- :borderless="true"
1456
- @click="handleActionButtonClick(i, $event)"
1457
- @keyup.enter="handleActionButtonClick(i, $event)"
1458
- @keyup.space="handleActionButtonClick(i, $event)"
1483
+ :button-aria-label="t('sortableTable.tableActionsLabel', { resource: row?.row?.id || '' })"
1459
1484
  />
1460
1485
  </slot>
1461
1486
  </td>
@@ -1753,7 +1778,6 @@ export default {
1753
1778
  min-width: 400px;
1754
1779
  border-radius: 5px 5px 0 0;
1755
1780
  outline: 1px solid var(--border);
1756
- overflow: hidden;
1757
1781
  background: var(--sortable-table-bg);
1758
1782
  border-radius: 4px;
1759
1783