Merge branch 'develop' into feature/CSR-335

This commit is contained in:
FrankRua 2022-02-22 16:36:49 -05:00
commit d64cd068a2
24 changed files with 480 additions and 221 deletions

View file

@ -7,4 +7,5 @@
@import "@/styles/common-styles.scss"; @import "@/styles/common-styles.scss";
@import "@/styles/common-typography-styles.scss"; @import "@/styles/common-typography-styles.scss";
@import "@/styles/common-error-styles.scss"; @import "@/styles/common-error-styles.scss";
@import "@/styles/common-animations.scss";
</style> </style>

View file

@ -53,7 +53,7 @@ describe("buttonQuestion.vue", () => {
answers: ["2022", "2021", "2020"], answers: ["2022", "2021", "2020"],
isMultiSelect: false isMultiSelect: false
}); });
const val = {isChecked: true, buttonId: "2021", } const val = {checkValue: true, value: "2021", }
wrapper.vm.handleCheckedChanged(val); wrapper.vm.handleCheckedChanged(val);
// Assert // Assert
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2021"]]); expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2021"]]);
@ -69,7 +69,7 @@ describe("buttonQuestion.vue", () => {
isMultiSelect: true, isMultiSelect: true,
} }
}); });
const val = {isChecked: true, buttonId: "2019", } const val = {checkValue: true, value: "2019", }
wrapper.vm.handleCheckedChanged(val); wrapper.vm.handleCheckedChanged(val);
// Assert // Assert
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2022", "2021", "2020", "2019"]]); expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2022", "2021", "2020", "2019"]]);

View file

@ -12,7 +12,7 @@
v-for="answer in answers" v-for="answer in answers"
:key="answer.Name ? answer.Name : answer" :key="answer.Name ? answer.Name : answer"
@isCheckedChanged="handleCheckedChanged" @isCheckedChanged="handleCheckedChanged"
:buttonID="answer.Name ? answer.Name : answer" :buttonID="answer.Name ? groupName + '-' + answer.Name : groupName + '-' + answer"
:value="answer.Name ? answer.Name : answer" :value="answer.Name ? answer.Name : answer"
:buttonLabel="answer.Text ? answer.Text : answer" :buttonLabel="answer.Text ? answer.Text : answer"
:buttonLabelSubCopy="answer.SubText" :buttonLabelSubCopy="answer.SubText"
@ -29,7 +29,7 @@
:altText="answer.Name ? answer.Name : answer" :altText="answer.Name ? answer.Name : answer"
screenReaderOnlyText="(opens new window)" screenReaderOnlyText="(opens new window)"
:colLength="getColLength" :colLength="getColLength"
:selectedButtonIDs="selectedValues" :selectedValues="selectedValues"
data-test="button" data-test="button"
:validationRules="validationRules" :validationRules="validationRules"
/> />
@ -124,11 +124,13 @@ export default {
handleCheckedChanged(val) { handleCheckedChanged(val) {
if(this.isMultiSelect && this.selectedValues) { if(this.isMultiSelect && this.selectedValues) {
// Add or remove item to array of data to emit // Add or remove item to array of data to emit
const newSelectedValues = this.selectedValues; if(Array.isArray(this.selectedValues)) {
val.isChecked ? newSelectedValues.push(val.buttonId) : newSelectedValues.splice(newSelectedValues.indexOf(val.buttonId), 1); const newSelectedValues = this.selectedValues;
this.selectedValues = newSelectedValues; val.checkValue ? newSelectedValues.push(val.value) : newSelectedValues.splice(newSelectedValues.indexOf(val.value), 1);
this.selectedValues = newSelectedValues;
}
} else { } else {
this.selectedValues = [val.buttonId]; this.selectedValues = [val.value];
} }
}, },
}, },

View file

@ -60,7 +60,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.funnel-header { .funnel-header {
height: 56px; padding: 0.97rem 0;
} }
.logo-image { .logo-image {

View file

@ -23,18 +23,17 @@ import buttonQuestion from "@/common-components/button-question/button-question"
import listCard from "@/ux-components/list-card/list-card"; import listCard from "@/ux-components/list-card/list-card";
export default { export default {
name: "nestedRadio", name: "nestedRadio",
components: { components: {
buttonQuestion, buttonQuestion,
listCard, listCard,
}, },
data() { data() {
return { return {
demoArray: [ demoArray: [
"rain sensor, solar, 3rd visor band", "rain sensor, solar, 3rd visor band",
"rain sensor, heated glass, solar, 3rd visor band", "rain sensor, heated glass, solar, 3rd visor band",
] ]
}
} }
}; };
</script> </script>

View file

@ -41,7 +41,7 @@ describe("damage-location-question.vue", () => {
damageLocationQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, damageOptions, "car-group"); damageLocationQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, damageOptions, "car-group");
//Assert //Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'car-Windshield' }, { Name: 'car-SideDoor' } ]) expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'Windshield' }, { Name: 'SideDoor' } ])
}); });
}); });

View file

@ -67,13 +67,18 @@ export default ({
} }
}, },
answersToDisplay(){ answersToDisplay(){
return Array.isArray(this.answersFromCms) const filteredAnswers = Array.isArray(this.answersFromCms)
? this.answersFromCms.filter(ans => ? this.answersFromCms.filter(ans =>
{ {
const name = ans.Name.split('-'); const name = ans.Name.split('-');
return name[0].toUpperCase() === store.getters.vehicle.category && this.damageOptionsMap[name[1]]; return name[0].toUpperCase() === store.getters.vehicle.category && this.damageOptionsMap[name[1]];
}) })
: []; : [];
return filteredAnswers.map(ans => {
const newName = ans.Name.includes('-') ? ans.Name.split('-')[1] : ans.Name;
ans.Name = newName;
return ans;
});
}, },
}, },
components: { components: {

View file

@ -30,7 +30,7 @@ describe("replace-options-question.vue", () => {
replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group"); replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group");
//Assert //Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'car-Windshield' }, { Name: 'car-FrontDoor' } ]) expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'Windshield' }, { Name: 'FrontDoor' } ])
}); });
}); });

View file

@ -1,8 +1,7 @@
<template> <template>
<transition name="fade"> <transition name="fade" mode="out-in">
<div class="replace-options-question" :class="this.answersToDisplay.length < 2 ? 'd-none' : ''"> <div v-if="isAvailable && this.answersToDisplay.length > 0" class="replace-options-question" :class="this.answersToDisplay.length < 2 ? 'd-none' : ''" aria-live="polite">
<buttonQuestion <buttonQuestion
v-if="isAvailable"
isWide isWide
:questionText="questionText" :questionText="questionText"
isMultiSelect isMultiSelect
@ -61,13 +60,19 @@ export default ({
} }
}, },
answersToDisplay(){ answersToDisplay(){
return Array.isArray(this.answersFromCms) const filteredAnswers = Array.isArray(this.answersFromCms)
? this.answersFromCms.filter(ans => ? this.answersFromCms.filter(ans =>
{ {
const name = ans.Name.split('-'); const name = ans.Name.split('-');
return this.filterByVehicleCategory ? name[0].toUpperCase() === store.getters.vehicle.category && this.replaceOptions.includes(name[1]) : this.replaceOptions.includes(ans.Name); return this.filterByVehicleCategory ? name[0].toUpperCase() === store.getters.vehicle.category && this.replaceOptions.includes(name[1]) : this.replaceOptions.includes(ans.Name);
}) })
: []; : [];
return filteredAnswers.map(ans => {
const newName = ans.Name.includes('-') ? ans.Name.split('-')[1] : ans.Name;
ans.Name = newName;
return ans;
});
}, },
}, },
mounted(){ mounted(){
@ -82,15 +87,3 @@ export default ({
} }
}) })
</script> </script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View file

@ -0,0 +1,126 @@
import { shallowMount } from "@vue/test-utils";
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true});
describe("replace-options-question.vue", () => {
test("Selected side door option is emitted upon selection.", async () => {
//Arrange
const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] });
const sideDoorOptions = ["Backseat"];
//Act
wrapper.setValue({ modelValue: sideDoorOptions });
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]);
});
});
describe("replace-options-question.vue", () => {
test("Selected door side option is updated when selection made.", async () => {
//Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.selectedDoorSidesValues = ["DriverSide"]
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.emitted()["update:selectedDoorSides"][0]).toEqual([["DriverSide"]]);
});
});
describe("replace-options-question.vue", () => {
test("Selected driver side option is updated when selection made.", async () => {
//Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.selectedDriverSideReplacOptionsValues = ["FrontDoor"]
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.emitted()["update:selectedDriverSideReplacOptions"][0]).toEqual([["FrontDoor"]]);
});
});
describe("replace-options-question.vue", () => {
test("Selected passenger side option is updated when selection made.", async () => {
//Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.selectedPassengerSideReplaceOptionsValues = ["BacktDoor"]
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.emitted()["update:selectedPassengerSideReplaceOptions"][0]).toEqual([["BacktDoor"]]);
});
});
describe("replace-options-question.vue", () => {
test("Answers to display filtered by data from api.", async () => {
//Arrange
const { wrapper, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions } = setupMocks({});
//Act
sideDoorOptions.methods.initializeComponent.call(wrapper.vm, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions, "car-group");
//Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'DriverSide' }, { Name: 'PassengerSide' } ])
});
});
function setupMocks({
modelValueProp = ["DriverSide"],
groupName = "sideDoorOptions",
cmsQuestionText = "CMS text goes here",
cmsAnswers = [{Name: "Car-DriverSide"}, {Name: "Car-PassengerSide"}],
driverSideReplaceOptions = ["FrontDoor", "BackDoor"],
passengerSideReplaceOptions = ["FrontDoor", "BackDoor"],
driverSideOptions = ["Car-FrontDoor", "Car-BackDoor"],
passengerSideOptions = ["Car-FrontDoor", "Car-BackDoor"],
selectedDamageLocations = ["SideDoor"]
}) {
//Mock store
store.dispatch = jest.fn(() => dataFromStoreApi);
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
const mountOptions = getMountOptions({
store: {
dispatch: store.dispatch,
getters: store.getters,
},
});
//Mock props
mountOptions.propsData = {
modelValue: modelValueProp,
groupName: groupName,
selectedDamageLocations: selectedDamageLocations,
};
const wrapper = shallowMount(sideDoorOptions, mountOptions);
const OptionsWrapper = wrapper.findAllComponents({name: "replaceOptionsQuestion"});
OptionsWrapper[0].vm.initializeComponent = replaceOptionsQuestion.methods.initializeComponent;
OptionsWrapper[1].vm.initializeComponent = replaceOptionsQuestion.methods.initializeComponent;
//Mock CMS content
const cmsContent = {
groupName: groupName,
QuestionText: cmsQuestionText,
Answers: cmsAnswers,
};
return { wrapper, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions };
}

View file

@ -0,0 +1,130 @@
<template>
<div class="side-door-options">
<transition name="fade" mode="out-in">
<div class="side-doors" v-if="selectedDamageLocations.includes('SideDoor')" aria-live="polite">
<buttonQuestion
:questionText="questionText"
isMultiSelect
:answers="answersToDisplay"
:groupName="groupName"
buttonType="listCard"
v-model="selectedDoorSidesValues"
/>
</div>
</transition>
<replaceOptionsQuestion ref="driverSideOptions" :isAvailable="isDriverSideReplaceOptionsQuestionAvailable" groupName="driverSideOptions" filterByVehicleCategory v-model="selectedDriverSideReplacOptionsValues" />
<replaceOptionsQuestion ref="passengerSideOptions" :isAvailable="isPassengerSideReplaceOptionsQuestionAvailable" groupName="passengerSideOptions" filterByVehicleCategory v-model="selectedPassengerSideReplaceOptionsValues" />
</div>
</template>
<script>
import buttonQuestion from "@/common-components/button-question/button-question";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
import store from "@/store";
export default ({
name: "sideDoorOptions",
data(){
return {
questionText: String,
answersFromCms: Array,
selectedDoorSides: [],
// Child component data
selectedDriverSideReplacOptions: [],
selectedPassengerSideReplaceOptions: [],
}
},
props: {
groupName: String,
modelValue: Array,
selectedDamageLocations: Array,
},
methods: {
initializeComponent(cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
// Child Components intialize
this.$refs.driverSideOptions.initializeComponent(driverSideReplaceOptions, driverSideOptions);
this.$refs.passengerSideOptions.initializeComponent(passengerSideReplaceOptions, passengerSideOptions);
},
getSideDoorReplacementOptions(){
return {
selectedDoorSides: this.selectedDoorSides,
selectedDriverSideReplacOptions: this.selectedDriverSideReplacOptions,
selectedPassengerSideReplaceOptions: this.selectedPassengerSideReplaceOptions
}
},
},
computed: {
selectedValues: {
get: function() {
return this.modelValue;
},
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
},
selectedDoorSidesValues: {
get: function() {
return this.selectedDoorSides;
},
set: function(newValue) {
this.$emit("update:selectedDoorSides", newValue);
this.selectedValues = this.getSideDoorReplacementOptions();
}
},
selectedDriverSideReplacOptionsValues: {
get: function() {
return this.selectedDriverSideReplacOptions;
},
set: function(newValue) {
this.$emit("update:selectedDriverSideReplacOptions", newValue);
this.selectedValues = this.getSideDoorReplacementOptions();
}
},
selectedPassengerSideReplaceOptionsValues: {
get: function() {
return this.selectedPassengerSideReplaceOptions;
},
set: function(newValue) {
this.$emit("update:selectedPassengerSideReplaceOptions", newValue);
this.selectedValues = this.getSideDoorReplacementOptions();
}
},
answersToDisplay(){
const filteredAnswers = Array.isArray(this.answersFromCms)
? this.answersFromCms.filter(ans =>
{
const name = ans.Name.split('-');
return name[0].toUpperCase() === store.getters.vehicle.category;
})
: [];
return filteredAnswers.map(ans => {
const newName = ans.Name.includes('-') ? ans.Name.split('-')[1] : ans.Name;
ans.Name = newName;
return ans;
});
},
isDriverSideReplaceOptionsQuestionAvailable(){
return Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes("DriverSide");
},
isPassengerSideReplaceOptionsQuestionAvailable(){
return Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes("PassengerSide");
},
},
mounted() {
if(typeof(this.selectedValues) === "object"){
this.selectedDoorSides = this.selectedValues.selectedDoorSides;
this.selectedDriverSideReplacOptions = this.selectedValues.selectedDriverSideReplacOptions;
this.selectedPassengerSideReplaceOptions = this.selectedValues.selectedPassengerSideReplaceOptions;
}
},
components: {
buttonQuestion,
replaceOptionsQuestion,
}
})
</script>

View file

@ -4,7 +4,7 @@ import funnelHeader from "@/common-components/funnel-header/funnel-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question"; import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question"; import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options"; import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
@ -172,16 +172,6 @@ function setupMocks({
}) { }) {
//Mock api responses //Mock api responses
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn();
baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation(() => {
return Promise.resolve({
driverSideOptions: {
availableReplacementOptions: ["Front", "Back", "Side"],
},
windshieldOptions: {
availableReplacementOptions: ["Single", "Driver", "Passenger"],
},
});
});
const apiResponses = { const apiResponses = {
cmsContent: { cmsContent: {
FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
@ -198,6 +188,9 @@ function setupMocks({
driverSideOptions: { driverSideOptions: {
availableReplacementOptions: ["Front", "Back", "Side"], availableReplacementOptions: ["Front", "Back", "Side"],
}, },
passengerSideOptions: {
availableReplacementOptions: ["Front", "Back", "Side"],
},
windshieldOptions: { windshieldOptions: {
availableReplacementOptions: ["Single", "Driver", "Passenger"], availableReplacementOptions: ["Single", "Driver", "Passenger"],
}, },
@ -222,12 +215,11 @@ function setupMocks({
initializeComponent: jest.fn(), initializeComponent: jest.fn(),
}; };
const driverSideOptions = replaceOptionsQuestion damageLocationQuestion.methods = {
driverSideOptions.methods = {
initializeComponent: jest.fn(), initializeComponent: jest.fn(),
}; };
damageLocationQuestion.methods = { sideDoorOptions.methods = {
initializeComponent: jest.fn(), initializeComponent: jest.fn(),
}; };
@ -250,17 +242,9 @@ function setupMocks({
vehicleBannerWrapper.vm.initializeComponent = vehicleBannerWrapper.vm.initializeComponent =
vehicleBanner.methods.initializeComponent; vehicleBanner.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({ const sideDoorOptionsWrapper = wrapper.findComponent({ name: "sideDoorOptions" });
name: "funnelSubHeader", sideDoorOptionsWrapper.vm.initializeComponent =
}); sideDoorOptions.methods.initializeComponent;
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
const driverSideOptionsWrapper = wrapper.findAllComponents({
name: "replaceOptionsQuestion",
}).at(0);
driverSideOptionsWrapper.vm.initializeComponent =
replaceOptionsQuestion.methods.initializeComponent;
const windshieldOptionsWrapper = wrapper.findComponent({ const windshieldOptionsWrapper = wrapper.findComponent({
name: "windshieldOptions", name: "windshieldOptions",
@ -269,6 +253,11 @@ function setupMocks({
windshieldOptionsWrapper.vm.initializeComponent = windshieldOptionsWrapper.vm.initializeComponent =
windshieldOptions.methods.initializeComponent; windshieldOptions.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({
name: "funnelSubHeader",
});
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
const damageLocationQuestionWrapper = wrapper.findComponent({ const damageLocationQuestionWrapper = wrapper.findComponent({
name: "damageLocationQuestion", name: "damageLocationQuestion",

View file

@ -4,12 +4,11 @@
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false /> <vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
<funnelSubHeader ref="funnelSubHeader" /> <funnelSubHeader ref="funnelSubHeader" />
<damageLocationQuestion ref="damageLocation" v-model="selectedDamageLocations" groupName="DamageLocationQuestion" /> <damageLocationQuestion ref="damageLocation" v-model="selectedDamageLocations" groupName="DamageLocationQuestion" />
<replaceOptionsQuestion ref="driverSideOptions" isAvailable filterByVehicleCategory v-model="driverSideOptionsData" groupName="DriverSideReplaceOptionsQuestion" />
<replaceOptionsQuestion ref="windshieldOptions" isAvailable groupName="windshieldOptions" v-model="windshieldOptionsData" />
<windshieldOptions ref="windshieldOptions" <windshieldOptions ref="windshieldOptions"
v-model="selectedWindshieldOptions" v-model="selectedWindshieldOptions"
:selectedDamageLocations="selectedDamageLocations" :selectedDamageLocations="selectedDamageLocations"
/> />
<sideDoorOptions ref="sideDoorOptions" groupName="SideDoorSideQuestion" v-model="sideDoorOptionsData" :selectedDamageLocations="selectedDamageLocations" />
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" /> <funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
</div> </div>
</template> </template>
@ -20,8 +19,8 @@ import funnelHeader from "@/common-components/funnel-header/funnel-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question"; import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from"@/layouts/vehicle-damage/damage-location-question/damage-location-question"; import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options"; import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
// Supporting files // Supporting files
@ -68,12 +67,12 @@ export default {
vm.$refs.funnelSubHeader.initializeComponent( vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget resultMap.cmsContent.FunnelSubHeaderWidget
); );
vm.$refs.driverSideOptions.initializeComponent(
resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions
);
vm.$refs.damageLocation.initializeComponent( vm.$refs.damageLocation.initializeComponent(
resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions
); );
vm.$refs.sideDoorOptions.initializeComponent(
resultMap.cmsContent.SideDoorSideQuestion, resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.cmsContent.PassengerSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
);
vm.$refs.windshieldOptions.initializeComponent( vm.$refs.windshieldOptions.initializeComponent(
resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion, resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion,
resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions
@ -86,7 +85,11 @@ export default {
data(){ data(){
return { return {
selectedDamageLocations: [], selectedDamageLocations: [],
driverSideOptionsData: [], sideDoorOptionsData: {
selectedDoorSides: [],
selectedDriverSideReplacOptions: [],
selectedPassengerSideReplaceOptions: []
},
selectedWindshieldOptions: [] selectedWindshieldOptions: []
} }
}, },
@ -111,7 +114,7 @@ export default {
funnelFooter, funnelFooter,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
replaceOptionsQuestion, sideDoorOptions,
damageLocationQuestion, damageLocationQuestion,
windshieldOptions, windshieldOptions,
}, },

View file

@ -1,8 +1,7 @@
<template> <template>
<transition name="fade"> <transition name="fade" mode="out-in">
<div class="windshield-chip-count-question"> <div class="windshield-chip-count-question" v-if="isAvailable" aria-live="polite">
<buttonQuestion <buttonQuestion
v-if="isAvailable"
:questionText="questionText" :questionText="questionText"
:answers="answersFromCms" :answers="answersFromCms"
:groupName="groupName" :groupName="groupName"

View file

@ -1,8 +1,7 @@
<template> <template>
<transition name="fade"> <transition name="fade" mode="out-in">
<div class="windshield-damage-type-question"> <div class="windshield-damage-type-question" v-if="isAvailable" aria-live="polite">
<buttonQuestion <buttonQuestion
v-if="isAvailable"
:questionText="questionText" :questionText="questionText"
:answers="answersFromCms" :answers="answersFromCms"
:groupName="groupName" :groupName="groupName"

View file

@ -1,24 +1,22 @@
<template> <template>
<transition name="fade"> <div class="windshield-options">
<div class="windshield-options" aria-live="polite"> <windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion"
<windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion" :isAvailable=isWindshieldDamageLocation
:isAvailable=isWindshieldDamageLocation :suppressError="suppressError"
:suppressError="suppressError" groupName="WindshieldDamageTypeQuestion"
groupName="WindshieldDamageTypeQuestion" v-model="selectedWindshieldDamageTypeValues"
v-model="selectedWindshieldDamageTypeValues" />
/> <windshieldChipCountQuestion ref="windshieldChipCountQuestion"
<windshieldChipCountQuestion ref="windshieldChipCountQuestion" :isAvailable=isRepairOptionSelected
:isAvailable=isRepairOptionSelected groupName="WindshieldChipCountQuestion"
groupName="WindshieldChipCountQuestion" v-model="selectedWindshieldChipCountValues"
v-model="selectedWindshieldChipCountValues" />
/> <replaceOptionsQuestion ref="replaceOptionsQuestion"
<replaceOptionsQuestion ref="replaceOptionsQuestion" :isAvailable=isReplaceOptionSelected
:isAvailable=isReplaceOptionSelected groupName="WindshieldReplaceOptions"
groupName="WindshieldReplaceOptions" v-model="selectedWindshieldReplaceOptionsValues"
v-model="selectedWindshieldReplaceOptionsValues" />
/> </div>
</div>
</transition>
</template> </template>
<script> <script>
@ -27,16 +25,6 @@ import windshieldChipCountQuestion from"@/layouts/vehicle-damage/windshield-opti
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question"; import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question";
export default ({ export default ({
name: "windshieldOptions", name: "windshieldOptions",
data(){
return {
chipCountQuestionText: String,
chipCountAnswersFromCms: Array,
selectedWindshieldDamageType: null,
selectedWindshieldChipCount: null,
selectedWindshieldReplaceOptions: [],
}
},
props: { props: {
modelValue: Array, modelValue: Array,
@ -50,9 +38,6 @@ export default ({
initializeComponent(windshieldDamageTypeQuestionFromCms, windshieldChipCountQuestionFromCms, initializeComponent(windshieldDamageTypeQuestionFromCms, windshieldChipCountQuestionFromCms,
windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions){ windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions){
this.chipCountQuestionText = windshieldChipCountQuestionFromCms.QuestionText;
this.chipCountAnswersFromCms = windshieldChipCountQuestionFromCms.Answers;
this.$refs.windshieldDamageTypeQuestion.initializeComponent(windshieldDamageTypeQuestionFromCms); this.$refs.windshieldDamageTypeQuestion.initializeComponent(windshieldDamageTypeQuestionFromCms);
this.$refs.windshieldChipCountQuestion.initializeComponent(windshieldChipCountQuestionFromCms); this.$refs.windshieldChipCountQuestion.initializeComponent(windshieldChipCountQuestionFromCms);
this.$refs.replaceOptionsQuestion.initializeComponent(windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions); this.$refs.replaceOptionsQuestion.initializeComponent(windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions);
@ -79,9 +64,7 @@ export default ({
return this.selectedValues.selectedWindshieldDamageType; return this.selectedValues.selectedWindshieldDamageType;
}, },
set: function(newValue) { set: function(newValue) {
this.selectedValues.selectedWindshieldChipCount = null; this.selectedValues = this.getWindshieldOptions(newValue, null, null);
this.selectedValues.selectedWindshieldReplaceOptions = null;
this.selectedValues = this.getWindshieldOptions(newValue, this.selectedWindshieldChipCountValues, this.selectedWindshieldReplaceOptionsValues);
} }
}, },
selectedWindshieldChipCountValues: { selectedWindshieldChipCountValues: {
@ -89,8 +72,7 @@ export default ({
return this.selectedValues.selectedChipCount; return this.selectedValues.selectedChipCount;
}, },
set: function(newValue) { set: function(newValue) {
this.selectedValues.selectedWindshieldReplaceOptions = null; this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, newValue, null);
this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, newValue, this.selectedWindshieldReplaceOptionsValues);
} }
}, },
selectedWindshieldReplaceOptionsValues: { selectedWindshieldReplaceOptionsValues: {
@ -98,15 +80,13 @@ export default ({
return this.selectedValues.selectedWindshieldReplaceOptions; return this.selectedValues.selectedWindshieldReplaceOptions;
}, },
set: function(newValue) { set: function(newValue) {
this.selectedValues.selectedWindshieldChipCount = null; this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, null, newValue);
this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, this.selectedWindshieldChipCountValues, newValue);
} }
}, },
isWindshieldDamageLocation() { isWindshieldDamageLocation() {
return this.selectedDamageLocations.some(selectedDamages => return this.selectedDamageLocations.some(selectedDamages =>
{ {
const categoryAndGlass = selectedDamages.split('-'); //ie: "Suv-Windshield" Splits the answers into [0]"Suv" [1]"Windshield" return Boolean(selectedDamages.toUpperCase() === "WINDSHIELD");
return Boolean(categoryAndGlass[1].toUpperCase() === "WINDSHIELD");
}); });
}, },
isRepairOptionSelected(){ isRepairOptionSelected(){

View file

@ -0,0 +1,9 @@
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

View file

@ -161,7 +161,7 @@ describe("list-button-horizontal.vue", () => {
propsData: { propsData: {
isRadioHorizontal: true, isRadioHorizontal: true,
buttonLabel: "Windshield", buttonLabel: "Windshield",
buttonID: "List Card Checkbox", value: "List Card Checkbox",
groupID: "radio-demo-1", groupID: "radio-demo-1",
groupName: "radio 1", groupName: "radio 1",
buttonImage: "windshield-damage.svg", buttonImage: "windshield-damage.svg",
@ -172,7 +172,7 @@ describe("list-button-horizontal.vue", () => {
}); });
wrapper.vm.handleCheckChange(); wrapper.vm.handleCheckChange();
// Assert // Assert
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{buttonId: "List Card Checkbox", isChecked: Boolean}]); expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean}]);
}); });
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => { it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
@ -189,7 +189,7 @@ describe("list-button-horizontal.vue", () => {
isWide: false, isWide: false,
modelValue: ["List Card Checkbox"], modelValue: ["List Card Checkbox"],
isMultiSelect: false, isMultiSelect: false,
selectedButtonIDs: ["Car-Front"] selectedValues: ["Car-Front"]
}, },
}); });
// Assert // Assert

View file

@ -9,7 +9,7 @@
:type="isMultiSelect ? 'checkbox' : 'radio'" :type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID" :id="buttonID"
:name="groupName" :name="groupName"
:value="buttonID" :value="value"
:aria-required="isRequired" :aria-required="isRequired"
:data-focus-target="groupName" :data-focus-target="groupName"
v-model="checkValue" v-model="checkValue"
@ -69,7 +69,7 @@ export default {
type: String, type: String,
default: "", default: "",
}, },
selectedButtonIDs: [Array, String], selectedValues: [Array, String],
hasError: Boolean, hasError: Boolean,
}, },
data() { data() {
@ -79,8 +79,8 @@ export default {
}; };
}, },
created(){ created(){
if(this.selectedButtonIDs){ if(Array.isArray(this.selectedValues)){
this.checkValue = this.isMultiSelect ? this.selectedButtonIDs.includes(this.buttonID) : this.selectedButtonIDs[0]; this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0];
} }
}, },
methods: { methods: {
@ -97,7 +97,7 @@ export default {
handleCheckChange(newValue, oldValue){ handleCheckChange(newValue, oldValue){
const isInitialization = typeof(oldValue) === 'function'; const isInitialization = typeof(oldValue) === 'function';
if (!isInitialization) { if (!isInitialization) {
this.$emit('isCheckedChanged', { isChecked: this.checkValue, buttonId: this.buttonID.toString() }); this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() });
} }
} }
}, },
@ -133,7 +133,7 @@ export default {
height: 0; height: 0;
&:focus-visible + label { &:focus-visible + label {
box-shadow: 0 0 0 2px $blue; box-shadow: 0 0 0 2px $blue;
z-index: 3; z-index: 2;
} }
&:focus + label { &:focus + label {
box-shadow: 0 0 0 2px $blue; box-shadow: 0 0 0 2px $blue;
@ -159,7 +159,7 @@ export default {
&:hover { &:hover {
box-shadow: 0 0 0 4px $blue-300; box-shadow: 0 0 0 4px $blue-300;
cursor: pointer; cursor: pointer;
z-index: 3 !important; z-index: 4 !important;
} }
+ p { + p {
display: none; display: none;

View file

@ -159,7 +159,7 @@ describe("list-button.vue", () => {
propsData: { propsData: {
isRadioHorizontal: true, isRadioHorizontal: true,
buttonLabel: "Windshield", buttonLabel: "Windshield",
buttonID: "List Card Checkbox", value: "List Card Checkbox",
groupID: "radio-demo-1", groupID: "radio-demo-1",
groupName: "radio 1", groupName: "radio 1",
buttonImage: "windshield-damage.svg", buttonImage: "windshield-damage.svg",
@ -170,7 +170,7 @@ describe("list-button.vue", () => {
}); });
wrapper.vm.handleCheckChange(); wrapper.vm.handleCheckChange();
// Assert // Assert
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{buttonId: "List Card Checkbox", isChecked: Boolean}]); expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean}]);
}); });
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => { it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
@ -186,7 +186,7 @@ describe("list-button.vue", () => {
isRequired: true, isRequired: true,
isWide: false, isWide: false,
modelValue: ["List Card Checkbox"], modelValue: ["List Card Checkbox"],
selectedButtonIDs: ["Car-Front"] selectedValues: ["Car-Front"]
}, },
}); });
// Assert // Assert

View file

@ -9,7 +9,7 @@
:type="isMultiSelect ? 'checkbox' : 'radio'" :type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID" :id="buttonID"
:name="groupName" :name="groupName"
:value="buttonID" :value="value"
:aria-required="isRequired" :aria-required="isRequired"
:data-focus-target="groupName" :data-focus-target="groupName"
v-model="checkValue" v-model="checkValue"
@ -71,7 +71,7 @@ export default {
type: [String, Number], type: [String, Number],
default: "", default: "",
}, },
selectedButtonIDs: [Array, String], selectedValues: [Array, String],
hasError: Boolean, hasError: Boolean,
}, },
data() { data() {
@ -81,8 +81,8 @@ export default {
}; };
}, },
created(){ created(){
if(this.selectedButtonIDs){ if(Array.isArray(this.selectedValues)){
this.checkValue = this.isMultiSelect ? this.selectedButtonIDs.includes(this.buttonID) : this.selectedButtonIDs[0]; this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0];
} }
}, },
methods: { methods: {
@ -99,7 +99,7 @@ export default {
handleCheckChange(newValue, oldValue){ handleCheckChange(newValue, oldValue){
const isInitialization = typeof(oldValue) === 'function'; const isInitialization = typeof(oldValue) === 'function';
if (!isInitialization) { if (!isInitialization) {
this.$emit('isCheckedChanged', { isChecked: this.checkValue, buttonId: this.buttonID.toString() }); this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() });
} }
}, },
}, },

View file

@ -197,7 +197,7 @@ describe("list-card.vue", () => {
propsData: { propsData: {
isRadioHorizontal: true, isRadioHorizontal: true,
buttonLabel: "Windshield", buttonLabel: "Windshield",
buttonID: "List Card Checkbox", value: "List Card Checkbox",
groupID: "radio-demo-1", groupID: "radio-demo-1",
groupName: "radio 1", groupName: "radio 1",
buttonImage: "windshield-damage.svg", buttonImage: "windshield-damage.svg",
@ -208,7 +208,7 @@ describe("list-card.vue", () => {
}); });
wrapper.vm.handleCheckChange(); wrapper.vm.handleCheckChange();
// Assert // Assert
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{buttonId: "List Card Checkbox", isChecked: Boolean}]); expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean}]);
}); });
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => { it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
@ -217,14 +217,14 @@ describe("list-card.vue", () => {
propsData: { propsData: {
isRadioHorizontal: true, isRadioHorizontal: true,
buttonLabel: "Windshield", buttonLabel: "Windshield",
buttonID: "List Card Checkbox", value: "List Card Checkbox",
groupID: "radio-demo-1", groupID: "radio-demo-1",
groupName: "radio 1", groupName: "radio 1",
buttonImage: "windshield-damage.svg", buttonImage: "windshield-damage.svg",
isRequired: true, isRequired: true,
isWide: false, isWide: false,
modelValue: ["List Card Checkbox"], modelValue: ["List Card Checkbox"],
selectedButtonIDs: ["Car-Front"] selectedValues: ["Car-Front"]
}, },
}); });
// Assert // Assert

View file

@ -1,22 +1,48 @@
<template> <template>
<div :class="'col' + colLength"> <div :class="'col' + colLength">
<div class="list-card w-100 rounded-3 d-flex align-items-center h-100" :class="[isWide ? 'horizontal' : '', errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"> <div
<input :type="isMultiSelect ? 'checkbox' : 'radio'" :id="buttonID" :name="groupName" :value="buttonID" :aria-required="isRequired" :data-focus-target="groupName" @click="handleChange(value)" v-model="checkValue" @change="handleCheckChange()" /> class="list-card w-100 rounded-3 d-flex align-items-center h-100"
<label :for="buttonID" class="d-flex w-100 align-items-center px-2 h-100" :class="getLabelClasses" tabindex="-1"> :class="[isWide ? 'horizontal' : '', errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"
<img :id="buttonImageId" :class="!isWide ? 'order-1' : 'ms-auto order-3'" :src="buttonImage" :alt="altText" /> >
<p v-if=!isWide class="small order-3" :class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"> <input
{{buttonLabel}} :type="isMultiSelect ? 'checkbox' : 'radio'"
</p> :id="buttonID"
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4 sub-copy"> :name="groupName"
{{buttonLabelSubCopy}} :value="value"
</p> :aria-required="isRequired"
<div v-if=isWide class="order-2"> :data-focus-target="groupName"
<p class="m-0 small">{{ buttonLabel }}</p> @click="handleChange(value)"
<p v-if="buttonLabelSubCopy" class="m-0 fs-7 sub-copy"> v-model="checkValue"
{{ buttonLabelSubCopy }} @change="handleCheckChange()"
</p> />
</div> <label
</label> :for="buttonID"
class="d-flex w-100 align-items-center px-2 h-100"
:class="getLabelClasses" tabindex="-1"
>
<img
:id="buttonImageId"
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
:src="buttonImage"
:alt="altText"
/>
<p
v-if="!isWide"
class="small order-3"
:class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"
>
{{buttonLabel}}
</p>
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4 sub-copy">
{{buttonLabelSubCopy}}
</p>
<div v-if="isWide" class="order-2">
<p class="m-0 small">{{ buttonLabel }}</p>
<p v-if="buttonLabelSubCopy" class="m-0 fs-7 sub-copy">
{{ buttonLabelSubCopy }}
</p>
</div>
</label>
</div> </div>
</div> </div>
</template> </template>
@ -49,31 +75,40 @@ export default {
selectedButtonIDs: [Array, String], selectedButtonIDs: [Array, String],
hasError: Boolean, hasError: Boolean,
}, },
data() { colLength: String,
return { validationRules: String,
checkValue: Boolean, selectedValues: [Array, String],
hasError: Boolean,
},
data(){
return {
checkValue: Boolean,
}
},
created(){
if(Array.isArray(this.selectedValues)){
this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0];
}
},
computed: {
getLabelClasses() {
if (this.isWide) {
let classes = "flex-row py-r ps-3 pe-8";
if (this.buttonLabelSubCopy) {
classes += " checkboxTop";
} }
}, },
created() { },
if (this.selectedButtonIDs) { methods: {
this.checkValue = this.isMultiSelect ? this.selectedButtonIDs.includes(this.buttonID) : this.selectedButtonIDs[0]; handleCheckChange(newValue, oldValue){
} const isInitialization = typeof(oldValue) === 'function';
}, if (!isInitialization) {
computed: { this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() });
getLabelClasses() { }
if (this.isWide) { }
let classes = "flex-row py-r ps-3 pe-8"; },
if (this.buttonLabelSubCopy) { setup(props) {
classes += " checkboxTop"; const inputType = props.isMultiSelect ? "checkbox" : "radio";
}
return classes;
} else {
return "flex-column pt-4 pb-2";
}
},
},
methods: {
handleCheckChange() {
const emitEvent = { const emitEvent = {
isChecked: this.checkValue, isChecked: this.checkValue,

View file

@ -47,40 +47,29 @@ export default {
checkValue: Boolean, checkValue: Boolean,
}; };
}, },
methods: { handleCheckChange(newValue, oldValue){
handleCheckChanged(newValue, oldValue) { const isInitialization = typeof(oldValue) === 'function';
const isInitialization = typeof oldValue === "function"; if (!isInitialization) {
this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() });
if (!isInitialization) { }
this.$emit("isCheckedChanged", { }
isChecked: newValue.target.checked, },
buttonId: this.buttonID.toString(), setup(props) {
}); const { groupName, value } = toRefs(props);
} const { checked, handleChange, errorMessage } = useField(
}, groupName,
}, undefined,
setup(props) { {
const { type: "radio",
groupName, checkedValue: value,
value }
} = toRefs(props); );
const { return {
checked, checked,
handleChange, handleChange,
errorMessage errorMessage,
} = useField( };
groupName, },
undefined, {
type: "radio",
checkedValue: value,
}
);
return {
checked,
handleChange,
errorMessage,
};
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>