@rancher/shell 3.0.4 → 3.0.5-rc.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 (270) hide show
  1. package/assets/images/providers/sks.svg +1 -0
  2. package/assets/styles/base/_basic.scss +6 -0
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +1 -0
  5. package/assets/styles/global/_button.scss +1 -0
  6. package/assets/translations/en-us.yaml +65 -15
  7. package/assets/translations/zh-hans.yaml +4 -3
  8. package/chart/monitoring/index.vue +3 -1
  9. package/cloud-credential/aws.vue +2 -0
  10. package/components/ActionDropdownShell.vue +71 -0
  11. package/components/AppModal.vue +18 -4
  12. package/components/AsyncButton.vue +24 -7
  13. package/components/BannerGraphic.vue +1 -0
  14. package/components/CommunityLinks.vue +4 -59
  15. package/components/CopyToClipboardText.vue +2 -1
  16. package/components/CruResource.vue +6 -1
  17. package/components/DetailText.vue +5 -0
  18. package/components/ExplorerMembers.vue +1 -1
  19. package/components/ExplorerProjectsNamespaces.vue +68 -18
  20. package/components/GlobalRoleBindings.vue +5 -1
  21. package/components/GrowlManager.vue +1 -0
  22. package/components/LandingPagePreference.vue +7 -3
  23. package/components/LocaleSelector.vue +39 -95
  24. package/components/ModalManager.vue +55 -0
  25. package/components/ModalWithCard.vue +1 -0
  26. package/components/PromptModal.vue +47 -8
  27. package/components/PromptRemove.vue +1 -0
  28. package/components/PromptRestore.vue +1 -0
  29. package/components/ResourceCancelModal.vue +1 -0
  30. package/components/ResourceDetail/Masthead.vue +38 -12
  31. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  32. package/components/ResourceDetail/index.vue +47 -12
  33. package/components/ResourceTable.vue +54 -19
  34. package/components/SideNav.vue +5 -1
  35. package/components/SlideInPanelManager.vue +126 -0
  36. package/components/SortableTable/THead.vue +5 -2
  37. package/components/SortableTable/actions.js +1 -1
  38. package/components/SortableTable/index.vue +64 -51
  39. package/components/SortableTable/paging.js +16 -19
  40. package/components/SortableTable/selection.js +0 -11
  41. package/components/Wizard.vue +2 -2
  42. package/components/__tests__/AsyncButton.test.ts +2 -2
  43. package/components/__tests__/ModalManager.spec.ts +176 -0
  44. package/components/__tests__/PromptModal.test.ts +148 -0
  45. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  46. package/components/auth/AuthBanner.vue +13 -11
  47. package/components/auth/Principal.vue +1 -0
  48. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  49. package/components/auth/login/ldap.vue +1 -1
  50. package/components/fleet/FleetResources.vue +21 -6
  51. package/components/form/ArrayList.vue +76 -60
  52. package/components/form/BannerSettings.vue +17 -2
  53. package/components/form/ColorInput.vue +35 -6
  54. package/components/form/Command.vue +6 -15
  55. package/components/form/EnvVars.vue +16 -8
  56. package/components/form/HealthCheck.vue +3 -3
  57. package/components/form/HookOption.vue +11 -16
  58. package/components/form/LabeledSelect.vue +18 -22
  59. package/components/form/LifecycleHooks.vue +3 -3
  60. package/components/form/MatchExpressions.vue +14 -8
  61. package/components/form/NameNsDescription.vue +128 -104
  62. package/components/form/Networking.vue +20 -12
  63. package/components/form/NodeAffinity.vue +31 -23
  64. package/components/form/NodeScheduling.vue +13 -3
  65. package/components/form/NotificationSettings.vue +15 -1
  66. package/components/form/Password.vue +1 -0
  67. package/components/form/PodAffinity.vue +43 -43
  68. package/components/form/Probe.vue +68 -66
  69. package/components/form/ResourceQuota/Project.vue +5 -1
  70. package/components/form/ResourceSelector.vue +7 -9
  71. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +16 -24
  72. package/components/form/SSHKnownHosts/index.vue +30 -13
  73. package/components/form/Security.vue +54 -56
  74. package/components/form/Select.vue +32 -21
  75. package/components/form/ShellInput.vue +5 -1
  76. package/components/form/Tolerations.vue +5 -1
  77. package/components/form/ValueFromResource.vue +134 -121
  78. package/components/form/WorkloadPorts.vue +18 -18
  79. package/components/form/__tests__/ArrayList.test.ts +5 -2
  80. package/components/form/__tests__/ColorInput.test.ts +35 -0
  81. package/components/form/__tests__/LabeledSelect.test.ts +40 -0
  82. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  83. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  84. package/components/form/__tests__/Probe.test.ts +12 -8
  85. package/components/form/__tests__/SSHKnownHosts.test.ts +22 -2
  86. package/components/form/__tests__/Select.test.ts +37 -0
  87. package/components/formatter/InternalExternalIP.vue +2 -0
  88. package/components/formatter/SecretData.vue +20 -7
  89. package/components/nav/Group.vue +27 -5
  90. package/components/nav/Header.vue +17 -43
  91. package/components/nav/NamespaceFilter.vue +134 -86
  92. package/components/nav/TopLevelMenu.vue +4 -5
  93. package/components/nav/Type.vue +12 -1
  94. package/components/nav/WindowManager/ContainerLogs.vue +87 -61
  95. package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
  96. package/components/templates/blank.vue +4 -1
  97. package/components/templates/default.vue +8 -3
  98. package/components/templates/home.vue +10 -1
  99. package/components/templates/plain.vue +10 -4
  100. package/composables/focusTrap.ts +12 -4
  101. package/composables/useRuntimeFlag.ts +29 -0
  102. package/config/router/routes.js +20 -13
  103. package/config/store.js +4 -0
  104. package/config/uiplugins.js +5 -1
  105. package/core/types.ts +12 -6
  106. package/detail/catalog.cattle.io.app.vue +6 -1
  107. package/detail/fleet.cattle.io.bundle.vue +70 -6
  108. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  109. package/detail/namespace.vue +0 -3
  110. package/detail/node.vue +17 -13
  111. package/detail/provisioning.cattle.io.cluster.vue +72 -6
  112. package/dialog/AddCustomBadgeDialog.vue +1 -1
  113. package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
  114. package/{components/AssignTo.vue → dialog/AssignToDialog.vue} +71 -80
  115. package/dialog/ChangePasswordDialog.vue +106 -0
  116. package/dialog/DeactivateDriverDialog.vue +1 -0
  117. package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
  118. package/dialog/DisableAuthProviderDialog.vue +101 -0
  119. package/dialog/DrainNode.vue +1 -1
  120. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
  121. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +69 -57
  122. package/dialog/FeatureFlagListDialog.vue +288 -0
  123. package/dialog/ForceMachineRemoveDialog.vue +5 -2
  124. package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
  125. package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
  126. package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -59
  127. package/dialog/MoveNamespaceDialog.vue +157 -0
  128. package/dialog/ScalePoolDownDialog.vue +1 -1
  129. package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
  130. package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
  131. package/dialog/WechatDialog.vue +57 -0
  132. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
  133. package/edit/auth/__tests__/oidc.test.ts +152 -109
  134. package/edit/auth/azuread.vue +2 -1
  135. package/edit/auth/github.vue +1 -1
  136. package/edit/auth/googleoauth.vue +5 -1
  137. package/edit/auth/ldap/index.vue +1 -1
  138. package/edit/auth/oidc.vue +38 -5
  139. package/edit/auth/saml.vue +1 -1
  140. package/edit/cloudcredential.vue +24 -9
  141. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
  142. package/edit/management.cattle.io.user.vue +28 -3
  143. package/edit/namespace.vue +1 -4
  144. package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
  145. package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
  146. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
  147. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
  148. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
  149. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
  150. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
  151. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
  152. package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
  153. package/edit/provisioning.cattle.io.cluster/rke2.vue +49 -41
  154. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
  155. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
  156. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
  157. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
  158. package/edit/token.vue +2 -0
  159. package/edit/workload/index.vue +1 -0
  160. package/edit/workload/mixins/workload.js +0 -2
  161. package/initialize/install-plugins.js +2 -1
  162. package/list/harvesterhci.io.management.cluster.vue +4 -1
  163. package/list/management.cattle.io.feature.vue +4 -287
  164. package/list/provisioning.cattle.io.cluster.vue +20 -12
  165. package/machine-config/azure.vue +16 -4
  166. package/mixins/vue-select-overrides.js +0 -4
  167. package/models/__tests__/namespace.test.ts +25 -1
  168. package/models/cloudcredential.js +5 -0
  169. package/models/fleet.cattle.io.cluster.js +8 -2
  170. package/models/fleet.cattle.io.gitrepo.js +8 -34
  171. package/models/kontainerdriver.js +6 -3
  172. package/models/management.cattle.io.feature.js +7 -1
  173. package/models/management.cattle.io.node.js +3 -3
  174. package/models/namespace.js +11 -6
  175. package/models/nodedriver.js +6 -3
  176. package/models/workload.js +4 -1
  177. package/package.json +3 -3
  178. package/pages/about.vue +13 -3
  179. package/pages/account/index.vue +16 -6
  180. package/pages/auth/login.vue +18 -7
  181. package/pages/auth/logout.vue +4 -1
  182. package/pages/auth/setup.vue +2 -0
  183. package/pages/auth/verify.vue +13 -8
  184. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  185. package/pages/c/_cluster/apps/charts/install.vue +26 -26
  186. package/pages/c/_cluster/auth/config/index.vue +10 -12
  187. package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
  188. package/pages/c/_cluster/explorer/index.vue +17 -15
  189. package/pages/c/_cluster/istio/index.vue +2 -2
  190. package/pages/c/_cluster/longhorn/index.vue +1 -1
  191. package/pages/c/_cluster/monitoring/index.vue +1 -1
  192. package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
  193. package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
  194. package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
  195. package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
  196. package/pages/c/_cluster/neuvector/index.vue +1 -1
  197. package/pages/c/_cluster/settings/banners.vue +4 -3
  198. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
  199. package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
  200. package/pages/c/_cluster/uiplugins/index.vue +98 -55
  201. package/pages/diagnostic.vue +59 -11
  202. package/pages/fail-whale.vue +14 -8
  203. package/pages/home.vue +24 -18
  204. package/pages/prefs.vue +7 -6
  205. package/pages/support/index.vue +4 -1
  206. package/plugins/internal-api/index.ts +37 -0
  207. package/plugins/internal-api/shared/base-api.ts +13 -0
  208. package/plugins/internal-api/shell/shell.api.ts +108 -0
  209. package/plugins/steve/actions.js +0 -12
  210. package/public/index.html +1 -0
  211. package/rancher-components/Card/Card.vue +1 -1
  212. package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
  213. package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
  214. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +47 -0
  215. package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
  216. package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
  217. package/rancher-components/Form/Radio/RadioButton.vue +20 -4
  218. package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
  219. package/rancher-components/Form/Radio/RadioGroup.vue +52 -10
  220. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
  221. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +5 -0
  222. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
  223. package/rancher-components/RcButton/RcButton.vue +2 -1
  224. package/rancher-components/RcButton/types.ts +1 -0
  225. package/rancher-components/RcDropdown/RcDropdown.vue +18 -6
  226. package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
  227. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
  228. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
  229. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
  230. package/rancher-components/RcDropdown/index.ts +2 -0
  231. package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
  232. package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
  233. package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
  234. package/scripts/extension/bundle +20 -0
  235. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
  236. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  237. package/scripts/extension/helmpatch +44 -31
  238. package/scripts/extension/publish +12 -12
  239. package/scripts/typegen.sh +2 -4
  240. package/server/har-file.js +25 -3
  241. package/store/action-menu.js +26 -56
  242. package/store/features.js +2 -1
  243. package/store/index.js +5 -0
  244. package/store/modal.ts +71 -0
  245. package/store/slideInPanel.ts +47 -0
  246. package/store/type-map.js +12 -1
  247. package/store/type-map.utils.ts +4 -4
  248. package/types/global-vue.d.ts +5 -0
  249. package/types/internal-api/shell/growl.d.ts +25 -0
  250. package/types/internal-api/shell/modal.d.ts +77 -0
  251. package/types/internal-api/shell/slideIn.d.ts +15 -0
  252. package/types/resources/fleet.d.ts +0 -14
  253. package/types/shell/index.d.ts +43 -24
  254. package/types/vue-shim.d.ts +4 -1
  255. package/utils/__mocks__/tabbable.js +13 -0
  256. package/utils/__tests__/object.test.ts +38 -4
  257. package/utils/cluster.js +35 -0
  258. package/utils/fleet.ts +15 -73
  259. package/utils/object.js +48 -5
  260. package/utils/validators/formRules/__tests__/index.test.ts +10 -1
  261. package/utils/validators/formRules/index.ts +27 -3
  262. package/utils/validators/machine-pool.ts +20 -0
  263. package/components/DisableAuthProviderModal.vue +0 -114
  264. package/components/MoveModal.vue +0 -166
  265. package/components/PromptChangePassword.vue +0 -123
  266. package/components/fleet/FleetBundleResources.vue +0 -86
  267. package/components/formatter/ExtensionCache.vue +0 -74
  268. package/components/formatter/Port.vue +0 -24
  269. package/components/formatter/SecretType.vue +0 -41
  270. package/types/vue-shim.d +0 -20
@@ -5,7 +5,6 @@ import { MANAGEMENT, NORMAN, STEVE } from '@shell/config/types';
5
5
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
6
6
  import { ucFirst } from '@shell/utils/string';
7
7
  import { isAlternate, isMac } from '@shell/utils/platform';
8
- import Import from '@shell/components/Import';
9
8
  import BrandImage from '@shell/components/BrandImage';
10
9
  import { getProduct, getVendor } from '@shell/config/private-label';
11
10
  import ClusterProviderIcon from '@shell/components/ClusterProviderIcon';
@@ -16,7 +15,6 @@ import NamespaceFilter from './NamespaceFilter';
16
15
  import WorkspaceSwitcher from './WorkspaceSwitcher';
17
16
  import TopLevelMenu from './TopLevelMenu';
18
17
 
19
- import Jump from './Jump';
20
18
  import { allHash } from '@shell/utils/promise';
21
19
  import { ActionLocation, ExtensionPoint } from '@shell/core/types';
22
20
  import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
@@ -36,9 +34,7 @@ export default {
36
34
  components: {
37
35
  NamespaceFilter,
38
36
  WorkspaceSwitcher,
39
- Import,
40
37
  TopLevelMenu,
41
- Jump,
42
38
  BrandImage,
43
39
  ClusterBadge,
44
40
  ClusterProviderIcon,
@@ -79,9 +75,7 @@ export default {
79
75
  LOGGED_OUT,
80
76
  navHeaderRight: null,
81
77
  extensionHeaderActions: getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, this.$route),
82
- ctx: this,
83
- showImportModal: false,
84
- showSearchModal: false
78
+ ctx: this
85
79
  };
86
80
  },
87
81
 
@@ -316,19 +310,24 @@ export default {
316
310
  },
317
311
 
318
312
  openImport() {
319
- this.showImportModal = true;
320
- },
321
-
322
- closeImport() {
323
- this.showImportModal = false;
313
+ this.$store.dispatch('cluster/promptModal', {
314
+ component: 'ImportDialog',
315
+ modalWidth: '75%',
316
+ height: 'auto',
317
+ styles: 'max-height: 90vh;',
318
+ componentProps: { cluster: this.currentCluster }
319
+ });
324
320
  },
325
321
 
326
322
  openSearch() {
327
- this.showSearchModal = true;
328
- },
329
-
330
- hideSearch() {
331
- this.showSearchModal = false;
323
+ this.$store.dispatch('cluster/promptModal', {
324
+ component: 'SearchDialog',
325
+ testId: 'search-modal',
326
+ modalWidth: '50%',
327
+ height: 'auto',
328
+ styles: 'max-height: 90vh;',
329
+ returnFocusSelector: '#header-btn-search'
330
+ });
332
331
  },
333
332
 
334
333
  checkClusterName() {
@@ -555,20 +554,6 @@ export default {
555
554
  >
556
555
  <i class="icon icon-upload icon-lg" />
557
556
  </button>
558
- <app-modal
559
- v-if="showImportModal"
560
- class="import-modal"
561
- name="importModal"
562
- width="75%"
563
- height="auto"
564
- styles="max-height: 90vh;"
565
- @close="closeImport"
566
- >
567
- <Import
568
- :cluster="currentCluster"
569
- @close="closeImport"
570
- />
571
- </app-modal>
572
557
 
573
558
  <button
574
559
  v-if="showKubeShell"
@@ -641,18 +626,6 @@ export default {
641
626
  >
642
627
  <i class="icon icon-search icon-lg" />
643
628
  </button>
644
- <app-modal
645
- v-if="showSearch && showSearchModal"
646
- class="search-modal"
647
- name="searchModal"
648
- width="50%"
649
- height="auto"
650
- :trigger-focus-trap="true"
651
- return-focus-selector="#header-btn-search"
652
- @close="hideSearch()"
653
- >
654
- <Jump @closeSearch="hideSearch()" />
655
- </app-modal>
656
629
  </div>
657
630
 
658
631
  <!-- Extension header actions -->
@@ -702,6 +675,7 @@ export default {
702
675
  :class="{'avatar-round': principal.roundAvatar}"
703
676
  width="36"
704
677
  height="36"
678
+ :alt="t('nav.alt.userAvatar')"
705
679
  >
706
680
  <i
707
681
  v-else
@@ -20,11 +20,15 @@ import { KEY } from '@shell/utils/platform';
20
20
  import pAndNFiltering from '@shell/plugins/steve/projectAndNamespaceFiltering.utils';
21
21
  import { SETTING } from '@shell/config/settings';
22
22
  import paginationUtils from '@shell/utils/pagination-utils';
23
+ import { randomStr } from '@shell/utils/string';
24
+ import { RcButton } from '@components/RcButton';
23
25
 
24
26
  const forcedNamespaceValidTypes = [NAMESPACE_FILTER_KINDS.DIVIDER, NAMESPACE_FILTER_KINDS.PROJECT, NAMESPACE_FILTER_KINDS.NAMESPACE];
25
27
 
26
28
  export default {
27
29
 
30
+ components: { RcButton },
31
+
28
32
  data() {
29
33
  return {
30
34
  isOpen: false,
@@ -35,6 +39,7 @@ export default {
35
39
  cachedFiltered: [],
36
40
  NAMESPACE_FILTER_KINDS,
37
41
  namespaceFilterMode: undefined,
42
+ containerId: `dropdown-${ randomStr() }`,
38
43
  };
39
44
  },
40
45
 
@@ -381,10 +386,6 @@ export default {
381
386
  }
382
387
  },
383
388
 
384
- beforeUnmount() {
385
- this.removeCloseKeyHandler();
386
- },
387
-
388
389
  mounted() {
389
390
  this.layout();
390
391
  },
@@ -477,28 +478,25 @@ export default {
477
478
  }
478
479
  });
479
480
  },
480
- addCloseKeyHandler() {
481
- document.addEventListener('keyup', this.closeKeyHandler);
482
- },
483
- removeCloseKeyHandler() {
484
- document.removeEventListener('keyup', this.closeKeyHandler);
485
- },
486
- closeKeyHandler(e) {
487
- if (e.keyCode === KEY.ESCAPE ) {
488
- this.close();
489
- }
490
- },
491
481
  // Keyboard support
492
482
  itemKeyHandler(e, opt) {
493
483
  if (e.keyCode === KEY.DOWN ) {
494
484
  e.preventDefault();
495
485
  e.stopPropagation();
496
486
  this.down();
497
- } else if (e.keyCode === KEY.UP ) {
487
+
488
+ return;
489
+ }
490
+
491
+ if (e.keyCode === KEY.UP) {
498
492
  e.preventDefault();
499
493
  e.stopPropagation();
500
494
  this.up();
501
- } else if (e.keyCode === KEY.SPACE || e.keyCode === KEY.CR) {
495
+
496
+ return;
497
+ }
498
+
499
+ if (e.keyCode === KEY.SPACE || e.keyCode === KEY.CR) {
502
500
  if (this.namespaceFilterMode && !opt.enabled) {
503
501
  return;
504
502
  }
@@ -515,10 +513,8 @@ export default {
515
513
  e.stopPropagation();
516
514
  this.down(true);
517
515
  break;
518
- case KEY.TAB:
519
- // Tab out of the input box
516
+ case KEY.ESCAPE:
520
517
  this.close();
521
- e.target.blur();
522
518
  break;
523
519
  case KEY.CR:
524
520
  if (this.filtered.length === 1) {
@@ -526,6 +522,10 @@ export default {
526
522
  this.filter = '';
527
523
  }
528
524
  break;
525
+ case KEY.TAB:
526
+ if (e.shiftKey) {
527
+ this.close();
528
+ }
529
529
  }
530
530
  },
531
531
  mouseOver(event) {
@@ -541,11 +541,11 @@ export default {
541
541
  el.focus();
542
542
  this.activeElement = null;
543
543
  },
544
- down(input) {
545
- const exising = this.activeElement || document.activeElement;
544
+ down(input, focusFirst = true) {
545
+ const existing = this.activeElement || document.activeElement;
546
546
 
547
547
  // Focus the first element in the list
548
- if (input || !exising) {
548
+ if (input || (!existing && !focusFirst)) {
549
549
  if (this.$refs.options) {
550
550
  const c = this.$refs.options.children;
551
551
 
@@ -554,15 +554,15 @@ export default {
554
554
  }
555
555
  }
556
556
  } else {
557
- let next = exising.nextSibling;
557
+ let next = existing.nextElementSibling;
558
558
 
559
- if (next?.children?.length) {
560
- const item = next.children[0];
559
+ if (!next) {
560
+ next = existing.parentNode.firstElementChild;
561
+ }
561
562
 
562
- // Skip over dividers (assumes we don't have two dividers next to each other)
563
- if (item.classList.contains('ns-divider')) {
564
- next = next.nextSibling;
565
- }
563
+ // Skip over dividers (assumes we don't have two dividers next to each other)
564
+ if (next?.classList.contains('ns-divider')) {
565
+ next = next.nextElementSibling;
566
566
  }
567
567
 
568
568
  if (next?.focus) {
@@ -571,15 +571,17 @@ export default {
571
571
  }
572
572
  },
573
573
  up() {
574
- if (document.activeElement) {
575
- let prev = document.activeElement.previousSibling;
574
+ const existing = this.activeElement || document.activeElement;
576
575
 
577
- if (prev?.children?.length) {
578
- const item = prev.children[0];
576
+ if (existing) {
577
+ let prev = document.activeElement.previousElementSibling;
579
578
 
580
- if (item.classList.contains('ns-divider')) {
581
- prev = prev.previousSibling;
582
- }
579
+ if (!prev) {
580
+ prev = existing.parentNode.lastElementChild;
581
+ }
582
+
583
+ if (prev.classList.contains('ns-divider')) {
584
+ prev = prev.previousElementSibling;
583
585
  }
584
586
 
585
587
  if (prev?.focus) {
@@ -599,17 +601,20 @@ export default {
599
601
  this.$nextTick(() => {
600
602
  this.focusFilter();
601
603
  });
602
- this.addCloseKeyHandler();
603
604
  this.layout();
604
605
  },
606
+ clearFilter() {
607
+ this.filter = '';
608
+ this.focusFilter();
609
+ },
605
610
  focusFilter() {
606
- this.$refs.filter.focus();
611
+ this.$refs.filterInput.focus();
607
612
  },
608
613
  close() {
609
614
  this.isOpen = false;
610
615
  this.activeElement = null;
611
- this.removeCloseKeyHandler();
612
616
  this.layout();
617
+ this.$refs.namespaceFilterInput.focus();
613
618
  },
614
619
  clear() {
615
620
  this.value = [];
@@ -689,11 +694,15 @@ export default {
689
694
  <template>
690
695
  <div
691
696
  v-if="!$fetchState.pending"
697
+ ref="namespaceFilterInput"
698
+ role="combobox"
699
+ :aria-expanded="isOpen"
700
+ :aria-activedescendant="containerId"
692
701
  class="ns-filter"
693
702
  data-testid="namespaces-filter"
694
703
  tabindex="0"
695
704
  @mousedown.prevent
696
- @focus="open()"
705
+ @keydown.down.enter.space.prevent="open"
697
706
  >
698
707
  <div
699
708
  v-if="isOpen"
@@ -703,6 +712,7 @@ export default {
703
712
 
704
713
  <!-- Select Dropdown control -->
705
714
  <div
715
+ :id="containerId"
706
716
  ref="dropdown"
707
717
  class="ns-dropdown"
708
718
  data-testid="namespaces-dropdown"
@@ -754,13 +764,18 @@ export default {
754
764
  >
755
765
  <div>{{ ns.label }}</div>
756
766
  <!-- block user from removing the last selection if ns forced filtering is on -->
757
- <i
767
+ <RcButton
758
768
  v-if="!namespaceFilterMode || value.length > 1"
759
- class="icon icon-close"
769
+ small
770
+ ghost
771
+ class="ns-chip-button"
760
772
  :data-testid="`namespaces-values-close-${j}`"
761
773
  @click="removeOption(ns, $event)"
774
+ @keydown.enter.space.stop="removeOption(ns, $event)"
762
775
  @mousedown="handleValueMouseDown(ns, $event)"
763
- />
776
+ >
777
+ <i class="icon icon-close" />
778
+ </RcButton>
764
779
  </div>
765
780
  </div>
766
781
 
@@ -795,20 +810,33 @@ export default {
795
810
  data-testid="namespaces-menu"
796
811
  >
797
812
  <div class="ns-controls">
798
- <div class="ns-input">
813
+ <div
814
+ class="ns-input"
815
+ role="status"
816
+ aria-live="polite"
817
+ >
799
818
  <input
800
- ref="filter"
819
+ ref="filterInput"
801
820
  v-model="filter"
802
821
  tabindex="0"
803
822
  class="ns-filter-input"
823
+ :aria-label="t('namespaceFilter.input')"
804
824
  @click="focusFilter"
805
825
  @keydown="inputKeyHandler($event)"
806
826
  >
807
- <i
827
+ <RcButton
808
828
  v-if="hasFilter"
809
- class="ns-filter-clear icon icon-close"
810
- @click="filter = ''"
811
- />
829
+ small
830
+ ghost
831
+ class="ns-filter-clear"
832
+ :aria-label="t('namespaceFilter.button.clearFilter')"
833
+ @click="clearFilter"
834
+ @keydown.enter.stop="clearFilter"
835
+ >
836
+ <i
837
+ class="icon icon-close"
838
+ />
839
+ </RcButton>
812
840
  </div>
813
841
  <div
814
842
  v-if="namespaceFilterMode"
@@ -819,57 +847,74 @@ export default {
819
847
  class="icon icon-info"
820
848
  />
821
849
  </div>
822
- <div
850
+ <RcButton
823
851
  v-else
852
+ small
853
+ ghost
824
854
  class="ns-clear"
855
+ :aria-label="t('namespaceFilter.button.clear')"
856
+ @click="clear"
857
+ @keydown.enter.stop="clear"
858
+ @keydown.tab.exact.prevent="down"
825
859
  >
826
860
  <i
827
861
  class="icon icon-close"
828
- @click="clear()"
829
862
  />
830
- </div>
863
+ </RcButton>
831
864
  </div>
832
865
  <div class="ns-divider mt-0" />
833
866
  <div
834
867
  ref="options"
835
868
  class="ns-options"
836
- role="list"
869
+ role="listbox"
870
+ tabindex="0"
871
+ aria-live="polite"
872
+ @keydown.down.enter.stop="down"
873
+ @keydown.escape="close"
874
+ @keydown.tab.exact.stop="close"
837
875
  >
838
- <div
876
+ <template
839
877
  v-for="(opt, i) in cachedFiltered"
840
- :id="opt.elementId"
841
878
  :key="opt.id"
842
- tabindex="0"
843
- class="ns-option"
844
- :disabled="opt.enabled ? null : true"
845
- :class="{
846
- 'ns-selected': opt.selected,
847
- 'ns-single-match': cachedFiltered.length === 1 && !opt.selected,
848
- }"
849
- :data-testid="`namespaces-option-${i}`"
850
- @click="opt.enabled && selectOption(opt)"
851
- @mouseover="opt.enabled && mouseOver($event)"
852
- @keydown="itemKeyHandler($event, opt)"
853
879
  >
854
- <div
880
+ <hr
855
881
  v-if="opt.kind === NAMESPACE_FILTER_KINDS.DIVIDER"
882
+ role="separator"
883
+ aria-orientation="horizontal"
856
884
  class="ns-divider"
857
- />
885
+ >
858
886
  <div
859
887
  v-else
860
- class="ns-item"
888
+ :id="opt.elementId"
889
+ tabindex="-1"
890
+ role="option"
891
+ class="ns-option"
892
+ :aria-selected="opt.selected"
893
+ :disabled="opt.enabled ? null : true"
894
+ :class="{
895
+ 'ns-selected': opt.selected,
896
+ 'ns-single-match': cachedFiltered.length === 1 && !opt.selected,
897
+ }"
898
+ :data-testid="`namespaces-option-${i}`"
899
+ @click="opt.enabled && selectOption(opt)"
900
+ @mouseover="opt.enabled && mouseOver($event)"
901
+ @keydown="itemKeyHandler($event, opt)"
861
902
  >
862
- <i
863
- v-if="opt.kind === NAMESPACE_FILTER_KINDS.NAMESPACE"
864
- class="icon icon-folder"
865
- />
866
- <div>{{ opt.label }}</div>
867
- <i
868
- v-if="opt.selected"
869
- class="icon icon-checkmark"
870
- />
903
+ <div
904
+ class="ns-item"
905
+ >
906
+ <i
907
+ v-if="opt.kind === NAMESPACE_FILTER_KINDS.NAMESPACE"
908
+ class="icon icon-folder"
909
+ />
910
+ <div>{{ opt.label }}</div>
911
+ <i
912
+ v-if="opt.selected"
913
+ class="icon icon-checkmark"
914
+ />
915
+ </div>
871
916
  </div>
872
- </div>
917
+ </template>
873
918
  <div
874
919
  v-if="cachedFiltered.length === 0"
875
920
  class="ns-none"
@@ -892,6 +937,7 @@ export default {
892
937
  .ns-filter {
893
938
  width: 280px;
894
939
  display: inline-block;
940
+ border-radius: var(--border-radius);
895
941
 
896
942
  .ns-glass {
897
943
  top: 0;
@@ -910,18 +956,15 @@ export default {
910
956
  }
911
957
 
912
958
  .ns-clear {
959
+ padding: 0 5px;
913
960
  &:hover {
914
961
  color: var(--primary);
915
- cursor: pointer;
916
962
  }
917
963
  }
918
964
 
919
965
  .ns-singleton-info, .ns-clear {
920
966
  align-items: center;
921
967
  display: flex;
922
- > i {
923
- padding-right: 5px;
924
- }
925
968
  }
926
969
 
927
970
  .ns-input {
@@ -938,10 +981,15 @@ export default {
938
981
  cursor: pointer;
939
982
  position: absolute;
940
983
  right: 10px;
941
- top: 5px;
984
+ top: 10px;
942
985
  line-height: 24px;
943
986
  text-align: center;
944
- width: 24px;
987
+ width: 14px;
988
+ min-height: 14px;
989
+ }
990
+
991
+ .ns-chip-button {
992
+ min-height: 14px;
945
993
  }
946
994
 
947
995
  .ns-dropdown-menu {
@@ -430,8 +430,7 @@ export default {
430
430
 
431
431
  return {
432
432
  content,
433
- placement: 'right',
434
- popperOptions: { modifiers: { preventOverflow: { enabled: false }, hide: { enabled: false } } },
433
+ placement: 'right',
435
434
  popperClass
436
435
  };
437
436
  },
@@ -699,7 +698,7 @@ export default {
699
698
  v-if="clustersFiltered.length > 0"
700
699
  class="category-title"
701
700
  >
702
- <hr>
701
+ <hr role="none">
703
702
  </div>
704
703
  </div>
705
704
 
@@ -813,7 +812,7 @@ export default {
813
812
  <div
814
813
  class="category-title"
815
814
  >
816
- <hr>
815
+ <hr role="none">
817
816
  <span>
818
817
  {{ t('nav.categories.multiCluster') }}
819
818
  </span>
@@ -846,7 +845,7 @@ export default {
846
845
  <div
847
846
  class="category-title"
848
847
  >
849
- <hr>
848
+ <hr role="none">
850
849
  <span>
851
850
  {{ t('nav.categories.configuration') }}
852
851
  </span>
@@ -58,7 +58,18 @@ export default {
58
58
 
59
59
  isActive() {
60
60
  const typeFullPath = this.$router.resolve(this.type.route)?.fullPath.toLowerCase();
61
- const pageFullPath = this.$route.fullPath?.toLowerCase();
61
+ const pageFullPath = this.$route.fullPath?.toLowerCase().split('#')[0]; // Ignore the shebang when comparing routes
62
+ const routeMetaNav = this.$route.meta?.nav;
63
+
64
+ // If the route explicitly declares the nav path that should be highlighted, then use that
65
+ if (routeMetaNav) {
66
+ const cluster = this.$route.params?.cluster;
67
+ const navPath = routeMetaNav.replace(':cluster', cluster);
68
+
69
+ if (navPath === typeFullPath) {
70
+ return true;
71
+ }
72
+ }
62
73
 
63
74
  if ( !this.type.exact) {
64
75
  const typeSplit = typeFullPath.split('/');