@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -7,8 +7,7 @@
7
7
  *
8
8
  * <rc-button variant="primary" @click="doAction">Perform an Action</rc-button>
9
9
  */
10
- import { computed, ref, resolveComponent } from 'vue';
11
- import type { RouteLocationRaw } from 'vue-router';
10
+ import { computed, ref, resolveComponent, watchEffect } from 'vue';
12
11
  import {
13
12
  ButtonVariantProps,
14
13
  ButtonSizeProps,
@@ -16,6 +15,7 @@ import {
16
15
  ButtonSizeNewProps,
17
16
  ButtonSize,
18
17
  IconProps,
18
+ NavigationProps,
19
19
  } from './types';
20
20
  import RcIcon from '@components/RcIcon/RcIcon.vue';
21
21
 
@@ -44,16 +44,48 @@ const props = withDefaults(
44
44
  ButtonSizeProps &
45
45
  ButtonVariantNewProps &
46
46
  ButtonSizeNewProps &
47
- IconProps & { to?: RouteLocationRaw }
47
+ IconProps &
48
+ NavigationProps
48
49
  >(),
49
50
  {
50
51
  size: 'medium',
51
52
  to: undefined,
53
+ href: undefined,
52
54
  }
53
55
  );
54
56
 
55
- const tag = computed(() => (props.to ? resolveComponent('RouterLink') : 'button'));
56
- const role = computed(() => (props.to ? 'link' : 'button'));
57
+ if (process.env.NODE_ENV !== 'production') {
58
+ watchEffect(() => {
59
+ if (props.to && props.href) {
60
+ console.warn('[RcButton] "to" and "href" are mutually exclusive. Provide only one.'); // eslint-disable-line no-console
61
+ }
62
+ });
63
+ }
64
+
65
+ const tag = computed(() => {
66
+ if (props.to) {
67
+ return resolveComponent('RouterLink');
68
+ }
69
+ if (props.href) {
70
+ return 'a';
71
+ }
72
+
73
+ return 'button';
74
+ });
75
+
76
+ const role = computed(() => (props.to || props.href ? 'link' : 'button'));
77
+
78
+ const linkProps = computed(() => {
79
+ if (props.to) {
80
+ return { to: props.to };
81
+ }
82
+
83
+ if (props.href) {
84
+ return { href: props.href };
85
+ }
86
+
87
+ return {};
88
+ });
57
89
 
58
90
  const activeVariantClassName = computed(() => {
59
91
  if (props.variant === 'multiAction' || props.multiAction) {
@@ -110,6 +142,22 @@ const focus = () => {
110
142
  RcFocusTarget?.value?.focus();
111
143
  };
112
144
 
145
+ const preventScroll = (event: KeyboardEvent) => {
146
+ if (tag.value === 'button') {
147
+ return;
148
+ }
149
+
150
+ event.preventDefault();
151
+ };
152
+
153
+ const handleSpace = (event: KeyboardEvent) => {
154
+ if (tag.value === 'button') {
155
+ return;
156
+ }
157
+
158
+ (event.target as HTMLElement).click();
159
+ };
160
+
113
161
  defineExpose({ focus });
114
162
  </script>
115
163
 
@@ -118,8 +166,10 @@ defineExpose({ focus });
118
166
  :is="tag"
119
167
  ref="RcFocusTarget"
120
168
  :role="role"
121
- :to="to"
169
+ v-bind="linkProps"
122
170
  :class="{ ...buttonClass }"
171
+ @keydown.space="preventScroll"
172
+ @keyup.space="handleSpace"
123
173
  >
124
174
  <slot
125
175
  v-if="$slots.before || props.leftIcon"
@@ -185,9 +235,12 @@ defineExpose({ focus });
185
235
  }
186
236
 
187
237
  &:disabled {
188
- background: var(--primary);
189
- color: var(--primary-text);
190
- opacity: 0.5;
238
+ &, &:hover, &:focus {
239
+ color: var(--disabled-text);
240
+ background: var(--disabled-bg);
241
+ border-color: var(--disabled-bg);
242
+ cursor: not-allowed;
243
+ }
191
244
  }
192
245
  }
193
246
 
@@ -210,6 +263,15 @@ defineExpose({ focus });
210
263
  @include focus-outline;
211
264
  outline-offset: 2px;
212
265
  }
266
+
267
+ &:disabled {
268
+ &, &:hover, &:focus {
269
+ color: var(--disabled-text);
270
+ background: var(--disabled-bg);
271
+ border-color: var(--disabled-bg);
272
+ cursor: not-allowed;
273
+ }
274
+ }
213
275
  }
214
276
 
215
277
  &.variant-tertiary {
@@ -231,6 +293,15 @@ defineExpose({ focus });
231
293
  @include focus-outline;
232
294
  outline-offset: 2px;
233
295
  }
296
+
297
+ &:disabled {
298
+ &, &:hover, &:focus {
299
+ color: var(--disabled-text);
300
+ background: var(--disabled-bg);
301
+ border-color: var(--disabled-bg);
302
+ cursor: not-allowed;
303
+ }
304
+ }
234
305
  }
235
306
 
236
307
  &.variant-link {
@@ -283,11 +354,15 @@ defineExpose({ focus });
283
354
  &.btn-small {
284
355
  //:not(.btn-sm) is being used to make the style more specific to override global styles. We may want to get rid of those styles at some point.
285
356
  &, &:not(.btn-sm) {
286
- line-height: 140%;
357
+ // Unitless ratio chosen so font-size * line-height (12px * 4/3 = 16px)
358
+ // is a whole pixel value. A fractional computed line-height (e.g. the
359
+ // previous 140% = 16.8px) shifts text ~1px off-center within the
360
+ // flex-centered button in Chrome, but not in Firefox.
361
+ line-height: calc(4 / 3);
287
362
  font-size: 12px;
288
363
  min-height: 24px;
289
364
 
290
- padding: 0 8px;
365
+ padding: var(--rc-button-padding, 0 8px);
291
366
  gap: 8px;
292
367
  }
293
368
  }
@@ -295,11 +370,13 @@ defineExpose({ focus });
295
370
  &.btn-medium {
296
371
  //:not(.btn-sm) is being used to make the style more specific to override global styles. We may want to get rid of those styles at some point.
297
372
  &, &:not(.btn-sm) {
298
- line-height: 140%;
373
+ // Unitless ratio chosen so font-size * line-height (14px * 10/7 = 20px)
374
+ // is a whole pixel value. See note in .btn-small for why this matters.
375
+ line-height: calc(10 / 7);
299
376
  font-size: 14px;
300
377
  min-height: 32px;
301
378
 
302
- padding: 0 12px;
379
+ padding: var(--rc-button-padding, 0 12px);
303
380
  gap: 8px;
304
381
  }
305
382
  }
@@ -307,11 +384,13 @@ defineExpose({ focus });
307
384
  &.btn-large {
308
385
  // This is the default size brought by the global button styling
309
386
  &, &:not(.btn-sm) {
310
- line-height: 140%;
387
+ // Unitless ratio chosen so font-size * line-height (16px * 1.5 = 24px)
388
+ // is a whole pixel value. See note in .btn-small for why this matters.
389
+ line-height: 1.5;
311
390
  font-size: 16px;
312
391
  min-height: 40px;
313
392
 
314
- padding: 0 16px;
393
+ padding: var(--rc-button-padding, 0 16px);
315
394
  gap: 12px;
316
395
  }
317
396
  }
@@ -1,2 +1,2 @@
1
1
  export { default as RcButton } from './RcButton.vue';
2
- export type { RcButtonType } from './types';
2
+ export type { RcButtonType, ButtonSize } from './types';
@@ -1,5 +1,8 @@
1
+ import type { RouteLocationRaw } from 'vue-router';
1
2
  import { RcIconType } from '@components/RcIcon/types';
2
3
 
4
+ export type NavigationProps = { to?: RouteLocationRaw; href?: string }
5
+
3
6
  // TODO: 13211 Investigate why `InstanceType<typeof RcButton>` fails prod builds
4
7
  // export type RcButtonType = InstanceType<typeof RcButton>
5
8
  export type RcButtonType = {
@@ -3,9 +3,13 @@
3
3
  * A button that opens a menu. Used in conjunction with `RcDropdown.vue`.
4
4
  */
5
5
  import { inject, onMounted, ref } from 'vue';
6
- import { RcButton, RcButtonType } from '@components/RcButton';
6
+ import { RcButton, RcButtonType, ButtonSize } from '@components/RcButton';
7
7
  import { DropdownContext, defaultContext } from './types';
8
8
 
9
+ defineProps<{
10
+ size?: ButtonSize,
11
+ }>();
12
+
9
13
  const {
10
14
  showMenu,
11
15
  registerTrigger,
@@ -32,6 +36,7 @@ defineExpose({ focus });
32
36
  role="button"
33
37
  aria-haspopup="menu"
34
38
  :aria-expanded="isMenuOpen"
39
+ :size="size"
35
40
  @keydown.enter.space="handleKeydown"
36
41
  @click="showMenu(true)"
37
42
  >
@@ -36,6 +36,24 @@ describe('rcItemCard', () => {
36
36
  expect(wrapper.findAll(`[data-testid="item-card-header-statuses-status"]`)).toHaveLength(2);
37
37
  });
38
38
 
39
+ // Regression: when image.src is falsy, LazyImage must still render so its own
40
+ // empty-src fallback (generic catalog icon) is shown. Previously the template
41
+ // was gated by `v-else-if="image.src"` and showed an empty box for missing icons.
42
+ it.each(['medium', 'small'] as const)('renders LazyImage with an empty src when image.src is falsy (%s variant)', (variant) => {
43
+ const wrapper = mount(RcItemCard, {
44
+ props: {
45
+ ...baseProps,
46
+ variant,
47
+ image: { src: '', alt: { text: 'Logo' } },
48
+ }
49
+ });
50
+
51
+ const lazy = wrapper.findComponent({ name: 'LazyImage' });
52
+
53
+ expect(lazy.exists()).toBe(true);
54
+ expect(lazy.props('src')).toBe('');
55
+ });
56
+
39
57
  it('renders pill only in medium variant', () => {
40
58
  const wrapper = mount(RcItemCard, {
41
59
  props: {
@@ -208,7 +208,7 @@ const cursorValue = computed(() => props.clickable ? 'pointer' : 'auto');
208
208
  size="xxlarge"
209
209
  />
210
210
  </template>
211
- <template v-else-if="image.src">
211
+ <template v-else>
212
212
  <LazyImage
213
213
  :src="image.src"
214
214
  :alt="imageAlt"
@@ -255,7 +255,7 @@ const cursorValue = computed(() => props.clickable ? 'pointer' : 'auto');
255
255
  size="xlarge"
256
256
  />
257
257
  </template>
258
- <template v-else-if="image.src">
258
+ <template v-else>
259
259
  <LazyImage
260
260
  :src="image.src"
261
261
  :alt="imageAlt"
@@ -33,9 +33,12 @@
33
33
  * <p>Section content here</p>
34
34
  * </RcSection>
35
35
  */
36
- import { computed, inject, provide, type Ref } from 'vue';
36
+ import {
37
+ computed, inject, provide, useTemplateRef, type Ref
38
+ } from 'vue';
37
39
  import RcButton from '@components/RcButton/RcButton.vue';
38
40
  import RcIcon from '@components/RcIcon/RcIcon.vue';
41
+ import { useInSummary } from '@shell/components/TableOfContents/composables';
39
42
  import type { RcSectionProps, SectionBackground } from './types';
40
43
 
41
44
  const RC_SECTION_BG_KEY = 'rc-section-background';
@@ -58,6 +61,24 @@ provide(RC_SECTION_BG_KEY, resolvedBackground);
58
61
 
59
62
  const expanded = defineModel<boolean>('expanded', { default: true });
60
63
 
64
+ // Expose summary, name, and a display label on the component public instance so
65
+ // TOC discovery can access component
66
+ const displayTitle = computed(() => props.title);
67
+
68
+ const name = 'RcSection';
69
+
70
+ // Register this section in form summary/table-of-contents context (if provided)
71
+ const sectionRef = useTemplateRef<HTMLElement>('rc-section-summarized-container');
72
+ const { summary } = useInSummary({
73
+ label: displayTitle,
74
+ scrollTo: () => sectionRef.value?.scrollIntoView(true),
75
+ elementRef: sectionRef,
76
+ });
77
+
78
+ defineExpose({
79
+ summary, displayTitle, name
80
+ });
81
+
61
82
  const hasHeader = computed(() => {
62
83
  return props.mode === 'with-header';
63
84
  });
@@ -84,7 +105,10 @@ function toggle() {
84
105
  </script>
85
106
 
86
107
  <template>
87
- <div :class="sectionClass">
108
+ <div
109
+ ref="rc-section-summarized-container"
110
+ :class="sectionClass"
111
+ >
88
112
  <div
89
113
  v-if="hasHeader"
90
114
  class="section-header"
@@ -203,7 +227,8 @@ function toggle() {
203
227
  color: var(--body-text, inherit);
204
228
  }
205
229
 
206
- button.btn-medium.toggle-button {
230
+ // TODO: Considering removing specificity override when RcButton sizes are refactored (#18062)
231
+ .left-wrapper :deep(button.toggle-button.btn-medium:not(.btn-sm)) {
207
232
  flex-shrink: 0;
208
233
  font-size: 16px;
209
234
  color: var(--body-text, inherit);
@@ -1,4 +1,4 @@
1
- FROM registry.suse.com/bci/bci-base:15.5
1
+ FROM registry.suse.com/bci/bci-base:15.7
2
2
 
3
3
  RUN zypper -n install nginx jq
4
4
 
@@ -240,7 +240,8 @@ clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
240
240
  # clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
241
241
  clone_repo_test_extension_build "neuvector" "manager-ext" "neuvector-ui-ext"
242
242
  # clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
243
- clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
243
+ # Uncomment once https://github.com/harvester/harvester/issues/10691 is resolved
244
+ #clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
244
245
  clone_repo_test_extension_build "rancher" "ali-ui" "ali"
245
246
  clone_repo_test_extension_build "rancher" "virtual-clusters-ui" "virtual-clusters"
246
247
  # clone_repo_test_extension_build "rancher" "rancher-ai-ui" "rancher-ai-ui"
@@ -0,0 +1,131 @@
1
+ import {
2
+ getters,
3
+ actions,
4
+ mapFeature,
5
+ create,
6
+ MULTI_CLUSTER,
7
+ LEGACY,
8
+ RKE2,
9
+ } from '@shell/store/features';
10
+ import { MANAGEMENT } from '@shell/config/types';
11
+
12
+ describe('features store', () => {
13
+ describe('create', () => {
14
+ it('returns the feature name', () => {
15
+ const name = 'test-feature-create-only';
16
+ const result = create(name, true);
17
+
18
+ expect(result).toStrictEqual(name);
19
+ });
20
+ });
21
+
22
+ describe('mapFeature', () => {
23
+ it('calls the features/get getter with the feature name', () => {
24
+ const mockGetFn = jest.fn(() => true);
25
+ const ctx = { $store: { getters: { 'features/get': mockGetFn } } };
26
+ const mapped = mapFeature('some-feature');
27
+
28
+ const result = mapped.get.call(ctx);
29
+
30
+ expect(mockGetFn).toHaveBeenCalledWith('some-feature');
31
+ expect(result).toBe(true);
32
+ });
33
+
34
+ it('set throws an error indicating the store is get-only', () => {
35
+ const mapped = mapFeature('some-feature');
36
+
37
+ expect(() => mapped.set(true)).toThrow('The feature store only supports getting');
38
+ });
39
+ });
40
+
41
+ describe('getters', () => {
42
+ describe('get', () => {
43
+ it('throws for an unknown feature name', () => {
44
+ const rootGetters = { 'management/byId': jest.fn(() => undefined) };
45
+
46
+ expect(() => getters.get({}, {}, {}, rootGetters)('unknown-feature-xyz')).toThrow('Unknown feature: unknown-feature-xyz');
47
+ });
48
+
49
+ it('returns entry.enabled from the server when an entry is found', () => {
50
+ const entry = { enabled: false };
51
+ const rootGetters = { 'management/byId': jest.fn(() => entry) };
52
+
53
+ const result = getters.get({}, {}, {}, rootGetters)(MULTI_CLUSTER);
54
+
55
+ expect(result).toBe(false);
56
+ });
57
+
58
+ it.each([
59
+ {
60
+ desc: 'multi-cluster-management (default true)',
61
+ feature: MULTI_CLUSTER,
62
+ expected: true,
63
+ },
64
+ {
65
+ desc: 'legacy (default false)',
66
+ feature: LEGACY,
67
+ expected: false,
68
+ },
69
+ {
70
+ desc: 'rke2 (default true)',
71
+ feature: RKE2,
72
+ expected: true,
73
+ },
74
+ ])('returns the registered default when no server entry exists for $desc', ({ feature, expected }) => {
75
+ const rootGetters = { 'management/byId': jest.fn(() => undefined) };
76
+
77
+ const result = getters.get({}, {}, {}, rootGetters)(feature);
78
+
79
+ expect(result).toBe(expected);
80
+ });
81
+
82
+ it('calls management/byId with the MANAGEMENT.FEATURE type and the feature name', () => {
83
+ const byId = jest.fn(() => undefined);
84
+ const rootGetters = { 'management/byId': byId };
85
+
86
+ getters.get({}, {}, {}, rootGetters)(RKE2);
87
+
88
+ expect(byId).toHaveBeenCalledWith(MANAGEMENT.FEATURE, RKE2);
89
+ });
90
+ });
91
+ });
92
+
93
+ describe('actions', () => {
94
+ describe('loadServer', () => {
95
+ it('dispatches management/findAll when canList returns true', async() => {
96
+ const findAllResult = [{ name: 'feature-1' }];
97
+ const dispatch = jest.fn().mockResolvedValue(findAllResult);
98
+ const rootGetters = { 'management/canList': jest.fn(() => true) };
99
+
100
+ const result = await actions.loadServer({ rootGetters, dispatch });
101
+
102
+ expect(dispatch).toHaveBeenCalledWith(
103
+ 'management/findAll',
104
+ { type: MANAGEMENT.FEATURE, opt: { watch: false } },
105
+ { root: true }
106
+ );
107
+ expect(result).toStrictEqual(findAllResult);
108
+ });
109
+
110
+ it('does not dispatch when canList returns false', async() => {
111
+ const dispatch = jest.fn();
112
+ const rootGetters = { 'management/canList': jest.fn(() => false) };
113
+
114
+ const result = await actions.loadServer({ rootGetters, dispatch });
115
+
116
+ expect(dispatch).not.toHaveBeenCalled();
117
+ expect(result).toBeUndefined();
118
+ });
119
+
120
+ it('calls management/canList with the MANAGEMENT.FEATURE type', async() => {
121
+ const canList = jest.fn(() => false);
122
+ const dispatch = jest.fn();
123
+ const rootGetters = { 'management/canList': canList };
124
+
125
+ await actions.loadServer({ rootGetters, dispatch });
126
+
127
+ expect(canList).toHaveBeenCalledWith(MANAGEMENT.FEATURE);
128
+ });
129
+ });
130
+ });
131
+ });