Merge branch 'develop' into feature/CSR-254

This commit is contained in:
Adam Caouette 2022-02-11 15:16:55 -05:00
commit b23c483823
9 changed files with 285 additions and 81 deletions

View file

@ -7,9 +7,9 @@
<fieldset class="w-100" :class="getFieldSetClasses" role="radiogroup" :aria-labelledby="groupName ? groupName + '-radio-group' : ''">
<legend class="sr-only">{{groupName}}</legend>
<div :class="getComponentWrapperClasses">
<component
:is="buttonType"
v-for="answer in answers"
<component
:is="buttonType"
v-for="answer in answers"
:key="answer.Name ? answer.Name : answer"
@isCheckedChanged="handleCheckedChanged"
:buttonID="answer.Name ? answer.Name : answer"
@ -48,6 +48,7 @@ import listButton from "@/ux-components/list-button/list-button";
import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-button-horizontal";
import listCard from "@/ux-components/list-card/list-card";
import { ErrorMessage } from 'vee-validate';
import radio from "@/ux-components/radio/radio";
export default {
name: "buttonQuestion",
@ -104,6 +105,9 @@ export default {
case 'listCard':
classes = 'row justify-content-center g-2'
break;
case 'radio':
classes = 'ui-radio d-flex'
break;
}
return classes;
},
@ -143,6 +147,7 @@ export default {
listButtonHorizontal,
listCard,
ErrorMessage,
radio,
},
};
</script>

View file

@ -28,7 +28,12 @@
/>
</div>
<div class="col-12 link-col py-1">
<textLink linkType="navigation" :text="backLink" @click-event="linkClick"/>
<textLink
linkType="navigation"
:text="backLink"
@click-event="linkClick"
href="javascript:void(0)"
/>
</div>
</div>
</div>
@ -73,10 +78,10 @@ export default {
this.buttonText = cmsContent.ForwardButtonText;
},
buttonClick() {
this.$emit("forwardClicked");
this.$emit("ForwardClicked");
},
linkClick() {
this.$emit("backClicked");
this.$emit("BackClicked");
},
}
};

View file

@ -2,7 +2,7 @@
<div class="container-fluid container-shadow p-2 rounded-3">
<div class="row my-4">
<div class="col">
<h4 class="m-0 p-2 bg-light rounded">Inputs</h4>
<h4 class="m-0 p-2 bg-light rounded">Checkbox</h4>
</div>
</div>
<div class="row">
@ -15,6 +15,36 @@
/>
</div>
</div>
<div class="row my-4">
<div class="col">
<h4 class="m-0 p-2 bg-light rounded">Radio Button</h4>
<p class="m-0">Sample radio button group</p>
</div>
</div>
<div class="row">
<div class="col">
<radio
groupName="radio-demo-2"
buttonLabel="I'm a radio button!"
buttonID="radio-button-a"
isRequired
value="I'm a radio button!"
modelValue="Model Value 1"
screenReaderOnlyText="Model Value 1"
/>
</div>
<div class="col">
<radio
groupName="radio-demo-2"
buttonLabel="I'm also a radio button!"
buttonID="radio-button-b"
isRequired
value="I'm also a radio button!"
modelValue="Model Value 2"
screenReaderOnlyText="Model Value 2"
/>
</div>
</div>
<div class="row my-4">
<div class="col">
<h4 class="m-0 p-2 bg-light rounded">Buttons</h4>
@ -50,14 +80,17 @@
<textLink
linkType=navigation
text="Navigation Link"
href="#!"
/><br><br>
<textLink
linkType="text"
text="Default Link"
href="#!"
/><br><br>
<textLink
linkType=textSmall
text="Small Link"
href="#!"
/><br><br>
<textLink
linkType=footer
@ -926,6 +959,7 @@ 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";
import radio from "@/ux-components/radio/radio";
import textLink from "@/ux-components/text-link/text-link";
export default {
name: "App",
@ -938,6 +972,7 @@ export default {
vehicleBanner,
listButtonHorizontal,
checkbox,
radio,
funnelHeader,
textLink
},

View file

@ -3,51 +3,9 @@
<funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
<funnelSubHeader ref="funnelSubHeader" />
<Form
@submit="onSubmit"
@invalid-submit="onInvalidSubmit"
ref="theForm"
v-slot="{ values, meta, errors }"
>
<damageLocationQuestion
ref="damageLocation"
v-model="selectedDamageLocations"
groupName="DamageLocationQuestion"
/>
<windshieldOptions
ref="windshieldOptions"
:showWindshieldDamageTypeQuestion="values.DamageLocationQuestion && values.DamageLocationQuestion.toString().includes('-Windshield')"
:suppressError="errors.windshieldDamageTypeQuestion && errors.windshieldDamageTypeQuestion.startsWith('checkForRepairAndReplace')"
/>
<alert
v-if="errors.windshieldDamageTypeQuestion && errors.windshieldDamageTypeQuestion.startsWith('checkForRepairAndReplace')"
alertClass="alert-danger"
alertHeadline="You'll need to schedule separate appointments"
alertCopy="Vehicle service requiring both glass repair and replacement must be scheduled separately, as they're performed by different technicians."
:isDismissible="false"
/>
<replaceOptionsQuestion
ref="driverSideOptions"
isAvailable
v-model="driverSideOptionsData"
groupName="DriverSideReplaceOptionsQuestion"
/>
<funnel-footer
ref="funnelFooter"
:isDisabled="!meta.valid"
/>
<!-- KEEP TEMPORARILY FOR TROUBLESHOOTING VALIDATION
<div class="g-2 mt-6 mx-4 w-25 position-fixed fixed-top">
<p>Current Form, values:</p>
<pre>{{ values }}</pre>
<p>Errors:</p>
<pre>{{ errors }}</pre>
<p>Is Form Valid? (Meta.valid):</p>
<pre>{{ meta.valid }}</pre>
</div> -->
</Form>
<damageLocationQuestion ref="damageLocation" v-model="selectedDamageLocations" groupName="DamageLocationQuestion" />
<replaceOptionsQuestion ref="driverSideOptions" isAvailable v-model="driverSideOptionsData" groupName="DriverSideReplaceOptionsQuestion" />
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
</div>
</template>
@ -59,11 +17,6 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
import alert from "@/ux-components/alert/alert";
import { Form } from 'vee-validate';
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -127,15 +80,12 @@ export default {
}
},
components: {
Form,
funnelHeader,
vehicleBanner,
funnelSubHeader,
damageLocationQuestion,
replaceOptionsQuestion,
funnelFooter,
windshieldOptions,
alert,
},
methods: {
arePagePrerequisitesValid() {
@ -145,24 +95,21 @@ export default {
// Invokes
store.dispatch(storeActions.RESET_PARTS_AND_DEPS);
},
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 error names array
const errorNames = errors ? Object.keys(errors) : [];
const firstErrorEl = errorNames[0];
if (firstErrorEl) {
console.log('firstErrorEl: ', firstErrorEl);
const qsString = "[data-focus-target='" + firstErrorEl + "']";
document.querySelector(qsString).focus();
}
backButtonAction() {
// route to move backwards
this.$router.navigate(
this.navigationScenarios.CLICKED_BACK,
this.$route
);
},
},
components: {
funnelHeader,
funnelFooter,
vehicleBanner,
funnelSubHeader,
replaceOptionsQuestion,
damageLocationQuestion,
},
};
</script>

View file

@ -50,6 +50,15 @@ const routingTable = [
},
],
},
{
fmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
},
],
},
];
export { routingTable };

View file

@ -44,12 +44,15 @@ export default {
+ label {
display: block;
position: relative;
&:hover {
cursor: pointer;
}
}
+ label::before {
content: "";
position: relative;
display: inline-block;
margin-right: 10px;
margin-right: 8px;
width: 16px;
height: 16px;
background: white;
@ -71,7 +74,7 @@ export default {
transform: rotate(-45deg);
}
&:hover + label::before {
box-shadow: 0 0 0 4px $blue-100;
box-shadow: 0 0 0 4px $blue-300;
}
&:focus + label::before {
box-shadow: 0 0 0 2px $blue;

View file

@ -0,0 +1,63 @@
import { shallowMount } from "@vue/test-utils";
import radio from "./radio";
import { nextTick } from "vue";
describe("radio.vue", () => {
it("Should return group name", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
groupName: "radio-button-test",
},
});
// Assert
const input = wrapper.find("input");
// Expect
expect(input.attributes().name).toEqual("radio-button-test");
});
it("Should return checkbox id", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
buttonID: "Radio ID",
},
});
// Assert
const input = wrapper.find("input");
// Expect
expect(input.attributes().id).toEqual("Radio ID");
});
it("Should return label text", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
buttonLabel: "label text",
},
});
// Assert
const paragraph = wrapper.find("p");
expect(paragraph.text()).toEqual("label text");
});
it("Should return label text", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
screenReaderOnlyText: "screenreader text",
},
});
// Assert
const paragraph = wrapper.find("span");
expect(paragraph.text()).toEqual("screenreader text");
});
});

View file

@ -0,0 +1,138 @@
<template>
<!-- Checkbox groups MUST be wrapped in a <fieldset> and <legend> tag and must contain tabindex -->
<div class="ui-radio d-flex">
<input
type="radio"
aria-checked="false"
:name="groupName"
:id="buttonID"
:aria-required="isRequired"
:value="value"
:v-model="checkValue"
@change="handleCheckChanged"
/>
<label class="d-flex align-items-center" :for="buttonID">
<p v-if="buttonLabel" class="m-0">{{ buttonLabel }}</p>
<span v-if="screenReaderOnlyText" class="sr-only">{{
screenReaderOnlyText
}}</span>
</label>
</div>
</template>
<script>
import { toRefs } from "vue";
import { useField } from "vee-validate";
export default {
name: "radio",
props: {
groupName: String,
buttonLabel: String,
buttonID: String,
isRequired: Boolean,
value: {
type: [String, Number],
default: "",
},
screenReaderOnlyText: String,
},
data() {
return {
checkValue: Boolean,
};
},
created(){
if(this.selectedButtonIDs){
this.checkValue = this.selectedButtonIDs[0];
}
},
methods: {
handleClick(value) {
this.handleChange(value);
},
handleCheckChange(newValue, oldValue){
const isInitialization = typeof(oldValue) === 'function';
if (!isInitialization) {
this.$emit('isCheckedChanged', { isChecked: this.checkValue, buttonId: this.buttonID.toString() });
}
}
},
setup(props) {
const { groupName, value } = toRefs(props);
const { checked, handleChange, errorMessage } = useField(
groupName,
undefined,
{
type: "radio",
checkedValue: value,
}
);
return {
checked,
handleChange,
errorMessage,
};
},
};
</script>
<style lang="scss">
.ui-radio {
position: relative;
input[type="radio"] {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
+ label {
display: block;
position: relative;
&:hover {
cursor: pointer;
}
}
+ label::before {
content: "";
position: relative;
display: inline-block;
margin-right: 8px;
width: 16px;
height: 16px;
background: white;
border: 1px solid $gray-500;
border-radius: 50%;
}
&:checked + label::before {
background: $white;
border: 1px solid $blue;
}
&:checked + label::after {
content: "";
position: absolute;
top: 8px;
left: 3px;
height: 6px;
width: 6px;
transform: rotate(-45deg);
border: 5px solid $blue;
border-radius: 50%;
}
&:hover + label::before {
box-shadow: 0 0 0 4px $blue-300;
}
&:focus + label::before {
box-shadow: 0 0 0 2px $blue;
}
&:focus:checked + label::before {
box-shadow: 0 0 0 2px transparent;
}
&:disabled + label {
color: $gray-200;
}
&:disabled + label::before {
background: $gray-200;
}
}
}
</style>

View file

@ -13,11 +13,10 @@ export default {
text: String,
href: {
type: String,
default: `javascript:void(0)`, //Prevents page jump when value is empty or #
},
},
methods: {
handleClick() {
handleClick(event) {
this.$emit("click-event");
},
},