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

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 (272) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/translations/en-us.yaml +22 -5
  56. package/chart/monitoring/index.vue +10 -1
  57. package/components/ActionDropdownShell.vue +2 -1
  58. package/components/CruResourceFooter.vue +9 -5
  59. package/components/ExplorerProjectsNamespaces.vue +1 -1
  60. package/components/InstallHelmCharts.vue +2 -2
  61. package/components/LandingPagePreference.vue +14 -5
  62. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
  63. package/components/Resource/Detail/Metadata/index.vue +6 -0
  64. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  65. package/components/Resource/Detail/SpacedRow.vue +3 -1
  66. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  67. package/components/ResourceList/Masthead.vue +12 -8
  68. package/components/SelectIconGrid.vue +0 -10
  69. package/components/SingleClusterInfo.vue +1 -0
  70. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  71. package/components/SortableTable/index.vue +6 -9
  72. package/components/SortableTable/selection.js +23 -5
  73. package/components/SortableTable/sorting.js +6 -3
  74. package/components/Wizard.vue +14 -13
  75. package/components/fleet/FleetBundles.vue +100 -12
  76. package/components/fleet/FleetClusterTargets/index.vue +37 -15
  77. package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
  78. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  79. package/components/form/LabeledSelect.vue +20 -3
  80. package/components/form/NameNsDescription.vue +11 -0
  81. package/components/form/Security.vue +6 -2
  82. package/components/form/WorkloadPorts.vue +2 -7
  83. package/components/form/__tests__/Security.test.ts +76 -0
  84. package/components/formatter/Autoscaler.vue +4 -4
  85. package/components/formatter/ClusterKubeVersion.vue +27 -0
  86. package/components/formatter/ClusterLink.vue +1 -7
  87. package/components/formatter/ClusterProvider.vue +6 -10
  88. package/components/formatter/FleetSummaryGraph.vue +0 -3
  89. package/components/formatter/MachineSummaryGraph.vue +1 -1
  90. package/components/formatter/PodsUsage.vue +2 -2
  91. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  92. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  93. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  94. package/components/nav/NamespaceFilter.vue +2 -2
  95. package/components/nav/TopLevelMenu.helper.ts +15 -3
  96. package/components/nav/TopLevelMenu.vue +16 -5
  97. package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
  98. package/components/templates/home.vue +18 -0
  99. package/components/templates/plain.vue +18 -0
  100. package/components/templates/standalone.vue +17 -0
  101. package/composables/useFormValidation.ts +93 -0
  102. package/composables/useVeeValidateField.test.ts +159 -0
  103. package/composables/useVeeValidateField.ts +67 -0
  104. package/config/pagination-table-headers.js +18 -1
  105. package/config/product/manager.js +82 -21
  106. package/config/router/routes.js +6 -0
  107. package/config/table-headers.js +20 -1
  108. package/config/types.js +2 -1
  109. package/core/__tests__/plugin-products.test.ts +904 -20
  110. package/core/plugin-products-base.ts +107 -7
  111. package/core/plugin-products.ts +4 -0
  112. package/core/plugin-types.ts +111 -1
  113. package/core/plugin.ts +15 -7
  114. package/core/productDebugger.js +9 -4
  115. package/core/types-provisioning.ts +43 -30
  116. package/core/types.ts +57 -20
  117. package/detail/__tests__/pod.test.ts +41 -0
  118. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  119. package/detail/pod.vue +1 -1
  120. package/detail/provisioning.cattle.io.cluster.vue +4 -10
  121. package/edit/auth/__tests__/azuread.test.ts +217 -34
  122. package/edit/auth/azuread.vue +122 -14
  123. package/edit/auth/oidc.vue +2 -2
  124. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  125. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  126. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  127. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  128. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  129. package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
  130. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  131. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  132. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  133. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  134. package/edit/secret/__tests__/ssh.test.ts +5 -6
  135. package/edit/secret/basic.vue +31 -0
  136. package/edit/secret/index.vue +68 -17
  137. package/edit/secret/registry.vue +38 -0
  138. package/edit/secret/ssh.vue +29 -0
  139. package/edit/secret/tls.vue +30 -0
  140. package/edit/service.vue +4 -4
  141. package/edit/workload/Upgrading.vue +3 -3
  142. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  143. package/edit/workload/mixins/workload.js +2 -1
  144. package/list/fleet.cattle.io.bundle.vue +7 -104
  145. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  146. package/list/provisioning.cattle.io.cluster.vue +262 -180
  147. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  148. package/mixins/__tests__/chart.test.ts +112 -0
  149. package/mixins/brand.js +2 -1
  150. package/mixins/chart.js +12 -8
  151. package/mixins/resource-fetch-api-pagination.js +41 -5
  152. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  153. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  154. package/models/__tests__/management.cattle.io.node.ts +6 -5
  155. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  156. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
  157. package/models/base-cluster.x-k8s.io.js +26 -0
  158. package/models/cluster.js +1 -1
  159. package/models/cluster.x-k8s.io.machine.js +4 -22
  160. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  161. package/models/cluster.x-k8s.io.machineset.js +2 -20
  162. package/models/compliance.cattle.io.clusterscan.js +130 -2
  163. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  164. package/models/fleet-application.js +3 -1
  165. package/models/management.cattle.io.cluster.js +417 -40
  166. package/models/management.cattle.io.node.js +6 -4
  167. package/models/management.cattle.io.nodepool.js +1 -1
  168. package/models/networking.k8s.io.ingress.js +12 -4
  169. package/models/provisioning.cattle.io.cluster.js +47 -330
  170. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  171. package/package.json +11 -29
  172. package/pages/__tests__/readme.test.ts +49 -0
  173. package/pages/auth/setup.vue +2 -3
  174. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  175. package/pages/c/_cluster/apps/charts/chart.vue +60 -8
  176. package/pages/c/_cluster/apps/charts/install.vue +10 -7
  177. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  178. package/pages/c/_cluster/explorer/index.vue +5 -49
  179. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  180. package/pages/c/_cluster/istio/index.vue +21 -6
  181. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  182. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
  183. package/pages/c/_cluster/uiplugins/index.vue +203 -197
  184. package/pages/diagnostic.vue +13 -17
  185. package/pages/fail-whale.vue +18 -0
  186. package/pages/home.vue +77 -260
  187. package/pages/readme.vue +88 -0
  188. package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
  189. package/plugins/dashboard-store/actions.js +40 -18
  190. package/plugins/dashboard-store/resource-class.js +5 -2
  191. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  192. package/plugins/steve/steve-pagination-utils.ts +11 -3
  193. package/plugins/steve/subscribe.js +35 -5
  194. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  195. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  196. package/rancher-components/RcButton/RcButton.test.ts +37 -1
  197. package/rancher-components/RcButton/RcButton.vue +38 -8
  198. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  199. package/store/__tests__/catalog.test.ts +115 -1
  200. package/store/__tests__/type-map.test.ts +556 -1
  201. package/store/action-menu.js +8 -3
  202. package/store/auth.js +1 -1
  203. package/store/aws.js +27 -16
  204. package/store/catalog.js +27 -3
  205. package/store/digitalocean.js +20 -38
  206. package/store/index.js +2 -0
  207. package/store/linode.js +25 -40
  208. package/store/pnap.js +1 -0
  209. package/store/type-map.js +111 -29
  210. package/tsconfig.paths.json +8 -8
  211. package/types/kube/kube-api.ts +14 -1
  212. package/types/rancher/steve.api.ts +12 -12
  213. package/types/resources/settings.d.ts +2 -1
  214. package/types/shell/index.d.ts +102 -2
  215. package/types/store/dashboard-store.types.ts +108 -11
  216. package/types/store/pagination.types.ts +6 -3
  217. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  218. package/utils/__tests__/async.test.ts +87 -0
  219. package/utils/__tests__/aws.test.ts +140 -0
  220. package/utils/__tests__/banners.test.ts +176 -0
  221. package/utils/__tests__/chart.test.ts +64 -1
  222. package/utils/__tests__/color.test.ts +226 -0
  223. package/utils/__tests__/duration.test.ts +140 -0
  224. package/utils/__tests__/fleet.test.ts +340 -0
  225. package/utils/__tests__/ingress.test.ts +553 -0
  226. package/utils/__tests__/kube.test.ts +68 -0
  227. package/utils/__tests__/namespace-filter.test.ts +109 -0
  228. package/utils/__tests__/pagination-utils.test.ts +361 -0
  229. package/utils/__tests__/parse-externalid.test.ts +137 -0
  230. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  231. package/utils/__tests__/poller-sequential.test.ts +177 -0
  232. package/utils/__tests__/poller.test.ts +170 -0
  233. package/utils/__tests__/promise.test.ts +346 -0
  234. package/utils/__tests__/settings.test.ts +140 -0
  235. package/utils/__tests__/sort-utils.test.ts +301 -0
  236. package/utils/__tests__/string-utils.test.ts +798 -0
  237. package/utils/__tests__/string.test.ts +23 -1
  238. package/utils/__tests__/style.test.ts +154 -0
  239. package/utils/__tests__/svg-filter.test.ts +184 -0
  240. package/utils/__tests__/units.test.ts +417 -0
  241. package/utils/__tests__/versions.test.ts +128 -0
  242. package/utils/__tests__/xccdf.test.ts +391 -0
  243. package/utils/chart.js +36 -0
  244. package/utils/fleet.ts +13 -3
  245. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  246. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  247. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  248. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  249. package/utils/gc/__tests__/gc.test.ts +487 -0
  250. package/utils/ingress.ts +9 -1
  251. package/utils/pagination-utils.ts +2 -1
  252. package/utils/string.js +25 -2
  253. package/utils/uiplugins.ts +5 -5
  254. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  255. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  256. package/utils/validators/__tests__/index.test.ts +481 -0
  257. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  258. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  259. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  260. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  261. package/utils/validators/__tests__/role-template.test.ts +149 -0
  262. package/utils/validators/__tests__/service.test.ts +283 -0
  263. package/utils/validators/__tests__/setting.test.js +32 -0
  264. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  265. package/utils/validators/formRules/index.ts +5 -5
  266. package/utils/validators/machine-pool.ts +1 -1
  267. package/utils/validators/setting.js +18 -3
  268. package/utils/xccdf.ts +418 -0
  269. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  270. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  271. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  272. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
@@ -35,6 +35,7 @@ const setBulkActionOfInterest = (act: HiddenAction | null, event: 'mouseover' |
35
35
  >
36
36
  <rc-dropdown-trigger
37
37
  class="bulk-actions-dropdown"
38
+ size="large"
38
39
  :disabled="disabled"
39
40
  >
40
41
  <template #before>
@@ -42,7 +43,7 @@ const setBulkActionOfInterest = (act: HiddenAction | null, event: 'mouseover' |
42
43
  </template>
43
44
  <span>{{ t('sortableTable.bulkActions.collapsed.label') }}</span>
44
45
  <template #after>
45
- <i class="ml-10 icon icon-chevron-down" />
46
+ <i class="icon icon-chevron-down" />
46
47
  </template>
47
48
  </rc-dropdown-trigger>
48
49
  <template #dropdownCollection>
@@ -3,13 +3,16 @@ import { mapGetters } from 'vuex';
3
3
 
4
4
  import AsyncButton from '@shell/components/AsyncButton';
5
5
  import ResourceCancelModal from '@shell/components/ResourceCancelModal';
6
+ import { RcButton } from '@components/RcButton';
6
7
  import { _VIEW } from '@shell/config/query-params';
7
8
 
8
9
  export default {
9
10
  emits: ['cancel-confirmed', 'finish'],
10
11
 
11
- components: { AsyncButton, ResourceCancelModal },
12
- props: {
12
+ components: {
13
+ AsyncButton, RcButton, ResourceCancelModal
14
+ },
15
+ props: {
13
16
  mode: {
14
17
  type: String,
15
18
  default: 'create',
@@ -84,16 +87,17 @@ export default {
84
87
  <div class="cru-resource-footer">
85
88
  <slot name="footer-prefix" />
86
89
  <slot name="cancel">
87
- <button
90
+ <RcButton
88
91
  v-if="!isView && showCancel"
89
92
  id="cru-cancel"
90
93
  :data-testid="componentTestid + '-cancel'"
91
94
  type="button"
92
- class="btn role-secondary"
95
+ variant="secondary"
96
+ size="large"
93
97
  @click="confirmCancelRequired ? checkCancel(true) : $emit('cancel-confirmed', true)"
94
98
  >
95
99
  <t k="generic.cancel" />
96
- </button>
100
+ </RcButton>
97
101
  </slot>
98
102
  <slot :checkCancel="checkCancel">
99
103
  <AsyncButton
@@ -295,7 +295,7 @@ export default {
295
295
  return this.$store.getters['i18n/t']('resourceTable.groupLabel.notInAProject');
296
296
  },
297
297
  showCreateNsButton() {
298
- return this.groupPreference !== 'namespace';
298
+ return this.groupPreference !== 'namespace' && this.isNamespaceCreatable;
299
299
  },
300
300
  projectGroupBy() {
301
301
  return this.groupPreference === 'none' ? null : 'groupById';
@@ -248,7 +248,7 @@ export default {
248
248
 
249
249
  };
250
250
 
251
- this.installCmd.namespace = this.targetNamespace || this.chart?.targetNamespace || 'default';
251
+ this.installCmd.namespace = this.targetNamespace || this.versionInfo?.chart?.annotations?.[CATALOG_ANNOTATIONS.NAMESPACE] || this.chart?.targetNamespace || 'default';
252
252
  }
253
253
  },
254
254
 
@@ -360,7 +360,7 @@ export default {
360
360
  this.canInstallChart = false;
361
361
  } else {
362
362
  try {
363
- const app = await this.$store.dispatch(`${ this.store }/find`, { type: CATALOG.APP, id: `${ this.targetNamespace || this.chart?.targetNamespace || 'default' }/${ this.chartName }` });
363
+ const app = await this.$store.dispatch(`${ this.store }/find`, { type: CATALOG.APP, id: `${ this.targetNamespace || this.versionInfo?.chart?.annotations?.[CATALOG_ANNOTATIONS.NAMESPACE] || this.chart?.targetNamespace || 'default' }/${ this.chartName }` });
364
364
 
365
365
  if (app) {
366
366
  this.installedVersion = app?.spec?.chart?.metadata?.appVersion;
@@ -14,10 +14,6 @@ export default {
14
14
  Select,
15
15
  },
16
16
 
17
- async fetch() {
18
- this.clusters = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
19
- },
20
-
21
17
  data() {
22
18
  // Store the route as it was on page load (before the user may have changed it)
23
19
  const customRoute = this.$store.getters['prefs/get'](AFTER_LOGIN_ROUTE);
@@ -90,13 +86,25 @@ export default {
90
86
  },
91
87
 
92
88
  methods: {
93
- updateLoginRoute(neu) {
89
+ async updateLoginRoute(neu) {
94
90
  if (neu) {
95
91
  this.afterLoginRoute = neu;
96
92
  } else {
93
+ this.clusters = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
97
94
  this.afterLoginRoute = this.routeFromDropdown?.value || this.routeDropdownOptions[0]?.value;
98
95
  }
99
96
  },
97
+ },
98
+
99
+ watch: {
100
+ afterLoginRoute: {
101
+ async handler(neu) {
102
+ if (typeof neu === 'object') {
103
+ this.clusters = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
104
+ }
105
+ },
106
+ immediate: true
107
+ },
100
108
  }
101
109
  };
102
110
  </script>
@@ -129,6 +137,7 @@ export default {
129
137
  />
130
138
  <Select
131
139
  v-model:value="routeFromDropdown"
140
+ :loading="clusters.length < 1"
132
141
  :aria-label="t('landing.landingPrefs.ariaLabelTakeMeToCluster')"
133
142
  :searchable="true"
134
143
  :disabled="afterLoginRoute === 'home' || afterLoginRoute === 'last-visited'"
@@ -103,12 +103,26 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
103
103
  flex-direction: column;
104
104
 
105
105
  .row {
106
+ gap: 8px;
107
+
108
+ // Hide clearfix pseudo-elements inherited from the global .row class
109
+ &::before, &::after {
110
+ display: none;
111
+ }
112
+
106
113
  &:not(:last-of-type) {
107
114
  margin-bottom: 8px;
108
115
  }
109
116
 
110
117
  .full-custom-value {
111
118
  flex: 1;
119
+ min-width: 0;
120
+
121
+ // Override inline-block on .popover-card-target so it respects the
122
+ // parent's width constraint instead of sizing to its content
123
+ :deep(.popover-card-target) {
124
+ width: 100%;
125
+ }
112
126
  }
113
127
 
114
128
  .value {
@@ -128,7 +142,7 @@ const getRowValueId = (row:Row): string => `value-${ row.label }:${ row.value }`
128
142
 
129
143
  .label {
130
144
  width: 30%;
131
- min-width: 120px;
145
+ min-width: min-content;
132
146
  }
133
147
 
134
148
  .status {
@@ -82,6 +82,12 @@ const showBothEmpty = computed(() => labels.length === 0 && annotations.length =
82
82
 
83
83
  <style lang="scss" scoped>
84
84
  .metadata {
85
+ .identifying-info {
86
+ // Allow the grid cell to shrink below its content size without
87
+ // using overflow:hidden, which would clip the namespace popover
88
+ min-width: 0;
89
+ }
90
+
85
91
  .labels-and-annotations-empty {
86
92
  grid-column: span 2;
87
93
  }
@@ -115,11 +115,22 @@ const actionInvoked = () => {
115
115
 
116
116
  .display {
117
117
  display: inline-flex;
118
+ align-items: center;
119
+ max-width: 100%;
120
+
121
+ // Truncate the link text instead of wrapping to a second line
122
+ a {
123
+ overflow: hidden;
124
+ text-overflow: ellipsis;
125
+ white-space: nowrap;
126
+ min-width: 0;
127
+ }
118
128
  }
119
129
 
120
130
  .rc-status-indicator {
131
+ // Keep the status dot from collapsing when the link text is long
132
+ flex-shrink: 0;
121
133
  margin-right: 12px;
122
- margin-top: 4px;
123
134
  height: initial;
124
135
  line-height: initial;
125
136
  }
@@ -7,9 +7,11 @@
7
7
  <style lang="scss" scoped>
8
8
  .spaced-row {
9
9
  display: grid;
10
- grid-template-columns: repeat(3, minmax(0, 1fr));;
10
+ grid-template-columns: repeat(3, minmax(0, 1fr));
11
11
  grid-auto-flow: dense;
12
12
  grid-gap: 24px;
13
13
  justify-content: space-evenly;
14
+ // Match the title bar floor so sections scroll together
15
+ min-width: $resource-detail-min-width;
14
16
  }
15
17
  </style>
@@ -159,23 +159,20 @@ const showAdditionalActionButtons = computed(() => isArray(additionalActions));
159
159
 
160
160
  <style lang="scss" scoped>
161
161
  .title-bar {
162
- min-width: 740px;
162
+ min-width: $resource-detail-min-width;
163
163
 
164
164
  .badge-state {
165
165
  font-size: 16px;
166
166
  margin-left: 12px;
167
167
  position: relative;
168
- }
169
-
170
- .icon-document {
171
- width: 15px;
172
- font-size: 16px;
173
- margin-right: 10px;
168
+ flex: 0 0 auto;
174
169
  }
175
170
 
176
171
  .actions {
177
172
  display: flex;
178
173
  align-items: center;
174
+ flex: 0 0 auto;
175
+ margin-left: 16px;
179
176
  }
180
177
 
181
178
  .show-configuration, &:deep() .actions > button {
@@ -198,15 +195,17 @@ const showAdditionalActionButtons = computed(() => isArray(additionalActions));
198
195
  max-width: 60%;
199
196
  }
200
197
 
201
- // This prevents the title from overlapping with the actions
198
+ // Title takes the remaining row space; min-width: 0 lets its children
199
+ // (resource-name) shrink so the action buttons stay visible on narrow viewports.
202
200
  .title {
203
- max-width: calc(100% - 260px);
201
+ flex: 1 1 auto;
202
+ min-width: 0;
204
203
  }
205
204
 
206
- // We want the resource name to be what collaspes wh
207
205
  .resource-name {
208
206
  display: inline-block;
209
- flex: 1;
207
+ flex: 0 1 auto;
208
+ min-width: 0;
210
209
  white-space: nowrap;
211
210
  overflow-x: hidden;
212
211
  overflow-y: clip;
@@ -2,6 +2,7 @@
2
2
  import { mapGetters } from 'vuex';
3
3
  import Favorite from '@shell/components/nav/Favorite';
4
4
  import TypeDescription from '@shell/components/TypeDescription';
5
+ import { RcButton } from '@components/RcButton';
5
6
  import { get } from '@shell/utils/object';
6
7
  import { AS, _YAML } from '@shell/config/query-params';
7
8
  import ResourceLoadingIndicator from './ResourceLoadingIndicator';
@@ -16,6 +17,7 @@ export default {
16
17
 
17
18
  components: {
18
19
  Favorite,
20
+ RcButton,
19
21
  TypeDescription,
20
22
  ResourceLoadingIndicator,
21
23
  TabTitle
@@ -221,22 +223,24 @@ export default {
221
223
  <slot name="extraActions" />
222
224
 
223
225
  <slot name="createButton">
224
- <router-link
226
+ <RcButton
225
227
  v-if="hasEditComponent && _isCreatable"
226
- :to="_createLocation"
227
- class="btn role-primary"
228
+ variant="primary"
229
+ size="large"
228
230
  :data-testid="componentTestid+'-create'"
231
+ :to="_createLocation"
229
232
  >
230
233
  {{ _createButtonlabel }}
231
- </router-link>
232
- <router-link
234
+ </RcButton>
235
+ <RcButton
233
236
  v-else-if="_isYamlCreatable"
234
- :to="_yamlCreateLocation"
235
- class="btn role-primary"
237
+ variant="primary"
238
+ size="large"
236
239
  :data-testid="componentTestid+'-create-yaml'"
240
+ :to="_yamlCreateLocation"
237
241
  >
238
242
  {{ t("resourceList.head.createFromYaml") }}
239
- </router-link>
243
+ </RcButton>
240
244
  </slot>
241
245
  </div>
242
246
  </slot>
@@ -131,16 +131,6 @@ export default {
131
131
  class="side-label"
132
132
  :class="{'indicator': true }"
133
133
  />
134
- <div v-if="r.deploysOnWindows">
135
- <label class="deploys-os-label">
136
- {{ t('catalog.charts.deploysOnWindows') }}
137
- </label>
138
- </div>
139
- <div v-if="r.windowsIncompatible">
140
- <label class="os-incompatible-label">
141
- {{ t('catalog.charts.windowsIncompatible') }}
142
- </label>
143
- </div>
144
134
  <div
145
135
  v-if="get(r, sideLabelField)"
146
136
  class="side-label"
@@ -13,6 +13,7 @@ export default {
13
13
  },
14
14
 
15
15
  async fetch() {
16
+ // There's only one cluster, might as well fetch them all...
16
17
  this.clusters = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
17
18
  },
18
19
 
@@ -0,0 +1,126 @@
1
+ import sorting from '@shell/components/SortableTable/sorting';
2
+
3
+ const { arrangedRows } = sorting.computed;
4
+
5
+ describe('sorting mixin', () => {
6
+ describe('arrangedRows', () => {
7
+ function createContext({
8
+ rows = [] as any[],
9
+ sortFields = ['id'],
10
+ descending = false,
11
+ sortGeneration = undefined as string | undefined,
12
+ sortGenerationFn = (() => 'gen1') as (() => string) | undefined,
13
+ externalPaginationEnabled = false,
14
+ cacheKey = null as string | null,
15
+ cachedRows = null as any[] | null,
16
+ cachedRowsRef = null as any[] | null,
17
+ } = {}) {
18
+ return {
19
+ rows,
20
+ sortFields,
21
+ descending,
22
+ sortGeneration,
23
+ sortGenerationFn,
24
+ externalPaginationEnabled,
25
+ cacheKey,
26
+ cachedRows,
27
+ cachedRowsRef,
28
+ };
29
+ }
30
+
31
+ it('should return undefined when externalPaginationEnabled is true', () => {
32
+ const ctx = createContext({ externalPaginationEnabled: true });
33
+
34
+ expect(arrangedRows.call(ctx)).toBeUndefined();
35
+ });
36
+
37
+ it('should sort rows by the given sort fields', () => {
38
+ const rows = [{ id: 'b' }, { id: 'a' }, { id: 'c' }];
39
+ const ctx = createContext({ rows, sortGenerationFn: undefined });
40
+
41
+ const result = arrangedRows.call(ctx);
42
+
43
+ expect(result.map((r: any) => r.id)).toStrictEqual(['a', 'b', 'c']);
44
+ });
45
+
46
+ it('should cache results when sortGenerationFn is provided', () => {
47
+ const rows = [{ id: 'b' }, { id: 'a' }];
48
+ const ctx = createContext({ rows });
49
+
50
+ const result1 = arrangedRows.call(ctx);
51
+
52
+ expect(ctx.cacheKey).not.toBeNull();
53
+ expect(ctx.cachedRows).toStrictEqual(result1);
54
+
55
+ const result2 = arrangedRows.call(ctx);
56
+
57
+ expect(result2).toBe(ctx.cachedRows);
58
+ });
59
+
60
+ it('should invalidate cache when rows change to different items with the same count', () => {
61
+ const rowsA = [{ id: 'alpha' }];
62
+ const rowsB = [{ id: 'beta' }];
63
+ const ctx = createContext({ rows: rowsA });
64
+
65
+ const resultA = arrangedRows.call(ctx);
66
+
67
+ expect(resultA.map((r: any) => r.id)).toStrictEqual(['alpha']);
68
+
69
+ ctx.rows = rowsB;
70
+ const resultB = arrangedRows.call(ctx);
71
+
72
+ expect(resultB.map((r: any) => r.id)).toStrictEqual(['beta']);
73
+ });
74
+
75
+ it('should return cached rows when same rows are passed again', () => {
76
+ const rows = [{ id: 'x' }, { id: 'y' }];
77
+ const ctx = createContext({ rows });
78
+
79
+ arrangedRows.call(ctx);
80
+ const cached = ctx.cachedRows;
81
+
82
+ const result = arrangedRows.call(ctx);
83
+
84
+ expect(result).toBe(cached);
85
+ });
86
+
87
+ it('should invalidate cache when descending changes', () => {
88
+ const rows = [{ id: 'a' }, { id: 'b' }];
89
+ const ctx = createContext({ rows });
90
+
91
+ arrangedRows.call(ctx);
92
+
93
+ ctx.descending = true;
94
+ const result = arrangedRows.call(ctx);
95
+
96
+ expect(result.map((r: any) => r.id)).toStrictEqual(['b', 'a']);
97
+ });
98
+
99
+ it('should not cache when there is no sortGenerationFn or sortGeneration', () => {
100
+ const rows = [{ id: 'a' }];
101
+ const ctx = createContext({ rows });
102
+
103
+ ctx.sortGenerationFn = undefined;
104
+ ctx.sortGeneration = undefined;
105
+
106
+ arrangedRows.call(ctx);
107
+
108
+ expect(ctx.cacheKey).toBeNull();
109
+ expect(ctx.cachedRows).toBeNull();
110
+ });
111
+
112
+ it('should use sortGeneration over sortGenerationFn when provided', () => {
113
+ const rows = [{ id: 'a' }];
114
+ const ctx = createContext({
115
+ rows,
116
+ sortGeneration: 'custom-gen',
117
+ sortGenerationFn: () => 'fn-gen',
118
+ });
119
+
120
+ arrangedRows.call(ctx);
121
+
122
+ expect(ctx.cacheKey).toContain('custom-gen');
123
+ expect(ctx.cacheKey).not.toContain('fn-gen');
124
+ });
125
+ });
126
+ });
@@ -26,6 +26,7 @@ import ButtonMultiAction from '@shell/components/ButtonMultiAction.vue';
26
26
  import ActionMenu from '@shell/components/ActionMenuShell.vue';
27
27
  import { useRuntimeFlag } from '@shell/composables/useRuntimeFlag';
28
28
  import ActionDropdownShell from '@shell/components/ActionDropdownShell.vue';
29
+ import { RcButton } from '@components/RcButton';
29
30
  import { useTabCountUpdater } from '@shell/components/form/ResourceTabs/composable';
30
31
 
31
32
  // Uncomment for table performance debugging
@@ -65,6 +66,7 @@ export default {
65
66
  ButtonMultiAction,
66
67
  ActionMenu,
67
68
  ActionDropdownShell,
69
+ RcButton,
68
70
  },
69
71
 
70
72
  mixins: [
@@ -1116,17 +1118,17 @@ export default {
1116
1118
  >
1117
1119
  <slot name="header-left">
1118
1120
  <template v-if="tableActions">
1119
- <button
1121
+ <RcButton
1120
1122
  v-for="(act) in availableActions"
1121
1123
  :id="act.action"
1122
1124
  :key="act.action"
1123
1125
  v-clean-tooltip="actionTooltip"
1124
1126
  type="button"
1125
- class="btn role-primary"
1127
+ variant="primary"
1128
+ size="large"
1126
1129
  :class="{[bulkActionClass]:true}"
1127
1130
  :disabled="!act.enabled"
1128
1131
  :data-testid="componentTestid + '-' + act.action"
1129
- role="button"
1130
1132
  :aria-label="act.label"
1131
1133
  @click="applyTableAction(act, null, $event)"
1132
1134
  @keydown.enter.stop
@@ -1138,7 +1140,7 @@ export default {
1138
1140
  :class="act.icon"
1139
1141
  />
1140
1142
  <span v-clean-html="act.label" />
1141
- </button>
1143
+ </RcButton>
1142
1144
  <template v-if="featureDropdownMenu">
1143
1145
  <ActionDropdownShell
1144
1146
  :disabled="!selectedRows.length"
@@ -2154,11 +2156,6 @@ export default {
2154
2156
  }
2155
2157
  }
2156
2158
 
2157
- .bulk-action {
2158
- .icon {
2159
- vertical-align: -10%;
2160
- }
2161
- }
2162
2159
  }
2163
2160
 
2164
2161
  .middle {
@@ -626,27 +626,45 @@ function _execute(resources, action, args, opts = {}, ctx) {
626
626
  return action.invoke.apply(ctx, [actionOpts, resources || [], args]);
627
627
  }
628
628
 
629
+ /**
630
+ * for the given resource find it's action matching the target action. if that target action has an alt resource return it
631
+ */
632
+ const findResourceFromAction = (r) => {
633
+ const actualAction = r.availableActions.find((aa) => aa.action === action.action);
634
+
635
+ return actualAction?.altResource || r;
636
+ };
637
+
638
+ // if there there are multiple resources and a bulk action, use it and pass in the resources
639
+ // for example cluster management cluster list Download KubeConfig
629
640
  if ( resources.length > 1 && action.bulkAction && !opts.alt ) {
630
- const fn = resources[0][action.bulkAction];
641
+ const applyResource = findResourceFromAction(resources[0]);
642
+ const fn = applyResource[action.bulkAction];
631
643
 
632
644
  if ( fn ) {
633
- return fn.call(resources[0], resources, ...args);
645
+ const applyResources = resources.map(findResourceFromAction);
646
+
647
+ return fn.call(applyResource, applyResources, ...args);
634
648
  }
635
649
  }
636
650
 
637
651
  const promises = [];
638
652
 
653
+ // if there is a single resource or no bulk action, for each resource execute it's action
654
+ // for example delete when only one row is selected
639
655
  for ( const resource of resources ) {
640
656
  let fn;
641
657
 
658
+ const applyResource = findResourceFromAction(resource);
659
+
642
660
  if (opts.alt && action.altAction) {
643
- fn = resource[action.altAction];
661
+ fn = applyResource[action.altAction];
644
662
  } else {
645
- fn = resource[action.action];
663
+ fn = applyResource[action.action];
646
664
  }
647
665
 
648
666
  if ( fn ) {
649
- promises.push(fn.apply(resource, args));
667
+ promises.push(fn.apply(applyResource, args));
650
668
  }
651
669
  }
652
670
 
@@ -50,7 +50,8 @@ export default {
50
50
 
51
51
  if ( sortGenerationKey) {
52
52
  key = `${ sortGenerationKey }/${ this.rows.length }/${ this.descending }/${ this.sortFields.join(',') }`;
53
- if ( this.cacheKey === key ) {
53
+
54
+ if ( this.cacheKey === key && this.cachedRowsRef === this.rows ) {
54
55
  return this.cachedRows;
55
56
  }
56
57
  }
@@ -60,6 +61,7 @@ export default {
60
61
  if ( key ) {
61
62
  this.cacheKey = key;
62
63
  this.cachedRows = out;
64
+ this.cachedRowsRef = this.rows;
63
65
  }
64
66
 
65
67
  return out;
@@ -104,8 +106,9 @@ export default {
104
106
  return {
105
107
  sortBy,
106
108
  descending,
107
- cachedRows: null,
108
- cacheKey: null,
109
+ cachedRows: null,
110
+ cachedRowsRef: null,
111
+ cacheKey: null,
109
112
  };
110
113
  },
111
114