@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.8

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 (243) hide show
  1. package/assets/brand/classic/metadata.json +3 -0
  2. package/assets/styles/app.scss +1 -0
  3. package/assets/styles/base/_color.scss +16 -0
  4. package/assets/styles/base/_helpers.scss +10 -0
  5. package/assets/styles/base/_variables.scss +18 -12
  6. package/assets/styles/fonts/_icons.scss +1 -32
  7. package/assets/styles/global/_layout.scss +1 -1
  8. package/assets/styles/themes/_dark.scss +262 -258
  9. package/assets/styles/themes/_light.scss +538 -509
  10. package/assets/styles/themes/_modern.scss +914 -0
  11. package/assets/translations/en-us.yaml +110 -29
  12. package/chart/__tests__/S3.test.ts +2 -1
  13. package/cloud-credential/generic.vue +18 -10
  14. package/cloud-credential/harvester.vue +1 -9
  15. package/components/AdvancedSection.vue +8 -0
  16. package/components/ChartReadme.vue +17 -7
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/Drawer/Chrome.vue +0 -1
  19. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +27 -28
  20. package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -24
  21. package/components/Drawer/ResourceDetailDrawer/index.vue +18 -4
  22. package/components/InstallHelmCharts.vue +656 -0
  23. package/components/LazyImage.vue +60 -4
  24. package/components/Loading.vue +1 -1
  25. package/components/LocaleSelector.vue +7 -2
  26. package/components/Markdown.vue +4 -0
  27. package/components/PaginatedResourceTable.vue +46 -1
  28. package/components/PromptRestore.vue +22 -44
  29. package/components/Resource/Detail/Masthead/composable.ts +16 -0
  30. package/components/Resource/Detail/Masthead/index.vue +37 -0
  31. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
  32. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +26 -7
  33. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
  34. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
  35. package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
  36. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
  37. package/components/Resource/Detail/Metadata/composables.ts +9 -7
  38. package/components/Resource/Detail/Metadata/index.vue +17 -2
  39. package/components/Resource/Detail/Page.vue +35 -21
  40. package/components/Resource/Detail/SpacedRow.vue +1 -1
  41. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
  42. package/components/Resource/Detail/TitleBar/composables.ts +5 -5
  43. package/components/Resource/Detail/TitleBar/index.vue +12 -3
  44. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  45. package/components/ResourceDetail/index.vue +569 -72
  46. package/components/ResourceList/index.vue +1 -0
  47. package/components/ResourceTable.vue +6 -1
  48. package/components/ResourceYaml.vue +1 -1
  49. package/components/RichTranslation.vue +106 -0
  50. package/components/SlideInPanelManager.vue +13 -10
  51. package/components/SortableTable/index.vue +5 -5
  52. package/components/SortableTable/selection.js +0 -1
  53. package/components/Tabbed/index.vue +35 -4
  54. package/components/__tests__/LazyImage.spec.ts +121 -0
  55. package/components/__tests__/PromptRestore.test.ts +1 -65
  56. package/components/__tests__/RichTranslation.test.ts +115 -0
  57. package/components/fleet/FleetStatus.vue +4 -0
  58. package/components/fleet/dashboard/ResourcePanel.vue +2 -1
  59. package/components/form/ClusterAppearance.vue +5 -0
  60. package/components/form/FileImageSelector.vue +1 -1
  61. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  62. package/components/form/NameNsDescription.vue +1 -0
  63. package/components/form/Networking.vue +24 -19
  64. package/components/form/ProjectMemberEditor.vue +1 -1
  65. package/components/form/ResourceLabeledSelect.vue +22 -8
  66. package/components/form/ResourceTabs/index.vue +20 -0
  67. package/components/form/SecretSelector.vue +9 -0
  68. package/components/form/SelectOrCreateAuthSecret.vue +6 -3
  69. package/components/form/__tests__/Networking.test.ts +116 -0
  70. package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
  71. package/components/formatter/FleetApplicationSource.vue +25 -17
  72. package/components/formatter/PodImages.vue +1 -1
  73. package/components/formatter/__tests__/LiveDate.test.ts +10 -2
  74. package/components/google/AccountAccess.vue +44 -46
  75. package/components/nav/Favorite.vue +4 -0
  76. package/components/nav/Group.vue +4 -1
  77. package/components/nav/NotificationCenter/Notification.vue +1 -27
  78. package/components/nav/WindowManager/index.vue +3 -3
  79. package/composables/resources.ts +2 -2
  80. package/config/labels-annotations.js +3 -2
  81. package/config/pagination-table-headers.js +8 -1
  82. package/config/product/explorer.js +27 -2
  83. package/config/product/manager.js +0 -1
  84. package/config/query-params.js +10 -0
  85. package/config/router/routes.js +21 -1
  86. package/config/system-namespaces.js +1 -1
  87. package/config/table-headers.js +30 -1
  88. package/config/types.js +1 -1
  89. package/config/version.js +1 -1
  90. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
  91. package/detail/__tests__/workload.test.ts +164 -0
  92. package/detail/configmap.vue +33 -75
  93. package/detail/projectsecret.vue +11 -0
  94. package/detail/provisioning.cattle.io.cluster.vue +351 -369
  95. package/detail/secret.vue +49 -308
  96. package/detail/workload/index.vue +38 -21
  97. package/dialog/InstallExtensionDialog.vue +8 -5
  98. package/dialog/RotateEncryptionKeyDialog.vue +10 -30
  99. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  100. package/edit/auth/ldap/__tests__/config.test.ts +14 -0
  101. package/edit/auth/ldap/config.vue +24 -0
  102. package/edit/compliance.cattle.io.clusterscan.vue +1 -1
  103. package/edit/configmap.vue +4 -1
  104. package/edit/fleet.cattle.io.gitrepo.vue +5 -6
  105. package/edit/fleet.cattle.io.helmop.vue +78 -56
  106. package/edit/logging.banzaicloud.io.output/index.vue +1 -1
  107. package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
  108. package/edit/networking.k8s.io.ingress/Certificate.vue +20 -22
  109. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
  110. package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
  111. package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
  112. package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
  113. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
  114. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
  115. package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
  116. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
  117. package/edit/provisioning.cattle.io.cluster/rke2.vue +123 -61
  118. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
  119. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
  120. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
  121. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
  122. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
  124. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +32 -33
  125. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
  126. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
  127. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
  128. package/edit/secret/basic.vue +1 -0
  129. package/edit/secret/index.vue +126 -15
  130. package/edit/workload/index.vue +5 -14
  131. package/list/projectsecret.vue +345 -0
  132. package/list/provisioning.cattle.io.cluster.vue +1 -69
  133. package/list/secret.vue +109 -0
  134. package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
  135. package/machine-config/google.vue +9 -1
  136. package/machine-config/vmwarevsphere.vue +7 -17
  137. package/mixins/__tests__/brand.spec.ts +2 -2
  138. package/mixins/chart.js +0 -2
  139. package/mixins/create-edit-view/impl.js +10 -1
  140. package/mixins/resource-fetch-api-pagination.js +11 -12
  141. package/mixins/resource-fetch.js +3 -1
  142. package/models/__tests__/chart.test.ts +111 -80
  143. package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  144. package/models/__tests__/node.test.ts +7 -63
  145. package/models/catalog.cattle.io.app.js +1 -1
  146. package/models/catalog.cattle.io.operation.js +1 -1
  147. package/models/chart.js +36 -20
  148. package/models/cloudcredential.js +2 -163
  149. package/models/cluster/node.js +7 -7
  150. package/models/cluster.x-k8s.io.machine.js +3 -3
  151. package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
  152. package/models/compliance.cattle.io.clusterscan.js +2 -2
  153. package/models/configmap.js +4 -0
  154. package/models/constraints.gatekeeper.sh.constraint.js +1 -1
  155. package/models/fleet-application.js +0 -17
  156. package/models/fleet.cattle.io.cluster.js +2 -2
  157. package/models/fleet.cattle.io.gitrepo.js +15 -1
  158. package/models/fleet.cattle.io.helmop.js +26 -22
  159. package/models/management.cattle.io.setting.js +4 -0
  160. package/models/persistentvolumeclaim.js +1 -1
  161. package/models/pod.js +2 -2
  162. package/models/provisioning.cattle.io.cluster.js +39 -67
  163. package/models/rke.cattle.io.etcdsnapshot.js +1 -1
  164. package/models/secret.js +161 -2
  165. package/models/storage.k8s.io.storageclass.js +2 -2
  166. package/models/workload.js +3 -3
  167. package/package.json +11 -10
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +422 -174
  172. package/pages/c/_cluster/apps/charts/index.vue +46 -35
  173. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  174. package/pages/c/_cluster/explorer/projectsecret.vue +24 -0
  175. package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
  176. package/pages/c/_cluster/fleet/index.vue +103 -45
  177. package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
  178. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
  179. package/pages/c/_cluster/uiplugins/index.vue +36 -25
  180. package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
  181. package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
  182. package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
  183. package/plugins/dashboard-store/actions.js +42 -22
  184. package/plugins/dashboard-store/normalize.js +29 -17
  185. package/plugins/dashboard-store/resource-class.js +83 -17
  186. package/plugins/steve/__tests__/getters.test.ts +1 -1
  187. package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
  188. package/plugins/steve/getters.js +8 -2
  189. package/plugins/steve/resourceWatcher.js +10 -3
  190. package/plugins/steve/steve-pagination-utils.ts +14 -3
  191. package/plugins/steve/subscribe.js +192 -19
  192. package/plugins/steve/worker/web-worker.advanced.js +2 -0
  193. package/rancher-components/Card/Card.vue +0 -18
  194. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
  195. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
  196. package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
  197. package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
  198. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
  199. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
  200. package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
  201. package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
  202. package/rancher-components/Pill/types.ts +2 -0
  203. package/rancher-components/RcButton/RcButton.vue +1 -1
  204. package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
  205. package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
  206. package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
  207. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
  208. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
  209. package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
  210. package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
  213. package/store/__tests__/catalog.test.ts +93 -1
  214. package/store/aws.js +19 -8
  215. package/store/catalog.js +8 -3
  216. package/types/kube/kube-api.ts +12 -0
  217. package/types/resources/settings.d.ts +1 -1
  218. package/types/shell/index.d.ts +643 -585
  219. package/types/store/pagination.types.ts +16 -6
  220. package/types/uiplugins.ts +73 -0
  221. package/utils/__tests__/back-off.test.ts +354 -0
  222. package/utils/__tests__/create-yaml.test.ts +235 -0
  223. package/utils/__tests__/kontainer.test.ts +19 -0
  224. package/utils/__tests__/uiplugins.test.ts +84 -0
  225. package/utils/back-off.ts +176 -0
  226. package/utils/create-yaml.js +103 -9
  227. package/utils/dynamic-importer.js +8 -0
  228. package/utils/kontainer.ts +3 -5
  229. package/utils/pagination-utils.ts +18 -0
  230. package/utils/style.ts +3 -0
  231. package/utils/uiplugins.ts +29 -2
  232. package/utils/validators/__tests__/setting.test.js +92 -0
  233. package/utils/validators/formRules/__tests__/index.test.ts +88 -7
  234. package/utils/validators/formRules/index.ts +83 -8
  235. package/utils/validators/setting.js +17 -0
  236. package/cloud-credential/__tests__/harvester.test.ts +0 -18
  237. package/components/ResourceDetail/__tests__/index.test.ts +0 -135
  238. package/components/ResourceDetail/legacy.vue +0 -562
  239. package/components/formatter/CloudCredExpired.vue +0 -69
  240. package/models/etcdbackup.js +0 -45
  241. package/pages/explorer/resource/detail/configmap.vue +0 -42
  242. package/pages/explorer/resource/detail/secret.vue +0 -50
  243. package/utils/aws.js +0 -0
@@ -337,7 +337,8 @@ export default {
337
337
  }
338
338
 
339
339
  &.bg-error {
340
- background: var(--click-badge-error-bg);
340
+ // background: var(--click-badge-error-bg); TODO revert after dashboard theme colors refactoring
341
+ background: var(--error);
341
342
  color: var(--click-badge-error);
342
343
  }
343
344
 
@@ -75,6 +75,7 @@ export default {
75
75
  <button
76
76
  :disabled="disable"
77
77
  @click="customBadgeDialog"
78
+ @keydown.enter.space.prevent="customBadgeDialog"
78
79
  >
79
80
  <i class="icon icon-brush-icon" />
80
81
  <span>
@@ -118,6 +119,10 @@ export default {
118
119
  color: var(--link);
119
120
  background: transparent;
120
121
 
122
+ &:focus-visible {
123
+ @include focus-outline;
124
+ }
125
+
121
126
  i {
122
127
  margin-right: 2px;
123
128
  }
@@ -132,7 +132,7 @@ $logo: 60px;
132
132
  left: 0;
133
133
  width: 100%;
134
134
  height: 100%;
135
- background-color: var(--overlay-bg);
135
+ background-color: var(--subtle-overlay-bg);
136
136
  }
137
137
 
138
138
  }
@@ -282,7 +282,7 @@ export default {
282
282
  <i
283
283
  v-if="permission.locked"
284
284
  v-clean-tooltip="permission.tooltip"
285
- class="icon icon-lock icon-fw"
285
+ class="icon icon-lock"
286
286
  />
287
287
  </div>
288
288
  </div>
@@ -429,6 +429,7 @@ export default {
429
429
 
430
430
  <template>
431
431
  <div class="row mb-20">
432
+ <slot name="project-selector" />
432
433
  <div
433
434
  v-if="namespaced && !nameNsHidden && createNamespace"
434
435
  :data-testid="componentTestid + '-namespace-create'"
@@ -31,28 +31,23 @@ export default {
31
31
  const t = this.$store.getters['i18n/t'];
32
32
 
33
33
  return {
34
- dnsPolicy: this.value.dnsPolicy || 'ClusterFirst',
35
- networkMode: this.value.hostNetwork ? { label: t('workload.networking.networkMode.options.hostNetwork'), value: true } : { label: t('workload.networking.networkMode.options.normal'), value: false },
36
- hostAliases: [],
37
- nameservers: null,
38
- searches: null,
39
- hostname: null,
40
- subdomain: null,
41
- options: null,
34
+ dnsPolicy: this.value.dnsPolicy || 'ClusterFirst',
35
+ networkMode: this.value.hostNetwork ? { label: t('workload.networking.networkMode.options.hostNetwork'), value: true } : { label: t('workload.networking.networkMode.options.normal'), value: false },
36
+ tmpHostAliases: [],
37
+ nameservers: null,
38
+ searches: null,
39
+ hostname: null,
40
+ subdomain: null,
41
+ options: null,
42
42
  };
43
43
  },
44
44
 
45
45
  created() {
46
- const hostAliases = (this.value.hostAliases || []).map((entry) => {
47
- return {
48
- ip: entry.ip,
49
- hostnames: entry.hostnames.join(', ')
50
- };
51
- });
46
+ const hostAliases = this.value.hostAliases || [];
52
47
  const { dnsConfig = {}, hostname, subdomain } = this.value;
53
48
  const { nameservers, searches, options } = dnsConfig;
54
49
 
55
- this.hostAliases = hostAliases;
50
+ this.tmpHostAliases = hostAliases;
56
51
  this.nameservers = nameservers;
57
52
  this.searches = searches;
58
53
  this.hostname = hostname;
@@ -89,6 +84,15 @@ export default {
89
84
  ];
90
85
  },
91
86
 
87
+ hostAliases() {
88
+ return (this.value.hostAliases || []).map((entry) => {
89
+ return {
90
+ ip: entry.ip,
91
+ hostnames: entry.hostnames?.join(', ') ?? ''
92
+ };
93
+ });
94
+ },
95
+
92
96
  ...mapGetters({ t: 'i18n/t' })
93
97
  },
94
98
 
@@ -121,12 +125,12 @@ export default {
121
125
 
122
126
  methods: {
123
127
  updateHostAliases(neu) {
124
- this.hostAliases = neu.map((entry) => {
128
+ this.tmpHostAliases = neu.map((entry) => {
125
129
  const ip = entry.ip.trim();
126
130
  const hostnames = entry.hostnames.trim().split(/[\s,]+/).filter((x) => !!x);
127
131
 
128
132
  return { ip, hostnames };
129
- }).filter((entry) => entry.ip && entry.hostnames.length);
133
+ });
130
134
  this.update();
131
135
  },
132
136
 
@@ -142,7 +146,7 @@ export default {
142
146
  dnsConfig,
143
147
  dnsPolicy: this.dnsPolicy,
144
148
  hostname: this.hostname,
145
- hostAliases: this.hostAliases,
149
+ hostAliases: this.tmpHostAliases,
146
150
  subdomain: this.subdomain,
147
151
  hostNetwork: this.networkMode.value
148
152
  };
@@ -256,7 +260,8 @@ export default {
256
260
  <div class="col span-12">
257
261
  <KeyValue
258
262
  key="hostAliases"
259
- v-model:value="hostAliases"
263
+ data-test="hostAliases"
264
+ :value="hostAliases"
260
265
  :mode="mode"
261
266
  :title="t('workload.networking.hostAliases.label')"
262
267
  :protip="t('workload.networking.hostAliases.tip')"
@@ -309,7 +309,7 @@ export default {
309
309
  <i
310
310
  v-if="permission.locked"
311
311
  v-clean-tooltip="permission.tooltip"
312
- class="icon icon-lock icon-fw"
312
+ class="icon icon-lock"
313
313
  />
314
314
  </div>
315
315
  </div>
@@ -34,7 +34,7 @@ export default defineComponent({
34
34
 
35
35
  inStore: {
36
36
  type: String,
37
- default: 'cluster',
37
+ default: undefined,
38
38
  },
39
39
 
40
40
  /**
@@ -63,7 +63,20 @@ export default defineComponent({
63
63
  },
64
64
 
65
65
  data() {
66
- return { paginate: false };
66
+ let validInStore = this.inStore;
67
+
68
+ if (!validInStore && this.resourceType) {
69
+ validInStore = this.$store.getters['currentStore'](this.resourceType);
70
+ }
71
+
72
+ if (!validInStore) {
73
+ validInStore = 'cluster';
74
+ }
75
+
76
+ return {
77
+ paginate: false,
78
+ validInStore,
79
+ };
67
80
  },
68
81
 
69
82
  async fetch() {
@@ -72,13 +85,13 @@ export default defineComponent({
72
85
  this.paginate = false;
73
86
  break;
74
87
  case RESOURCE_LABEL_SELECT_MODE.DYNAMIC:
75
- this.paginate = this.$store.getters[`${ this.inStore }/paginationEnabled`](this.resourceType);
88
+ this.paginate = this.$store.getters[`${ this.validInStore }/paginationEnabled`](this.resourceType);
76
89
  break;
77
90
  }
78
91
 
79
92
  if (!this.paginate) {
80
93
  // The resource won't be paginated and component expects everything up front
81
- await this.$store.dispatch(`${ this.inStore }/findAll`, { type: this.resourceType });
94
+ await this.$store.dispatch(`${ this.validInStore }/findAll`, { type: this.resourceType });
82
95
  }
83
96
  },
84
97
 
@@ -104,7 +117,7 @@ export default defineComponent({
104
117
  return [];
105
118
  }
106
119
 
107
- const all = this.$store.getters[`${ this.inStore }/all`](this.resourceType);
120
+ const all = this.$store.getters[`${ this.validInStore }/all`](this.resourceType);
108
121
 
109
122
  return this.allResourcesSettings?.updateResources ? this.allResourcesSettings.updateResources(all) : all;
110
123
  }
@@ -127,9 +140,10 @@ export default defineComponent({
127
140
  const defaultOptions: LabelSelectPaginationFunctionOptions = {
128
141
  opts,
129
142
  filters,
130
- type: this.resourceType,
131
- ctx: { getters: this.$store.getters, dispatch: this.$store.dispatch },
132
- sort: [{ asc: true, field: 'metadata.name' }],
143
+ type: this.resourceType,
144
+ ctx: { getters: this.$store.getters, dispatch: this.$store.dispatch },
145
+ sort: [{ asc: true, field: 'metadata.name' }],
146
+ store: this.validInStore
133
147
  };
134
148
  const options = this.paginatedResourceSettings?.requestSettings ? this.paginatedResourceSettings.requestSettings(defaultOptions) : defaultOptions;
135
149
  const res = await labelSelectPaginationFunction(options);
@@ -234,6 +234,8 @@ export default {
234
234
 
235
235
  <template>
236
236
  <Tabbed
237
+ class="resource-tabs"
238
+ :class="{[mode]: true}"
237
239
  v-bind="$attrs"
238
240
  :default-tab="defaultTab"
239
241
  :resource="value"
@@ -296,3 +298,21 @@ export default {
296
298
  </Tab>
297
299
  </Tabbed>
298
300
  </template>
301
+
302
+ <style lang="scss" scoped>
303
+ .resource-tabs {
304
+ // For the time being we're only targeting detail pages for the new styling. Remove this if we want this style to apply to all pages.
305
+ &.view {
306
+ :deep() .tabs.horizontal {
307
+ border: none;
308
+ }
309
+
310
+ :deep() .tabs.horizontal + .tab-container {
311
+ border: none;
312
+ border-top: 1px solid var(--border);
313
+ padding: 0;
314
+ padding-top: 24px;
315
+ }
316
+ }
317
+ }
318
+ </style>
@@ -138,6 +138,13 @@ export default {
138
138
  }
139
139
  },
140
140
 
141
+ watch: {
142
+ namespace() {
143
+ // Namespace has changed, reset the selections
144
+ this.$emit('update:value', { [this.mountKey]: { secretKeyRef: { [this.nameKey]: undefined, [this.keyKey]: '' } } });
145
+ }
146
+ },
147
+
141
148
  methods: {
142
149
  /**
143
150
  * Provide a set of options for the LabelSelect ([none, ...{label, value}])
@@ -208,6 +215,7 @@ export default {
208
215
  <div class="input-container">
209
216
  <!-- key by namespace to ensure label select current page is recreated on ns change -->
210
217
  <ResourceLabeledSelect
218
+ :key="namespace"
211
219
  v-model:value="name"
212
220
  :disabled="!isView && disabled"
213
221
  :label="secretNameLabel"
@@ -219,6 +227,7 @@ export default {
219
227
  />
220
228
  <LabeledSelect
221
229
  v-if="showKeySelector"
230
+ :key="namespace"
222
231
  v-model:value="key"
223
232
  class="col span-6"
224
233
  :disabled="isKeyDisabled"
@@ -237,7 +237,7 @@ export default {
237
237
  let filteredSecrets = [];
238
238
 
239
239
  if (this.allSecrets) {
240
- // Fitler secrets given their namespace and required secret type
240
+ // Filter secrets given their namespace and required secret type
241
241
  filteredSecrets = this.allSecrets
242
242
  .filter((x) => this.filterByNamespace ? x.metadata.namespace === this.namespace : true
243
243
  )
@@ -450,6 +450,7 @@ export default {
450
450
  ),
451
451
  ],
452
452
  }),
453
+ watch: this.cacheSecrets,
453
454
  };
454
455
 
455
456
  if (this.cacheSecrets) {
@@ -464,9 +465,11 @@ export default {
464
465
  null,
465
466
  findPageArgs
466
467
  );
467
- const res = await this.$store.dispatch(`cluster/request`, { url });
468
+ // Strictly speaking this could be any store (request action should be agnostic)
469
+ const res = await this.$store.dispatch(`${ this.inStore }/request`, { url });
468
470
 
469
- return res?.data || [];
471
+ // Classify
472
+ return await this.$store.dispatch(`${ this.inStore }/createMany`, res?.data || []);
470
473
  },
471
474
 
472
475
  updateKeyVal() {
@@ -0,0 +1,116 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Networking from '@shell/components/form/Networking.vue';
3
+ import { nextTick } from 'vue';
4
+
5
+ jest.mock('lodash/debounce', () => jest.fn((fn) => fn));
6
+
7
+ describe('component: Networking', () => {
8
+ it.each([
9
+ {
10
+ ip: '10.1.1.1',
11
+ hostnames: ['host1']
12
+ },
13
+ {
14
+ ip: '10.1.1.2',
15
+ hostnames: ['host1', 'host2']
16
+ },
17
+ {
18
+ ip: '10.1.1.3',
19
+ hostnames: []
20
+ }
21
+ ])('should render host aliases form if the value contains host aliases config: [%j]', (hostAliasesConfig) => {
22
+ const wrapper = mount(Networking, {
23
+ props: {
24
+ value: { hostAliases: [hostAliasesConfig] },
25
+ mode: 'create',
26
+ },
27
+ global: { mocks: { $store: { getters: { 'i18n/t': jest.fn() } } } }
28
+ });
29
+
30
+ const hostAliases = wrapper.findComponent('[data-test="hostAliases"]');
31
+ const keyInput = hostAliases.find('[data-testid="input-kv-item-key-0"]');
32
+ const valueInput = hostAliases.find('[data-testid="kv-item-value-0"]');
33
+
34
+ expect(keyInput.element.value).toBe(hostAliasesConfig.ip);
35
+ expect(valueInput.find('[data-testid="value-multiline"]').element.value).toBe(hostAliasesConfig.hostnames.join(', '));
36
+ });
37
+
38
+ it('should not render host aliases form, if there is no host aliases configuration', async() => {
39
+ const wrapper = mount(Networking, {
40
+ props: {
41
+ value: {},
42
+ 'onUpdate:value': (e) => wrapper.setProps({ value: e }),
43
+ mode: 'create',
44
+ },
45
+ global: { mocks: { $store: { getters: { 'i18n/t': jest.fn() } } } }
46
+ });
47
+
48
+ const hostAliases = wrapper.findComponent('[data-test="hostAliases"]');
49
+ const keyInput = hostAliases.find('[data-testid="input-kv-item-key-0"]');
50
+ const valueInput = hostAliases.find('[data-testid="kv-item-value-0"]');
51
+
52
+ expect(keyInput.exists()).toBe(false);
53
+ expect(valueInput.exists()).toBe(false);
54
+ expect(wrapper.props('value')).toStrictEqual({});
55
+ });
56
+
57
+ it('should render host aliases form correctly if click add host aliases button', async() => {
58
+ const wrapper = mount(Networking, {
59
+ props: {
60
+ value: {},
61
+ 'onUpdate:value': (e) => wrapper.setProps({ value: e }),
62
+ mode: 'create',
63
+ },
64
+ global: { mocks: { $store: { getters: { 'i18n/t': jest.fn() } } } }
65
+ });
66
+
67
+ const hostAliases = wrapper.findComponent('[data-test="hostAliases"]');
68
+ const addButton = hostAliases.find('[data-testid="add_row_item_button"]');
69
+
70
+ addButton.trigger('click');
71
+ await nextTick();
72
+
73
+ const keyInput = hostAliases.find('[data-testid="input-kv-item-key-0"]');
74
+ const valueInput = hostAliases.find('[data-testid="kv-item-value-0"]');
75
+ const v = wrapper.props('value');
76
+
77
+ expect(keyInput.exists()).toBe(true);
78
+ expect(valueInput.exists()).toBe(true);
79
+ expect(v.hostAliases).toHaveLength(1);
80
+ expect(v.hostAliases[0].ip).toBe('');
81
+ expect(v.hostAliases[0].hostnames).toStrictEqual([]);
82
+ });
83
+
84
+ it('should render host aliases form correctly if modify the form values', async() => {
85
+ const wrapper = mount(Networking, {
86
+ props: {
87
+ value: {},
88
+ 'onUpdate:value': (e) => wrapper.setProps({ value: e }),
89
+ mode: 'create',
90
+ },
91
+ global: { mocks: { $store: { getters: { 'i18n/t': jest.fn() } } } }
92
+ });
93
+
94
+ const hostAliases = wrapper.findComponent('[data-test="hostAliases"]');
95
+ const addButton = hostAliases.find('[data-testid="add_row_item_button"]');
96
+
97
+ addButton.trigger('click');
98
+ await nextTick();
99
+ let v = wrapper.props('value');
100
+ let keyInput = hostAliases.find('[data-testid="input-kv-item-key-0"]');
101
+ let valueInput = hostAliases.find('[data-testid="kv-item-value-0"]');
102
+
103
+ keyInput.setValue('10.1.1.1');
104
+ valueInput.find('[data-testid="value-multiline"]').setValue('test1, test2');
105
+ await nextTick();
106
+ v = wrapper.props('value');
107
+ keyInput = hostAliases.find('[data-testid="input-kv-item-key-0"]');
108
+ valueInput = hostAliases.find('[data-testid="kv-item-value-0"]');
109
+
110
+ expect(v.hostAliases).toHaveLength(1);
111
+ expect(v.hostAliases[0].ip).toBe('10.1.1.1');
112
+ expect(v.hostAliases[0].hostnames).toStrictEqual(['test1', 'test2']);
113
+ expect(keyInput.exists()).toBe(true);
114
+ expect(valueInput.exists()).toBe(true);
115
+ });
116
+ });
@@ -1,46 +1,11 @@
1
1
  import { debounce } from 'lodash';
2
2
  import { PropType, defineComponent } from 'vue';
3
- import { ComputedOptions, MethodOptions } from 'vue/types/v3-component-options';
4
3
  import { LabelSelectPaginateFn, LABEL_SELECT_NOT_OPTION_KINDS, LABEL_SELECT_KINDS } from '@shell/types/components/labeledSelect';
5
4
 
6
- interface Props {
7
- paginate?: LabelSelectPaginateFn
8
- }
9
-
10
- interface Data {
11
- currentPage: number,
12
- search: string,
13
- pageSize: number,
14
-
15
- page: any[],
16
- pages: number,
17
- totalResults: number,
18
-
19
- paginating: boolean,
20
-
21
- debouncedRequestPagination: Function
22
- }
23
-
24
- interface Computed extends ComputedOptions {
25
- canPaginate: () => boolean,
26
-
27
- canLoadMore: () => boolean,
28
-
29
- optionsInPage: () => number,
30
-
31
- optionCounts: () => string,
32
- }
33
-
34
- interface Methods extends MethodOptions {
35
- loadMore: () => void
36
- setPaginationFilter: (filter: string) => void
37
- requestPagination: () => Promise<any>;
38
- }
39
-
40
5
  /**
41
6
  * 'mixin' to provide pagination support to LabeledSelect
42
7
  */
43
- export default defineComponent<Props, any, Data, Computed, Methods>({
8
+ export default defineComponent({
44
9
  props: {
45
10
  paginate: {
46
11
  default: null,
@@ -61,7 +26,7 @@ export default defineComponent<Props, any, Data, Computed, Methods>({
61
26
  },
62
27
  },
63
28
 
64
- data(): Data {
29
+ data() {
65
30
  return {
66
31
  // Internal
67
32
  currentPage: 1,
@@ -72,7 +37,7 @@ export default defineComponent<Props, any, Data, Computed, Methods>({
72
37
  debouncedRequestPagination: debounce(this.requestPagination, 700),
73
38
 
74
39
  // External
75
- page: [],
40
+ page: [] as any[],
76
41
  totalResults: 0,
77
42
  paginating: false,
78
43
  };
@@ -40,32 +40,40 @@ export default {
40
40
  </script>
41
41
 
42
42
  <template>
43
- <template v-if="row.source.showLink">
43
+ <template v-if="row.source.value">
44
44
  <Link
45
+ v-if="row.source.showLink"
45
46
  class="source-link"
46
47
  label-key="source.display"
47
48
  before-icon-key="source.icon"
48
49
  url-key="source.value"
49
50
  :row="row"
50
- :value="row.source.value || ''"
51
+ :value="row.source.value"
51
52
  />
52
- <template v-if="row.source.value && row.sourceSub.value">
53
- <div class="text-label">
54
- <Shortened
55
- long-value-key="sourceSub.value"
56
- :row="row"
57
- :value="row.sourceSub.display"
58
- />
59
- </div>
60
- </template>
53
+ <span v-else>
54
+ {{ row.source.value }}
55
+ </span>
56
+ <div
57
+ v-if="row.sourceSub.value"
58
+ class="text-label"
59
+ >
60
+ <Shortened
61
+ long-value-key="sourceSub.value"
62
+ :row="row"
63
+ :value="row.sourceSub.display"
64
+ />
65
+ </div>
61
66
  </template>
62
- <span v-else>
63
- {{ row.source.value }}
64
- </span>
67
+ <div
68
+ v-else
69
+ class="text-muted"
70
+ >
71
+ &mdash;
72
+ </div>
65
73
  </template>
66
74
 
67
75
  <style lang="scss" scoped>
68
- .source-link {
69
- width: fit-content;
70
- }
76
+ .source-link {
77
+ width: fit-content;
78
+ }
71
79
  </style>
@@ -56,7 +56,7 @@ export default {
56
56
 
57
57
  <template>
58
58
  <span class="formatter-pod-images">
59
- <span>{{ mainImage }}</span><br>
59
+ <span v-clean-tooltip.bottom="mainImage">{{ mainImage }}</span><br>
60
60
  <span
61
61
  v-if="images.length-1>0"
62
62
  v-clean-tooltip.bottom="imageLabels"
@@ -16,6 +16,15 @@ const defaultMock = {
16
16
  };
17
17
 
18
18
  describe('component: LiveDate', () => {
19
+ beforeEach(() => {
20
+ jest.useFakeTimers();
21
+ jest.setSystemTime(new Date('2025-07-10T10:00:00.000Z'));
22
+ });
23
+
24
+ afterEach(() => {
25
+ jest.useRealTimers();
26
+ });
27
+
19
28
  it('should show a dash if no date is provided', () => {
20
29
  const wrapper = mount(LiveDate as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, { global: { mocks: defaultMock } });
21
30
  const element = wrapper.find('span');
@@ -55,8 +64,7 @@ describe('component: LiveDate', () => {
55
64
  let element = wrapper.find('span');
56
65
 
57
66
  expect(element.text()).toContain('Just now');
58
- await new Promise((resolve) => setTimeout(resolve, 1000));
59
- await wrapper.vm.liveUpdate(Date.now());
67
+ await wrapper.vm.liveUpdate(Date.now() + 1000);
60
68
 
61
69
  element = wrapper.find('span');
62
70
  expect(element.text()).toContain('1 %unit.sec%');