@rancher/shell 3.0.12-rc.1 → 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 (376) 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/images/providers/entraid-black.svg +4 -0
  54. package/assets/images/providers/entraid.svg +9 -0
  55. package/assets/images/vendor/entraid.svg +9 -0
  56. package/assets/styles/app.scss +0 -1
  57. package/assets/styles/base/_variables.scss +2 -0
  58. package/assets/styles/fonts/_fontstack.scss +132 -8
  59. package/assets/translations/en-us.yaml +41 -22
  60. package/assets/translations/zh-hans.yaml +4 -8
  61. package/chart/__tests__/S3.test.ts +10 -3
  62. package/chart/monitoring/index.vue +10 -1
  63. package/components/ActionDropdownShell.vue +2 -1
  64. package/components/CountBox.vue +20 -0
  65. package/components/CreateDriver.vue +0 -12
  66. package/components/CruResourceFooter.vue +9 -5
  67. package/components/DetailText.vue +12 -3
  68. package/components/ExplorerProjectsNamespaces.vue +1 -1
  69. package/components/InstallHelmCharts.vue +2 -2
  70. package/components/LandingPagePreference.vue +14 -5
  71. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
  72. package/components/Resource/Detail/Metadata/index.vue +6 -0
  73. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  74. package/components/Resource/Detail/SpacedRow.vue +3 -1
  75. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  76. package/components/ResourceList/Masthead.vue +12 -8
  77. package/components/SelectIconGrid.vue +5 -10
  78. package/components/SingleClusterInfo.vue +1 -0
  79. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  80. package/components/SortableTable/index.vue +6 -9
  81. package/components/SortableTable/selection.js +23 -5
  82. package/components/SortableTable/sorting.js +6 -3
  83. package/components/Wizard.vue +14 -13
  84. package/components/__tests__/CountBox.test.ts +72 -0
  85. package/components/__tests__/DetailText.test.ts +113 -0
  86. package/components/fleet/FleetBundles.vue +100 -12
  87. package/components/fleet/FleetClusterTargets/index.vue +54 -15
  88. package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
  89. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  90. package/components/form/InputWithSelect.vue +18 -10
  91. package/components/form/KeyValue.vue +17 -1
  92. package/components/form/LabeledSelect.vue +101 -26
  93. package/components/form/NameNsDescription.vue +11 -0
  94. package/components/form/Security.vue +6 -2
  95. package/components/form/Select.vue +73 -56
  96. package/components/form/ServiceNameSelect.vue +13 -11
  97. package/components/form/WorkloadPorts.vue +2 -7
  98. package/components/form/__tests__/KeyValue.test.ts +66 -0
  99. package/components/form/__tests__/NodeScheduling.test.ts +9 -0
  100. package/components/form/__tests__/Security.test.ts +76 -0
  101. package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
  102. package/components/formatter/Autoscaler.vue +4 -4
  103. package/components/formatter/ClusterKubeVersion.vue +27 -0
  104. package/components/formatter/ClusterLink.vue +1 -7
  105. package/components/formatter/ClusterProvider.vue +6 -10
  106. package/components/formatter/FleetSummaryGraph.vue +0 -3
  107. package/components/formatter/MachineSummaryGraph.vue +1 -1
  108. package/components/formatter/PodsUsage.vue +2 -2
  109. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  110. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  111. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  112. package/components/nav/Group.vue +7 -6
  113. package/components/nav/Header.vue +24 -3
  114. package/components/nav/NamespaceFilter.vue +2 -2
  115. package/components/nav/NotificationCenter/Notification.vue +4 -1
  116. package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
  117. package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
  118. package/components/nav/TopLevelMenu.helper.ts +15 -3
  119. package/components/nav/TopLevelMenu.vue +16 -5
  120. package/components/nav/Type.vue +8 -7
  121. package/components/nav/WindowManager/index.vue +2 -1
  122. package/components/nav/WorkspaceSwitcher.vue +13 -0
  123. package/components/nav/__tests__/Group.test.ts +67 -0
  124. package/components/nav/__tests__/Header.test.ts +235 -0
  125. package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
  126. package/components/nav/__tests__/Type.test.ts +20 -3
  127. package/components/templates/default.vue +34 -4
  128. package/components/templates/home.vue +30 -25
  129. package/components/templates/plain.vue +31 -26
  130. package/components/templates/standalone.vue +17 -0
  131. package/composables/useFormValidation.ts +93 -0
  132. package/composables/useLabeledFormElement.ts +10 -2
  133. package/composables/useLabeledSelect.ts +60 -0
  134. package/composables/useUserRetentionValidation.ts +1 -49
  135. package/composables/useVeeValidateField.test.ts +159 -0
  136. package/composables/useVeeValidateField.ts +67 -0
  137. package/config/cookies.js +0 -1
  138. package/config/labels-annotations.js +1 -0
  139. package/config/pagination-table-headers.js +18 -1
  140. package/config/product/manager.js +82 -21
  141. package/config/query-params.js +1 -0
  142. package/config/router/routes.js +6 -8
  143. package/config/table-headers.js +20 -1
  144. package/config/types.js +2 -1
  145. package/core/__tests__/plugin-products.test.ts +1505 -30
  146. package/core/plugin-products-base.ts +137 -20
  147. package/core/plugin-products-helpers.ts +5 -4
  148. package/core/plugin-products.ts +4 -0
  149. package/core/plugin-types.ts +129 -4
  150. package/core/plugin.ts +15 -7
  151. package/core/productDebugger.js +9 -4
  152. package/core/types-provisioning.ts +43 -30
  153. package/core/types.ts +58 -19
  154. package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
  155. package/detail/__tests__/pod.test.ts +41 -0
  156. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  157. package/detail/management.cattle.io.fleetworkspace.vue +49 -0
  158. package/detail/pod.vue +1 -1
  159. package/detail/provisioning.cattle.io.cluster.vue +4 -10
  160. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
  161. package/edit/__tests__/kontainerDriver.test.ts +0 -13
  162. package/edit/__tests__/nodeDriver.test.ts +5 -11
  163. package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
  164. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  165. package/edit/auth/__tests__/azuread.test.ts +217 -34
  166. package/edit/auth/__tests__/oidc.test.ts +54 -0
  167. package/edit/auth/azuread.vue +123 -15
  168. package/edit/auth/oidc.vue +10 -2
  169. package/edit/kontainerDriver.vue +1 -2
  170. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  171. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  172. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  173. package/edit/nodeDriver.vue +0 -2
  174. package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
  175. package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
  176. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  177. package/edit/provisioning.cattle.io.cluster/index.vue +81 -106
  178. package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
  179. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  180. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  181. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  182. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  183. package/edit/secret/__tests__/ssh.test.ts +5 -6
  184. package/edit/secret/basic.vue +31 -0
  185. package/edit/secret/index.vue +68 -17
  186. package/edit/secret/registry.vue +38 -0
  187. package/edit/secret/ssh.vue +29 -0
  188. package/edit/secret/tls.vue +30 -0
  189. package/edit/service.vue +4 -4
  190. package/edit/workload/Upgrading.vue +3 -3
  191. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  192. package/edit/workload/mixins/workload.js +2 -1
  193. package/initialize/App.vue +29 -2
  194. package/initialize/install-plugins.js +0 -2
  195. package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
  196. package/list/catalog.cattle.io.app.vue +25 -5
  197. package/list/fleet.cattle.io.bundle.vue +7 -104
  198. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  199. package/list/management.cattle.io.feature.vue +1 -1
  200. package/list/management.cattle.io.fleetworkspace.vue +8 -0
  201. package/list/provisioning.cattle.io.cluster.vue +262 -180
  202. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  203. package/machine-config/amazonec2.vue +1 -0
  204. package/mixins/__tests__/chart.test.ts +112 -0
  205. package/mixins/brand.js +2 -1
  206. package/mixins/chart.js +50 -15
  207. package/mixins/resource-fetch-api-pagination.js +41 -5
  208. package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
  209. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
  210. package/models/__tests__/chart.test.ts +99 -6
  211. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  212. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  213. package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
  214. package/models/__tests__/management.cattle.io.node.ts +6 -5
  215. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  216. package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
  217. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
  218. package/models/base-cluster.x-k8s.io.js +26 -0
  219. package/models/catalog.cattle.io.app.js +21 -17
  220. package/models/catalog.cattle.io.clusterrepo.js +39 -11
  221. package/models/chart.js +33 -19
  222. package/models/cluster.js +1 -1
  223. package/models/cluster.x-k8s.io.machine.js +4 -22
  224. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  225. package/models/cluster.x-k8s.io.machineset.js +2 -20
  226. package/models/compliance.cattle.io.clusterscan.js +130 -2
  227. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  228. package/models/fleet-application.js +4 -2
  229. package/models/fleet.cattle.io.bundle.js +1 -1
  230. package/models/kontainerdriver.js +11 -0
  231. package/models/management.cattle.io.authconfig.js +5 -1
  232. package/models/management.cattle.io.cluster.js +402 -78
  233. package/models/management.cattle.io.feature.js +3 -3
  234. package/models/management.cattle.io.kontainerdriver.js +1 -26
  235. package/models/management.cattle.io.node.js +6 -4
  236. package/models/management.cattle.io.nodepool.js +1 -1
  237. package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
  238. package/models/networking.k8s.io.ingress.js +12 -4
  239. package/models/nodedriver.js +7 -0
  240. package/models/provisioning.cattle.io.cluster.js +47 -330
  241. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  242. package/package.json +20 -37
  243. package/pages/__tests__/readme.test.ts +49 -0
  244. package/pages/auth/setup.vue +2 -3
  245. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +265 -0
  246. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
  247. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
  248. package/pages/c/_cluster/apps/charts/chart.vue +275 -39
  249. package/pages/c/_cluster/apps/charts/index.vue +2 -2
  250. package/pages/c/_cluster/apps/charts/install.vue +18 -10
  251. package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
  252. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  253. package/pages/c/_cluster/explorer/index.vue +5 -49
  254. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  255. package/pages/c/_cluster/istio/index.vue +21 -6
  256. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
  257. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +40 -2
  258. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
  259. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +735 -13
  260. package/pages/c/_cluster/uiplugins/index.vue +226 -222
  261. package/pages/diagnostic.vue +13 -17
  262. package/pages/fail-whale.vue +18 -0
  263. package/pages/home.vue +77 -260
  264. package/pages/readme.vue +88 -0
  265. package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
  266. package/plugins/dashboard-store/actions.js +40 -18
  267. package/plugins/dashboard-store/resource-class.js +5 -2
  268. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  269. package/plugins/steve/steve-pagination-utils.ts +11 -3
  270. package/plugins/steve/subscribe.js +35 -5
  271. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +211 -1
  272. package/rancher-components/Form/LabeledInput/LabeledInput.vue +37 -4
  273. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
  274. package/rancher-components/RcButton/RcButton.test.ts +37 -1
  275. package/rancher-components/RcButton/RcButton.vue +38 -8
  276. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  277. package/scripts/test-plugins-build.sh +5 -2
  278. package/server/server-middleware.js +2 -2
  279. package/static/humans.txt +1 -0
  280. package/static/robots.txt +34 -0
  281. package/static/welcome-cow.svg +18 -0
  282. package/store/__tests__/catalog.test.ts +276 -12
  283. package/store/__tests__/type-map.test.ts +556 -1
  284. package/store/action-menu.js +8 -3
  285. package/store/auth.js +1 -4
  286. package/store/aws.js +27 -16
  287. package/store/catalog.js +87 -11
  288. package/store/digitalocean.js +20 -38
  289. package/store/index.js +2 -0
  290. package/store/linode.js +25 -40
  291. package/store/pnap.js +1 -0
  292. package/store/type-map.js +111 -29
  293. package/tsconfig.paths.json +8 -8
  294. package/types/kube/kube-api.ts +14 -1
  295. package/types/rancher/steve.api.ts +12 -12
  296. package/types/resources/settings.d.ts +2 -1
  297. package/types/shell/index.d.ts +128 -24
  298. package/types/store/dashboard-store.types.ts +108 -11
  299. package/types/store/pagination.types.ts +6 -3
  300. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  301. package/utils/__tests__/async.test.ts +87 -0
  302. package/utils/__tests__/aws.test.ts +140 -0
  303. package/utils/__tests__/banners.test.ts +176 -0
  304. package/utils/__tests__/chart.test.ts +64 -1
  305. package/utils/__tests__/color.test.ts +226 -0
  306. package/utils/__tests__/duration.test.ts +140 -0
  307. package/utils/__tests__/fleet.test.ts +340 -0
  308. package/utils/__tests__/git.test.ts +270 -0
  309. package/utils/__tests__/inactivity.test.ts +316 -0
  310. package/utils/__tests__/ingress.test.ts +553 -0
  311. package/utils/__tests__/kube.test.ts +68 -0
  312. package/utils/__tests__/namespace-filter.test.ts +109 -0
  313. package/utils/__tests__/object.test.ts +77 -0
  314. package/utils/__tests__/pagination-utils.test.ts +361 -0
  315. package/utils/__tests__/parse-externalid.test.ts +137 -0
  316. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  317. package/utils/__tests__/poller-sequential.test.ts +177 -0
  318. package/utils/__tests__/poller.test.ts +170 -0
  319. package/utils/__tests__/promise.test.ts +346 -0
  320. package/utils/__tests__/settings.test.ts +140 -0
  321. package/utils/__tests__/sort-utils.test.ts +301 -0
  322. package/utils/__tests__/string-utils.test.ts +798 -0
  323. package/utils/__tests__/string.test.ts +23 -1
  324. package/utils/__tests__/style.test.ts +154 -0
  325. package/utils/__tests__/svg-filter.test.ts +184 -0
  326. package/utils/__tests__/time.test.ts +14 -1
  327. package/utils/__tests__/units.test.ts +417 -0
  328. package/utils/__tests__/url.test.ts +246 -0
  329. package/utils/__tests__/versions.test.ts +128 -0
  330. package/utils/__tests__/xccdf.test.ts +391 -0
  331. package/utils/chart.js +36 -0
  332. package/utils/fleet.ts +13 -3
  333. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  334. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  335. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  336. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  337. package/utils/gc/__tests__/gc.test.ts +487 -0
  338. package/utils/ingress.ts +9 -1
  339. package/utils/object.js +33 -2
  340. package/utils/pagination-utils.ts +2 -1
  341. package/utils/string.js +25 -2
  342. package/utils/time.ts +5 -0
  343. package/utils/uiplugins.ts +5 -5
  344. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  345. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  346. package/utils/validators/__tests__/index.test.ts +481 -0
  347. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  348. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  349. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  350. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  351. package/utils/validators/__tests__/role-template.test.ts +149 -0
  352. package/utils/validators/__tests__/service.test.ts +283 -0
  353. package/utils/validators/__tests__/setting.test.js +32 -0
  354. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  355. package/utils/validators/formRules/index.ts +5 -5
  356. package/utils/validators/machine-pool.ts +1 -1
  357. package/utils/validators/setting.js +18 -3
  358. package/utils/xccdf.ts +418 -0
  359. package/vue.config.js +0 -9
  360. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  361. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  362. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  363. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
  364. package/assets/images/providers/azuread-black.svg +0 -22
  365. package/assets/images/providers/azuread.svg +0 -25
  366. package/assets/images/vendor/azuread.svg +0 -18
  367. package/assets/styles/fonts/_dots.scss +0 -18
  368. package/components/EmberPage.vue +0 -622
  369. package/components/EmberPageView.vue +0 -39
  370. package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
  371. package/mixins/labeled-form-element.ts +0 -225
  372. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
  373. package/pages/c/_cluster/manager/pages/_page.vue +0 -22
  374. package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
  375. package/plugins/ember-cookie.js +0 -17
  376. package/utils/ember-page.js +0 -30
@@ -660,12 +660,8 @@ export default {
660
660
  return getters.all(type);
661
661
  },
662
662
 
663
- // opt:
664
- // filter: Filter by fields, e.g. {field: value, anotherField: anotherValue} (default: none)
665
- // limit: Number of records to return per page (default: 1000)
666
- // sortBy: Sort by field
667
- // sortOrder: asc or desc
668
- // url: Use this specific URL instead of looking up the URL for the type/id. This should only be used for bootstrapping schemas on startup.
663
+ // opt: @ActionFindArgs
664
+ // @returns @ActionFindResponse
669
665
  // @TODO depaginate: If the response is paginated, retrieve all the pages. (default: true)
670
666
  async find(ctx, { type, id, opt }) {
671
667
  if (!id) {
@@ -697,11 +693,23 @@ export default {
697
693
  }
698
694
  }
699
695
 
696
+ const havePage = getters.havePage(type);
697
+
700
698
  opt = opt || {};
701
699
  opt.url = getters.urlFor(type, id, opt);
702
700
 
703
701
  const res = await dispatch('request', { opt, type });
704
702
 
703
+ if (!havePage && getters.havePage(type)) {
704
+ // There may be a super edge case where list --> detail (whilst loading) --> list navigation causes the list's rows to disappear
705
+ // Somehow the `findPage` from the list page returns before the `find`. The `find` then clears the page state in the cache.
706
+ // If this has happened silently return (we don't care about result)
707
+ // https://github.com/rancher/dashboard/issues/17524
708
+ console.warn(`Prevented \`find\` action from polluting cache for type "${ type }" (currently represents a page).`); // eslint-disable-line no-console
709
+
710
+ return;
711
+ }
712
+
705
713
  if (!opt.transient) {
706
714
  await dispatch('load', { data: res, invalidatePageCache: opt.invalidatePageCache });
707
715
  }
@@ -830,19 +838,33 @@ export default {
830
838
  /**
831
839
  * Remove all cached entries for a resource and stop watches
832
840
  */
833
- forgetType({ commit, dispatch, state }, type, compareWatches) {
834
- // Stop all known watches
835
- state.started
836
- .filter((entry) => compareWatches ? compareWatches(entry) : entry.type === type)
837
- .forEach((entry) => dispatch('unwatch', entry));
838
-
839
- // Stop all known back-off watch processes for this type
840
- dispatch('resetWatchBackOff', {
841
- type, compareWatches, resetStarted: false
842
- });
841
+ forgetType({ commit, dispatch, state }, payload) {
842
+ let type = payload;
843
+ let config = {};
844
+
845
+ if ( typeof payload === 'object' && payload !== null && payload.type ) {
846
+ type = payload.type;
847
+ config = payload;
848
+ }
849
+
850
+ const { compareWatches, unwatch = true, forget = true } = config;
843
851
 
844
- // Remove entries from store
845
- commit('forgetType', type);
852
+ if (unwatch) {
853
+ // Stop all known watches
854
+ state.started
855
+ .filter((entry) => compareWatches ? compareWatches(entry) : entry.type === type)
856
+ .forEach((entry) => dispatch('unwatch', entry));
857
+
858
+ // Stop all known back-off watch processes for this type
859
+ dispatch('resetWatchBackOff', {
860
+ type, compareWatches, resetStarted: false
861
+ });
862
+ }
863
+
864
+ if (forget) {
865
+ // Remove entries from store
866
+ commit('forgetType', type);
867
+ }
846
868
  },
847
869
 
848
870
  promptRemove({ commit, state }, resources ) {
@@ -946,12 +946,15 @@ export default class Resource {
946
946
  // where mostly likely extension CRD model is extending from resource-class
947
947
  const isResourceDetailDrawerCompatibleWithRancherSystem = semver.satisfies(parsedRancherVersion, '>= 2.13.0');
948
948
 
949
+ // If the resource can't show an edit or a yaml we don't want to show the configuration drawer
950
+ const showConfigEnabled = isResourceDetailDrawerCompatibleWithRancherSystem && this.disableResourceDetailDrawer !== true && (this.canCustomEdit || this.canYaml);
951
+
949
952
  const all = [
950
953
  {
951
954
  action: 'showConfiguration',
952
955
  label: this.t('action.showConfiguration'),
953
956
  icon: 'icon icon-document',
954
- enabled: isResourceDetailDrawerCompatibleWithRancherSystem && this.disableResourceDetailDrawer !== true && (this.canCustomEdit || this.canYaml), // If the resource can't show an edit or a yaml we don't want to show the configuration drawer
957
+ enabled: showConfigEnabled,
955
958
  },
956
959
  { divider: true },
957
960
  {
@@ -964,7 +967,7 @@ export default class Resource {
964
967
  action: this.canEditYaml ? 'goToEditYaml' : 'goToViewYaml',
965
968
  label: this.t(this.canEditYaml ? 'action.editYaml' : 'action.viewYaml'),
966
969
  icon: 'icon icon-file',
967
- enabled: this.canYaml,
970
+ enabled: this.canYaml && (this.canEditYaml || !showConfigEnabled), // Hide "View YAML" when "Show Configuration" is available since it already includes YAML viewing
968
971
  },
969
972
  {
970
973
  action: (this.canCustomEdit ? 'goToClone' : 'cloneYaml'),
@@ -14,7 +14,8 @@ describe('steve: subscribe', () => {
14
14
  schemaFor: () => null,
15
15
  inError: () => false,
16
16
  watchStarted: () => false,
17
- listenerManager: state.listenerManager
17
+ listenerManager: state.listenerManager,
18
+ typeRegistered: () => true,
18
19
  };
19
20
  const rootGetters = {
20
21
  'type-map/isSpoofed': () => false,
@@ -343,7 +344,7 @@ describe('steve: subscribe', () => {
343
344
 
344
345
  // call watch
345
346
  actions.watch({
346
- state, dispatch, getters, rootGetters
347
+ state, dispatch, getters, rootGetters, commit
347
348
  }, {
348
349
  ...obj,
349
350
  revision,
@@ -488,6 +489,7 @@ describe('steve: subscribe', () => {
488
489
  const state = {
489
490
  started: [],
490
491
  inError: {},
492
+ queue: [],
491
493
  listenerManager: new SteveWatchEventListenerManager()
492
494
  };
493
495
  const _getters = {
@@ -498,7 +500,8 @@ describe('steve: subscribe', () => {
498
500
  watchStarted: (...args) => getters.watchStarted(state)(...args),
499
501
  backOffId: (...args) => getters.backOffId()(...args),
500
502
  canBackoff: () => true,
501
- listenerManager: state.listenerManager
503
+ listenerManager: state.listenerManager,
504
+ typeRegistered: () => true,
502
505
  };
503
506
  const commit = (type, ...args) => mutations[type](state, ...args);
504
507
 
@@ -258,6 +258,11 @@ class StevePaginationUtils extends NamespaceProjectFilters {
258
258
  { field: 'spec.displayName' },
259
259
  { field: `status.provider` },
260
260
  { field: `status.connected` },
261
+ { field: `status.info.machineProvider` },
262
+ { field: `status.driver` },
263
+ { field: `status.provider` },
264
+ { field: `status.info.kubernetesVersion` },
265
+ { field: `spec.fleetWorkspaceName` },
261
266
  ],
262
267
  [SECRET]: [
263
268
  { field: `metadata.annotations[${ UI_PROJECT_SECRET_COPY }]` },
@@ -265,7 +270,10 @@ class StevePaginationUtils extends NamespaceProjectFilters {
265
270
  [NAMESPACE]: [
266
271
  ],
267
272
  [CAPI.MACHINE]: [
268
- { field: 'spec.clusterName' }
273
+ { field: 'spec.clusterName' },
274
+ ],
275
+ [CAPI.MACHINE_DEPLOYMENT]: [
276
+ { field: 'spec.clusterName' },
269
277
  ],
270
278
  [EVENT]: [
271
279
  { field: '_type' },
@@ -770,8 +778,8 @@ export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStores = {
770
778
  enableAll: false,
771
779
  enableSome: {
772
780
  enabled: [
773
- { resource: CAPI.RANCHER_CLUSTER, context: ['side-bar'] },
774
- { resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
781
+ { resource: CAPI.RANCHER_CLUSTER, context: ['side-bar', 'home', 'cluster-management'] },
782
+ { resource: MANAGEMENT.CLUSTER, context: ['side-bar', 'home', 'cluster-management'] },
775
783
  { resource: CATALOG.APP, context: ['branding'] },
776
784
  SECRET
777
785
  ],
@@ -469,8 +469,10 @@ const sharedActions = {
469
469
  const worker = (this.$workers || {})[getters.storeName];
470
470
 
471
471
  if (worker) {
472
+ const storeName = getters.storeName;
473
+
472
474
  worker.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
473
- cleanupTasks.push(waitFor(() => !this.$workers[getters.storeName], 'Worker is destroyed'));
475
+ cleanupTasks.push(waitFor(() => !this.$workers?.[storeName], 'Worker to be destroyed', 30000, 10, true));
474
476
  }
475
477
 
476
478
  if ( socket ) {
@@ -553,17 +555,32 @@ const sharedActions = {
553
555
  * @param {STEVE_WATCH_PARAMS} params
554
556
  */
555
557
  watch({
556
- state, dispatch, getters, rootGetters
558
+ state, dispatch, getters, rootGetters, commit
557
559
  }, params) {
558
560
  state.debugSocket && console.info(`Watch Request [${ getters.storeName }]`, JSON.stringify(params)); // eslint-disable-line no-console
559
561
  let {
560
562
  // eslint-disable-next-line prefer-const
561
- type, selector, id, revision, namespace, stop, force, mode, standardWatch = true
563
+ type, selector, id, revision, namespace, stop, force, mode, standardWatch = true, registerType = false
562
564
  } = params;
563
565
 
564
566
  namespace = acceptOrRejectSocketMessage.subscribeNamespace(namespace);
565
567
  type = getters.normalizeType(type);
566
568
 
569
+ if ( !getters.typeRegistered(type) ) {
570
+ if (registerType) {
571
+ commit('registerType', type);
572
+ } else if (mode !== STEVE_WATCH_MODE.RESOURCE_CHANGES) {
573
+ // - If we continue and open up a watch whenever we receive a `resource.` notification we go to queueChanges (bar resource.changes mode).
574
+ // - queueChanges ignores any change that's for a type that hasn't been registered
575
+ // - So here we're just exiting early, avoiding the watch --> queueChanges --> ignore loop
576
+ //
577
+ // Interestingly this is hit quite a few times (on cluster create screens there's token, cluster, project, projectRoleTemplateBinding, etc)
578
+ state.debugSocket && console.info('Will not Watch (type is not registered)', JSON.stringify(params)); // eslint-disable-line no-console
579
+
580
+ return;
581
+ }
582
+ }
583
+
567
584
  if (rootGetters['type-map/isSpoofed'](type)) {
568
585
  state.debugSocket && console.info('Will not Watch (type is spoofed)', JSON.stringify(params)); // eslint-disable-line no-console
569
586
 
@@ -620,6 +637,9 @@ const sharedActions = {
620
637
  if (debounceMs) {
621
638
  msg.debounceMs = debounceMs;
622
639
  }
640
+
641
+ // Anything in the queue will pollute the result set, so clear (and print to console so we know it's working)
642
+ commit('clearFromQueue', { type, log: true });
623
643
  }
624
644
  }
625
645
 
@@ -1556,10 +1576,20 @@ const defaultMutations = {
1556
1576
  state.socketListenerManager = new SteveWatchEventListenerManager(state.config.namespace);
1557
1577
  },
1558
1578
 
1559
- clearFromQueue(state, type) {
1579
+ clearFromQueue(state, args) {
1580
+ const safeArgs = typeof args === 'object' ? args : { type: args };
1581
+ const { type, log } = safeArgs;
1582
+
1560
1583
  // Remove anything in the queue that is a resource update for the given type
1561
1584
  state.queue = state.queue.filter((item) => {
1562
- return item.body?.type !== type;
1585
+ const keep = item.body?.type !== type;
1586
+
1587
+ if (!keep && log) {
1588
+ // eslint-disable-next-line no-console
1589
+ console.info(`Clearing queued item of type \`${ type }\` from queue`, item);
1590
+ }
1591
+
1592
+ return keep;
1563
1593
  });
1564
1594
  },
1565
1595
  };
@@ -1,4 +1,6 @@
1
- import { mount } from '@vue/test-utils';
1
+ import { defineComponent, nextTick, provide, ref } from 'vue';
2
+ import { mount, flushPromises } from '@vue/test-utils';
3
+ import { useForm } from 'vee-validate';
2
4
  import { LabeledInput } from './index';
3
5
 
4
6
  describe('component: LabeledInput', () => {
@@ -105,4 +107,212 @@ describe('component: LabeledInput', () => {
105
107
  expect(mainInput.attributes('aria-label')).toBeUndefined();
106
108
  expect(wrapper.find('label').text()).toBe(label);
107
109
  });
110
+
111
+ describe('vee-validate integration', () => {
112
+ const i18nMock = { $store: { getters: { 'i18n/t': jest.fn() } } };
113
+
114
+ it('without name prop: existing rules-based validation message is shown after blur', async() => {
115
+ const errorMessage = 'This field cannot be empty';
116
+ const notEmptyRule = (v: string) => (!v ? errorMessage : undefined);
117
+
118
+ const wrapper = mount(LabeledInput, {
119
+ propsData: {
120
+ rules: [notEmptyRule],
121
+ value: '',
122
+ },
123
+ mocks: i18nMock
124
+ });
125
+
126
+ await wrapper.find('input').trigger('blur');
127
+ await nextTick();
128
+
129
+ expect(wrapper.vm.validationMessage).toBe(errorMessage);
130
+ });
131
+
132
+ it('without name prop: error CSS class is not applied automatically', async() => {
133
+ const notEmptyRule = (v: string) => (!v ? 'Error' : undefined);
134
+
135
+ const wrapper = mount(LabeledInput, {
136
+ propsData: {
137
+ rules: [notEmptyRule],
138
+ value: '',
139
+ },
140
+ mocks: i18nMock
141
+ });
142
+
143
+ await wrapper.find('input').trigger('blur');
144
+ await nextTick();
145
+
146
+ expect(wrapper.find('.labeled-input').classes()).not.toContain('error');
147
+ });
148
+
149
+ it('with name prop: name attribute is set on the input element', () => {
150
+ const wrapper = mount(LabeledInput, {
151
+ propsData: { name: 'myField' },
152
+ mocks: i18nMock
153
+ });
154
+
155
+ expect(wrapper.find('input').attributes('name')).toStrictEqual('myField');
156
+ });
157
+
158
+ it('with name prop: existing rules run through vee-validate and show error message after blur', async() => {
159
+ const errorMessage = 'Field cannot be empty';
160
+ const notEmptyRule = (v: string) => (!v ? errorMessage : undefined);
161
+
162
+ const wrapper = mount(LabeledInput, {
163
+ propsData: {
164
+ name: 'testField',
165
+ rules: [notEmptyRule],
166
+ value: '',
167
+ },
168
+ mocks: i18nMock
169
+ });
170
+
171
+ await wrapper.find('input').trigger('blur');
172
+ await flushPromises();
173
+
174
+ expect(wrapper.vm.validationMessage).toStrictEqual(errorMessage);
175
+ });
176
+
177
+ it('with name prop: no error class when validation passes', async() => {
178
+ const notEmptyRule = (v: string) => (!v ? 'Error' : undefined);
179
+
180
+ const wrapper = mount(LabeledInput, {
181
+ propsData: {
182
+ name: 'testField',
183
+ rules: [notEmptyRule],
184
+ value: 'valid value',
185
+ },
186
+ mocks: i18nMock
187
+ });
188
+
189
+ await wrapper.find('input').trigger('blur');
190
+ await flushPromises();
191
+
192
+ expect(wrapper.find('.labeled-input').classes()).not.toContain('error');
193
+ expect(wrapper.vm.validationMessage).toBeUndefined();
194
+ });
195
+
196
+ it('with name prop: form-level validation schema error is shown when the form validates', async() => {
197
+ const errorMessage = 'Username is required';
198
+ const showAllErrors = ref(false);
199
+ let triggerFormValidation!: () => Promise<unknown>;
200
+
201
+ const TestWrapper = defineComponent({
202
+ components: { LabeledInput },
203
+ setup() {
204
+ provide('vee-show-all-errors', showAllErrors);
205
+
206
+ const { validate } = useForm({
207
+ validationSchema: { username: (v: string) => (!v ? errorMessage : true) },
208
+ initialValues: { username: '' },
209
+ });
210
+
211
+ triggerFormValidation = async() => {
212
+ await validate();
213
+ showAllErrors.value = true;
214
+ };
215
+
216
+ return {};
217
+ },
218
+ template: '<LabeledInput name="username" value="" />',
219
+ });
220
+
221
+ const wrapper = mount(TestWrapper, { global: { mocks: { $store: { getters: { 'i18n/t': jest.fn() } } } } });
222
+
223
+ await triggerFormValidation();
224
+ await flushPromises();
225
+
226
+ const labeledInput = wrapper.findComponent(LabeledInput);
227
+
228
+ expect(labeledInput.vm.validationMessage).toStrictEqual(errorMessage);
229
+ });
230
+
231
+ it('without name prop: error clears when a previously invalid value becomes valid', async() => {
232
+ const errorMessage = 'This field cannot be empty';
233
+ const notEmptyRule = (v: string) => (!v ? errorMessage : undefined);
234
+
235
+ const wrapper = mount(LabeledInput, {
236
+ propsData: {
237
+ rules: [notEmptyRule],
238
+ value: '',
239
+ },
240
+ mocks: i18nMock
241
+ });
242
+
243
+ await wrapper.find('input').trigger('blur');
244
+ await nextTick();
245
+
246
+ expect(wrapper.vm.validationMessage).toBe(errorMessage);
247
+
248
+ await wrapper.setProps({ value: 'valid value' });
249
+ await nextTick();
250
+
251
+ expect(wrapper.vm.validationMessage).toBeUndefined();
252
+ });
253
+
254
+ it('with name prop: error clears when a previously invalid value becomes valid', async() => {
255
+ const errorMessage = 'Field cannot be empty';
256
+ const notEmptyRule = (v: string) => (!v ? errorMessage : undefined);
257
+
258
+ const wrapper = mount(LabeledInput, {
259
+ propsData: {
260
+ name: 'testField',
261
+ rules: [notEmptyRule],
262
+ value: '',
263
+ },
264
+ mocks: i18nMock
265
+ });
266
+
267
+ await wrapper.find('input').trigger('blur');
268
+ await flushPromises();
269
+
270
+ expect(wrapper.vm.validationMessage).toStrictEqual(errorMessage);
271
+
272
+ await wrapper.setProps({ value: 'valid value' });
273
+ await flushPromises();
274
+
275
+ expect(wrapper.vm.validationMessage).toBeUndefined();
276
+ });
277
+
278
+ describe('with both name and rules provided', () => {
279
+ it('shows the error message exactly once when invalid (not duplicated across both validation paths)', async() => {
280
+ const errorMessage = 'Field cannot be empty';
281
+ const notEmptyRule = (v: string) => (!v ? errorMessage : undefined);
282
+
283
+ const wrapper = mount(LabeledInput, {
284
+ propsData: {
285
+ name: 'testField',
286
+ rules: [notEmptyRule],
287
+ value: '',
288
+ },
289
+ mocks: i18nMock
290
+ });
291
+
292
+ await wrapper.find('input').trigger('blur');
293
+ await flushPromises();
294
+
295
+ expect(wrapper.vm.validationMessage).toStrictEqual(errorMessage);
296
+ expect(wrapper.vm.validationMessage).not.toContain(`${ errorMessage }, ${ errorMessage }`);
297
+ });
298
+
299
+ it('shows no error when the value satisfies the rules', async() => {
300
+ const notEmptyRule = (v: string) => (!v ? 'Field cannot be empty' : undefined);
301
+
302
+ const wrapper = mount(LabeledInput, {
303
+ propsData: {
304
+ name: 'testField',
305
+ rules: [notEmptyRule],
306
+ value: 'valid value',
307
+ },
308
+ mocks: i18nMock
309
+ });
310
+
311
+ await wrapper.find('input').trigger('blur');
312
+ await flushPromises();
313
+
314
+ expect(wrapper.vm.validationMessage).toBeUndefined();
315
+ });
316
+ });
317
+ });
108
318
  });
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { defineComponent, inject } from 'vue';
2
+ import { defineComponent, inject, computed, toRef } from 'vue';
3
3
  import TextAreaAutoGrow from '@components/Form/TextArea/TextAreaAutoGrow.vue';
4
4
  import LabeledTooltip from '@components/LabeledTooltip/LabeledTooltip.vue';
5
5
  import { escapeHtml, generateRandomAlphaString } from '@shell/utils/string';
@@ -8,6 +8,7 @@ import { isValidCron } from 'cron-validator';
8
8
  import { debounce } from 'lodash';
9
9
  import { useLabeledFormElement, labeledFormElementProps } from '@shell/composables/useLabeledFormElement';
10
10
  import { useCompactInput } from '@shell/composables/useCompactInput';
11
+ import { useVeeValidateField } from '@shell/composables/useVeeValidateField';
11
12
 
12
13
  interface NonReactiveProps {
13
14
  onInput: (event: Event) => void | ((event: Event) => void);
@@ -114,6 +115,15 @@ export default defineComponent({
114
115
  ariaLabel: {
115
116
  type: String,
116
117
  default: ''
118
+ },
119
+
120
+ /**
121
+ * The field name used for vee-validate integration. When provided, the
122
+ * component registers with a parent vee-validate form context
123
+ */
124
+ name: {
125
+ type: String,
126
+ default: null
117
127
  }
118
128
  },
119
129
 
@@ -132,15 +142,27 @@ export default defineComponent({
132
142
 
133
143
  const onInput = inject('onInput', provideProps.onInput);
134
144
 
145
+ const { effectiveValidationMessage, veeHandleBlur, veeValidate } = useVeeValidateField({
146
+ name: toRef(props, 'name'),
147
+ rules: toRef(props, 'rules'),
148
+ value: toRef(props, 'value'),
149
+ validationMessage,
150
+ });
151
+
152
+ const effectiveStatus = computed(() => props.status);
153
+
135
154
  return {
136
155
  focused,
137
156
  onFocusLabeled,
138
157
  onBlurLabeled,
139
158
  onInput,
140
159
  isDisabled,
141
- validationMessage,
160
+ validationMessage: effectiveValidationMessage,
142
161
  requiredField,
143
162
  isCompact,
163
+ veeHandleBlur,
164
+ veeValidate,
165
+ effectiveStatus,
144
166
  };
145
167
  },
146
168
 
@@ -339,6 +361,11 @@ export default defineComponent({
339
361
  onBlur(event: string | FocusEvent): void {
340
362
  this.$emit('blur', event);
341
363
  this.onBlurLabeled();
364
+ // Mark the field as touched in vee-validate without relying on its
365
+ // 'validated-only' guard, then run validation unconditionally so
366
+ // errors surface on the first blur (matching useLabeledFormElement behavior).
367
+ this.veeHandleBlur(event instanceof FocusEvent ? event : undefined, false);
368
+ this.veeValidate();
342
369
  },
343
370
 
344
371
  escapeHtml
@@ -353,7 +380,7 @@ export default defineComponent({
353
380
  focused,
354
381
  [mode]: true,
355
382
  disabled: isDisabled,
356
- [status]: status,
383
+ [effectiveStatus]: effectiveStatus,
357
384
  suffix: hasSuffix,
358
385
  'has-clean-tooltip': hasTooltip,
359
386
  'compact-input': isCompact,
@@ -389,13 +416,14 @@ export default defineComponent({
389
416
  ref="value"
390
417
  v-bind="$attrs"
391
418
  v-stripped-aria-label="!hasLabel && ariaLabel ? ariaLabel : undefined"
419
+ :name="name || undefined"
392
420
  :maxlength="_maxlength"
393
421
  :disabled="isDisabled"
394
422
  :aria-disabled="isDisabled"
395
423
  :value="value || ''"
396
424
  :placeholder="_placeholder"
397
425
  autocapitalize="off"
398
- :class="{ conceal: type === 'multiline-password' }"
426
+ :class="{ 'multiline-password': type === 'multiline-password' }"
399
427
  :aria-describedby="ariaDescribedBy"
400
428
  :aria-required="requiredField"
401
429
  @update:value="onInput"
@@ -410,6 +438,7 @@ export default defineComponent({
410
438
  :role="type === 'number' ? undefined : 'textbox'"
411
439
  :class="{ 'no-label': !hasLabel }"
412
440
  v-bind="$attrs"
441
+ :name="name || undefined"
413
442
  :maxlength="_maxlength"
414
443
  :disabled="isDisabled"
415
444
  :aria-disabled="isDisabled"
@@ -464,6 +493,10 @@ export default defineComponent({
464
493
  </div>
465
494
  </template>
466
495
  <style scoped lang="scss">
496
+ .multiline-password:not(:focus) {
497
+ -webkit-text-security: disc;
498
+ }
499
+
467
500
  .labeled-input.view {
468
501
  input {
469
502
  text-overflow: ellipsis;
@@ -6,7 +6,7 @@ type StateType = boolean | 'true' | 'false' | undefined;
6
6
  export default defineComponent({
7
7
  props: {
8
8
  value: {
9
- type: [Boolean, String, Number],
9
+ type: [Boolean, String, Number, null],
10
10
  default: false
11
11
  },
12
12
 
@@ -1,4 +1,4 @@
1
- import { mount } from '@vue/test-utils';
1
+ import { mount, RouterLinkStub } from '@vue/test-utils';
2
2
  import RcButton from './RcButton.vue';
3
3
 
4
4
  describe('rcButton.vue', () => {
@@ -138,4 +138,40 @@ describe('rcButton.vue', () => {
138
138
  expect(button.classes()).toContain('variant-ghost');
139
139
  });
140
140
  });
141
+
142
+ describe('to prop', () => {
143
+ it('renders as a <button> when no "to" prop is provided', () => {
144
+ const wrapper = mount(RcButton);
145
+
146
+ expect(wrapper.find('button').exists()).toBe(true);
147
+ expect(wrapper.findComponent(RouterLinkStub).exists()).toBe(false);
148
+ });
149
+
150
+ it('renders as a RouterLink when "to" prop is provided', () => {
151
+ const to = { name: 'some-route' };
152
+ const wrapper = mount(RcButton, {
153
+ props: { to },
154
+ global: { stubs: { RouterLink: RouterLinkStub } },
155
+ });
156
+
157
+ const link = wrapper.findComponent(RouterLinkStub);
158
+
159
+ expect(link.exists()).toBe(true);
160
+ expect(wrapper.find('button').exists()).toBe(false);
161
+ expect(link.props('to')).toStrictEqual(to);
162
+ });
163
+
164
+ it('applies button classes when rendered as a RouterLink', () => {
165
+ const wrapper = mount(RcButton, {
166
+ props: { to: '/foo', variant: 'secondary' },
167
+ global: { stubs: { RouterLink: RouterLinkStub } },
168
+ });
169
+
170
+ const link = wrapper.findComponent(RouterLinkStub);
171
+
172
+ expect(link.classes()).toContain('rc-button');
173
+ expect(link.classes()).toContain('btn');
174
+ expect(link.classes()).toContain('variant-secondary');
175
+ });
176
+ });
141
177
  });