@rancher/shell 3.0.7 → 3.0.8-rc.10

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 (375) hide show
  1. package/assets/brand/suse/banner.svg +1 -0
  2. package/assets/brand/suse/dark/banner.svg +1 -0
  3. package/assets/brand/suse/dark/login-landscape.svg +1 -0
  4. package/assets/brand/suse/dark/rancher-logo.svg +1 -1
  5. package/assets/brand/suse/favicon.png +0 -0
  6. package/assets/brand/suse/login-landscape.svg +1 -0
  7. package/assets/brand/suse/metadata.json +11 -1
  8. package/assets/brand/suse/rancher-logo.svg +1 -1
  9. package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
  10. package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
  11. package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
  12. package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
  13. package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
  14. package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
  15. package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
  16. package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
  17. package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
  18. package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
  19. package/assets/images/content/README.md +5 -0
  20. package/assets/images/content/cloud-native.svg +84 -0
  21. package/assets/images/content/dark/cloud-native.svg +21 -0
  22. package/assets/images/content/dark/shield.svg +59 -0
  23. package/assets/images/content/dark/suse.svg +10 -0
  24. package/assets/images/content/shield.svg +59 -0
  25. package/assets/images/content/suse.svg +10 -0
  26. package/assets/images/vendor/githubapp.svg +13 -0
  27. package/assets/styles/base/_typography.scss +2 -1
  28. package/assets/styles/fonts/_fontstack.scss +53 -1
  29. package/assets/styles/global/_cards.scss +0 -3
  30. package/assets/styles/global/_layout.scss +21 -35
  31. package/assets/styles/themes/_dark.scss +1 -1
  32. package/assets/styles/themes/_light.scss +1 -1
  33. package/assets/styles/themes/_modern.scss +16 -8
  34. package/assets/styles/themes/_suse.scss +116 -24
  35. package/assets/translations/en-us.yaml +185 -21
  36. package/assets/translations/zh-hans.yaml +0 -4
  37. package/components/AutoscalerCard.vue +113 -0
  38. package/components/AutoscalerTab.vue +94 -0
  39. package/components/BackLink.vue +8 -0
  40. package/components/BannerGraphic.vue +36 -21
  41. package/components/BrandImage.vue +17 -6
  42. package/components/ClusterIconMenu.vue +1 -1
  43. package/components/ClusterProviderIcon.vue +1 -1
  44. package/components/Cron/CronExpressionEditor.vue +1 -1
  45. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  46. package/components/Drawer/Chrome.vue +2 -6
  47. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
  48. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
  49. package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
  50. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
  51. package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
  52. package/components/Drawer/types.ts +3 -0
  53. package/components/DynamicContent/DynamicContentBanner.vue +102 -0
  54. package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
  55. package/components/DynamicContent/DynamicContentIcon.vue +132 -0
  56. package/components/DynamicContent/DynamicContentPanel.vue +112 -0
  57. package/components/DynamicContent/content.ts +78 -0
  58. package/components/EmberPage.vue +1 -1
  59. package/components/IconOrSvg.vue +2 -2
  60. package/components/Inactivity.vue +222 -106
  61. package/components/InstallHelmCharts.vue +2 -2
  62. package/components/PaginatedResourceTable.vue +2 -6
  63. package/components/PopoverCard.vue +192 -0
  64. package/components/Questions/__tests__/index.test.ts +159 -0
  65. package/components/Resource/Detail/CopyToClipboard.vue +4 -1
  66. package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
  67. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  68. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
  69. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
  70. package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
  71. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  72. package/components/Resource/Detail/Metadata/composables.ts +9 -9
  73. package/components/Resource/Detail/Metadata/index.vue +3 -3
  74. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
  75. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
  76. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
  77. package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
  78. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  79. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  80. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  81. package/components/Resource/Detail/composables.ts +12 -0
  82. package/components/ResourceDetail/Masthead/latest.vue +29 -0
  83. package/components/ResourceDetail/index.vue +5 -2
  84. package/components/ResourceList/Masthead.vue +1 -1
  85. package/components/SortableTable/index.vue +18 -2
  86. package/components/Tabbed/__tests__/index.test.ts +86 -0
  87. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  88. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  89. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  90. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  91. package/components/__tests__/AutoscalerCard.test.ts +154 -0
  92. package/components/__tests__/AutoscalerTab.test.ts +125 -0
  93. package/components/__tests__/PopoverCard.test.ts +204 -0
  94. package/components/auth/SelectPrincipal.vue +24 -6
  95. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  96. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  97. package/components/fleet/FleetSecretSelector.vue +127 -0
  98. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  99. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  100. package/components/form/FileImageSelector.vue +13 -4
  101. package/components/form/FileSelector.vue +11 -2
  102. package/components/form/ResourceLabeledSelect.vue +1 -0
  103. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  104. package/components/formatter/Autoscaler.vue +97 -0
  105. package/components/formatter/InternalExternalIP.vue +198 -24
  106. package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
  107. package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
  108. package/components/google/util/__tests__/formatter.test.ts +47 -0
  109. package/components/google/util/formatter.ts +5 -2
  110. package/components/nav/Group.vue +12 -3
  111. package/components/nav/Header.vue +37 -16
  112. package/components/nav/NamespaceFilter.vue +13 -1
  113. package/components/nav/NotificationCenter/index.vue +2 -1
  114. package/components/nav/TopLevelMenu.helper.ts +16 -6
  115. package/components/nav/TopLevelMenu.vue +4 -2
  116. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  117. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  118. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  119. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  120. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  121. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  122. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  123. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  124. package/components/nav/WindowManager/constants.ts +23 -0
  125. package/components/nav/WindowManager/index.vue +61 -575
  126. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  127. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  128. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  129. package/components/templates/default.vue +4 -40
  130. package/components/templates/home.vue +31 -5
  131. package/components/templates/plain.vue +30 -4
  132. package/components/templates/standalone.vue +1 -1
  133. package/composables/useI18n.ts +10 -1
  134. package/composables/useInterval.ts +15 -0
  135. package/config/__test__/uiplugins.test.ts +309 -0
  136. package/config/labels-annotations.js +9 -1
  137. package/config/product/auth.js +1 -0
  138. package/config/product/explorer.js +3 -1
  139. package/config/product/manager.js +20 -9
  140. package/config/query-params.js +1 -0
  141. package/config/router/routes.js +10 -2
  142. package/config/settings.ts +10 -2
  143. package/config/store.js +4 -2
  144. package/config/table-headers.js +8 -0
  145. package/config/types.js +11 -0
  146. package/config/uiplugins.js +46 -2
  147. package/config/version.js +1 -1
  148. package/core/__test__/extension-manager-impl.test.js +236 -0
  149. package/core/extension-manager-impl.js +23 -6
  150. package/core/plugin-helpers.ts +2 -0
  151. package/core/types-provisioning.ts +4 -1
  152. package/detail/pod.vue +1 -0
  153. package/detail/provisioning.cattle.io.cluster.vue +13 -1
  154. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  155. package/dialog/DeveloperLoadExtensionDialog.vue +12 -3
  156. package/dialog/RollbackWorkloadDialog.vue +2 -5
  157. package/directives/ui-context.ts +103 -0
  158. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  159. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  160. package/edit/auth/__tests__/oidc.test.ts +26 -0
  161. package/edit/auth/github-app-steps.vue +97 -0
  162. package/edit/auth/github-steps.vue +75 -0
  163. package/edit/auth/github.vue +99 -65
  164. package/edit/auth/oidc.vue +5 -1
  165. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  166. package/edit/cloudcredential.vue +1 -1
  167. package/edit/configmap.vue +1 -0
  168. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  169. package/edit/fleet.cattle.io.gitrepo.vue +0 -10
  170. package/edit/fleet.cattle.io.helmop.vue +51 -2
  171. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  172. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  173. package/edit/logging-flow/index.vue +1 -0
  174. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  175. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  176. package/edit/management.cattle.io.project.vue +1 -0
  177. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  178. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  179. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  180. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  181. package/edit/monitoring.coreos.com.route.vue +1 -1
  182. package/edit/namespace.vue +1 -0
  183. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  184. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  185. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  186. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  187. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  188. package/edit/node.vue +1 -0
  189. package/edit/persistentvolume/index.vue +27 -22
  190. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  191. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  192. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  193. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  194. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  195. package/edit/persistentvolume/plugins/csi.vue +18 -16
  196. package/edit/persistentvolume/plugins/fc.vue +13 -14
  197. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  198. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  199. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  200. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  201. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  202. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  203. package/edit/persistentvolume/plugins/local.vue +1 -3
  204. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  205. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  206. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  207. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  208. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  209. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  210. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  211. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  212. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  213. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
  214. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
  215. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
  216. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  217. package/edit/provisioning.cattle.io.cluster/index.vue +25 -15
  218. package/edit/provisioning.cattle.io.cluster/rke2.vue +98 -17
  219. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  220. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
  221. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
  222. package/edit/secret/index.vue +1 -1
  223. package/edit/service.vue +9 -4
  224. package/edit/serviceaccount.vue +1 -0
  225. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  226. package/edit/workload/index.vue +2 -1
  227. package/edit/workload/mixins/workload.js +1 -1
  228. package/initialize/App.vue +4 -4
  229. package/initialize/install-directives.js +2 -0
  230. package/initialize/install-plugins.js +19 -2
  231. package/list/projectsecret.vue +1 -1
  232. package/list/provisioning.cattle.io.cluster.vue +15 -2
  233. package/machine-config/amazonec2.vue +42 -135
  234. package/machine-config/azure.vue +1 -1
  235. package/machine-config/components/EC2Networking.vue +490 -0
  236. package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
  237. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
  238. package/machine-config/digitalocean.vue +11 -0
  239. package/machine-config/google.vue +1 -1
  240. package/mixins/__tests__/brand.spec.ts +2 -2
  241. package/mixins/__tests__/chart.test.ts +21 -0
  242. package/mixins/brand.js +1 -7
  243. package/mixins/chart.js +8 -2
  244. package/mixins/create-edit-view/index.js +5 -0
  245. package/models/__tests__/chart.test.ts +49 -12
  246. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  247. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
  248. package/models/catalog.cattle.io.app.js +1 -1
  249. package/models/chart.js +28 -14
  250. package/models/cluster/node.js +13 -6
  251. package/models/cluster.x-k8s.io.machine.js +10 -20
  252. package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
  253. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  254. package/models/management.cattle.io.authconfig.js +1 -0
  255. package/models/management.cattle.io.cluster.js +21 -3
  256. package/models/management.cattle.io.kontainerdriver.js +1 -0
  257. package/models/provisioning.cattle.io.cluster.js +249 -33
  258. package/package.json +6 -5
  259. package/pages/auth/login.vue +43 -4
  260. package/pages/auth/verify.vue +1 -1
  261. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  262. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  263. package/pages/c/_cluster/apps/charts/chart.vue +35 -17
  264. package/pages/c/_cluster/apps/charts/index.vue +11 -13
  265. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  266. package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
  267. package/pages/c/_cluster/explorer/index.vue +8 -6
  268. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  269. package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
  270. package/pages/c/_cluster/settings/brand.vue +1 -1
  271. package/pages/c/_cluster/settings/performance.vue +12 -25
  272. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  273. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  274. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  275. package/pages/home.vue +327 -16
  276. package/pkg/dynamic-importer.lib.js +4 -0
  277. package/plugins/axios.js +2 -1
  278. package/plugins/dashboard-client-init.js +3 -0
  279. package/plugins/dashboard-store/actions.js +1 -1
  280. package/plugins/dashboard-store/getters.js +18 -1
  281. package/plugins/dashboard-store/resource-class.js +21 -6
  282. package/plugins/dynamic-content.js +13 -0
  283. package/plugins/i18n.js +8 -0
  284. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
  285. package/plugins/steve/steve-pagination-utils.ts +41 -22
  286. package/plugins/steve/subscribe.js +17 -9
  287. package/plugins/subscribe-events.ts +4 -2
  288. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  289. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
  290. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  291. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  292. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  293. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  294. package/rancher-components/Pill/types.ts +0 -1
  295. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
  296. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  297. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  298. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  299. package/rancher-components/RcIcon/index.ts +1 -0
  300. package/rancher-components/RcIcon/types.ts +160 -0
  301. package/rancher-components/utils/status.test.ts +67 -0
  302. package/rancher-components/utils/status.ts +77 -0
  303. package/scripts/extension/publish +1 -1
  304. package/scripts/typegen.sh +1 -0
  305. package/store/action-menu.js +8 -0
  306. package/store/auth.js +11 -6
  307. package/store/aws.js +8 -6
  308. package/store/catalog.js +6 -0
  309. package/store/features.js +2 -0
  310. package/store/index.js +45 -20
  311. package/store/notifications.ts +51 -4
  312. package/store/plugins.js +7 -3
  313. package/store/prefs.js +12 -6
  314. package/store/type-map.js +3 -3
  315. package/store/ui-context.ts +86 -0
  316. package/store/wm.ts +244 -0
  317. package/types/kube/kube-api.ts +2 -1
  318. package/types/notifications/index.ts +27 -3
  319. package/types/rancher/index.d.ts +1 -0
  320. package/types/resources/settings.d.ts +29 -7
  321. package/types/shell/index.d.ts +138 -4
  322. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  323. package/types/store/pagination.types.ts +157 -9
  324. package/types/store/subscribe-events.types.ts +8 -1
  325. package/types/store/subscribe.types.ts +1 -0
  326. package/types/window-manager.ts +24 -0
  327. package/utils/__tests__/cluster.test.ts +379 -1
  328. package/utils/__tests__/object.test.ts +19 -0
  329. package/utils/__tests__/provider.test.ts +98 -0
  330. package/utils/__tests__/selector-typed.test.ts +263 -0
  331. package/utils/__tests__/version.test.ts +19 -1
  332. package/utils/autoscaler-utils.ts +7 -0
  333. package/utils/back-off.ts +3 -3
  334. package/utils/brand.ts +29 -0
  335. package/utils/chart.js +18 -0
  336. package/utils/cluster.js +157 -3
  337. package/utils/color.js +1 -1
  338. package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
  339. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  340. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  341. package/utils/dynamic-content/__tests__/info.test.ts +275 -0
  342. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  343. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  344. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  345. package/utils/dynamic-content/announcement.ts +142 -0
  346. package/utils/dynamic-content/config.ts +55 -0
  347. package/utils/dynamic-content/example.json +40 -0
  348. package/utils/dynamic-content/index.ts +277 -0
  349. package/utils/dynamic-content/info.ts +261 -0
  350. package/utils/dynamic-content/new-release.ts +126 -0
  351. package/utils/dynamic-content/notification-handler.ts +48 -0
  352. package/utils/dynamic-content/support-notice.ts +169 -0
  353. package/utils/dynamic-content/types.d.ts +153 -0
  354. package/utils/dynamic-content/util.ts +122 -0
  355. package/utils/dynamic-importer.js +2 -2
  356. package/utils/favicon.js +4 -4
  357. package/utils/inactivity.ts +104 -0
  358. package/utils/object.js +20 -2
  359. package/utils/pagination-utils.ts +19 -4
  360. package/utils/pagination-wrapper.ts +12 -8
  361. package/utils/provider.ts +14 -0
  362. package/utils/release-notes.ts +1 -1
  363. package/utils/scroll.js +7 -0
  364. package/utils/selector-typed.ts +6 -2
  365. package/utils/settings.ts +15 -0
  366. package/utils/validators/machine-pool.ts +13 -3
  367. package/utils/version.js +15 -0
  368. package/assets/images/icons/document.svg +0 -3
  369. package/plugins/nuxt-client-init.js +0 -3
  370. package/store/wm.js +0 -95
  371. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  372. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  373. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  374. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  375. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
@@ -1,5 +1,7 @@
1
1
  import {
2
- CAPI, MANAGEMENT, NAMESPACE, NORMAN, SNAPSHOT, HCI, LOCAL_CLUSTER
2
+ CAPI, MANAGEMENT, NAMESPACE, NORMAN, SNAPSHOT, HCI, LOCAL_CLUSTER,
3
+ CONFIG_MAP, AUTOSCALER_CONFIG_MAP_ID,
4
+ EVENT
3
5
  } from '@shell/config/types';
4
6
  import SteveModel from '@shell/plugins/steve/steve-class';
5
7
  import { findBy } from '@shell/utils/array';
@@ -7,10 +9,14 @@ import { get, set } from '@shell/utils/object';
7
9
  import { sortBy } from '@shell/utils/sort';
8
10
  import { ucFirst } from '@shell/utils/string';
9
11
  import { compare } from '@shell/utils/version';
10
- import { AS, MODE, _VIEW, _YAML } from '@shell/config/query-params';
11
12
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
12
13
  import { CAPI as CAPI_ANNOTATIONS, NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
13
14
  import { KEV1 } from '@shell/models/management.cattle.io.kontainerdriver';
15
+ import jsyaml from 'js-yaml';
16
+ import { defineAsyncComponent, markRaw } from 'vue';
17
+ import stevePaginationUtils from '@shell/plugins/steve/steve-pagination-utils';
18
+ import { PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
19
+ import { isHostedProvider } from '@shell/utils/provider';
14
20
 
15
21
  const RKE1_ALLOWED_ACTIONS = [
16
22
  'promptRemove',
@@ -21,6 +27,11 @@ const RKE1_ALLOWED_ACTIONS = [
21
27
  'viewInApi'
22
28
  ];
23
29
 
30
+ const AUTOSCALER_STATUS = {
31
+ PROVISIONING: 'provisioning',
32
+ UNAVAILABLE: 'unavailable'
33
+ };
34
+
24
35
  /**
25
36
  * Class representing Cluster resource.
26
37
  * @extends SteveModel
@@ -48,6 +59,17 @@ export default class ProvCluster extends SteveModel {
48
59
  label: this.t('cluster.detail.machines'),
49
60
  content: this.desired,
50
61
  },
62
+ {
63
+ label: 'Autoscaler',
64
+ content: this.isAutoscalerEnabled,
65
+ valueOverride: {
66
+ component: markRaw(defineAsyncComponent(() => import('@shell/components/formatter/Autoscaler.vue'))),
67
+ props: {
68
+ value: true,
69
+ row: this
70
+ }
71
+ }
72
+ }
51
73
  ].filter((x) => !!x.content);
52
74
 
53
75
  if (!this.machineProvider) {
@@ -148,7 +170,14 @@ export default class ProvCluster extends SteveModel {
148
170
  label: this.$rootGetters['i18n/t']('nav.rotateEncryptionKeys'),
149
171
  icon: 'icon icon-refresh',
150
172
  enabled: canEditRKE2cluster
151
- }, { divider: true }];
173
+ },
174
+ {
175
+ action: 'toggleAutoscalerRunner',
176
+ label: this.isAutoscalerPaused ? 'Resume Autoscaler' : 'Pause Autoscaler',
177
+ icon: `icon ${ this.isAutoscalerPaused ? 'icon-play' : 'icon-pause' }`,
178
+ enabled: this.canPauseResumeAutoscaler
179
+ },
180
+ { divider: true }];
152
181
 
153
182
  const all = actions.concat(out);
154
183
 
@@ -231,26 +260,6 @@ export default class ProvCluster extends SteveModel {
231
260
  }
232
261
  }
233
262
 
234
- goToViewYaml() {
235
- let location;
236
-
237
- if ( !this.isRke2 ) {
238
- location = this.mgmt?.detailLocation;
239
- }
240
-
241
- if ( !location ) {
242
- location = this.detailLocation;
243
- }
244
-
245
- location.query = {
246
- ...location.query,
247
- [MODE]: _VIEW,
248
- [AS]: _YAML
249
- };
250
-
251
- this.currentRouter().push(location);
252
- }
253
-
254
263
  get canDelete() {
255
264
  return super.canDelete && this.stateObj?.name !== 'removing';
256
265
  }
@@ -264,9 +273,26 @@ export default class ProvCluster extends SteveModel {
264
273
  }
265
274
 
266
275
  get isHostedKubernetesProvider() {
267
- const providers = ['AKS', 'EKS', 'GKE'];
276
+ const context = {
277
+ dispatch: this.$dispatch,
278
+ getters: this.$getters,
279
+ axios: this.$axios,
280
+ $extension: this.$plugin,
281
+ t: (...args) => this.t.apply(this, args),
282
+ };
283
+
284
+ return isHostedProvider(context, this.provisioner);
285
+ }
268
286
 
269
- return providers.includes(this.provisioner);
287
+ get providerConfig() {
288
+ if ( this.isRke2 ) {
289
+ return this.spec.rkeConfig;
290
+ }
291
+ if (this.mgmt && this.mgmt.config) {
292
+ return this.mgmt.config;
293
+ }
294
+
295
+ return null;
270
296
  }
271
297
 
272
298
  get isPrivateHostedProvider() {
@@ -306,13 +332,7 @@ export default class ProvCluster extends SteveModel {
306
332
 
307
333
  // imported KEv2
308
334
  // we can't rely on this.provisioner to determine imported-ness for these clusters, as it will return 'aks' 'eks' 'gke' for both provisioned and imported clusters
309
- const kontainerConfigs = ['aksConfig', 'eksConfig', 'gkeConfig'];
310
-
311
- const isImportedKontainer = kontainerConfigs.filter((key) => {
312
- return this.mgmt?.spec?.[key]?.imported === true;
313
- }).length;
314
-
315
- if (isImportedKontainer) {
335
+ if (this.isHostedKubernetesProvider && !!this.providerConfig.imported) {
316
336
  return true;
317
337
  }
318
338
 
@@ -361,7 +381,11 @@ export default class ProvCluster extends SteveModel {
361
381
  }
362
382
 
363
383
  get mgmtClusterId() {
364
- return this.status?.clusterName;
384
+ // when a cluster is created `this` instance isn't immediately updated with `status.clusterName`
385
+ // Workaround - Get fresh copy from the store
386
+ const pCluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, this.id);
387
+
388
+ return this.status?.clusterName || pCluster?.status?.clusterName;
365
389
  }
366
390
 
367
391
  get mgmt() {
@@ -1023,4 +1047,196 @@ export default class ProvCluster extends SteveModel {
1023
1047
  get fullDetailPageOverride() {
1024
1048
  return true;
1025
1049
  }
1050
+
1051
+ async loadAutoscalerEvents() {
1052
+ const autoscalerConfigMap = await this.loadAutoscalerConfigMap();
1053
+ const eventSchema = this.$rootGetters['management/schemaFor'](EVENT);
1054
+ const fields = [new PaginationFilterField({
1055
+ field: 'involvedObject.uid',
1056
+ value: autoscalerConfigMap.metadata.uid,
1057
+ exact: true,
1058
+ }),
1059
+ new PaginationFilterField({
1060
+ field: 'metadata.namespace',
1061
+ value: 'kube-system',
1062
+ exact: true,
1063
+ })];
1064
+ const pagination = {
1065
+ page: 1,
1066
+ pageSize: 200,
1067
+ filters: [
1068
+ new PaginationParamFilter({ fields })
1069
+ ]
1070
+
1071
+ };
1072
+ const params = stevePaginationUtils.createParamsForPagination({ schema: eventSchema, opt: { pagination } });
1073
+ const url = `/k8s/clusters/${ this.mgmtClusterId }/v1/${ EVENT }?${ params }`;
1074
+
1075
+ const events = (await this.$dispatch('cluster/request', { url }, { root: true }))?.data || [];
1076
+
1077
+ return events.filter((event) => event.involvedObject.name === 'cluster-autoscaler-status');
1078
+ }
1079
+
1080
+ get hasAccessToAutoscalerConfigMap() {
1081
+ // This may change but for now this is the best proxy we have for access to the configmap without attempting to access it and getting a 404.
1082
+ return this.canEdit;
1083
+ }
1084
+
1085
+ async loadAutoscalerConfigMap() {
1086
+ const url = `/k8s/clusters/${ this.mgmtClusterId }/v1/${ CONFIG_MAP }/${ AUTOSCALER_CONFIG_MAP_ID }`;
1087
+
1088
+ return await this.$dispatch('cluster/request', { url }, { root: true });
1089
+ }
1090
+
1091
+ get canPauseResumeAutoscaler() {
1092
+ return this.isAutoscalerEnabled && this.canExplore && this.hasAccessToAutoscalerConfigMap;
1093
+ }
1094
+
1095
+ async loadAutoscalerStatus() {
1096
+ if (!this.canExplore) {
1097
+ return AUTOSCALER_STATUS.PROVISIONING;
1098
+ }
1099
+
1100
+ if (!this.hasAccessToAutoscalerConfigMap) {
1101
+ return AUTOSCALER_STATUS.UNAVAILABLE;
1102
+ }
1103
+
1104
+ try {
1105
+ const configMap = await this.loadAutoscalerConfigMap();
1106
+ const yaml = configMap?.data?.status || '';
1107
+
1108
+ return jsyaml.load(yaml);
1109
+ } catch (ex) {
1110
+ console.error(ex); // eslint-disable-line no-console
1111
+
1112
+ return AUTOSCALER_STATUS.UNAVAILABLE;
1113
+ }
1114
+ }
1115
+
1116
+ async loadAutoscalerDetails() {
1117
+ const out = [];
1118
+
1119
+ if (this.isAutoscalerPaused) {
1120
+ out.push({
1121
+ label: this.t('autoscaler.card.details.status'),
1122
+ value: this.t('autoscaler.card.details.paused')
1123
+ });
1124
+
1125
+ return out;
1126
+ }
1127
+
1128
+ const status = await this.loadAutoscalerStatus();
1129
+
1130
+ if (status === AUTOSCALER_STATUS.UNAVAILABLE) {
1131
+ out.push({
1132
+ label: this.t('autoscaler.card.details.status'),
1133
+ value: this.t('autoscaler.card.details.unavailable')
1134
+ });
1135
+
1136
+ return out;
1137
+ }
1138
+
1139
+ if (status === AUTOSCALER_STATUS.PROVISIONING) {
1140
+ out.push({
1141
+ label: this.t('autoscaler.card.details.status'),
1142
+ value: this.t('autoscaler.card.details.provisioning')
1143
+ });
1144
+
1145
+ return out;
1146
+ }
1147
+
1148
+ if (status.autoscalerStatus) {
1149
+ out.push({
1150
+ label: this.t('autoscaler.card.details.status'),
1151
+ value: status.autoscalerStatus
1152
+ });
1153
+ }
1154
+
1155
+ if (status.clusterWide?.health?.status) {
1156
+ const statusValue = status.clusterWide.health.status;
1157
+
1158
+ out.push({
1159
+ label: this.t('autoscaler.card.details.health'),
1160
+ value: {
1161
+ component: 'BadgeStateFormatter',
1162
+ props: {
1163
+ value: statusValue, arbitrary: true, row: {}
1164
+ },
1165
+ }
1166
+ });
1167
+ }
1168
+
1169
+ if (status.clusterWide?.scaleDown?.lastTransitionTime) {
1170
+ out.push({
1171
+ label: this.t('autoscaler.card.details.scaleDown'),
1172
+ value: {
1173
+ component: 'LiveDate',
1174
+ props: {
1175
+ value: status.clusterWide.scaleDown.lastTransitionTime,
1176
+ addSuffix: true
1177
+ }
1178
+ }
1179
+ });
1180
+ }
1181
+
1182
+ if (status.clusterWide?.scaleUp?.lastTransitionTime) {
1183
+ out.push({
1184
+ label: this.t('autoscaler.card.details.scaleUp'),
1185
+ value: {
1186
+ component: 'LiveDate',
1187
+ props: {
1188
+ value: status.clusterWide.scaleUp.lastTransitionTime,
1189
+ addSuffix: true
1190
+ }
1191
+ }
1192
+ });
1193
+ }
1194
+
1195
+ if (status.clusterWide?.health?.nodeCounts?.registered) {
1196
+ out.push({ label: this.t('autoscaler.card.details.nodes') });
1197
+
1198
+ out.push({
1199
+ label: this.t('autoscaler.card.details.ready'),
1200
+ value: status.clusterWide.health.nodeCounts.registered.ready || '0'
1201
+ });
1202
+ out.push({
1203
+ label: this.t('autoscaler.card.details.notStarted'),
1204
+ value: status.clusterWide.health.nodeCounts.registered.notStarted || '0'
1205
+ });
1206
+ out.push({
1207
+ label: this.t('autoscaler.card.details.inTotal'),
1208
+ value: status.clusterWide.health.nodeCounts.registered.total || '0'
1209
+ });
1210
+ }
1211
+
1212
+ return out;
1213
+ }
1214
+
1215
+ get isAutoscalerEnabled() {
1216
+ return !!this.spec?.rkeConfig?.machinePools?.some((pool) => {
1217
+ return typeof pool.autoscalingMinSize !== 'undefined' || typeof pool.autoscalingMaxSize !== 'undefined';
1218
+ });
1219
+ }
1220
+
1221
+ get isAutoscalerPaused() {
1222
+ return !!this.metadata?.annotations?.[CAPI_ANNOTATIONS.AUTOSCALER_CLUSTER_PAUSE];
1223
+ }
1224
+
1225
+ pauseAutoscaler() {
1226
+ this.setAnnotation(CAPI_ANNOTATIONS.AUTOSCALER_CLUSTER_PAUSE, 'true');
1227
+ }
1228
+
1229
+ resumeAutoscaler() {
1230
+ this.setAnnotation(CAPI_ANNOTATIONS.AUTOSCALER_CLUSTER_PAUSE, undefined);
1231
+ }
1232
+
1233
+ toggleAutoscalerRunner() {
1234
+ if (this.isAutoscalerPaused) {
1235
+ this.resumeAutoscaler();
1236
+ } else {
1237
+ this.pauseAutoscaler();
1238
+ }
1239
+
1240
+ return this.save();
1241
+ }
1026
1242
  }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.7",
3
+ "version": "3.0.8-rc.10",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
7
7
  "author": "SUSE",
8
8
  "private": false,
9
+ "types": "types/shell/index.d.ts",
9
10
  "engines": {
10
11
  "node": ">=20.0.0"
11
12
  },
@@ -38,7 +39,7 @@
38
39
  "@babel/preset-typescript": "7.16.7",
39
40
  "@novnc/novnc": "1.2.0",
40
41
  "@popperjs/core": "2.11.8",
41
- "@rancher/icons": "2.0.38",
42
+ "@rancher/icons": "2.0.54",
42
43
  "@types/is-url": "1.2.30",
43
44
  "@types/node": "20.10.8",
44
45
  "@types/semver": "^7.5.8",
@@ -61,7 +62,7 @@
61
62
  "clipboard-polyfill": "4.0.1",
62
63
  "codemirror-editor-vue3": "2.8.0",
63
64
  "codemirror": ">=5.64.0 <6",
64
- "color": "4.2.3",
65
+ "color": "5.0.3",
65
66
  "cookie-universal": "2.2.2",
66
67
  "cookie": "0.7.0",
67
68
  "core-js": "3.45.0",
@@ -105,7 +106,7 @@
105
106
  "jquery": "3.5.1",
106
107
  "js-cookie": "3.0.5",
107
108
  "js-yaml-loader": "1.2.2",
108
- "js-yaml": "4.1.0",
109
+ "js-yaml": "4.1.1",
109
110
  "jsdiff": "1.1.1",
110
111
  "jsonpath-plus": "10.3.0",
111
112
  "jsrsasign": "11.0.0",
@@ -114,7 +115,7 @@
114
115
  "marked": "4.0.17",
115
116
  "node-polyfill-webpack-plugin": "3.0.0",
116
117
  "nodemon": "2.0.22",
117
- "nyc": "15.1.0",
118
+ "nyc": "17.1.0",
118
119
  "papaparse": "5.3.0",
119
120
  "portal-vue": "~3.0.0",
120
121
  "sass-loader": "12.6.0",
@@ -10,7 +10,7 @@ import CopyCode from '@shell/components/CopyCode';
10
10
  import { Banner } from '@components/Banner';
11
11
  import {
12
12
  LOCAL, LOGGED_OUT, TIMED_OUT, IS_SSO, _FLAGGED,
13
- IS_SLO
13
+ IS_SLO, IS_SESSION_IDLE
14
14
  } from '@shell/config/query-params';
15
15
  import { Checkbox } from '@components/Form/Checkbox';
16
16
  import Password from '@shell/components/form/Password';
@@ -32,6 +32,7 @@ import loadPlugins from '@shell/plugins/plugin';
32
32
  import Loading from '@shell/components/Loading';
33
33
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
34
34
  import TabTitle from '@shell/components/TabTitle.vue';
35
+ import { getBrandMeta } from '@shell/utils/brand';
35
36
 
36
37
  export default {
37
38
  name: 'Login',
@@ -47,6 +48,7 @@ export default {
47
48
 
48
49
  timedOut: this.$route.query[TIMED_OUT] === _FLAGGED,
49
50
  loggedOut: this.$route.query[LOGGED_OUT] === _FLAGGED,
51
+ isSessionIdle: this.$route.query[IS_SESSION_IDLE] === _FLAGGED,
50
52
  isSsoLogout: this.$route.query[IS_SSO] === _FLAGGED,
51
53
  isSlo: this.$route.query[IS_SLO] === _FLAGGED,
52
54
  err: this.$route.query.err,
@@ -67,7 +69,9 @@ export default {
67
69
  ...mapGetters({ t: 'i18n/t', hasMultipleLocales: 'i18n/hasMultipleLocales' }),
68
70
 
69
71
  loggedOutSuccessMsg() {
70
- if (this.isSlo) {
72
+ if (this.isSessionIdle) {
73
+ return this.t('login.loggedOutSessionIdle');
74
+ } else if (this.isSlo) {
71
75
  return this.t('login.loggedOutFromSlo');
72
76
  } else if (this.isSsoLogout) {
73
77
  return this.t('login.loggedOutFromSso');
@@ -131,6 +135,25 @@ export default {
131
135
  hasLoginMessage() {
132
136
  return this.errorToDisplay || this.loggedOut || this.timedOut;
133
137
  },
138
+
139
+ customizations() {
140
+ const brandMeta = getBrandMeta(this.$store.getters['management/brand']);
141
+ const login = brandMeta?.login || {};
142
+
143
+ return {
144
+ welcomeLabelKey: 'login.welcome',
145
+ logoClass: 'login-logo',
146
+ ...login,
147
+ };
148
+ },
149
+
150
+ bannerClass() {
151
+ return this.customizations.bannerClass;
152
+ },
153
+
154
+ brandLogo() {
155
+ return this.customizations.logo;
156
+ }
134
157
  },
135
158
 
136
159
  async fetch() {
@@ -332,11 +355,20 @@ export default {
332
355
  </TabTitle>
333
356
  <div class="row gutless mb-20">
334
357
  <div class="col span-6 p-20">
335
- <p class="text-center">
358
+ <p
359
+ v-if="!brandLogo"
360
+ class="text-center"
361
+ >
336
362
  {{ t('login.howdy') }}
337
363
  </p>
364
+ <BrandImage
365
+ v-else
366
+ :class="{[customizations.logoClass]: !!customizations.logoClass}"
367
+ :file-name="brandLogo"
368
+ :alt="t('login.landscapeAlt')"
369
+ />
338
370
  <h1 class="text-center login-welcome">
339
- {{ t('login.welcome', {vendor}) }}
371
+ {{ t(customizations.welcomeLabelKey, {vendor}) }}
340
372
  </h1>
341
373
  <div
342
374
  class="login-messages"
@@ -521,6 +553,7 @@ export default {
521
553
  </div>
522
554
  </div>
523
555
  <BrandImage
556
+ :class="bannerClass"
524
557
  class="col span-6 landscape"
525
558
  data-testid="login-landscape__img"
526
559
  file-name="login-landscape.svg"
@@ -549,6 +582,12 @@ export default {
549
582
  margin: 0
550
583
  }
551
584
 
585
+ .login-logo {
586
+ align-self: center;
587
+ max-width: 260px;
588
+ margin-bottom: 20px;
589
+ }
590
+
552
591
  .login-messages {
553
592
  display: flex;
554
593
  justify-content: center;
@@ -10,7 +10,7 @@ import { AUTH_BROADCAST_CHANNEL_NAME } from '@shell/utils/auth';
10
10
 
11
11
  const samlProviders = ['ping', 'adfs', 'keycloak', 'okta', 'shibboleth'];
12
12
 
13
- const oauthProviders = ['github', 'googleoauth', 'azuread'];
13
+ const oauthProviders = ['github', 'githubapp', 'googleoauth', 'azuread'];
14
14
 
15
15
  function reply(err, code) {
16
16
  try {
@@ -19,7 +19,7 @@ defineProps<{
19
19
  v-for="(subHeaderItem, i) in items"
20
20
  :key="i"
21
21
  class="app-chart-card-sub-header-item"
22
- data-testid="app-chart-card-version"
22
+ data-testid="app-chart-card-sub-header-item"
23
23
  >
24
24
  <i
25
25
  v-clean-tooltip="t(subHeaderItem.iconTooltip.key)"
@@ -35,7 +35,8 @@ defineProps<{
35
35
  <style scoped lang="scss">
36
36
  .app-chart-card-sub-header {
37
37
  display: flex;
38
- gap: var(--gap-md);
38
+ flex-wrap: wrap;
39
+ gap: var(--gap) var(--gap-md);
39
40
  color: var(--link-text-secondary);
40
41
  margin-bottom: 8px;
41
42
 
@@ -0,0 +1,135 @@
1
+ import Chart from '@shell/pages/c/_cluster/apps/charts/chart.vue';
2
+
3
+ jest.mock('clipboard-polyfill', () => ({ writeText: () => {} }));
4
+
5
+ describe('page: Chart Detail', () => {
6
+ describe('computed: maintainers', () => {
7
+ it('should return an empty array if no maintainers are provided', () => {
8
+ const thisContext = {
9
+ version: {},
10
+ versionInfo: null,
11
+ };
12
+ const result = (Chart.computed!.maintainers as () => any[]).call(thisContext);
13
+
14
+ expect(result).toStrictEqual([]);
15
+ });
16
+
17
+ it('should return an empty array for empty maintainers arrays', () => {
18
+ const thisContext = {
19
+ version: { maintainers: [] },
20
+ versionInfo: { chart: { maintainers: [] } },
21
+ };
22
+ const result = (Chart.computed!.maintainers as () => any[]).call(thisContext);
23
+
24
+ expect(result).toStrictEqual([]);
25
+ });
26
+
27
+ it('should prioritize maintainers from "version"', () => {
28
+ const thisContext = {
29
+ version: {
30
+ maintainers: [
31
+ { name: 'Version Maintainer', email: 'version@test.com' }
32
+ ]
33
+ },
34
+ versionInfo: {
35
+ chart: {
36
+ maintainers: [
37
+ { name: 'VersionInfo Maintainer', email: 'versioninfo@test.com' }
38
+ ]
39
+ }
40
+ },
41
+ };
42
+ const result = (Chart.computed!.maintainers as () => any[]).call(thisContext);
43
+
44
+ expect(result).toHaveLength(1);
45
+ expect(result[0].name).toBe('Version Maintainer');
46
+ });
47
+
48
+ it('should fall back to maintainers from "versionInfo"', () => {
49
+ const thisContext = {
50
+ version: {},
51
+ versionInfo: {
52
+ chart: {
53
+ maintainers: [
54
+ { name: 'VersionInfo Maintainer', email: 'versioninfo@test.com' }
55
+ ]
56
+ }
57
+ },
58
+ };
59
+ const result = (Chart.computed!.maintainers as () => any[]).call(thisContext);
60
+
61
+ expect(result).toHaveLength(1);
62
+ expect(result[0].name).toBe('VersionInfo Maintainer');
63
+ });
64
+
65
+ it('should correctly map all maintainer fields', () => {
66
+ const thisContext = {
67
+ version: {
68
+ maintainers: [
69
+ {
70
+ name: 'Full Maintainer', email: 'full@test.com', url: 'http://full.com'
71
+ }
72
+ ]
73
+ },
74
+ versionInfo: null,
75
+ };
76
+ const result = (Chart.computed!.maintainers as () => any[]).call(thisContext);
77
+ const expected = {
78
+ id: 'Full Maintainer-0',
79
+ name: 'Full Maintainer',
80
+ href: 'http://full.com',
81
+ label: 'Full Maintainer'
82
+ };
83
+
84
+ expect(result[0]).toStrictEqual(expected);
85
+ });
86
+
87
+ it('should handle maintainers with missing optional fields', () => {
88
+ const thisContext = {
89
+ version: {
90
+ maintainers: [
91
+ { name: 'No Email Maintainer', url: 'http://noemail.com' },
92
+ { name: 'No URL Maintainer', email: 'nourl@test.com' },
93
+ { name: 'Name Only Maintainer' },
94
+ { url: 'http://noname.com' },
95
+ { email: 'noname@test.com' }
96
+ ]
97
+ },
98
+ versionInfo: null,
99
+ };
100
+ const result = (Chart.computed!.maintainers as () => any[]).call(thisContext);
101
+
102
+ expect(result).toHaveLength(5);
103
+ expect(result[0]).toStrictEqual({
104
+ id: 'No Email Maintainer-0',
105
+ name: 'No Email Maintainer',
106
+ href: 'http://noemail.com',
107
+ label: 'No Email Maintainer'
108
+ });
109
+ expect(result[1]).toStrictEqual({
110
+ id: 'No URL Maintainer-1',
111
+ name: 'No URL Maintainer',
112
+ href: 'mailto:nourl@test.com',
113
+ label: 'No URL Maintainer'
114
+ });
115
+ expect(result[2]).toStrictEqual({
116
+ id: 'Name Only Maintainer-2',
117
+ name: 'Name Only Maintainer',
118
+ href: null,
119
+ label: 'Name Only Maintainer'
120
+ });
121
+ expect(result[3]).toStrictEqual({
122
+ id: 'undefined-3',
123
+ name: undefined,
124
+ href: 'http://noname.com',
125
+ label: 'http://noname.com'
126
+ });
127
+ expect(result[4]).toStrictEqual({
128
+ id: 'undefined-4',
129
+ name: undefined,
130
+ href: 'mailto:noname@test.com',
131
+ label: 'noname@test.com'
132
+ });
133
+ });
134
+ });
135
+ });