@rancher/shell 0.3.20 → 0.3.22

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 (74) hide show
  1. package/assets/translations/en-us.yaml +8 -2
  2. package/assets/translations/zh-hans.yaml +8 -1
  3. package/cloud-credential/__tests__/azure.test.ts +53 -0
  4. package/cloud-credential/azure.vue +6 -0
  5. package/components/GrowlManager.vue +33 -30
  6. package/components/Questions/Array.vue +2 -2
  7. package/components/Questions/Boolean.vue +7 -1
  8. package/components/Questions/CloudCredential.vue +1 -0
  9. package/components/Questions/Enum.vue +21 -2
  10. package/components/Questions/Float.vue +8 -3
  11. package/components/Questions/Int.vue +8 -3
  12. package/components/Questions/Question.js +72 -0
  13. package/components/Questions/QuestionMap.vue +2 -1
  14. package/components/Questions/Radio.vue +33 -0
  15. package/components/Questions/Reference.vue +2 -0
  16. package/components/Questions/String.vue +8 -3
  17. package/components/Questions/Yaml.vue +46 -0
  18. package/components/Questions/__tests__/Boolean.test.ts +123 -0
  19. package/components/Questions/__tests__/Float.test.ts +123 -0
  20. package/components/Questions/__tests__/Int.test.ts +123 -0
  21. package/components/Questions/__tests__/String.test.ts +123 -0
  22. package/components/Questions/__tests__/Yaml.test.ts +123 -0
  23. package/components/Questions/index.vue +8 -1
  24. package/components/ResourceTable.vue +6 -12
  25. package/components/SideNav.vue +634 -0
  26. package/components/__tests__/NamespaceFilter.test.ts +3 -4
  27. package/components/form/ResourceQuota/ProjectRow.vue +38 -15
  28. package/components/form/UnitInput.vue +1 -0
  29. package/components/form/__tests__/KeyValue.test.ts +2 -1
  30. package/components/form/__tests__/UnitInput.test.ts +2 -2
  31. package/components/formatter/ClusterProvider.vue +9 -3
  32. package/components/formatter/LinkName.vue +12 -1
  33. package/components/formatter/__tests__/ClusterProvider.test.ts +5 -1
  34. package/components/nav/Header.vue +1 -0
  35. package/components/nav/WorkspaceSwitcher.vue +4 -1
  36. package/config/settings.ts +59 -2
  37. package/config/types.js +2 -0
  38. package/core/plugin-helpers.js +4 -1
  39. package/core/types.ts +1 -0
  40. package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +28 -0
  41. package/creators/pkg/files/.github/workflows/build-extension-charts.yml +26 -0
  42. package/creators/pkg/init +63 -4
  43. package/detail/provisioning.cattle.io.cluster.vue +4 -2
  44. package/edit/fleet.cattle.io.gitrepo.vue +8 -0
  45. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -4
  46. package/layouts/default.vue +11 -597
  47. package/middleware/authenticated.js +2 -14
  48. package/mixins/__tests__/chart.test.ts +40 -0
  49. package/mixins/chart.js +5 -0
  50. package/models/catalog.cattle.io.clusterrepo.js +6 -2
  51. package/models/fleet.cattle.io.cluster.js +10 -2
  52. package/models/fleet.cattle.io.gitrepo.js +3 -1
  53. package/package.json +1 -1
  54. package/pages/c/_cluster/fleet/index.vue +4 -0
  55. package/pages/c/_cluster/gatekeeper/index.vue +10 -1
  56. package/pages/c/_cluster/uiplugins/index.vue +3 -3
  57. package/plugins/steve/__tests__/header-warnings.spec.ts +238 -0
  58. package/plugins/steve/actions.js +4 -23
  59. package/plugins/steve/header-warnings.ts +91 -0
  60. package/promptRemove/management.cattle.io.project.vue +9 -6
  61. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +8 -0
  62. package/rancher-components/components/Form/Radio/RadioButton.test.ts +7 -3
  63. package/scripts/extension/parse-tag-name +30 -0
  64. package/types/shell/index.d.ts +3 -0
  65. package/utils/auth.js +17 -0
  66. package/utils/object.js +0 -1
  67. package/utils/settings.ts +2 -17
  68. package/utils/validators/__tests__/cidr.test.ts +33 -0
  69. package/utils/validators/cidr.js +5 -0
  70. package/vue-config-helper.js +135 -0
  71. package/vue.config.js +23 -139
  72. package/yarn-error.log +200 -0
  73. package/creators/pkg/files/.github/workflows/build-container.yml +0 -64
  74. package/creators/pkg/files/.github/workflows/build-extension.yml +0 -110
@@ -2291,6 +2291,7 @@ gatekeeperIndex:
2291
2291
  poweredBy: OPA Gatekeeper
2292
2292
  unavailable: OPA + Gatekeeper is not available in the system-charts catalog.
2293
2293
  violations: Violations
2294
+ deprecated: The OPA Gatekeeper chart is deprecated as of Rancher 2.8 and will be removed in a future release. Please consider switching to <a target="_blank" rel="noopener noreferrer nofollow" href="https://www.kubewarden.io">Kubewarden</a>.
2294
2295
 
2295
2296
  gatekeeperInstall:
2296
2297
  auditInterval: Auto Interval
@@ -2357,6 +2358,9 @@ growl:
2357
2358
  podSecurity:
2358
2359
  message: "The creation of this Pod would violate existing restricted policies for the adopted Namespace"
2359
2360
  title: PodSecurity violation
2361
+ kubeApiHeaderWarning:
2362
+ titleCreate: "{resourceType} Creation Warning"
2363
+ titleUpdate: "{resourceType} Update Warning"
2360
2364
 
2361
2365
  hpa:
2362
2366
  detail:
@@ -3041,7 +3045,7 @@ login:
3041
3045
  noResponse: "No response received"
3042
3046
  error: An error occurred logging in. Please try again.
3043
3047
  clientError: Invalid username or password. Please try again.
3044
- specificError: 'An error occured logging in: {msg}'
3048
+ specificError: 'An error occurred logging in: {msg}'
3045
3049
  useLocal: Use a local user
3046
3050
  loginWithProvider: Log in with {provider}
3047
3051
  username: Username
@@ -3442,7 +3446,7 @@ namespace:
3442
3446
  workloads: Workloads
3443
3447
  label: Namespace
3444
3448
  selectNamespace: Select Namespace
3445
- createNamespace: Create a New Namespace
3449
+ createNamespace: Create a new Namespace
3446
3450
  selectOrCreate: Select or Create a Namespace
3447
3451
  resourceStates:
3448
3452
  success: 'Active'
@@ -5634,6 +5638,8 @@ validation:
5634
5638
  logdna:
5635
5639
  apiKey: Required an "Api Key" to be set.
5636
5640
  invalidCron: Invalid cron schedule
5641
+ invalidCidr: "Invalid CIDR"
5642
+ invalidIP: "Invalid IP"
5637
5643
  k8s:
5638
5644
  name: Must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc').
5639
5645
  identifier:
@@ -3051,6 +3051,7 @@ login:
3051
3051
  noResponse: "没有收到响应。"
3052
3052
  error: 登录时发生错误,请重试。
3053
3053
  clientError: 无效的用户名或密码,请重试。
3054
+ specificError: '登录时发生错误:{msg}'
3054
3055
  useLocal: 使用本地账号登录
3055
3056
  loginWithProvider: 使用 {provider} 登录
3056
3057
  username: 用户名
@@ -3525,8 +3526,11 @@ navLink:
3525
3526
  label: 链接标签
3526
3527
  description:
3527
3528
  label: 链接描述
3529
+ groupImage:
3530
+ label: 组图片
3528
3531
  iconSrc:
3529
- label: 添加镜像
3532
+ tip: '图片的高度应为 21 像素,最大宽度为 200 像素。最大文件大小为 20KB。支持的格式:JPEG、PNG、SVG。'
3533
+ label: 添加图片
3530
3534
  networkpolicy:
3531
3535
  egress:
3532
3536
  label: Egress 规则
@@ -5640,6 +5644,8 @@ validation:
5640
5644
  logdna:
5641
5645
  apiKey: 需要设置“API 密钥”。
5642
5646
  invalidCron: 无效的 cron 调度
5647
+ invalidCidr: "无效的 CIDR"
5648
+ invalidIP: "无效的 IP"
5643
5649
  k8s:
5644
5650
  name: 必须由小写字母数字或“-”组成,并且开头和结尾必须是字母数字(例如“my-name”或“123-abc”)。
5645
5651
  identifier:
@@ -5776,6 +5782,7 @@ wm:
5776
5782
  containerShell:
5777
5783
  clear: 清除
5778
5784
  containerName: "容器:{label}"
5785
+ failed: "无法打开容器 shell(所有 shell 命令均未成功)\n\r"
5779
5786
  kubectlShell:
5780
5787
  title: "Kubectl: {name}"
5781
5788
 
@@ -0,0 +1,53 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import AzureCloudCreds from '@shell/cloud-credential/azure.vue';
3
+
4
+ const mockStore = {
5
+ getters: { 'i18n/t': jest.fn() },
6
+ dispatch: () => jest.fn()
7
+ };
8
+
9
+ describe('cloud credentials: Azure', () => {
10
+ const wrapper = mount(AzureCloudCreds, {
11
+ propsData: {
12
+ value: {
13
+ decodedData: {
14
+ environment: '',
15
+ subscriptionId: '',
16
+ clientId: '',
17
+ clientSecret: '',
18
+ },
19
+ setData: jest.fn()
20
+ }
21
+ },
22
+ mocks: { $store: mockStore }
23
+ });
24
+
25
+ it('should pass all the correct fields when checking if credentials are valid', async() => {
26
+ const spyDispatch = jest.spyOn(mockStore, 'dispatch');
27
+
28
+ wrapper.setData({
29
+ value: {
30
+ decodedData: {
31
+ environment: 'my-env',
32
+ subscriptionId: 'my-sub-id',
33
+ clientId: 'my-c-id',
34
+ clientSecret: 'my-c-secret',
35
+ }
36
+ }
37
+ });
38
+
39
+ await wrapper.vm.test();
40
+
41
+ expect(spyDispatch).toHaveBeenCalledWith('management/request', {
42
+ data: {
43
+ environment: 'my-env',
44
+ subscriptionId: 'my-sub-id',
45
+ clientId: 'my-c-id',
46
+ clientSecret: 'my-c-secret',
47
+ },
48
+ method: 'POST',
49
+ redirectUnauthorized: false,
50
+ url: '/meta/aksCheckCredentials'
51
+ });
52
+ });
53
+ });
@@ -40,6 +40,7 @@ export default {
40
40
  clientId,
41
41
  clientSecret,
42
42
  subscriptionId,
43
+ environment,
43
44
  } = this.value.decodedData;
44
45
 
45
46
  try {
@@ -50,6 +51,7 @@ export default {
50
51
  clientId,
51
52
  clientSecret,
52
53
  subscriptionId,
54
+ environment,
53
55
  },
54
56
  redirectUnauthorized: false,
55
57
  });
@@ -96,6 +98,7 @@ export default {
96
98
  :searchable="false"
97
99
  :required="true"
98
100
  :label="t('cluster.credential.azure.environment.label')"
101
+ data-testid="azure-cloud-credentials-environment"
99
102
  @input="value.setData('environment', $event)"
100
103
  />
101
104
  </div>
@@ -106,6 +109,7 @@ export default {
106
109
  type="text"
107
110
  :mode="mode"
108
111
  :required="true"
112
+ data-testid="azure-cloud-credentials-subscription-id"
109
113
  @input="value.setData('subscriptionId', $event)"
110
114
  />
111
115
  </div>
@@ -118,6 +122,7 @@ export default {
118
122
  type="text"
119
123
  :mode="mode"
120
124
  :required="true"
125
+ data-testid="azure-cloud-credentials-client-id"
121
126
  @input="value.setData('clientId', $event)"
122
127
  />
123
128
  </div>
@@ -128,6 +133,7 @@ export default {
128
133
  type="password"
129
134
  :mode="mode"
130
135
  :required="true"
136
+ data-testid="azure-cloud-credentials-client-secret"
131
137
  @input="value.setData('clientSecret', $event)"
132
138
  />
133
139
  </div>
@@ -99,15 +99,17 @@ export default {
99
99
  <i :class="{icon: true, ['icon-'+growl.icon]: true}" />
100
100
  </div>
101
101
  <div class="growl-text">
102
- <div>{{ growl.title }}</div>
102
+ <i
103
+ class="close hand icon icon-close"
104
+ @click="close(growl)"
105
+ />
106
+ <div class="growl-text-title">
107
+ {{ growl.title }}
108
+ </div>
103
109
  <p v-if="growl.message">
104
110
  {{ growl.message }}
105
111
  </p>
106
112
  </div>
107
- <i
108
- class="close hand icon icon-close"
109
- @click="close(growl)"
110
- />
111
113
  </div>
112
114
  </div>
113
115
  </div>
@@ -135,7 +137,7 @@ export default {
135
137
  width: 100%;
136
138
 
137
139
  @media only screen and (min-width: map-get($breakpoints, '--viewport-7')) {
138
- width: 350px;
140
+ width: 420px;
139
141
  }
140
142
  }
141
143
 
@@ -149,13 +151,15 @@ export default {
149
151
  margin: 10px;
150
152
  position: relative;
151
153
  word-break: break-all;
152
-
153
- .close {
154
- padding: 5px;
155
- }
154
+ box-shadow: 0 3px 5px 0px var(--shadow);
156
155
 
157
156
  .icon-container {
158
157
  align-self: center;
158
+ flex-basis: 10%;
159
+ padding: 10px 20px 10px 10px;
160
+ i {
161
+ font-size: 24px;
162
+ }
159
163
  }
160
164
 
161
165
  .growl-message {
@@ -164,28 +168,27 @@ export default {
164
168
  &.growl-center {
165
169
  align-items: center;
166
170
  }
167
- }
168
-
169
- .growl-text {
170
- flex-basis: 90%;
171
- padding: 10px 10px 10px 0;
172
- word-break: break-word;
173
- white-space: normal;
174
-
175
- > div {
176
- font-size: 16px;
177
- }
178
171
 
179
- > P {
180
- margin-top: 5px;
181
- }
182
- }
172
+ .growl-text {
173
+ position: relative;
174
+ flex-basis: 90%;
175
+ padding: 10px 10px 10px 0;
176
+ word-break: break-word;
177
+ white-space: normal;
178
+
179
+ .close {
180
+ position: absolute;
181
+ top: 12px;
182
+ right: 10px;
183
+ }
184
+ .growl-text-title {
185
+ font-size: 16px;
186
+ margin-bottom: 20px;
187
+ }
183
188
 
184
- .icon-container {
185
- flex-basis: 10%;
186
- padding: 10px;
187
- i {
188
- font-size: 24px;
189
+ > P {
190
+ margin-top: 5px;
191
+ }
189
192
  }
190
193
  }
191
194
  }
@@ -19,11 +19,11 @@ export default {
19
19
  <div class="col span-6">
20
20
  <ArrayList
21
21
  :key="question.variable"
22
- v-model="value[question.variable]"
22
+ v-model="value"
23
23
  :title="question.label"
24
24
  :mode="mode"
25
- :protip="false"
26
25
  :disabled="disabled"
26
+ :protip="displayTooltip"
27
27
  @input="update"
28
28
  />
29
29
  </div>
@@ -9,18 +9,24 @@ export default {
9
9
  </script>
10
10
 
11
11
  <template>
12
- <div class="row">
12
+ <div
13
+ :data-testid="`boolean-row-${question.variable}`"
14
+ class="row"
15
+ >
13
16
  <div class="col span-6">
14
17
  <Checkbox
15
18
  :mode="mode"
16
19
  :label="displayLabel"
17
20
  :value="value"
18
21
  :disabled="disabled"
22
+ :tooltip="displayTooltip"
23
+ :data-testid="`boolean-input-${question.variable}`"
19
24
  @input="$emit('input', $event)"
20
25
  />
21
26
  </div>
22
27
  <div
23
28
  v-if="showDescription"
29
+ :data-testid="`boolean-description-${question.variable}`"
24
30
  class="col span-6 mt-10"
25
31
  >
26
32
  {{ displayDescription }}
@@ -39,6 +39,7 @@ export default {
39
39
  :placeholder="question.description"
40
40
  :required="question.required"
41
41
  :value="value"
42
+ :tooltip="displayTooltip"
42
43
  @input="!$fetchState.pending && $emit('input', $event)"
43
44
  />
44
45
  </div>
@@ -4,7 +4,23 @@ import Question from './Question';
4
4
 
5
5
  export default {
6
6
  components: { LabeledSelect },
7
- mixins: [Question]
7
+ mixins: [Question],
8
+ computed: {
9
+ options() {
10
+ const options = this.question.options;
11
+
12
+ if (Array.isArray(options)) {
13
+ return options;
14
+ }
15
+
16
+ return Object.entries(options).map(([key, value]) => {
17
+ return {
18
+ value: key,
19
+ label: value,
20
+ };
21
+ });
22
+ }
23
+ }
8
24
  };
9
25
  </script>
10
26
 
@@ -14,11 +30,14 @@ export default {
14
30
  <LabeledSelect
15
31
  :mode="mode"
16
32
  :label="displayLabel"
17
- :options="question.options"
33
+ :options="options"
18
34
  :placeholder="question.description"
19
35
  :required="question.required"
36
+ :multiple="question.multiple"
20
37
  :value="value"
21
38
  :disabled="disabled"
39
+ :tooltip="displayTooltip"
40
+ :searchable="question.searchable"
22
41
  @input="$emit('input', $event)"
23
42
  />
24
43
  </div>
@@ -2,8 +2,6 @@
2
2
  import { LabeledInput } from '@components/Form/LabeledInput';
3
3
  import Question from './Question';
4
4
 
5
- // @TODO valid_chars, invalid_chars
6
-
7
5
  export default {
8
6
  components: { LabeledInput },
9
7
  mixins: [Question]
@@ -11,7 +9,10 @@ export default {
11
9
  </script>
12
10
 
13
11
  <template>
14
- <div class="row">
12
+ <div
13
+ :data-testid="`float-row-${question.variable}`"
14
+ class="row"
15
+ >
15
16
  <div class="col span-6">
16
17
  <LabeledInput
17
18
  type="text"
@@ -21,11 +22,15 @@ export default {
21
22
  :required="question.required"
22
23
  :value="value"
23
24
  :disabled="disabled"
25
+ :tooltip="displayTooltip"
26
+ :rules="rules"
27
+ :data-testid="`float-input-${question.variable}`"
24
28
  @input="val = parseFloat($event); if ( !isNaN(val) ) { $emit('input', val) }"
25
29
  />
26
30
  </div>
27
31
  <div
28
32
  v-if="showDescription"
33
+ :data-testid="`float-description-${question.variable}`"
29
34
  class="col span-6 mt-10"
30
35
  >
31
36
  {{ question.description }}
@@ -2,8 +2,6 @@
2
2
  import { LabeledInput } from '@components/Form/LabeledInput';
3
3
  import Question from './Question';
4
4
 
5
- // @TODO valid_chars, invalid_chars
6
-
7
5
  export default {
8
6
  components: { LabeledInput },
9
7
  mixins: [Question]
@@ -11,7 +9,10 @@ export default {
11
9
  </script>
12
10
 
13
11
  <template>
14
- <div class="row">
12
+ <div
13
+ :data-testid="`int-row-${question.variable}`"
14
+ class="row"
15
+ >
15
16
  <div class="col span-6">
16
17
  <LabeledInput
17
18
  type="text"
@@ -21,11 +22,15 @@ export default {
21
22
  :required="question.required"
22
23
  :value="value"
23
24
  :disabled="disabled"
25
+ :tooltip="displayTooltip"
26
+ :rules="rules"
27
+ :data-testid="`int-input-${question.variable}`"
24
28
  @input="val = parseInt($event, 10); if ( !isNaN(val) ) { $emit('input', val) }"
25
29
  />
26
30
  </div>
27
31
  <div
28
32
  v-if="showDescription"
33
+ :data-testid="`int-description-${question.variable}`"
29
34
  class="col span-6 mt-10"
30
35
  >
31
36
  {{ displayDescription }}
@@ -1,4 +1,7 @@
1
1
  import { _EDIT } from '@shell/config/query-params';
2
+ import { validateChars, validateHostname, validateLength } from '@shell/utils/validators';
3
+ import { cronSchedule } from '@shell/utils/validators/cron-schedule';
4
+ import { isValidCIDR, isValidIP } from '@shell/utils/validators/cidr';
2
5
 
3
6
  export default {
4
7
  props: {
@@ -57,6 +60,75 @@ export default {
57
60
 
58
61
  return this.$store.getters['i18n/withFallback'](`charts.${ this.chartName }."${ variable }".description`, null, this.question?.description);
59
62
  },
63
+
64
+ displayTooltip() {
65
+ if (!this.question?.tooltip) {
66
+ return null;
67
+ }
68
+ const variable = this.question?.variable;
69
+
70
+ return this.$store.getters['i18n/withFallback'](`charts.${ this.chartName }."${ variable }".tooltip`, null, this.question?.tooltip);
71
+ },
72
+
73
+ rules() {
74
+ return [
75
+ (val) => {
76
+ let errors = [];
77
+
78
+ errors = validateChars(
79
+ val,
80
+ {
81
+ validChars: this.question.valid_chars,
82
+ invalidChars: this.question.invalid_chars
83
+ },
84
+ this.displayLabel,
85
+ this.$store.getters,
86
+ errors,
87
+ );
88
+
89
+ errors = validateLength(
90
+ val,
91
+ {
92
+ minLength: this.question?.min_length,
93
+ maxLenght: this.question?.max_length,
94
+ min: this.question?.min,
95
+ max: this.question?.max,
96
+ },
97
+ this.displayLabel,
98
+ this.$store.getters,
99
+ errors,
100
+ );
101
+
102
+ if (this.question.type === 'hostname') {
103
+ errors = validateHostname(
104
+ val,
105
+ this.displayLabel,
106
+ this.$store.getters,
107
+ {},
108
+ errors,
109
+ );
110
+ }
111
+
112
+ if (this.question.type === 'cron') {
113
+ cronSchedule(
114
+ val,
115
+ this.$store.getters,
116
+ errors,
117
+ );
118
+ }
119
+
120
+ if (this.question.type === 'cidr' && !isValidCIDR(val)) {
121
+ errors.push(this.$store.getters['i18n/t']('validation.invalidCidr'));
122
+ }
123
+
124
+ if (this.question.type === 'ipaddr' && !isValidIP(val)) {
125
+ errors.push(this.$store.getters['i18n/t']('validation.invalidIP'));
126
+ }
127
+
128
+ return errors;
129
+ }
130
+ ];
131
+ }
60
132
  },
61
133
 
62
134
  created() {
@@ -29,11 +29,12 @@ export default {
29
29
  <div class="col span-12 mt-10">
30
30
  <KeyValue
31
31
  :key="question.variable"
32
- v-model="value[question.variable]"
32
+ v-model="value"
33
33
  :title="question.label"
34
34
  :mode="mode"
35
35
  :protip="false"
36
36
  :disabled="disabled"
37
+ :title-protip="displayTooltip"
37
38
  @input="update"
38
39
  />
39
40
  </div>
@@ -0,0 +1,33 @@
1
+ <script>
2
+ import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
3
+ import Question from './Question';
4
+
5
+ export default {
6
+ components: { RadioGroup },
7
+ mixins: [Question]
8
+ };
9
+ </script>
10
+
11
+ <template>
12
+ <div class="row">
13
+ <div class="col span-6">
14
+ <RadioGroup
15
+ name="question.variable"
16
+ :mode="mode"
17
+ :labels="Array.isArray(question.options) ? question.options : Object.values(question.options)"
18
+ :options="Array.isArray(question.options) ? question.options : Object.keys(question.options)"
19
+ :value="value"
20
+ :disabled="disabled"
21
+ :label="displayLabel"
22
+ :tooltip="displayTooltip"
23
+ @input="$emit('input', $event)"
24
+ />
25
+ </div>
26
+ <div
27
+ v-if="showDescription"
28
+ class="col span-6 mt-10"
29
+ >
30
+ {{ displayDescription }}
31
+ </div>
32
+ </div>
33
+ </template>
@@ -96,6 +96,7 @@ export default {
96
96
  :placeholder="question.description"
97
97
  :required="question.required"
98
98
  :value="value"
99
+ :tooltip="displayTooltip"
99
100
  @input="!$fetchState.pending && $emit('input', $event)"
100
101
  />
101
102
  </div>
@@ -118,6 +119,7 @@ export default {
118
119
  :placeholder="question.description"
119
120
  :required="question.required"
120
121
  :value="value"
122
+ :tooltip="displayTooltip"
121
123
  @input="!$fetchState.pending && $emit('input', $event)"
122
124
  />
123
125
  </div>
@@ -2,8 +2,6 @@
2
2
  import { LabeledInput } from '@components/Form/LabeledInput';
3
3
  import Question from './Question';
4
4
 
5
- // @TODO valid_chars, invalid_chars
6
-
7
5
  export default {
8
6
  components: { LabeledInput },
9
7
  mixins: [Question],
@@ -21,7 +19,10 @@ export default {
21
19
  </script>
22
20
 
23
21
  <template>
24
- <div class="row">
22
+ <div
23
+ :data-testid="`string-row-${question.variable}`"
24
+ class="row"
25
+ >
25
26
  <div class="col span-6">
26
27
  <LabeledInput
27
28
  :mode="mode"
@@ -31,11 +32,15 @@ export default {
31
32
  :required="question.required"
32
33
  :value="value"
33
34
  :disabled="disabled"
35
+ :tooltip="displayTooltip"
36
+ :rules="rules"
37
+ :data-testid="`string-input-${question.variable}`"
34
38
  @input="$emit('input', $event)"
35
39
  />
36
40
  </div>
37
41
  <div
38
42
  v-if="showDescription"
43
+ :data-testid="`string-description-${question.variable}`"
39
44
  class="col span-6 mt-10"
40
45
  >
41
46
  {{ displayDescription }}
@@ -0,0 +1,46 @@
1
+ <script>
2
+ import YamlEditor from '@shell/components/YamlEditor';
3
+ import Question from './Question';
4
+ import { _VIEW } from '@shell/config/query-params';
5
+
6
+ export default {
7
+ components: { YamlEditor },
8
+ mixins: [Question],
9
+ data() {
10
+ return { VIEW: _VIEW };
11
+ }
12
+ };
13
+ </script>
14
+
15
+ <template>
16
+ <div
17
+ :data-testid="`yaml-row-${question.variable}`"
18
+ class="row"
19
+ >
20
+ <div class="col span-6">
21
+ <h3>
22
+ {{ displayLabel }}
23
+ <i
24
+ v-if="displayTooltip"
25
+ v-clean-tooltip="displayTooltip"
26
+ class="icon icon-info icon-lg"
27
+ />
28
+ </h3>
29
+ <YamlEditor
30
+ class="yaml-editor mb-6"
31
+ :editor-mode="mode === VIEW ? 'VIEW_CODE' : 'EDIT_CODE'"
32
+ :disabled="disabled"
33
+ :value="value"
34
+ :data-testid="`yaml-input-${question.variable}`"
35
+ @input="$emit('input', $event)"
36
+ />
37
+ </div>
38
+ <div
39
+ v-if="showDescription"
40
+ :data-testid="`yaml-description-${question.variable}`"
41
+ class="col span-6 mt-10"
42
+ >
43
+ {{ displayDescription }}
44
+ </div>
45
+ </div>
46
+ </template>