CSR-254: create form-test page and begin testing validation
This commit is contained in:
parent
59b6be78f2
commit
549a8b3593
5 changed files with 317 additions and 16 deletions
|
|
@ -16,6 +16,7 @@
|
|||
"core-js": "^3.6.5",
|
||||
"http-status-codes": "^2.1.4",
|
||||
"jest-junit": "^13.0.0",
|
||||
"vee-validate": "^4.5.7",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.11",
|
||||
"vuex": "^4.0.2",
|
||||
|
|
|
|||
162
src/layouts/form-test/form-test.vue
Normal file
162
src/layouts/form-test/form-test.vue
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<Form :validation-schema="schema" @submit="onSubmit" v-slot="{values}">
|
||||
<div class="row px-3">
|
||||
<!-- The role="radiogroup" and aria-labelledby must be included in the parent component for the group -->
|
||||
<h6 class="mx-2 my-0">How many chips are we repairing?</h6>
|
||||
<div role="radiogroup" aria-labelledby="demo-group" class="d-flex flex-row p-0">
|
||||
<!-- The h3 and id must be included. The id must match the aria-labelledby of the parent div. -->
|
||||
<h3 class="sr-only" id="demo-group">
|
||||
How many chips are we repairing?
|
||||
</h3>
|
||||
<listButtonHorizontal
|
||||
groupName="demo"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="1"
|
||||
value="1"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
v-bind:totalInGroup="3"
|
||||
v-bind:positionInGroup="1"
|
||||
screenReaderOnlyText=" opens new window"
|
||||
/>
|
||||
<listButtonHorizontal
|
||||
groupName="demo"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="2"
|
||||
value="2"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
v-bind:totalInGroup="3"
|
||||
v-bind:positionInGroup="2"
|
||||
screenReaderOnlyText=" opens new window"
|
||||
/>
|
||||
<listButtonHorizontal
|
||||
groupName="demo"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="3"
|
||||
value="3"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
v-bind:totalInGroup="3"
|
||||
v-bind:positionInGroup="3"
|
||||
screenReaderOnlyText=" opens new window"
|
||||
/>
|
||||
</div>
|
||||
<div class="row px-3">
|
||||
<error-message name="demo"></error-message>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<h6 class="mx-2 my-4">Which windshield part needs fixed?</h6>
|
||||
<fieldset>
|
||||
<legend class="sr-only">Which windshield part needs fixed?</legend>
|
||||
<listCard
|
||||
isMultiSelectHorizontal
|
||||
buttonImage="windshield-damage.svg"
|
||||
buttonLabel="Single Windshield"
|
||||
value="Single Windshield"
|
||||
altText=""
|
||||
buttonID="List Card Horizontal Checkbox a"
|
||||
groupName="demo2"
|
||||
/>
|
||||
<listCard
|
||||
isMultiSelectHorizontal
|
||||
buttonImage="windshield-damage.svg"
|
||||
buttonLabel="Driver Side"
|
||||
value="Driver Side"
|
||||
altText=""
|
||||
buttonID="List Card Horizontal Checkbox b"
|
||||
groupName="demo2"
|
||||
/>
|
||||
<listCard
|
||||
isMultiSelectHorizontal
|
||||
buttonImage="windshield-damage.svg"
|
||||
buttonLabel="Passenger Side"
|
||||
value="Passenger Side"
|
||||
altText=""
|
||||
buttonID="List Card Horizontal Checkbox c"
|
||||
groupName="demo2"
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="row px-3">
|
||||
<error-message name="demo2"></error-message>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="g-2 mx-2 my-4">
|
||||
<button>Get your estimate</button>
|
||||
<p>Values</p>
|
||||
<pre>{{ values }}</pre>
|
||||
</div>
|
||||
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { Form, ErrorMessage } from 'vee-validate';
|
||||
|
||||
|
||||
import buttonPrimary from "@/ux-components/button-primary/button-primary";
|
||||
import buttonSecondary from "@/ux-components/button-secondary/button-secondary";
|
||||
import buttonBack from "@/common-components/button-back/button-back";
|
||||
import listCard from "@/ux-components/list-card/list-card";
|
||||
import listButton from "@/ux-components/list-button/list-button";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||
import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-button-horizontal";
|
||||
import checkbox from "@/ux-components/checkbox/checkbox";
|
||||
export default {
|
||||
name: "App",
|
||||
data() {
|
||||
return {
|
||||
schema: { // VALIDATION SETUP
|
||||
demo: (value) => {
|
||||
if (value && value.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return 'Please select chip(s)';
|
||||
},
|
||||
demo2: (value) => {
|
||||
if (value && value.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return 'Please select a windshield part';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
// buttonPrimary,
|
||||
// buttonSecondary,
|
||||
// buttonBack,
|
||||
listCard,
|
||||
// listButton,
|
||||
// alert,
|
||||
// vehicleBanner,
|
||||
listButtonHorizontal,
|
||||
Form,
|
||||
ErrorMessage,
|
||||
// checkbox,
|
||||
// funnelHeader,
|
||||
},
|
||||
methods: {
|
||||
onSubmit(values) {
|
||||
console.log('submitted: ', JSON.stringify(values, null ,2));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -3,6 +3,7 @@ import { storeActions } from "@/constants/store-actions.js";
|
|||
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
|
||||
import { routingTable } from "@/router/router-constants/routing-table.js";
|
||||
import ComponentTest from "@/layouts/component-test/component-test.vue";
|
||||
import FormTest from "@/layouts/form-test/form-test.vue";
|
||||
import NotFound from "@/layouts/not-found/not-found.vue";
|
||||
import store from "@/store";
|
||||
|
||||
|
|
@ -17,6 +18,11 @@ const routes = [
|
|||
name: "ComponentTest",
|
||||
component: ComponentTest,
|
||||
},
|
||||
{
|
||||
path: "/form-test", // This is a temporary route for testing.
|
||||
name: "FormTest",
|
||||
component: FormTest,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
beforeEnter(to, from, next) {
|
||||
|
|
|
|||
|
|
@ -2,16 +2,46 @@
|
|||
<template>
|
||||
<!-- IMPORTANT: Refrain from using more than 4 horizontal buttons on desktop, 3 on mobile. -->
|
||||
<div v-if="isMultiSelect" class="list-group list-button-horizontal d-flex flex-column w-100 mb-2">
|
||||
<input type="checkbox" :id="buttonID" :name="groupName" :value="buttonID" :aria-required="isRequired">
|
||||
<label tabindex="-1" aria-checked="false" :for="buttonID" :aria-labelledby="buttonID" class="d-flex flex-column justify-content-center py-3 px-4" :class="isFirstOrLastButton">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@keyup.space="handleClick(value)"
|
||||
@click="handleClick(value)"
|
||||
/>
|
||||
<label
|
||||
tabindex="-1"
|
||||
aria-checked="false"
|
||||
:for="buttonID"
|
||||
:aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
:class="isFirstOrLastButton"
|
||||
>
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="[this.textPosition]">{{buttonLabelSubCopy}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else class="col list-group list-button-horizontal d-flex flex-column mb-2">
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonID" aria-required="true" @keyup.space="handleClick()" @click="handleClick()" />
|
||||
<label tabindex="-1" aria-checked="false" :for="buttonID" :aria-labelledby="buttonID" class="d-flex flex-column justify-content-center py-3 px-4" :class="isFirstOrLastButton">
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
aria-required="true"
|
||||
@keyup.space="handleClick(value)"
|
||||
@click="handleClick(value)"
|
||||
/>
|
||||
<label
|
||||
tabindex="-1"
|
||||
aria-checked="false"
|
||||
:for="buttonID"
|
||||
:aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
:class="isFirstOrLastButton"
|
||||
>
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="[this.textPosition]">{{buttonLabelSubCopy}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
|
|
@ -21,6 +51,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from 'vue';
|
||||
import { useField } from 'vee-validate';
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
|
||||
export default {
|
||||
|
|
@ -39,9 +71,14 @@ export default {
|
|||
totalInGroup: Number, /* Required, total number of buttons in group. Used to tell first and last in group to apply border radius. */
|
||||
positionInGroup: Number, /* Required, position of button in group. Example, 1,2,3 */
|
||||
isRequired: Boolean,
|
||||
value: { // Field initial value
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasError: false,
|
||||
isLoaderDisplayed: false,
|
||||
};
|
||||
},
|
||||
|
|
@ -49,8 +86,9 @@ export default {
|
|||
displayLoader() {
|
||||
this.isLoaderDisplayed = true;
|
||||
},
|
||||
handleClick() {
|
||||
handleClick(value) {
|
||||
this.loaderEnabled && this.displayLoader();
|
||||
this.handleChange(value);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -67,6 +105,18 @@ export default {
|
|||
return className;
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange } = useField(groupName, undefined, {
|
||||
type: inputType,
|
||||
checkedValue: value
|
||||
});
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,61 @@
|
|||
<template>
|
||||
<!-- Heavily documented below -->
|
||||
<div v-if="isMultiSelect" class="list-card w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="checkbox" :id="buttonID" :name="groupName" :value="buttonLabel" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-column w-100 align-items-center pt-4 pb-2" tabindex="1">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonLabel"
|
||||
:aria-required="isRequired"
|
||||
@keyup.space="handleChange(value)"
|
||||
@click="handleChange(value)"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-column w-100 align-items-center pt-4 pb-2"
|
||||
tabindex="1"
|
||||
>
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<p class="small m-0 order-3">{{buttonLabel}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 order-4">{{buttonLabelSubCopy}}</p>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isRadio" class="list-card w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonID" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-column w-100 align-items-center pt-4 pb-2" tabindex="1">
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonID"
|
||||
:aria-required="isRequired"
|
||||
@keyup.space="handleChange(value)"
|
||||
@click="handleChange(value)"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-column w-100 align-items-center pt-4 pb-2"
|
||||
tabindex="1"
|
||||
>
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<p class="small mt-2 mb-0 order-2">{{buttonLabel}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 order-3">{{buttonLabelSubCopy}}</p>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isMultiSelectHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="checkbox" :id="buttonID" :name="groupName" :value="buttonLabel" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-row w-100 align-items-center py-3 ps-3 pe-8" tabindex="1" :class="{'checkboxTop': buttonLabelSubCopy}">
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonLabel"
|
||||
:aria-required="isRequired"
|
||||
@keyup.space="handleChange(value)"
|
||||
@click="handleChange(value)"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-row w-100 align-items-center py-3 ps-3 pe-8"
|
||||
tabindex="1"
|
||||
:class="{'checkboxTop': buttonLabelSubCopy}"
|
||||
>
|
||||
<img class="ms-auto order-3" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0 small">{{buttonLabel}}</p>
|
||||
|
|
@ -27,8 +64,21 @@
|
|||
</label>
|
||||
</div>
|
||||
<div v-else-if="isRadioHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonLabel" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-row w-100 align-items-center py-3 ps-3 pe-8" tabindex="1" :class="{'checkboxTop': buttonLabelSubCopy}">
|
||||
<input
|
||||
type="radio"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:value="buttonLabel"
|
||||
:aria-required="isRequired"
|
||||
@keyup.space="handleChange(value)"
|
||||
@click="handleChange(value)"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex flex-row w-100 align-items-center py-3 ps-3 pe-8"
|
||||
tabindex="1"
|
||||
:class="{'checkboxTop': buttonLabelSubCopy}"
|
||||
>
|
||||
<img class="ms-auto order-3" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0 small">{{buttonLabel}}</p>
|
||||
|
|
@ -39,6 +89,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from 'vue';
|
||||
import { useField } from 'vee-validate';
|
||||
|
||||
export default {
|
||||
name: "listCard",
|
||||
props: {
|
||||
|
|
@ -53,9 +106,38 @@ export default {
|
|||
isRequired: Boolean, //Required: is aria-required required or not?
|
||||
altText: String,//Leave empty. Screen readers read the buttonLabel text. If alt has content, it will repeat unnecessarily.
|
||||
buttonID: String,//Required: Unique
|
||||
groupName: String,//Required: Unique
|
||||
buttonLabelSubCopy: String,//Optional: sub tex
|
||||
}
|
||||
groupName: String,//Rquired: Unique
|
||||
buttonLabelSubCopy: String,//Optional: sub text
|
||||
value: { // Field initial value
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = () => { // TODO: refactor props to streamline this logic for the whole component
|
||||
if (props.isMultiSelect) {
|
||||
return "checkbox";
|
||||
} else if (props.isRadio) {
|
||||
return "radio";
|
||||
} else if (props.isMultiSelectHorizontal) {
|
||||
return "checkbox";
|
||||
} else if (props.isRadioHorizontal) {
|
||||
return "radio";
|
||||
}
|
||||
}
|
||||
|
||||
props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange } = useField(groupName, undefined, {
|
||||
type: inputType(),
|
||||
checkedValue: value,
|
||||
});
|
||||
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue