@rancher/shell 3.0.4 → 3.0.5-rc.2

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 (270) hide show
  1. package/assets/images/providers/sks.svg +1 -0
  2. package/assets/styles/base/_basic.scss +6 -0
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +1 -0
  5. package/assets/styles/global/_button.scss +1 -0
  6. package/assets/translations/en-us.yaml +65 -15
  7. package/assets/translations/zh-hans.yaml +4 -3
  8. package/chart/monitoring/index.vue +3 -1
  9. package/cloud-credential/aws.vue +2 -0
  10. package/components/ActionDropdownShell.vue +71 -0
  11. package/components/AppModal.vue +18 -4
  12. package/components/AsyncButton.vue +24 -7
  13. package/components/BannerGraphic.vue +1 -0
  14. package/components/CommunityLinks.vue +4 -59
  15. package/components/CopyToClipboardText.vue +2 -1
  16. package/components/CruResource.vue +6 -1
  17. package/components/DetailText.vue +5 -0
  18. package/components/ExplorerMembers.vue +1 -1
  19. package/components/ExplorerProjectsNamespaces.vue +68 -18
  20. package/components/GlobalRoleBindings.vue +5 -1
  21. package/components/GrowlManager.vue +1 -0
  22. package/components/LandingPagePreference.vue +7 -3
  23. package/components/LocaleSelector.vue +39 -95
  24. package/components/ModalManager.vue +55 -0
  25. package/components/ModalWithCard.vue +1 -0
  26. package/components/PromptModal.vue +47 -8
  27. package/components/PromptRemove.vue +1 -0
  28. package/components/PromptRestore.vue +1 -0
  29. package/components/ResourceCancelModal.vue +1 -0
  30. package/components/ResourceDetail/Masthead.vue +38 -12
  31. package/components/ResourceDetail/__tests__/Masthead.test.ts +5 -1
  32. package/components/ResourceDetail/index.vue +47 -12
  33. package/components/ResourceTable.vue +54 -19
  34. package/components/SideNav.vue +5 -1
  35. package/components/SlideInPanelManager.vue +126 -0
  36. package/components/SortableTable/THead.vue +5 -2
  37. package/components/SortableTable/actions.js +1 -1
  38. package/components/SortableTable/index.vue +64 -51
  39. package/components/SortableTable/paging.js +16 -19
  40. package/components/SortableTable/selection.js +0 -11
  41. package/components/Wizard.vue +2 -2
  42. package/components/__tests__/AsyncButton.test.ts +2 -2
  43. package/components/__tests__/ModalManager.spec.ts +176 -0
  44. package/components/__tests__/PromptModal.test.ts +148 -0
  45. package/components/__tests__/SlideInPanelManager.spec.ts +166 -0
  46. package/components/auth/AuthBanner.vue +13 -11
  47. package/components/auth/Principal.vue +1 -0
  48. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  49. package/components/auth/login/ldap.vue +1 -1
  50. package/components/fleet/FleetResources.vue +21 -6
  51. package/components/form/ArrayList.vue +76 -60
  52. package/components/form/BannerSettings.vue +17 -2
  53. package/components/form/ColorInput.vue +35 -6
  54. package/components/form/Command.vue +6 -15
  55. package/components/form/EnvVars.vue +16 -8
  56. package/components/form/HealthCheck.vue +3 -3
  57. package/components/form/HookOption.vue +11 -16
  58. package/components/form/LabeledSelect.vue +18 -22
  59. package/components/form/LifecycleHooks.vue +3 -3
  60. package/components/form/MatchExpressions.vue +14 -8
  61. package/components/form/NameNsDescription.vue +128 -104
  62. package/components/form/Networking.vue +20 -12
  63. package/components/form/NodeAffinity.vue +31 -23
  64. package/components/form/NodeScheduling.vue +13 -3
  65. package/components/form/NotificationSettings.vue +15 -1
  66. package/components/form/Password.vue +1 -0
  67. package/components/form/PodAffinity.vue +43 -43
  68. package/components/form/Probe.vue +68 -66
  69. package/components/form/ResourceQuota/Project.vue +5 -1
  70. package/components/form/ResourceSelector.vue +7 -9
  71. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +16 -24
  72. package/components/form/SSHKnownHosts/index.vue +30 -13
  73. package/components/form/Security.vue +54 -56
  74. package/components/form/Select.vue +32 -21
  75. package/components/form/ShellInput.vue +5 -1
  76. package/components/form/Tolerations.vue +5 -1
  77. package/components/form/ValueFromResource.vue +134 -121
  78. package/components/form/WorkloadPorts.vue +18 -18
  79. package/components/form/__tests__/ArrayList.test.ts +5 -2
  80. package/components/form/__tests__/ColorInput.test.ts +35 -0
  81. package/components/form/__tests__/LabeledSelect.test.ts +40 -0
  82. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  83. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  84. package/components/form/__tests__/Probe.test.ts +12 -8
  85. package/components/form/__tests__/SSHKnownHosts.test.ts +22 -2
  86. package/components/form/__tests__/Select.test.ts +37 -0
  87. package/components/formatter/InternalExternalIP.vue +2 -0
  88. package/components/formatter/SecretData.vue +20 -7
  89. package/components/nav/Group.vue +27 -5
  90. package/components/nav/Header.vue +17 -43
  91. package/components/nav/NamespaceFilter.vue +134 -86
  92. package/components/nav/TopLevelMenu.vue +4 -5
  93. package/components/nav/Type.vue +12 -1
  94. package/components/nav/WindowManager/ContainerLogs.vue +87 -61
  95. package/components/nav/WindowManager/ContainerLogsActions.vue +76 -0
  96. package/components/templates/blank.vue +4 -1
  97. package/components/templates/default.vue +8 -3
  98. package/components/templates/home.vue +10 -1
  99. package/components/templates/plain.vue +10 -4
  100. package/composables/focusTrap.ts +12 -4
  101. package/composables/useRuntimeFlag.ts +29 -0
  102. package/config/router/routes.js +20 -13
  103. package/config/store.js +4 -0
  104. package/config/uiplugins.js +5 -1
  105. package/core/types.ts +12 -6
  106. package/detail/catalog.cattle.io.app.vue +6 -1
  107. package/detail/fleet.cattle.io.bundle.vue +70 -6
  108. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  109. package/detail/namespace.vue +0 -3
  110. package/detail/node.vue +17 -13
  111. package/detail/provisioning.cattle.io.cluster.vue +72 -6
  112. package/dialog/AddCustomBadgeDialog.vue +1 -1
  113. package/{pages/c/_cluster/uiplugins/AddExtensionRepos.vue → dialog/AddExtensionReposDialog.vue} +72 -42
  114. package/{components/AssignTo.vue → dialog/AssignToDialog.vue} +71 -80
  115. package/dialog/ChangePasswordDialog.vue +106 -0
  116. package/dialog/DeactivateDriverDialog.vue +1 -0
  117. package/{pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue → dialog/DeveloperLoadExtensionDialog.vue} +74 -71
  118. package/dialog/DisableAuthProviderDialog.vue +101 -0
  119. package/dialog/DrainNode.vue +1 -1
  120. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue → dialog/ExtensionCatalogInstallDialog.vue} +100 -88
  121. package/{pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue → dialog/ExtensionCatalogUninstallDialog.vue} +69 -57
  122. package/dialog/FeatureFlagListDialog.vue +288 -0
  123. package/dialog/ForceMachineRemoveDialog.vue +5 -2
  124. package/{components/Import.vue → dialog/ImportDialog.vue} +0 -5
  125. package/{pages/c/_cluster/uiplugins/InstallDialog.vue → dialog/InstallExtensionDialog.vue} +124 -106
  126. package/{components/form/SSHKnownHosts → dialog}/KnownHostsEditDialog.vue +52 -59
  127. package/dialog/MoveNamespaceDialog.vue +157 -0
  128. package/dialog/ScalePoolDownDialog.vue +1 -1
  129. package/{components/nav/Jump.vue → dialog/SearchDialog.vue} +34 -14
  130. package/{pages/c/_cluster/uiplugins/UninstallDialog.vue → dialog/UninstallExtensionDialog.vue} +67 -58
  131. package/dialog/WechatDialog.vue +57 -0
  132. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
  133. package/edit/auth/__tests__/oidc.test.ts +152 -109
  134. package/edit/auth/azuread.vue +2 -1
  135. package/edit/auth/github.vue +1 -1
  136. package/edit/auth/googleoauth.vue +5 -1
  137. package/edit/auth/ldap/index.vue +1 -1
  138. package/edit/auth/oidc.vue +38 -5
  139. package/edit/auth/saml.vue +1 -1
  140. package/edit/cloudcredential.vue +24 -9
  141. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
  142. package/edit/management.cattle.io.user.vue +28 -3
  143. package/edit/namespace.vue +1 -4
  144. package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
  145. package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
  146. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
  147. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +4 -1
  148. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +26 -9
  149. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +8 -8
  150. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +26 -12
  151. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +66 -0
  152. package/edit/provisioning.cattle.io.cluster/__tests__/utils/rke2-test-data.ts +58 -0
  153. package/edit/provisioning.cattle.io.cluster/rke2.vue +49 -41
  154. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
  155. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +5 -3
  156. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +33 -2
  157. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
  158. package/edit/token.vue +2 -0
  159. package/edit/workload/index.vue +1 -0
  160. package/edit/workload/mixins/workload.js +0 -2
  161. package/initialize/install-plugins.js +2 -1
  162. package/list/harvesterhci.io.management.cluster.vue +4 -1
  163. package/list/management.cattle.io.feature.vue +4 -287
  164. package/list/provisioning.cattle.io.cluster.vue +20 -12
  165. package/machine-config/azure.vue +16 -4
  166. package/mixins/vue-select-overrides.js +0 -4
  167. package/models/__tests__/namespace.test.ts +25 -1
  168. package/models/cloudcredential.js +5 -0
  169. package/models/fleet.cattle.io.cluster.js +8 -2
  170. package/models/fleet.cattle.io.gitrepo.js +8 -34
  171. package/models/kontainerdriver.js +6 -3
  172. package/models/management.cattle.io.feature.js +7 -1
  173. package/models/management.cattle.io.node.js +3 -3
  174. package/models/namespace.js +11 -6
  175. package/models/nodedriver.js +6 -3
  176. package/models/workload.js +4 -1
  177. package/package.json +3 -3
  178. package/pages/about.vue +13 -3
  179. package/pages/account/index.vue +16 -6
  180. package/pages/auth/login.vue +18 -7
  181. package/pages/auth/logout.vue +4 -1
  182. package/pages/auth/setup.vue +2 -0
  183. package/pages/auth/verify.vue +13 -8
  184. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  185. package/pages/c/_cluster/apps/charts/install.vue +26 -26
  186. package/pages/c/_cluster/auth/config/index.vue +10 -12
  187. package/pages/c/_cluster/explorer/EventsTable.vue +38 -33
  188. package/pages/c/_cluster/explorer/index.vue +17 -15
  189. package/pages/c/_cluster/istio/index.vue +2 -2
  190. package/pages/c/_cluster/longhorn/index.vue +1 -1
  191. package/pages/c/_cluster/monitoring/index.vue +1 -1
  192. package/pages/c/_cluster/monitoring/monitor/_namespace/_id.vue +4 -2
  193. package/pages/c/_cluster/monitoring/monitor/create.vue +4 -2
  194. package/pages/c/_cluster/monitoring/route-receiver/_id.vue +4 -2
  195. package/pages/c/_cluster/monitoring/route-receiver/create.vue +5 -2
  196. package/pages/c/_cluster/neuvector/index.vue +1 -1
  197. package/pages/c/_cluster/settings/banners.vue +4 -3
  198. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +8 -10
  199. package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +4 -7
  200. package/pages/c/_cluster/uiplugins/index.vue +98 -55
  201. package/pages/diagnostic.vue +59 -11
  202. package/pages/fail-whale.vue +14 -8
  203. package/pages/home.vue +24 -18
  204. package/pages/prefs.vue +7 -6
  205. package/pages/support/index.vue +4 -1
  206. package/plugins/internal-api/index.ts +37 -0
  207. package/plugins/internal-api/shared/base-api.ts +13 -0
  208. package/plugins/internal-api/shell/shell.api.ts +108 -0
  209. package/plugins/steve/actions.js +0 -12
  210. package/public/index.html +1 -0
  211. package/rancher-components/Card/Card.vue +1 -1
  212. package/rancher-components/Form/Checkbox/Checkbox.test.ts +59 -1
  213. package/rancher-components/Form/Checkbox/Checkbox.vue +27 -3
  214. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +47 -0
  215. package/rancher-components/Form/LabeledInput/LabeledInput.vue +20 -2
  216. package/rancher-components/Form/Radio/RadioButton.test.ts +36 -1
  217. package/rancher-components/Form/Radio/RadioButton.vue +20 -4
  218. package/rancher-components/Form/Radio/RadioGroup.test.ts +60 -0
  219. package/rancher-components/Form/Radio/RadioGroup.vue +52 -10
  220. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +17 -0
  221. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +5 -0
  222. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +10 -1
  223. package/rancher-components/RcButton/RcButton.vue +2 -1
  224. package/rancher-components/RcButton/types.ts +1 -0
  225. package/rancher-components/RcDropdown/RcDropdown.vue +18 -6
  226. package/rancher-components/RcDropdown/RcDropdownItem.vue +3 -56
  227. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +68 -0
  228. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +92 -0
  229. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
  230. package/rancher-components/RcDropdown/index.ts +2 -0
  231. package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
  232. package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
  233. package/rancher-components/RcDropdown/useDropdownItem.ts +63 -0
  234. package/scripts/extension/bundle +20 -0
  235. package/scripts/extension/helm/charts/ui-plugin-server/templates/cr.yaml +2 -1
  236. package/scripts/extension/helm/charts/ui-plugin-server/values.yaml +2 -0
  237. package/scripts/extension/helmpatch +44 -31
  238. package/scripts/extension/publish +12 -12
  239. package/scripts/typegen.sh +2 -4
  240. package/server/har-file.js +25 -3
  241. package/store/action-menu.js +26 -56
  242. package/store/features.js +2 -1
  243. package/store/index.js +5 -0
  244. package/store/modal.ts +71 -0
  245. package/store/slideInPanel.ts +47 -0
  246. package/store/type-map.js +12 -1
  247. package/store/type-map.utils.ts +4 -4
  248. package/types/global-vue.d.ts +5 -0
  249. package/types/internal-api/shell/growl.d.ts +25 -0
  250. package/types/internal-api/shell/modal.d.ts +77 -0
  251. package/types/internal-api/shell/slideIn.d.ts +15 -0
  252. package/types/resources/fleet.d.ts +0 -14
  253. package/types/shell/index.d.ts +43 -24
  254. package/types/vue-shim.d.ts +4 -1
  255. package/utils/__mocks__/tabbable.js +13 -0
  256. package/utils/__tests__/object.test.ts +38 -4
  257. package/utils/cluster.js +35 -0
  258. package/utils/fleet.ts +15 -73
  259. package/utils/object.js +48 -5
  260. package/utils/validators/formRules/__tests__/index.test.ts +10 -1
  261. package/utils/validators/formRules/index.ts +27 -3
  262. package/utils/validators/machine-pool.ts +20 -0
  263. package/components/DisableAuthProviderModal.vue +0 -114
  264. package/components/MoveModal.vue +0 -166
  265. package/components/PromptChangePassword.vue +0 -123
  266. package/components/fleet/FleetBundleResources.vue +0 -86
  267. package/components/formatter/ExtensionCache.vue +0 -74
  268. package/components/formatter/Port.vue +0 -24
  269. package/components/formatter/SecretType.vue +0 -41
  270. package/types/vue-shim.d +0 -20
package/store/type-map.js CHANGED
@@ -107,6 +107,7 @@
107
107
  // graphConfig: undefined -- Use this to pass along the graph configuration
108
108
  // notFilterNamespace: undefined -- Define namespaces that do not need to be filtered
109
109
  // localOnly: False -- Hide this type from the nav/search bar on downstream clusters
110
+ // custom: any - Custom options for a given type
110
111
  // }
111
112
  // )
112
113
  // ignoreGroup(group): Never show group or any types in it
@@ -524,6 +525,7 @@ export const getters = {
524
525
  depaginate: false,
525
526
  customRoute: undefined,
526
527
  resourceEditMasthead: true,
528
+ custom: {},
527
529
  };
528
530
 
529
531
  return (schemaOrType, pagination) => {
@@ -657,7 +659,14 @@ export const getters = {
657
659
 
658
660
  const label = typeObj.labelKey ? rootGetters['i18n/t'](typeObj.labelKey) || typeObj.label : typeObj.label;
659
661
 
660
- const labelDisplay = highlightLabel(label, count, typeObj.schema);
662
+ let labelDisplay = highlightLabel(label, count, typeObj.schema);
663
+
664
+ // If we did not match on just the label, add the schema name and see if that matches
665
+ if (!labelDisplay && typeObj.schema?.attributes) {
666
+ const schemaName = `${ typeObj.schema.attributes.resource }.${ typeObj.schema.attributes.group }`;
667
+
668
+ labelDisplay = highlightLabel(`${ label } (${ schemaName })`, count, typeObj.schema);
669
+ }
661
670
 
662
671
  if ( !labelDisplay ) {
663
672
  // Search happens in highlight and returns null if not found
@@ -1729,6 +1738,8 @@ export const mutations = {
1729
1738
  let obj = { ...options, match };
1730
1739
 
1731
1740
  if ( idx >= 0 ) {
1741
+ // Merge the custom data object - multiple configures will update existing rather than overwrite
1742
+ obj.custom = Object.assign(state.typeOptions[idx].custom || {}, obj.custom || {});
1732
1743
  obj = Object.assign(state.typeOptions[idx], obj);
1733
1744
  state.typeOptions.splice(idx, 1, obj);
1734
1745
  } else {
@@ -62,12 +62,12 @@ export function createHeaders(
62
62
  } = columns;
63
63
  const { rootGetters } = ctx;
64
64
  const out = typeOptions.showState ? [stateColumn] : [];
65
- const attributes = (schema.attributes as SchemaAttribute) || {};
66
- const columnsFromSchema = attributes.columns || [];
65
+ const attributes = (schema?.attributes as SchemaAttribute) || {};
66
+ const columnsFromSchema = attributes?.columns || [];
67
67
 
68
68
  // A specific list has been provided
69
- if ( headers?.[schema.id]?.length ) {
70
- return headers[schema.id].map((entry: any) => {
69
+ if ( headers?.[schema?.id]?.length ) {
70
+ return headers[schema?.id].map((entry: any) => {
71
71
  if ( typeof entry === 'string' ) {
72
72
  const col = findBy(columnsFromSchema, 'name', entry);
73
73
 
@@ -0,0 +1,5 @@
1
+ declare module '*.vue' {
2
+ import type { DefineComponent } from 'vue';
3
+ const component: DefineComponent<{}, {}, any>;
4
+ export default component;
5
+ }
@@ -0,0 +1,25 @@
1
+ export interface DetailedMessage {
2
+ title?: string;
3
+ description: string;
4
+ }
5
+
6
+ export interface GrowlConfig {
7
+ /**
8
+ * The content of the notification message.
9
+ * Either a simple string or an object with `title` and `description` for detailed notifications.
10
+ */
11
+ message: string | DetailedMessage;
12
+
13
+ /**
14
+ * Optional type of the growl notification.
15
+ * Determines the visual style of the notification.
16
+ * Defaults to `'error'` if not provided.
17
+ */
18
+ type?: 'success' | 'info' | 'warning' | 'error';
19
+
20
+ /**
21
+ * Optional duration (in milliseconds) for which the notification should be displayed.
22
+ * Defaults to `5000` milliseconds. A value of `0` keeps the notification indefinitely.
23
+ */
24
+ timeout?: number;
25
+ }
@@ -0,0 +1,77 @@
1
+ import { Component } from 'vue';
2
+
3
+ /**
4
+ * Configuration object for opening a modal.
5
+ */
6
+ export interface ModalConfig {
7
+ /**
8
+ * The Vue component to be displayed inside the modal.
9
+ * This can be any SFC (Single-File Component) imported and passed in as a `Component`.
10
+ *
11
+ * Example:
12
+ * ```ts
13
+ * import MyCustomModal from '@/components/MyCustomModal.vue';
14
+ *
15
+ * this.$shell.modal({
16
+ * component: MyCustomModal,
17
+ * componentProps: { title: 'Hello Modal' }
18
+ * });
19
+ * ```
20
+ */
21
+ component: Component;
22
+
23
+ /**
24
+ * Optional props to pass directly to the component rendered inside the modal.
25
+ *
26
+ * Example:
27
+ * ```ts
28
+ * componentProps: { title: 'Hello Modal', isVisible: true }
29
+ * ```
30
+ */
31
+ componentProps?: Record<string, any>;
32
+
33
+ /**
34
+ * Optional array of resources that the modal component might need.
35
+ * These are passed directly into the modal's `resources` prop.
36
+ *
37
+ * Example:
38
+ * ```ts
39
+ * resources: [myResource, anotherResource]
40
+ * ```
41
+ */
42
+ resources?: any[];
43
+
44
+ /**
45
+ * Custom width for the modal. Defaults to `600px`.
46
+ * The width can be specified as a string with a valid unit (`px`, `%`, `rem`, etc.).
47
+ *
48
+ * Examples:
49
+ * ```ts
50
+ * modalWidth: '800px' // Width in pixels
51
+ * modalWidth: '75%' // Width as a percentage
52
+ * ```
53
+ */
54
+ modalWidth?: string;
55
+
56
+ /**
57
+ * Determines if clicking outside the modal will close it. Defaults to `true`.
58
+ * Set this to `false` to prevent closing via outside clicks.
59
+ *
60
+ * Example:
61
+ * ```ts
62
+ * closeOnClickOutside: false
63
+ * ```
64
+ */
65
+ closeOnClickOutside?: boolean;
66
+
67
+ /**
68
+ * If true, the modal is considered "sticky" and may not close automatically
69
+ * on certain user interactions. Defaults to `false`.
70
+ *
71
+ * Example:
72
+ * ```ts
73
+ * modalSticky: true
74
+ * ```
75
+ */
76
+ // modalSticky?: boolean; // Not implemented yet
77
+ }
@@ -0,0 +1,15 @@
1
+ import type { Component } from 'vue';
2
+
3
+ /**
4
+ * Configuration object for opening a slide-in panel.
5
+ *
6
+ * @property component - The Vue component to render in the slide-in panel.
7
+ * This should be a valid Vue Component, such as an imported SFC or functional component.
8
+ *
9
+ * @property componentProps - (Optional) An object containing props to be passed to the component rendered in the slide-in panel.
10
+ * Keys should match the props defined in the provided component.
11
+ */
12
+ export interface SlideInConfig {
13
+ component: Component | null;
14
+ componentProps?: Record<string, any>;
15
+ }
@@ -19,11 +19,6 @@ export interface BundleNonReadyResource extends BundleResourceKey {
19
19
  summary: { [state: string]: string }
20
20
  }
21
21
 
22
- export interface BundleNonReadyBundle {
23
- modifiedStatus: BundleModifiedResource[],
24
- nonReadyStatus: BundleNonReadyResource[],
25
- }
26
-
27
22
  export interface Condition {
28
23
  status: string,
29
24
  type: string,
@@ -39,15 +34,6 @@ export interface BundleDeploymentStatus {
39
34
  conditions: Condition[],
40
35
  }
41
36
 
42
- export interface BundleStatusSummary {
43
- nonReadyResources?: BundleNonReadyBundle[],
44
- }
45
-
46
- export interface BundleStatus {
47
- resourceKey?: BundleResourceKey[],
48
- summary?: BundleStatusSummary,
49
- }
50
-
51
37
  export interface BundleDeployment {
52
38
  spec: {
53
39
  deploymentId: string,
@@ -1,25 +1,7 @@
1
1
  // Auto-generated type definitions for shell
2
2
  // Do not modify this file as changes will get overwritten
3
- declare module '*.vue' {
4
- import Vue from 'vue';
5
- export default Vue;
6
- }
7
-
8
- // This is required to keep typescript from complaining. It is required for
9
- // our i18n plugin. For more info see:
10
- // https://v2.vuejs.org/v2/guide/typescript.html?redirect=true#Augmenting-Types-for-Use-with-Plugins
11
- declare module 'vue/types/vue' {
12
- // eslint-disable-next-line no-unused-vars
13
- interface Vue {
14
- /**
15
- * Lookup a given string with the given arguments
16
- * @param raw if set, do not do HTML escaping.
17
- */
18
- t: (key: string, args?: Record<string, any>, raw?: boolean) => string,
19
- }
20
- }
21
-
22
- declare module 'js-yaml';
3
+ /// <reference types="@rancher/shell/types/vue-shim" />
4
+ /// <reference types="@rancher/shell/types/global-vue" />
23
5
 
24
6
  // @shell/config/labels-annotations
25
7
 
@@ -2475,7 +2457,7 @@ export default class Namespace {
2475
2457
  get isObscure(): any;
2476
2458
  get projectId(): any;
2477
2459
  get project(): any;
2478
- get groupByLabel(): any;
2460
+ get groupById(): any;
2479
2461
  get projectNameSort(): any;
2480
2462
  get istioInstalled(): boolean;
2481
2463
  get injectionEnabled(): boolean;
@@ -3498,6 +3480,13 @@ export function abbreviateClusterName(input: string): string;
3498
3480
  export function labelForAddon(store: any, name: any, configuration?: boolean): any;
3499
3481
  export function filterOutDeprecatedPatchVersions(allVersions: any, currentVersion: any): any;
3500
3482
  export function getAllOptionsAfterCurrentVersion(store: any, versions: any, currentVersion: any, defaultVersion: any): any;
3483
+ export function initSchedulingCustomization(value: any, features: any, store: any, mode: any): Promise<{
3484
+ clusterAgentDefaultPC: any;
3485
+ clusterAgentDefaultPDB: any;
3486
+ schedulingCustomizationFeatureEnabled: any;
3487
+ schedulingCustomizationOriginallyEnabled: boolean;
3488
+ errors: any[];
3489
+ }>;
3501
3490
  }
3502
3491
 
3503
3492
  // @shell/utils/color
@@ -3980,7 +3969,8 @@ export function dropKeys(obj: any, keys: any): void;
3980
3969
  */
3981
3970
  export function deepToRaw(obj: any, cache?: any): any;
3982
3971
  /**
3983
- * Helper function to alter Lodash merge function default behaviour on merging arrays while updating machine pool configuration.
3972
+ * Helper function to alter Lodash merge function default behaviour on merging
3973
+ * arrays and objects.
3984
3974
  *
3985
3975
  * In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
3986
3976
  * merging the latest configuration received from the backend with the current configuration updated by the user.
@@ -3992,10 +3982,39 @@ export function deepToRaw(obj: any, cache?: any): any;
3992
3982
  * merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
3993
3983
  *
3994
3984
  * More info: https://github.com/lodash/lodash/issues/1313
3995
- *
3985
+
3996
3986
  * This helper function addresses the issue by always replacing the old array with the new array during the merge process.
3987
+ *
3988
+ * This helper is also used for another case in rke2.vue to handle merging addon chart default values with the user's current values.
3989
+ * It fixed https://github.com/rancher/dashboard/issues/12418
3990
+ *
3991
+ * If `mutateOriginal` is true, the merge is done directly into `obj1` (mutating it).
3992
+ * This is useful in cases like:
3993
+ * machinePool.config = mergeWithReplace(clonedLatestConfig, clonedCurrentConfig, { mutateOriginal: true })
3994
+ * where merging into a new empty object may lead to incomplete structure.
3995
+ *
3996
+ * Use `mutateOriginal` when you want to preserve obj1’s original shape, references,
3997
+ * or when assigning the result directly to an existing object.
3998
+ *
3999
+ * @param {Object} [obj1={}] - The first object to merge
4000
+ * @param {Object} [obj2={}] - The second object to merge
4001
+ * @param {Object} [options={}] - Options for customizing merge behavior
4002
+ * @param {boolean} [options.mutateOriginal=false] - true: mutates obj1
4003
+ * directly. false: returns a new object
4004
+ * @param {boolean} [options.replaceArray=true] - true: replaces arrays in obj1
4005
+ * with arrays in obj2 when both properties are arrays
4006
+ * false: default lodash merge behavior - recursively merges
4007
+ * array members
4008
+ * @param {boolean} [options.replaceObjectProps=false] - true: merges objects in
4009
+ * obj1 with objects in obj2, overwriting duplicate props
4010
+ * false: default lodash merge behavior - recursively merges
4011
+ * object props
3997
4012
  */
3998
- export function mergeWithReplaceArrays(obj1?: {}, obj2?: {}): any;
4013
+ export function mergeWithReplace(obj1?: any, obj2?: any, { mutateOriginal, replaceArray, replaceObjectProps, }?: {
4014
+ mutateOriginal?: boolean;
4015
+ replaceArray?: boolean;
4016
+ replaceObjectProps?: boolean;
4017
+ }): any;
3999
4018
  export { isEqualBasic as isEqual };
4000
4019
  export function toDictionary(array: any, callback: any): any;
4001
4020
  /**
@@ -1,4 +1,6 @@
1
1
  /* eslint-disable */
2
+ import type ShellApi from '@shell/plugins/internal-api/shell/shell.api';
3
+
2
4
  export {};
3
5
 
4
6
  declare module 'vue' {
@@ -15,6 +17,7 @@ declare module 'vue' {
15
17
  getters: Record<string, any>,
16
18
  dispatch: (action: string, payload?: any) => Promise<any>,
17
19
  commit: (mutation: string, payload?: any) => void,
18
- }
20
+ },
21
+ $shell: ShellApi,
19
22
  }
20
23
  }
@@ -0,0 +1,13 @@
1
+ // __mocks__/tabbable.js
2
+
3
+ const lib = jest.requireActual('tabbable');
4
+
5
+ const tabbable = {
6
+ ...lib,
7
+ tabbable: (node, options) => lib.tabbable(node, { ...options, displayCheck: 'none' }),
8
+ focusable: (node, options) => lib.focusable(node, { ...options, displayCheck: 'none' }),
9
+ isFocusable: (node, options) => lib.isFocusable(node, { ...options, displayCheck: 'none' }),
10
+ isTabbable: (node, options) => lib.isTabbable(node, { ...options, displayCheck: 'none' }),
11
+ };
12
+
13
+ module.exports = tabbable;
@@ -1,6 +1,6 @@
1
1
  import { reactive, isReactive } from 'vue';
2
2
  import {
3
- clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw, mergeWithReplaceArrays
3
+ clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw, mergeWithReplace
4
4
  } from '@shell/utils/object';
5
5
 
6
6
  describe('fx: get', () => {
@@ -379,7 +379,7 @@ describe('fx: deepToRaw', () => {
379
379
  });
380
380
  });
381
381
 
382
- describe('fx: mergeWithReplaceArrays', () => {
382
+ describe('fx: mergeWithReplace', () => {
383
383
  const testCases: Array<[object?, object?, object?]> = [
384
384
  // Some array test cases, an array from the first object should be replaced with the array from the second object
385
385
  [{ a: ['one'] }, { a: [] }, { a: [] }],
@@ -401,8 +401,42 @@ describe('fx: mergeWithReplaceArrays', () => {
401
401
  [undefined, {}, {}],
402
402
  ];
403
403
 
404
- it.each(testCases)('should merge properly', (obj1, obj2, expected) => {
405
- const result = mergeWithReplaceArrays(obj1, obj2);
404
+ it.each(testCases)('should merge arrays properly', (obj1, obj2, expected) => {
405
+ const result = mergeWithReplace(obj1, obj2);
406
+
407
+ expect(result).toStrictEqual(expected);
408
+ });
409
+
410
+ it.each([
411
+ [
412
+ { a: { b: false, c: false } }, { a: { b: true, c: null } }, { a: { b: true, c: null } }
413
+ ],
414
+ [
415
+ {
416
+ a: [{
417
+ b: 'test', c: 'test', value: true
418
+ }]
419
+ }, {
420
+ a: [{
421
+ b: 'test', c: 'test', operator: 'exists'
422
+ }]
423
+ }, {
424
+ a: [{
425
+ b: 'test', c: 'test', operator: 'exists'
426
+ }]
427
+ }
428
+ ],
429
+ [
430
+ {
431
+ a: { enabled: false }, b: { enabled: false }, c: { enabled: false }
432
+ },
433
+ { c: { enabled: true, stripUnderscores: true } },
434
+ {
435
+ a: { enabled: false }, b: { enabled: false }, c: { enabled: true, stripUnderscores: true }
436
+ }
437
+ ]
438
+ ])('should overwrite duplicate object properties when merging objects', (left, right, expected) => {
439
+ const result = mergeWithReplace(left, right, { replaceObjectProps: true });
406
440
 
407
441
  expect(result).toStrictEqual(expected);
408
442
  });
package/utils/cluster.js CHANGED
@@ -6,6 +6,10 @@ import { SETTING } from '@shell/config/settings';
6
6
  import { PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
7
7
  import { compare, sortable } from '@shell/utils/version';
8
8
  import { sortBy } from '@shell/utils/sort';
9
+ import { SCHEDULING_CUSTOMIZATION } from '@shell/store/features';
10
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
11
+ import isEmpty from 'lodash/isEmpty';
12
+ import { set } from '@shell/utils/object';
9
13
 
10
14
  /**
11
15
  * Combination of paginationFilterHiddenLocalCluster and paginationFilterOnlyKubernetesClusters
@@ -296,3 +300,34 @@ export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion
296
300
 
297
301
  return sortedWithDeprecatedLabel;
298
302
  }
303
+
304
+ export async function initSchedulingCustomization(value, features, store, mode) {
305
+ const schedulingCustomizationFeatureEnabled = features(SCHEDULING_CUSTOMIZATION);
306
+ let clusterAgentDefaultPC = null;
307
+ let clusterAgentDefaultPDB = null;
308
+ let schedulingCustomizationOriginallyEnabled = false;
309
+ const errors = [];
310
+
311
+ try {
312
+ clusterAgentDefaultPC = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS })).value) || null;
313
+ } catch (e) {
314
+ errors.push(e);
315
+ }
316
+ try {
317
+ clusterAgentDefaultPDB = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET })).value) || null;
318
+ } catch (e) {
319
+ errors.push(e);
320
+ }
321
+
322
+ if (schedulingCustomizationFeatureEnabled && mode === _CREATE && isEmpty(value?.clusterAgentDeploymentCustomization?.schedulingCustomization)) {
323
+ set(value, 'clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: clusterAgentDefaultPC, podDisruptionBudget: clusterAgentDefaultPDB });
324
+ }
325
+
326
+ if (mode === _EDIT && !!value?.clusterAgentDeploymentCustomization?.schedulingCustomization) {
327
+ schedulingCustomizationOriginallyEnabled = true;
328
+ }
329
+
330
+ return {
331
+ clusterAgentDefaultPC, clusterAgentDefaultPDB, schedulingCustomizationFeatureEnabled, schedulingCustomizationOriginallyEnabled, errors
332
+ };
333
+ }
package/utils/fleet.ts CHANGED
@@ -3,11 +3,11 @@ import {
3
3
  BundleResourceKey,
4
4
  BundleDeployment,
5
5
  BundleDeploymentStatus,
6
- BundleStatus,
7
6
  Condition,
8
7
  } from '@shell/types/resources/fleet';
9
- import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
8
+ import { mapStateToEnum, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
10
9
  import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
10
+ import { NAME as EXPLORER_NAME } from '@shell/config/product/explorer';
11
11
 
12
12
  interface Resource extends BundleDeploymentResource {
13
13
  state: string,
@@ -17,15 +17,6 @@ type Labels = {
17
17
  [key: string]: string,
18
18
  }
19
19
 
20
- interface StatesCounter { [state: string]: number }
21
-
22
- function incr(counter: StatesCounter, state: string) {
23
- if (!counter[state]) {
24
- counter[state] = 0;
25
- }
26
- counter[state]++;
27
- }
28
-
29
20
  function resourceKey(r: BundleResourceKey): string {
30
21
  return `${ r.kind }/${ r.namespace }/${ r.name }`;
31
22
  }
@@ -57,6 +48,19 @@ class Fleet {
57
48
  return `${ r.apiVersion.split('/', 2)[0] }.${ type }`;
58
49
  }
59
50
 
51
+ detailLocation(r: Resource, mgmtClusterName: string): any {
52
+ return mapStateToEnum(r.state) === STATES_ENUM.MISSING ? undefined : {
53
+ name: `c-cluster-product-resource${ r.namespace ? '-namespace' : '' }-id`,
54
+ params: {
55
+ product: EXPLORER_NAME,
56
+ cluster: mgmtClusterName,
57
+ resource: this.resourceType(r),
58
+ namespace: r.namespace,
59
+ id: r.name,
60
+ },
61
+ };
62
+ }
63
+
60
64
  /**
61
65
  * resourcesFromBundleDeploymentStatus extracts the list of resources deployed by a BundleDeployment
62
66
  */
@@ -94,68 +98,6 @@ class Fleet {
94
98
  return modified.concat(Object.values(resources));
95
99
  }
96
100
 
97
- /**
98
- * resourcesFromBundleStatus extracts the list of resources deployed by a Bundle
99
- */
100
- resourcesFromBundleStatus(status: BundleStatus): Resource[] {
101
- // The state of every resource is spread all over the bundle status.
102
- // resourceKey contains one entry per resource AND cluster (built by Fleet from all the child BundleDeployments).
103
- // However, those entries do not contain the cluster that they belong to, leading to duplicate entries
104
-
105
- // 1. Fold resourceKey by using a unique key, initializing counters for multiple occurrences of the same resource
106
- const resources = (status.resourceKey || []).reduce((res, r) => {
107
- const k = resourceKey(r);
108
-
109
- if (!res[k]) {
110
- res[k] = { r, count: {} };
111
- }
112
- incr(res[k].count, STATES_ENUM.READY);
113
-
114
- return res;
115
- }, {} as { [resourceKey: string]: { r: BundleResourceKey, count: StatesCounter } });
116
-
117
- // 2. Non-ready resources are counted differently and may also appear in resourceKey, depending on their state
118
- for (const bundle of status.summary?.nonReadyResources || []) {
119
- for (const r of bundle.modifiedStatus || []) {
120
- const k = resourceKey(r);
121
-
122
- if (!resources[k]) {
123
- resources[k] = { r, count: {} };
124
- }
125
-
126
- if (r.missing) {
127
- incr(resources[k].count, STATES_ENUM.MISSING);
128
- } else if (r.delete) {
129
- resources[k].count[STATES_ENUM.READY]--;
130
- incr(resources[k].count, STATES_ENUM.ORPHANED);
131
- } else {
132
- resources[k].count[STATES_ENUM.READY]--;
133
- incr(resources[k].count, STATES_ENUM.MODIFIED);
134
- }
135
- }
136
- for (const r of bundle.nonReadyStatus || []) {
137
- const k = resourceKey(r);
138
- const state = r.summary?.state || STATES_ENUM.UNKNOWN;
139
-
140
- resources[k].count[STATES_ENUM.READY]--;
141
- incr(resources[k].count, state);
142
- }
143
- }
144
-
145
- // 3. Unfold back to an array of resources for display
146
- return Object.values(resources).reduce((res, e) => {
147
- const { r, count } = e;
148
-
149
- for (const state in count) {
150
- for (let x = 0; x < count[state]; x++) {
151
- res.push(Object.assign({ state }, r));
152
- }
153
- }
154
-
155
- return res;
156
- }, [] as Resource[]);
157
- }
158
-
159
101
  clusterIdFromBundleDeploymentLabels(labels?: Labels): string {
160
102
  const clusterNamespace = labels?.[FLEET_LABELS.CLUSTER_NAMESPACE];
161
103
  const clusterName = labels?.[FLEET_LABELS.CLUSTER];
package/utils/object.js CHANGED
@@ -474,7 +474,8 @@ export function deepToRaw(obj, cache = new WeakSet()) {
474
474
  }
475
475
 
476
476
  /**
477
- * Helper function to alter Lodash merge function default behaviour on merging arrays while updating machine pool configuration.
477
+ * Helper function to alter Lodash merge function default behaviour on merging
478
+ * arrays and objects.
478
479
  *
479
480
  * In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
480
481
  * merging the latest configuration received from the backend with the current configuration updated by the user.
@@ -486,13 +487,55 @@ export function deepToRaw(obj, cache = new WeakSet()) {
486
487
  * merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
487
488
  *
488
489
  * More info: https://github.com/lodash/lodash/issues/1313
489
- *
490
+
490
491
  * This helper function addresses the issue by always replacing the old array with the new array during the merge process.
492
+ *
493
+ * This helper is also used for another case in rke2.vue to handle merging addon chart default values with the user's current values.
494
+ * It fixed https://github.com/rancher/dashboard/issues/12418
495
+ *
496
+ * If `mutateOriginal` is true, the merge is done directly into `obj1` (mutating it).
497
+ * This is useful in cases like:
498
+ * machinePool.config = mergeWithReplace(clonedLatestConfig, clonedCurrentConfig, { mutateOriginal: true })
499
+ * where merging into a new empty object may lead to incomplete structure.
500
+ *
501
+ * Use `mutateOriginal` when you want to preserve obj1’s original shape, references,
502
+ * or when assigning the result directly to an existing object.
503
+ *
504
+ * @param {Object} [obj1={}] - The first object to merge
505
+ * @param {Object} [obj2={}] - The second object to merge
506
+ * @param {Object} [options={}] - Options for customizing merge behavior
507
+ * @param {boolean} [options.mutateOriginal=false] - true: mutates obj1
508
+ * directly. false: returns a new object
509
+ * @param {boolean} [options.replaceArray=true] - true: replaces arrays in obj1
510
+ * with arrays in obj2 when both properties are arrays
511
+ * false: default lodash merge behavior - recursively merges
512
+ * array members
513
+ * @param {boolean} [options.replaceObjectProps=false] - true: merges objects in
514
+ * obj1 with objects in obj2, overwriting duplicate props
515
+ * false: default lodash merge behavior - recursively merges
516
+ * object props
491
517
  */
492
- export function mergeWithReplaceArrays(obj1 = {}, obj2 = {}) {
493
- return mergeWith(obj1, obj2, (obj1Value, obj2Value) => {
494
- if (Array.isArray(obj1Value) && Array.isArray(obj2Value)) {
518
+ export function mergeWithReplace(
519
+ obj1 = {},
520
+ obj2 = {},
521
+ {
522
+ mutateOriginal = false,
523
+ replaceArray = true,
524
+ replaceObjectProps = false,
525
+ } = {}
526
+ ) {
527
+ const destination = mutateOriginal ? obj1 : {};
528
+
529
+ return mergeWith(destination, obj1, obj2, (obj1Value, obj2Value) => {
530
+ if (replaceArray && Array.isArray(obj1Value) && Array.isArray(obj2Value)) {
495
531
  return obj2Value;
496
532
  }
533
+
534
+ if (replaceObjectProps && isObject(obj1Value) && isObject(obj2Value)) {
535
+ return {
536
+ ...obj1Value,
537
+ ...obj2Value,
538
+ };
539
+ }
497
540
  });
498
541
  }
@@ -1,6 +1,7 @@
1
+ import { Translation } from '@shell/types/t';
1
2
  import formRulesGenerator from '@shell/utils/validators/formRules';
2
3
 
3
- const mockT = (key: string, args: any) => {
4
+ const mockT: Translation = (key: string, args: any) => {
4
5
  return JSON.stringify({
5
6
  message: key,
6
7
  ...args
@@ -8,6 +9,14 @@ const mockT = (key: string, args: any) => {
8
9
  };
9
10
 
10
11
  describe('formRules', () => {
12
+ it('should use "Value" as default label', () => {
13
+ const validators = formRulesGenerator(mockT, {});
14
+
15
+ const message = validators.required(null);
16
+
17
+ expect(message).toStrictEqual('{\"message\":\"validation.required\",\"key\":\"Value\"}');
18
+ });
19
+
11
20
  const formRules = formRulesGenerator(mockT, { key: 'testDisplayKey' });
12
21
 
13
22
  it('"required" : returns undefined when value supplied', () => {