@rancher/shell 3.0.2-rc.5 → 3.0.2

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 (219) 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 +4 -1
  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 +130 -23
  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 +9 -2
  17. package/components/ClusterBadge.vue +1 -0
  18. package/components/ClusterIconMenu.vue +3 -0
  19. package/components/ClusterProviderIcon.vue +14 -1
  20. package/components/CodeMirror.vue +96 -5
  21. package/components/Collapse.vue +16 -3
  22. package/components/CruResource.vue +9 -0
  23. package/components/CruResourceFooter.vue +1 -1
  24. package/components/ExplorerMembers.vue +2 -1
  25. package/components/FixedBanner.vue +19 -12
  26. package/components/Import.vue +14 -1
  27. package/components/LandingPagePreference.vue +4 -2
  28. package/components/PodSecurityAdmission.vue +8 -6
  29. package/components/PromptChangePassword.vue +1 -0
  30. package/components/PromptRemove.vue +23 -21
  31. package/components/ResourceDetail/Masthead.vue +30 -11
  32. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  33. package/components/ResourceDetail/index.vue +6 -0
  34. package/components/ResourceTable.vue +6 -1
  35. package/components/ResourceYaml.vue +1 -0
  36. package/components/Setting.vue +115 -0
  37. package/components/SortableTable/THead.vue +2 -0
  38. package/components/SortableTable/index.vue +7 -12
  39. package/components/StatusBadge.vue +71 -0
  40. package/components/Tabbed/index.vue +16 -15
  41. package/components/Wizard.vue +108 -104
  42. package/components/YamlEditor.vue +12 -2
  43. package/components/__tests__/Collapse.test.ts +2 -2
  44. package/components/__tests__/FixedBanner.test.ts +3 -3
  45. package/components/auth/Principal.vue +29 -17
  46. package/components/auth/__tests__/Principal.test.ts +40 -0
  47. package/components/auth/login/ldap.vue +7 -0
  48. package/components/fleet/FleetBundles.vue +1 -1
  49. package/components/fleet/FleetRepos.vue +1 -1
  50. package/components/fleet/FleetResources.vue +0 -2
  51. package/components/fleet/FleetSummary.vue +60 -65
  52. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  53. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  54. package/components/form/ArrayList.vue +6 -2
  55. package/components/form/ColorInput.vue +1 -0
  56. package/components/form/KeyValue.vue +11 -12
  57. package/components/form/LabeledSelect.vue +15 -3
  58. package/components/form/Labels.vue +8 -1
  59. package/components/form/Members/MembershipEditor.vue +230 -222
  60. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  61. package/components/form/Password.vue +3 -0
  62. package/components/form/ProjectMemberEditor.vue +6 -3
  63. package/components/form/ResourceTabs/index.vue +15 -13
  64. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  65. package/components/form/SchedulingCustomization.vue +85 -0
  66. package/components/form/Select.vue +3 -2
  67. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  68. package/components/form/UnitInput.vue +3 -4
  69. package/components/form/__tests__/ArrayList.test.ts +9 -6
  70. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  71. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  72. package/components/form/__tests__/UnitInput.test.ts +4 -5
  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/TopLevelMenu.vue +1 -12
  83. package/components/nav/WindowManager/ContainerLogs.vue +96 -58
  84. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  85. package/components/nav/WindowManager/index.vue +74 -6
  86. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  87. package/components/templates/default.vue +2 -47
  88. package/config/features.js +1 -0
  89. package/config/labels-annotations.js +11 -1
  90. package/config/router/navigation-guards/index.js +2 -1
  91. package/config/router/navigation-guards/record-last-route.js +24 -0
  92. package/config/settings.ts +66 -98
  93. package/config/version.js +1 -1
  94. package/core/types-provisioning.ts +7 -0
  95. package/detail/fleet.cattle.io.bundle.vue +7 -0
  96. package/detail/fleet.cattle.io.cluster.vue +0 -3
  97. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  98. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  99. package/dialog/DeactivateDriverDialog.vue +5 -5
  100. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  101. package/directives/strip-html-aria-label.js +19 -0
  102. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  103. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  104. package/edit/auth/__tests__/oidc.test.ts +60 -12
  105. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  106. package/edit/auth/ldap/config.vue +67 -89
  107. package/edit/auth/oidc.vue +16 -2
  108. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  109. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  110. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  111. package/edit/logging-flow/Match.vue +0 -21
  112. package/edit/management.cattle.io.project.vue +1 -1
  113. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  114. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  115. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  116. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  117. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +0 -2
  119. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  120. package/edit/provisioning.cattle.io.cluster/index.vue +28 -30
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +64 -13
  122. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  124. package/edit/resources.cattle.io.backup.vue +150 -15
  125. package/edit/secret/__tests__/ssh.test.ts +79 -0
  126. package/edit/secret/ssh.vue +7 -1
  127. package/edit/service.vue +0 -3
  128. package/edit/workload/Job.vue +8 -8
  129. package/edit/workload/__tests__/Job.test.ts +0 -1
  130. package/edit/workload/index.vue +3 -1
  131. package/initialize/install-directives.js +2 -0
  132. package/initialize/install-plugins.js +6 -1
  133. package/list/catalog.cattle.io.app.vue +21 -4
  134. package/list/fleet.cattle.io.bundle.vue +1 -1
  135. package/list/management.cattle.io.setting.vue +34 -132
  136. package/list/provisioning.cattle.io.cluster.vue +11 -3
  137. package/machine-config/vmwarevsphere.vue +15 -8
  138. package/mixins/__tests__/auth-config.test.ts +74 -0
  139. package/mixins/__tests__/chart.test.ts +5 -4
  140. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  141. package/mixins/auth-config.js +8 -0
  142. package/mixins/chart.js +2 -2
  143. package/mixins/create-edit-view/impl.js +4 -1
  144. package/mixins/vue-select-overrides.js +10 -0
  145. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  146. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  147. package/models/__tests__/secret.test.ts +56 -13
  148. package/models/catalog.cattle.io.app.js +112 -37
  149. package/models/cluster.js +11 -0
  150. package/models/fleet.cattle.io.bundle.js +40 -2
  151. package/models/fleet.cattle.io.gitrepo.js +169 -109
  152. package/models/management.cattle.io.fleetworkspace.js +4 -0
  153. package/models/management.cattle.io.kontainerdriver.js +7 -0
  154. package/models/nodedriver.js +4 -1
  155. package/models/provisioning.cattle.io.cluster.js +24 -0
  156. package/models/secret.js +1 -1
  157. package/package.json +5 -5
  158. package/pages/auth/login.vue +5 -11
  159. package/pages/auth/verify.vue +11 -1
  160. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  161. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  162. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  163. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  164. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  165. package/pages/c/_cluster/explorer/index.vue +33 -35
  166. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  167. package/pages/c/_cluster/fleet/index.vue +0 -5
  168. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  169. package/pages/c/_cluster/settings/performance.vue +52 -53
  170. package/pages/c/_cluster/uiplugins/index.vue +19 -22
  171. package/pages/home.vue +17 -12
  172. package/pages/prefs.vue +5 -1
  173. package/plugins/shortkey.js +10 -1
  174. package/plugins/steve/steve-pagination-utils.ts +58 -8
  175. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  176. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  177. package/promptRemove/management.cattle.io.project.vue +2 -8
  178. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  179. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  180. package/promptRemove/pod.vue +7 -28
  181. package/rancher-components/Card/Card.vue +9 -1
  182. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  183. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  184. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  185. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  186. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  187. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  188. package/rancher-components/RcButton/RcButton.vue +14 -9
  189. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  190. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -2
  191. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  192. package/rancher-components/RcDropdown/index.ts +1 -0
  193. package/rancher-components/RcDropdown/types.ts +27 -0
  194. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  195. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
  196. package/scripts/typegen.sh +1 -0
  197. package/store/__tests__/auth.test.ts +120 -0
  198. package/store/action-menu.js +13 -3
  199. package/store/auth.js +14 -9
  200. package/store/aws.js +9 -2
  201. package/store/catalog.js +14 -7
  202. package/store/features.js +1 -0
  203. package/store/prefs.js +9 -28
  204. package/store/type-map.utils.ts +4 -0
  205. package/types/resources/settings.d.ts +27 -20
  206. package/types/shell/index.d.ts +18 -12
  207. package/utils/__tests__/array.test.ts +13 -1
  208. package/utils/__tests__/string.test.ts +80 -1
  209. package/utils/array.ts +13 -0
  210. package/utils/auth.js +4 -0
  211. package/utils/banners.js +0 -45
  212. package/utils/cluster.js +1 -1
  213. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  214. package/utils/object.js +0 -3
  215. package/utils/pagination-utils.ts +15 -2
  216. package/utils/string.js +31 -7
  217. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  218. package/utils/validators/formRules/index.ts +16 -0
  219. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -0,0 +1,85 @@
1
+ <script>
2
+ import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
3
+ import Banner from '@components/Banner/Banner.vue';
4
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
5
+ export default {
6
+ name: 'SchedulingCustomization',
7
+ components: { Checkbox, Banner },
8
+ emits: ['scheduling-customization-changed'],
9
+ props: {
10
+ value: {
11
+ type: Object,
12
+ default: () => {},
13
+ },
14
+ mode: {
15
+ type: String,
16
+ default: _CREATE
17
+ },
18
+ feature: {
19
+ type: Boolean,
20
+ required: true
21
+ },
22
+ defaultPC: {
23
+ type: Object,
24
+ default: () => {},
25
+ },
26
+ defaultPDB: {
27
+ type: Object,
28
+ default: () => {},
29
+ }
30
+ },
31
+ data() {
32
+ return { applyGlobal: false };
33
+ },
34
+ computed: {
35
+ isEdit() {
36
+ return this.mode === _EDIT;
37
+ },
38
+ enabled() {
39
+ return !!this.value;
40
+ },
41
+ settingMissmatch() {
42
+ const pdbMaxUnavailableMismatch = this.value?.podDisruptionBudget.maxUnavailable !== this.defaultPDB.maxUnavailable;
43
+ const pdbMinAvailableMismatch = this.value?.podDisruptionBudget.minAvailable !== this.defaultPDB.minAvailable;
44
+ const pcValueMismatch = this.value?.priorityClass.value !== this.defaultPC.value;
45
+ const pcPreemptionMismatch = this.value?.priorityClass.preemptionPolicy !== this.defaultPC.preemptionPolicy;
46
+ const pdbMismatch = !!this.value?.podDisruptionBudget && ( pdbMaxUnavailableMismatch || pdbMinAvailableMismatch);
47
+ const pcMismatch = !!this.value?.priorityClass && ( pcValueMismatch || pcPreemptionMismatch);
48
+
49
+ return pdbMismatch || pcMismatch;
50
+ },
51
+ }
52
+ };
53
+ </script>
54
+
55
+ <template>
56
+ <div
57
+ class="mt-20"
58
+ >
59
+ <Checkbox
60
+ :value="enabled"
61
+ :mode="mode"
62
+ label-key="cluster.agentConfig.subGroups.schedulingCustomization.label"
63
+ descriptionKey="cluster.agentConfig.subGroups.schedulingCustomization.description"
64
+ data-testid="scheduling-customization-checkbox"
65
+ @update:value="$emit('scheduling-customization-changed', $event)"
66
+ >
67
+ <template
68
+ v-if="feature && isEdit && settingMissmatch"
69
+ #extra
70
+ >
71
+ <Banner
72
+ class="mt-10 mb-10"
73
+ color="info"
74
+ label-key="cluster.agentConfig.subGroups.schedulingCustomization.banner"
75
+ />
76
+ <Checkbox
77
+ :value="applyGlobal"
78
+ :mode="mode"
79
+ label-key="cluster.agentConfig.subGroups.schedulingCustomization.innerCheckbox"
80
+ @update:value="$emit('scheduling-customization-changed', feature)"
81
+ />
82
+ </template>
83
+ </Checkbox>
84
+ </div>
85
+ </template>
@@ -256,7 +256,8 @@ export default {
256
256
  }"
257
257
  :tabindex="disabled || isView ? -1 : 0"
258
258
  @click="focusSearch"
259
- @keydown.enter.down="focusSearch"
259
+ @keydown.enter="focusSearch"
260
+ @keydown.down.prevent="focusSearch"
260
261
  @keydown.space.prevent="focusSearch"
261
262
  >
262
263
  <v-select
@@ -281,7 +282,7 @@ export default {
281
282
  :modelValue="value != null ? value : ''"
282
283
  :dropdownShouldOpen="handleDropdownOpen"
283
284
  :tabindex="-1"
284
-
285
+ role="listbox"
285
286
  @update:modelValue="$emit('update:value', $event)"
286
287
  @search:blur="onBlur"
287
288
  @search:focus="onFocus"
@@ -147,7 +147,7 @@ export default {
147
147
 
148
148
  showSshKnownHosts: {
149
149
  type: Boolean,
150
- default: true,
150
+ default: false,
151
151
  },
152
152
  },
153
153
 
@@ -632,6 +632,7 @@ export default {
632
632
  >
633
633
  <SSHKnownHosts
634
634
  v-model:value="sshKnownHosts"
635
+ data-testid="auth-secret-known-ssh-hosts"
635
636
  :mode="mode"
636
637
  />
637
638
  </div>
@@ -206,7 +206,7 @@ export default {
206
206
  }
207
207
 
208
208
  if (this.outputModifier) {
209
- out = out === null ? null : `${ inputValue }${ this.unit }`;
209
+ out = out === null ? null : `${ parseInt(inputValue) }${ this.unit }`;
210
210
  } else if ( this.outputAs === 'string' ) {
211
211
  out = out === null ? '' : `${ inputValue }`;
212
212
  } else if (out) {
@@ -235,7 +235,7 @@ export default {
235
235
  :required="required"
236
236
  :placeholder="placeholder"
237
237
  :hide-arrows="hideArrows"
238
- @change="update($event.target.value)"
238
+ @update:value="update"
239
239
  @blur="update($event.target.value)"
240
240
  >
241
241
  <template #suffix>
@@ -252,7 +252,6 @@ export default {
252
252
 
253
253
  <style lang="scss" scoped>
254
254
  .addon.with-tooltip {
255
- position: relative;
256
- right: 30px;
255
+ padding-right: 42px;
257
256
  }
258
257
  </style>
@@ -47,19 +47,22 @@ describe('the ArrayList', () => {
47
47
  expect(arrayListBoxes).toHaveLength(2);
48
48
  });
49
49
 
50
- it('contracts when a delete button is clicked', async() => {
50
+ it('should remove the correct item, emit the removed item and the updated values', async() => {
51
51
  const wrapper = mount(ArrayList, {
52
52
  props: {
53
- value: ['string 1', 'string 2'],
53
+ value: ['string 0', 'string 1', 'string 2'],
54
54
  mode: _EDIT,
55
55
  },
56
56
  });
57
- const deleteButton = wrapper.get('[data-testid^="remove-item"]').element as HTMLElement;
58
57
 
59
- await deleteButton.click();
60
- const arrayListBoxes = wrapper.findAll('[data-testid^="array-list-box"]');
58
+ jest.useFakeTimers();
59
+ await (wrapper.get('[data-testid="remove-item-1"]').element as HTMLElement).click();
60
+ jest.advanceTimersByTime(50);
61
+ jest.useRealTimers();
61
62
 
62
- expect(arrayListBoxes).toHaveLength(1);
63
+ expect(wrapper.find('[data-testid="remove-item-2"]').exists()).toBe(false);
64
+ expect((wrapper.emitted('remove')![0][0] as any).row.value).toStrictEqual('string 1');
65
+ expect(wrapper.emitted('update:value')![0][0]).toStrictEqual(['string 0', 'string 2']);
63
66
  });
64
67
 
65
68
  it('add button is hidden in read-only mode', () => {
@@ -1,4 +1,5 @@
1
1
  import { mount } from '@vue/test-utils';
2
+ import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params';
2
3
  import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
3
4
  import { defineComponent } from 'vue';
4
5
 
@@ -137,6 +138,42 @@ describe('component: LabeledSelect', () => {
137
138
  });
138
139
  });
139
140
 
141
+ describe(`given {'disabled', 'mode', 'loading'} options`, () => {
142
+ it.each([
143
+ ['open', false, _EDIT, false, true],
144
+ ['open', false, _CREATE, false, true],
145
+ ['not open', false, _VIEW, false, false],
146
+ ['not open', false, _EDIT, true, false],
147
+ ['not open', true, _EDIT, false, false],
148
+ ])('should %p dropdown element if options are { disabled: %p, mode: %p, loading: %p }', async(
149
+ _,
150
+ disabled,
151
+ mode,
152
+ loading,
153
+ isOpen
154
+ ) => {
155
+ const label = 'Foo';
156
+ const value = 'foo';
157
+ const wrapper = mount(LabeledSelect, {
158
+ props: {
159
+ value,
160
+ options: [
161
+ { label, value },
162
+ ],
163
+ disabled,
164
+ mode,
165
+ loading
166
+ }
167
+ });
168
+
169
+ await wrapper.trigger('click');
170
+
171
+ const dropdownOpen = wrapper.find('.vs--open');
172
+
173
+ expect(dropdownOpen.exists()).toBe(isOpen);
174
+ });
175
+ });
176
+
140
177
  describe('given attributes from parent element', () => {
141
178
  it('should not pass classes to the select element', () => {
142
179
  const customClass = 'bananas';
@@ -0,0 +1,34 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { _EDIT } from '@shell/config/query-params';
3
+ import { AUTH_TYPE } from '@shell/config/types';
4
+ import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret.vue';
5
+
6
+ const requiredSetup = () => {
7
+ return { global: { mocks: { $fetchState: {} } } };
8
+ };
9
+
10
+ describe('component: SelectOrCreateAuthSecret', () => {
11
+ it.each([
12
+ ['display', true],
13
+ ['not display', false],
14
+ ['not display', undefined], // prop's default value is used
15
+ ])('mode edit: should %p sshKnownHosts field if enabled and auth type is SSH', (_, showSshKnownHosts) => {
16
+ const wrapper = mount(SelectOrCreateAuthSecret, {
17
+ ...requiredSetup(),
18
+ props: {
19
+ mode: _EDIT,
20
+ namespace: 'default',
21
+ value: {},
22
+ showSshKnownHosts,
23
+ registerBeforeHook: () => {},
24
+ },
25
+ data() {
26
+ return { selected: AUTH_TYPE._SSH } as any;
27
+ }
28
+ });
29
+
30
+ const knownSshHosts = wrapper.find('[data-testid="auth-secret-known-ssh-hosts"]');
31
+
32
+ expect(knownSshHosts.exists()).toBe(showSshKnownHosts || false);
33
+ });
34
+ });
@@ -11,7 +11,7 @@ describe('component: UnitInput', () => {
11
11
  expect(wrapper.isVisible()).toBe(true);
12
12
  });
13
13
 
14
- it.each(['blur', 'change'])('should emit input event when "%p" is fired', async(event) => {
14
+ it.each(['blur', 'update:value'])('should emit input event when "%p" is fired', async(event) => {
15
15
  const wrapper = mount(UnitInput, { props: { value: 1, delay: 0 } });
16
16
  const input = wrapper.find('input');
17
17
 
@@ -20,7 +20,7 @@ describe('component: UnitInput', () => {
20
20
  input.trigger(event);
21
21
 
22
22
  expect(wrapper.emitted('update:value')).toBeTruthy();
23
- expect(wrapper.emitted('update:value')[2]).toStrictEqual([4]);
23
+ expect(wrapper.emitted('update:value')[1]).toStrictEqual([4]);
24
24
  });
25
25
 
26
26
  it.each([
@@ -184,7 +184,7 @@ describe('component: UnitInput', () => {
184
184
  input.trigger('blur');
185
185
 
186
186
  expect(wrapper.emitted('update:value')).toBeTruthy();
187
- expect(wrapper.emitted('update:value')[3][0]).toBe(value);
187
+ expect(wrapper.emitted('update:value')[0][0]).toBe(value);
188
188
  });
189
189
 
190
190
  describe.each([
@@ -207,7 +207,7 @@ describe('component: UnitInput', () => {
207
207
  expect(inputElement.value).toBe('123');
208
208
  });
209
209
 
210
- it.each(['input', 'blur'])('on %p 123 should display input value 123', async(trigger) => {
210
+ it.each(['update:value', 'blur'])('on %p 123 should display input value 123', async(trigger) => {
211
211
  const wrapper = mount(UnitInput, {
212
212
  props: {
213
213
  value: '0',
@@ -248,7 +248,6 @@ describe('component: UnitInput', () => {
248
248
  const input = wrapper.find('input');
249
249
 
250
250
  await input.trigger('update:value');
251
- await input.trigger('input');
252
251
 
253
252
  expect(input.element.value).toBe('123');
254
253
  });
@@ -151,7 +151,9 @@ export default {
151
151
  </span>
152
152
  <span
153
153
  v-else-if="showTooltip"
154
- v-clean-tooltip="{content: title, placement: tooltipPlacement}"
154
+ v-clean-tooltip="{content: title, placement: tooltipPlacement, triggers: ['hover', 'touch', 'focus'] }"
155
+ v-stripped-aria-label="title"
156
+ tabindex="0"
155
157
  class="live-date"
156
158
  >
157
159
  {{ suffixedLabel }}
@@ -16,11 +16,19 @@ export default {
16
16
  default: () => {}
17
17
  },
18
18
  },
19
- data() {
20
- const cloned = this.getLabel(this.value.toLowerCase());
21
- const headless = this.value === 'ClusterIP' && this.row?.spec?.clusterIP === 'None' ? this.getLabel('headless') : undefined;
22
19
 
23
- return { translated: cloned, headless };
20
+ computed: {
21
+ translated() {
22
+ const value = this.value;
23
+
24
+ return this.getLabel(value.toLocaleLowerCase());
25
+ },
26
+ clusterIp() {
27
+ return this.row?.spec?.clusterIP;
28
+ },
29
+ headless() {
30
+ return this.value === 'ClusterIP' && this.clusterIp === 'None' ? this.getLabel('headless') : undefined;
31
+ }
24
32
  },
25
33
 
26
34
  methods: {
@@ -189,7 +189,8 @@ export default {
189
189
  :aria-label="t('workload.healthScaleToggle')"
190
190
  :aria-expanded="expanded"
191
191
  @click="expanded = !expanded"
192
- @keyup.enter.space="expanded = !expanded"
192
+ @keyup.enter="expanded = !expanded"
193
+ @keydown.space.prevent="expanded = !expanded"
193
194
  >
194
195
  <ProgressBarMulti
195
196
  v-if="parts"
@@ -81,7 +81,7 @@ export default {
81
81
  extensionHeaderActions: getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, this.$route),
82
82
  ctx: this,
83
83
  showImportModal: false,
84
- showSearchModal: false,
84
+ showSearchModal: false
85
85
  };
86
86
  },
87
87
 
@@ -403,6 +403,7 @@ export default {
403
403
  <div>
404
404
  <TopLevelMenu v-if="isRancherInHarvester || isMultiCluster || !isSingleProduct" />
405
405
  </div>
406
+
406
407
  <div
407
408
  class="menu-spacer"
408
409
  :class="{'isSingleProduct': isSingleProduct }"
@@ -410,20 +411,25 @@ export default {
410
411
  <router-link
411
412
  v-if="isSingleProduct && !isRancherInHarvester"
412
413
  :to="singleProductLogoRoute"
414
+ role="link"
415
+ :alt="t('branding.logos.home')"
413
416
  >
414
417
  <BrandImage
415
418
  v-if="isSingleProduct.supportCustomLogo && isHarvester"
416
419
  class="side-menu-logo"
417
420
  file-name="harvester.svg"
418
421
  :support-custom-logo="true"
422
+ :alt="t('branding.logos.label')"
419
423
  />
420
424
  <img
421
425
  v-else
422
426
  class="side-menu-logo"
423
427
  :src="isSingleProduct.logo"
428
+ :alt="t('branding.logos.label')"
424
429
  >
425
430
  </router-link>
426
431
  </div>
432
+
427
433
  <div
428
434
  v-if="!simple"
429
435
  ref="product"
@@ -450,6 +456,7 @@ export default {
450
456
  v-if="currentCluster"
451
457
  :cluster="currentCluster"
452
458
  class="mr-10"
459
+ :alt="t('branding.logos.label')"
453
460
  />
454
461
  <div
455
462
  v-if="currentCluster"
@@ -462,6 +469,7 @@ export default {
462
469
  v-if="currentCluster"
463
470
  :cluster="currentCluster"
464
471
  class="ml-10"
472
+ :alt="t('branding.logos.label')"
465
473
  />
466
474
  <div
467
475
  v-if="!currentCluster"
@@ -470,6 +478,7 @@ export default {
470
478
  <BrandImage
471
479
  class="side-menu-logo-img"
472
480
  file-name="rancher-logo.svg"
481
+ :alt="t('branding.logos.label')"
473
482
  />
474
483
  </div>
475
484
  </template>
@@ -484,12 +493,14 @@ export default {
484
493
  :src="currentProduct.iconHeader"
485
494
  class="cluster-os-logo mr-10"
486
495
  style="width: 44px; height: 36px;"
496
+ :alt="t('branding.logos.label')"
487
497
  >
488
498
  <div class="product-name">
489
499
  {{ prod }}
490
500
  </div>
491
501
  </div>
492
502
  </div>
503
+
493
504
  <div
494
505
  v-else
495
506
  class="simple-title"
@@ -507,8 +518,9 @@ export default {
507
518
  >
508
519
  <BrandImage
509
520
  class="side-menu-logo-img"
510
- data-testid="header-side-menu__brand-img"
521
+ data-testid="header__brand-img"
511
522
  file-name="rancher-logo.svg"
523
+ :alt="t('branding.logos.label')"
512
524
  />
513
525
  </div>
514
526
  </div>
@@ -536,6 +548,9 @@ export default {
536
548
  type="button"
537
549
  class="btn header-btn role-tertiary"
538
550
  data-testid="header-action-import-yaml"
551
+ role="button"
552
+ tabindex="0"
553
+ :aria-label="t('nav.import')"
539
554
  @click="openImport()"
540
555
  >
541
556
  <i class="icon icon-upload icon-lg" />
@@ -563,6 +578,9 @@ export default {
563
578
  :disabled="!shellEnabled"
564
579
  type="button"
565
580
  class="btn header-btn role-tertiary"
581
+ role="button"
582
+ tabindex="0"
583
+ :aria-label="t('nav.shellShortcut', {key:''})"
566
584
  @shortkey="currentCluster.openShell()"
567
585
  @click="currentCluster.openShell()"
568
586
  >
@@ -576,6 +594,9 @@ export default {
576
594
  type="button"
577
595
  class="btn header-btn role-tertiary"
578
596
  data-testid="btn-download-kubeconfig"
597
+ role="button"
598
+ tabindex="0"
599
+ :aria-label="t('nav.kubeconfig.download')"
579
600
  @click="currentCluster.downloadKubeConfig()"
580
601
  >
581
602
  <i class="icon icon-file icon-lg" />
@@ -588,6 +609,9 @@ export default {
588
609
  type="button"
589
610
  class="btn header-btn role-tertiary"
590
611
  data-testid="btn-copy-kubeconfig"
612
+ role="button"
613
+ tabindex="0"
614
+ :aria-label="t('nav.kubeconfig.copy')"
591
615
  @click="copyKubeConfig($event)"
592
616
  >
593
617
  <i
@@ -603,11 +627,15 @@ export default {
603
627
 
604
628
  <button
605
629
  v-if="showSearch"
630
+ id="header-btn-search"
606
631
  v-clean-tooltip="t('nav.resourceSearch.toolTip', {key: searchShortcut})"
607
632
  v-shortkey="{windows: ['ctrl', 'k'], mac: ['meta', 'k']}"
608
633
  type="button"
609
634
  class="btn header-btn role-tertiary"
610
635
  data-testid="header-resource-search"
636
+ role="button"
637
+ tabindex="0"
638
+ :aria-label="t('nav.resourceSearch.toolTip', {key: ''})"
611
639
  @shortkey="openSearch()"
612
640
  @click="openSearch()"
613
641
  >
@@ -619,6 +647,8 @@ export default {
619
647
  name="searchModal"
620
648
  width="50%"
621
649
  height="auto"
650
+ :trigger-focus-trap="true"
651
+ return-focus-selector="#header-btn-search"
622
652
  @close="hideSearch()"
623
653
  >
624
654
  <Jump @closeSearch="hideSearch()" />
@@ -639,6 +669,9 @@ export default {
639
669
  type="button"
640
670
  class="btn header-btn role-tertiary"
641
671
  :data-testid="`extension-header-action-${ action.labelKey || action.label }`"
672
+ role="button"
673
+ tabindex="0"
674
+ :aria-label="action.label"
642
675
  @shortkey="handleExtensionAction(action, $event)"
643
676
  @click="handleExtensionAction(action, $event)"
644
677
  >
@@ -1,51 +1,22 @@
1
1
  <script setup lang="ts">
2
- import { ref, computed } from 'vue';
2
+ import { computed } from 'vue';
3
3
  import { useStore } from 'vuex';
4
- import {
5
- RcDropdown,
6
- RcDropdownItem,
7
- RcDropdownSeparator,
8
- RcDropdownTrigger
9
- } from '@components/RcDropdown';
10
-
11
- const isPageActionMenuOpen = ref(false);
12
-
13
- const showPageActionsMenu = (show: boolean) => {
14
- isPageActionMenuOpen.value = show;
15
- };
4
+ import { RcDropdownMenu } from '@components/RcDropdown';
16
5
 
17
6
  const store = useStore();
18
7
  const pageActions = computed(() => store.getters.pageActions);
19
- const pageAction = (action: string) => {
8
+ const pageAction = (_event: Event, action: string) => {
20
9
  store.dispatch('handlePageAction', action);
21
- showPageActionsMenu(false);
22
10
  };
23
11
  </script>
24
12
 
25
13
  <template>
26
- <rc-dropdown :aria-label="t('nav.actionMenu.label')">
27
- <rc-dropdown-trigger
28
- tertiary
29
- data-testid="page-actions-menu"
30
- :aria-label="t('nav.actionMenu.button.label')"
31
- >
32
- <i class="icon icon-actions" />
33
- </rc-dropdown-trigger>
34
- <template #dropdownCollection>
35
- <template
36
- v-for="(a) in pageActions"
37
- :key="a.label"
38
- >
39
- <rc-dropdown-item
40
- v-if="!a.separator"
41
- @click="pageAction(a)"
42
- >
43
- {{ a.labelKey ? t(a.labelKey) : a.label }}
44
- </rc-dropdown-item>
45
- <rc-dropdown-separator
46
- v-else
47
- />
48
- </template>
49
- </template>
50
- </rc-dropdown>
14
+ <rc-dropdown-menu
15
+ :options="pageActions"
16
+ :button-aria-label="t('nav.actionMenu.label')"
17
+ :dropdown-aria-label="t('nav.actionMenu.button.label')"
18
+ data-testid="page-actions-menu-action-button"
19
+ button-role="tertiary"
20
+ @select="pageAction"
21
+ />
51
22
  </template>
@@ -64,7 +64,7 @@ export default {
64
64
 
65
65
  g.hidden = !!hidden;
66
66
  });
67
- },
67
+ }
68
68
  },
69
69
  };
70
70
  </script>
@@ -76,6 +76,8 @@ export default {
76
76
  v-model="value"
77
77
  :placeholder="t('nav.resourceSearch.placeholder')"
78
78
  class="search"
79
+ role="textbox"
80
+ :aria-label="t('nav.resourceSearch.label')"
79
81
  @keyup.esc="$emit('closeSearch')"
80
82
  >
81
83
  <div class="results">
@@ -103,13 +105,17 @@ export default {
103
105
  </template>
104
106
 
105
107
  <style lang="scss" scoped>
106
- .search, .search:hover, .search:focus {
108
+ .search, .search:hover {
107
109
  position: relative;
108
110
  background-color: var(--dropdown-bg);
109
111
  border-radius: 0;
110
112
  box-shadow: none;
111
113
  }
112
114
 
115
+ .search:focus-visible {
116
+ outline-offset: -2px;
117
+ }
118
+
113
119
  .results {
114
120
  margin-top: -1px;
115
121
  overflow-y: auto;
@@ -894,12 +894,13 @@ export default {
894
894
  display: inline-block;
895
895
 
896
896
  .ns-glass {
897
- height: 100vh;
897
+ top: 0;
898
+ bottom: 0;
898
899
  left: 0;
900
+ right: 0;
899
901
  opacity: 0;
900
- position: absolute;
901
- top: 0;
902
- width: 100vw;
902
+ position: fixed;
903
+
903
904
  z-index: z-index('overContent');
904
905
  }
905
906
 
@@ -37,7 +37,7 @@ export default {
37
37
  class="pin icon"
38
38
  :class="{'icon-pin-outlined': !pinned, 'icon-pin': pinned}"
39
39
  aria-role="button"
40
- :aria-label="`${t('nav.ariaLabel.pinCluster')} ${ cluster.label }`"
40
+ :aria-label="t('nav.ariaLabel.pinCluster', { cluster: cluster.label })"
41
41
  @click.stop.prevent="toggle"
42
42
  @keydown.enter.prevent="toggle"
43
43
  @keydown.space.prevent="toggle"
@@ -37,7 +37,7 @@ type ProvCluster = {
37
37
  }
38
38
 
39
39
  /**
40
- * Order
40
+ * Order of v1 mgmt clusters
41
41
  * 1. local cluster - https://github.com/rancher/dashboard/issues/10975
42
42
  * 2. working clusters
43
43
  * 3. name
@@ -47,10 +47,10 @@ const DEFAULT_SORT: Array<PaginationSort> = [
47
47
  asc: false,
48
48
  field: 'spec.internal',
49
49
  },
50
- // {
51
- // asc: true,
52
- // field: 'status.conditions[0].status' // Pending API changes https://github.com/rancher/rancher/issues/48092
53
- // },
50
+ {
51
+ asc: false,
52
+ field: 'status.connected'
53
+ },
54
54
  {
55
55
  asc: true,
56
56
  field: 'spec.displayName',