@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
@@ -62,6 +62,10 @@ export default {
62
62
 
63
63
  <template>
64
64
  <div class="dashboard-root">
65
+ <a
66
+ href="#main-content"
67
+ class="skip-to-content btn role-primary"
68
+ >{{ t('nav.skipToContent') }}</a>
65
69
  <FixedBanner :header="true" />
66
70
  <Inactivity />
67
71
  <AwsComplianceBanner />
@@ -77,8 +81,10 @@ export default {
77
81
  />
78
82
 
79
83
  <main
84
+ id="main-content"
80
85
  class="main-layout"
81
86
  :aria-label="t('layouts.home')"
87
+ tabindex="-1"
82
88
  >
83
89
  <router-view
84
90
  :key="$route.path"
@@ -142,4 +148,16 @@ export default {
142
148
  padding: 0;
143
149
  }
144
150
  }
151
+
152
+ .skip-to-content {
153
+ position: fixed;
154
+ top: 0;
155
+ left: 0;
156
+ z-index: 9999;
157
+ transform: translateY(-100%);
158
+
159
+ &:focus {
160
+ transform: translate(1rem, 1rem);
161
+ }
162
+ }
145
163
  </style>
@@ -66,6 +66,10 @@ export default {
66
66
 
67
67
  <template>
68
68
  <div class="dashboard-root">
69
+ <a
70
+ href="#main-content"
71
+ class="skip-to-content btn role-primary"
72
+ >{{ t('nav.skipToContent') }}</a>
69
73
  <FixedBanner :header="true" />
70
74
  <AwsComplianceBanner />
71
75
 
@@ -75,8 +79,10 @@ export default {
75
79
  >
76
80
  <Header :simple="true" />
77
81
  <main
82
+ id="main-content"
78
83
  class="main-layout"
79
84
  :aria-label="t('layouts.plain')"
85
+ tabindex="-1"
80
86
  >
81
87
  <IndentedPanel class="pt-20">
82
88
  <router-view
@@ -147,4 +153,16 @@ export default {
147
153
  padding: 0;
148
154
  }
149
155
  }
156
+
157
+ .skip-to-content {
158
+ position: fixed;
159
+ top: 0;
160
+ left: 0;
161
+ z-index: 9999;
162
+ transform: translateY(-100%);
163
+
164
+ &:focus {
165
+ transform: translate(1rem, 1rem);
166
+ }
167
+ }
150
168
  </style>
@@ -1,3 +1,9 @@
1
+ <script>
2
+ import Brand from '@shell/mixins/brand';
3
+
4
+ export default { mixins: [Brand] };
5
+ </script>
6
+
1
7
  <template>
2
8
  <router-view :key="$route.path" />
3
9
  </template>
@@ -5,5 +11,16 @@
5
11
  <style lang="scss">
6
12
  body, #__root, #__layout {
7
13
  height: 100%;
14
+ background: var(--body-bg);
15
+ color: var(--body-text);
16
+ }
17
+
18
+ // Standalone pages should not reserve dashboard layout areas (e.g. side nav and header).
19
+ #__layout {
20
+ --header-height: 0px;
21
+ --nav-width: 0px;
22
+ --wm-vl-width: 0px;
23
+ --wm-vr-width: 0px;
24
+ --wm-height: 0px;
8
25
  }
9
26
  </style>
@@ -0,0 +1,93 @@
1
+ import { computed, provide, ref } from 'vue';
2
+ import { useForm } from 'vee-validate';
3
+ import formRulesGenerator from '@shell/utils/validators/formRules/index';
4
+ import type { Validator } from '@shell/utils/validators/formRules/index';
5
+ import type { Translation } from '@shell/types/t';
6
+
7
+ export interface RuleSet {
8
+ path: string;
9
+ rules: string[];
10
+ translationKey?: string;
11
+ }
12
+
13
+ const nullValidator: Validator = () => undefined;
14
+
15
+ function createRuleResolver(
16
+ t: Translation,
17
+ ruleSets: RuleSet[],
18
+ extraRules: Record<string, Validator>
19
+ ) {
20
+ return (path: string): Validator[] => {
21
+ const set = ruleSets.find((s) => s.path === path);
22
+
23
+ if (!set) return [];
24
+
25
+ const key = set.translationKey ? t(set.translationKey) : 'Value';
26
+ const allRules = {
27
+ ...formRulesGenerator(t, { key }),
28
+ ...extraRules,
29
+ };
30
+
31
+ return set.rules.map((r) => {
32
+ const rule = allRules[r] as Validator | undefined;
33
+
34
+ if (rule) {
35
+ return rule;
36
+ }
37
+
38
+ if (process.env.NODE_ENV !== 'production') {
39
+ throw new Error(`[useFormValidation] Unknown validation rule: "${ r }". Check for a typo in your ruleSets.`);
40
+ }
41
+
42
+ return nullValidator;
43
+ });
44
+ };
45
+ }
46
+
47
+ /**
48
+ * For root/parent form components. Creates a vee-validate form context and
49
+ * resolves fvFormRuleSets-style rule string arrays into validator functions.
50
+ * Child components rendered within this form should use useFormRules instead.
51
+ */
52
+ export function useFormValidation(
53
+ t: Translation,
54
+ ruleSets: RuleSet[],
55
+ extraRules: Record<string, Validator> = {}
56
+ ) {
57
+ const { errors, validate } = useForm();
58
+ const showAllErrors = ref(false);
59
+
60
+ provide('vee-show-all-errors', showAllErrors);
61
+
62
+ const getRules = createRuleResolver(t, ruleSets, extraRules);
63
+ const isFormValid = computed(() => Object.keys(errors.value).length === 0);
64
+
65
+ const validateForm: typeof validate = async(...args) => {
66
+ const result = await validate(...args);
67
+
68
+ showAllErrors.value = true;
69
+
70
+ return result;
71
+ };
72
+
73
+ return {
74
+ getRules,
75
+ isFormValid,
76
+ validateForm,
77
+ veeErrors: errors,
78
+ showAllErrors,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * For child components that render inside a parent useFormValidation() form.
84
+ * Resolves rule strings into validator functions without creating a new form
85
+ * context — fields register with the nearest ancestor's useFormValidation() context.
86
+ */
87
+ export function useFormRules(
88
+ t: Translation,
89
+ ruleSets: RuleSet[],
90
+ extraRules: Record<string, Validator> = {}
91
+ ) {
92
+ return { getRules: createRuleResolver(t, ruleSets, extraRules) };
93
+ }
@@ -0,0 +1,159 @@
1
+ import {
2
+ computed, defineComponent, nextTick, provide, ref
3
+ } from 'vue';
4
+ import { mount, flushPromises } from '@vue/test-utils';
5
+ import { useVeeValidateField } from './useVeeValidateField';
6
+
7
+ function createHarness(opts: {
8
+ name?: string | null;
9
+ rules?: Array<(v: unknown) => string | undefined>;
10
+ value?: unknown;
11
+ validationMessage?: string;
12
+ showAllErrors?: boolean;
13
+ }) {
14
+ return defineComponent({
15
+ setup() {
16
+ const nameRef = ref(opts.name ?? null);
17
+ const rulesRef = ref(opts.rules ?? []);
18
+ const valueRef = ref(opts.value ?? '');
19
+ const validationMessageRef = computed(() => opts.validationMessage);
20
+
21
+ if (opts.showAllErrors) {
22
+ provide('vee-show-all-errors', ref(true));
23
+ }
24
+
25
+ const result = useVeeValidateField({
26
+ name: nameRef,
27
+ rules: rulesRef,
28
+ value: valueRef,
29
+ validationMessage: validationMessageRef,
30
+ });
31
+
32
+ return {
33
+ ...result,
34
+ nameRef,
35
+ valueRef,
36
+ };
37
+ },
38
+ template: '<div />',
39
+ });
40
+ }
41
+
42
+ describe('useVeeValidateField', () => {
43
+ describe('without a name prop', () => {
44
+ it('falls back to the passed validationMessage', async() => {
45
+ const errorMessage = 'This field is required';
46
+ const wrapper = mount(createHarness({
47
+ name: null,
48
+ validationMessage: errorMessage,
49
+ }));
50
+
51
+ await flushPromises();
52
+
53
+ expect(wrapper.vm.effectiveValidationMessage).toBe(errorMessage);
54
+ });
55
+
56
+ it('does not run rules through vee-validate', async() => {
57
+ const rule = jest.fn(() => 'error');
58
+ const wrapper = mount(createHarness({
59
+ name: null,
60
+ rules: [rule],
61
+ value: '',
62
+ }));
63
+
64
+ await flushPromises();
65
+
66
+ expect(wrapper.vm.effectiveValidationMessage).toBeUndefined();
67
+ });
68
+ });
69
+
70
+ describe('with a name prop', () => {
71
+ it('does not show error before the field is touched', async() => {
72
+ const errorMessage = 'Cannot be empty';
73
+ const wrapper = mount(createHarness({
74
+ name: 'testField',
75
+ rules: [(v) => (!v ? errorMessage : undefined)],
76
+ value: '',
77
+ }));
78
+
79
+ await flushPromises();
80
+
81
+ expect(wrapper.vm.effectiveValidationMessage).toBeUndefined();
82
+ });
83
+
84
+ it('shows vee-validate error after veeHandleBlur + veeValidate', async() => {
85
+ const errorMessage = 'Cannot be empty';
86
+ const wrapper = mount(createHarness({
87
+ name: 'testField',
88
+ rules: [(v) => (!v ? errorMessage : undefined)],
89
+ value: '',
90
+ }));
91
+
92
+ wrapper.vm.veeHandleBlur(undefined, false);
93
+ await wrapper.vm.veeValidate();
94
+ await flushPromises();
95
+
96
+ expect(wrapper.vm.effectiveValidationMessage).toBe(errorMessage);
97
+ });
98
+
99
+ it('shows error without touch when showAllErrors is injected as true', async() => {
100
+ const errorMessage = 'Cannot be empty';
101
+ const showAllErrors = ref(false);
102
+
103
+ const inner = defineComponent({
104
+ setup() {
105
+ const nameRef = ref('testField');
106
+ const rulesRef = ref([(v: unknown) => (!v ? errorMessage : undefined)]);
107
+ const valueRef = ref('');
108
+ const validationMessageRef = computed(() => undefined as string | undefined);
109
+
110
+ return useVeeValidateField({
111
+ name: nameRef,
112
+ rules: rulesRef,
113
+ value: valueRef,
114
+ validationMessage: validationMessageRef,
115
+ });
116
+ },
117
+ template: '<div />',
118
+ });
119
+
120
+ const outer = defineComponent({
121
+ components: { inner },
122
+ setup() {
123
+ provide('vee-show-all-errors', showAllErrors);
124
+ },
125
+ template: '<inner ref="innerRef" />',
126
+ });
127
+
128
+ const wrapper = mount(outer);
129
+ const innerVm = wrapper.getComponent(inner);
130
+
131
+ await flushPromises();
132
+ expect(innerVm.vm.effectiveValidationMessage).toBeUndefined();
133
+
134
+ showAllErrors.value = true;
135
+ await nextTick();
136
+
137
+ expect(innerVm.vm.effectiveValidationMessage).toBe(errorMessage);
138
+ });
139
+
140
+ it('clears vee-validate error when value becomes valid', async() => {
141
+ const errorMessage = 'Cannot be empty';
142
+ const wrapper = mount(createHarness({
143
+ name: 'testField',
144
+ rules: [(v) => (!v ? errorMessage : undefined)],
145
+ value: '',
146
+ }));
147
+
148
+ wrapper.vm.veeHandleBlur(undefined, false);
149
+ await wrapper.vm.veeValidate();
150
+ await flushPromises();
151
+ expect(wrapper.vm.effectiveValidationMessage).toBe(errorMessage);
152
+
153
+ wrapper.vm.valueRef = 'some value';
154
+ await flushPromises();
155
+
156
+ expect(wrapper.vm.effectiveValidationMessage).toBeUndefined();
157
+ });
158
+ });
159
+ });
@@ -0,0 +1,67 @@
1
+ import { computed, inject, ref, watch } from 'vue';
2
+ import type { Ref } from 'vue';
3
+ import { useField } from 'vee-validate';
4
+ import { generateRandomAlphaString } from '@shell/utils/string';
5
+
6
+ interface UseVeeValidateFieldOptions {
7
+ name: Ref<string | null | undefined>;
8
+ rules: Ref<Array<any>>;
9
+ value: Ref<unknown>;
10
+ validationMessage: Ref<unknown>;
11
+ }
12
+
13
+ export function useVeeValidateField({
14
+ name,
15
+ rules,
16
+ value,
17
+ validationMessage,
18
+ }: UseVeeValidateFieldOptions) {
19
+ const showAllErrors = inject<Ref<boolean>>('vee-show-all-errors', ref(false));
20
+ const standaloneFieldId = `__field__${ generateRandomAlphaString(12) }`;
21
+ const veeFieldName = computed(() => name.value || standaloneFieldId);
22
+
23
+ const veeValidator = (v: unknown): boolean | string => {
24
+ if (!name.value) return true;
25
+ for (const rule of rules.value as Array<(v: unknown) => string | undefined>) {
26
+ const msg = rule(v);
27
+
28
+ if (msg) return msg;
29
+ }
30
+
31
+ return true;
32
+ };
33
+
34
+ const {
35
+ errorMessage: veeError,
36
+ handleBlur: veeHandleBlur,
37
+ validate: veeValidate,
38
+ value: veeValue,
39
+ meta: veeMeta,
40
+ } = useField<unknown>(veeFieldName, veeValidator, {
41
+ initialValue: value.value,
42
+ validateOnValueUpdate: true,
43
+ validateOnMount: true,
44
+ });
45
+
46
+ // Keep vee-validate's internal value in sync with the controlled prop value.
47
+ watch(value, (v) => {
48
+ if (veeValue.value !== v) {
49
+ veeValue.value = v;
50
+ }
51
+ });
52
+
53
+ const effectiveValidationMessage = computed(() => {
54
+ if (name.value && veeError.value && (veeMeta.touched || showAllErrors.value)) {
55
+ return veeError.value;
56
+ }
57
+
58
+ return validationMessage.value;
59
+ });
60
+
61
+ return {
62
+ effectiveValidationMessage,
63
+ veeHandleBlur,
64
+ veeValidate,
65
+ showAllErrors,
66
+ };
67
+ }
@@ -5,7 +5,10 @@ import {
5
5
  EVENT_TYPE,
6
6
  SECRET_ORIGIN,
7
7
  EVENT_FIRST_SEEN_TIME,
8
- WORKLOAD_HEALTH_SCALE
8
+ WORKLOAD_HEALTH_SCALE,
9
+ MGMT_CLUSTER_PROVIDER,
10
+ MGMT_CLUSTER_KUBE_VERSION,
11
+ AUTOSCALER_ENABLED
9
12
  } from '@shell/config/table-headers';
10
13
 
11
14
  // This file contains table headers
@@ -102,3 +105,17 @@ export const STEVE_WORKLOAD_HEALTH_SCALE = {
102
105
  sort: false,
103
106
  search: false,
104
107
  };
108
+
109
+ export const STEVE_MGMT_CLUSTER_PROVIDER = { ...MGMT_CLUSTER_PROVIDER };
110
+
111
+ export const STEVE_MGMT_CLUSTER_KUBE_VERSION = {
112
+ ...MGMT_CLUSTER_KUBE_VERSION,
113
+ sort: 'status.info.kubernetesVersion',
114
+ search: 'status.info.kubernetesVersion',
115
+ };
116
+
117
+ export const STEVE_AUTOSCALER_ENABLED = {
118
+ ...AUTOSCALER_ENABLED,
119
+ sort: false,
120
+ search: false,
121
+ };
@@ -1,4 +1,6 @@
1
- import { AGE, NAME as NAME_COL, STATE } from '@shell/config/table-headers';
1
+ import {
2
+ AGE, MGMT_CLUSTER_KUBE_VERSION, MGMT_CLUSTER_PROVIDER, NAME as NAME_COL, STATE
3
+ } from '@shell/config/table-headers';
2
4
  import {
3
5
  CAPI,
4
6
  CATALOG,
@@ -16,6 +18,9 @@ import { MULTI_CLUSTER } from '@shell/store/features';
16
18
  import { DSL } from '@shell/store/type-map';
17
19
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
18
20
  import { markRaw } from 'vue';
21
+ import {
22
+ STEVE_AGE_COL, STEVE_MGMT_CLUSTER_KUBE_VERSION, STEVE_MGMT_CLUSTER_PROVIDER, STEVE_NAMESPACE_COL, STEVE_STATE_COL
23
+ } from '@shell/config/pagination-table-headers';
19
24
 
20
25
  export const NAME = 'manager';
21
26
 
@@ -73,6 +78,7 @@ export function init(store) {
73
78
  ]);
74
79
 
75
80
  configureType(SNAPSHOT, { depaginate: true });
81
+ configureType(CATALOG.CLUSTER_REPO, { listCreateButtonLabelKey: 'catalog.repo.add' });
76
82
 
77
83
  configureType(CAPI.RANCHER_CLUSTER, {
78
84
  showListMasthead: false, namespaced: false, alias: [HCI.CLUSTER]
@@ -159,7 +165,16 @@ export function init(store) {
159
165
  width: 100,
160
166
  };
161
167
 
162
- headers(CAPI.RANCHER_CLUSTER, [
168
+ const EXPLORER = {
169
+ name: 'explorer',
170
+ label: ' ',
171
+ align: 'right',
172
+ width: 65,
173
+ sort: false,
174
+ search: false
175
+ };
176
+
177
+ headers(MANAGEMENT.CLUSTER, [
163
178
  STATE,
164
179
  {
165
180
  name: 'name',
@@ -169,35 +184,61 @@ export function init(store) {
169
184
  formatter: 'ClusterLink',
170
185
  canBeVariable: true,
171
186
  },
187
+ MGMT_CLUSTER_PROVIDER,
188
+ MGMT_CLUSTER_KUBE_VERSION,
172
189
  {
173
- name: 'kubernetesVersion',
174
- labelKey: 'tableHeaders.version',
175
- subLabel: 'Architecture',
176
- value: 'kubernetesVersion',
177
- sort: 'kubernetesVersion',
178
- search: 'kubernetesVersion',
190
+ ...MACHINE_SUMMARY,
191
+ sort: 'statusInfo.nodeCount'
179
192
  },
193
+ AGE,
194
+ EXPLORER,
195
+ ], [
196
+ STEVE_STATE_COL,
180
197
  {
181
- name: 'provider',
182
- labelKey: 'tableHeaders.provider',
183
- subLabel: 'Distro',
184
- value: 'machineProvider',
185
- sort: ['machineProvider', 'provisioner'],
186
- formatter: 'ClusterProvider',
198
+ name: 'name',
199
+ labelKey: 'tableHeaders.name',
200
+ value: 'spec.displayName',
201
+ sort: ['spec.displayName'],
202
+ search: ['spec.displayName'],
203
+ formatter: 'ClusterLink',
204
+ canBeVariable: true,
187
205
  },
188
- MACHINE_SUMMARY,
189
- AGE,
206
+ STEVE_MGMT_CLUSTER_PROVIDER,
207
+ STEVE_MGMT_CLUSTER_KUBE_VERSION,
190
208
  {
191
- name: 'explorer',
192
- label: ' ',
193
- align: 'right',
194
- width: 65,
209
+ ...MACHINE_SUMMARY,
210
+ sort: 'status.info.nodeCount'
195
211
  },
212
+ STEVE_AGE_COL,
213
+ EXPLORER
196
214
  ]);
197
215
 
216
+ configureType(MANAGEMENT.CLUSTER, {
217
+ listGroups: [{
218
+ tooltipKey: 'resourceTable.groupBy.none',
219
+ icon: 'icon-list-flat',
220
+ value: 'none',
221
+ }, {
222
+ icon: 'icon-folder',
223
+ // Given management.cattle.io.cluster is not namespaced we group by fleet workspace
224
+ value: 'spec.fleetWorkspaceName',
225
+ field: 'spec.fleetWorkspaceName',
226
+ hideColumn: STEVE_NAMESPACE_COL.name,
227
+ tooltipKey: 'resourceTable.groupBy.workspace',
228
+ groupLabelKey: 'groupByLabel',
229
+ }],
230
+ listGroupsWillOverride: true,
231
+ });
232
+
198
233
  headers(CAPI.MACHINE_DEPLOYMENT, [
199
234
  STATE,
200
- NAME_COL,
235
+ NAME_COL, {
236
+ name: 'cluster',
237
+ labelKey: 'tableHeaders.cluster',
238
+ value: 'clusterName',
239
+ getValue: (row) => row.clusterName,
240
+ sort: ['clusterName'],
241
+ },
201
242
  MACHINE_SUMMARY,
202
243
  AGE
203
244
  ]);
@@ -236,4 +277,24 @@ export function init(store) {
236
277
  })
237
278
  }
238
279
  });
280
+
281
+ const clusterGroupConfig = {
282
+ listGroups: [{
283
+ tooltipKey: 'resourceTable.groupBy.none',
284
+ icon: 'icon-list-flat',
285
+ value: 'none',
286
+ }, {
287
+ icon: 'icon-folder',
288
+ value: 'clusterName',
289
+ field: 'clusterName',
290
+ hideColumn: 'cluster',
291
+ tooltipKey: 'resourceTable.groupBy.cluster',
292
+ groupLabelKey: 'groupByLabel',
293
+ }],
294
+ listGroupsWillOverride: true,
295
+ };
296
+
297
+ configureType(CAPI.MACHINE_DEPLOYMENT, { ...clusterGroupConfig });
298
+ configureType(CAPI.MACHINE_SET, { ...clusterGroupConfig });
299
+ configureType(CAPI.MACHINE, { ...clusterGroupConfig });
239
300
  }
@@ -138,6 +138,12 @@ export default [
138
138
  component: () => interopDefault(import('@shell/components/templates/standalone.vue')),
139
139
  name: 'standalone',
140
140
  children: [
141
+ {
142
+ path: '/c/:cluster/readme',
143
+ component: () => interopDefault(import('@shell/pages/readme.vue')),
144
+ name: 'readme',
145
+ meta: { requiresAuthentication: true, standalone: true }
146
+ },
141
147
  ]
142
148
  },
143
149
  {
@@ -1185,6 +1185,25 @@ export const AUTOSCALER_ENABLED = {
1185
1185
  name: 'autoscaler',
1186
1186
  labelKey: 'tableHeaders.autoscaler',
1187
1187
  value: 'isAutoscalerEnabled',
1188
- sort: ['autoscaler'],
1188
+ sort: ['isAutoscalerEnabled'],
1189
1189
  formatter: 'Autoscaler',
1190
1190
  };
1191
+
1192
+ export const MGMT_CLUSTER_PROVIDER = {
1193
+ name: 'provider',
1194
+ labelKey: 'tableHeaders.provider',
1195
+ subLabel: 'Distro',
1196
+ value: 'statusInfo.machineProvider',
1197
+ sort: ['status.info.machineProvider', 'status.provider', 'status.driver'],
1198
+ search: ['status.info.machineProvider', 'status.provider', 'status.driver'],
1199
+ formatter: 'ClusterProvider',
1200
+ };
1201
+
1202
+ export const MGMT_CLUSTER_KUBE_VERSION = {
1203
+ name: 'kubernetesVersion',
1204
+ labelKey: 'tableHeaders.version',
1205
+ subLabel: 'Architecture',
1206
+ sort: 'statusInfo.kubernetesVersion',
1207
+ search: 'statusInfo.kubernetesVersion',
1208
+ formatter: 'ClusterKubeVersion',
1209
+ };
package/config/types.js CHANGED
@@ -253,7 +253,8 @@ export const MANAGEMENT = {
253
253
  RKE_TEMPLATE: 'management.cattle.io.clustertemplate',
254
254
  RKE_TEMPLATE_REVISION: 'management.cattle.io.clustertemplaterevision',
255
255
  CLUSTER_PROXY_CONFIG: 'management.cattle.io.clusterproxyconfig',
256
- OIDC_CLIENT: 'management.cattle.io.oidcclient'
256
+ OIDC_CLIENT: 'management.cattle.io.oidcclient',
257
+ PROXY_ENDPOINT: 'management.cattle.io.proxyEndpoint',
257
258
  };
258
259
 
259
260
  export const BRAND = {