@rancher/shell 3.0.5-rc.5 → 3.0.5-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/assets/data/aws-regions.json +1 -0
  2. package/assets/images/key.svg +17 -0
  3. package/assets/styles/base/_spacing.scss +2 -2
  4. package/assets/styles/global/_form.scss +1 -1
  5. package/assets/styles/global/_labeled-input.scss +1 -1
  6. package/assets/styles/themes/_dark.scss +3 -0
  7. package/assets/styles/themes/_light.scss +3 -0
  8. package/assets/styles/vendor/vue-select.scss +1 -1
  9. package/assets/translations/en-us.yaml +404 -64
  10. package/assets/translations/zh-hans.yaml +3 -4
  11. package/cloud-credential/gcp.vue +9 -1
  12. package/components/AppModal.vue +2 -0
  13. package/components/CodeMirror.vue +1 -1
  14. package/components/ConfigMapSettings/Settings.vue +377 -0
  15. package/components/ConfigMapSettings/index.vue +354 -0
  16. package/components/CruResource.vue +1 -2
  17. package/components/DetailText.vue +61 -11
  18. package/components/Drawer/Chrome.vue +116 -0
  19. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +61 -0
  20. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +48 -0
  21. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +54 -0
  22. package/components/Drawer/ResourceDetailDrawer/__tests__/YamlTab.test.ts +80 -0
  23. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +82 -0
  24. package/components/Drawer/ResourceDetailDrawer/__tests__/helpers.test.ts +42 -0
  25. package/components/Drawer/ResourceDetailDrawer/composables.ts +50 -0
  26. package/components/Drawer/ResourceDetailDrawer/helpers.ts +10 -0
  27. package/components/Drawer/ResourceDetailDrawer/index.vue +110 -0
  28. package/components/GrowlManager.vue +16 -15
  29. package/components/IconOrSvg.vue +5 -0
  30. package/components/KeyValueView.vue +1 -1
  31. package/components/LocaleSelector.vue +9 -1
  32. package/components/ProgressBarMulti.vue +1 -0
  33. package/components/PromptModal.vue +6 -1
  34. package/components/RelatedResources.vue +4 -12
  35. package/components/Resource/Detail/Additional.vue +46 -0
  36. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  37. package/components/Resource/Detail/Metadata/Annotations/index.vue +5 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +223 -0
  39. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +37 -254
  40. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +298 -0
  41. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +27 -5
  42. package/components/Resource/Detail/Metadata/KeyValue.vue +25 -17
  43. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -1
  44. package/components/Resource/Detail/Metadata/Labels/index.vue +4 -0
  45. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +1 -1
  46. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +1 -1
  47. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +75 -0
  48. package/components/Resource/Detail/Metadata/composables.ts +60 -11
  49. package/components/Resource/Detail/Metadata/index.vue +12 -5
  50. package/components/Resource/Detail/Page.vue +15 -0
  51. package/components/Resource/Detail/ResourceRow.vue +37 -18
  52. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/composables.test.ts +29 -0
  53. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/index.test.ts +48 -0
  54. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/composables.ts +31 -0
  55. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/index.vue +50 -0
  56. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/__tests__/composables.test.ts +66 -0
  57. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/composables.ts +21 -0
  58. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/index.vue +31 -0
  59. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Basic.vue +45 -0
  60. package/components/Resource/Detail/ResourceTabs/SecretDataTab/BasicAuth.vue +31 -0
  61. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Certificate.vue +31 -0
  62. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Registry.vue +22 -0
  63. package/components/Resource/Detail/ResourceTabs/SecretDataTab/ServiceAccountToken.vue +31 -0
  64. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Ssh.vue +32 -0
  65. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Basic.test.ts +40 -0
  66. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/BasicAuth.test.ts +33 -0
  67. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Certificate.test.ts +33 -0
  68. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Registry.test.ts +27 -0
  69. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/ServiceAccountToken.test.ts +33 -0
  70. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Ssh.test.ts +33 -0
  71. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/auth-types.test.ts +186 -0
  72. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/composables.test.ts +102 -0
  73. package/components/Resource/Detail/ResourceTabs/SecretDataTab/auth-types.ts +109 -0
  74. package/components/Resource/Detail/ResourceTabs/SecretDataTab/composeables.ts +52 -0
  75. package/components/Resource/Detail/ResourceTabs/SecretDataTab/index.vue +71 -0
  76. package/components/Resource/Detail/TitleBar/Title.vue +2 -1
  77. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +1 -1
  78. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +1 -1
  79. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +63 -0
  80. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  81. package/components/Resource/Detail/TitleBar/composables.ts +44 -0
  82. package/components/Resource/Detail/TitleBar/index.vue +83 -11
  83. package/components/Resource/Detail/composables.ts +45 -0
  84. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +70 -0
  85. package/components/ResourceDetail/{__tests__/Masthead.test.ts → Masthead/__tests__/legacy.test.ts} +3 -3
  86. package/components/ResourceDetail/Masthead/index.vue +65 -0
  87. package/components/ResourceDetail/Masthead/latest.vue +44 -0
  88. package/components/ResourceDetail/__tests__/index.test.ts +26 -5
  89. package/components/ResourceDetail/index.vue +30 -16
  90. package/components/ResourceDetail/legacy.vue +18 -1
  91. package/components/ResourceList/Masthead.vue +6 -0
  92. package/components/ResourceYaml.vue +14 -1
  93. package/components/SlideInPanelManager.vue +46 -7
  94. package/components/StateDot/index.vue +28 -0
  95. package/components/Tabbed/index.vue +11 -15
  96. package/components/Wizard.vue +4 -2
  97. package/components/__tests__/ConfigMapSettings.test.ts +376 -0
  98. package/components/__tests__/GrowlManager.test.ts +0 -25
  99. package/components/auth/login/ldap.vue +1 -1
  100. package/components/fleet/FleetApplications.vue +0 -7
  101. package/components/fleet/FleetClusterTargets/TargetsList.vue +66 -0
  102. package/components/fleet/FleetClusterTargets/index.vue +455 -0
  103. package/components/fleet/FleetClusters.vue +25 -6
  104. package/components/fleet/FleetGitRepoPaths.vue +476 -0
  105. package/components/fleet/FleetHelmOps.vue +8 -0
  106. package/components/fleet/FleetRepos.vue +1 -6
  107. package/components/fleet/FleetResources.vue +4 -5
  108. package/components/fleet/FleetValuesFrom.vue +295 -0
  109. package/components/fleet/__tests__/FleetClusterTargets.test.ts +1224 -0
  110. package/components/fleet/__tests__/FleetGitRepoPaths.test.ts +265 -0
  111. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +13 -13
  112. package/components/fleet/__tests__/FleetValuesFrom.test.ts +300 -0
  113. package/components/fleet/dashboard/ResourceCard.vue +1 -0
  114. package/components/fleet/dashboard/ResourceCardSummary.vue +1 -5
  115. package/components/fleet/dashboard/ResourceDetails.vue +8 -10
  116. package/components/fleet/dashboard/ResourcePanel.vue +15 -8
  117. package/components/form/ArrayList.vue +13 -2
  118. package/components/form/ChangePassword.vue +3 -1
  119. package/components/form/Footer.vue +10 -4
  120. package/components/form/KeyValue.vue +81 -43
  121. package/components/form/LabeledSelect.vue +56 -16
  122. package/components/form/Labels.vue +90 -17
  123. package/components/form/MatchExpressions.vue +46 -5
  124. package/components/form/NameNsDescription.vue +1 -1
  125. package/components/form/ResourceSelector.vue +1 -0
  126. package/components/form/ResourceTabs/index.vue +5 -0
  127. package/components/form/SecretSelector.vue +9 -2
  128. package/components/form/Select.vue +57 -19
  129. package/components/form/SimpleSecretSelector.vue +9 -2
  130. package/components/form/Taints.vue +21 -2
  131. package/components/form/UnitInput.vue +8 -0
  132. package/components/form/ValueFromResource.vue +1 -1
  133. package/components/form/__tests__/LabeledSelect.test.ts +8 -4
  134. package/components/form/__tests__/Labels.test.ts +360 -0
  135. package/components/form/__tests__/MatchExpressions.test.ts +16 -13
  136. package/components/form/__tests__/Select.test.ts +5 -2
  137. package/components/formatter/FleetApplicationSource.vue +1 -1
  138. package/components/formatter/WorkloadHealthScale.vue +1 -1
  139. package/components/google/AccountAccess.vue +211 -0
  140. package/components/google/types/gcp.d.ts +136 -0
  141. package/components/google/types/index.d.ts +101 -0
  142. package/components/google/util/__mocks__/gcp.ts +465 -0
  143. package/components/google/util/formatter.ts +82 -0
  144. package/components/google/util/gcp.ts +134 -0
  145. package/components/google/util/index.d.ts +11 -0
  146. package/components/nav/Favorite.vue +1 -1
  147. package/components/nav/Group.vue +70 -47
  148. package/components/nav/Header.vue +5 -1
  149. package/components/nav/NamespaceFilter.vue +13 -1
  150. package/components/nav/NotificationCenter/Notification.vue +510 -0
  151. package/components/nav/NotificationCenter/NotificationHeader.vue +112 -0
  152. package/components/nav/NotificationCenter/index.vue +148 -0
  153. package/composables/drawer.ts +26 -0
  154. package/composables/resources.test.ts +63 -0
  155. package/composables/resources.ts +38 -0
  156. package/composables/useIsNewDetailPageEnabled.ts +17 -0
  157. package/config/labels-annotations.js +6 -0
  158. package/config/product/auth.js +16 -1
  159. package/config/product/{cis.js → compliance.js} +23 -26
  160. package/config/product/explorer.js +5 -1
  161. package/config/product/fleet.js +7 -0
  162. package/config/product/settings.js +22 -11
  163. package/config/query-params.js +3 -0
  164. package/config/roles.ts +1 -1
  165. package/config/router/navigation-guards/authentication.js +51 -2
  166. package/config/router/routes.js +27 -31
  167. package/config/settings.ts +21 -3
  168. package/config/store.js +2 -0
  169. package/config/system-namespaces.js +1 -1
  170. package/config/table-headers.js +2 -2
  171. package/config/types.js +15 -6
  172. package/core/plugin.ts +32 -7
  173. package/core/types.ts +18 -1
  174. package/detail/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +22 -18
  175. package/detail/management.cattle.io.fleetworkspace.vue +18 -27
  176. package/detail/management.cattle.io.oidcclient.vue +369 -0
  177. package/detail/node.vue +2 -2
  178. package/detail/pod.vue +2 -2
  179. package/detail/service.vue +10 -1
  180. package/detail/workload/index.vue +8 -2
  181. package/dialog/ExtensionCatalogUninstallDialog.vue +7 -4
  182. package/dialog/GenericPrompt.vue +1 -1
  183. package/dialog/ImportDialog.vue +8 -8
  184. package/dialog/OidcClientSecretDialog.vue +117 -0
  185. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +3 -3
  186. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +5 -2
  187. package/edit/autoscaling.horizontalpodautoscaler/index.vue +4 -1
  188. package/edit/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +30 -31
  189. package/edit/{cis.cattle.io.clusterscanbenchmark.vue → compliance.cattle.io.clusterscanbenchmark.vue} +4 -4
  190. package/edit/{cis.cattle.io.clusterscanprofile.vue → compliance.cattle.io.clusterscanprofile.vue} +5 -5
  191. package/edit/configmap.vue +4 -1
  192. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  193. package/edit/fleet.cattle.io.gitrepo.vue +44 -222
  194. package/edit/fleet.cattle.io.helmop.vue +44 -269
  195. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  196. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  197. package/edit/logging-flow/index.vue +1 -0
  198. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  199. package/edit/management.cattle.io.fleetworkspace.vue +1 -0
  200. package/edit/management.cattle.io.oidcclient.vue +162 -0
  201. package/edit/management.cattle.io.project.vue +4 -1
  202. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  203. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +5 -0
  204. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  205. package/edit/monitoring.coreos.com.receiver/auth.vue +30 -30
  206. package/edit/monitoring.coreos.com.receiver/index.vue +1 -0
  207. package/edit/monitoring.coreos.com.receiver/types/email.vue +1 -1
  208. package/edit/monitoring.coreos.com.route.vue +1 -0
  209. package/edit/namespace.vue +1 -0
  210. package/edit/networking.istio.io.destinationrule/index.vue +4 -1
  211. package/edit/networking.k8s.io.ingress/index.vue +4 -1
  212. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +7 -2
  213. package/edit/networking.k8s.io.networkpolicy/index.vue +6 -2
  214. package/edit/node.vue +1 -0
  215. package/edit/persistentvolume/index.vue +4 -1
  216. package/edit/provisioning.cattle.io.cluster/rke2.vue +418 -382
  217. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +27 -27
  218. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +5 -0
  219. package/edit/resources.cattle.io.restore.vue +1 -1
  220. package/edit/secret/index.vue +1 -0
  221. package/edit/service.vue +4 -1
  222. package/edit/serviceaccount.vue +4 -1
  223. package/edit/storage.k8s.io.storageclass/index.vue +4 -1
  224. package/edit/workload/index.vue +5 -0
  225. package/list/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +2 -2
  226. package/list/management.cattle.io.oidcclient.vue +108 -0
  227. package/list/node.vue +2 -0
  228. package/machine-config/amazonec2.vue +3 -24
  229. package/machine-config/components/GCEImage.vue +374 -0
  230. package/machine-config/google.vue +617 -0
  231. package/mixins/__tests__/brand.spec.ts +170 -0
  232. package/mixins/brand.js +16 -17
  233. package/mixins/create-edit-view/index.js +5 -0
  234. package/mixins/resource-fetch-api-pagination.js +16 -0
  235. package/mixins/vue-select-overrides.js +1 -0
  236. package/models/{cis.cattle.io.clusterscan.js → compliance.cattle.io.clusterscan.js} +8 -8
  237. package/models/{cis.cattle.io.clusterscanbenchmark.js → compliance.cattle.io.clusterscanbenchmark.js} +1 -1
  238. package/models/{cis.cattle.io.clusterscanprofile.js → compliance.cattle.io.clusterscanprofile.js} +5 -5
  239. package/models/{cis.cattle.io.clusterscanreport.js → compliance.cattle.io.clusterscanreport.js} +1 -1
  240. package/models/fleet-application.js +8 -79
  241. package/models/fleet.cattle.io.cluster.js +11 -0
  242. package/models/fleet.cattle.io.gitrepo.js +2 -2
  243. package/models/fleet.cattle.io.helmop.js +9 -39
  244. package/models/management.cattle.io.fleetworkspace.js +2 -1
  245. package/models/management.cattle.io.oidcclient.js +18 -0
  246. package/models/management.cattle.io.registration.js +3 -0
  247. package/models/provisioning.cattle.io.cluster.js +5 -5
  248. package/models/service.js +4 -0
  249. package/models/workload.js +5 -0
  250. package/package.json +1 -1
  251. package/pages/about.vue +4 -58
  252. package/pages/auth/login.vue +1 -1
  253. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +0 -1
  254. package/pages/c/_cluster/apps/charts/index.vue +285 -81
  255. package/pages/c/_cluster/auth/user.retention/index.vue +87 -78
  256. package/pages/c/_cluster/explorer/index.vue +3 -3
  257. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -1
  258. package/pages/c/_cluster/fleet/application/create.vue +3 -2
  259. package/pages/c/_cluster/fleet/index.vue +94 -56
  260. package/pages/c/_cluster/fleet/settings/index.vue +229 -0
  261. package/pages/c/_cluster/longhorn/index.vue +5 -2
  262. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +16 -1
  263. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -2
  264. package/pages/explorer/resource/detail/configmap.vue +30 -7
  265. package/pages/explorer/resource/detail/secret.vue +50 -0
  266. package/pages/home.vue +9 -55
  267. package/pages/support/index.vue +4 -6
  268. package/plugins/dashboard-store/actions.js +19 -5
  269. package/plugins/dashboard-store/getters.js +4 -0
  270. package/plugins/dashboard-store/resource-class.js +16 -2
  271. package/plugins/steve/steve-pagination-utils.ts +26 -18
  272. package/plugins/steve/subscribe.js +6 -1
  273. package/rancher-components/Banner/Banner.vue +13 -0
  274. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -4
  275. package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
  276. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -0
  277. package/rancher-components/RcItemCard/RcItemCard.vue +8 -3
  278. package/store/auth.js +2 -0
  279. package/store/catalog.js +23 -1
  280. package/store/growl.js +97 -8
  281. package/store/index.js +6 -0
  282. package/store/notifications.ts +426 -0
  283. package/store/prefs.js +0 -1
  284. package/store/type-map.js +19 -16
  285. package/store/uiplugins.ts +15 -1
  286. package/types/fleet.d.ts +24 -0
  287. package/types/notifications/index.ts +74 -0
  288. package/types/shell/index.d.ts +46 -32
  289. package/types/store/dashboard-store.types.ts +16 -0
  290. package/utils/__tests__/fleet.test.ts +148 -0
  291. package/utils/__tests__/object.test.ts +54 -1
  292. package/utils/__tests__/string.test.ts +273 -1
  293. package/utils/__tests__/time.test.ts +31 -0
  294. package/utils/auth.js +9 -2
  295. package/utils/crypto/encryption.ts +103 -0
  296. package/utils/cspAdaptor.ts +51 -0
  297. package/utils/fleet.ts +54 -65
  298. package/utils/object.js +36 -0
  299. package/utils/pagination-utils.ts +1 -1
  300. package/utils/release-notes.ts +48 -0
  301. package/utils/selector-typed.ts +7 -2
  302. package/utils/string.js +24 -0
  303. package/utils/{time.js → time.ts} +25 -6
  304. package/utils/uiplugins.ts +22 -0
  305. package/utils/validators/formRules/index.ts +3 -0
  306. package/components/Resource/Detail/TitleBar/composable.ts +0 -31
  307. package/config/product/legacy.js +0 -62
  308. package/pages/c/_cluster/legacy/pages/_page.vue +0 -29
  309. package/pages/c/_cluster/legacy/project/_page.vue +0 -57
  310. package/pages/c/_cluster/legacy/project/index.vue +0 -32
  311. package/pages/c/_cluster/legacy/project/pipelines.vue +0 -96
  312. /package/components/ResourceDetail/{Masthead.vue → Masthead/legacy.vue} +0 -0
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import { Banner } from '@components/Banner';
3
+ import TitleBar from '@shell/components/Resource/Detail/TitleBar/index.vue';
4
+ import { useDefaultTitleBarProps } from '@shell/components/Resource/Detail/TitleBar/composables';
5
+ import Metadata from '@shell/components/Resource/Detail/Metadata/index.vue';
6
+ import { useDefaultMetadataForLegacyPagesProps } from '@shell/components/Resource/Detail/Metadata/composables';
7
+ import { useResourceDetailBannerProps } from '@shell/components/Resource/Detail/composables';
8
+ import { computed } from 'vue';
9
+
10
+ export interface Props {
11
+ value?: Object;
12
+ resourceSubtype?: string;
13
+ }
14
+
15
+ </script>
16
+
17
+ <script lang="ts" setup>
18
+ const props = withDefaults(defineProps<Props>(), { value: () => ({}), resourceSubtype: undefined });
19
+
20
+ const resourceSubtype = computed(() => props.resourceSubtype);
21
+ const titleBarProps = useDefaultTitleBarProps(props.value, resourceSubtype);
22
+ const metadataProps = useDefaultMetadataForLegacyPagesProps(props.value);
23
+ const bannerProps = useResourceDetailBannerProps(props.value);
24
+ </script>
25
+
26
+ <template>
27
+ <TitleBar v-bind="titleBarProps" />
28
+ <Banner
29
+ v-if="bannerProps"
30
+ class="new state-banner"
31
+ v-bind="bannerProps"
32
+ />
33
+ <Metadata
34
+ v-bind="metadataProps"
35
+ class="mmt-4"
36
+ />
37
+ </template>
38
+
39
+ <style lang="scss" scoped>
40
+ .new.state-banner {
41
+ margin: 0;
42
+ margin-top: 16px;
43
+ }
44
+ </style>
@@ -1,8 +1,9 @@
1
1
  import { mount } from '@vue/test-utils';
2
-
3
2
  import ResourceDetail from '@shell/components/ResourceDetail/index.vue';
4
3
  import { _EDIT, _VIEW, LEGACY, MODE } from '@shell/config/query-params';
4
+ import * as pageEnabled from '@shell/composables/useIsNewDetailPageEnabled';
5
5
  import flushPromises from 'flush-promises';
6
+ import { computed } from 'vue';
6
7
 
7
8
  const mockQuery: any = {};
8
9
  const mockParams: any = {};
@@ -30,13 +31,22 @@ jest.mock('vue-router', () => ({
30
31
  })
31
32
  }));
32
33
 
34
+ jest.mock('@shell/composables/useIsNewDetailPageEnabled');
35
+
33
36
  describe('component: ResourceDetail/index', () => {
34
37
  const resourceName = 'configmap';
38
+ const useIsNewDetailPageEnabledSpy = jest.spyOn(pageEnabled, 'useIsNewDetailPageEnabled');
39
+
40
+ beforeEach(() => {
41
+ jest.clearAllMocks();
42
+ });
35
43
 
36
- it('should render legacy component with default props if LEGACY=false is not present', async() => {
44
+ it('should render legacy component with default props if useIsNewDetailPageEnabledSpy is false', async() => {
37
45
  mockParams.resource = resourceName;
38
46
  mockQuery[MODE] = _VIEW;
39
47
 
48
+ useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => false));
49
+
40
50
  const wrapper = mount(ResourceDetail, { });
41
51
  const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
42
52
 
@@ -46,26 +56,33 @@ describe('component: ResourceDetail/index', () => {
46
56
  expect(legacyComponent.props('resourceOverride')).toBeUndefined();
47
57
  expect(legacyComponent.props('parentRouteOverride')).toBeUndefined();
48
58
  expect(legacyComponent.props('errorsMap')).toBeUndefined();
59
+ expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
49
60
  });
50
61
 
51
- it('should render legacy component with default props if LEGACY=false is present but resourceName has not been added to our mapping', async() => {
62
+ it('should render legacy component with default props if useIsNewDetailPageEnabledSpy is false but resourceName has not been added to our mapping', async() => {
52
63
  mockParams.resource = 'notMapped';
53
64
  mockQuery[MODE] = _VIEW;
54
65
 
66
+ useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => false));
67
+
55
68
  const wrapper = mount(ResourceDetail, {});
56
69
  const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
57
70
 
58
71
  expect(legacyComponent.exists()).toBeTruthy();
72
+ expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
59
73
  });
60
74
 
61
- it('should render legacy component with default props if LEGACY=false is present but mode is not VIEW', async() => {
75
+ it('should render legacy component with default props if useIsNewDetailPageEnabledSpy is false but mode is not VIEW', async() => {
62
76
  mockParams.resource = resourceName;
63
77
  mockQuery[MODE] = _EDIT;
64
78
 
79
+ useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => false));
80
+
65
81
  const wrapper = mount(ResourceDetail, {});
66
82
  const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
67
83
 
68
84
  expect(legacyComponent.exists()).toBeTruthy();
85
+ expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
69
86
  });
70
87
 
71
88
  it('should render legacy component while forwarding props', async() => {
@@ -99,16 +116,20 @@ describe('component: ResourceDetail/index', () => {
99
116
  expect(legacyComponent.props('errorsMap')).toStrictEqual(errorsMap);
100
117
  });
101
118
 
102
- it('should render new component if LEGACY=false is present', async() => {
119
+ it('should render new component if useIsNewDetailPageEnabledSpy is true', async() => {
103
120
  mockParams.resource = resourceName;
121
+ mockParams.id = 'ID';
104
122
  mockQuery[MODE] = _VIEW;
105
123
  mockQuery[LEGACY] = 'false';
106
124
 
125
+ useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => true));
126
+
107
127
  const wrapper = mount(ResourceDetail);
108
128
 
109
129
  await flushPromises();
110
130
  const configmapComponent = wrapper.findComponent<any>({ name: 'configmap' });
111
131
 
112
132
  expect(configmapComponent.exists()).toBeTruthy();
133
+ expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
113
134
  });
114
135
  });
@@ -2,9 +2,10 @@
2
2
  import { useRoute } from 'vue-router';
3
3
  import { computed, defineAsyncComponent } from 'vue';
4
4
 
5
- import { MODE, _VIEW, LEGACY } from '@shell/config/query-params';
5
+ import { MODE, _VIEW } from '@shell/config/query-params';
6
6
  import Legacy from '@shell/components/ResourceDetail/legacy.vue';
7
7
  import Loading from '@shell/components/Loading.vue';
8
+ import { useIsNewDetailPageEnabled } from '@shell/composables/useIsNewDetailPageEnabled';
8
9
 
9
10
  export interface Props {
10
11
  flexContent?: boolean;
@@ -22,19 +23,20 @@ export interface Props {
22
23
  // I could also dynamically check for and import these pages but I wanted this to be easier
23
24
  // to be explicit and easier to search for.
24
25
  const resourceToPage: any = {
25
- // 'apps.daemonset': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/apps.daemonset.vue')),
26
- // 'apps.deployment': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/apps.deployment.vue')),
27
- // 'apps.statefulset': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/apps.statefulset.vue')),
28
- // 'batch.cronjob': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/batch.cronjob.vue')),
29
- // 'batch.job': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/batch.job.vue')),
30
- // 'cluster-dashboard': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/cluster-dashboard.vue')),
26
+ // 'apps.daemonset': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/apps.daemonset.vue')),
27
+ // 'apps.deployment': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/apps.deployment.vue')),
28
+ // 'apps.statefulset': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/apps.statefulset.vue')),
29
+ // 'batch.cronjob': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/batch.cronjob.vue')),
30
+ // 'batch.job': defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/batch.job.vue')),
31
31
  configmap: defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/configmap.vue')),
32
32
  // namespace: defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/namespace.vue')),
33
33
  // node: defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/node.vue')),
34
34
  // pod: defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/pod.vue')),
35
- // secret: defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/secret.vue')),
35
+ secret: defineAsyncComponent(() => import('@shell/pages/explorer/resource/detail/secret.vue')),
36
36
  };
37
37
 
38
+ defineOptions({ inheritAttrs: false });
39
+
38
40
  const route = useRoute();
39
41
  const props = withDefaults(defineProps<Props>(), {
40
42
  flexContent: false,
@@ -45,14 +47,26 @@ const props = withDefaults(defineProps<Props>(), {
45
47
  errorsMap: undefined
46
48
  });
47
49
 
48
- const currentResourceName = computed<string>(() => route.params.resource as string);
49
- const mode = computed(() => route.query[MODE]);
50
- const isView = computed(() => !mode.value || mode.value === _VIEW);
51
- // We're defaulting to legacy being on, we'll switch this once we want to enable the new detail page by default
52
- const isLegacy = computed(() => route.query[LEGACY] !== 'false');
53
- const page = computed(() => resourceToPage[currentResourceName.value]);
50
+ const currentResourceName = computed(() => {
51
+ const resource = route?.params?.resource;
52
+
53
+ if (!resource) {
54
+ return;
55
+ }
54
56
 
55
- const useLatest = computed(() => !!(!isLegacy.value && isView.value && page.value));
57
+ if (typeof resource === 'string') {
58
+ return resource;
59
+ }
60
+
61
+ // This should never occur, just satisfying the types
62
+ return resource[0];
63
+ });
64
+ const mode = computed(() => route?.query?.[MODE]);
65
+ const isView = computed(() => route?.params?.id && (!mode.value || mode.value === _VIEW));
66
+ // We're defaulting to legacy being on, we'll switch this once we want to enable the new detail page by default
67
+ const iseNewDetailPageEnabled = useIsNewDetailPageEnabled();
68
+ const page = computed(() => currentResourceName.value ? resourceToPage[currentResourceName.value] : undefined);
69
+ const useLatest = computed(() => !!(iseNewDetailPageEnabled.value && isView.value && page.value));
56
70
  </script>
57
71
 
58
72
  <template>
@@ -66,6 +80,6 @@ const useLatest = computed(() => !!(!isLegacy.value && isView.value && page.valu
66
80
  </Suspense>
67
81
  <Legacy
68
82
  v-else
69
- v-bind="props"
83
+ v-bind="{...$attrs, ...props}"
70
84
  />
71
85
  </template>
@@ -379,6 +379,16 @@ export default {
379
379
  closeError(index) {
380
380
  this.errors = this.errors.filter((_, i) => i !== index);
381
381
  },
382
+ onYamlError(err) {
383
+ this.errors = [];
384
+ const errors = Array.isArray(err) ? err : [err];
385
+
386
+ errors.forEach((e) => {
387
+ if (this.errors.indexOf(e) === -1) {
388
+ this.errors.push(e);
389
+ }
390
+ });
391
+ },
382
392
  /**
383
393
  * Initializes the resource components based on the provided user and
384
394
  * resource override.
@@ -483,8 +493,9 @@ export default {
483
493
  :offer-preview="offerPreview"
484
494
  :done-route="doneRoute"
485
495
  :done-override="value ? value.doneOverride : null"
496
+ :show-errors="false"
486
497
  @update:value="$emit('input', $event)"
487
- @error="e=>errors.push(e)"
498
+ @error="onYamlError"
488
499
  />
489
500
 
490
501
  <component
@@ -542,4 +553,10 @@ export default {
542
553
  flex-direction: column;
543
554
  flex-grow: 1;
544
555
  }
556
+ .cru__errors {
557
+ position: sticky;
558
+ top: 0;
559
+ z-index: 1;
560
+ background-color: var(--header-bg);
561
+ }
545
562
  </style>
@@ -157,6 +157,12 @@ export default {
157
157
  },
158
158
 
159
159
  _createButtonlabel() {
160
+ const overrideLabel = this.$store.getters['type-map/optionsFor'](this.resource).listCreateButtonLabelKey;
161
+
162
+ if (overrideLabel) {
163
+ return this.t(overrideLabel);
164
+ }
165
+
160
166
  return this.createButtonLabel || this.t('resourceList.head.create');
161
167
  },
162
168
  }
@@ -73,6 +73,11 @@ export default {
73
73
  default: true
74
74
  },
75
75
 
76
+ showErrors: {
77
+ type: Boolean,
78
+ default: true
79
+ },
80
+
76
81
  applyHooks: {
77
82
  type: Function,
78
83
  default: null,
@@ -289,6 +294,13 @@ export default {
289
294
  }
290
295
  },
291
296
 
297
+ refresh() {
298
+ this.$refs.yamleditor.refresh();
299
+ },
300
+
301
+ closeError(index) {
302
+ this.errors = (this.errors || []).filter((_, i) => i !== index);
303
+ },
292
304
  }
293
305
  };
294
306
  </script>
@@ -318,7 +330,8 @@ export default {
318
330
  class="footer"
319
331
  :class="{ 'edit': !isView }"
320
332
  :mode="mode"
321
- :errors="errors"
333
+ :errors="showErrors ? errors : []"
334
+ @close-error="closeError"
322
335
  @save="save"
323
336
  @done="done"
324
337
  >
@@ -1,10 +1,12 @@
1
1
  <script lang="ts" setup>
2
- import { computed, watch } from 'vue';
2
+ import { computed, onBeforeUnmount, watch } from 'vue';
3
3
  import { useStore } from 'vuex';
4
4
  import {
5
5
  DEFAULT_FOCUS_TRAP_OPTS,
6
6
  useWatcherBasedSetupFocusTrapWithDestroyIncluded
7
7
  } from '@shell/composables/focusTrap';
8
+ import { isEqual } from 'lodash';
9
+ import { useRouter } from 'vue-router';
8
10
 
9
11
  const HEADER_HEIGHT = 55;
10
12
 
@@ -15,6 +17,11 @@ const currentComponent = computed(() => store.getters['slideInPanel/component'])
15
17
  const currentProps = computed(() => store.getters['slideInPanel/componentProps']);
16
18
 
17
19
  const panelTop = computed(() => {
20
+ // Some components like the ResourceDetailDrawer are designed to take up the full height of the viewport so we want to be able to specify the top.
21
+ if (currentProps?.value?.top) {
22
+ return currentProps?.value?.top;
23
+ }
24
+
18
25
  const banner = document.getElementById('banner-header');
19
26
  let height = HEADER_HEIGHT;
20
27
 
@@ -25,13 +32,24 @@ const panelTop = computed(() => {
25
32
  return `${ height }px`;
26
33
  });
27
34
 
28
- const panelHeight = computed(() => `calc(100vh - ${ panelTop?.value })`);
35
+ // Some components like the ResourceDetailDrawer are designed to take up the full height of the viewport so we want to be able to specify the height.
36
+ const panelHeight = computed(() => (currentProps?.value?.height) ? (currentProps?.value?.height) : `calc(100vh - ${ panelTop?.value })`);
29
37
  const panelWidth = computed(() => currentProps?.value?.width || '33%');
30
38
  const panelRight = computed(() => (isOpen?.value ? '0' : `-${ panelWidth?.value }`));
31
39
  const panelZIndex = computed(() => `${ (isOpen?.value ? 1 : 2) * (currentProps?.value?.zIndex ?? 1000) }`);
32
40
 
33
41
  const showHeader = computed(() => currentProps?.value?.showHeader ?? true);
34
42
  const panelTitle = showHeader.value ? computed(() => currentProps?.value?.title || 'Details') : null;
43
+ const closeOnRouteChange = computed(() => {
44
+ const propsCloseOnRouteChange = currentProps?.value.closeOnRouteChange;
45
+
46
+ if (!propsCloseOnRouteChange) {
47
+ return ['name', 'params', 'hash', 'query'];
48
+ }
49
+
50
+ return propsCloseOnRouteChange;
51
+ });
52
+ const router = useRouter();
35
53
 
36
54
  watch(
37
55
  /**
@@ -75,15 +93,33 @@ watch(
75
93
  );
76
94
 
77
95
  watch(
78
- () => (store as any).$router?.currentRoute,
79
- () => {
80
- if (isOpen?.value && currentProps?.value.closeOnRouteChange !== false) {
96
+ () => router?.currentRoute?.value,
97
+ (newValue, oldValue) => {
98
+ if (!isOpen?.value) {
99
+ return;
100
+ }
101
+
102
+ if (closeOnRouteChange.value.includes('name') && !isEqual(newValue?.name, oldValue?.name)) {
103
+ closePanel();
104
+ }
105
+
106
+ if (closeOnRouteChange.value.includes('params') && !isEqual(newValue?.params, oldValue?.params)) {
107
+ closePanel();
108
+ }
109
+
110
+ if (closeOnRouteChange.value.includes('hash') && !isEqual(newValue?.hash, oldValue?.hash)) {
111
+ closePanel();
112
+ }
113
+
114
+ if (closeOnRouteChange.value.includes('query') && !isEqual(newValue?.query, oldValue?.query)) {
81
115
  closePanel();
82
116
  }
83
117
  },
84
118
  { deep: true }
85
119
  );
86
120
 
121
+ onBeforeUnmount(closePanel);
122
+
87
123
  function closePanel() {
88
124
  store.commit('slideInPanel/close');
89
125
  }
@@ -100,9 +136,12 @@ function closePanel() {
100
136
  data-testid="slide-in-glass"
101
137
  class="slide-in-glass"
102
138
  :class="{ 'slide-in-glass-open': isOpen }"
139
+ :style="{
140
+ ['z-index']: panelZIndex
141
+ }"
103
142
  @click="closePanel"
104
143
  />
105
- <div
144
+ <aside
106
145
  class="slide-in"
107
146
  :class="{ 'slide-in-open': isOpen }"
108
147
  :style="{
@@ -136,7 +175,7 @@ function closePanel() {
136
175
  class="dynamic-panel-content"
137
176
  />
138
177
  </div>
139
- </div>
178
+ </aside>
140
179
  </div>
141
180
  </Teleport>
142
181
  </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { StateColor, stateColorCssVar } from '@shell/utils/style';
3
+ import { computed } from 'vue';
4
+
5
+ interface Props {
6
+ color: StateColor;
7
+ size?: string;
8
+ }
9
+
10
+ const props = withDefaults(defineProps<Props>(), { size: '8px' });
11
+ const backgroundColor = computed(() => stateColorCssVar(props.color));
12
+ </script>
13
+
14
+ <template>
15
+ <span class="state-dot" />
16
+ </template>
17
+
18
+ <style lang="scss" scoped>
19
+ .state-dot {
20
+ display: inline-block;
21
+
22
+ width: v-bind('props.size');
23
+ height: v-bind('props.size');
24
+
25
+ border-radius: 50%;
26
+ background-color: v-bind('backgroundColor');
27
+ }
28
+ </style>
@@ -145,17 +145,10 @@ export default {
145
145
  this.select(activeTab.name);
146
146
  }
147
147
  },
148
- },
149
-
150
- mounted() {
151
- if ( this.useHash ) {
152
- window.addEventListener('hashchange', this.hashChange);
153
- }
154
- },
155
-
156
- unmounted() {
157
- if ( this.useHash ) {
158
- window.removeEventListener('hashchange', this.hashChange);
148
+ '$route.hash'() {
149
+ if ( this.useHash ) {
150
+ this.hashChange();
151
+ }
159
152
  }
160
153
  },
161
154
 
@@ -164,7 +157,7 @@ export default {
164
157
  return tab.displayAlertIcon || (tab.error && !tab.active);
165
158
  },
166
159
  hashChange() {
167
- if (!this.scrollOnChange) {
160
+ if (this.scrollOnChange) {
168
161
  const scrollable = document.getElementsByTagName('main')[0];
169
162
 
170
163
  if (scrollable) {
@@ -182,8 +175,9 @@ export default {
182
175
  select(name/* , event */) {
183
176
  const { sortedTabs } = this;
184
177
 
185
- const selected = this.find(name);
186
- const hashName = `#${ name }`;
178
+ const cleanName = name.replace('#', '');
179
+ const selected = this.find(cleanName);
180
+ const hashName = `#${ cleanName }`;
187
181
 
188
182
  if ( !selected || selected.disabled) {
189
183
  return;
@@ -281,7 +275,7 @@ export default {
281
275
  >
282
276
  <a
283
277
  :data-testid="`btn-${tab.name}`"
284
- :aria-controls="'#' + tab.name"
278
+ :aria-controls="tab.name"
285
279
  :aria-selected="tab.active"
286
280
  :aria-label="tab.labelDisplay || ''"
287
281
  role="tab"
@@ -318,6 +312,7 @@ export default {
318
312
  type="button"
319
313
  class="btn bg-transparent"
320
314
  data-testid="tab-list-add"
315
+ :aria-label="t('tabs.addItem')"
321
316
  @click="tabAddClicked"
322
317
  >
323
318
  <i class="icon icon-plus" />
@@ -327,6 +322,7 @@ export default {
327
322
  class="btn bg-transparent"
328
323
  :disabled="!sortedTabs.length"
329
324
  data-testid="tab-list-remove"
325
+ :aria-label="t('tabs.removeItem')"
330
326
  @click="tabRemoveClicked"
331
327
  >
332
328
  <i class="icon icon-minus" />
@@ -361,7 +361,7 @@ export default {
361
361
  role="presentation"
362
362
  >
363
363
  <span
364
- :aria-controls="'step' + idx+1"
364
+ :aria-controls="'step-container-' + step.name"
365
365
  :aria-selected="step.name === activeStep.name"
366
366
  role="tab"
367
367
  class="controls"
@@ -376,7 +376,7 @@ export default {
376
376
  </span>
377
377
  </span>
378
378
  </li>
379
- <div
379
+ <li
380
380
  v-if="idx!==visibleSteps.length-1"
381
381
  :key="step.name"
382
382
  class="divider"
@@ -397,7 +397,9 @@ export default {
397
397
  >
398
398
  <div
399
399
  v-if="step.name === activeStep.name || step.hidden"
400
+ :id="'step-container-' + step.name"
400
401
  :key="step.name"
402
+ role="tabpanel"
401
403
  class="step-container__step"
402
404
  :class="{'hide': step.name !== activeStep.name && step.hidden}"
403
405
  >