Skip to content

Commit 32a614c

Browse files
fixing some validation issues when using autoPage prop
1 parent b1c2baf commit 32a614c

File tree

10 files changed

+109
-57
lines changed

10 files changed

+109
-57
lines changed

src/playground/configs/templates/PlaygroundPage.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,6 @@ const formSettings = ref({
913913
// altLabels: true, // * VStepper
914914
// bgColor: 'secondary', // * VStepper
915915
// border: 'lg', // * VStepper
916-
// canReview: false, // ! VStepper - Needs more testing and refining
917916
// color: 'yellow', // * Global, VStepper, VStepperItem
918917
// completeIcon: 'fas fa-check', // ? VStepper Not sure how this works yet
919918
// density: 'default', // * Global

src/plugin/VStepperForm.vue

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,15 @@
3535
>
3636
<v-stepper-item
3737
:color="settings.color"
38-
:disabled="headerItemDisabled(page)"
3938
:edit-icon="page.isReview ? '$complete' : settings.editIcon"
40-
:editable="page.editable"
39+
:editable="headerItemDisabled(page)"
4140
elevation="0"
4241
:error="page.error"
4342
:title="page.title"
4443
:value="getIndex(i)"
4544
></v-stepper-item>
4645
<v-divider
47-
v-if="getIndex(i) !== Object.keys(pages).length"
46+
v-if="getIndex(i) !== Object.keys(computedPages).length"
4847
:key="getIndex(i)"
4948
></v-divider>
5049
</template>
@@ -247,9 +246,14 @@ const nextButtonDisabled = computed(() => {
247246
return fieldsHaveErrors.value || isDisabled;
248247
});
249248
250-
// TODO: Make this disabled if the previous page is not editable //
251249
const canReviewPreviousButtonDisabled = computed<boolean>(() => {
252-
return stepperModel.value === pages.length && !props.canReview;
250+
const previousPage = computedPages.value[stepperModel.value - 2];
251+
252+
if (previousPage) {
253+
return previousPage.editable === false;
254+
}
255+
256+
return stepperModel.value === computedPages.value.length && !props.editable;
253257
});
254258
255259
@@ -269,12 +273,23 @@ const lastPage = computed<boolean>(() => {
269273
270274
// TODO: This needs some more work and add a setting to not allow users to jump ahead in the questions //
271275
function headerItemDisabled(page: Page): boolean {
272-
const totalSteps = Object.keys(pages).length;
276+
const totalSteps = Object.keys(computedPages.value).length;
273277
const lastStep = totalSteps - 1;
274278
275-
// If you're on the last page
279+
if (page.editable === false && currentPageHasErrors.value) {
280+
return true;
281+
}
282+
283+
const currentPageIdx = stepperModel.value - 1;
284+
const pageIdx = computedPages.value.findIndex((p) => p === page);
285+
286+
if (page.editable !== false && pageIdx < currentPageIdx) {
287+
return true;
288+
}
289+
290+
// If you're on the last step (not review page) //
276291
if (stepperModel.value === lastStep) {
277-
return !page.isReview && (!settings.value.canReview) && (!page.editable && settings.value?.editable !== false);
292+
return !page.isReview && (!settings.value.editable) && (!page.editable && settings.value?.editable !== false);
278293
}
279294
280295
return false;
@@ -297,6 +312,8 @@ function runValidation(
297312
validate()
298313
.then((response: ValidateResult) => {
299314
const errors = response.errors as unknown as ValidateResult['errors'];
315+
316+
300317
checkForPageErrors(errors, source, next);
301318
})
302319
.catch((error: Error) => {
@@ -321,13 +338,13 @@ function removePageError(pageIndex: number): void {
321338
// ------------------------ Check the if the page has errors //
322339
function checkForPageErrors(errors: ValidateResult['errors'], source: string, next = () => { }): void {
323340
const currentPage = stepperModel.value - 1;
324-
const page = pages[currentPage];
341+
const page = computedPages.value[currentPage];
325342
326343
if (!page) {
327344
return;
328345
}
329346
330-
const pageIndex = pages.findIndex((p) => p === page);
347+
const pageIndex = computedPages.value.findIndex((p) => p === page);
331348
const pageFields = page?.fields ?? [];
332349
const hasErrorInField = Object.keys(errors as object[]).some(errorKey => pageFields.some(field => field.name === errorKey));
333350
@@ -364,16 +381,36 @@ function setPageToError(pageIndex: number, page?: Page, source = 'submit'): void
364381
let debounceTimer: ReturnType<typeof setTimeout>;
365382
366383
function onFieldValidate(field: Field, next: () => void): void {
367-
const errors = parentForm.value?.errors as unknown as ValidateResult['errors'];
384+
const errors = parentForm.value?.errors as ValidateResult['errors'];
368385
const shouldAutoPage = (field.autoPage || settings.value.autoPage ? next : null) as () => void;
369386
370-
// If autoPage debounce next //
387+
// If autoPage //
371388
if (field?.autoPage || settings.value?.autoPage) {
372-
clearTimeout(debounceTimer);
373389
374-
debounceTimer = setTimeout(() => {
375-
checkForPageErrors(errors, 'field', shouldAutoPage);
376-
}, (field?.autoPageDelay ?? settings.value?.autoPageDelay));
390+
if (parentForm.value) {
391+
// First validate the page before proceeding to the next page //
392+
(parentForm.value as { validate: () => Promise<ValidateResult>; }).validate()
393+
.then((res: ValidateResult) => {
394+
if (res.valid) {
395+
// debounce next //
396+
clearTimeout(debounceTimer);
397+
debounceTimer = setTimeout(() => {
398+
checkForPageErrors(errors, 'field', shouldAutoPage);
399+
}, (field?.autoPageDelay ?? settings.value?.autoPageDelay));
400+
401+
return;
402+
}
403+
404+
const currentPage = stepperModel.value - 1;
405+
const page = computedPages.value[currentPage];
406+
const pageIndex = computedPages.value.findIndex((p) => p === page);
407+
408+
setPageToError(pageIndex, page, 'validating');
409+
})
410+
.catch((error: Error) => {
411+
console.error('Error', error);
412+
});
413+
}
377414
378415
return;
379416
}
@@ -384,7 +421,6 @@ function onFieldValidate(field: Field, next: () => void): void {
384421
385422
// -------------------------------------------------- Submit //
386423
function onSubmit(values: any): void {
387-
console.log('%c%s', 'color: #00ff00; font-weight: bold;', 'onSubmit SUBMIT FORM \n', values);
388424
emit('submit', values);
389425
}
390426
@@ -414,12 +450,12 @@ const computedPages = computed<Page[]>(() => {
414450
});
415451
416452
function whenCallback(): void {
417-
Object.values(pages).forEach((page: Page, pageIdx: number) => {
453+
Object.values(computedPages.value).forEach((page: Page, pageIdx: number) => {
418454
if (page.fields) {
419455
Object.values(page.fields).forEach((field: Field, fieldIdx) => {
420456
if (field.when) {
421457
const enabledField: boolean = field.when(modelValue.value);
422-
const indexPage = pages[pageIdx];
458+
const indexPage = computedPages.value[pageIdx];
423459
424460
if (indexPage?.fields && indexPage?.fields[fieldIdx]) {
425461
indexPage.fields[fieldIdx].type = enabledField ? originalPages[pageIdx].fields[fieldIdx].type : 'hidden';

src/plugin/components/fields/VSFCheckbox/VSFCheckbox.vue

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
v-model="modelValue"
1414
v-bind="(boundSettings as Omit<Settings, 'validateOn'>)"
1515
:density="fieldDensity"
16+
:disabled="isValidating"
1617
:error="errorMessage ? errorMessage?.length > 0 : false"
1718
:error-messages="errorMessage"
1819
@blur="onActions(validate, 'blur')"
@@ -80,6 +81,7 @@
8081
:id="option.id"
8182
v-model="modelValue"
8283
:density="fieldDensity"
84+
:disabled="isValidating"
8385
:error="errorMessage ? errorMessage?.length > 0 : false"
8486
:error-messages="errorMessage"
8587
:hide-details="true"
@@ -144,14 +146,22 @@ onUnmounted(() => {
144146
145147
146148
// ------------------------- Validate On Actions //
149+
const isValidating = ref<boolean>(field?.disabled as boolean);
150+
147151
async function onActions(validate: FieldValidateResult, action: ValidateAction): Promise<void> {
148-
await useOnActions({
149-
action,
150-
emit,
151-
field,
152-
settingsValidateOn: settings.value.validateOn,
153-
validate,
154-
});
152+
if (!isValidating.value) {
153+
isValidating.value = true;
154+
155+
await useOnActions({
156+
action: field?.autoPage ? 'click' : action,
157+
emit,
158+
field,
159+
settingsValidateOn: settings.value.validateOn,
160+
validate,
161+
}).then(() => {
162+
isValidating.value = false;
163+
});
164+
}
155165
}
156166
157167

src/plugin/components/fields/VSFRadio/VSFRadio.vue

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
:append-icon="field?.appendIcon"
3939
:density="fieldDensity"
4040
:direction="field?.direction"
41+
:disabled="isValidating"
4142
:error="hasErrors"
4243
:error-messages="errorMessage || field?.errorMessages"
4344
:hideDetails="field?.hideDetails || settings?.hideDetails"
@@ -67,7 +68,9 @@
6768
:name="field.name"
6869
:style="radioStyle"
6970
:value="option.value"
70-
@click="onActions(validate, 'click')"
71+
@blur="onActions(validate, 'blur')"
72+
@change="onActions(validate, 'change')"
73+
@input="onActions(validate, 'input')"
7174
>
7275
</v-radio>
7376
</div>
@@ -111,14 +114,22 @@ onUnmounted(() => {
111114
112115
113116
// ------------------------- Validate On Actions //
117+
const isValidating = ref<boolean>(field?.disabled as boolean);
118+
114119
async function onActions(validate: FieldValidateResult, action: ValidateAction): Promise<void> {
115-
await useOnActions({
116-
action,
117-
emit,
118-
field,
119-
settingsValidateOn: settings.value.validateOn,
120-
validate,
121-
});
120+
if (!isValidating.value) {
121+
isValidating.value = true;
122+
123+
await useOnActions({
124+
action: field?.autoPage ? 'click' : action,
125+
emit,
126+
field,
127+
settingsValidateOn: settings.value.validateOn,
128+
validate,
129+
}).then(() => {
130+
isValidating.value = false;
131+
});
132+
}
122133
}
123134
124135

src/plugin/components/fields/VSFSwitch/VSFSwitch.vue

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
v-bind="(boundSettings as Omit<Settings, 'validateOn'>)"
1313
v-model="modelValue"
1414
:density="fieldDensity"
15+
:disabled="isValidating"
1516
:error="errorMessage ? errorMessage?.length > 0 : false"
1617
:error-messages="errorMessage"
1718
@blur="onActions((validate as ValidateFieldResult), 'blur')"
@@ -61,14 +62,22 @@ onUnmounted(() => {
6162
6263
6364
// ------------------------- Validate On Actions //
65+
const isValidating = ref<boolean>(field?.disabled as boolean);
66+
6467
async function onActions(validate: ValidateFieldResult, action: ValidateAction): Promise<void> {
65-
await useOnActions({
66-
action,
67-
emit,
68-
field,
69-
settingsValidateOn: settings.value.validateOn,
70-
validate,
71-
});
68+
if (!isValidating.value) {
69+
isValidating.value = true;
70+
71+
await useOnActions({
72+
action: field?.autoPage ? 'click' : action,
73+
emit,
74+
field,
75+
settingsValidateOn: settings.value.validateOn,
76+
validate,
77+
}).then(() => {
78+
isValidating.value = false;
79+
});
80+
}
7281
}
7382
7483

src/plugin/components/shared/PageReviewContainer.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
color="background"
2626
>
2727
<v-list-item
28-
v-if="settings.canReview && checkIfEditable(field)"
29-
@click="settings.canReview ? goToQuestion(field) : null"
28+
v-if="checkIfEditable(field)"
29+
@click="settings.editable ? goToQuestion(field) : null"
3030
>
3131
<v-list-item-title>
3232
{{ field.label }}

src/plugin/composables/helpers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export const useBuildSettings: UseBuildSettings = (stepperProps: Settings) => {
3131
autoPageDelay: stepperProps.autoPageDelay,
3232
bgColor: stepperProps.bgColor,
3333
border: stepperProps.border,
34-
canReview: stepperProps.canReview,
3534
color: stepperProps.color,
3635
density: stepperProps.density,
3736
disabled: stepperProps.disabled,

src/plugin/types/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ export interface ResponsiveColumns {
122122
export interface Field {
123123
autoPage?: Props['autoPage'];
124124
autoPageDelay?: Props['autoPageDelay'];
125-
canReview?: Props['canReview'];
126125
class?: string;
127126
color?: Props['color'];
128127
columns?: Props['fieldColumns'];
@@ -169,7 +168,6 @@ export interface Page {
169168

170169

171170
// -------------------------------------------------- Props //
172-
// TODO: Revert this back to the stupid way //
173171
export interface Props extends /* @vue-ignore */ VStepperProps, VStepperWindowItemProps {
174172
// Required //
175173
pages: Page[];
@@ -178,7 +176,6 @@ export interface Props extends /* @vue-ignore */ VStepperProps, VStepperWindowIt
178176
// Optional //
179177
autoPage?: boolean;
180178
autoPageDelay?: number;
181-
canReview?: boolean; // TODO: Determine a better prop name for canReview //
182179
color?: string | undefined;
183180
density?: GlobalDensity;
184181
direction?: 'horizontal' | 'vertical';

src/plugin/utils/props.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
export const AllProps = {
22
autoPageDelay: 250,
3-
canReview: true,
4-
5-
// density: 'default' as const,
63
direction: 'horizontal' as const,
74
disabled: false,
85
editable: true,

src/stores/props.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,6 @@ export const usePropsStore = defineStore('props', () => {
8585
name: 'border',
8686
type: 'string | number | boolean',
8787
},
88-
{
89-
default: 'true',
90-
desc: 'Determines if the user can review the form after completion.',
91-
name: 'canReview',
92-
type: 'boolean',
93-
},
9488
{
9589
default: 'undefined',
9690
desc: 'Applies specified color to the control and fields - supports utility colors (for example success or purple) or css color (#033 or rgba(255, 0, 0, 0.5)).',

0 commit comments

Comments
 (0)