@rancher/shell 0.4.0 → 0.5.0

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 (274) hide show
  1. package/assets/images/providers/ovhcloudmks.svg +122 -0
  2. package/assets/images/providers/ovhcloudpubliccloud.svg +122 -0
  3. package/assets/styles/global/_layout.scss +99 -0
  4. package/assets/translations/en-us.yaml +30 -5
  5. package/assets/translations/zh-hans.yaml +1 -1
  6. package/babel.config.js +7 -1
  7. package/chart/monitoring/alerting/index.vue +7 -21
  8. package/chart/monitoring/grafana/index.vue +55 -0
  9. package/chart/monitoring/index.vue +51 -17
  10. package/chart/monitoring/prometheus/index.vue +37 -43
  11. package/chart/rancher-backup/index.vue +2 -1
  12. package/cloud-credential/azure.vue +4 -17
  13. package/components/AsyncButton.vue +17 -5
  14. package/components/Certificates.vue +164 -0
  15. package/components/CodeMirror.vue +19 -21
  16. package/components/CruResource.vue +1 -0
  17. package/components/DraggableZone.vue +2 -2
  18. package/components/EtcdInfoBanner.vue +1 -1
  19. package/components/ExplorerProjectsNamespaces.vue +25 -1
  20. package/components/IconOrSvg.vue +1 -1
  21. package/components/LandingPagePreference.vue +1 -4
  22. package/components/PodSecurityAdmission.vue +2 -2
  23. package/components/Questions/index.vue +1 -1
  24. package/components/ResourceDetail/Masthead.vue +16 -3
  25. package/components/ResourceTable.vue +14 -2
  26. package/components/ResourceYaml.vue +5 -0
  27. package/components/SideNav.vue +1 -1
  28. package/components/SingleClusterInfo.vue +1 -4
  29. package/components/Tabbed/index.vue +12 -0
  30. package/components/fleet/FleetRepos.vue +62 -27
  31. package/components/fleet/FleetResources.vue +6 -1
  32. package/components/form/ArrayListSelect.vue +10 -0
  33. package/components/form/Error.vue +3 -3
  34. package/components/form/Footer.vue +2 -2
  35. package/components/form/GitPicker.vue +83 -38
  36. package/components/form/KeyValue.vue +4 -0
  37. package/components/form/LabeledSelect.vue +4 -0
  38. package/components/formatter/Checked.vue +11 -3
  39. package/components/formatter/FleetClusterSummaryGraph.vue +27 -0
  40. package/components/formatter/FleetSummaryGraph.vue +23 -11
  41. package/components/formatter/LiveDuration.vue +1 -1
  42. package/components/formatter/PercentageBar.vue +1 -1
  43. package/components/formatter/__tests__/Checked.test.ts +19 -0
  44. package/components/nav/Group.vue +2 -2
  45. package/components/nav/Header.vue +0 -1
  46. package/components/nav/TopLevelMenu.vue +36 -6
  47. package/components/nav/Type.vue +1 -3
  48. package/components/nav/WindowManager/ContainerLogs.vue +101 -3
  49. package/components/nav/WindowManager/ContainerShell.vue +6 -1
  50. package/components/nav/WindowManager/__tests__/ContainerLogs.test.ts +186 -0
  51. package/components/nav/WindowManager/index.vue +11 -10
  52. package/components/nav/__tests__/TopLevelMenu.test.ts +33 -0
  53. package/components/nav/__tests__/Type.test.ts +1 -1
  54. package/components/nuxt/nuxt-child.js +14 -78
  55. package/components/nuxt/nuxt.js +1 -1
  56. package/{layouts → components/templates}/blank.vue +1 -1
  57. package/{layouts → components/templates}/default.vue +8 -98
  58. package/{layouts → components/templates}/error.vue +10 -19
  59. package/{layouts → components/templates}/home.vue +4 -1
  60. package/{layouts → components/templates}/plain.vue +4 -1
  61. package/{layouts → components/templates}/standalone.vue +1 -1
  62. package/{layouts → components/templates}/unauthenticated.vue +1 -1
  63. package/composables/useCompactInput.test.ts +36 -0
  64. package/composables/useCompactInput.ts +20 -0
  65. package/composables/useLabeledFormElement.test.ts +135 -0
  66. package/composables/useLabeledFormElement.ts +138 -0
  67. package/config/harvester-manager-types.js +2 -0
  68. package/config/private-label.js +22 -0
  69. package/config/product/explorer.js +3 -0
  70. package/config/product/fleet.js +6 -1
  71. package/config/product/manager.js +8 -2
  72. package/config/query-params.js +1 -0
  73. package/config/router.js +385 -364
  74. package/config/settings.ts +1 -0
  75. package/config/store.js +1 -1
  76. package/config/system-namespaces.js +3 -0
  77. package/config/table-headers.js +47 -0
  78. package/core/plugin-routes.ts +56 -114
  79. package/core/plugin.ts +16 -10
  80. package/core/plugins-loader.js +7 -9
  81. package/core/plugins.js +0 -3
  82. package/creators/app/files/.gitlab-ci.yml +1 -1
  83. package/detail/fleet.cattle.io.cluster.vue +11 -1
  84. package/detail/provisioning.cattle.io.cluster.vue +4 -3
  85. package/dialog/ScaleMachineDownDialog.vue +34 -17
  86. package/edit/__tests__/service.test.ts +89 -0
  87. package/edit/auth/googleoauth.vue +1 -5
  88. package/edit/cloudcredential.vue +2 -0
  89. package/edit/configmap.vue +2 -1
  90. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +2 -2
  91. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +1 -1
  92. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +15 -7
  93. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +112 -0
  94. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +473 -0
  95. package/edit/provisioning.cattle.io.cluster/__tests__/{CustomCommand.tests.ts → CustomCommand.test.ts} +4 -0
  96. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +1 -1
  97. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +73 -0
  98. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +7 -1
  99. package/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster.ts +386 -0
  100. package/edit/provisioning.cattle.io.cluster/import.vue +2 -2
  101. package/edit/provisioning.cattle.io.cluster/index.vue +92 -36
  102. package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -583
  103. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +137 -0
  104. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +157 -0
  105. package/edit/provisioning.cattle.io.cluster/{Basics.vue → tabs/Basics.vue} +94 -19
  106. package/edit/provisioning.cattle.io.cluster/{MachinePool.vue → tabs/MachinePool.vue} +1 -0
  107. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +135 -0
  108. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +189 -0
  109. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +144 -0
  110. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/index.vue +76 -0
  111. package/edit/service.vue +12 -0
  112. package/edit/workload/mixins/workload.js +1 -1
  113. package/initialize/App.js +25 -71
  114. package/initialize/client.js +21 -162
  115. package/initialize/index.js +27 -123
  116. package/list/management.cattle.io.feature.vue +1 -7
  117. package/list/node.vue +1 -0
  118. package/machine-config/__tests__/vmwarevsphere.test.ts +100 -21
  119. package/machine-config/vmwarevsphere.vue +73 -51
  120. package/middleware/authenticated.js +10 -17
  121. package/mixins/auth-config.js +2 -7
  122. package/mixins/brand.js +29 -41
  123. package/mixins/create-edit-view/index.js +2 -2
  124. package/mixins/labeled-form-element.ts +6 -1
  125. package/models/__tests__/management.cattle.io.node.ts +85 -0
  126. package/models/__tests__/management.cattle.io.nodepool.ts +83 -0
  127. package/models/__tests__/namespace.test.ts +49 -9
  128. package/models/__tests__/workload.test.ts +91 -0
  129. package/models/cluster/node.js +4 -4
  130. package/models/cluster.x-k8s.io.machinedeployment.js +14 -0
  131. package/models/fleet.cattle.io.cluster.js +4 -0
  132. package/models/fleet.cattle.io.gitrepo.js +56 -13
  133. package/models/management.cattle.io.kontainerdriver.js +1 -1
  134. package/models/management.cattle.io.node.js +18 -14
  135. package/models/management.cattle.io.nodepool.js +17 -0
  136. package/models/namespace.js +1 -1
  137. package/models/pod.js +20 -0
  138. package/models/provisioning.cattle.io.cluster.js +20 -3
  139. package/models/secret.js +117 -18
  140. package/models/workload.js +16 -0
  141. package/models/workload.service.js +18 -0
  142. package/package.json +10 -9
  143. package/pages/about.vue +0 -1
  144. package/pages/account/create-key.vue +0 -1
  145. package/pages/account/index.vue +0 -1
  146. package/pages/auth/login.vue +0 -1
  147. package/pages/auth/logout.vue +0 -2
  148. package/pages/auth/setup.vue +0 -4
  149. package/pages/auth/verify.vue +14 -8
  150. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  151. package/pages/c/_cluster/apps/index.vue +0 -2
  152. package/pages/c/_cluster/auth/index.vue +0 -2
  153. package/pages/c/_cluster/ecm/index.vue +0 -2
  154. package/pages/c/_cluster/explorer/index.vue +28 -2
  155. package/pages/c/_cluster/fleet/index.vue +1 -1
  156. package/pages/c/_cluster/index.vue +0 -2
  157. package/pages/c/_cluster/settings/banners.vue +0 -2
  158. package/pages/c/_cluster/settings/brand.vue +0 -2
  159. package/pages/c/_cluster/settings/index.vue +0 -2
  160. package/pages/c/_cluster/settings/links.vue +0 -1
  161. package/pages/c/_cluster/settings/performance.vue +0 -1
  162. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +2 -1
  163. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +10 -46
  164. package/pages/c/_cluster/uiplugins/index.vue +0 -2
  165. package/pages/diagnostic.vue +1 -2
  166. package/pages/fail-whale.vue +0 -1
  167. package/pages/prefs.vue +0 -1
  168. package/pages/support/index.vue +2 -8
  169. package/pkg/auto-import.js +1 -1
  170. package/plugins/axios.js +0 -36
  171. package/plugins/back-button.js +3 -5
  172. package/plugins/codemirror-loader.js +1 -1
  173. package/plugins/codemirror.js +41 -0
  174. package/plugins/dashboard-store/__tests__/{mutations.spec.ts → mutations.test.ts} +1 -1
  175. package/plugins/dashboard-store/__tests__/resource-class.test.ts +49 -0
  176. package/plugins/dashboard-store/__tests__/utils/store-mocks.ts +7 -0
  177. package/plugins/dashboard-store/actions.js +30 -4
  178. package/plugins/dashboard-store/classify.js +1 -18
  179. package/plugins/dashboard-store/getters.js +10 -5
  180. package/plugins/dashboard-store/index.js +0 -12
  181. package/plugins/dashboard-store/mutations.js +0 -4
  182. package/plugins/dashboard-store/resource-class.js +59 -18
  183. package/plugins/steve/__tests__/steve-class.spec.ts +59 -0
  184. package/plugins/steve/__tests__/utils/steve-mocks.ts +31 -0
  185. package/plugins/steve/getters.js +4 -1
  186. package/plugins/steve/norman-class.js +19 -0
  187. package/plugins/steve/steve-class.js +22 -0
  188. package/plugins/steve/subscribe.js +4 -10
  189. package/rancher-components/Accordion/Accordion.test.ts +45 -0
  190. package/rancher-components/Accordion/Accordion.vue +86 -0
  191. package/rancher-components/Accordion/index.ts +1 -0
  192. package/rancher-components/BadgeState/BadgeState.vue +3 -3
  193. package/rancher-components/Banner/Banner.vue +2 -2
  194. package/rancher-components/Card/Card.vue +3 -3
  195. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  196. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  197. package/rancher-components/Form/LabeledInput/LabeledInput.vue +65 -24
  198. package/rancher-components/Form/Radio/RadioButton.test.ts +7 -3
  199. package/rancher-components/Form/Radio/RadioButton.vue +13 -7
  200. package/rancher-components/Form/Radio/RadioGroup.test.ts +30 -0
  201. package/rancher-components/Form/Radio/RadioGroup.vue +8 -3
  202. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
  203. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
  204. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +9 -4
  205. package/rancher-components/StringList/StringList.test.ts +270 -0
  206. package/rancher-components/StringList/StringList.vue +65 -26
  207. package/rancher-components/components/Accordion/Accordion.test.ts +45 -0
  208. package/rancher-components/components/Accordion/Accordion.vue +86 -0
  209. package/rancher-components/components/Accordion/index.ts +1 -0
  210. package/rancher-components/components/BadgeState/BadgeState.vue +3 -3
  211. package/rancher-components/components/Banner/Banner.vue +2 -2
  212. package/rancher-components/components/Card/Card.vue +3 -3
  213. package/rancher-components/components/Form/Checkbox/Checkbox.vue +3 -3
  214. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +18 -1
  215. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +57 -24
  216. package/rancher-components/components/Form/Radio/RadioButton.vue +13 -7
  217. package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -3
  218. package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +6 -4
  219. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +7 -4
  220. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +9 -4
  221. package/rancher-components/components/StringList/StringList.vue +8 -8
  222. package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +50 -0
  223. package/scripts/extension/parse-tag-name +2 -2
  224. package/scripts/publish-shell.sh +10 -0
  225. package/scripts/test-plugins-build.sh +85 -9
  226. package/server/har-file.js +183 -0
  227. package/store/catalog.js +1 -1
  228. package/store/features.js +1 -0
  229. package/store/i18n.js +11 -0
  230. package/store/index.js +10 -11
  231. package/store/prefs.js +33 -35
  232. package/store/type-map.js +8 -7
  233. package/tsconfig.json +35 -9
  234. package/tsconfig.paths.json +21 -0
  235. package/types/shell/index.d.ts +427 -234
  236. package/types/vue-shim.d.ts +42 -0
  237. package/utils/__tests__/create-yaml.test.ts +60 -0
  238. package/utils/axios.js +0 -19
  239. package/utils/azure.js +24 -0
  240. package/utils/create-yaml.js +17 -10
  241. package/utils/git.ts +1 -1
  242. package/utils/monitoring.js +1 -1
  243. package/utils/nuxt.js +18 -39
  244. package/utils/object.js +14 -0
  245. package/utils/router.scrollBehavior.js +12 -14
  246. package/utils/time.js +1 -1
  247. package/utils/url.ts +1 -1
  248. package/vue.config.js +23 -2
  249. package/.DS_Store +0 -0
  250. package/assets/images/providers/aks-black.svg +0 -28
  251. package/assets/images/providers/aks.svg +0 -31
  252. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -234
  253. package/initialize/layouts.ts +0 -26
  254. package/mixins/fetch.server.js +0 -73
  255. package/pages/c/index.vue +0 -9
  256. package/pages/rio/mesh.vue +0 -508
  257. package/plugins/transitions.js +0 -4
  258. package/scripts/.DS_Store +0 -0
  259. package/scripts/verdaccio.log +0 -205
  260. package/tsconfig.default.json +0 -46
  261. package/yarn-error.log +0 -200
  262. /package/components/form/__tests__/{NameNsDescription.ts → NameNsDescription.test.ts} +0 -0
  263. /package/edit/networking.k8s.io.networkpolicy/__tests__/utils/{selectors.ts → selectors.test.ts} +0 -0
  264. /package/edit/provisioning.cattle.io.cluster/{AgentConfiguration.vue → tabs/AgentConfiguration.vue} +0 -0
  265. /package/edit/provisioning.cattle.io.cluster/{MemberRoles.vue → tabs/MemberRoles.vue} +0 -0
  266. /package/edit/provisioning.cattle.io.cluster/{S3Config.vue → tabs/etcd/S3Config.vue} +0 -0
  267. /package/edit/provisioning.cattle.io.cluster/{ACE.vue → tabs/networking/ACE.vue} +0 -0
  268. /package/edit/provisioning.cattle.io.cluster/{RegistryConfigs.vue → tabs/registries/RegistryConfigs.vue} +0 -0
  269. /package/edit/provisioning.cattle.io.cluster/{RegistryMirrors.vue → tabs/registries/RegistryMirrors.vue} +0 -0
  270. /package/edit/provisioning.cattle.io.cluster/{DrainOptions.vue → tabs/upgrade/DrainOptions.vue} +0 -0
  271. /package/plugins/dashboard-store/__tests__/{actions.spec.ts → actions.test.ts} +0 -0
  272. /package/plugins/dashboard-store/__tests__/{getters.spec.ts → getters.test.ts} +0 -0
  273. /package/rancher-components/BadgeState/{BadgeState.spec.ts → BadgeState.test.ts} +0 -0
  274. /package/rancher-components/components/BadgeState/{BadgeState.spec.ts → BadgeState.test.ts} +0 -0
@@ -23,10 +23,6 @@ function registerType(state, type) {
23
23
  // Not enumerable so they don't get sent back to the client for SSR
24
24
  Object.defineProperty(cache, 'map', { value: new Map() });
25
25
 
26
- if ( process.server && !cache.list.__rehydrateAll ) {
27
- Object.defineProperty(cache.list, '__rehydrateAll', { value: `${ state.config.namespace }/${ type }`, enumerable: true });
28
- }
29
-
30
26
  Vue.set(state.types, type, cache);
31
27
  }
32
28
 
@@ -105,6 +105,7 @@ export const STATES_ENUM = {
105
105
  ERRORING: 'erroring',
106
106
  ERRORS: 'errors',
107
107
  EXPIRED: 'expired',
108
+ EXPIRING: 'expiring',
108
109
  FAIL: 'fail',
109
110
  FAILED: 'failed',
110
111
  HEALTHY: 'healthy',
@@ -167,6 +168,13 @@ export const STATES_ENUM = {
167
168
  WARNING: 'warning',
168
169
  };
169
170
 
171
+ export function mapStateToEnum(statusString) {
172
+ // e.g. in fleet Status is Capitalized. This function will map it to the enum
173
+ return Object.values(STATES_ENUM).find((val) => {
174
+ return val.toLowerCase() === statusString.toLocaleLowerCase();
175
+ });
176
+ }
177
+
170
178
  export const STATES = {
171
179
  [STATES_ENUM.IN_USE]: {
172
180
  color: 'success', icon: 'dot-open', label: 'In Use', compoundIcon: 'checkmark'
@@ -253,7 +261,10 @@ export const STATES = {
253
261
  color: 'error', icon: 'error', label: 'Errors', compoundIcon: 'error'
254
262
  },
255
263
  [STATES_ENUM.EXPIRED]: {
256
- color: 'warning', icon: 'error', label: 'Expired', compoundIcon: 'warning'
264
+ color: 'error', icon: 'error', label: 'Expired', compoundIcon: 'warning'
265
+ },
266
+ [STATES_ENUM.EXPIRING]: {
267
+ color: 'warning', icon: 'error', label: 'Expiring', compoundIcon: 'error'
257
268
  },
258
269
  [STATES_ENUM.FAIL]: {
259
270
  color: 'error', icon: 'error', label: 'Fail', compoundIcon: 'error'
@@ -512,6 +523,28 @@ export function stateDisplay(state) {
512
523
  return key.split(/-/).map(ucFirst).join('-');
513
524
  }
514
525
 
526
+ export function primaryDisplayStatusFromCount(status) {
527
+ const statusOrder = [
528
+ STATES_ENUM.ERROR,
529
+ STATES_ENUM.FAILED,
530
+ STATES_ENUM.WARNING,
531
+ STATES_ENUM.MODIFIED,
532
+ STATES_ENUM.WAIT_APPLIED,
533
+ STATES_ENUM.ORPHANED,
534
+ STATES_ENUM.MISSING,
535
+ STATES_ENUM.UNKNOWN,
536
+ STATES_ENUM.NOT_READY,
537
+ STATES_ENUM.READY,
538
+ ];
539
+
540
+ // sort status by order of statusOrder
541
+ const existingStatuses = Object.keys(status).filter((key) => {
542
+ return status[key] > 0 && statusOrder.includes(key.toLowerCase());
543
+ }).sort((a, b) => statusOrder.indexOf(a.toLowerCase()) - statusOrder.indexOf(b.toLowerCase()));
544
+
545
+ return existingStatuses[0] ? existingStatuses[0] : STATES_ENUM.UNKNOWN;
546
+ }
547
+
515
548
  export function stateSort(color, display) {
516
549
  color = color.replace(/^(text|bg)-/, '');
517
550
 
@@ -1089,6 +1122,16 @@ export default class Resource {
1089
1122
  return this._save(...arguments);
1090
1123
  }
1091
1124
 
1125
+ /**
1126
+ * Remove any unwanted properties from the object that will be saved
1127
+ */
1128
+ cleanForSave(data, forNew) {
1129
+ delete data.__rehydrate;
1130
+ delete data.__clone;
1131
+
1132
+ return data;
1133
+ }
1134
+
1092
1135
  /**
1093
1136
  * Allow to handle the response of the save request
1094
1137
  * @param {*} res Full request response
@@ -1096,9 +1139,6 @@ export default class Resource {
1096
1139
  processSaveResponse(res) { }
1097
1140
 
1098
1141
  async _save(opt = {}) {
1099
- delete this.__rehydrate;
1100
- delete this.__clone;
1101
-
1102
1142
  const forNew = !this.id;
1103
1143
 
1104
1144
  const errors = await this.validationErrors(this, opt.ignoreFields);
@@ -1145,22 +1185,24 @@ export default class Resource {
1145
1185
  // @TODO remove this once the API maps steve _type <-> k8s type in both directions
1146
1186
  opt.data = this.toSave() || { ...this };
1147
1187
 
1148
- if (opt?.data._type) {
1188
+ if (opt.data._type) {
1149
1189
  opt.data.type = opt.data._type;
1150
1190
  }
1151
1191
 
1152
- if (opt?.data._name) {
1192
+ if (opt.data._name) {
1153
1193
  opt.data.name = opt.data._name;
1154
1194
  }
1155
1195
 
1156
- if (opt?.data._labels) {
1196
+ if (opt.data._labels) {
1157
1197
  opt.data.labels = opt.data._labels;
1158
1198
  }
1159
1199
 
1160
- if (opt?.data._annotations) {
1200
+ if (opt.data._annotations) {
1161
1201
  opt.data.annotations = opt.data._annotations;
1162
1202
  }
1163
1203
 
1204
+ opt.data = this.cleanForSave(opt.data, forNew);
1205
+
1164
1206
  // handle "replace" opt as a query param _replace=true for norman PUT requests
1165
1207
  if (opt?.replace && opt.method === 'put') {
1166
1208
  const argParam = opt.url.includes('?') ? '&' : '?';
@@ -1218,19 +1260,11 @@ export default class Resource {
1218
1260
  // ------------------------------------------------------------------
1219
1261
 
1220
1262
  currentRoute() {
1221
- if ( process.server ) {
1222
- return this.$rootState.$route;
1223
- } else {
1224
- return window.$nuxt.$route;
1225
- }
1263
+ return window.$nuxt.$route;
1226
1264
  }
1227
1265
 
1228
1266
  currentRouter() {
1229
- if ( process.server ) {
1230
- return this.$rootState.$router;
1231
- } else {
1232
- return window.$nuxt.$router;
1233
- }
1267
+ return window.$nuxt.$router;
1234
1268
  }
1235
1269
 
1236
1270
  get listLocation() {
@@ -1931,4 +1965,11 @@ export default class Resource {
1931
1965
  get creationTimestamp() {
1932
1966
  return this.metadata?.creationTimestamp;
1933
1967
  }
1968
+
1969
+ /**
1970
+ * Allows model to specify JSON Paths that should be folded in the YAML editor by default
1971
+ */
1972
+ get yamlFolding() {
1973
+ return [];
1974
+ }
1934
1975
  }
@@ -0,0 +1,59 @@
1
+ import Steve from '@shell/plugins/steve/steve-class.js';
2
+ import { steveClassJunkObject } from './utils/steve-mocks';
3
+
4
+ describe('class: Steve', () => {
5
+ describe('given custom resource keys', () => {
6
+ const customResource = steveClassJunkObject;
7
+
8
+ it('should keep internal keys', () => {
9
+ const steve = new Steve(customResource, {
10
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
11
+ dispatch: jest.fn(),
12
+ rootGetters: { 'i18n/t': jest.fn() },
13
+ });
14
+
15
+ expect({ ...steve }).toStrictEqual(customResource);
16
+ });
17
+
18
+ describe('method: save', () => {
19
+ it('should remove all the internal keys', async() => {
20
+ const dispatch = jest.fn();
21
+ const steve = new Steve(customResource, {
22
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
23
+ dispatch,
24
+ rootGetters: { 'i18n/t': jest.fn() },
25
+ });
26
+
27
+ const expectation = {
28
+ type: customResource.type,
29
+ metadata: {
30
+ resourceVersion: 'whatever',
31
+ fields: 'whatever',
32
+ clusterName: 'whatever',
33
+ deletionGracePeriodSeconds: 'whatever',
34
+ generateName: 'whatever',
35
+ },
36
+ spec: { versions: {} }
37
+ };
38
+
39
+ await steve.save();
40
+
41
+ const opt = {
42
+ data: expectation,
43
+ headers: {
44
+ accept: 'application/json',
45
+ 'content-type': 'application/json',
46
+ },
47
+ method: 'post',
48
+ url: undefined,
49
+ };
50
+
51
+ // Data sent should have been cleaned
52
+ expect(dispatch).toHaveBeenCalledWith('request', { opt, type: customResource.type });
53
+
54
+ // Original workload model should remain unchanged
55
+ expect({ ...steve }).toStrictEqual(customResource);
56
+ });
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,31 @@
1
+ import { resourceClassJunkObject } from '@shell/plugins/dashboard-store/__tests__/utils/store-mocks';
2
+
3
+ const customType = 'asdasd';
4
+
5
+ export const steveClassJunkObject = {
6
+ ...resourceClassJunkObject,
7
+ type: customType,
8
+ __clone: 'whatever',
9
+ metadata: {
10
+ clusterName: 'whatever',
11
+ creationTimestamp: 'whatever',
12
+ deletionGracePeriodSeconds: 'whatever',
13
+ deletionTimestamp: 'whatever',
14
+ fields: 'whatever',
15
+ finalizers: 'whatever',
16
+ generateName: 'whatever',
17
+ generation: 'whatever',
18
+ initializers: 'whatever',
19
+ managedFields: 'whatever',
20
+ ownerReferences: 'whatever',
21
+ relationships: 'whatever',
22
+ selfLink: 'whatever',
23
+ state: 'whatever',
24
+ uid: 'whatever',
25
+ resourceVersion: 'whatever',
26
+ },
27
+ spec: { versions: { schema: 'whatever' } },
28
+ links: 'whatever',
29
+ status: 'whatever',
30
+ stringData: 'whatever',
31
+ };
@@ -24,11 +24,14 @@ const GC_IGNORE_TYPES = {
24
24
  [UI.NAV_LINK]: true,
25
25
  };
26
26
 
27
+ // Include calls to /v1 AND /k8s/clusters/<cluster id>/v1
28
+ const steveRegEx = new RegExp('(/v1)|(\/k8s\/clusters\/[a-z0-9-]+\/v1)');
29
+
27
30
  export default {
28
31
  urlOptions: () => (url, opt) => {
29
32
  opt = opt || {};
30
33
  const parsedUrl = parse(url);
31
- const isSteve = parsedUrl.path.startsWith('/v1');
34
+ const isSteve = steveRegEx.test(parsedUrl.path);
32
35
 
33
36
  // Filter
34
37
  if ( opt.filter ) {
@@ -3,6 +3,7 @@ import pickBy from 'lodash/pickBy';
3
3
  import Vue from 'vue';
4
4
  import { matchesSomeRegex } from '@shell/utils/string';
5
5
  import Resource from '@shell/plugins/dashboard-store/resource-class';
6
+ import { findBy } from '@shell/utils/array';
6
7
 
7
8
  export default class NormanModel extends Resource {
8
9
  setLabels(val) {
@@ -56,4 +57,22 @@ export default class NormanModel extends Resource {
56
57
  Vue.set(this, key, { ...spec[key] });
57
58
  });
58
59
  }
60
+
61
+ isCondition(condition, withStatus = 'True') {
62
+ if ( !this.conditions ) {
63
+ return false;
64
+ }
65
+
66
+ const entry = findBy((this.conditions || []), 'type', condition);
67
+
68
+ if ( !entry ) {
69
+ return false;
70
+ }
71
+
72
+ if ( !withStatus ) {
73
+ return true;
74
+ }
75
+
76
+ return (entry.status || '').toLowerCase() === `${ withStatus }`.toLowerCase();
77
+ }
59
78
  }
@@ -1,5 +1,17 @@
1
1
  import { DESCRIPTION } from '@shell/config/labels-annotations';
2
2
  import HybridModel from './hybrid-class';
3
+ import { NEVER_ADD } from '@shell/utils/create-yaml';
4
+ import { deleteProperty } from '@shell/utils/object';
5
+
6
+ // Some fields that are removed for YAML (NEVER_ADD) are required via API
7
+ const STEVE_ADD = [
8
+ 'metadata.resourceVersion',
9
+ 'metadata.fields',
10
+ 'metadata.clusterName',
11
+ 'metadata.deletionGracePeriodSeconds',
12
+ 'metadata.generateName',
13
+ ];
14
+ const STEVE_NEVER_SAVE = NEVER_ADD.filter((na) => !STEVE_ADD.includes(na));
3
15
 
4
16
  export default class SteveModel extends HybridModel {
5
17
  get name() {
@@ -28,4 +40,14 @@ export default class SteveModel extends HybridModel {
28
40
 
29
41
  this._description = value;
30
42
  }
43
+
44
+ cleanForSave(data, forNew) {
45
+ const val = super.cleanForSave(data);
46
+
47
+ for (const field of STEVE_NEVER_SAVE) {
48
+ deleteProperty(val, field);
49
+ }
50
+
51
+ return val;
52
+ }
31
53
  }
@@ -332,10 +332,6 @@ const sharedActions = {
332
332
 
333
333
  commit('setWantSocket', true);
334
334
 
335
- if ( process.server ) {
336
- return;
337
- }
338
-
339
335
  state.debugSocket && console.info(`Subscribe [${ getters.storeName }]`); // eslint-disable-line no-console
340
336
 
341
337
  const url = `${ state.config.baseUrl }/subscribe`;
@@ -601,7 +597,7 @@ const defaultActions = {
601
597
  },
602
598
 
603
599
  rehydrateSubscribe({ state, dispatch }) {
604
- if ( process.client && state.wantSocket && !state.socket ) {
600
+ if ( state.wantSocket && !state.socket ) {
605
601
  dispatch('subscribe');
606
602
  }
607
603
  },
@@ -728,11 +724,9 @@ const defaultActions = {
728
724
  }
729
725
 
730
726
  // Try resending any frames that were attempted to be sent while the socket was down, once.
731
- if ( !process.server ) {
732
- for ( const obj of state.pendingFrames.slice() ) {
733
- commit('dequeuePendingFrame', obj);
734
- dispatch('sendImmediate', obj);
735
- }
727
+ for ( const obj of state.pendingFrames.slice() ) {
728
+ commit('dequeuePendingFrame', obj);
729
+ dispatch('sendImmediate', obj);
736
730
  }
737
731
  },
738
732
 
@@ -0,0 +1,45 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { Accordion } from './index';
3
+
4
+ describe('component: Accordion', () => {
5
+ it('is closed initially by default', () => {
6
+ const title = 'Test Title';
7
+
8
+ const wrapper = shallowMount(Accordion, { propsData: { title } });
9
+
10
+ expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(false);
11
+ expect(wrapper.find('[data-testid="accordion-header"]').text()).toBe(title);
12
+ });
13
+
14
+ it('is opened initially when openInitially prop is true', () => {
15
+ const wrapper = shallowMount(Accordion, { propsData: { openInitially: true } });
16
+
17
+ expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(true);
18
+ });
19
+
20
+ it('when closed, opens when the header is clicked', async() => {
21
+ const wrapper = shallowMount(Accordion, { });
22
+
23
+ await wrapper.find('[data-testid="accordion-header"]').trigger('click');
24
+ expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(true);
25
+ });
26
+
27
+ it('when open, closes when the header is clicked', async() => {
28
+ const wrapper = shallowMount(Accordion, { propsData: { openInitially: true } });
29
+
30
+ await wrapper.find('[data-testid="accordion-header"]').trigger('click');
31
+ expect(wrapper.find('[data-testid="accordion-body"]').isVisible()).toBe(false);
32
+ });
33
+
34
+ it('displays a chevron when closed', async() => {
35
+ const wrapper = shallowMount(Accordion, { propsData: { } });
36
+
37
+ expect(wrapper.find('[data-testid="accordion-header"] .icon-chevron-up').exists()).toBe(true);
38
+ });
39
+
40
+ it('displays an inverted chevron when open', async() => {
41
+ const wrapper = shallowMount(Accordion, { propsData: { openInitially: true } });
42
+
43
+ expect(wrapper.find('[data-testid="accordion-header"] .icon-chevron-down').exists()).toBe(true);
44
+ });
45
+ });
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import { defineComponent } from 'vue';
3
+ import { mapGetters } from 'vuex';
4
+
5
+ export default defineComponent({
6
+ props: {
7
+ title: {
8
+ type: String,
9
+ default: ''
10
+ },
11
+
12
+ titleKey: {
13
+ type: String,
14
+ default: null
15
+ },
16
+
17
+ openInitially: {
18
+ type: Boolean,
19
+ default: false
20
+ }
21
+ },
22
+
23
+ data() {
24
+ return { isOpen: this.openInitially };
25
+ },
26
+
27
+ computed: { ...mapGetters({ t: 'i18n/t' }) },
28
+
29
+ methods: {
30
+ toggle() {
31
+ this.isOpen = !this.isOpen;
32
+ }
33
+ }
34
+ });
35
+ </script>
36
+
37
+ <template>
38
+ <div class="accordion-container">
39
+ <div
40
+ class="accordion-header"
41
+ data-testid="accordion-header"
42
+ @click="toggle"
43
+ >
44
+ <i
45
+ class="icon text-primary"
46
+ :class="{'icon-chevron-down':isOpen, 'icon-chevron-up':!isOpen}"
47
+ data-testid="accordion-chevron"
48
+ />
49
+ <slot name="header">
50
+ <h4
51
+ data-testid="accordion-title-slot-content"
52
+ class="mb-0"
53
+ >
54
+ {{ titleKey ? t(titleKey) : title }}
55
+ </h4>
56
+ </slot>
57
+ </div>
58
+ <div
59
+ v-show="isOpen"
60
+ class="accordion-body"
61
+ data-testid="accordion-body"
62
+ >
63
+ <slot />
64
+ </div>
65
+ </div>
66
+ </template>
67
+
68
+ <style lang="scss" scoped>
69
+ .accordion-container {
70
+ border: 1px solid var(--border)
71
+ }
72
+ .accordion-header {
73
+ padding: 5px;
74
+ display: flex;
75
+ align-items: center;
76
+ &>*{
77
+ padding: 5px 0px 5px 0px;
78
+ }
79
+ I {
80
+ margin: 0px 10px 0px 10px;
81
+ }
82
+ }
83
+ .accordion-body {
84
+ padding: 10px;
85
+ }
86
+ </style>
@@ -0,0 +1 @@
1
+ export { default as Accordion } from './Accordion.vue';
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import Vue, { PropType } from 'vue';
2
+ import { PropType, defineComponent } from 'vue';
3
3
 
4
4
  interface Badge {
5
5
  stateBackground: string;
@@ -11,7 +11,7 @@ interface Badge {
11
11
  * <p>Represents a badge whose label and color is either taken from the value property or
12
12
  * from the label and color properties. The state property takes precedence.</p>
13
13
  */
14
- export default Vue.extend({
14
+ export default defineComponent({
15
15
  props: {
16
16
  /**
17
17
  * A value having the properties `stateBackground` and `stateDisplay`
@@ -59,7 +59,7 @@ export default Vue.extend({
59
59
  </script>
60
60
 
61
61
  <template>
62
- <span :class="{'badge-state': true, [bg]: true}">
62
+ <span :class="['badge-state', bg]">
63
63
  <i
64
64
  v-if="icon"
65
65
  class="icon"
@@ -1,9 +1,9 @@
1
1
  <script lang="ts">
2
- import Vue from 'vue';
2
+ import { defineComponent } from 'vue';
3
3
  import { nlToBr } from '@shell/utils/string';
4
4
  import { stringify } from '@shell/utils/error';
5
5
 
6
- export default Vue.extend({
6
+ export default defineComponent({
7
7
  props: {
8
8
  /**
9
9
  * A color class that represents the color of the banner.
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
- import Vue from 'vue';
2
+ import { defineComponent, PropType } from 'vue';
3
3
 
4
- export default Vue.extend({
4
+ export default defineComponent({
5
5
  name: 'Card',
6
6
  props: {
7
7
  /**
@@ -22,7 +22,7 @@ export default Vue.extend({
22
22
  * The function to invoke when the default action button is clicked.
23
23
  */
24
24
  buttonAction: {
25
- type: Function,
25
+ type: Function as PropType<(event: MouseEvent) => void>,
26
26
  default: (): void => { }
27
27
  },
28
28
  /**
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
- import Vue, { PropType } from 'vue';
2
+ import { PropType, defineComponent } from 'vue';
3
3
  import { _EDIT, _VIEW } from '@shell/config/query-params';
4
4
  import { addObject, removeObject } from '@shell/utils/array';
5
5
  import cloneDeep from 'lodash/cloneDeep';
6
6
 
7
- export default Vue.extend({
7
+ export default defineComponent({
8
8
  name: 'Checkbox',
9
9
 
10
10
  props: {
@@ -140,7 +140,7 @@ export default Vue.extend({
140
140
  /**
141
141
  * Toggles the checked state for the checkbox and emits an 'input' event.
142
142
  */
143
- clicked(event: MouseEvent): boolean | void {
143
+ clicked(event: MouseEvent | KeyboardEvent): boolean | void {
144
144
  if ((event.target as HTMLLinkElement).tagName === 'A' && (event.target as HTMLLinkElement).href) {
145
145
  // Ignore links inside the checkbox label so you can click them
146
146
  return true;
@@ -1,4 +1,3 @@
1
-
2
1
  import { mount } from '@vue/test-utils';
3
2
  import { LabeledInput } from './index';
4
3
 
@@ -20,4 +19,22 @@ describe('component: LabeledInput', () => {
20
19
  expect(wrapper.emitted('input')).toHaveLength(1);
21
20
  expect(wrapper.emitted('input')![0][0]).toBe(value);
22
21
  });
22
+
23
+ it('using mode "multiline" should emit input value correctly', () => {
24
+ const value = 'any-string';
25
+ const delay = 1;
26
+ const wrapper = mount(LabeledInput as any, {
27
+ propsData: { delay, multiline: true },
28
+ mocks: { $store: { getters: { 'i18n/t': jest.fn() } } }
29
+ });
30
+
31
+ jest.useFakeTimers();
32
+ wrapper.find('input').setValue('1');
33
+ wrapper.find('input').setValue(value);
34
+ jest.advanceTimersByTime(delay);
35
+ jest.useRealTimers();
36
+
37
+ expect(wrapper.emitted('input')).toHaveLength(1);
38
+ expect(wrapper.emitted('input')![0][0]).toBe(value);
39
+ });
23
40
  });