Skip to content

Commit 3e9ae35

Browse files
committed
Add vee-validate to ArrayList
Signed-off-by: Phillip Rak <rak.phillip@gmail.com>
1 parent 957e4e8 commit 3e9ae35

3 files changed

Lines changed: 64 additions & 6 deletions

File tree

shell/assets/translations/en-us.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ authConfig:
797797
label: Scopes
798798
placeholder: openid
799799
protip: The <code>openid</code>, <code>profile</code>, and <code>email</code> scopes are required and cannot be removed.
800+
missingRequired: "The required {count, plural, =1 {scope is} other {scopes are}} missing: {scopes}"
800801
pkce:
801802
label: Enable PKCE (S256)
802803
tooltip: Enable Proof Key for Code Exchange (PKCE) using the S256 code challenge method. When enabled, this client must use PKCE with S256 for authorization requests.

shell/components/form/ArrayList.vue

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
<script>
2-
import { ref, watch, computed } from 'vue';
2+
import { ref, watch, computed, toRef } from 'vue';
33
import debounce from 'lodash/debounce';
44
import { _EDIT, _VIEW } from '@shell/config/query-params';
55
import { removeAt } from '@shell/utils/array';
66
import { TextAreaAutoGrow } from '@components/Form/TextArea';
77
import { clone } from '@shell/utils/object';
88
import { LabeledInput } from '@components/Form/LabeledInput';
9+
import LabeledTooltip from '@components/LabeledTooltip/LabeledTooltip.vue';
10+
import { useVeeValidateField } from '@shell/composables/useVeeValidateField';
911
const DEFAULT_PROTIP = 'Tip: Paste lines into any list field for easy bulk entry';
1012
1113
export default {
1214
emits: ['add', 'remove', 'update:value'],
1315
14-
components: { TextAreaAutoGrow, LabeledInput },
15-
props: {
16+
components: {
17+
TextAreaAutoGrow, LabeledInput, LabeledTooltip
18+
},
19+
props: {
1620
value: {
1721
type: Array,
1822
default: null,
@@ -106,7 +110,17 @@ export default {
106110
componentTestid: {
107111
type: String,
108112
default: 'array-list',
109-
}
113+
},
114+
115+
/**
116+
* Field name for vee-validate integration. When provided, the component
117+
* registers with a parent form context for schema-level validation.
118+
*/
119+
name: {
120+
type: String,
121+
default: null,
122+
},
123+
110124
},
111125
112126
setup(props, { emit }) {
@@ -126,6 +140,17 @@ export default {
126140
return props.mode === _VIEW;
127141
});
128142
143+
// vee-validate integration: array-level validation via a parent form schema.
144+
// Per-item validation continues to use the existing `rules` prop on
145+
// LabeledInput.
146+
const arrayValue = computed(() => props.value || []);
147+
const { effectiveValidationMessage, veeHandleBlur, veeValidate } = useVeeValidateField({
148+
name: toRef(props, 'name'),
149+
rules: ref([]),
150+
value: arrayValue,
151+
validationMessage: ref(null),
152+
});
153+
129154
/**
130155
* Cleanup rows and emit input
131156
*/
@@ -144,6 +169,8 @@ export default {
144169
}
145170
}
146171
emit('update:value', out);
172+
veeHandleBlur(undefined, false);
173+
veeValidate();
147174
};
148175
149176
const lastUpdateWasFromValue = ref(false);
@@ -177,6 +204,7 @@ export default {
177204
queueUpdate,
178205
isView,
179206
update,
207+
effectiveValidationMessage,
180208
};
181209
},
182210
@@ -431,10 +459,18 @@ export default {
431459
</slot>
432460
</div>
433461
</div>
462+
<LabeledTooltip
463+
v-if="effectiveValidationMessage"
464+
:value="effectiveValidationMessage"
465+
/>
434466
</div>
435467
</template>
436468

437469
<style lang="scss" scoped>
470+
.array-list-main-container {
471+
position: relative;
472+
}
473+
438474
.title {
439475
margin-bottom: 10px;
440476
}

shell/edit/auth/oidc.vue

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export default {
5353
const modelId = ref(null);
5454
const sloTypeRef = ref(null);
5555
const customEndpointEnabled = ref(false);
56+
const requiredScopesRef = ref([]);
5657
5758
const isAmazonCognito = computed(() => modelId.value === 'cognito');
5859
const isKeycloak = computed(() => modelId.value === 'keycloakoidc');
@@ -87,6 +88,17 @@ export default {
8788
8889
endSessionEndpoint: sloEndSessionEndpointUiEnabled.value ? requiredUrlField('authConfig.oidc.endSessionEndpoint.title') : optionalField,
8990
91+
scope: z.preprocess(
92+
(v) => (Array.isArray(v) ? v : []),
93+
z.array(z.string()).refine(
94+
(arr) => requiredScopesRef.value?.every((s) => arr.includes(s)),
95+
(arr) => {
96+
const missing = requiredScopesRef.value?.filter((s) => !arr.includes(s));
97+
98+
return { message: t('authConfig.oidc.scope.missingRequired', { scopes: missing?.join(', '), count: missing?.length }) };
99+
}
100+
)
101+
),
90102
})
91103
));
92104
@@ -109,6 +121,7 @@ export default {
109121
modelId,
110122
sloTypeRef,
111123
customEndpointEnabled,
124+
requiredScopesRef,
112125
isAmazonCognito,
113126
isKeycloak,
114127
isGenericOidc,
@@ -168,7 +181,7 @@ export default {
168181
},
169182
170183
validationPassed() {
171-
if ( this.model.enabled && !this.editConfig ) {
184+
if ( this.model?.enabled && !this.editConfig ) {
172185
return true;
173186
}
174187
@@ -181,7 +194,7 @@ export default {
181194
* The scopes for given auth provider (model.id) have format of ['scope1 scope2 scope3']
182195
*/
183196
requiredScopes() {
184-
return this.model.id ? (BASE_SCOPES[this.model.id] || []) ? (BASE_SCOPES[this.model.id] || [])[0].split(' ') : [] : [];
197+
return this.model?.id ? (BASE_SCOPES[this.model.id] || []) ? (BASE_SCOPES[this.model.id] || [])[0].split(' ') : [] : [];
185198
},
186199
187200
isLogoutAllSupported() {
@@ -219,6 +232,13 @@ export default {
219232
this.customEndpointEnabled = v;
220233
},
221234
235+
requiredScopes: {
236+
handler(v) {
237+
this.requiredScopesRef = v;
238+
},
239+
immediate: true,
240+
},
241+
222242
'oidcUrls.url'() {
223243
this.updateEndpoints();
224244
},
@@ -686,6 +706,7 @@ export default {
686706
<div class="col span-6">
687707
<ArrayList
688708
v-model:value="oidcScope"
709+
name="scope"
689710
:mode="mode"
690711
:title="t('authConfig.oidc.scope.label')"
691712
:value-placeholder="t('authConfig.oidc.scope.placeholder')"

0 commit comments

Comments
 (0)