358 lines
No EOL
11 KiB
Vue
358 lines
No EOL
11 KiB
Vue
<template>
|
|
<Form
|
|
:validation-schema="schema"
|
|
@submit="onSubmit"
|
|
@invalid-submit="onInvalidSubmit"
|
|
v-slot="{ values, meta }"
|
|
class="w-50"
|
|
>
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<h2 class="mx-2 mt-4 mb-0">FORM VALIDATION TEST</h2>
|
|
<p class="mx-2">NOTE: This page is testing functionality only; styling will not match Figma mocks.</p>
|
|
</div>
|
|
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<h4 class="mx-2 mt-4 mb-0">How many chips are we repairing? (Required)</h4>
|
|
<div
|
|
role="radiogroup"
|
|
aria-labelledby="demo-group"
|
|
class="d-flex flex-row p-0"
|
|
>
|
|
<h4 class="sr-only" id="demo-group">
|
|
How many chips are we repairing?
|
|
</h4>
|
|
<listButtonHorizontal
|
|
groupName="demo"
|
|
aria-required="true"
|
|
buttonID="1"
|
|
buttonLabel="1"
|
|
value="1"
|
|
buttonLabelSubCopy=""
|
|
textPosition="text-center"
|
|
sizeInRem="1"
|
|
:totalInGroup="3"
|
|
:positionInGroup="1"
|
|
screenReaderOnlyText=" opens new window"
|
|
/>
|
|
<listButtonHorizontal
|
|
groupName="demo"
|
|
aria-required="true"
|
|
buttonID="2"
|
|
buttonLabel="2"
|
|
value="2"
|
|
buttonLabelSubCopy=""
|
|
textPosition="text-center"
|
|
sizeInRem="1"
|
|
:totalInGroup="3"
|
|
:positionInGroup="2"
|
|
screenReaderOnlyText=" opens new window"
|
|
/>
|
|
<listButtonHorizontal
|
|
groupName="demo"
|
|
aria-required="true"
|
|
buttonID="3"
|
|
buttonLabel="3"
|
|
value="3"
|
|
buttonLabelSubCopy=""
|
|
textPosition="text-center"
|
|
sizeInRem="1"
|
|
:totalInGroup="3"
|
|
:positionInGroup="3"
|
|
screenReaderOnlyText=" opens new window"
|
|
/>
|
|
</div>
|
|
<div class="row px-3 form-test-error">
|
|
<error-message name="demo"></error-message>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<div class="col">
|
|
<h4 class="mx-2 mt-4 mb-0">Which windshield part needs fixed? (Required)</h4>
|
|
<fieldset>
|
|
<legend class="sr-only">Which windshield part needs fixed?</legend>
|
|
<listButton
|
|
isMultiSelect
|
|
buttonID="Single Windshield"
|
|
buttonLabel="Single Windshield"
|
|
value="Single Windshield"
|
|
groupName="demo2"
|
|
/>
|
|
<listButton
|
|
isMultiSelect
|
|
buttonID="Driver Side"
|
|
buttonLabel="Driver Side"
|
|
value="Driver Side"
|
|
groupName="demo2"
|
|
/>
|
|
<listButton
|
|
isMultiSelect
|
|
buttonID="Passenger Side"
|
|
buttonLabel="Passenger Side"
|
|
value="Passenger Side"
|
|
groupName="demo2"
|
|
/>
|
|
</fieldset>
|
|
</div>
|
|
<div class="row px-3 form-test-error">
|
|
<error-message name="demo2"></error-message>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<div class="col">
|
|
<h4 class="mx-2 mt-4 mb-0">demo3: Enter your VIN (Required)</h4>
|
|
<textInput
|
|
name="demo3"
|
|
type="text"
|
|
label="VIN (Required)"
|
|
:isRequired="true"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- <div class="row g-2 mt-6 mx-4">
|
|
<div class="col">
|
|
<h4 class="mx-2 mt-4 mb-0">demo4: Enter some numbers (optional)</h4>
|
|
<textInput
|
|
name="demo4"
|
|
type="text"
|
|
label="Numbers Only"
|
|
:isRequired="true"
|
|
/>
|
|
</div>
|
|
</div> -->
|
|
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<h4 class="mx-2 mt-4 mb-0">demo5: Where's your damage? (Required)</h4>
|
|
<div
|
|
aria-labelledby="demo5-group"
|
|
class="d-flex flex-row p-0"
|
|
>
|
|
<h4 class="sr-only" id="demo5-group">
|
|
Where's your damage?
|
|
</h4>
|
|
<listCard
|
|
isMultiSelect
|
|
buttonImage="windshield-damage.svg"
|
|
buttonLabel="Windshield"
|
|
value="Windshield"
|
|
altText=""
|
|
buttonID="List Card Checkbox a"
|
|
groupName="demo5"
|
|
/>
|
|
<listCard
|
|
isMultiSelect
|
|
buttonImage="side-window-damage-left-all.svg"
|
|
buttonLabel="Side Door"
|
|
value="Side Door"
|
|
altText=""
|
|
buttonID="List Card Checkbox b"
|
|
groupName="demo5"
|
|
/>
|
|
<listCard
|
|
isMultiSelect
|
|
buttonImage="back-glass-damage.svg"
|
|
buttonLabel="Rear Window"
|
|
value="Rear Window"
|
|
altText=""
|
|
buttonID="List Card Checkbox c"
|
|
groupName="demo5"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row px-3 form-test-error">
|
|
<error-message name="demo5"></error-message>
|
|
</div>
|
|
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<h4 class="mx-2 mt-4 mb-0">demo6: What's your windshield damage? (Required if demo5 "Windshield" is selected)</h4>
|
|
<div
|
|
role="radiogroup"
|
|
aria-labelledby="demo6-group"
|
|
class="d-flex flex-row p-0"
|
|
>
|
|
<h4 class="sr-only" id="demo6-group">
|
|
What's your windshield damage?
|
|
</h4>
|
|
<listCard
|
|
isRadio
|
|
buttonImage="windshield-damage.svg"
|
|
buttonLabel="Crack"
|
|
value="Crack"
|
|
buttonLabelSubCopy="My damage is larger than six inches."
|
|
altText=""
|
|
buttonID="Crack Checkbox a"
|
|
groupName="demo6"
|
|
/>
|
|
<listCard
|
|
isRadio
|
|
buttonImage="windshield-damage.svg"
|
|
buttonLabel="Chip(s)"
|
|
value="Chip"
|
|
buttonLabelSubCopy="I have three or fewer chips smaller than six inches."
|
|
altText=""
|
|
buttonID="Chip Checkbox b"
|
|
groupName="demo6"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row px-3 form-test-error">
|
|
<error-message name="demo6"></error-message>
|
|
</div>
|
|
|
|
<!-- <div class="row g-2 mt-6 mx-4">
|
|
<div class="col">
|
|
<h4 class="mx-2 mt-4 mb-0">Password (Required, enter any string)</h4>
|
|
<textInput
|
|
name="password"
|
|
type="text"
|
|
label="Password (Required)"
|
|
:isRequired="true"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-2 mt-6 mx-4">
|
|
<div class="col">
|
|
<h4 class="mx-2 mt-4 mb-0">Confirm Password (Required, must match above Password field)</h4>
|
|
<textInput
|
|
name="passwordConfirmation"
|
|
type="text"
|
|
label="Confirm Password (Required)"
|
|
:isRequired="true"
|
|
/>
|
|
</div>
|
|
</div> -->
|
|
|
|
<div class="row px-3 form-test-error" data-focus-target="demoX">
|
|
<error-message name="demoX"></error-message>
|
|
</div>
|
|
|
|
<div class="g-2 mt-6 mx-4">
|
|
<button class="mb-6" :class="!meta.valid && 'form-test-invalid'">Get your estimate</button>
|
|
<p>Current Form Values:</p>
|
|
<pre>{{ values }}</pre>
|
|
<p>Is Form Valid? (Meta.valid):</p>
|
|
<pre>{{ meta.valid }}</pre>
|
|
</div>
|
|
|
|
</Form>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import { Form, ErrorMessage } from 'vee-validate';
|
|
import * as Yup from "yup";
|
|
|
|
import listCard from "@/ux-components/list-card/list-card";
|
|
import textInput from "@/common-components/text-input/text-input";
|
|
import listButton from "@/ux-components/list-button/list-button";
|
|
import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-button-horizontal";
|
|
|
|
export default {
|
|
name: "App",
|
|
data() {
|
|
const errorMessages = {
|
|
demo: 'Please select chip(s)',
|
|
demo2: 'Please select a windshield part',
|
|
demo3: 'You entered an invalid VIN. Please check to make sure that you inserted a 17 digit, alpha-numeric number. VINs do not contain the letters I, O or Q.',
|
|
demo4: 'Invalid Entry. Must enter only numbers, no letters.',
|
|
demo5: 'Please select damage location',
|
|
};
|
|
return {
|
|
schema: Yup.object().shape({
|
|
demo: Yup.string().required(errorMessages.demo),
|
|
demo2: Yup.array().required(errorMessages.demo2).min(1, errorMessages.demo2),
|
|
demo3: Yup.string()
|
|
.uppercase()
|
|
.required('Please enter a VIN.')
|
|
.min(17, errorMessages.demo3)
|
|
.max(17, errorMessages.demo3)
|
|
.matches('^[^IOQ]+$', errorMessages.demo3),
|
|
// demo4: Yup.string().matches('^[0-9]*$', errorMessages.demo4),
|
|
demo5: Yup.array().required(errorMessages.demo5).min(1, errorMessages.demo5),
|
|
demo6: Yup.string()
|
|
.when('demo5', function (demo5, schema, value) {
|
|
if (demo5) {
|
|
const demo5String = demo5.toString();
|
|
if (demo5String.includes("Windshield")) {
|
|
// make demo6 required
|
|
return schema.required('You must pick an option for Windshield Damage.')
|
|
}
|
|
return schema;
|
|
}
|
|
return schema;
|
|
}),
|
|
demoX: Yup.string().notRequired(),
|
|
// PASSWORD EXAMPLE, NOT BEING USED NOW
|
|
// password: Yup.string().min(6).required(),
|
|
// passwordConfirmation: Yup.string()
|
|
// .required()
|
|
// .oneOf([Yup.ref("password")], "Passwords do not match"),
|
|
}).test('repair-replace-conflict',
|
|
null, // need to pass null as error message so it won't get added to list of errors
|
|
function(value) {
|
|
// whole form test
|
|
console.log('ALL FORM VALUES: ', value);
|
|
|
|
// test problem scenario... return new error if conditions are true, allow submit if not
|
|
if (value.demo5 && value.demo6) {
|
|
const demo5String = value.demo5.toString();
|
|
console.log('demo5String: ', demo5String);
|
|
console.log('demo6: ', value.demo6);
|
|
|
|
if (demo5String.includes("Windshield") && value.demo5.length > 1 && value.demo6 == "Chip") {
|
|
return this.createError({ name: 'demoX', message: "Sorry, at this time we can't schedule a repair and replace at the same time. Please schedule these appointments separately as they are different technicians. Thanks.", path: 'demoX' });
|
|
}
|
|
return true;
|
|
}
|
|
return true;
|
|
}),
|
|
}
|
|
},
|
|
components: {
|
|
listCard,
|
|
listButton,
|
|
listButtonHorizontal,
|
|
textInput,
|
|
Form,
|
|
ErrorMessage,
|
|
},
|
|
methods: {
|
|
onSubmit(values) {
|
|
window.alert('Success! Submitted values: ' + JSON.stringify(values, null ,2))
|
|
console.log('submitted: ', JSON.stringify(values, null ,2));
|
|
},
|
|
onInvalidSubmit({ values, errors, results }) {
|
|
// identify the first error field and put focus on it
|
|
console.log("values: ", values);
|
|
console.log("errors: ", errors);
|
|
console.log("results: ", results);
|
|
// get errorEls and order by alpha
|
|
const errorEls = Object.keys(errors).sort();
|
|
const firstErrorEl = errorEls[0];
|
|
console.log('firstErrorEl: ', firstErrorEl);
|
|
if (firstErrorEl) {
|
|
const qsString = "[data-focus-target='" + firstErrorEl + "']";
|
|
document.querySelector(qsString).focus();
|
|
}
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.form-test-error {
|
|
color: red;
|
|
font-weight: bold;
|
|
}
|
|
.form-test-invalid {
|
|
opacity: 0.5;
|
|
}
|
|
.has-error {
|
|
border: 1px solid red;
|
|
}
|
|
</style> |