@rancher/shell 3.0.8-rc.8 → 3.0.8

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 (260) hide show
  1. package/apis/impl/apis.ts +61 -0
  2. package/apis/index.ts +40 -0
  3. package/apis/intf/modal.ts +90 -0
  4. package/apis/intf/shell.ts +36 -0
  5. package/apis/intf/slide-in.ts +98 -0
  6. package/apis/intf/system.ts +41 -0
  7. package/apis/shell/__tests__/modal.test.ts +80 -0
  8. package/apis/shell/__tests__/notifications.test.ts +71 -0
  9. package/apis/shell/__tests__/slide-in.test.ts +54 -0
  10. package/apis/shell/__tests__/system.test.ts +129 -0
  11. package/apis/shell/index.ts +38 -0
  12. package/apis/shell/modal.ts +41 -0
  13. package/apis/shell/notifications.ts +65 -0
  14. package/apis/shell/slide-in.ts +33 -0
  15. package/apis/shell/system.ts +65 -0
  16. package/apis/vue-shim.d.ts +11 -0
  17. package/assets/brand/suse/dark/rancher-logo.svg +1 -64
  18. package/assets/styles/global/_tooltip.scss +6 -1
  19. package/assets/translations/en-us.yaml +14 -1
  20. package/components/ActionMenuShell.vue +3 -1
  21. package/components/BackLink.vue +8 -0
  22. package/components/BannerGraphic.vue +1 -5
  23. package/components/BrandImage.vue +17 -6
  24. package/components/Cron/CronExpressionEditor.vue +1 -1
  25. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  26. package/components/CruResource.vue +8 -1
  27. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +1 -0
  28. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +50 -1
  29. package/components/Drawer/ResourceDetailDrawer/composables.ts +19 -0
  30. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -1
  31. package/components/Drawer/ResourceDetailDrawer/types.ts +2 -1
  32. package/components/LocaleSelector.vue +2 -2
  33. package/components/ModalManager.vue +11 -1
  34. package/components/Questions/__tests__/Yaml.test.ts +1 -1
  35. package/components/Questions/__tests__/index.test.ts +159 -0
  36. package/components/RelatedResources.vue +5 -0
  37. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  38. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  39. package/components/Resource/Detail/Metadata/index.vue +3 -3
  40. package/components/Resource/Detail/ResourcePopover/index.vue +5 -1
  41. package/components/Resource/Detail/composables.ts +2 -2
  42. package/components/ResourceDetail/Masthead/latest.vue +23 -21
  43. package/components/ResourceDetail/index.vue +3 -0
  44. package/components/ResourceTable.vue +54 -21
  45. package/components/SlideInPanelManager.vue +16 -11
  46. package/components/SortableTable/THead.vue +2 -1
  47. package/components/SortableTable/index.vue +20 -2
  48. package/components/Tabbed/__tests__/index.test.ts +86 -0
  49. package/components/Tabbed/index.vue +37 -2
  50. package/components/__tests__/NamespaceFilter.test.ts +49 -0
  51. package/components/auth/SelectPrincipal.vue +28 -6
  52. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  53. package/components/auth/login/ldap.vue +3 -3
  54. package/components/fleet/FleetSecretSelector.vue +1 -1
  55. package/components/form/KeyValue.vue +1 -1
  56. package/components/form/NameNsDescription.vue +1 -1
  57. package/components/form/NodeScheduling.vue +2 -2
  58. package/components/form/ResourceTabs/composable.ts +2 -2
  59. package/components/form/ResourceTabs/index.vue +0 -2
  60. package/components/form/__tests__/NameNsDescription.test.ts +42 -0
  61. package/components/formatter/InternalExternalIP.vue +4 -1
  62. package/components/formatter/LinkName.vue +5 -0
  63. package/components/formatter/__tests__/InternalExternalIP.test.ts +1 -1
  64. package/components/nav/Group.vue +25 -7
  65. package/components/nav/Header.vue +1 -1
  66. package/components/nav/NamespaceFilter.vue +1 -0
  67. package/components/nav/Type.vue +17 -6
  68. package/components/nav/WindowManager/panels/TabBodyContainer.vue +1 -1
  69. package/components/nav/__tests__/Type.test.ts +59 -0
  70. package/components/templates/standalone.vue +1 -1
  71. package/composables/cruResource.ts +27 -0
  72. package/composables/focusTrap.ts +3 -1
  73. package/composables/resourceDetail.ts +15 -0
  74. package/composables/useI18n.ts +10 -1
  75. package/composables/useLabeledFormElement.ts +3 -4
  76. package/config/__test__/uiplugins.test.ts +309 -0
  77. package/config/labels-annotations.js +1 -0
  78. package/config/product/explorer.js +3 -1
  79. package/config/product/fleet.js +1 -1
  80. package/config/router/navigation-guards/clusters.js +3 -3
  81. package/config/router/navigation-guards/products.js +1 -1
  82. package/config/router/routes.js +7 -7
  83. package/config/types.js +7 -0
  84. package/config/uiplugins.js +46 -2
  85. package/core/__tests__/extension-manager-impl.test.js +437 -0
  86. package/core/extension-manager-impl.js +21 -25
  87. package/core/plugin-helpers.ts +2 -2
  88. package/core/plugin.ts +9 -1
  89. package/core/plugins-loader.js +2 -2
  90. package/core/types-provisioning.ts +5 -1
  91. package/core/types.ts +35 -0
  92. package/detail/provisioning.cattle.io.cluster.vue +9 -6
  93. package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
  94. package/dialog/MoveNamespaceDialog.vue +20 -4
  95. package/dialog/RollbackWorkloadDialog.vue +2 -5
  96. package/dialog/SearchDialog.vue +1 -0
  97. package/dialog/__tests__/MoveNamespaceDialog.test.ts +249 -0
  98. package/directives/__tests__/clean-tooltip.test.ts +298 -0
  99. package/directives/clean-tooltip.ts +234 -0
  100. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -2
  101. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +100 -3
  102. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  103. package/edit/configmap.vue +1 -0
  104. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  105. package/edit/fleet.cattle.io.helmop.vue +11 -6
  106. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  107. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  108. package/edit/logging-flow/index.vue +1 -0
  109. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  110. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  111. package/edit/management.cattle.io.project.vue +1 -0
  112. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  113. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  114. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  115. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  116. package/edit/monitoring.coreos.com.route.vue +1 -1
  117. package/edit/namespace.vue +1 -0
  118. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  119. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  120. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  121. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  122. package/edit/node.vue +1 -0
  123. package/edit/persistentvolume/index.vue +27 -22
  124. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  125. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  126. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  127. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  128. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  129. package/edit/persistentvolume/plugins/csi.vue +18 -16
  130. package/edit/persistentvolume/plugins/fc.vue +13 -14
  131. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  132. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  133. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  134. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  135. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  136. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  137. package/edit/persistentvolume/plugins/local.vue +1 -3
  138. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  139. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  140. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  141. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  142. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  143. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  144. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  145. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  146. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  147. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
  148. package/edit/provisioning.cattle.io.cluster/index.vue +5 -5
  149. package/edit/provisioning.cattle.io.cluster/rke2.vue +9 -8
  150. package/edit/resources.cattle.io.restore.vue +1 -1
  151. package/edit/secret/index.vue +1 -1
  152. package/edit/service.vue +1 -0
  153. package/edit/serviceaccount.vue +1 -0
  154. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  155. package/edit/workload/Job.vue +2 -2
  156. package/edit/workload/index.vue +2 -1
  157. package/edit/workload/mixins/workload.js +1 -1
  158. package/initialize/App.vue +4 -4
  159. package/initialize/install-plugins.js +19 -5
  160. package/machine-config/azure.vue +1 -1
  161. package/machine-config/components/GCEImage.vue +1 -1
  162. package/mixins/__tests__/brand.spec.ts +2 -2
  163. package/mixins/brand.js +1 -7
  164. package/mixins/create-edit-view/index.js +5 -0
  165. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +128 -5
  166. package/models/chart.js +70 -74
  167. package/models/management.cattle.io.cluster.js +21 -3
  168. package/models/provisioning.cattle.io.cluster.js +31 -11
  169. package/package.json +11 -10
  170. package/pages/auth/login.vue +4 -6
  171. package/pages/auth/setup.vue +1 -1
  172. package/pages/auth/verify.vue +3 -3
  173. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  174. package/pages/c/_cluster/apps/charts/chart.vue +33 -15
  175. package/pages/c/_cluster/apps/charts/index.vue +122 -24
  176. package/pages/c/_cluster/apps/charts/install.vue +33 -0
  177. package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
  178. package/pages/c/_cluster/explorer/index.vue +8 -6
  179. package/pages/c/_cluster/fleet/index.vue +4 -7
  180. package/pages/c/_cluster/manager/hostedprovider/index.vue +12 -6
  181. package/pages/c/_cluster/settings/brand.vue +1 -1
  182. package/pages/c/_cluster/settings/index.vue +5 -0
  183. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  184. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  185. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  186. package/pkg/auto-import.js +3 -3
  187. package/pkg/dynamic-importer.lib.js +1 -1
  188. package/pkg/import.js +1 -1
  189. package/plugins/__tests__/mutations.tests.ts +179 -0
  190. package/plugins/dashboard-client-init.js +3 -0
  191. package/plugins/dashboard-store/getters.js +19 -2
  192. package/plugins/dashboard-store/model-loader.js +1 -1
  193. package/plugins/dashboard-store/mutations.js +23 -2
  194. package/plugins/dashboard-store/resource-class.js +11 -5
  195. package/plugins/i18n.js +8 -0
  196. package/plugins/plugin.js +2 -2
  197. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +506 -0
  198. package/plugins/steve/steve-class.js +1 -1
  199. package/plugins/steve/steve-pagination-utils.ts +131 -47
  200. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  201. package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
  202. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -42
  203. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  204. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  205. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  206. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  207. package/rancher-components/Pill/types.ts +0 -1
  208. package/rancher-components/RcDropdown/useDropdownContext.ts +2 -4
  209. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  210. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  211. package/rancher-components/RcIcon/index.ts +1 -0
  212. package/rancher-components/RcIcon/types.ts +160 -0
  213. package/rancher-components/RcItemCard/RcItemCard.vue +1 -1
  214. package/rancher-components/utils/status.test.ts +67 -0
  215. package/rancher-components/utils/status.ts +77 -0
  216. package/scripts/publish-shell.sh +25 -0
  217. package/scripts/typegen.sh +1 -0
  218. package/store/__tests__/catalog.test.ts +1 -1
  219. package/store/__tests__/type-map.test.ts +164 -2
  220. package/store/action-menu.js +8 -0
  221. package/store/auth.js +25 -13
  222. package/store/catalog.js +6 -0
  223. package/store/i18n.js +3 -3
  224. package/store/index.js +8 -6
  225. package/store/notifications.ts +2 -0
  226. package/store/prefs.js +6 -7
  227. package/store/type-map.js +17 -7
  228. package/store/wm.ts +4 -4
  229. package/types/internal-api/shell/modal.d.ts +6 -6
  230. package/types/notifications/index.ts +126 -15
  231. package/types/rancher/index.d.ts +9 -0
  232. package/types/shell/index.d.ts +54 -3
  233. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  234. package/types/store/pagination.types.ts +157 -9
  235. package/types/vue-shim.d.ts +5 -4
  236. package/utils/__tests__/provider.test.ts +98 -0
  237. package/utils/__tests__/router.test.js +238 -0
  238. package/utils/__tests__/selector-typed.test.ts +263 -0
  239. package/utils/cluster.js +4 -1
  240. package/utils/color.js +1 -1
  241. package/utils/dynamic-content/__tests__/info.test.ts +6 -0
  242. package/utils/dynamic-content/info.ts +43 -0
  243. package/utils/favicon.js +4 -4
  244. package/utils/fleet.ts +8 -1
  245. package/utils/pagination-utils.ts +2 -2
  246. package/utils/pagination-wrapper.ts +1 -1
  247. package/utils/provider.ts +14 -0
  248. package/utils/router.js +50 -0
  249. package/utils/selector-typed.ts +6 -2
  250. package/utils/unit-tests/pagination-utils.spec.ts +8 -8
  251. package/vue.config.js +3 -3
  252. package/composables/useExtensionManager.ts +0 -17
  253. package/core/plugins.js +0 -38
  254. package/directives/clean-tooltip.js +0 -32
  255. package/plugins/internal-api/index.ts +0 -37
  256. package/plugins/internal-api/shared/base-api.ts +0 -13
  257. package/plugins/internal-api/shell/shell.api.ts +0 -108
  258. package/plugins/nuxt-client-init.js +0 -3
  259. package/types/internal-api/shell/growl.d.ts +0 -25
  260. package/types/internal-api/shell/slideIn.d.ts +0 -15
@@ -39,10 +39,10 @@ export const useTabCountWatcher = () => {
39
39
 
40
40
  export const useTabCountUpdater = () => {
41
41
  const tabKey = randomStr();
42
- const updateCount = inject<UpdateCountFn>(UPDATE_COUNT_PROVIDER_KEY);
42
+ const updateCount = inject<UpdateCountFn>(UPDATE_COUNT_PROVIDER_KEY, () => { });
43
43
 
44
44
  const updateTabCount = (count: number | undefined) => {
45
- updateCount?.(tabKey, count);
45
+ updateCount(tabKey, count);
46
46
  };
47
47
 
48
48
  const clearTabCount = () => updateTabCount(undefined);
@@ -101,7 +101,6 @@ export default {
101
101
  return {
102
102
  eventSchema,
103
103
  EVENT,
104
- selectedTab: this.defaultTab,
105
104
  inStore,
106
105
  showConditions: false,
107
106
  paginationHeaders
@@ -245,7 +244,6 @@ export default {
245
244
  :default-tab="defaultTab"
246
245
  :resource="value"
247
246
  :use-hash="useHash"
248
- @changed="tabChange"
249
247
  >
250
248
  <slot />
251
249
  <Tab
@@ -87,6 +87,48 @@ describe('component: NameNsDescription', () => {
87
87
  expect(wrapper.emitted().isNamespaceNew?.[0][0]).toBe(true);
88
88
  });
89
89
 
90
+ it('should set the namespace using the namespaceKey prop', () => {
91
+ const namespaceName = 'custom-namespace';
92
+ const store = createStore({
93
+ getters: {
94
+ allowedNamespaces: () => () => ({ [namespaceName]: true }),
95
+ currentStore: () => () => 'cluster',
96
+ 'cluster/schemaFor': () => jest.fn()
97
+ }
98
+ });
99
+
100
+ const wrapper = mount(NameNsDescription, {
101
+ props: {
102
+ value: {
103
+ setAnnotation: jest.fn(),
104
+ metadata: {},
105
+ value: { metadata: { namespace: namespaceName } }
106
+ },
107
+ mode: 'create',
108
+ namespaceKey: 'value.metadata.namespace',
109
+ },
110
+ global: {
111
+ provide: { store },
112
+ mocks: {
113
+ $store: {
114
+ dispatch: jest.fn(),
115
+ getters: {
116
+ namespaces: jest.fn(),
117
+ 'customizations/getPreviewCluster': {
118
+ ready: true,
119
+ isLocal: false,
120
+ badge: {},
121
+ },
122
+ 'i18n/t': jest.fn(),
123
+ },
124
+ },
125
+ },
126
+ },
127
+ });
128
+
129
+ expect((wrapper.vm as any).namespace).toBe(namespaceName);
130
+ });
131
+
90
132
  it('renders the name input with the expected value', () => {
91
133
  const namespaceName = 'test';
92
134
  const store = createStore({
@@ -108,10 +108,13 @@ export default {
108
108
  >
109
109
  <template #default>
110
110
  <RcStatusBadge
111
- v-clean-tooltip="tooltipContent"
111
+ v-clean-tooltip="{content: tooltipContent, triggers: ['hover', 'focus']}"
112
+ :aria-label="t('generic.plusMore', {n: remainingIpCount})"
113
+ tabindex="0"
112
114
  status="info"
113
115
  data-testid="plus-more"
114
116
  @click.stop
117
+ @keyup.enter.space="$refs.dropdown.show()"
115
118
  >
116
119
  {{ t('generic.plusMore', {n: remainingIpCount}) }}
117
120
  </RcStatusBadge>
@@ -41,6 +41,11 @@ export default {
41
41
  product: this.product || EXPLORER,
42
42
  };
43
43
 
44
+ // Having an undefined param can yield a console warning like [Vue Router warn]: Discarded invalid param(s) "namespace" when navigating
45
+ if (!params.namespace) {
46
+ delete params.namespace;
47
+ }
48
+
44
49
  return { name, params };
45
50
  },
46
51
 
@@ -24,7 +24,7 @@ describe('component: InternalExternalIP', () => {
24
24
  components: { 'v-dropdown': { name: 'v-dropdown', template: '<div><slot /><slot name="popper" /></div>' } },
25
25
  directives: {
26
26
  'clean-tooltip': (el, binding) => {
27
- el.setAttribute('v-clean-tooltip', binding.value);
27
+ el.setAttribute('v-clean-tooltip', binding.value.content);
28
28
  }
29
29
  },
30
30
  mocks: { $store: { getters: mockGetters } }
@@ -1,5 +1,6 @@
1
1
  <script>
2
2
  import Type from '@shell/components/nav/Type';
3
+ import { filterLocationValidParams } from '@shell/utils/router';
3
4
  export default {
4
5
  name: 'Group',
5
6
 
@@ -41,6 +42,11 @@ export default {
41
42
  fixedOpen: {
42
43
  type: Boolean,
43
44
  default: false,
45
+ },
46
+
47
+ highlightRoute: {
48
+ type: Boolean,
49
+ default: true,
44
50
  }
45
51
  },
46
52
 
@@ -73,7 +79,8 @@ export default {
73
79
  const overviewRoute = grp?.route;
74
80
 
75
81
  if (overviewRoute && grp.overview) {
76
- const route = this.$router.resolve(overviewRoute || {});
82
+ const validRoute = filterLocationValidParams(this.$router, overviewRoute || {});
83
+ const route = this.$router.resolve(validRoute);
77
84
 
78
85
  return this.$route.fullPath.split('#')[0] === route?.fullPath;
79
86
  }
@@ -89,6 +96,10 @@ export default {
89
96
  set(v) {
90
97
  this.expanded = v;
91
98
  }
99
+ },
100
+
101
+ headerRoute() {
102
+ return filterLocationValidParams(this.$router, this.group.children[0].route);
92
103
  }
93
104
  },
94
105
 
@@ -134,7 +145,9 @@ export default {
134
145
  const route = item.route;
135
146
 
136
147
  if (route) {
137
- this.$router.replace(route);
148
+ const validRoute = filterLocationValidParams(this.$router, route);
149
+
150
+ this.$router.replace(validRoute);
138
151
  } else if (item) {
139
152
  this.routeToFirstChild(item);
140
153
  }
@@ -143,7 +156,9 @@ export default {
143
156
 
144
157
  routeToFirstChild(item) {
145
158
  if (item.children.length && item.children[0].route) {
146
- this.$router.replace(item.children[0].route);
159
+ const validRoute = filterLocationValidParams(this.$router, item.children[0].route);
160
+
161
+ this.$router.replace(validRoute);
147
162
  }
148
163
  },
149
164
 
@@ -191,7 +206,8 @@ export default {
191
206
  const matchesNavLevel = navLevels.filter((param) => !this.$route.params[param] || this.$route.params[param] !== item.route.params[param]).length === 0;
192
207
  const withoutHash = this.$route.hash ? this.$route.fullPath.slice(0, this.$route.fullPath.indexOf(this.$route.hash)) : this.$route.fullPath;
193
208
  const withoutQuery = withoutHash.split('?')[0];
194
- const itemFullPath = this.$router.resolve(item.route).fullPath;
209
+ const validItemRoute = filterLocationValidParams(this.$router, item.route);
210
+ const itemFullPath = this.$router.resolve(validItemRoute).fullPath;
195
211
 
196
212
  if (matchesNavLevel || itemFullPath === withoutQuery) {
197
213
  return true;
@@ -233,7 +249,7 @@ export default {
233
249
  <template>
234
250
  <div
235
251
  class="accordion"
236
- :class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': isGroupActive }"
252
+ :class="{[`depth-${depth}`]: true, 'expanded': isExpanded, 'has-children': hasChildren, 'group-highlight': highlightRoute && isGroupActive }"
237
253
  >
238
254
  <div
239
255
  v-if="showHeader || (!onlyHasOverview && canCollapse)"
@@ -242,7 +258,7 @@ export default {
242
258
  <div
243
259
  v-if="showHeader"
244
260
  class="header"
245
- :class="{'active': isOverview, 'noHover': !canCollapse || fixedOpen}"
261
+ :class="{'active': highlightRoute && isOverview, 'noHover': !canCollapse || fixedOpen}"
246
262
  role="button"
247
263
  :tabindex="fixedOpen ? -1 : 0"
248
264
  :aria-label="group.labelDisplay || group.label || ''"
@@ -253,7 +269,7 @@ export default {
253
269
  <slot name="header">
254
270
  <router-link
255
271
  v-if="hasOverview"
256
- :to="group.children[0].route"
272
+ :to="headerRoute"
257
273
  :exact="group.children[0].exact"
258
274
  :tabindex="-1"
259
275
  >
@@ -311,6 +327,7 @@ export default {
311
327
  :can-collapse="canCollapse"
312
328
  :group="child"
313
329
  :fixed-open="fixedOpen"
330
+ :highlight-route="highlightRoute"
314
331
  @selected="groupSelected($event)"
315
332
  @expand="expandGroup($event)"
316
333
  @close="close($event)"
@@ -322,6 +339,7 @@ export default {
322
339
  :is-root="depth == 0 && !showHeader"
323
340
  :type="child"
324
341
  :depth="depth"
342
+ :highlight-route="highlightRoute"
325
343
  @selected="selectType($event)"
326
344
  />
327
345
  </template>
@@ -254,7 +254,7 @@ export default {
254
254
  this.extensionHeaderActions = getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, neu);
255
255
  this.updateExtensionActionsEnabled();
256
256
 
257
- this.navHeaderRight = this.$plugin?.getDynamic('component', 'NavHeaderRight');
257
+ this.navHeaderRight = this.$extension?.getDynamic('component', 'NavHeaderRight');
258
258
  }
259
259
  },
260
260
  immediate: true,
@@ -841,6 +841,7 @@ export default {
841
841
  tabindex="0"
842
842
  class="ns-filter-input"
843
843
  :aria-label="t('namespaceFilter.input')"
844
+ @mousedown.stop
844
845
  @click="focusFilter"
845
846
  @keydown="inputKeyHandler($event)"
846
847
  >
@@ -3,6 +3,7 @@ import Favorite from '@shell/components/nav/Favorite';
3
3
  import { TYPE_MODES } from '@shell/store/type-map';
4
4
 
5
5
  import TabTitle from '@shell/components/TabTitle';
6
+ import { filterLocationValidParams } from '@shell/utils/router';
6
7
 
7
8
  const showFavoritesFor = [TYPE_MODES.FAVORITE, TYPE_MODES.USED];
8
9
 
@@ -27,6 +28,11 @@ export default {
27
28
  type: Number,
28
29
  default: 0,
29
30
  },
31
+
32
+ highlightRoute: {
33
+ type: Boolean,
34
+ default: true,
35
+ },
30
36
  },
31
37
 
32
38
  data() {
@@ -57,7 +63,7 @@ export default {
57
63
  },
58
64
 
59
65
  isActive() {
60
- const typeFullPath = this.$router.resolve(this.type.route)?.fullPath.toLowerCase();
66
+ const typeFullPath = this.$router.resolve(this.typeRoute)?.fullPath.toLowerCase();
61
67
  const pageFullPath = this.$route.fullPath?.toLowerCase().split('#')[0]; // Ignore the shebang when comparing routes
62
68
  const routeMetaNav = this.$route.meta?.nav;
63
69
 
@@ -88,6 +94,10 @@ export default {
88
94
  }
89
95
 
90
96
  return typeFullPath === pageFullPath;
97
+ },
98
+
99
+ typeRoute() {
100
+ return filterLocationValidParams(this.$router, this.type.route);
91
101
  }
92
102
 
93
103
  },
@@ -100,7 +110,8 @@ export default {
100
110
  selectType() {
101
111
  // Prevent issues if custom NavLink is used #5047
102
112
  if (this.type?.route) {
103
- const typePath = this.$router.resolve(this.type.route)?.fullPath;
113
+ const validRoute = filterLocationValidParams(this.$router, this.type.route);
114
+ const typePath = this.$router.resolve(validRoute)?.fullPath;
104
115
 
105
116
  if (typePath !== this.$route.fullPath) {
106
117
  this.$emit('selected');
@@ -117,16 +128,16 @@ export default {
117
128
  :key="type.name"
118
129
  v-slot="{ href, navigate,isExactActive }"
119
130
  custom
120
- :to="type.route"
131
+ :to="typeRoute"
121
132
  >
122
133
  <li
123
134
  class="child nav-type"
124
- :class="{'root': isRoot, [`depth-${depth}`]: true, 'router-link-active': isActive, 'router-link-exact-active': isExactActive}"
135
+ :class="{'root': isRoot, [`depth-${depth}`]: true, 'router-link-active': highlightRoute && isActive, 'router-link-exact-active': highlightRoute && isExactActive}"
125
136
  @click="navigate"
126
137
  @keypress.enter="navigate"
127
138
  >
128
139
  <TabTitle
129
- v-if="isExactActive"
140
+ v-if="highlightRoute && isExactActive"
130
141
  :show-child="false"
131
142
  >
132
143
  {{ type.labelKey ? t(type.labelKey) : (type.labelDisplay || type.label) }}
@@ -136,7 +147,7 @@ export default {
136
147
  :aria-label="type.labelKey ? t(type.labelKey) : (type.labelDisplay || type.label)"
137
148
  :href="href"
138
149
  class="type-link"
139
- :aria-current="isActive ? 'page' : undefined"
150
+ :aria-current="highlightRoute && isActive ? 'page' : undefined"
140
151
  @click="selectType(); navigate($event);"
141
152
  @mouseenter="setNear(true)"
142
153
  @mouseleave="setNear(false)"
@@ -7,7 +7,7 @@
7
7
  </template>
8
8
 
9
9
  <script setup>
10
- import { onMounted, defineProps, defineEmits } from 'vue';
10
+ import { onMounted } from 'vue';
11
11
 
12
12
  /**
13
13
  * This component serves as a container for the body of a tab within a window manager panel.
@@ -250,6 +250,65 @@ describe('component: Type', () => {
250
250
  });
251
251
  });
252
252
 
253
+ describe('should respect highlightRoute prop', () => {
254
+ it('should not use active class when highlightRoute is false even if route is active', () => {
255
+ const wrapper = shallowMount(Type as any, {
256
+ props: { type: defaultRouteTypeProp, highlightRoute: false },
257
+
258
+ global: {
259
+ directives: { cleanHtml: (identity) => identity },
260
+
261
+ mocks: {
262
+ $store: storeMock, $router: routerMock, $route: routeMock
263
+ },
264
+ stubs: { routerLink: createChildRenderingRouterLinkStub() },
265
+ },
266
+ });
267
+
268
+ const elementWithSelector = wrapper.find(`.${ activeClass }`);
269
+
270
+ expect(elementWithSelector.exists()).toBe(false);
271
+ });
272
+
273
+ it('should not use exact active class when highlightRoute is false even if route is exact active', () => {
274
+ const wrapper = shallowMount(Type as any, {
275
+ props: { type: defaultRouteTypeProp, highlightRoute: false },
276
+
277
+ global: {
278
+ directives: { cleanHtml: (identity) => identity },
279
+
280
+ mocks: {
281
+ $store: storeMock, $router: routerMock, $route: routeMock
282
+ },
283
+ stubs: { routerLink: createChildRenderingRouterLinkStub({ isExactActive: true }) },
284
+ },
285
+ });
286
+
287
+ const elementWithSelector = wrapper.find(`.${ exactActiveClass }`);
288
+
289
+ expect(elementWithSelector.exists()).toBe(false);
290
+ });
291
+
292
+ it('should use active class when highlightRoute is true (default) and route is active', () => {
293
+ const wrapper = shallowMount(Type as any, {
294
+ props: { type: defaultRouteTypeProp, highlightRoute: true },
295
+
296
+ global: {
297
+ directives: { cleanHtml: (identity) => identity },
298
+
299
+ mocks: {
300
+ $store: storeMock, $router: routerMock, $route: routeMock
301
+ },
302
+ stubs: { routerLink: createChildRenderingRouterLinkStub() },
303
+ },
304
+ });
305
+
306
+ const elementWithSelector = wrapper.find(`.${ activeClass }`);
307
+
308
+ expect(elementWithSelector.exists()).toBe(true);
309
+ });
310
+ });
311
+
253
312
  describe('should handle the favorite icon appropriately', () => {
254
313
  it('should show favorite icon if mouse is over and type is favorite', async() => {
255
314
  const wrapper = shallowMount(Type as any, {
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <style lang="scss">
6
- body, #__nuxt, #__layout {
6
+ body, #__root, #__layout {
7
7
  height: 100%;
8
8
  }
9
9
  </style>
@@ -0,0 +1,27 @@
1
+ import { inject, provide } from 'vue';
2
+ const IS_IN_RESOURCE_EDIT_PAGE_KEY = 'isInResourceEditKey';
3
+ const IS_IN_RESOURCE_CREATE_PAGE_KEY = 'isInResourceCreateKey';
4
+
5
+ /**
6
+ * Used to determine if the current component was instantiated as an ancestor of a CruResource EDIT page.
7
+ * @returns true if the component is an ancestor of CruResource EDIT page, otherwise false
8
+ */
9
+ export function useIsInResourceEditPage() {
10
+ return inject(IS_IN_RESOURCE_EDIT_PAGE_KEY, false);
11
+ }
12
+
13
+ /**
14
+ * Used to determine if the current component was instantiated as an ancestor of a CruResource CREATE page.
15
+ * @returns true if the component is an ancestor of CruResource CREATE page, otherwise false
16
+ */
17
+ export function useIsInResourceCreatePage() {
18
+ return inject(IS_IN_RESOURCE_CREATE_PAGE_KEY, false);
19
+ }
20
+
21
+ export function useResourceEditPageProvider() {
22
+ provide(IS_IN_RESOURCE_EDIT_PAGE_KEY, true);
23
+ }
24
+
25
+ export function useResourceCreatePageProvider() {
26
+ provide(IS_IN_RESOURCE_CREATE_PAGE_KEY, true);
27
+ }
@@ -57,8 +57,10 @@ export function useWatcherBasedSetupFocusTrapWithDestroyIncluded(watchVar:any, f
57
57
 
58
58
  focusTrapInstance = createFocusTrap(focusEl, opts);
59
59
 
60
+ const activate = () => focusTrapInstance.activate();
61
+
60
62
  nextTick(() => {
61
- focusTrapInstance.activate();
63
+ setTimeout(activate, 0);
62
64
  });
63
65
  });
64
66
  } else if (!neu && focusTrapInstance && Object.keys(focusTrapInstance).length && !useUnmountHook) {
@@ -0,0 +1,15 @@
1
+ import { inject, provide } from 'vue';
2
+
3
+ const IS_IN_RESOURCE_DETAIL_PAGE_KEY = 'isInResourceDetailKey';
4
+
5
+ /**
6
+ * Used to determine if the current component was instantiated as an ancestor of a ResourceDetail.
7
+ * @returns true if the component is an ancestor of ResourceDetail, otherwise false
8
+ */
9
+ export function useIsInResourceDetailPage() {
10
+ return inject(IS_IN_RESOURCE_DETAIL_PAGE_KEY, false);
11
+ }
12
+
13
+ export function useResourceDetailPageProvider() {
14
+ provide(IS_IN_RESOURCE_DETAIL_PAGE_KEY, true);
15
+ }
@@ -11,7 +11,16 @@ let store: Store<any> | null = null;
11
11
  * @returns A translated string or the raw value if the raw parameter is set to true.
12
12
  */
13
13
  const t = (key: string, args?: unknown, raw?: boolean): string => {
14
- return stringFor(store, key, args, raw);
14
+ if (!store) {
15
+ if (!!process.env.dev) {
16
+ // eslint-disable-next-line no-console
17
+ console.warn('useI18n: store not available');
18
+ }
19
+
20
+ return key;
21
+ }
22
+
23
+ return stringFor(store, key, args as any, raw);
15
24
  };
16
25
 
17
26
  export type I18n = { t: typeof t };
@@ -1,5 +1,6 @@
1
1
  import {
2
- ref, computed, ComputedRef, Ref, defineEmits
2
+ ref, computed, ComputedRef, Ref,
3
+ EmitFn
3
4
  } from 'vue';
4
5
  import { _VIEW, _EDIT } from '@shell/config/query-params';
5
6
 
@@ -72,9 +73,7 @@ export const labeledFormElementProps = {
72
73
  }
73
74
  };
74
75
 
75
- const labeledFormElementEmits = defineEmits(['update:validation']);
76
-
77
- export const useLabeledFormElement = (props: LabeledFormElementProps, emit: typeof labeledFormElementEmits): UseLabeledFormElement => {
76
+ export const useLabeledFormElement = (props: LabeledFormElementProps, emit: EmitFn<['update:validation']>): UseLabeledFormElement => {
78
77
  const raised = ref(props.mode === _VIEW || !!`${ props.value }`);
79
78
  const focused = ref(false);
80
79
  const blurred = ref<number | null>(null);