@rancher/shell 3.0.2-rc.5 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/assets/images/providers/nutanix.svg +12 -1
  2. package/assets/styles/base/_basic.scss +2 -1
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +2 -0
  5. package/assets/styles/global/_labeled-input.scss +5 -13
  6. package/assets/styles/global/_layout.scss +4 -1
  7. package/assets/styles/global/_select.scss +5 -0
  8. package/assets/styles/themes/_dark.scss +1 -3
  9. package/assets/styles/themes/_light.scss +5 -1
  10. package/assets/translations/en-us.yaml +130 -23
  11. package/assets/translations/zh-hans.yaml +0 -3
  12. package/cloud-credential/azure.vue +1 -1
  13. package/components/ActionMenuShell.vue +105 -0
  14. package/components/AppModal.vue +2 -2
  15. package/components/AsyncButton.vue +2 -0
  16. package/components/ButtonGroup.vue +9 -2
  17. package/components/ClusterBadge.vue +1 -0
  18. package/components/ClusterIconMenu.vue +3 -0
  19. package/components/ClusterProviderIcon.vue +14 -1
  20. package/components/CodeMirror.vue +96 -5
  21. package/components/Collapse.vue +16 -3
  22. package/components/CruResource.vue +9 -0
  23. package/components/CruResourceFooter.vue +1 -1
  24. package/components/ExplorerMembers.vue +2 -1
  25. package/components/FixedBanner.vue +19 -12
  26. package/components/Import.vue +14 -1
  27. package/components/LandingPagePreference.vue +4 -2
  28. package/components/PodSecurityAdmission.vue +8 -6
  29. package/components/PromptChangePassword.vue +1 -0
  30. package/components/PromptRemove.vue +23 -21
  31. package/components/ResourceDetail/Masthead.vue +30 -11
  32. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  33. package/components/ResourceDetail/index.vue +6 -0
  34. package/components/ResourceTable.vue +6 -1
  35. package/components/ResourceYaml.vue +1 -0
  36. package/components/Setting.vue +115 -0
  37. package/components/SortableTable/THead.vue +2 -0
  38. package/components/SortableTable/index.vue +7 -12
  39. package/components/StatusBadge.vue +71 -0
  40. package/components/Tabbed/index.vue +16 -15
  41. package/components/Wizard.vue +108 -104
  42. package/components/YamlEditor.vue +12 -2
  43. package/components/__tests__/Collapse.test.ts +2 -2
  44. package/components/__tests__/FixedBanner.test.ts +3 -3
  45. package/components/auth/Principal.vue +29 -17
  46. package/components/auth/__tests__/Principal.test.ts +40 -0
  47. package/components/auth/login/ldap.vue +7 -0
  48. package/components/fleet/FleetBundles.vue +1 -1
  49. package/components/fleet/FleetRepos.vue +1 -1
  50. package/components/fleet/FleetResources.vue +0 -2
  51. package/components/fleet/FleetSummary.vue +60 -65
  52. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  53. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  54. package/components/form/ArrayList.vue +6 -2
  55. package/components/form/ColorInput.vue +1 -0
  56. package/components/form/KeyValue.vue +11 -12
  57. package/components/form/LabeledSelect.vue +15 -3
  58. package/components/form/Labels.vue +8 -1
  59. package/components/form/Members/MembershipEditor.vue +230 -222
  60. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  61. package/components/form/Password.vue +3 -0
  62. package/components/form/ProjectMemberEditor.vue +6 -3
  63. package/components/form/ResourceTabs/index.vue +15 -13
  64. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  65. package/components/form/SchedulingCustomization.vue +85 -0
  66. package/components/form/Select.vue +3 -2
  67. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  68. package/components/form/UnitInput.vue +3 -4
  69. package/components/form/__tests__/ArrayList.test.ts +9 -6
  70. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  71. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  72. package/components/form/__tests__/UnitInput.test.ts +4 -5
  73. package/components/formatter/LiveDate.vue +3 -1
  74. package/components/formatter/ServiceType.vue +12 -4
  75. package/components/formatter/WorkloadHealthScale.vue +2 -1
  76. package/components/nav/Header.vue +35 -2
  77. package/components/nav/HeaderPageActionMenu.vue +11 -40
  78. package/components/nav/Jump.vue +8 -2
  79. package/components/nav/NamespaceFilter.vue +5 -4
  80. package/components/nav/Pinned.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +5 -5
  82. package/components/nav/TopLevelMenu.vue +1 -12
  83. package/components/nav/WindowManager/ContainerLogs.vue +96 -58
  84. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  85. package/components/nav/WindowManager/index.vue +74 -6
  86. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  87. package/components/templates/default.vue +2 -47
  88. package/config/features.js +1 -0
  89. package/config/labels-annotations.js +11 -1
  90. package/config/router/navigation-guards/index.js +2 -1
  91. package/config/router/navigation-guards/record-last-route.js +24 -0
  92. package/config/settings.ts +66 -98
  93. package/config/version.js +1 -1
  94. package/core/types-provisioning.ts +7 -0
  95. package/detail/fleet.cattle.io.bundle.vue +7 -0
  96. package/detail/fleet.cattle.io.cluster.vue +0 -3
  97. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  98. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  99. package/dialog/DeactivateDriverDialog.vue +5 -5
  100. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  101. package/directives/strip-html-aria-label.js +19 -0
  102. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  103. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  104. package/edit/auth/__tests__/oidc.test.ts +60 -12
  105. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  106. package/edit/auth/ldap/config.vue +67 -89
  107. package/edit/auth/oidc.vue +16 -2
  108. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  109. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  110. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  111. package/edit/logging-flow/Match.vue +0 -21
  112. package/edit/management.cattle.io.project.vue +1 -1
  113. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  114. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  115. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  116. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  117. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +0 -2
  119. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  120. package/edit/provisioning.cattle.io.cluster/index.vue +28 -30
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +64 -13
  122. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  124. package/edit/resources.cattle.io.backup.vue +150 -15
  125. package/edit/secret/__tests__/ssh.test.ts +79 -0
  126. package/edit/secret/ssh.vue +7 -1
  127. package/edit/service.vue +0 -3
  128. package/edit/workload/Job.vue +8 -8
  129. package/edit/workload/__tests__/Job.test.ts +0 -1
  130. package/edit/workload/index.vue +3 -1
  131. package/initialize/install-directives.js +2 -0
  132. package/initialize/install-plugins.js +6 -1
  133. package/list/catalog.cattle.io.app.vue +21 -4
  134. package/list/fleet.cattle.io.bundle.vue +1 -1
  135. package/list/management.cattle.io.setting.vue +34 -132
  136. package/list/provisioning.cattle.io.cluster.vue +11 -3
  137. package/machine-config/vmwarevsphere.vue +15 -8
  138. package/mixins/__tests__/auth-config.test.ts +74 -0
  139. package/mixins/__tests__/chart.test.ts +5 -4
  140. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  141. package/mixins/auth-config.js +8 -0
  142. package/mixins/chart.js +2 -2
  143. package/mixins/create-edit-view/impl.js +4 -1
  144. package/mixins/vue-select-overrides.js +10 -0
  145. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  146. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  147. package/models/__tests__/secret.test.ts +56 -13
  148. package/models/catalog.cattle.io.app.js +112 -37
  149. package/models/cluster.js +11 -0
  150. package/models/fleet.cattle.io.bundle.js +40 -2
  151. package/models/fleet.cattle.io.gitrepo.js +169 -109
  152. package/models/management.cattle.io.fleetworkspace.js +4 -0
  153. package/models/management.cattle.io.kontainerdriver.js +7 -0
  154. package/models/nodedriver.js +4 -1
  155. package/models/provisioning.cattle.io.cluster.js +24 -0
  156. package/models/secret.js +1 -1
  157. package/package.json +5 -5
  158. package/pages/auth/login.vue +5 -11
  159. package/pages/auth/verify.vue +11 -1
  160. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  161. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  162. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  163. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  164. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  165. package/pages/c/_cluster/explorer/index.vue +33 -35
  166. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  167. package/pages/c/_cluster/fleet/index.vue +0 -5
  168. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  169. package/pages/c/_cluster/settings/performance.vue +52 -53
  170. package/pages/c/_cluster/uiplugins/index.vue +19 -22
  171. package/pages/home.vue +17 -12
  172. package/pages/prefs.vue +5 -1
  173. package/plugins/shortkey.js +10 -1
  174. package/plugins/steve/steve-pagination-utils.ts +58 -8
  175. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  176. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  177. package/promptRemove/management.cattle.io.project.vue +2 -8
  178. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  179. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  180. package/promptRemove/pod.vue +7 -28
  181. package/rancher-components/Card/Card.vue +9 -1
  182. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  183. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  184. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  185. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  186. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  187. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  188. package/rancher-components/RcButton/RcButton.vue +14 -9
  189. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  190. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -2
  191. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  192. package/rancher-components/RcDropdown/index.ts +1 -0
  193. package/rancher-components/RcDropdown/types.ts +27 -0
  194. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  195. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
  196. package/scripts/typegen.sh +1 -0
  197. package/store/__tests__/auth.test.ts +120 -0
  198. package/store/action-menu.js +13 -3
  199. package/store/auth.js +14 -9
  200. package/store/aws.js +9 -2
  201. package/store/catalog.js +14 -7
  202. package/store/features.js +1 -0
  203. package/store/prefs.js +9 -28
  204. package/store/type-map.utils.ts +4 -0
  205. package/types/resources/settings.d.ts +27 -20
  206. package/types/shell/index.d.ts +18 -12
  207. package/utils/__tests__/array.test.ts +13 -1
  208. package/utils/__tests__/string.test.ts +80 -1
  209. package/utils/array.ts +13 -0
  210. package/utils/auth.js +4 -0
  211. package/utils/banners.js +0 -45
  212. package/utils/cluster.js +1 -1
  213. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  214. package/utils/object.js +0 -3
  215. package/utils/pagination-utils.ts +15 -2
  216. package/utils/string.js +31 -7
  217. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  218. package/utils/validators/formRules/index.ts +16 -0
  219. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -1,222 +1,230 @@
1
- <script>
2
- import { MANAGEMENT, NORMAN } from '@shell/config/types';
3
- import ArrayList from '@shell/components/form/ArrayList';
4
- import Loading from '@shell/components/Loading';
5
- import { _CREATE, _VIEW } from '@shell/config/query-params';
6
- import { get, set } from '@shell/utils/object';
7
-
8
- function normalizeId(id) {
9
- return id?.replace(':', '/') || id;
10
- }
11
-
12
- export function canViewMembershipEditor(store, needsProject = false) {
13
- return (!!store.getters['management/schemaFor'](MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING) || !needsProject) &&
14
- !!store.getters['management/schemaFor'](MANAGEMENT.ROLE_TEMPLATE) &&
15
- !!store.getters['rancher/schemaFor'](NORMAN.PRINCIPAL);
16
- }
17
-
18
- export default {
19
- emits: ['membership-update'],
20
-
21
- components: { ArrayList, Loading },
22
-
23
- props: {
24
- addMemberDialogName: {
25
- type: String,
26
- required: true
27
- },
28
-
29
- parentKey: {
30
- type: String,
31
- required: true
32
- },
33
-
34
- parentId: {
35
- type: String,
36
- default: null
37
- },
38
-
39
- mode: {
40
- type: String,
41
- required: true
42
- },
43
-
44
- type: {
45
- type: String,
46
- required: true
47
- },
48
-
49
- defaultBindingHandler: {
50
- type: Function,
51
- default: null,
52
- },
53
-
54
- modalSticky: {
55
- type: Boolean,
56
- default: false,
57
- }
58
- },
59
-
60
- async fetch() {
61
- const roleBindingRequestParams = { type: this.type, opt: { force: true } };
62
-
63
- if (this.type === NORMAN.PROJECT_ROLE_TEMPLATE_BINDING && this.parentId) {
64
- Object.assign(roleBindingRequestParams, { opt: { filter: { projectId: this.parentId.split('/').join(':') } } });
65
- }
66
- const userHydration = [
67
- this.schema ? this.$store.dispatch(`rancher/findAll`, roleBindingRequestParams) : [],
68
- this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL }),
69
- this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE }),
70
- this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER })
71
- ];
72
- const [allBindings] = await Promise.all(userHydration);
73
-
74
- const bindings = allBindings
75
- .filter((b) => normalizeId(get(b, this.parentKey)) === normalizeId(this.parentId));
76
-
77
- this['lastSavedBindings'] = [...bindings];
78
-
79
- // Add the current user as the project owner. This will get created by default
80
- if (this.mode === _CREATE && bindings.length === 0 && this.defaultBindingHandler) {
81
- const defaultBinding = await this.defaultBindingHandler();
82
-
83
- defaultBinding.isDefaultBinding = true;
84
- bindings.push(defaultBinding);
85
- }
86
-
87
- this['bindings'] = bindings;
88
- },
89
-
90
- data() {
91
- return {
92
- schema: this.$store.getters[`rancher/schemaFor`](this.type),
93
- bindings: [],
94
- lastSavedBindings: [],
95
- };
96
- },
97
-
98
- computed: {
99
- newBindings() {
100
- return this.bindings
101
- .filter((binding) => !binding.id && !this.lastSavedBindings.includes(binding) && !binding.isDefaultBinding);
102
- },
103
- removedBindings() {
104
- return this.lastSavedBindings
105
- .filter((binding) => !this.bindings.includes(binding));
106
- },
107
- membershipUpdate() {
108
- const newBindings = this.newBindings;
109
- const removedBindings = this.removedBindings;
110
-
111
- return {
112
- newBindings: this.newBindings,
113
- removedBindings: this.removedBindings,
114
- save: (parentId) => {
115
- const savedPromises = newBindings.map((binding) => {
116
- set(binding, this.parentKey, parentId);
117
-
118
- return binding.save();
119
- });
120
-
121
- const removedPromises = removedBindings.map((binding) => binding.remove());
122
-
123
- return Promise.all([...savedPromises, ...removedPromises]);
124
- }
125
- };
126
- },
127
-
128
- isCreate() {
129
- return this.mode === _CREATE;
130
- },
131
-
132
- isView() {
133
- return this.mode === _VIEW;
134
- },
135
- },
136
- watch: {
137
- membershipUpdate: {
138
- deep: true,
139
- handler() {
140
- this.$emit('membership-update', this.membershipUpdate);
141
- }
142
- }
143
- },
144
-
145
- methods: {
146
- addMember() {
147
- this.$store.dispatch('cluster/promptModal', {
148
- component: this.addMemberDialogName,
149
- componentProps: { onAdd: this.onAddMember },
150
- modalSticky: this.modalSticky
151
- });
152
- },
153
-
154
- onAddMember(bindings) {
155
- this['bindings'] = [...this.bindings, ...bindings];
156
- },
157
- }
158
- };
159
- </script>
160
- <template>
161
- <Loading v-if="$fetchState.pending" />
162
- <ArrayList
163
- v-else
164
- v-model:value="bindings"
165
- :mode="mode"
166
- :show-header="true"
167
- >
168
- <template #column-headers>
169
- <div class="box mb-0">
170
- <div class="column-headers row">
171
- <div class="col span-6">
172
- <label class="text-label">{{ t('membershipEditor.user') }}</label>
173
- </div>
174
- <div class="col span-6">
175
- <label class="text-label">{{ t('membershipEditor.role') }}</label>
176
- </div>
177
- </div>
178
- </div>
179
- </template>
180
- <template #columns="{row}">
181
- <div class="columns row">
182
- <div class="col span-6">
183
- <Principal
184
- :value="row.value.principalId"
185
- />
186
- </div>
187
- <div class="col span-6 role">
188
- {{ row.value.roleDisplay }}
189
- </div>
190
- </div>
191
- </template>
192
- <template #add>
193
- <button
194
- type="button"
195
- class="btn role-primary mt-10"
196
- @click="addMember"
197
- >
198
- {{ t('generic.add') }}
199
- </button>
200
- </template>
201
- <template #remove-button="{remove, i}">
202
- <span v-if="(isCreate && i === 0) || isView" />
203
- <button
204
- v-else
205
- type="button"
206
- :disabled="isView"
207
- class="btn role-link"
208
- @click="remove"
209
- >
210
- {{ t('generic.remove') }}
211
- </button>
212
- </template>
213
- </ArrayList>
214
- </template>
215
-
216
- <style lang="scss" scoped>
217
- .role {
218
- display: flex;
219
- align-items: center;
220
- flex-direction: row;
221
- }
222
- </style>
1
+ <script>
2
+ import { MANAGEMENT, NORMAN } from '@shell/config/types';
3
+ import ArrayList from '@shell/components/form/ArrayList';
4
+ import Principal from '@shell/components/auth/Principal';
5
+ import Loading from '@shell/components/Loading';
6
+ import { _CREATE, _VIEW } from '@shell/config/query-params';
7
+ import { get, set } from '@shell/utils/object';
8
+
9
+ function normalizeId(id) {
10
+ return id?.replace(':', '/') || id;
11
+ }
12
+
13
+ export function canViewMembershipEditor(store, needsProject = false) {
14
+ return (!!store.getters['management/schemaFor'](MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING) || !needsProject) &&
15
+ !!store.getters['management/schemaFor'](MANAGEMENT.ROLE_TEMPLATE) &&
16
+ !!store.getters['rancher/schemaFor'](NORMAN.PRINCIPAL);
17
+ }
18
+
19
+ export default {
20
+ emits: ['membership-update'],
21
+
22
+ components: {
23
+ ArrayList, Loading, Principal
24
+ },
25
+
26
+ props: {
27
+ addMemberDialogName: {
28
+ type: String,
29
+ required: true
30
+ },
31
+
32
+ parentKey: {
33
+ type: String,
34
+ required: true
35
+ },
36
+
37
+ parentId: {
38
+ type: String,
39
+ default: null
40
+ },
41
+
42
+ mode: {
43
+ type: String,
44
+ required: true
45
+ },
46
+
47
+ type: {
48
+ type: String,
49
+ required: true
50
+ },
51
+
52
+ defaultBindingHandler: {
53
+ type: Function,
54
+ default: null,
55
+ },
56
+
57
+ modalSticky: {
58
+ type: Boolean,
59
+ default: false,
60
+ }
61
+ },
62
+
63
+ async fetch() {
64
+ const roleBindingRequestParams = { type: this.type, opt: { force: true } };
65
+
66
+ if (this.type === NORMAN.PROJECT_ROLE_TEMPLATE_BINDING && this.parentId) {
67
+ Object.assign(roleBindingRequestParams, { opt: { filter: { projectId: this.parentId.split('/').join(':') }, force: true } });
68
+ }
69
+ const userHydration = [
70
+ this.schema ? this.$store.dispatch(`rancher/findAll`, roleBindingRequestParams) : [],
71
+ this.$store.dispatch('rancher/findAll', { type: NORMAN.PRINCIPAL }),
72
+ this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.ROLE_TEMPLATE }),
73
+ this.$store.dispatch(`management/findAll`, { type: MANAGEMENT.USER })
74
+ ];
75
+ const [allBindings] = await Promise.all(userHydration);
76
+
77
+ const bindings = allBindings
78
+ .filter((b) => normalizeId(get(b, this.parentKey)) === normalizeId(this.parentId));
79
+
80
+ this['lastSavedBindings'] = [...bindings];
81
+
82
+ // Add the current user as the project owner. This will get created by default
83
+ if (this.mode === _CREATE && bindings.length === 0 && this.defaultBindingHandler) {
84
+ const defaultBinding = await this.defaultBindingHandler();
85
+
86
+ defaultBinding.isDefaultBinding = true;
87
+ bindings.push(defaultBinding);
88
+ }
89
+
90
+ this['bindings'] = bindings;
91
+ },
92
+
93
+ data() {
94
+ return {
95
+ schema: this.$store.getters[`rancher/schemaFor`](this.type),
96
+ bindings: [],
97
+ lastSavedBindings: [],
98
+ };
99
+ },
100
+
101
+ computed: {
102
+ newBindings() {
103
+ return this.bindings
104
+ .filter((binding) => !binding.id && !this.lastSavedBindings.includes(binding) && !binding.isDefaultBinding);
105
+ },
106
+ removedBindings() {
107
+ return this.lastSavedBindings
108
+ .filter((binding) => !this.bindings.includes(binding));
109
+ },
110
+ membershipUpdate() {
111
+ const newBindings = this.newBindings;
112
+ const removedBindings = this.removedBindings;
113
+
114
+ return {
115
+ newBindings: this.newBindings,
116
+ removedBindings: this.removedBindings,
117
+ save: (parentId) => {
118
+ const savedPromises = newBindings.map((binding) => {
119
+ set(binding, this.parentKey, parentId);
120
+
121
+ return binding.save();
122
+ });
123
+
124
+ const removedPromises = removedBindings.map((binding) => binding.remove());
125
+
126
+ return Promise.all([...savedPromises, ...removedPromises]);
127
+ }
128
+ };
129
+ },
130
+
131
+ isCreate() {
132
+ return this.mode === _CREATE;
133
+ },
134
+
135
+ isView() {
136
+ return this.mode === _VIEW;
137
+ },
138
+ },
139
+ watch: {
140
+ membershipUpdate: {
141
+ deep: true,
142
+ handler() {
143
+ this.$emit('membership-update', this.membershipUpdate);
144
+ }
145
+ }
146
+ },
147
+
148
+ methods: {
149
+ addMember() {
150
+ this.$store.dispatch('cluster/promptModal', {
151
+ component: this.addMemberDialogName,
152
+ componentProps: { onAdd: this.onAddMember },
153
+ modalSticky: this.modalSticky
154
+ });
155
+ },
156
+
157
+ onAddMember(bindings) {
158
+ this['bindings'] = [...this.bindings, ...bindings];
159
+ },
160
+ }
161
+ };
162
+ </script>
163
+ <template>
164
+ <Loading v-if="$fetchState.pending" />
165
+ <ArrayList
166
+ v-else
167
+ v-model:value="bindings"
168
+ :mode="mode"
169
+ :show-header="true"
170
+ >
171
+ <template #column-headers>
172
+ <div class="box mb-0">
173
+ <div class="column-headers row">
174
+ <div class="col span-6">
175
+ <label class="text-label">{{ t('membershipEditor.user') }}</label>
176
+ </div>
177
+ <div class="col span-6">
178
+ <label class="text-label">{{ t('membershipEditor.role') }}</label>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </template>
183
+ <template #columns="{row, i}">
184
+ <div class="columns row">
185
+ <div class="col span-6">
186
+ <Principal
187
+ :value="row.value.principalId"
188
+ />
189
+ </div>
190
+ <div
191
+ :data-testid="`role-item-${i}`"
192
+ class="col span-6 role"
193
+ >
194
+ {{ row.value.roleDisplay }}
195
+ </div>
196
+ </div>
197
+ </template>
198
+ <template #add>
199
+ <button
200
+ type="button"
201
+ class="btn role-primary mt-10"
202
+ data-testid="add-item"
203
+ @click="addMember"
204
+ >
205
+ {{ t('generic.add') }}
206
+ </button>
207
+ </template>
208
+ <template #remove-button="{remove, i}">
209
+ <span v-if="(isCreate && i === 0) || isView" />
210
+ <button
211
+ v-else
212
+ type="button"
213
+ :disabled="isView"
214
+ class="btn role-link"
215
+ :data-testid="`remove-item-${i}`"
216
+ @click="remove"
217
+ >
218
+ {{ t('generic.remove') }}
219
+ </button>
220
+ </template>
221
+ </ArrayList>
222
+ </template>
223
+
224
+ <style lang="scss" scoped>
225
+ .role {
226
+ display: flex;
227
+ align-items: center;
228
+ flex-direction: row;
229
+ }
230
+ </style>
@@ -0,0 +1,62 @@
1
+ import { mount, type VueWrapper } from '@vue/test-utils';
2
+ import MembershipEditor from '@shell/components/form/Members/MembershipEditor.vue';
3
+
4
+ describe('component: MembershipEditor', () => {
5
+ let wrapper: VueWrapper<any, any>;
6
+
7
+ beforeEach(() => {
8
+ wrapper = mount(MembershipEditor, {
9
+ data() {
10
+ return {
11
+ schema: null,
12
+ lastSavedBindings: [],
13
+ bindings: [
14
+ {
15
+ principalId: 'local://user-nkkph',
16
+ roleDisplay: 'Cluster Owner',
17
+ },
18
+ {
19
+ principalId: 'local://u-jsvzm',
20
+ roleDisplay: 'Cluster Member 1',
21
+ },
22
+ {
23
+ principalId: 'local://u-rgcfw',
24
+ roleDisplay: 'Cluster Member 2',
25
+ },
26
+ {
27
+ principalId: 'local://u-gxsrz',
28
+ roleDisplay: 'Cluster Member 3',
29
+ }
30
+ ] as any,
31
+ };
32
+ },
33
+ props: {
34
+ addMemberDialogName: 'addMemberDialogName',
35
+ parentKey: 'parentKey',
36
+ mode: 'edit',
37
+ type: 'no idea',
38
+ },
39
+ global: {
40
+ mocks: {
41
+ $store: { getters: { 'rancher/schemaFor': () => ({ type: 'object' }) } },
42
+ $fetchState: { pending: false },
43
+ },
44
+ stubs: { Principal: true },
45
+ }
46
+ });
47
+ });
48
+
49
+ it('should render the component', () => {
50
+ expect(wrapper.exists()).toBe(true);
51
+ });
52
+
53
+ it('should remove the correct member', async() => {
54
+ await wrapper.find('[data-testid="remove-item-1"]').trigger('click');
55
+
56
+ const removedMember = wrapper.find('[data-testid="role-item-1"]').text();
57
+ const lastMember = wrapper.find('[data-testid="role-item-4"]');
58
+
59
+ expect(removedMember).toStrictEqual('Cluster Member 2');
60
+ expect(lastMember.exists()).toBe(false);
61
+ });
62
+ });
@@ -164,6 +164,9 @@ export default {
164
164
 
165
165
  .labeled-input {
166
166
  .addon {
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: center;
167
170
  padding-left: 12px;
168
171
  min-width: 65px;
169
172
 
@@ -194,7 +194,7 @@ export default {
194
194
  return this.customPermissions.reduce((acc, customPermissionsItem) => {
195
195
  const lockedExist = this.roleTemplates.find((roleTemplateItem) => roleTemplateItem.id === customPermissionsItem.key);
196
196
 
197
- if (lockedExist.locked) {
197
+ if (lockedExist && lockedExist.locked) {
198
198
  customPermissionsItem['locked'] = true;
199
199
  customPermissionsItem['tooltip'] = this.t('members.clusterPermissions.custom.lockedRole');
200
200
  }
@@ -261,6 +261,7 @@ export default {
261
261
  <div class="row mt-10">
262
262
  <div class="col span-12">
263
263
  <SelectPrincipal
264
+ data-testid="cluster-member-select"
264
265
  project
265
266
  class="mb-20"
266
267
  :mode="mode"
@@ -285,6 +286,7 @@ export default {
285
286
  <template v-slot:body>
286
287
  <RadioGroup
287
288
  v-model:value="value.permissionGroup"
289
+ data-testid="permission-options"
288
290
  :options="options"
289
291
  name="permission-group"
290
292
  />
@@ -294,11 +296,12 @@ export default {
294
296
  :class="{'two-column': useTwoColumnsForCustom}"
295
297
  >
296
298
  <div
297
- v-for="(permission, i) in customPermissionsUpdate"
298
- :key="i"
299
+ v-for="(permission, i) in customPermissionsUpdate"
300
+ :key="i"
299
301
  >
300
302
  <Checkbox
301
303
  v-model:value="permission.value"
304
+ :data-testid="`custom-permission-${i}`"
302
305
  :disabled="permission.locked"
303
306
  class="mb-5"
304
307
  :label="permission.label"
@@ -75,23 +75,25 @@ export default {
75
75
  const inStore = this.$store.getters['currentStore'](EVENT);
76
76
  const eventSchema = this.$store.getters[`${ inStore }/schemaFor`](EVENT); // @TODO be smarter about which resources actually ever have events
77
77
 
78
+ const paginationHeaders = eventSchema ? [
79
+ STEVE_EVENT_LAST_SEEN,
80
+ STEVE_EVENT_TYPE,
81
+ REASON,
82
+ headerFromSchemaColString('Subobject', eventSchema, this.$store.getters, true),
83
+ headerFromSchemaColString('Source', eventSchema, this.$store.getters, true),
84
+ MESSAGE,
85
+ headerFromSchemaColString('First Seen', eventSchema, this.$store.getters, true),
86
+ headerFromSchemaColString('Count', eventSchema, this.$store.getters, true),
87
+ STEVE_NAME_COL,
88
+ ] : [];
89
+
78
90
  return {
79
91
  eventSchema,
80
92
  EVENT,
81
- selectedTab: this.defaultTab,
93
+ selectedTab: this.defaultTab,
82
94
  inStore,
83
- showConditions: false,
84
- paginationHeaders: [
85
- STEVE_EVENT_LAST_SEEN,
86
- STEVE_EVENT_TYPE,
87
- REASON,
88
- headerFromSchemaColString('Subobject', eventSchema, this.$store.getters, true),
89
- headerFromSchemaColString('Source', eventSchema, this.$store.getters, true),
90
- MESSAGE,
91
- headerFromSchemaColString('First Seen', eventSchema, this.$store.getters, true),
92
- headerFromSchemaColString('Count', eventSchema, this.$store.getters, true),
93
- STEVE_NAME_COL,
94
- ]
95
+ showConditions: false,
96
+ paginationHeaders
95
97
  };
96
98
  },
97
99
 
@@ -80,6 +80,7 @@ export default {
80
80
  <app-modal
81
81
  v-if="showModal"
82
82
  ref="sshKnownHostsDialog"
83
+ data-testid="sshKnownHostsDialog"
83
84
  height="auto"
84
85
  :scrollable="true"
85
86
  @close="closeDialog(false)"
@@ -93,7 +94,6 @@ export default {
93
94
  <div class="custom mt-10">
94
95
  <div class="dialog-panel">
95
96
  <CodeMirror
96
- class="code-mirror"
97
97
  :value="text"
98
98
  data-testid="ssh-known-hosts-dialog_code-mirror"
99
99
  :options="codeMirrorOptions"
@@ -145,14 +145,15 @@ export default {
145
145
  display: flex;
146
146
  flex-direction: column;
147
147
  min-height: 100px;
148
- border: 1px solid var(--border);
149
148
 
150
149
  :deep() .code-mirror {
151
150
  display: flex;
152
151
  flex-direction: column;
153
152
  resize: none;
154
- max-height: 400px;
155
- height: 50vh;
153
+
154
+ .codemirror-container {
155
+ border: 1px solid var(--border);
156
+ }
156
157
 
157
158
  .CodeMirror,
158
159
  .CodeMirror-gutters {