@rancher/shell 3.0.12-rc.1 → 3.0.12-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/assets/images/providers/entraid-black.svg +4 -0
  2. package/assets/images/providers/entraid.svg +9 -0
  3. package/assets/images/vendor/entraid.svg +9 -0
  4. package/assets/styles/app.scss +0 -1
  5. package/assets/translations/en-us.yaml +19 -17
  6. package/assets/translations/zh-hans.yaml +4 -8
  7. package/chart/__tests__/S3.test.ts +10 -3
  8. package/components/CountBox.vue +20 -0
  9. package/components/CreateDriver.vue +0 -12
  10. package/components/DetailText.vue +12 -3
  11. package/components/SelectIconGrid.vue +5 -0
  12. package/components/__tests__/CountBox.test.ts +72 -0
  13. package/components/__tests__/DetailText.test.ts +113 -0
  14. package/components/fleet/FleetClusterTargets/index.vue +18 -1
  15. package/components/form/InputWithSelect.vue +18 -10
  16. package/components/form/KeyValue.vue +17 -1
  17. package/components/form/LabeledSelect.vue +82 -24
  18. package/components/form/Select.vue +73 -56
  19. package/components/form/ServiceNameSelect.vue +13 -11
  20. package/components/form/__tests__/KeyValue.test.ts +66 -0
  21. package/components/form/__tests__/NodeScheduling.test.ts +9 -0
  22. package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
  23. package/components/nav/Group.vue +7 -6
  24. package/components/nav/Header.vue +24 -3
  25. package/components/nav/NotificationCenter/Notification.vue +4 -1
  26. package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
  27. package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
  28. package/components/nav/Type.vue +8 -7
  29. package/components/nav/WindowManager/index.vue +2 -1
  30. package/components/nav/WorkspaceSwitcher.vue +13 -0
  31. package/components/nav/__tests__/Group.test.ts +67 -0
  32. package/components/nav/__tests__/Header.test.ts +235 -0
  33. package/components/nav/__tests__/Type.test.ts +20 -3
  34. package/components/templates/default.vue +34 -4
  35. package/components/templates/home.vue +12 -25
  36. package/components/templates/plain.vue +13 -26
  37. package/composables/useLabeledFormElement.ts +10 -2
  38. package/composables/useLabeledSelect.ts +60 -0
  39. package/composables/useUserRetentionValidation.ts +1 -49
  40. package/config/cookies.js +0 -1
  41. package/config/labels-annotations.js +1 -0
  42. package/config/query-params.js +1 -0
  43. package/config/router/routes.js +0 -8
  44. package/core/__tests__/plugin-products.test.ts +616 -25
  45. package/core/plugin-products-base.ts +31 -14
  46. package/core/plugin-products-helpers.ts +5 -4
  47. package/core/plugin-types.ts +18 -3
  48. package/core/types.ts +3 -1
  49. package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
  50. package/detail/management.cattle.io.fleetworkspace.vue +49 -0
  51. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
  52. package/edit/__tests__/kontainerDriver.test.ts +0 -13
  53. package/edit/__tests__/nodeDriver.test.ts +5 -11
  54. package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
  55. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  56. package/edit/auth/__tests__/oidc.test.ts +54 -0
  57. package/edit/auth/azuread.vue +1 -1
  58. package/edit/auth/oidc.vue +8 -0
  59. package/edit/kontainerDriver.vue +1 -2
  60. package/edit/nodeDriver.vue +0 -2
  61. package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
  62. package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
  63. package/edit/provisioning.cattle.io.cluster/index.vue +70 -99
  64. package/initialize/App.vue +29 -2
  65. package/initialize/install-plugins.js +0 -2
  66. package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
  67. package/list/catalog.cattle.io.app.vue +25 -5
  68. package/list/management.cattle.io.feature.vue +1 -1
  69. package/list/management.cattle.io.fleetworkspace.vue +8 -0
  70. package/machine-config/amazonec2.vue +1 -0
  71. package/mixins/chart.js +40 -9
  72. package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
  73. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
  74. package/models/__tests__/chart.test.ts +99 -6
  75. package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
  76. package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
  77. package/models/catalog.cattle.io.app.js +21 -17
  78. package/models/catalog.cattle.io.clusterrepo.js +39 -11
  79. package/models/chart.js +33 -19
  80. package/models/fleet-application.js +1 -1
  81. package/models/fleet.cattle.io.bundle.js +1 -1
  82. package/models/kontainerdriver.js +11 -0
  83. package/models/management.cattle.io.authconfig.js +5 -1
  84. package/models/management.cattle.io.cluster.js +0 -53
  85. package/models/management.cattle.io.feature.js +3 -3
  86. package/models/management.cattle.io.kontainerdriver.js +1 -26
  87. package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
  88. package/models/nodedriver.js +7 -0
  89. package/package.json +13 -12
  90. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +189 -0
  91. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
  92. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
  93. package/pages/c/_cluster/apps/charts/chart.vue +217 -33
  94. package/pages/c/_cluster/apps/charts/index.vue +2 -2
  95. package/pages/c/_cluster/apps/charts/install.vue +8 -3
  96. package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
  97. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
  98. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +39 -2
  99. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
  100. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +15 -10
  101. package/pages/c/_cluster/uiplugins/index.vue +23 -25
  102. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +205 -1
  103. package/rancher-components/Form/LabeledInput/LabeledInput.vue +82 -4
  104. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
  105. package/scripts/test-plugins-build.sh +5 -2
  106. package/server/server-middleware.js +2 -2
  107. package/static/humans.txt +1 -0
  108. package/static/robots.txt +34 -0
  109. package/static/welcome-cow.svg +18 -0
  110. package/store/__tests__/catalog.test.ts +161 -11
  111. package/store/auth.js +0 -3
  112. package/store/catalog.js +60 -8
  113. package/types/shell/index.d.ts +26 -22
  114. package/utils/__tests__/git.test.ts +270 -0
  115. package/utils/__tests__/inactivity.test.ts +316 -0
  116. package/utils/__tests__/object.test.ts +77 -0
  117. package/utils/__tests__/time.test.ts +14 -1
  118. package/utils/__tests__/url.test.ts +246 -0
  119. package/utils/object.js +33 -2
  120. package/utils/time.ts +5 -0
  121. package/vue.config.js +0 -9
  122. package/assets/images/providers/azuread-black.svg +0 -22
  123. package/assets/images/providers/azuread.svg +0 -25
  124. package/assets/images/vendor/azuread.svg +0 -18
  125. package/assets/styles/fonts/_dots.scss +0 -18
  126. package/components/EmberPage.vue +0 -622
  127. package/components/EmberPageView.vue +0 -39
  128. package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
  129. package/mixins/labeled-form-element.ts +0 -225
  130. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
  131. package/pages/c/_cluster/manager/pages/_page.vue +0 -22
  132. package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
  133. package/plugins/ember-cookie.js +0 -17
  134. package/utils/ember-page.js +0 -30
@@ -778,11 +778,16 @@ export default {
778
778
  @onInput="onInputMarkdownMultiline(i, $event)"
779
779
  @onFocus="onFocusMarkdownMultiline(i, $event)"
780
780
  />
781
+ <div
782
+ v-else-if="valueConcealed"
783
+ class="concealed-value conceal"
784
+ data-testid="concealed-value"
785
+ :aria-label="t('generic.ariaLabel.value', {index: i+1})"
786
+ />
781
787
  <TextAreaAutoGrow
782
788
  v-else-if="valueMultiline && row[valueName] !== undefined"
783
789
  v-model:value="row[valueName]"
784
790
  data-testid="value-multiline"
785
- :class="{'conceal': valueConcealed}"
786
791
  :disabled="disabled"
787
792
  :mode="mode"
788
793
  :placeholder="_valuePlaceholder"
@@ -809,6 +814,7 @@ export default {
809
814
  class="btn btn-sm role-secondary file-selector"
810
815
  :label="t('generic.upload')"
811
816
  :include-file-name="true"
817
+ :accept="readAccept"
812
818
  :aria-label="t('generic.ariaLabel.value', {index: i+1})"
813
819
  @selected="onValueFileSelected(i, $event)"
814
820
  />
@@ -896,6 +902,7 @@ export default {
896
902
  class="role-tertiary"
897
903
  :label="t('generic.readFromFile')"
898
904
  :include-file-name="true"
905
+ :accept="readAccept"
899
906
  data-testid="read_all_key_value_button"
900
907
  @selected="onFileSelected"
901
908
  />
@@ -941,6 +948,15 @@ export default {
941
948
  padding: 10px 10px 10px 10px;
942
949
  }
943
950
 
951
+ .concealed-value {
952
+ padding: 10px;
953
+ min-height: 40px;
954
+ user-select: none;
955
+ &::before {
956
+ content: '••••••••••••••••••••';
957
+ }
958
+ }
959
+
944
960
  .text-monospace:not(.conceal) {
945
961
  font-family: monospace, monospace;
946
962
  }
@@ -1,16 +1,17 @@
1
1
  <script>
2
2
  import CompactInput from '@shell/mixins/compact-input';
3
- import LabeledFormElement from '@shell/mixins/labeled-form-element';
4
3
  import { get } from '@shell/utils/object';
5
4
  import { LabeledTooltip } from '@components/LabeledTooltip';
6
5
  import VueSelectOverrides from '@shell/mixins/vue-select-overrides';
7
6
  import { calculatePosition } from '@shell/utils/select';
8
7
  import { generateRandomAlphaString } from '@shell/utils/string';
9
- import LabeledSelectPagination from '@shell/components/form/labeled-select-utils/labeled-select-pagination';
8
+ import { useLabeledSelectPagination, labeledSelectPaginationProps } from '@shell/components/form/labeled-select-utils/useLabeledSelectPagination';
10
9
  import { LABEL_SELECT_NOT_OPTION_KINDS } from '@shell/types/components/labeledSelect';
11
10
  import { mapGetters } from 'vuex';
12
11
  import { _VIEW } from '@shell/config/query-params';
13
12
  import { useClickOutside } from '@shell/composables/useClickOutside';
13
+ import { useLabeledFormElement, labeledFormElementProps } from '@shell/composables/useLabeledFormElement';
14
+ import { useLabeledSelect } from '@shell/composables/useLabeledSelect';
14
15
  import { ref } from 'vue';
15
16
 
16
17
  export default {
@@ -21,14 +22,18 @@ export default {
21
22
  components: { LabeledTooltip },
22
23
  mixins: [
23
24
  CompactInput,
24
- LabeledFormElement,
25
25
  VueSelectOverrides,
26
- LabeledSelectPagination
27
26
  ],
28
27
 
29
- emits: ['on-open', 'on-close', 'selecting', 'deselecting', 'search', 'update:validation', 'update:value'],
28
+ emits: ['on-open', 'on-close', 'on-focus', 'on-blur', 'selecting', 'deselecting', 'search', 'update:validation', 'update:value'],
30
29
 
31
30
  props: {
31
+ ...labeledFormElementProps,
32
+ ...labeledSelectPaginationProps,
33
+ value: {
34
+ default: null,
35
+ type: [String, Object, Number, Array, Boolean]
36
+ },
32
37
  appendToBody: {
33
38
  default: true,
34
39
  type: Boolean,
@@ -37,14 +42,6 @@ export default {
37
42
  default: false,
38
43
  type: Boolean
39
44
  },
40
- disabled: {
41
- default: false,
42
- type: Boolean
43
- },
44
- required: {
45
- default: false,
46
- type: Boolean
47
- },
48
45
  hoverTooltip: {
49
46
  default: true,
50
47
  type: Boolean
@@ -99,14 +96,18 @@ export default {
99
96
  default: null,
100
97
  type: [String, Object]
101
98
  },
102
- value: {
103
- default: null,
104
- type: [String, Object, Number, Array, Boolean]
105
- },
106
99
  options: {
107
100
  type: Array,
108
101
  default: () => ([])
109
102
  },
103
+ searchable: {
104
+ default: false,
105
+ type: Boolean
106
+ },
107
+ filterable: {
108
+ default: true,
109
+ type: Boolean
110
+ },
110
111
  closeOnSelect: {
111
112
  type: Boolean,
112
113
  default: true
@@ -117,7 +118,7 @@ export default {
117
118
  }
118
119
  },
119
120
 
120
- setup() {
121
+ setup(props, { emit }) {
121
122
  const select = ref(null);
122
123
  const isOpen = ref(false);
123
124
 
@@ -125,7 +126,67 @@ export default {
125
126
  isOpen.value = false;
126
127
  });
127
128
 
128
- return { isOpen, select };
129
+ const {
130
+ raised,
131
+ focused,
132
+ blurred,
133
+ empty,
134
+ isView,
135
+ onFocusLabeled,
136
+ onBlurLabeled,
137
+ isDisabled,
138
+ validationMessage,
139
+ requiredField
140
+ } = useLabeledFormElement(props, emit);
141
+
142
+ const {
143
+ canPaginate,
144
+ canLoadMore,
145
+ optionCounts,
146
+ _options,
147
+ pages,
148
+ totalResults,
149
+ paginating,
150
+ loadMore,
151
+ setPaginationFilter,
152
+ } = useLabeledSelectPagination(props);
153
+
154
+ const {
155
+ isSearchable,
156
+ isFilterable,
157
+ resizeHandler: resizeHandlerFn
158
+ } = useLabeledSelect(props, canPaginate);
159
+
160
+ const resizeHandler = () => {
161
+ resizeHandlerFn(select);
162
+ };
163
+
164
+ return {
165
+ isOpen,
166
+ select,
167
+ raised,
168
+ focused,
169
+ blurred,
170
+ empty,
171
+ isView,
172
+ onFocusLabeled,
173
+ onBlurLabeled,
174
+ isDisabled,
175
+ validationMessage,
176
+ requiredField,
177
+ isSearchable,
178
+ isFilterable,
179
+ resizeHandler,
180
+ canPaginate,
181
+ canLoadMore,
182
+ optionCounts,
183
+ _options,
184
+ pages,
185
+ totalResults,
186
+ paginating,
187
+ loadMore,
188
+ setPaginationFilter,
189
+ };
129
190
  },
130
191
 
131
192
  data() {
@@ -148,11 +209,6 @@ export default {
148
209
  return this.canPaginate ? !!this._options.find((o) => o.kind === 'group' && !!o.icon) : false;
149
210
  },
150
211
 
151
- _options() {
152
- // If we're paginated show the page as provided by `paginate`. See label-select-pagination mixin
153
- return this.canPaginate ? this.page : this.options;
154
- },
155
-
156
212
  filteredAttrs() {
157
213
  const {
158
214
  class: _class,
@@ -207,11 +263,13 @@ export default {
207
263
  },
208
264
 
209
265
  onFocus() {
266
+ this.$emit('on-focus');
210
267
  this.selectedVisibility = 'hidden';
211
268
  this.onFocusLabeled();
212
269
  },
213
270
 
214
271
  onBlur() {
272
+ this.$emit('on-blur');
215
273
  this.selectedVisibility = 'visible';
216
274
  this.onBlurLabeled();
217
275
  },
@@ -1,39 +1,38 @@
1
1
  <script>
2
2
  import { get } from '@shell/utils/object';
3
- import LabeledFormElement from '@shell/mixins/labeled-form-element';
4
3
  import VueSelectOverrides from '@shell/mixins/vue-select-overrides';
5
4
  import { generateRandomAlphaString } from '@shell/utils/string';
6
5
  import { LabeledTooltip } from '@components/LabeledTooltip';
7
6
  import { calculatePosition } from '@shell/utils/select';
8
7
  import { _VIEW } from '@shell/config/query-params';
9
8
  import { useClickOutside } from '@shell/composables/useClickOutside';
9
+ import { useLabeledFormElement, labeledFormElementProps } from '@shell/composables/useLabeledFormElement';
10
+ import { useLabeledSelect } from '@shell/composables/useLabeledSelect';
10
11
  import { ref } from 'vue';
11
12
 
12
13
  export default {
13
- emits: ['update:value', 'createdListItem', 'on-open', 'on-close'],
14
+ inheritAttrs: false,
15
+
16
+ emits: ['update:value', 'createdListItem', 'on-open', 'on-close', 'on-focus', 'on-blur', 'update:validation'],
14
17
 
15
18
  components: { LabeledTooltip },
16
19
  mixins: [
17
- LabeledFormElement,
18
20
  VueSelectOverrides,
19
21
  ],
20
22
  props: {
23
+ ...labeledFormElementProps,
24
+ value: {
25
+ default: null,
26
+ type: [String, Object, Number, Array, Boolean],
27
+ },
21
28
  appendToBody: {
22
29
  default: true,
23
30
  type: Boolean,
24
31
  },
25
- disabled: {
26
- default: false,
27
- type: Boolean,
28
- },
29
32
  getKeyForOption: {
30
33
  default: null,
31
34
  type: Function
32
35
  },
33
- mode: {
34
- default: 'edit',
35
- type: String,
36
- },
37
36
  optionKey: {
38
37
  default: null,
39
38
  type: String,
@@ -46,10 +45,6 @@ export default {
46
45
  default: null,
47
46
  type: String,
48
47
  },
49
- placeholder: {
50
- type: String,
51
- default: '',
52
- },
53
48
  popperOverride: {
54
49
  type: Function,
55
50
  default: null,
@@ -78,10 +73,6 @@ export default {
78
73
  type: String,
79
74
  default: null,
80
75
  },
81
- value: {
82
- default: null,
83
- type: [String, Object, Number, Array, Boolean],
84
- },
85
76
  closeOnSelect: {
86
77
  type: Boolean,
87
78
  default: true,
@@ -99,8 +90,20 @@ export default {
99
90
  default: false,
100
91
  type: Boolean
101
92
  },
93
+ options: {
94
+ type: Array,
95
+ default: () => ([])
96
+ },
97
+ searchable: {
98
+ default: false,
99
+ type: Boolean
100
+ },
101
+ filterable: {
102
+ default: true,
103
+ type: Boolean
104
+ },
102
105
  },
103
- setup() {
106
+ setup(props, { emit }) {
104
107
  const select = ref(null);
105
108
  const isOpen = ref(false);
106
109
 
@@ -108,13 +111,61 @@ export default {
108
111
  isOpen.value = false;
109
112
  });
110
113
 
111
- return { isOpen, select };
114
+ const {
115
+ raised,
116
+ focused,
117
+ blurred,
118
+ empty,
119
+ isView,
120
+ onFocusLabeled,
121
+ onBlurLabeled,
122
+ isDisabled,
123
+ validationMessage,
124
+ requiredField
125
+ } = useLabeledFormElement(props, emit);
126
+
127
+ const onFocus = () => {
128
+ emit('on-focus');
129
+ onFocusLabeled();
130
+ };
131
+
132
+ const onBlur = () => {
133
+ emit('on-blur');
134
+ onBlurLabeled();
135
+ };
136
+
137
+ const {
138
+ isSearchable,
139
+ isFilterable,
140
+ resizeHandler: resizeHandlerFn
141
+ } = useLabeledSelect(props);
142
+
143
+ const resizeHandler = () => {
144
+ resizeHandlerFn(select);
145
+ };
146
+
147
+ return {
148
+ isOpen,
149
+ select,
150
+ raised,
151
+ focused,
152
+ blurred,
153
+ empty,
154
+ isView,
155
+ onFocus,
156
+ onBlur,
157
+ isDisabled,
158
+ validationMessage,
159
+ requiredField,
160
+ isSearchable,
161
+ isFilterable,
162
+ resizeHandler
163
+ };
112
164
  },
113
165
  data() {
114
166
  return { generatedUid: `s-uid-${ generateRandomAlphaString(12) }` };
115
167
  },
116
168
  methods: {
117
- // resizeHandler = in mixin
118
169
  getOptionLabel(option) {
119
170
  if (this.$attrs['get-option-label']) {
120
171
  return this.$attrs['get-option-label'](option);
@@ -252,40 +303,6 @@ export default {
252
303
  },
253
304
  },
254
305
  computed: {
255
- requiredField() {
256
- // using "any" for a type on "rule" here is dirty but the use of the optional chaining operator makes it safe for what we're doing here.
257
- return (this.required || this.rules.some((rule) => rule?.name === 'required'));
258
- },
259
- validationMessage() {
260
- // we want to grab the required rule passed in if we can but if it's not there then we can just grab it from the formRulesGenerator
261
- const requiredRule = this.rules.find((rule) => rule?.name === 'required');
262
- const ruleMessages = [];
263
- const value = this?.value;
264
-
265
- if (requiredRule && this.blurred && !this.focused) {
266
- const message = requiredRule(value);
267
-
268
- if (!!message) {
269
- return message;
270
- }
271
- }
272
-
273
- for (const rule of this.rules) {
274
- const message = rule(value);
275
-
276
- if (!!message && rule.name !== 'required') { // we're catching 'required' above so we can ignore it here
277
- ruleMessages.push(message);
278
- }
279
- }
280
- if (ruleMessages.length > 0 && (this.blurred || this.focused)) {
281
- return ruleMessages.join(', ');
282
- } else {
283
- return undefined;
284
- }
285
- },
286
- canPaginate() {
287
- return false;
288
- },
289
306
  deClassedAttrs() {
290
307
  const { class: _, ...rest } = this.$attrs;
291
308
 
@@ -1,16 +1,16 @@
1
1
  <script>
2
- import labeledFormElement from '@shell/mixins/labeled-form-element';
3
2
  import LabeledSelect from '@shell/components/form/LabeledSelect';
4
3
  import { Banner } from '@components/Banner';
5
4
  import { _VIEW } from '@shell/config/query-params';
5
+ import { computed } from 'vue';
6
6
 
7
7
  export default {
8
+ inheritAttrs: false,
9
+
8
10
  emits: ['update:value'],
9
11
 
10
12
  components: { LabeledSelect, Banner },
11
13
 
12
- mixins: [labeledFormElement],
13
-
14
14
  props: {
15
15
  disabled: {
16
16
  type: Boolean,
@@ -66,6 +66,16 @@ export default {
66
66
  type: Boolean,
67
67
  default: false,
68
68
  },
69
+ value: {
70
+ type: String,
71
+ default: null
72
+ }
73
+ },
74
+
75
+ setup(props) {
76
+ const isView = computed(() => props.mode === _VIEW);
77
+
78
+ return { isView };
69
79
  },
70
80
 
71
81
  data() {
@@ -73,10 +83,6 @@ export default {
73
83
  },
74
84
 
75
85
  computed: {
76
- isView() {
77
- return this.mode === _VIEW;
78
- },
79
-
80
86
  serviceNameNew() {
81
87
  if (!this.selected) {
82
88
  return false;
@@ -88,10 +94,6 @@ export default {
88
94
  serviceName() {
89
95
  return this.reduce(this.selected);
90
96
  },
91
-
92
- canPaginate() {
93
- return false;
94
- },
95
97
  },
96
98
 
97
99
  methods: {
@@ -177,6 +177,72 @@ describe('component: KeyValue', () => {
177
177
  expect(firstValueInput.element.value).toBe('testvalue1');
178
178
  });
179
179
 
180
+ describe('valueConcealed', () => {
181
+ it('should not render actual secret values in the DOM when valueConcealed is true', () => {
182
+ const secretValue = 'super-secret-api-key-12345';
183
+ const wrapper = mount(KeyValue, {
184
+ props: {
185
+ value: { mySecret: secretValue },
186
+ mode: 'view',
187
+ valueConcealed: true,
188
+ },
189
+
190
+ global: {
191
+ mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
192
+ stubs: { CodeMirror: true },
193
+ },
194
+ });
195
+
196
+ const concealedEl = wrapper.find('[data-testid="concealed-value"]');
197
+
198
+ expect(concealedEl.exists()).toBe(true);
199
+ expect(wrapper.html()).not.toContain(secretValue);
200
+ });
201
+
202
+ it('should render a TextAreaAutoGrow with the real value when valueConcealed is false', () => {
203
+ const secretValue = 'visible-value';
204
+ const wrapper = mount(KeyValue, {
205
+ props: {
206
+ value: { myKey: secretValue },
207
+ mode: 'view',
208
+ valueConcealed: false,
209
+ },
210
+
211
+ global: {
212
+ mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
213
+ stubs: { CodeMirror: true },
214
+ },
215
+ });
216
+
217
+ const concealedEl = wrapper.find('[data-testid="concealed-value"]');
218
+
219
+ expect(concealedEl.exists()).toBe(false);
220
+
221
+ const multilineEl = wrapper.find('[data-testid="value-multiline"]');
222
+
223
+ expect(multilineEl.exists()).toBe(true);
224
+ });
225
+
226
+ it('should have user-select none on the concealed placeholder to prevent text selection', () => {
227
+ const wrapper = mount(KeyValue, {
228
+ props: {
229
+ value: { mySecret: 'secret' },
230
+ mode: 'view',
231
+ valueConcealed: true,
232
+ },
233
+
234
+ global: {
235
+ mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
236
+ stubs: { CodeMirror: true },
237
+ },
238
+ });
239
+
240
+ const concealedEl = wrapper.find('[data-testid="concealed-value"]');
241
+
242
+ expect(concealedEl.classes()).toContain('concealed-value');
243
+ });
244
+ });
245
+
180
246
  it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
181
247
  const value = [{ key: 'testkey1', value: 'testvalue1' }];
182
248
 
@@ -1,9 +1,18 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import NodeScheduling from '@shell/components/form/NodeScheduling.vue';
3
3
  import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
4
+ import { createStore } from 'vuex';
4
5
 
5
6
  const requiredSetup = () => {
6
7
  return {
8
+ provide: {
9
+ store: createStore({
10
+ getters: {
11
+ currentStore: () => 'cluster',
12
+ 'cluster/paginationEnabled': () => () => false,
13
+ },
14
+ }),
15
+ },
7
16
  global: {
8
17
  mocks: {
9
18
  $fetchState: { pending: false },