Merge pull request #383 from Safelite/refactor/linting8

Baseline linting for the remainder of the layout pages and the router
This commit is contained in:
DavidAtSafelite 2023-07-31 13:04:13 -04:00 committed by GitHub
commit 9b0c54bf5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 1095 additions and 1189 deletions

View file

@ -5,7 +5,7 @@ const errorMessages = {
PASSENGER_SIDE_OPTIONS_REQUIRED: 'Please select window', PASSENGER_SIDE_OPTIONS_REQUIRED: 'Please select window',
WINDSHIELD_DAMAGE_TYPE_REQUIRED: 'Please select windshield damage', WINDSHIELD_DAMAGE_TYPE_REQUIRED: 'Please select windshield damage',
WINDSHIELD_CHIP_COUNT_REQUIRED: 'Please select chip(s)', WINDSHIELD_CHIP_COUNT_REQUIRED: 'Please select chip(s)',
WINSHIELD_REPLACE_OPTIONS_REQUIRED: 'Please select windshield part', WINDSHIELD_REPLACE_OPTIONS_REQUIRED: 'Please select windshield part',
REPLACE_OPTIONS_REQUIRED: 'Please select rear window type', REPLACE_OPTIONS_REQUIRED: 'Please select rear window type',
STREET_ADDRESS_REQUIRED: 'Please enter your street address', STREET_ADDRESS_REQUIRED: 'Please enter your street address',
CITY_REQUIRED: 'Please enter your city', CITY_REQUIRED: 'Please enter your city',

View file

@ -1,17 +1,20 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2 confirmation"> <div class="container-fluid pb-2 confirmation">
<p>Placeholder for tpa-confrimation page</p> <p>Placeholder for tpa-confirmation page</p>
<siteFooter <siteFooter
cmsWidgetName="SiteFooterWidget"
ref="siteFooter" ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" @back-clicked="backButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -27,15 +30,15 @@ import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js'; import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
export default { export default {
name: 'tpa-confirmation', name: 'tpa-confirmation',
components: {
siteHeader,
siteFooter,
Form
},
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
data() {
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -46,11 +49,17 @@ export default {
promise: cmsContentPromise promise: cmsContentPromise
}]; }];
// use resultMap to populate layout content. // use resultMap to populate layout content.
let resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
},
methods: methods:
{ {
backButtonAction() { backButtonAction() {
@ -61,20 +70,13 @@ export default {
}, },
navigateForward() { navigateForward() {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
this.navigationScenarios.CLICKED_FORWARD, this.$route);
this.$route
);
} }
}, }
components: { };
siteHeader,
siteFooter,
Form
}
}
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.confirmation p{ .confirmation p{
margin-bottom:0.75rem; margin-bottom:0.75rem;

View file

@ -1,17 +1,20 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2 search"> <div class="container-fluid pb-2 search">
<p>Placeholder for TPA-Search</p> <p>Placeholder for TPA-Search</p>
<siteFooter <siteFooter
cmsWidgetName="SiteFooterWidget"
ref="siteFooter" ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" @back-clicked="backButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -29,9 +32,13 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
export default { export default {
name: 'tpa-search', name: 'tpa-search',
mixins: [BaseFormMixin], components: {
data() { siteHeader,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form
}, },
mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -42,33 +49,30 @@ export default {
promise: cmsContentPromise promise: cmsContentPromise
}]; }];
// use resultMap to populate layout content. // use resultMap to populate layout content.
let resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
data() {
},
methods: methods:
{ {
backButtonAction() { backButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
forwardButtonAction() { forwardButtonAction() {
return this.navigateForward(); return this.navigateForward();
}, },
navigateForward() { navigateForward() {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
this.navigationScenarios.CLICKED_FORWARD, this.$route);
this.$route
);
} }
}, }
components: { };
siteHeader, </script>
siteFooter,
Form, <style lang="scss" scoped>
}
}
</script><style lang="scss">
.search p{ .search p{
margin-bottom:0.75rem; margin-bottom:0.75rem;
} }

View file

@ -1,17 +1,20 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2 submit"> <div class="container-fluid pb-2 submit">
<p>Placeholder for TPA-Submit</p> <p>Placeholder for TPA-Submit</p>
<siteFooter <siteFooter
cmsWidgetName="SiteFooterWidget"
ref="siteFooter" ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" @back-clicked="backButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -31,14 +34,12 @@ import { useMainStore } from '@/store';
export default { export default {
name: 'tpa-submit', name: 'tpa-submit',
components: {
siteHeader,
siteFooter,
Form
},
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
data() {
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -51,11 +52,18 @@ export default {
}]; }];
// use resultMap to populate layout content. // use resultMap to populate layout content.
let resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
}, },
methods: methods:
{ {
@ -68,20 +76,13 @@ export default {
}, },
navigateForward() { navigateForward() {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
this.navigationScenarios.CLICKED_FORWARD, this.$route);
this.$route
);
} }
}, }
components: { };
siteHeader,
siteFooter,
Form
}
}
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.submit { .submit {
margin-bottom:0.75rem; margin-bottom:0.75rem;

View file

@ -33,11 +33,9 @@ describe('damage-location-question.vue', () => {
}); });
// Act // Act
damageLocationQuestion.methods.initializeComponent.call( damageLocationQuestion.methods.initializeComponent.call(wrapper.vm,
wrapper.vm,
damageOptions, damageOptions,
'car-group' 'car-group');
);
// Assert // Assert
expect(wrapper.vm.damageOptions).toStrictEqual({ expect(wrapper.vm.damageOptions).toStrictEqual({
@ -66,7 +64,7 @@ function setupMocks({
}; };
mountOptions.propsData = { mountOptions.propsData = {
modelValue: modelValueProp, modelValue: modelValueProp,
isMultiSelect: isMultiSelect isMultiSelect
}; };
mountOptions.mixins = [mockMixin]; mountOptions.mixins = [mockMixin];
@ -74,7 +72,7 @@ function setupMocks({
// Mock CMS content // Mock CMS content
const cmsContent = { const cmsContent = {
groupName: groupName, groupName,
QuestionText: cmsQuestionText, QuestionText: cmsQuestionText,
Answers: cmsAnswers Answers: cmsAnswers
}; };

View file

@ -1,13 +1,13 @@
<template> <template>
<div class="damage-location-question"> <div class="damage-location-question">
<buttonQuestion <buttonQuestion
v-model="selectedValues"
:questionText="questionText" :questionText="questionText"
isMultiSelect isMultiSelect
:answers="answersToDisplay" :answers="answersToDisplay"
:groupName="groupName" :groupName="groupName"
buttonTypeString="listCard" buttonTypeString="listCard"
isRequired isRequired
v-model="selectedValues"
validationRules="damage-location-required" /> validationRules="damage-location-required" />
</div> </div>
</template> </template>
@ -22,21 +22,20 @@ import { errorMessages } from '@/constants/error-messages';
defineRule('damage-location-required', required(errorMessages.DAMAGE_LOCATION_REQUIRED)); defineRule('damage-location-required', required(errorMessages.DAMAGE_LOCATION_REQUIRED));
export default { export default {
name: 'damageLocationQuestion', name: 'damage-location-question',
data() { components: {
return { buttonQuestion
damageOptions: Object
};
}, },
props: { props: {
modelValue: Array, modelValue: Array,
groupName: String, groupName: String,
cmsWidgetName: String cmsWidgetName: String
}, },
methods: { emits: ['update:modelValue'],
initializeComponent(damageOptions) { data() {
this.damageOptions = damageOptions; return {
} damageOptions: Object
};
}, },
computed: { computed: {
questionText() { questionText() {
@ -46,10 +45,10 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'Answers'); return this.getCmsContent(this.cmsWidgetName, 'Answers');
}, },
selectedValues: { selectedValues: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
}, },
@ -57,8 +56,8 @@ export default {
return { return {
Windshield: true, Windshield: true,
SideDoor: SideDoor:
this.damageOptions.driverSideOptions.availableReplacementOptions.length || this.damageOptions.driverSideOptions.availableReplacementOptions.length
this.damageOptions.passengerSideOptions.availableReplacementOptions.length, || this.damageOptions.passengerSideOptions.availableReplacementOptions.length,
RearWindow: this.damageOptions.backGlassOptions.availableReplacementOptions.length RearWindow: this.damageOptions.backGlassOptions.availableReplacementOptions.length
}; };
}, },
@ -67,8 +66,8 @@ export default {
? this.answersFromCms.filter((ans) => { ? this.answersFromCms.filter((ans) => {
const name = ans.Name.split('-'); const name = ans.Name.split('-');
return ( return (
name[0].toUpperCase() === this.mainStore.vehicle.category && name[0].toUpperCase() === this.mainStore.vehicle.category
this.damageOptionsMap[name[1]] && this.damageOptionsMap[name[1]]
); );
}) })
: []; : [];
@ -79,8 +78,10 @@ export default {
}); });
} }
}, },
components: { methods: {
buttonQuestion initializeComponent(damageOptions) {
this.damageOptions = damageOptions;
}
} }
}; };
</script> </script>

View file

@ -26,11 +26,9 @@ describe('replace-options-question.vue', () => {
}); });
// Act // Act
replaceOptionsQuestion.methods.initializeComponent.call( replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm,
wrapper.vm,
replaceOptions, replaceOptions,
'car-group' 'car-group');
);
// Assert // Assert
expect(wrapper.vm.replaceOptions).toStrictEqual(['Windshield', 'FrontDoor']); expect(wrapper.vm.replaceOptions).toStrictEqual(['Windshield', 'FrontDoor']);
@ -71,12 +69,10 @@ describe('replace-options-question.vue', () => {
}); });
// Act // Act
wrapper.vm.$options.methods.initializeComponent.call( wrapper.vm.$options.methods.initializeComponent.call(wrapper.vm,
wrapper.vm,
cmsContent, cmsContent,
replaceOptions, replaceOptions,
'car-group' 'car-group');
);
wrapper.vm.$options.watch.isAvailable.call(wrapper.vm, true); wrapper.vm.$options.watch.isAvailable.call(wrapper.vm, true);
// Assert // Assert
@ -106,9 +102,9 @@ function setupMocks({
}; };
mountOptions.propsData = { mountOptions.propsData = {
modelValue: modelValueProp, modelValue: modelValueProp,
isAvailable: isAvailable, isAvailable,
isMultiSelect: isMultiSelect, isMultiSelect,
filterByVehicleCategory: filterByVehicleCategory, filterByVehicleCategory
}; };
mountOptions.mixins = [mockMixin]; mountOptions.mixins = [mockMixin];
@ -121,7 +117,7 @@ function setupMocks({
// Mock CMS content // Mock CMS content
const cmsContent = { const cmsContent = {
groupName: groupName, groupName,
QuestionText: cmsQuestionText, QuestionText: cmsQuestionText,
Answers: cmsAnswers Answers: cmsAnswers
}; };

View file

@ -1,18 +1,20 @@
<template> <template>
<transition name="fade" mode="out-in"> <transition
name="fade"
mode="out-in">
<div <div
v-if="shouldDisplayReplaceOptionsQuestion" v-if="shouldDisplayReplaceOptionsQuestion"
class="replace-options-question" class="replace-options-question"
:class="this.answersToDisplay.length < 2 ? 'd-none' : ''" :class="answersToDisplay.length < 2 ? 'd-none' : ''"
aria-live="polite"> aria-live="polite">
<buttonQuestion <buttonQuestion
v-model="selectedValues"
isWide isWide
:questionText="questionText" :questionText="questionText"
:isMultiSelect="isMultiSelect" :isMultiSelect="isMultiSelect"
:answers="answersToDisplay" :answers="answersToDisplay"
:groupName="groupName" :groupName="groupName"
buttonTypeString="listCard" buttonTypeString="listCard"
v-model="selectedValues"
:validationRules="validationRules" :validationRules="validationRules"
:suppressError="suppressError" :suppressError="suppressError"
:isRequired="isRequired" /> :isRequired="isRequired" />
@ -24,11 +26,9 @@
import buttonQuestion from '@/digital-components/button-question/button-question'; import buttonQuestion from '@/digital-components/button-question/button-question';
export default { export default {
name: 'replaceOptionsQuestion', name: 'replace-options-question',
data() { components: {
return { buttonQuestion
replaceOptions: this.isMultiSelect ? [] : ''
};
}, },
props: { props: {
isAvailable: Boolean, isAvailable: Boolean,
@ -41,18 +41,11 @@ export default {
cmsWidgetName: String, cmsWidgetName: String,
isRequired: Boolean isRequired: Boolean
}, },
methods: { emits: ['update:modelValue'],
initializeComponent(replaceOptions) { data() {
this.replaceOptions = replaceOptions; return {
}, replaceOptions: this.isMultiSelect ? [] : ''
updateSelectedValues() { };
// UPDATE SELECTEDVALUES IF ONLY ONE ANSWER
if (Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) {
this.selectedValues = this.isMultiSelect
? [this.answersToDisplay[0].Name]
: this.answersToDisplay[0].Name;
}
}
}, },
computed: { computed: {
questionText() { questionText() {
@ -62,10 +55,10 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'Answers'); return this.getCmsContent(this.cmsWidgetName, 'Answers');
}, },
selectedValues: { selectedValues: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
}, },
@ -74,8 +67,8 @@ export default {
? this.answersFromCms.filter((ans) => { ? this.answersFromCms.filter((ans) => {
const name = ans.Name.split('-'); const name = ans.Name.split('-');
return this.filterByVehicleCategory return this.filterByVehicleCategory
? name[0].toUpperCase() === this.mainStore.vehicle.category && ? name[0].toUpperCase() === this.mainStore.vehicle.category
this.replaceOptions.includes(name[1]) && this.replaceOptions.includes(name[1])
: this.replaceOptions.includes(ans.Name); : this.replaceOptions.includes(ans.Name);
}) })
: []; : [];
@ -93,6 +86,7 @@ export default {
watch: { watch: {
isAvailable(val) { isAvailable(val) {
// CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE // CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE
// eslint-disable-next-line no-unused-expressions
val && this.updateSelectedValues(); val && this.updateSelectedValues();
}, },
shouldDisplayReplaceOptionsQuestion(shouldDisplayReplaceOptionsQuestion) { shouldDisplayReplaceOptionsQuestion(shouldDisplayReplaceOptionsQuestion) {
@ -101,8 +95,18 @@ export default {
} }
} }
}, },
components: { methods: {
buttonQuestion initializeComponent(replaceOptions) {
this.replaceOptions = replaceOptions;
},
updateSelectedValues() {
// UPDATE SELECTEDVALUES IF ONLY ONE ANSWER
if (Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) {
this.selectedValues = this.isMultiSelect
? [this.answersToDisplay[0].Name]
: this.answersToDisplay[0].Name;
}
}
} }
}; };
</script> </script>

View file

@ -69,19 +69,17 @@ describe('replace-options-question.vue', () => {
driverSideReplaceOptions, driverSideReplaceOptions,
passengerSideReplaceOptions, passengerSideReplaceOptions,
driverSideOptions, driverSideOptions,
passengerSideOptions, passengerSideOptions
} = setupMocks({}); } = setupMocks({});
// Act // Act
sideDoorOptions.methods.initializeComponent.call( sideDoorOptions.methods.initializeComponent.call(wrapper.vm,
wrapper.vm,
cmsContent, cmsContent,
driverSideReplaceOptions, driverSideReplaceOptions,
passengerSideReplaceOptions, passengerSideReplaceOptions,
driverSideOptions, driverSideOptions,
passengerSideOptions, passengerSideOptions,
'car-group' 'car-group');
);
// Assert // Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([]); expect(wrapper.vm.answersToDisplay).toStrictEqual([]);
@ -110,8 +108,8 @@ function setupMocks({
}; };
mountOptions.propsData = { mountOptions.propsData = {
modelValue: modelValueProp, modelValue: modelValueProp,
groupName: groupName, groupName,
selectedDamageLocations: selectedDamageLocations, selectedDamageLocations
}; };
mountOptions.mixins = [mockMixin]; mountOptions.mixins = [mockMixin];
@ -122,7 +120,7 @@ function setupMocks({
OptionsWrapper[1].vm.initializeComponent = replaceOptionsQuestion.methods.initializeComponent; OptionsWrapper[1].vm.initializeComponent = replaceOptionsQuestion.methods.initializeComponent;
// Mock CMS content // Mock CMS content
const cmsContent = { const cmsContent = {
groupName: groupName, groupName,
QuestionText: cmsQuestionText, QuestionText: cmsQuestionText,
Answers: cmsAnswers Answers: cmsAnswers
}; };

View file

@ -1,39 +1,41 @@
<template> <template>
<div class="side-door-options"> <div class="side-door-options">
<transition name="fade" mode="out-in"> <transition
name="fade"
mode="out-in">
<div <div
class="side-doors"
v-if="selectedDamageLocations.includes('SideDoor')" v-if="selectedDamageLocations.includes('SideDoor')"
class="side-doors"
aria-live="polite"> aria-live="polite">
<buttonQuestion <buttonQuestion
v-model="selectedDoorSidesValues"
:questionText="questionText" :questionText="questionText"
isMultiSelect isMultiSelect
:answers="answersToDisplay" :answers="answersToDisplay"
:groupName="groupName" :groupName="groupName"
buttonTypeString="listCard" buttonTypeString="listCard"
v-model="selectedDoorSidesValues"
validationRules="damage-side-required" validationRules="damage-side-required"
isRequired /> isRequired />
</div> </div>
</transition> </transition>
<replaceOptionsQuestion <replaceOptionsQuestion
ref="driverSideOptions" ref="driverSideOptions"
v-model="selectedDriverSideReplaceOptionsValues"
cmsWidgetName="DriverSideReplaceOptionsQuestion" cmsWidgetName="DriverSideReplaceOptionsQuestion"
:isAvailable="isDriverSideReplaceOptionsQuestionAvailable" :isAvailable="isDriverSideReplaceOptionsQuestionAvailable"
groupName="driverSideOptions" groupName="driverSideOptions"
isMultiSelect isMultiSelect
filterByVehicleCategory filterByVehicleCategory
v-model="selectedDriverSideReplaceOptionsValues"
validationRules="driver-side-options-required" validationRules="driver-side-options-required"
isRequired /> isRequired />
<replaceOptionsQuestion <replaceOptionsQuestion
ref="passengerSideOptions" ref="passengerSideOptions"
v-model="selectedPassengerSideReplaceOptionsValues"
cmsWidgetName="PassengerSideReplaceOptionsQuestion" cmsWidgetName="PassengerSideReplaceOptionsQuestion"
:isAvailable="isPassengerSideReplaceOptionsQuestionAvailable" :isAvailable="isPassengerSideReplaceOptionsQuestionAvailable"
groupName="passengerSideOptions" groupName="passengerSideOptions"
isMultiSelect isMultiSelect
filterByVehicleCategory filterByVehicleCategory
v-model="selectedPassengerSideReplaceOptionsValues"
validationRules="passenger-side-options-required" validationRules="passenger-side-options-required"
isRequired /> isRequired />
</div> </div>
@ -53,30 +55,18 @@ defineRule('driver-side-options-required', required(errorMessages.DRIVER_SIDE_OP
defineRule('passenger-side-options-required', required(errorMessages.PASSENGER_SIDE_OPTIONS_REQUIRED)); defineRule('passenger-side-options-required', required(errorMessages.PASSENGER_SIDE_OPTIONS_REQUIRED));
export default { export default {
name: 'sideDoorOptions', name: 'side-door-options',
components: {
buttonQuestion,
replaceOptionsQuestion
},
props: { props: {
groupName: String, groupName: String,
modelValue: Object, modelValue: Object,
selectedDamageLocations: Array, selectedDamageLocations: Array,
cmsWidgetName: String cmsWidgetName: String
}, },
methods: { emits: ['update:modelValue'],
initializeComponent(driverSideOptions, passengerSideOptions) {
this.$refs.driverSideOptions.initializeComponent(driverSideOptions);
this.$refs.passengerSideOptions.initializeComponent(passengerSideOptions);
},
getSideDoorReplacementOptions(
selectedDoorSides,
selectedDriverSideReplaceOptions,
selectedPassengerSideReplaceOptions
) {
return {
selectedDoorSides: selectedDoorSides,
selectedDriverSideReplaceOptions: selectedDriverSideReplaceOptions,
selectedPassengerSideReplaceOptions: selectedPassengerSideReplaceOptions
};
},
},
computed: { computed: {
questionText() { questionText() {
return this.getCmsContent(this.cmsWidgetName, 'QuestionText'); return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
@ -85,47 +75,41 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'Answers'); return this.getCmsContent(this.cmsWidgetName, 'Answers');
}, },
selectedValues: { selectedValues: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
}, },
selectedDoorSidesValues: { selectedDoorSidesValues: {
get: function () { get() {
return this.selectedValues.selectedDoorSides; return this.selectedValues.selectedDoorSides;
}, },
set: function (newValue) { set(newValue) {
this.selectedValues = this.getSideDoorReplacementOptions( this.selectedValues = this.getSideDoorReplacementOptions(newValue,
newValue,
this.selectedValues.selectedDriverSideReplaceOptions, this.selectedValues.selectedDriverSideReplaceOptions,
this.selectedValues.selectedPassengerSideReplaceOptions this.selectedValues.selectedPassengerSideReplaceOptions);
);
} }
}, },
selectedDriverSideReplaceOptionsValues: { selectedDriverSideReplaceOptionsValues: {
get: function () { get() {
return this.selectedValues.selectedDriverSideReplaceOptions; return this.selectedValues.selectedDriverSideReplaceOptions;
}, },
set: function (newValue) { set(newValue) {
this.selectedValues = this.getSideDoorReplacementOptions( this.selectedValues = this.getSideDoorReplacementOptions(this.selectedValues.selectedDoorSides,
this.selectedValues.selectedDoorSides,
newValue, newValue,
this.selectedValues.selectedPassengerSideReplaceOptions this.selectedValues.selectedPassengerSideReplaceOptions);
);
} }
}, },
selectedPassengerSideReplaceOptionsValues: { selectedPassengerSideReplaceOptionsValues: {
get: function () { get() {
return this.selectedValues.selectedPassengerSideReplaceOptions; return this.selectedValues.selectedPassengerSideReplaceOptions;
}, },
set: function (newValue) { set(newValue) {
this.selectedValues = this.getSideDoorReplacementOptions( this.selectedValues = this.getSideDoorReplacementOptions(this.selectedValues.selectedDoorSides,
this.selectedValues.selectedDoorSides,
this.selectedValues.selectedDriverSideReplaceOptions, this.selectedValues.selectedDriverSideReplaceOptions,
newValue newValue);
);
} }
}, },
answersToDisplay() { answersToDisplay() {
@ -144,24 +128,35 @@ export default {
}, },
isDriverSideReplaceOptionsQuestionAvailable() { isDriverSideReplaceOptionsQuestionAvailable() {
return ( return (
Array.isArray(this.selectedDoorSidesValues) && Array.isArray(this.selectedDoorSidesValues)
this.selectedDoorSidesValues.includes(damageLocationsSelected.DRIVERSIDE) && && this.selectedDoorSidesValues.includes(damageLocationsSelected.DRIVERSIDE)
Array.isArray(this.selectedDamageLocations) && && Array.isArray(this.selectedDamageLocations)
this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR) && this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR)
); );
}, },
isPassengerSideReplaceOptionsQuestionAvailable() { isPassengerSideReplaceOptionsQuestionAvailable() {
return ( return (
Array.isArray(this.selectedDoorSidesValues) && Array.isArray(this.selectedDoorSidesValues)
this.selectedDoorSidesValues.includes(damageLocationsSelected.PASSENGERSIDE) && && this.selectedDoorSidesValues.includes(damageLocationsSelected.PASSENGERSIDE)
Array.isArray(this.selectedDamageLocations) && && Array.isArray(this.selectedDamageLocations)
this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR) && this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR)
); );
} }
}, },
components: { methods: {
buttonQuestion, initializeComponent(driverSideOptions, passengerSideOptions) {
replaceOptionsQuestion this.$refs.driverSideOptions.initializeComponent(driverSideOptions);
this.$refs.passengerSideOptions.initializeComponent(passengerSideOptions);
},
getSideDoorReplacementOptions(selectedDoorSides,
selectedDriverSideReplaceOptions,
selectedPassengerSideReplaceOptions) {
return {
selectedDoorSides,
selectedDriverSideReplaceOptions,
selectedPassengerSideReplaceOptions
};
}
} }
}; };
</script> </script>

View file

@ -4,7 +4,7 @@ import { createTestingPinia } from '@pinia/testing';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
import { routerParams } from '@/router/router-params'; import { routerParams } from '@/router/router-params';
import { vehicleCategories } from '@/constants/vehicle-categories'; import { vehicleCategories } from '@/constants/vehicle-categories';
import VehicleDamageComponent from './vehicle-damage.vue'; import VehicleDamageComponent from '@/layouts/vehicle-damage/vehicle-damage';
const mockRoute = { const mockRoute = {
params: {} params: {}
@ -74,9 +74,6 @@ describe('vehicle-damage.vue', () => {
await flushPromises(); await flushPromises();
expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate) expect(mockRouter.navigate)
.toHaveBeenCalledWith( .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, mockRoute);
navigationScenarios.CLICKED_FORWARD_WITH_REPAIR,
mockRoute
);
}); });
}); });

View file

@ -1,63 +1,67 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2"> <div class="container-fluid pb-2">
<div class="row mt-2 px-3"> <div class="row mt-2 px-3">
<div class="col"> <div class="col">
<alert <alert
ref="vehicleChangeAlert" v-if="shouldDisplayVehicleChangeAlert"
v-if="shouldDisplayVehicleChangeAlert" ref="vehicleChangeAlert"
class="mt-5 mb-0" class="mt-5 mb-0"
cmsWidgetName="VehicleChangeAlert" cmsWidgetName="VehicleChangeAlert"
alertClass="alert-warning" alertClass="alert-warning"
:isDismissible="false" /> :isDismissible="false" />
<vehicleBanner <vehicleBanner
class="mb-4" class="mb-4"
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" /> :displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" /> <siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<damageLocationQuestion <damageLocationQuestion
ref="damageLocation" ref="damageLocation"
cmsWidgetName="DamageLocationQuestion" v-model="selectedDamageLocations"
v-model="selectedDamageLocations" cmsWidgetName="DamageLocationQuestion"
groupName="DamageLocationQuestion" /> groupName="DamageLocationQuestion" />
<windshieldOptions <windshieldOptions
ref="windshieldOptions" ref="windshieldOptions"
v-model="selectedWindshieldOptions" v-model="selectedWindshieldOptions"
:hasRepairReplaceConflict="hasRepairReplaceConflict" :hasRepairReplaceConflict="hasRepairReplaceConflict"
:hasSplitSingleConflict="hasSplitSingleConflict" :hasSplitSingleConflict="hasSplitSingleConflict"
:selectedDamageLocations="selectedDamageLocations" :selectedDamageLocations="selectedDamageLocations"
class="mb-3" /> class="mb-3" />
<alert <alert
v-if="hasRepairReplaceConflict" v-if="hasRepairReplaceConflict"
class="my-5" class="my-5"
cmsWidgetName="HasReplacementConflict" cmsWidgetName="HasReplacementConflict"
alertClass="alert-danger" alertClass="alert-danger"
:isDismissible="false" /> :isDismissible="false" />
<sideDoorOptions <sideDoorOptions
ref="sideDoorOptions" v-show="!hasRepairReplaceConflict"
cmsWidgetName="SideDoorSideQuestion" ref="sideDoorOptions"
groupName="SideDoorSideQuestion" v-model="sideDoorOptionsData"
v-model="sideDoorOptionsData" cmsWidgetName="SideDoorSideQuestion"
v-show="!hasRepairReplaceConflict" groupName="SideDoorSideQuestion"
:selectedDamageLocations="selectedDamageLocations" class="mb-1"/> :selectedDamageLocations="selectedDamageLocations"
class="mb-1" />
<replaceOptionsQuestion <replaceOptionsQuestion
ref="backGlassOptions" ref="backGlassOptions"
cmsWidgetName="RearReplaceOptionsQuestion" v-model="selectedRearReplaceOptions"
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict" cmsWidgetName="RearReplaceOptionsQuestion"
v-model="selectedRearReplaceOptions" :isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict"
groupName="BackGlassReplaceOptionsQuestion" groupName="BackGlassReplaceOptionsQuestion"
validationRules="replace-options-required" /> validationRules="replace-options-required" />
<site-footer <site-footer
class="mt-5" ref="siteFooter"
cmsWidgetName="SiteFooterWidget" class="mt-5"
:isForwardActionDisabled="!meta.valid" cmsWidgetName="SiteFooterWidget"
@backClicked="backButtonAction" :isForwardActionDisabled="!meta.valid"
@forwardClicked="forwardButtonAction" @backClicked="backButtonAction"
ref="siteFooter" @forwardClicked="forwardButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -94,6 +98,20 @@ defineRule('replace-options-required', required(errorMessages.REPLACE_OPTIONS_RE
export default { export default {
name: 'vehicle-damage', name: 'vehicle-damage',
components: {
siteHeader,
siteFooter,
vehicleBanner,
siteSubHeader,
sideDoorOptions,
damageLocationQuestion,
windshieldOptions,
replaceOptionsQuestion,
// eslint-disable-next-line vue/no-reserved-component-names
Form,
alert
},
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const store = useMainStore(); const store = useMainStore();
@ -120,16 +138,10 @@ export default {
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions); vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions);
vm.$refs.sideDoorOptions.initializeComponent( vm.$refs.sideDoorOptions.initializeComponent(resultMap.damageOptions.driverSideOptions.availableReplacementOptions,
resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions);
resultMap.damageOptions.passengerSideOptions.availableReplacementOptions vm.$refs.windshieldOptions.initializeComponent(resultMap.damageOptions.windshieldOptions.availableReplacementOptions);
); vm.$refs.backGlassOptions.initializeComponent(resultMap.damageOptions.backGlassOptions.availableReplacementOptions);
vm.$refs.windshieldOptions.initializeComponent(
resultMap.damageOptions.windshieldOptions.availableReplacementOptions
);
vm.$refs.backGlassOptions.initializeComponent(
resultMap.damageOptions.backGlassOptions.availableReplacementOptions
);
}); });
}, },
setup() { setup() {
@ -149,6 +161,71 @@ export default {
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore() selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore()
}; };
}, },
computed: {
isWindshieldDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD);
},
isSideDoorDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR);
},
isRearWindowDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW);
},
isWindshieldRepair() {
return (
this.isWindshieldDamageLocation
&& this.selectedWindshieldOptions.selectedWindshieldDamageType
=== damageLocationsSelected.REPAIR
);
},
isDriverSideReplace() {
if (!this.isSideDoorDamageLocation) return false;
return this
.sideDoorOptionsData.selectedDoorSides
.some((selectedDriverSide) => selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE);
},
isPassengerSideReplace() {
if (!this.isSideDoorDamageLocation) return false;
return this
.sideDoorOptionsData.selectedDoorSides
.some((selectedPassengerSide) => selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE);
},
hasRepairReplaceConflict() {
return (
this.isWindshieldDamageLocation
&& this.selectedDamageLocations.length > 1
&& this.isWindshieldRepair
);
},
hasSplitSingleConflict() {
if (
!this.selectedDamageLocations?.includes('Windshield')
|| this.selectedWindshieldOptions.selectedWindshieldDamageType
=== damageLocationsSelected.REPAIR
|| !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions
) return false;
return (
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some((selectedSingleWindshield) => (
selectedSingleWindshield.toUpperCase()
=== damageLocationsSelected.SINGLE.toUpperCase()
))
&& (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some((selectedDriverWindshield) => (
selectedDriverWindshield.toUpperCase()
=== damageLocationsSelected.DRIVER.toUpperCase()
))
|| this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some((selectedPassengerWindshield) => (
selectedPassengerWindshield.toUpperCase()
=== damageLocationsSelected.PASSENGER.toUpperCase()
)))
);
},
shouldDisplayVehicleChangeAlert() {
return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT];
}
},
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
if (this.mainStore.order.vehicle.carId) { if (this.mainStore.order.vehicle.carId) {
@ -163,31 +240,25 @@ export default {
}, },
getDamageLocationsFromStore() { getDamageLocationsFromStore() {
var glassSelections = []; const glassSelections = [];
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.WINDSHIELD)
return glass.glassLocation === damageLocationsSelected.WINDSHIELD; || this.mainStore.order.damage.isRepair
}) ||
this.mainStore.order.damage.isRepair
) { ) {
glassSelections.push(damageLocationsSelected.WINDSHIELD); glassSelections.push(damageLocationsSelected.WINDSHIELD);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => (
return ( glass.glassLocation === damageLocationsSelected.DRIVER
glass.glassLocation === damageLocationsSelected.DRIVER || || glass.glassLocation === damageLocationsSelected.PASSENGER
glass.glassLocation === damageLocationsSelected.PASSENGER ))
);
})
) { ) {
glassSelections.push(damageLocationsSelected.SIDEDOOR); glassSelections.push(damageLocationsSelected.SIDEDOOR);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.REAR)
return glass.glassLocation === damageLocationsSelected.REAR;
})
) { ) {
glassSelections.push(damageLocationsSelected.REARWINDOW); glassSelections.push(damageLocationsSelected.REARWINDOW);
} }
@ -196,7 +267,7 @@ export default {
}, },
getWindshieldOptionsFromStore() { getWindshieldOptionsFromStore() {
var windShieldOptions = { const windShieldOptions = {
selectedWindshieldDamageType: '', selectedWindshieldDamageType: '',
selectedWindshieldChipCount: null, selectedWindshieldChipCount: null,
selectedWindshieldReplaceOptions: [] selectedWindshieldReplaceOptions: []
@ -209,48 +280,36 @@ export default {
windShieldOptions.selectedWindshieldChipCount = this.mainStore.order.damage.numberOfChips; windShieldOptions.selectedWindshieldChipCount = this.mainStore.order.damage.numberOfChips;
} else { } else {
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => (
return ( glass.glassLocation === damageLocationsSelected.WINDSHIELD
glass.glassLocation === damageLocationsSelected.WINDSHIELD && && glass.glassName === damageLocationsSelected.SINGLE
glass.glassName === damageLocationsSelected.SINGLE ))
);
})
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType
damageLocationsSelected.REPLACE; = damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push( windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE);
damageLocationsSelected.SINGLE
);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => (
return ( glass.glassLocation === damageLocationsSelected.WINDSHIELD
glass.glassLocation === damageLocationsSelected.WINDSHIELD && && glass.glassName === damageLocationsSelected.DRIVER
glass.glassName === damageLocationsSelected.DRIVER ))
);
})
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType
damageLocationsSelected.REPLACE; = damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push( windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER);
damageLocationsSelected.DRIVER
);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => (
return ( glass.glassLocation === damageLocationsSelected.WINDSHIELD
glass.glassLocation === damageLocationsSelected.WINDSHIELD && && glass.glassName === damageLocationsSelected.PASSENGER
glass.glassName === damageLocationsSelected.PASSENGER ))
);
})
) { ) {
windShieldOptions.selectedWindshieldDamageType = windShieldOptions.selectedWindshieldDamageType
damageLocationsSelected.REPLACE; = damageLocationsSelected.REPLACE;
windShieldOptions.selectedWindshieldReplaceOptions.push( windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER);
damageLocationsSelected.PASSENGER
);
} }
} }
@ -258,19 +317,15 @@ export default {
}, },
getDoorSidesFromStore() { getDoorSidesFromStore() {
var doorSides = []; const doorSides = [];
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.DRIVER)
return glass.glassLocation === damageLocationsSelected.DRIVER;
})
) { ) {
doorSides.push(damageLocationsSelected.DRIVERSIDE); doorSides.push(damageLocationsSelected.DRIVERSIDE);
} }
if ( if (
this.mainStore.order.damage.glassToReplace?.some((glass) => { this.mainStore.order.damage.glassToReplace?.some((glass) => glass.glassLocation === damageLocationsSelected.PASSENGER)
return glass.glassLocation === damageLocationsSelected.PASSENGER;
})
) { ) {
doorSides.push(damageLocationsSelected.PASSENGERSIDE); doorSides.push(damageLocationsSelected.PASSENGERSIDE);
} }
@ -279,7 +334,7 @@ export default {
}, },
getDriverSideReplaceOptionsFromStore() { getDriverSideReplaceOptionsFromStore() {
var driverSideReplaceOptions = []; const driverSideReplaceOptions = [];
this.mainStore.order.damage.glassToReplace?.forEach((glass) => { this.mainStore.order.damage.glassToReplace?.forEach((glass) => {
if (glass.glassLocation === damageLocationsSelected.DRIVER) { if (glass.glassLocation === damageLocationsSelected.DRIVER) {
@ -291,7 +346,7 @@ export default {
}, },
getPassengerSideReplaceOptionsFromStore() { getPassengerSideReplaceOptionsFromStore() {
var passengerSideReplaceOptions = []; const passengerSideReplaceOptions = [];
this.mainStore.order.damage.glassToReplace?.forEach((glass) => { this.mainStore.order.damage.glassToReplace?.forEach((glass) => {
if (glass.glassLocation === damageLocationsSelected.PASSENGER) { if (glass.glassLocation === damageLocationsSelected.PASSENGER) {
@ -303,9 +358,8 @@ export default {
}, },
getRearReplaceOptionsFromStore() { getRearReplaceOptionsFromStore() {
var rearReplaceOptions = this.mainStore.order.damage.glassToReplace?.filter( const rearReplaceOptions = this.mainStore.order.damage
(glass) => glass.glassLocation === damageLocationsSelected.REAR .glassToReplace?.filter((glass) => glass.glassLocation === damageLocationsSelected.REAR)[0]?.glassName;
)[0]?.glassName;
return rearReplaceOptions; return rearReplaceOptions;
}, },
@ -319,37 +373,27 @@ export default {
navigateForward() { navigateForward() {
if (this.mainStore.damage.isRepair) { if (this.mainStore.damage.isRepair) {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR,
this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, this.$route);
this.$route } else if (this.mainStore.order.vehicle.vin) {
);
} else {
// If vin already exists, navigate directly to vin-lookup // If vin already exists, navigate directly to vin-lookup
if (this.mainStore.order.vehicle.vin) { this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN,
this.$router.navigate( this.$route);
this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, } else {
this.$route this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
); this.$route);
} else {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
this.$route
);
}
} }
}, },
selectedGlassToReplace() { selectedGlassToReplace() {
const selectedGlassToReplace = []; const selectedGlassToReplace = [];
if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) { if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) {
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach( this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach((wsItem) => {
(wsItem) => { selectedGlassToReplace.push({
selectedGlassToReplace.push({ glassLocation: damageLocationsSelected.WINDSHIELD,
glassLocation: damageLocationsSelected.WINDSHIELD, glassName: wsItem
glassName: wsItem });
}); });
}
);
} }
if (this.isDriverSideReplace) { if (this.isDriverSideReplace) {
@ -362,14 +406,12 @@ export default {
} }
if (this.isPassengerSideReplace) { if (this.isPassengerSideReplace) {
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach( this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach((passengerItem) => {
(passengerItem) => { selectedGlassToReplace.push({
selectedGlassToReplace.push({ glassLocation: damageLocationsSelected.PASSENGER,
glassLocation: damageLocationsSelected.PASSENGER, glassName: passengerItem
glassName: passengerItem });
}); });
}
);
} }
if (this.isRearWindowDamageLocation) { if (this.isRearWindowDamageLocation) {
@ -381,103 +423,6 @@ export default {
return selectedGlassToReplace; return selectedGlassToReplace;
} }
},
computed: {
isWindshieldDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => {
return selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD;
});
},
isSideDoorDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => {
return selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR;
});
},
isRearWindowDamageLocation() {
return this.selectedDamageLocations.some((selectedDamages) => {
return selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW;
});
},
isWindshieldRepair() {
return (
this.isWindshieldDamageLocation &&
this.selectedWindshieldOptions.selectedWindshieldDamageType ===
damageLocationsSelected.REPAIR
);
},
isDriverSideReplace() {
if (!this.isSideDoorDamageLocation) return false;
return this.sideDoorOptionsData.selectedDoorSides.some((selectedDriverSide) => {
return selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE;
});
},
isPassengerSideReplace() {
if (!this.isSideDoorDamageLocation) return false;
return this.sideDoorOptionsData.selectedDoorSides.some((selectedPassengerSide) => {
return selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE;
});
},
hasRepairReplaceConflict() {
return (
this.isWindshieldDamageLocation &&
this.selectedDamageLocations.length > 1 &&
this.isWindshieldRepair
);
},
hasSplitSingleConflict() {
if (
!this.selectedDamageLocations?.includes('Windshield') ||
this.selectedWindshieldOptions.selectedWindshieldDamageType ===
damageLocationsSelected.REPAIR ||
!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions
)
return false;
return (
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
(selectedSingleWindshield) => {
return (
selectedSingleWindshield.toUpperCase() ===
damageLocationsSelected.SINGLE.toUpperCase()
);
}
) &&
(this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
(selectedDriverWindshield) => {
return (
selectedDriverWindshield.toUpperCase() ===
damageLocationsSelected.DRIVER.toUpperCase()
);
}
) ||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(
(selectedPassengerWindshield) => {
return (
selectedPassengerWindshield.toUpperCase() ===
damageLocationsSelected.PASSENGER.toUpperCase()
);
}
))
);
},
shouldDisplayVehicleChangeAlert() {
return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT];
}
},
components: {
siteHeader,
siteFooter,
vehicleBanner,
siteSubHeader,
sideDoorOptions,
damageLocationQuestion,
windshieldOptions,
replaceOptionsQuestion,
Form,
alert
} }
}; };
</script> </script>

View file

@ -36,7 +36,7 @@ function setupMocks({
// Mock props // Mock props
const mockMixin = { const mockMixin = {
methods: { methods: {
getCmsContent: jest.fn(), getCmsContent: jest.fn()
} }
}; };
mountOptions.propsData = { mountOptions.propsData = {
@ -48,7 +48,7 @@ function setupMocks({
// Mock CMS content // Mock CMS content
const cmsContent = { const cmsContent = {
groupName: groupName, groupName,
QuestionText: cmsQuestionText, QuestionText: cmsQuestionText,
Answers: cmsAnswers Answers: cmsAnswers
}; };

View file

@ -1,13 +1,18 @@
<template> <template>
<transition name="fade" mode="out-in"> <transition
<div class="windshield-chip-count-question" v-if="isAvailable" aria-live="polite"> name="fade"
mode="out-in">
<div
v-if="isAvailable"
class="windshield-chip-count-question"
aria-live="polite">
<buttonQuestion <buttonQuestion
v-model="selectedValue"
:questionText="questionText" :questionText="questionText"
:answers="answersFromCms" :answers="answersFromCms"
:groupName="groupName" :groupName="groupName"
buttonTypeString="listButtonHorizontal" buttonTypeString="listButtonHorizontal"
useTextForValue useTextForValue
v-model="selectedValue"
:validationRules="validationRules" :validationRules="validationRules"
isRequired /> isRequired />
</div> </div>
@ -18,7 +23,10 @@
import buttonQuestion from '@/digital-components/button-question/button-question'; import buttonQuestion from '@/digital-components/button-question/button-question';
export default { export default {
name: 'windshieldOptions', name: 'windshield-options',
components: {
buttonQuestion
},
props: { props: {
modelValue: [String, Number], modelValue: [String, Number],
groupName: String, groupName: String,
@ -26,6 +34,7 @@ export default {
validationRules: String, validationRules: String,
cmsWidgetName: String cmsWidgetName: String
}, },
emits: ['update:modelValue'],
computed: { computed: {
questionText() { questionText() {
return this.getCmsContent(this.cmsWidgetName, 'QuestionText'); return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
@ -34,17 +43,14 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'Answers'); return this.getCmsContent(this.cmsWidgetName, 'Answers');
}, },
selectedValue: { selectedValue: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
const numberValue = Number(newValue); const numberValue = Number(newValue);
this.$emit('update:modelValue', numberValue); this.$emit('update:modelValue', numberValue);
} }
} }
},
components: {
buttonQuestion
} }
}; };
</script> </script>

View file

@ -1,12 +1,17 @@
<template> <template>
<transition name="fade" mode="out-in"> <transition
<div class="windshield-damage-type-question" v-if="isAvailable" aria-live="polite"> name="fade"
mode="out-in">
<div
v-if="isAvailable"
class="windshield-damage-type-question"
aria-live="polite">
<buttonQuestion <buttonQuestion
v-model="selectedValues"
:questionText="questionText" :questionText="questionText"
:answers="answersFromCms" :answers="answersFromCms"
:groupName="groupName" :groupName="groupName"
buttonTypeString="listCard" buttonTypeString="listCard"
v-model="selectedValues"
:suppressError="suppressError" :suppressError="suppressError"
:validationRules="validationRules" :validationRules="validationRules"
isRequired /> isRequired />
@ -18,7 +23,10 @@
import buttonQuestion from '@/digital-components/button-question/button-question'; import buttonQuestion from '@/digital-components/button-question/button-question';
export default { export default {
name: 'windshieldDamageTypeQuestion', name: 'windshield-damage-type-question',
components: {
buttonQuestion
},
props: { props: {
modelValue: String, modelValue: String,
groupName: String, groupName: String,
@ -27,6 +35,7 @@ export default {
validationRules: String, validationRules: String,
cmsWidgetName: String cmsWidgetName: String
}, },
emits: ['update:modelValue'],
computed: { computed: {
questionText() { questionText() {
return this.getCmsContent(this.cmsWidgetName, 'QuestionText'); return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
@ -35,16 +44,13 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'Answers'); return this.getCmsContent(this.cmsWidgetName, 'Answers');
}, },
selectedValues: { selectedValues: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
} }
},
components: {
buttonQuestion
} }
}; };
</script> </script>

View file

@ -1,11 +1,11 @@
<template> <template>
<div class="windshield-options"> <div class="windshield-options">
<windshieldDamageTypeQuestion <windshieldDamageTypeQuestion
v-model="selectedWindshieldDamageTypeValue"
cmsWidgetName="WindshieldDamageTypeQuestion" cmsWidgetName="WindshieldDamageTypeQuestion"
:isAvailable="isWindshieldDamageLocation" :isAvailable="isWindshieldDamageLocation"
:suppressError="hasRepairReplaceConflict || showNoReplacementAvailableError" :suppressError="hasRepairReplaceConflict || showNoReplacementAvailableError"
groupName="WindshieldDamageTypeQuestion" groupName="WindshieldDamageTypeQuestion"
v-model="selectedWindshieldDamageTypeValue"
:validationRules="windshieldDamageTypeQuestionValidationRules" /> :validationRules="windshieldDamageTypeQuestionValidationRules" />
<alert <alert
v-if="showNoReplacementAvailableError" v-if="showNoReplacementAvailableError"
@ -14,18 +14,18 @@
alertClass="alert-danger" alertClass="alert-danger"
:isDismissible="false" /> :isDismissible="false" />
<windshieldChipCountQuestion <windshieldChipCountQuestion
v-model="selectedWindshieldChipCountValues"
cmsWidgetName="WindshieldChipCountQuestion" cmsWidgetName="WindshieldChipCountQuestion"
:isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict" :isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict"
groupName="WindshieldChipCountQuestion" groupName="WindshieldChipCountQuestion"
v-model="selectedWindshieldChipCountValues"
validationRules="windshield-chip-count-required" /> validationRules="windshield-chip-count-required" />
<replaceOptionsQuestion <replaceOptionsQuestion
ref="replaceOptionsQuestion" ref="replaceOptionsQuestion"
v-model="selectedWindshieldReplaceOptionsValues"
cmsWidgetName="WindshieldReplaceOptionsQuestion" cmsWidgetName="WindshieldReplaceOptionsQuestion"
:isAvailable="isReplaceOptionSelected" :isAvailable="isReplaceOptionSelected"
isMultiSelect isMultiSelect
groupName="WindshieldReplaceOptions" groupName="WindshieldReplaceOptions"
v-model="selectedWindshieldReplaceOptionsValues"
validationRules="windshield-replace-options-required|prevent-split-and-single-together" validationRules="windshield-replace-options-required|prevent-split-and-single-together"
:suppressError="hasSplitSingleConflict" :suppressError="hasSplitSingleConflict"
isRequired /> isRequired />
@ -39,8 +39,10 @@
</template> </template>
<script> <script>
import windshieldDamageTypeQuestion from '@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question'; import windshieldDamageTypeQuestion from
import windshieldChipCountQuestion from '@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question'; '@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question';
import windshieldChipCountQuestion from
'@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question';
import replaceOptionsQuestion from '@/layouts/vehicle-damage/replace-options-question/replace-options-question'; import replaceOptionsQuestion from '@/layouts/vehicle-damage/replace-options-question/replace-options-question';
import alert from '@/ux-components/alert/alert'; import alert from '@/ux-components/alert/alert';
@ -50,38 +52,26 @@ import { errorMessages } from '@/constants/error-messages';
import { damageLocationsSelected } from '@/constants/damage-locations-selected.js'; import { damageLocationsSelected } from '@/constants/damage-locations-selected.js';
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule( defineRule('windshield-damage-type-required',
'windshield-damage-type-required', required(errorMessages.WINDSHIELD_DAMAGE_TYPE_REQUIRED));
required(errorMessages.WINDSHIELD_DAMAGE_TYPE_REQUIRED) defineRule('windshield-chip-count-required',
); required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED));
defineRule( defineRule('windshield-replace-options-required',
'windshield-chip-count-required', required(errorMessages.WINDSHIELD_REPLACE_OPTIONS_REQUIRED));
required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED)
);
defineRule(
'windshield-replace-options-required',
required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED)
);
defineRule( defineRule('check-for-repair-and-replace',
'check-for-repair-and-replace', (selectedWindshieldDamageType, selectedDamageLocations) => (
(selectedWindshieldDamageType, selectedDamageLocations) => { selectedWindshieldDamageType.toString() !== damageLocationsSelected.REPAIR
return ( || (!selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD)
selectedWindshieldDamageType.toString() != damageLocationsSelected.REPAIR || && !selectedDamageLocations[0]?.includes(damageLocationsSelected.WINDSHIELD))
(!selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD) && || selectedDamageLocations[0].length === 1
!selectedDamageLocations[0]?.includes(damageLocationsSelected.WINDSHIELD)) || ));
selectedDamageLocations[0].length === 1 defineRule('repair-only', (value) => value.toString() === damageLocationsSelected.REPAIR);
);
}
);
defineRule('repair-only', (value) => {
return value.toString() === damageLocationsSelected.REPAIR;
});
defineRule('prevent-split-and-single-together', (value) => { defineRule('prevent-split-and-single-together', (value) => {
if ( if (
value.toString().toUpperCase().includes(damageLocationsSelected.SINGLE.toUpperCase()) && value.toString().toUpperCase().includes(damageLocationsSelected.SINGLE.toUpperCase())
(value.toString().toUpperCase().includes(damageLocationsSelected.DRIVER.toUpperCase()) || && (value.toString().toUpperCase().includes(damageLocationsSelected.DRIVER.toUpperCase())
value || value
.toString() .toString()
.toUpperCase() .toUpperCase()
.includes(damageLocationsSelected.PASSENGER.toUpperCase())) .includes(damageLocationsSelected.PASSENGER.toUpperCase()))
@ -92,12 +82,12 @@ defineRule('prevent-split-and-single-together', (value) => {
}); });
export default { export default {
name: 'windshieldOptions', name: 'windshield-options',
components: {
data() { windshieldDamageTypeQuestion,
return { windshieldChipCountQuestion,
windshieldAvailableReplacementOptions: Object replaceOptionsQuestion,
}; alert
}, },
props: { props: {
@ -106,102 +96,75 @@ export default {
hasRepairReplaceConflict: Boolean, hasRepairReplaceConflict: Boolean,
hasSplitSingleConflict: Boolean hasSplitSingleConflict: Boolean
}, },
emits: ['update:modelValue'],
methods: { data() {
initializeComponent(windshieldAvailableReplacementOptions) { return {
this.windshieldAvailableReplacementOptions = windshieldAvailableReplacementOptions; windshieldAvailableReplacementOptions: Object
this.$refs.replaceOptionsQuestion.initializeComponent( };
windshieldAvailableReplacementOptions
);
},
getWindshieldOptions(
selectedWindshieldDamageType,
selectedWindshieldChipCount,
selectedWindshieldReplaceOptions
) {
// ONLY UPDATE THE NEW VALUE IF IT IS TRUTHY (NOT NULL)
return {
selectedWindshieldDamageType: selectedWindshieldDamageType
? selectedWindshieldDamageType
: this.selectedValues.selectedWindshieldDamageType,
selectedWindshieldChipCount: selectedWindshieldChipCount
? selectedWindshieldChipCount
: this.selectedValues.selectedWindshieldChipCount,
selectedWindshieldReplaceOptions: selectedWindshieldReplaceOptions
? selectedWindshieldReplaceOptions
: this.selectedValues.selectedWindshieldReplaceOptions,
};
}
}, },
computed: { computed: {
selectedValues: { selectedValues: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
}, },
selectedWindshieldDamageTypeValue: { selectedWindshieldDamageTypeValue: {
get: function () { get() {
return this.selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD) return this.selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD)
? this.selectedValues.selectedWindshieldDamageType ? this.selectedValues.selectedWindshieldDamageType
: null; : null;
}, },
set: function (newValue) { set(newValue) {
this.selectedValues = this.getWindshieldOptions(newValue, null, null); this.selectedValues = this.getWindshieldOptions(newValue, null, null);
}, }
}, },
selectedWindshieldChipCountValues: { selectedWindshieldChipCountValues: {
get: function () { get() {
return this.selectedValues.selectedWindshieldChipCount; return this.selectedValues.selectedWindshieldChipCount;
}, },
set: function (newValue) { set(newValue) {
this.selectedValues = this.getWindshieldOptions( this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValue,
this.selectedWindshieldDamageTypeValue,
newValue, newValue,
null null);
);
} }
}, },
selectedWindshieldReplaceOptionsValues: { selectedWindshieldReplaceOptionsValues: {
get: function () { get() {
return this.selectedValues.selectedWindshieldReplaceOptions; return this.selectedValues.selectedWindshieldReplaceOptions;
}, },
set: function (newValue) { set(newValue) {
this.selectedValues = this.getWindshieldOptions( this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValue,
this.selectedWindshieldDamageTypeValue,
null, null,
newValue newValue);
);
} }
}, },
isWindshieldDamageLocation() { isWindshieldDamageLocation() {
return this.selectedDamageLocations.some( return this.selectedDamageLocations.some((selectedDamageLocation) =>
(selectedDamageLocation) => selectedDamageLocation === damageLocationsSelected.WINDSHIELD);
selectedDamageLocation === damageLocationsSelected.WINDSHIELD
);
}, },
isRepairOptionSelected() { isRepairOptionSelected() {
if (!this.selectedWindshieldDamageTypeValue) return false; if (!this.selectedWindshieldDamageTypeValue) return false;
return ( return (
this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPAIR && this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPAIR
this.isWindshieldDamageLocation && this.isWindshieldDamageLocation
); );
}, },
isReplaceOptionSelected() { isReplaceOptionSelected() {
if (!this.selectedWindshieldDamageTypeValue) return false; if (!this.selectedWindshieldDamageTypeValue) return false;
return ( return (
this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPLACE && this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPLACE
this.isWindshieldDamageLocation && this.isWindshieldDamageLocation
); );
}, },
isWindshieldReplaceAvailable() { isWindshieldReplaceAvailable() {
return !( return !(
Array.isArray(this.windshieldAvailableReplacementOptions) && Array.isArray(this.windshieldAvailableReplacementOptions)
this.windshieldAvailableReplacementOptions.length < 1 && this.windshieldAvailableReplacementOptions.length < 1
); );
}, },
isSplitWindshieldOption() { isSplitWindshieldOption() {
@ -219,8 +182,8 @@ export default {
}, },
windshieldDamageTypeQuestionValidationRules() { windshieldDamageTypeQuestionValidationRules() {
// Note: the validation rules string is not dynamic (it cannot be changed once component has been created) // Note: the validation rules string is not dynamic (it cannot be changed once component has been created)
let validationRules = let validationRules
'windshield-damage-type-required|check-for-repair-and-replace:@DamageLocationQuestion'; = 'windshield-damage-type-required|check-for-repair-and-replace:@DamageLocationQuestion';
// if vehicle has no windshield replacement option // if vehicle has no windshield replacement option
if (!this.isWindshieldReplaceAvailable) { if (!this.isWindshieldReplaceAvailable) {
validationRules = validationRules.concat('|repair-only'); validationRules = validationRules.concat('|repair-only');
@ -228,11 +191,22 @@ export default {
return validationRules; return validationRules;
} }
}, },
components: {
windshieldDamageTypeQuestion, methods: {
windshieldChipCountQuestion, initializeComponent(windshieldAvailableReplacementOptions) {
replaceOptionsQuestion, this.windshieldAvailableReplacementOptions = windshieldAvailableReplacementOptions;
alert this.$refs.replaceOptionsQuestion.initializeComponent(windshieldAvailableReplacementOptions);
},
getWindshieldOptions(selectedWindshieldDamageType,
selectedWindshieldChipCount,
selectedWindshieldReplaceOptions) {
// ONLY UPDATE THE NEW VALUE IF IT IS TRUTHY (NOT NULL)
return {
selectedWindshieldDamageType: selectedWindshieldDamageType || this.selectedValues.selectedWindshieldDamageType,
selectedWindshieldChipCount: selectedWindshieldChipCount || this.selectedValues.selectedWindshieldChipCount,
selectedWindshieldReplaceOptions: selectedWindshieldReplaceOptions || this.selectedValues.selectedWindshieldReplaceOptions
};
}
} }
}; };
</script> </script>

View file

@ -3,8 +3,7 @@ import { mount } from '@vue/test-utils';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
import { queryStrings } from '@/constants/query-strings'; import { queryStrings } from '@/constants/query-strings';
import { GaActions } from '@/constants/analytics'; import { GaActions } from '@/constants/analytics';
import VehicleLookup from './vehicle-lookup.vue'; import { vinLookupMethodSelections } from '@/constants/vin-lookup-methods';
import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
@ -12,6 +11,7 @@ import { defineRule } from 'vee-validate';
import { errorMessages } from '@/constants/error-messages'; import { errorMessages } from '@/constants/error-messages';
import globalRules from '@/constants/global-rules'; import globalRules from '@/constants/global-rules';
import { required } from '@/helpers/validation-rules'; import { required } from '@/helpers/validation-rules';
import VehicleLookup from '@/layouts/vehicle-lookup/vehicle-lookup';
const pinia = createTestingPinia(); const pinia = createTestingPinia();
useMainStore(pinia); useMainStore(pinia);
@ -59,7 +59,7 @@ function setupMocks() {
navigate: jest.fn() navigate: jest.fn()
}; };
const mockIsVinbyAddressPermissibleToBeFalse={ const mockIsVinbyAddressPermissibleToBeFalse = {
IsVinbyAddressPermissible: false IsVinbyAddressPermissible: false
}; };
@ -126,7 +126,7 @@ function setupMocks() {
describe('vehicle-lookup.vue', () => { describe('vehicle-lookup.vue', () => {
test('IsVinbyAddressPermissible to be false', () => { test('IsVinbyAddressPermissible to be false', () => {
const { wrapper,mockIsVinbyAddressPermissibleToBeFalse } = setupMocks(); const { wrapper, mockIsVinbyAddressPermissibleToBeFalse } = setupMocks();
const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' }); const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
// Not using initializeComponent() because it doesn't trigger reactivity in test. // Not using initializeComponent() because it doesn't trigger reactivity in test.
@ -139,7 +139,8 @@ describe('vehicle-lookup.vue', () => {
answersFromCms: Answers answersFromCms: Answers
}); });
const lookupByAddressOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-HomeAddress"]'); const lookupByAddressOptionLabel = wrapper
.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-HomeAddress"]');
expect(lookupByAddressOptionLabel.exists()).toBe(false); expect(lookupByAddressOptionLabel.exists()).toBe(false);
}); });
@ -165,7 +166,8 @@ describe('vehicle-lookup.vue', () => {
answersFromCms: vinLookupMethodsMockData.Answers answersFromCms: vinLookupMethodsMockData.Answers
}); });
const lookupByManualVinOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-ManualVin"]'); const lookupByManualVinOptionLabel = wrapper
.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-ManualVin"]');
await lookupByManualVinOptionLabel.trigger('mousedown'); await lookupByManualVinOptionLabel.trigger('mousedown');
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]'); const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
@ -193,7 +195,8 @@ describe('vehicle-lookup.vue', () => {
useMainStore().updateVehicleVin = jest.fn(); useMainStore().updateVehicleVin = jest.fn();
const lookupByManualVinOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-ManualVin"]'); const lookupByManualVinOptionLabel = wrapper
.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-ManualVin"]');
await lookupByManualVinOptionLabel.trigger('mousedown'); await lookupByManualVinOptionLabel.trigger('mousedown');
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]'); const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
@ -218,7 +221,8 @@ describe('vehicle-lookup.vue', () => {
answersFromCms: vinLookupMethodsMockData.Answers answersFromCms: vinLookupMethodsMockData.Answers
}); });
const lookupByLicensePlateOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-LicensePlate"]'); const lookupByLicensePlateOptionLabel = wrapper
.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-LicensePlate"]');
await lookupByLicensePlateOptionLabel.trigger('mousedown'); await lookupByLicensePlateOptionLabel.trigger('mousedown');
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]'); const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
@ -243,7 +247,8 @@ describe('vehicle-lookup.vue', () => {
answersFromCms: vinLookupMethodsMockData.Answers answersFromCms: vinLookupMethodsMockData.Answers
}); });
const lookupByAddressOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-HomeAddress"]'); const lookupByAddressOptionLabel = wrapper
.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-HomeAddress"]');
await lookupByAddressOptionLabel.trigger('mousedown'); await lookupByAddressOptionLabel.trigger('mousedown');
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]'); const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');

View file

@ -1,5 +1,7 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit"> <Form
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -8,10 +10,24 @@
<div class="row px-3"> <div class="row px-3">
<div class="col"> <div class="col">
<div class="select-car-form rounded"> <div class="select-car-form rounded">
<VehicleBanner class="mt-2 mb-4" cms-widget-name="VehicleBannerWidget" :display-generic-vehicle-image="false" /> <VehicleBanner
<SiteSubHeader cms-widget-name="SiteSubHeaderWidget" class="mt-5 mb-2" /> class="mt-2 mb-4"
<VinLookupMethods v-model="selectedVinLookupMethod" cms-widget-name="VINLookupMethod" group-name="VinLookupMethods" ref="VinLookupMethods" /> cmsWidgetName="VehicleBannerWidget"
<SiteFooter cms-widget-name="SiteFooterWidget" :is-forward-action-disabled="isForwardActionDisabled" @back-clicked="backButtonAction" @forward-clicked="forwardButtonAction" class="mt-5"/> :displayGenericVehicleImage="false" />
<SiteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="mt-5 mb-2" />
<VinLookupMethods
ref="VinLookupMethods"
v-model="selectedVinLookupMethod"
cmsWidgetName="VINLookupMethod"
groupName="VinLookupMethods" />
<SiteFooter
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="isForwardActionDisabled"
class="mt-5"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction" />
</div> </div>
</div> </div>
</div> </div>
@ -27,20 +43,20 @@ import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper'; import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin'; import BaseFormMixin from '@/mixins/base-form-mixin';
import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods'; import { vinLookupMethodSelections } from '@/constants/vin-lookup-methods';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
// Import Component // Import Component
import SiteFooter from '@/iss-components/site-footer/site-footer.vue'; import SiteFooter from '@/iss-components/site-footer/site-footer';
import SiteHeader from '@/iss-components/site-header/site-header.vue'; import SiteHeader from '@/iss-components/site-header/site-header';
import SiteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import SiteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import VehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import VehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import VinLookupMethods from './vin-lookup-methods/vin-lookup-methods.vue'; import VinLookupMethods from '@/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods';
export default { export default {
name: 'vehicle-lookup', name: 'vehicle-lookup',
mixins: [BaseFormMixin],
components: { components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
SiteFooter, SiteFooter,
SiteHeader, SiteHeader,
@ -48,11 +64,7 @@ export default {
VehicleBanner, VehicleBanner,
VinLookupMethods VinLookupMethods
}, },
data() { mixins: [BaseFormMixin],
return {
selectedVinLookupMethod: null
};
},
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -71,6 +83,11 @@ export default {
vm.$refs.VinLookupMethods.initializeComponent(); vm.$refs.VinLookupMethods.initializeComponent();
}); });
}, },
data() {
return {
selectedVinLookupMethod: null
};
},
computed: { computed: {
isForwardActionDisabled() { isForwardActionDisabled() {
return this.selectedVinLookupMethod === null; return this.selectedVinLookupMethod === null;

View file

@ -2,12 +2,11 @@
<ButtonQuestion <ButtonQuestion
v-model="selectedValue" v-model="selectedValue"
:answers="answersFromCms" :answers="answersFromCms"
button-type-string="listButton" buttonTypeString="listButton"
:question-text="questionTextFromCms" :questionText="questionTextFromCms"
is-required isRequired
:validation-rules="rules.optionRequired" :validationRules="rules.optionRequired"
data-test-id="vin-lookup-methods-button-question" data-test-id="vin-lookup-methods-button-question" />
/>
</template> </template>
<script> <script>
@ -19,18 +18,12 @@ import { settleAllPromises } from '@/helpers/layout-helper';
import globalRules from '@/constants/global-rules'; import globalRules from '@/constants/global-rules';
// Import Component // Import Component
import ButtonQuestion from '@/digital-components/button-question/button-question.vue'; import ButtonQuestion from '@/digital-components/button-question/button-question';
export default { export default {
name: 'vin-lookup-methods', name: 'vin-lookup-methods',
data() { components: {
return { ButtonQuestion
questionTextFromCms: '',
answersFromCms: [],
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
}, },
props: { props: {
modelValue: String, modelValue: String,
@ -40,8 +33,14 @@ export default {
} }
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
components: { data() {
ButtonQuestion return {
questionTextFromCms: '',
answersFromCms: [],
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
}, },
computed: { computed: {
selectedValue: { selectedValue: {

View file

@ -1,6 +1,6 @@
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import glassPartQuestion from '@/layouts/vehicle-parts/glass-part-question/glass-part-question.vue'; import glassPartQuestion from '@/layouts/vehicle-parts/glass-part-question/glass-part-question';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
const featureListData = { const featureListData = {
@ -31,16 +31,12 @@ describe('glass-part-question.vue', () => {
// Act // Act
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
console.log(wrapper.vm.featureListData['Green Tint'][0].Text) console.log(wrapper.vm.featureListData['Green Tint'][0].Text);
// Assert // Assert
expect(Object.keys(wrapper.vm.featureListData).length).toBe(2); expect(Object.keys(wrapper.vm.featureListData).length).toBe(2);
expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe( expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe('heated glass, solar, 1 hole');
'heated glass, solar, 1 hole'
);
expect(wrapper.vm.featureListData['Green Tint'][0].Name).toBe('DB12209GTYN'); expect(wrapper.vm.featureListData['Green Tint'][0].Name).toBe('DB12209GTYN');
expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe( expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe('heated glass, solar, 1 hole');
'heated glass, solar, 1 hole'
);
expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Name).toBe('DB12209YPYN'); expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Name).toBe('DB12209YPYN');
}); });
@ -161,8 +157,7 @@ describe('glass-part-question.vue', () => {
['Driver', 'Quarter', 'Green Tint', []] ['Driver', 'Quarter', 'Green Tint', []]
]; ];
test.each(partsForSelectedTintTestCases)( test.each(partsForSelectedTintTestCases)('partsForSelectedTint returns correct parts',
'partsForSelectedTint returns correct parts',
async (glassLocation, glassName, selectedTint, expectedResults) => { async (glassLocation, glassName, selectedTint, expectedResults) => {
// Arrange // Arrange
const pageData = { partsOrQuestions: [ const pageData = { partsOrQuestions: [
@ -187,7 +182,7 @@ describe('glass-part-question.vue', () => {
{ partNumber: 'Windshield2', color: 'Green Tint' } { partNumber: 'Windshield2', color: 'Green Tint' }
] ]
} }
] } ] };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
glassLocationProp: glassLocation, glassLocationProp: glassLocation,
glassNameProp: glassName, glassNameProp: glassName,
@ -196,12 +191,11 @@ describe('glass-part-question.vue', () => {
}); });
// Act // Act
await wrapper.setData({ selectedTint: selectedTint }); await wrapper.setData({ selectedTint });
// Assert // Assert
expect(expectedResults).toEqual(wrapper.vm.partsForSelectedTint); expect(expectedResults).toEqual(wrapper.vm.partsForSelectedTint);
} });
);
}); });
function setupMocks({ glassNameProp, glassLocationProp, colorAnswersProp, modelValueProp, pageData }) { function setupMocks({ glassNameProp, glassLocationProp, colorAnswersProp, modelValueProp, pageData }) {

View file

@ -1,6 +1,8 @@
<template> <template>
<div class="row"> <div class="row">
<p class="mb-0 color-question-text">{{ colorQuestionText }}</p> <p class="mb-0 color-question-text">
{{ colorQuestionText }}
</p>
</div> </div>
<div class="nested-radio"> <div class="nested-radio">
<div class="row my-2"> <div class="row my-2">
@ -14,7 +16,9 @@
isRequired isRequired
:groupName="replaceAllSpaceWithDash(`${glassLocation}-${glassName}`)" :groupName="replaceAllSpaceWithDash(`${glassLocation}-${glassName}`)"
:validationRules="tintValidationRules"> :validationRules="tintValidationRules">
<div class="row my-2" aria-live="polite"> <div
class="row my-2"
aria-live="polite">
<div class="col"> <div class="col">
<buttonQuestion <buttonQuestion
id="glass-part-question" id="glass-part-question"
@ -50,15 +54,10 @@ import { errorMessages } from '@/constants/error-messages';
export default { export default {
name: 'glass-part-question', name: 'glass-part-question',
emits: ['update:modelValue'], components: {
inheritAttrs: false, buttonQuestion
data() {
return {
glassColorQuestion: '',
glassFeatureQuestion: '',
selectedTint: ''
};
}, },
inheritAttrs: false,
props: { props: {
glassName: String, glassName: String,
glassLocation: String, glassLocation: String,
@ -69,11 +68,13 @@ export default {
default: () => [] default: () => []
} }
}, },
mounted() { emits: ['update:modelValue'],
this.LoadPreselectedValues(); data() {
}, return {
components: { glassColorQuestion: '',
buttonQuestion glassFeatureQuestion: '',
selectedTint: ''
};
}, },
computed: { computed: {
tintValidationRules() { tintValidationRules() {
@ -90,23 +91,19 @@ export default {
return validationRuleName; return validationRuleName;
}, },
colorQuestionText() { colorQuestionText() {
return getCustomTransformValue( return getCustomTransformValue(this.glassColorQuestion,
this.glassColorQuestion, `${this.glassLocation} ${this.glassName}`);
`${this.glassLocation} ${this.glassName}`
);
}, },
tintSelectionOptions() { tintSelectionOptions() {
let tintOptions = []; const tintOptions = [];
Object.keys(this.featureListData).forEach((tintOption) => { Object.keys(this.featureListData).forEach((tintOption) => {
tintOptions.push({ tintOptions.push({
value: tintOption, value: tintOption,
buttonLabel: tintOption, buttonLabel: tintOption,
buttonImage: require(`@/assets/img/tints/${this.getTintSourceImage( buttonImage: require(`@/assets/img/tints/${this.getTintSourceImage(this.glassLocation,
this.glassLocation, tintOption)}`)
tintOption
)}`),
}); });
}); });
@ -118,19 +115,18 @@ export default {
return this.modelValue?.partNumber; return this.modelValue?.partNumber;
}, },
set(newValue) { set(newValue) {
const part = this.partsForSelectedTint.filter((part) => part.partNumber == newValue?.value)[0]; const part = this.partsForSelectedTint
.filter((partItem) => partItem.partNumber === newValue?.value)[0];
this.$emit('update:modelValue', part); this.$emit('update:modelValue', part);
} }
}, },
partsForSelectedTint() { partsForSelectedTint() {
const matchingGlass = this.PartDataFromApi.partsOrQuestions?.filter( const matchingGlass = this.PartDataFromApi.partsOrQuestions?.filter((dataForGlassLocationAndName) =>
(dataForGlassLocationAndName) => dataForGlassLocationAndName.glassName === this.glassName
dataForGlassLocationAndName.glassName == this.glassName && && dataForGlassLocationAndName.glassLocation === this.glassLocation);
dataForGlassLocationAndName.glassLocation == this.glassLocation const matchingGlassParts = matchingGlass?.length === 1 ? matchingGlass[0].parts : [];
); return matchingGlassParts.filter((part) => part.color === this.selectedTint) ?? [];
const matchingGlassParts = matchingGlass?.length == 1 ? matchingGlass[0].parts : [];
return matchingGlassParts.filter((part) => part.color == this.selectedTint) ?? [];
}, },
// Creates a map of the feature list data in the correct Name/Value // Creates a map of the feature list data in the correct Name/Value
@ -142,9 +138,9 @@ export default {
arr[itemColor] = arr[itemColor] || []; arr[itemColor] = arr[itemColor] || [];
// Map the data to Name/Text object for ButtonQuestion // Map the data to Name/Text object for ButtonQuestion
const mappedItem = item.FeatureAnswers.reduce((featureArr, item) => { const mappedItem = item.FeatureAnswers.reduce((featureArr, it) => {
featureArr['Text'] = item.FeatureAnswerText; // Display to User featureArr.Text = it.FeatureAnswerText; // Display to User
featureArr['Name'] = item.PartNumber; // Backing Value featureArr.Name = it.PartNumber; // Backing Value
return featureArr; return featureArr;
}, {}); }, {});
@ -161,6 +157,14 @@ export default {
return this.mainStore.pageData(this.$route.query.issPage) ?? {}; return this.mainStore.pageData(this.$route.query.issPage) ?? {};
} }
}, },
watch: {
selectedTint() {
this.AutoSelectIfSinglePart();
}
},
mounted() {
this.LoadPreselectedValues();
},
methods: { methods: {
// Initialize the component data // Initialize the component data
initializeComponent(cmsContent) { initializeComponent(cmsContent) {
@ -173,7 +177,7 @@ export default {
getTintSourceImage(glassLocation, tintColor) { getTintSourceImage(glassLocation, tintColor) {
const tintSourceObject = getTintImage(glassLocation, tintColor); const tintSourceObject = getTintImage(glassLocation, tintColor);
if (tintSourceObject === undefined || tintSourceObject.src == undefined) { if (tintSourceObject === undefined || !tintSourceObject.src) {
return ''; return '';
} }
@ -183,12 +187,12 @@ export default {
AutoSelectIfSinglePart() { AutoSelectIfSinglePart() {
if (this.partsForSelectedTint?.length > 0) { if (this.partsForSelectedTint?.length > 0) {
// Check if only a single part is present for the tint and set the v-model if it is. // Check if only a single part is present for the tint and set the v-model if it is.
if (this.partsForSelectedTint?.length == 1) { if (this.partsForSelectedTint?.length === 1) {
this.selectedPartNumber = { value: this.partsForSelectedTint[0].partNumber }; this.selectedPartNumber = { value: this.partsForSelectedTint[0].partNumber };
// //select element with matching partNumber // //select element with matching partNumber
this.$nextTick(() => { this.$nextTick(() => {
const radioInput = document.querySelector('input[value=' + this.selectedPartNumber + ']'); const radioInput = document.querySelector(`input[value=${this.selectedPartNumber}]`);
// Fire a click event on the input so the field is updated // Fire a click event on the input so the field is updated
radioInput?.click(); radioInput?.click();
}); });
@ -211,16 +215,11 @@ export default {
replaceAllSpaceWithDash(str) { replaceAllSpaceWithDash(str) {
return String(str).replaceAll(' ', '-'); return String(str).replaceAll(' ', '-');
} }
},
watch: {
selectedTint() {
this.AutoSelectIfSinglePart();
}
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.nested-radio { .nested-radio {
.ui-radio { .ui-radio {
flex-direction: column; flex-direction: column;

View file

@ -1,5 +1,5 @@
// Components // Components
import vehicleParts from '@/layouts/vehicle-parts/vehicle-parts.vue'; import vehicleParts from '@/layouts/vehicle-parts/vehicle-parts';
import glassPartQuestion from '@/layouts/vehicle-parts/glass-part-question/glass-part-question'; import glassPartQuestion from '@/layouts/vehicle-parts/glass-part-question/glass-part-question';
// Supporting Files // Supporting Files
@ -82,12 +82,10 @@ describe('vehicle-parts.vue', () => {
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.setCmsContent = jest.fn();
// Assert // Assert
@ -117,14 +115,12 @@ describe('vehicle-parts.vue', () => {
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
await nextTick(); await nextTick();
// Assert // Assert
@ -151,12 +147,10 @@ describe('vehicle-parts.vue', () => {
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
await nextTick(); await nextTick();
@ -189,20 +183,16 @@ describe('vehicle-parts.vue', () => {
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
wrapper.vm.navigateBack(); wrapper.vm.navigateBack();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS, wrapper.vm.$route);
wrapper.vm.$route
);
}); });
test('User did not have part questions > navigateBack triggers a router.navigate change with correct scenario', async () => { test('User did not have part questions > navigateBack triggers a router.navigate change with correct scenario', async () => {
@ -221,20 +211,16 @@ describe('vehicle-parts.vue', () => {
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
wrapper.vm.navigateBack(); wrapper.vm.navigateBack();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS, wrapper.vm.$route);
wrapper.vm.$route
);
}); });
test('ForwardButtonAction triggers a router.navigate change if there are child part questions', async () => { test('ForwardButtonAction triggers a router.navigate change if there are child part questions', async () => {
@ -296,22 +282,20 @@ describe('vehicle-parts.vue', () => {
}); });
wrapper.setData({ wrapper.setData({
selectedGlassParts: { 'Rear-Stationary': { partNumber: 'FW03861GTYN' } }, selectedGlassParts: { 'Rear-Stationary': { partNumber: 'FW03861GTYN' } }
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith('CLICKED_FORWARD_WITH_MOLDING_QUESTIONS', {'query': {'issPage': 'vehicle-parts'}}, {}, {}, expect.anything()); expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith('CLICKED_FORWARD_WITH_MOLDING_QUESTIONS', { query: { issPage: 'vehicle-parts' } }, {}, {}, expect.anything());
}); });
test('ForwardButtonAction triggers a router.navigate change if there are capability questions', async () => { test('ForwardButtonAction triggers a router.navigate change if there are capability questions', async () => {
@ -363,18 +347,16 @@ describe('vehicle-parts.vue', () => {
}); });
// Act // Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'vehicle-parts' } }, { query: { issPage: 'vehicle-parts' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith('CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS', {'query': {'issPage': 'vehicle-parts'}}, {}, {}, expect.anything()); expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith('CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS', { query: { issPage: 'vehicle-parts' } }, {}, {}, expect.anything());
}); });
}); });
@ -401,8 +383,8 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}
}; };
const store = useMainStore(); const store = useMainStore();
let actionResult = []; const actionResult = [];
store.getCapabilityQuestions.mockImplementation(() => Promise.resolve({data: actionResult,})); store.getCapabilityQuestions.mockImplementation(() => Promise.resolve({ data: actionResult }));
const apiPromise = Promise.resolve(apiResponses); const apiPromise = Promise.resolve(apiResponses);

View file

@ -1,5 +1,8 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm"> <Form
ref="theForm"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -9,24 +12,28 @@
<div class="col"> <div class="col">
<div class="select-car-form rounded"> <div class="select-car-form rounded">
<vehicleBanner <vehicleBanner
class="mt-2 mb-4"
ref="vehicleBanner" ref="vehicleBanner"
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" /> :displayGenericVehicleImage="false" />
<siteSubHeader ref="siteSubHeader" cmsWidgetName="SiteSubHeaderWidget" /> <siteSubHeader
ref="siteSubHeader"
cmsWidgetName="SiteSubHeaderWidget" />
<div class="prevent-squish my-5"> <div class="prevent-squish my-5">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<alert <alert
id="vehicle-parts-alert"
class="rounded border-0 shadow-sm" class="rounded border-0 shadow-sm"
alertClass="alert-warning" alertClass="alert-warning"
cmsWidgetName="AlertWidget" cmsWidgetName="AlertWidget"
:isDismissible="false" :isDismissible="false" />
id="vehicle-parts-alert" />
</div> </div>
</div> </div>
</div> </div>
<div v-for="(item, i) in PartsOrQuestions" :key="i"> <div
v-for="(item, i) in PartsOrQuestions"
:key="i">
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) --> <!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
<hr v-if="i > 0" /> <hr v-if="i > 0" />
<glassPartQuestion <glassPartQuestion
@ -38,9 +45,9 @@
:alreadyPopulatedPartsData="alreadyPopulatedPartsData" /> :alreadyPopulatedPartsData="alreadyPopulatedPartsData" />
</div> </div>
<siteFooter <siteFooter
ref="siteFooter"
class="mt-5" class="mt-5"
cmsWidgetName="SiteFooterWidget" cmsWidgetName="SiteFooterWidget"
ref="siteFooter"
:isForwardActionDisabled="isForwardActionDisabled" :isForwardActionDisabled="isForwardActionDisabled"
@backClicked="navigateBack" @backClicked="navigateBack"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
@ -73,6 +80,16 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
export default { export default {
name: 'vehicle-parts', name: 'vehicle-parts',
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
glassPartQuestion,
siteHeader,
vehicleBanner,
siteSubHeader,
siteFooter,
alert
},
mixins: [BaseFormMixin, vehicleQuestionsMixin], mixins: [BaseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
@ -96,8 +113,7 @@ export default {
vm.$refs[c][0].initializeComponent({ vm.$refs[c][0].initializeComponent({
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget, ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget
}) }));
);
}); });
}, },
data() { data() {
@ -109,12 +125,12 @@ export default {
}, },
computed: { computed: {
isForwardActionDisabled() { isForwardActionDisabled() {
return this.selectedGlassPartNumbers?.length !== this.PartsFromApi.partsOrQuestions?.length return this.selectedGlassPartNumbers?.length !== this.PartsFromApi.partsOrQuestions?.length;
}, },
selectedGlassPartNumbers() { selectedGlassPartNumbers() {
// Compile all selected parts from the page. // Compile all selected parts from the page.
const numberArray = []; const numberArray = [];
for (let glassPart of Object.values(this.selectedGlassParts)) { for (const glassPart of Object.values(this.selectedGlassParts)) {
if (glassPart?.partNumber) { if (glassPart?.partNumber) {
numberArray.push(glassPart.partNumber); numberArray.push(glassPart.partNumber);
} }
@ -125,25 +141,23 @@ export default {
const partsData = this.PartsFromApi; const partsData = this.PartsFromApi;
// Map API result data, to vehicle-parts data structure // Map API result data, to vehicle-parts data structure
const mappedData = partsData.partsOrQuestions?.map((g) => { const mappedData = partsData.partsOrQuestions?.map((g) => ({
return { glassName: g.glassName,
glassName: g.glassName, glassLocation: g.glassLocation,
glassLocation: g.glassLocation, colorAnswers: g.parts?.reduce((arr, p) => {
colorAnswers: g.parts?.reduce((arr, p) => { arr.push({
arr.push({ ColorAnswerText: p.color,
ColorAnswerText: p.color, FeatureAnswers: [
FeatureAnswers: [ {
{ FeatureAnswerText:
FeatureAnswerText:
p.description === '' ? p.color : p.description, p.description === '' ? p.color : p.description,
PartNumber: p.partNumber PartNumber: p.partNumber
} }
] ]
}); });
return arr; return arr;
}, []) }, [])
}; }));
});
return mappedData; return mappedData;
}, },
@ -156,26 +170,27 @@ export default {
return 'partQuestion'; return 'partQuestion';
} }
}, },
mounted() {
this.LoadInitialPartsData();
},
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
// Check if isRepair is populated and if the pageData we need is here (Parts data) // Check if isRepair is populated and if the pageData we need is here (Parts data)
return ( return (
this.mainStore.damage.isRepair != null && this.mainStore.damage.isRepair != null
this.mainStore.pageData(issPageValues.VEHICLE_PARTS) && && this.mainStore.pageData(issPageValues.VEHICLE_PARTS)
Object.keys(this.mainStore.pageData(issPageValues.VEHICLE_PARTS)).length !== 0 && Object.keys(this.mainStore.pageData(issPageValues.VEHICLE_PARTS)).length !== 0
); );
}, },
async forwardButtonAction() { async forwardButtonAction() {
const matchedParts = []; const matchedParts = [];
// Match them to the parts from the API. // Match them to the parts from the API.
for (let [key, value] of Object.entries(this.PartsFromApi.partsOrQuestions)) { for (const [key, value] of Object.entries(this.PartsFromApi.partsOrQuestions)) {
for (let [partKey, partValue] of Object.entries(value.parts)) { for (const [partKey, partValue] of Object.entries(value.parts)) {
const currentPart = this.PartsFromApi.partsOrQuestions[key].parts[partKey]; const currentPart = this.PartsFromApi.partsOrQuestions[key].parts[partKey];
const isMatched = this.selectedGlassPartNumbers.some( const isMatched = this.selectedGlassPartNumbers.some((p) => p === currentPart.partNumber);
(p) => p === currentPart.partNumber
);
if (isMatched) { if (isMatched) {
matchedParts.push({ matchedParts.push({
@ -197,39 +212,27 @@ export default {
LoadInitialPartsData() { LoadInitialPartsData() {
const partsData = this.PartsFromApi; const partsData = this.PartsFromApi;
this.alreadyPopulatedPartsData = this.alreadyPopulatedPartsData
this.mainStore.lineItems.glassParts === null = this.mainStore.lineItems.glassParts === null
? [] ? []
: this.mainStore.lineItems.glassParts; : this.mainStore.lineItems.glassParts;
partsData.partsOrQuestions.map((g) => { partsData.partsOrQuestions.map((g) => {
// If the part is already populated, use the value from the store and populate the v-model. // If the part is already populated, use the value from the store and populate the v-model.
Object.keys(this.alreadyPopulatedPartsData).forEach((key) => { Object.keys(this.alreadyPopulatedPartsData).forEach((key) => {
const partNumber = this.alreadyPopulatedPartsData[key].partNumber; const { partNumber } = this.alreadyPopulatedPartsData[key];
g.parts.forEach((p) => { g.parts.forEach((p) => {
if (p.partNumber === partNumber) { if (p.partNumber === partNumber) {
this.selectedGlassParts[g.glassLocation + '-' + g.glassName] = p; this.selectedGlassParts[`${g.glassLocation}-${g.glassName}`] = p;
} }
}); });
}); });
}); });
} }
},
mounted() {
this.LoadInitialPartsData();
},
components: {
Form,
glassPartQuestion,
siteHeader,
vehicleBanner,
siteSubHeader,
siteFooter,
alert
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
#vehicle-parts-alert { #vehicle-parts-alert {
.alert-heading { .alert-heading {
margin-top: 0.25rem !important; margin-top: 0.25rem !important;

View file

@ -1,32 +1,32 @@
<template> <template>
<dropdownQuestion <dropdownQuestion
v-model="selectedValue"
:options="values" :options="values"
disableAutoFill disableAutoFill
placeHolderText="Select an option" placeHolderText="Select an option"
v-model="selectedValue" :isDisabled="!values.length" />
:isDisabled="!values.length"
/>
</template> </template>
<script> <script>
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue'; import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question';
export default { export default {
name: 'vehicle-question', name: 'vehicle-question',
components: {
dropdownQuestion
},
props: {
modelValue: String,
updateValues: Function
},
emits: ['update:modelValue'],
data() { data() {
return { return {
values: [], values: [],
selectedIndex: null selectedIndex: null
}; };
}, },
props: {
modelValue: String,
updateValues: Function,
},
components: {
dropdownQuestion
},
computed: { computed: {
selectedValue: { selectedValue: {
get() { get() {
@ -43,7 +43,7 @@ export default {
async getNewValues() { async getNewValues() {
const results = await this.updateValues(); const results = await this.updateValues();
this.values = results?.data; this.values = results?.data;
if (this.values.length == 1) { if (this.values.length === 1) {
this.selectedValue = 0; this.selectedValue = 0;
} else { } else {
this.selectedValue = null; this.selectedValue = null;

View file

@ -1,5 +1,9 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles position-relative"> <div class="page-container-grouped-styles position-relative">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -12,61 +16,54 @@
cmsWidgetName="SiteSubHeaderWidget" cmsWidgetName="SiteSubHeaderWidget"
class="siteSubHeader" class="siteSubHeader"
justification="center" justification="center"
darkGraySubText darkGraySubText />
/>
<vehicleQuestion <vehicleQuestion
ref="vehicleYearQuestion" ref="vehicleYearQuestion"
class="mb-2 mt-4"
v-model="selectedYear" v-model="selectedYear"
class="mb-2 mt-4"
cmsWidgetName="VehicleYearQuestion" cmsWidgetName="VehicleYearQuestion"
:updateValues="updateYearValues" :updateValues="updateYearValues"
validationRules="year-required" validationRules="year-required"
inputId="yearQuestionField" inputId="yearQuestionField" />
/>
<vehicleQuestion <vehicleQuestion
ref="vehicleMakeQuestion" ref="vehicleMakeQuestion"
class="mb-2 mt-4"
v-model="selectedMake" v-model="selectedMake"
class="mb-2 mt-4"
cmsWidgetName="VehicleMakeQuestion" cmsWidgetName="VehicleMakeQuestion"
:updateValues="updateMakeValues" :updateValues="updateMakeValues"
validationRules="make-required" validationRules="make-required"
inputId="makeQuestionField" inputId="makeQuestionField" />
/>
<vehicleQuestion <vehicleQuestion
ref="vehicleModelQuestion" ref="vehicleModelQuestion"
class="mb-2 mt-4"
v-model="selectedModel" v-model="selectedModel"
class="mb-2 mt-4"
cmsWidgetName="VehicleModelQuestion" cmsWidgetName="VehicleModelQuestion"
:updateValues="updateModelValues" :updateValues="updateModelValues"
validationRules="model-required" validationRules="model-required"
inputId="modelQuestionField" inputId="modelQuestionField" />
/>
<vehicleQuestion <vehicleQuestion
ref="vehicleStyleQuestion" ref="vehicleStyleQuestion"
class="mb-2 mt-4"
v-model="selectedStyle" v-model="selectedStyle"
class="mb-2 mt-4"
cmsWidgetName="VehicleStyleQuestion" cmsWidgetName="VehicleStyleQuestion"
:updateValues="updateStyleValues" :updateValues="updateStyleValues"
validationRules="style-required" validationRules="style-required"
inputId="styleQuestionField" inputId="styleQuestionField" />
/>
<vehicleBanner <vehicleBanner
ref="banner"
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="displayGeneric" :displayGenericVehicleImage="displayGeneric"
class="mt-5 mb-3" class="mt-5 mb-3" />
ref="banner"
/>
<siteFooter <siteFooter
ref="siteFooter"
class="mt-5" class="mt-5"
cmsWidgetName="SiteFooterWidget" cmsWidgetName="SiteFooterWidget"
@ForwardClicked="forwardButtonAction"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction"
ref="siteFooter" @back-clicked="backButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -83,14 +80,14 @@ import siteHeader from '@/iss-components/site-header/site-header';
import siteFooter from '@/iss-components/site-footer/site-footer'; import siteFooter from '@/iss-components/site-footer/site-footer';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import vehicleQuestion from '@/layouts/vehicle-selection/vehicle-question/vehicle-question.vue'; import vehicleQuestion from '@/layouts/vehicle-selection/vehicle-question/vehicle-question';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
// Supporting files // Supporting files
import baseFormMixin from '@/mixins/base-form-mixin.js'; import baseFormMixin from '@/mixins/base-form-mixin.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper'; import { settleAllPromises } from '@/helpers/layout-helper';
import { Form, defineRule, useForm } from 'vee-validate'; import { Form, defineRule } from 'vee-validate';
import { required } from '@/helpers/validation-rules'; import { required } from '@/helpers/validation-rules';
import { errorMessages } from '@/constants/error-messages'; import { errorMessages } from '@/constants/error-messages';
@ -102,22 +99,16 @@ defineRule('style-required', required(errorMessages.STYLE_REQUIRED));
export default { export default {
name: 'vehicle-selection', name: 'vehicle-selection',
components: {
siteHeader,
siteSubHeader,
vehicleBanner,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form,
vehicleQuestion
},
mixins: [baseFormMixin], mixins: [baseFormMixin],
data() {
return {
selectedYear: null,
selectedMake: null,
selectedModel: null,
selectedStyle: null
};
},
props: {
cmsWidgetName:String,
validationRules:String
},
mounted() {
this.$refs['vehicleYearQuestion'].getNewValues();
},
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
@ -131,34 +122,51 @@ export default {
} }
]; ];
let resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
props: {
cmsWidgetName: String,
validationRules: String
},
data() {
return {
selectedYear: null,
selectedMake: null,
selectedModel: null,
selectedStyle: null
};
},
computed: {
displayGeneric() {
return !this.selectedStyle;
}
},
watch: { watch: {
selectedYear(value) { selectedYear(value) {
this.mainStore.updateVehicleYear(value); this.mainStore.updateVehicleYear(value);
this.$refs['vehicleMakeQuestion'].clearValues(); this.$refs.vehicleMakeQuestion.clearValues();
if (value) { if (value) {
this.$refs['vehicleMakeQuestion'].getNewValues(); this.$refs.vehicleMakeQuestion.getNewValues();
} }
}, },
selectedMake(value) { selectedMake(value) {
this.mainStore.updateVehicleMake(value); this.mainStore.updateVehicleMake(value);
this.$refs['vehicleModelQuestion'].clearValues(); this.$refs.vehicleModelQuestion.clearValues();
if (value) { if (value) {
this.$refs['vehicleModelQuestion'].getNewValues(); this.$refs.vehicleModelQuestion.getNewValues();
} }
}, },
selectedModel(value) { selectedModel(value) {
this.mainStore.updateVehicleModel(value); this.mainStore.updateVehicleModel(value);
this.$refs['vehicleStyleQuestion'].clearValues(); this.$refs.vehicleStyleQuestion.clearValues();
if (value) { if (value) {
this.$refs['vehicleStyleQuestion'].getNewValues(); this.$refs.vehicleStyleQuestion.getNewValues();
} }
}, },
selectedStyle(value) { selectedStyle(value) {
@ -166,6 +174,9 @@ export default {
this.mainStore.setVehicle(); this.mainStore.setVehicle();
} }
}, },
mounted() {
this.$refs.vehicleYearQuestion.getNewValues();
},
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return true; return true;
@ -179,10 +190,8 @@ export default {
navigateForward() { navigateForward() {
this.mainStore.setVehicle().then(() => { this.mainStore.setVehicle().then(() => {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
this.navigationScenarios.CLICKED_FORWARD, this.$route);
this.$route
);
}); });
}, },
async updateYearValues() { async updateYearValues() {
@ -197,24 +206,11 @@ export default {
async updateStyleValues() { async updateStyleValues() {
return await this.mainStore.getVehicleStyles(); return await this.mainStore.getVehicleStyles();
} }
},
computed: {
displayGeneric() {
return !this.selectedStyle;
}
},
components: {
siteHeader,
siteSubHeader,
vehicleBanner,
siteFooter,
Form,
vehicleQuestion
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.select-car-form { .select-car-form {
margin-left: .75rem; margin-left: .75rem;
margin-right: .75rem; margin-right: .75rem;

View file

@ -4,8 +4,8 @@ import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { issPageValues } from '@/router/router-constants/issPage-values'; import { issPageValues } from '@/router/router-constants/issPage-values';
import { queryStrings } from '@/constants/query-strings'; import { queryStrings } from '@/constants/query-strings';
import { GaActions } from "@/constants/analytics"; import { GaActions } from '@/constants/analytics';
import VinLocationInformationComponent from './vin-location-information.vue'; import VinLocationInformationComponent from '@/layouts/vin-lookup/vin-location-information/vin-location-information';
const mockText = Object.freeze({ const mockText = Object.freeze({
HEADER: 'Mock Header', HEADER: 'Mock Header',

View file

@ -3,35 +3,30 @@
<div <div
class="vin-toggle" class="vin-toggle"
:class="[isVinLocationDetailVisible ? 'active' : '']" :class="[isVinLocationDetailVisible ? 'active' : '']"
@click="handleClickToggle" @click="handleClickToggle">
>
<textLink <textLink
linkType="text" linkType="text"
href="#!" href="#!"
:text="textForToggle" :text="textForToggle" />
/>
</div> </div>
<Transition> <Transition>
<div <div
v-show="isVinLocationDetailVisible" v-show="isVinLocationDetailVisible"
id="vin-location-detail-wrapper" id="vin-location-detail-wrapper"
class="mt-2" class="mt-2">
>
<div <div
v-html="vinLocationDetailContent"
id="vin-location-detail-content" id="vin-location-detail-content"
class="mb-2" class="mb-2"
/> v-html="vinLocationDetailContent" />
<img <img
:src="vinLocationDetailImage" :src="vinLocationDetailImage"
alt="VIN Locations" alt="VIN Locations">
>
</div> </div>
</Transition> </Transition>
</div> </div>
</template> </template>
<script> <script>
import textLink from '@/ux-components/text-link/text-link.vue'; import textLink from '@/ux-components/text-link/text-link';
export default { export default {
name: 'vin-location-information', name: 'vin-location-information',

View file

@ -1,14 +1,13 @@
<template> <template>
<alert <alert
id="perfect-match-alert"
alertClass="alert-success" alertClass="alert-success"
cmsWidgetName="AlertPerfectMatchInsuranceNotVerified" cmsWidgetName="AlertPerfectMatchInsuranceNotVerified"
:manualCopy="body" :manualCopy="body"
id="perfect-match-alert" aria-label="perfect-match-alert" />
aria-label="perfect-match-alert"
/>
</template> </template>
<script> <script>
import alert from '@/ux-components/alert/alert.vue'; import alert from '@/ux-components/alert/alert';
import { getDamageString } from '@/helpers/damage-helper'; import { getDamageString } from '@/helpers/damage-helper';
export default { export default {

View file

@ -4,13 +4,12 @@
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget" cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualCopy="body" :manualCopy="body"
:manualHeadline="header" :manualHeadline="header"
aria-label="two-identical-ymm-vehicle-alert" aria-label="two-identical-ymm-vehicle-alert" />
/>
</template> </template>
<script> <script>
import { getDamageString } from '@/helpers/damage-helper'; import { getDamageString } from '@/helpers/damage-helper';
import alert from '@/ux-components/alert/alert.vue'; import alert from '@/ux-components/alert/alert';
export default { export default {
name: 'two-identical-ymm-vehicle-alert', name: 'two-identical-ymm-vehicle-alert',

View file

@ -1,13 +1,12 @@
<template> <template>
<alert <alert
id="vehicle-not-found-alert"
alertClass="alert-danger" alertClass="alert-danger"
cmsWidgetName="AlertVinNotFoundWidget" cmsWidgetName="AlertVinNotFoundWidget"
id="vehicle-not-found-alert" aria-label="vehicle-not-found-alert" />
aria-label="vehicle-not-found-alert"
/>
</template> </template>
<script> <script>
import alert from '@/ux-components/alert/alert.vue'; import alert from '@/ux-components/alert/alert';
export default { export default {
name: 'vehicle-not-found-alert', name: 'vehicle-not-found-alert',

View file

@ -4,13 +4,12 @@
cmsWidgetName="AlertMatchedDifferentVehicleWidget" cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualCopy="body" :manualCopy="body"
:manualHeadline="header" :manualHeadline="header"
aria-label="vehicle-not-matched-alert" aria-label="vehicle-not-matched-alert" />
/>
</template> </template>
<script> <script>
import { getDamageString } from '@/helpers/damage-helper'; import { getDamageString } from '@/helpers/damage-helper';
import alert from '@/ux-components/alert/alert.vue'; import alert from '@/ux-components/alert/alert';
export default { export default {
name: 'vehicle-not-matched-alert', name: 'vehicle-not-matched-alert',

View file

@ -4,7 +4,7 @@ import vehicleLookupAlertTypes from '@/constants/vehicle-lookup-alert-types';
import { RouterLinkStub } from '@vue/test-utils'; import { RouterLinkStub } from '@vue/test-utils';
import { render } from '@testing-library/vue'; import { render } from '@testing-library/vue';
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import VinLookupAlertsComponent from './vin-lookup-alerts.vue'; import VinLookupAlertsComponent from '@/layouts/vin-lookup/vin-lookup-alerts/vin-lookup-alerts';
jest.mock('@/helpers/damage-helper'); jest.mock('@/helpers/damage-helper');
@ -18,7 +18,7 @@ const mountOptions = {
methods: { methods: {
getCmsContent: jest.fn(() => ''), getCmsContent: jest.fn(() => ''),
getFooterInfoBoxHeight: jest.fn(() => 80), getFooterInfoBoxHeight: jest.fn(() => 80),
cssClassNameForCmsWidget: jest.fn(() => 'widget-name-mock-class'), cssClassNameForCmsWidget: jest.fn(() => 'widget-name-mock-class')
} }
} }
] ]

View file

@ -1,28 +1,24 @@
<template> <template>
<div <div
id="vin-lookup-alerts-wrapper" id="vin-lookup-alerts-wrapper"
:class="wrapperCssClass" :class="wrapperCssClass">
>
<vehicleNotFoundAlert <vehicleNotFoundAlert
v-if="isVehicleNotFoundVisible" v-if="isVehicleNotFoundVisible" />
/>
<twoIdenticalYMMVehicleAlert <twoIdenticalYMMVehicleAlert
v-if="isTwoIdenticalYMMVehicleFoundVisible" v-if="isTwoIdenticalYMMVehicleFoundVisible" />
/>
<vehicleNotMatchedAlert <vehicleNotMatchedAlert
v-else-if="isVehicleNotMatchedVisible" v-else-if="isVehicleNotMatchedVisible" />
/>
<perfectMatchAlert <perfectMatchAlert
v-else-if="isPerfectMatchVisible" v-else-if="isPerfectMatchVisible" />
/>
</div> </div>
</template> </template>
<script> <script>
import vehicleLookupAlertTypes from '@/constants/vehicle-lookup-alert-types'; import vehicleLookupAlertTypes from '@/constants/vehicle-lookup-alert-types';
import vehicleNotFoundAlert from '@/layouts/vin-lookup/vin-lookup-alerts/vehicle-not-found-alert/vehicle-not-found-alert'; import vehicleNotFoundAlert from '@/layouts/vin-lookup/vin-lookup-alerts/vehicle-not-found-alert/vehicle-not-found-alert';
import vehicleNotMatchedAlert from '@/layouts/vin-lookup/vin-lookup-alerts/vehicle-not-matched-alert/vehicle-not-matched-alert'; import vehicleNotMatchedAlert from '@/layouts/vin-lookup/vin-lookup-alerts/vehicle-not-matched-alert/vehicle-not-matched-alert';
import twoIdenticalYMMVehicleAlert from '@/layouts/vin-lookup/vin-lookup-alerts/two-identical-ymm-vehicle-alert/two-identical-ymm-vehicle-alert' import twoIdenticalYMMVehicleAlert from
import perfectMatchAlert from '@/layouts/vin-lookup/vin-lookup-alerts//perfect-match-alert/perfect-match-alert.vue'; '@/layouts/vin-lookup/vin-lookup-alerts/two-identical-ymm-vehicle-alert/two-identical-ymm-vehicle-alert';
import perfectMatchAlert from '@/layouts/vin-lookup/vin-lookup-alerts//perfect-match-alert/perfect-match-alert';
export default { export default {
name: 'vin-lookup-alerts', name: 'vin-lookup-alerts',

View file

@ -11,7 +11,7 @@ import { GaActions } from '@/constants/analytics';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
import { routerParams } from '@/router/router-params'; import { routerParams } from '@/router/router-params';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import VinLookupComponent from './vin-lookup.vue'; import VinLookupComponent from '@/layouts/vin-lookup/vin-lookup';
const continueButtonQuerySelector = '[data-test-id="site-footer-main-button"]'; const continueButtonQuerySelector = '[data-test-id="site-footer-main-button"]';
const errorMessageWrapperElSelector = '#vin-question-wrapper .form-test-error'; const errorMessageWrapperElSelector = '#vin-question-wrapper .form-test-error';

View file

@ -1,5 +1,9 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -8,12 +12,27 @@
<div class="row px-3"> <div class="row px-3">
<div class="col"> <div class="col">
<div class="select-car-form rounded"> <div class="select-car-form rounded">
<vehicleBanner class="mt-2 mb-4" cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" /> <vehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" /> <siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<vinLookupAlerts class="mt-5" :activeAlertType="activeVehicleLookupAlertType" /> <vinLookupAlerts
<vinQuestion v-model="vin" :mask="vinMask" :isDisabled="vinPopulatedOnPageLoad" textPosition="left" /> class="mt-5"
:activeAlertType="activeVehicleLookupAlertType" />
<vinQuestion
v-model="vin"
:mask="vinMask"
:isDisabled="vinPopulatedOnPageLoad"
textPosition="left" />
<vinLocationInformation /> <vinLocationInformation />
<siteFooter cmsWidgetName="SiteFooterWidget" :isForwardActionDisabled="!meta.valid" @backClicked="backButtonAction" @forwardClicked="forwardButtonAction" ref="siteFooter" class="mt-5" /> <siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
class="mt-5"
@backClicked="backButtonAction"
@forwardClicked="forwardButtonAction" />
</div> </div>
</div> </div>
</div> </div>
@ -37,13 +56,13 @@ import { useMainStore } from '@/store';
import baseFormMixin from '@/mixins/base-form-mixin'; import baseFormMixin from '@/mixins/base-form-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer';
import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteHeader from '@/iss-components/site-header/site-header';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import vinLocationInformation from './vin-location-information/vin-location-information.vue'; import vinLocationInformation from '@/layouts/vin-lookup/vin-location-information/vin-location-information';
import vinLookupAlerts from './vin-lookup-alerts/vin-lookup-alerts.vue'; import vinLookupAlerts from '@/layouts/vin-lookup/vin-lookup-alerts/vin-lookup-alerts';
import vinQuestion from './vin-question/vin-question.vue'; import vinQuestion from '@/layouts/vin-lookup/vin-question/vin-question';
export default { export default {
name: 'vin-lookup', name: 'vin-lookup',
@ -51,6 +70,7 @@ export default {
siteFooter, siteFooter,
siteHeader, siteHeader,
siteSubHeader, siteSubHeader,
// eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
vehicleBanner, vehicleBanner,
vinLocationInformation, vinLocationInformation,
@ -60,23 +80,7 @@ export default {
mixins: [baseFormMixin, vehicleQuestionsMixin], mixins: [baseFormMixin, vehicleQuestionsMixin],
provide() { provide() {
return { return {
vehicleFromLookup: computed(() => this.vehicleFromLookup), vehicleFromLookup: computed(() => this.vehicleFromLookup)
};
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
return {
activeVehicleLookupAlertType: null,
needToLookupVehicle: true,
vehicleFromLookup: null,
vin: this.getVinFromStore(),
forwardButtonCarStyle: '',
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
bailout: false
}; };
}, },
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -96,6 +100,22 @@ export default {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
return {
activeVehicleLookupAlertType: null,
needToLookupVehicle: true,
vehicleFromLookup: null,
vin: this.getVinFromStore(),
forwardButtonCarStyle: '',
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
bailout: false
};
},
computed: { computed: {
isCarIdDifferentFromTheStore() { isCarIdDifferentFromTheStore() {
return ( return (
@ -105,18 +125,19 @@ export default {
}, },
isTwoIdenticalYMMVehicleFound() { isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`; const vinYmmFound = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`; const vinYmmExpected
return(vinYmmFound.toLowerCase()==vinYmmExpected.toLowerCase()); = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return (vinYmmFound.toLowerCase() === vinYmmExpected.toLowerCase());
}, },
vinMask() { vinMask() {
if (this.vinPopulatedOnPageLoad) { if (this.vinPopulatedOnPageLoad) {
// TODO: Modify to remove side effects in computed
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.PERFECT_MATCH; this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.PERFECT_MATCH;
this.needToLookupVehicle = false; this.needToLookupVehicle = false;
const lastSixChars = this.vin.substring(11, this.vin.length); const lastSixChars = this.vin.substring(11, this.vin.length);
return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`; return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`;
} else {
return 'XXXXXXXXXXXXXXXXX';
} }
return 'XXXXXXXXXXXXXXXXX';
} }
}, },
watch: { watch: {
@ -124,9 +145,7 @@ export default {
this.resetActiveAlert(); this.resetActiveAlert();
this.$refs.siteFooter.enableForwardAction(); this.$refs.siteFooter.enableForwardAction();
this.needToLookupVehicle = true; this.needToLookupVehicle = true;
this.$refs.siteFooter.updateButtonText( this.$refs.siteFooter.updateButtonText(this.getCmsContent('SiteFooterWidget', 'ForwardButtonText'));
this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')
);
} }
}, },
methods: { methods: {
@ -163,8 +182,7 @@ export default {
} }
if (this.bailout) { if (this.bailout) {
return this.$router.navigate( return this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route); this.$route);
} }
// Add vin bcs the response from the service doesn't contain vin // Add vin bcs the response from the service doesn't contain vin
@ -179,13 +197,14 @@ export default {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_MATCHED; this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_MATCHED;
} }
const vehicleYearMakeModelStyle = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model} ${this.forwardButtonCarStyle}`; const vehicleYearMakeModelStyle
= `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model} ${this.forwardButtonCarStyle}`;
this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleYearMakeModelStyle}`); this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleYearMakeModelStyle}`);
this.$refs.siteFooter.removeLoader(); this.$refs.siteFooter.removeLoader();
this.needToLookupVehicle = false; this.needToLookupVehicle = false;
return; return null;
} }
let isSelectedGlassAvailableForVehicle = true; let isSelectedGlassAvailableForVehicle = true;
@ -198,12 +217,10 @@ export default {
if (this.isCarIdDifferentFromTheStore && !isSelectedGlassAvailableForVehicle) { if (this.isCarIdDifferentFromTheStore && !isSelectedGlassAvailableForVehicle) {
this.mainStore.updateVehicle(this.vehicleFromLookup); this.mainStore.updateVehicle(this.vehicleFromLookup);
this.mainStore.resetDamageState(); this.mainStore.resetDamageState();
this.$router.navigate( this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route, this.$route,
{}, {},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true } { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
);
// navigate() doesn't stop the processing flow // navigate() doesn't stop the processing flow
return; return;
@ -219,7 +236,7 @@ export default {
// To Do: Need requirement on what to do here // To Do: Need requirement on what to do here
console.error('Error on retrieving PartsOrQuestions'); console.error('Error on retrieving PartsOrQuestions');
this.$refs.siteFooter.removeLoader(); this.$refs.siteFooter.removeLoader();
return; return null;
} }
// Comes from vehicleQuestionsMixin.navigateForward() // Comes from vehicleQuestionsMixin.navigateForward()
@ -233,20 +250,19 @@ export default {
} catch (responseError) { } catch (responseError) {
return { return {
error: { error: {
status: responseError.status, status: responseError.status
}, }
}; };
} }
}, },
async lookupVehicleByVin(vin) { async lookupVehicleByVin(vin) {
try { try {
return await this.mainStore.lookupVehicleByVin(vin); return await this.mainStore.lookupVehicleByVin(vin);
} } catch (responseError) {
catch (responseError) {
return { return {
error: { error: {
status: responseError.status, status: responseError.status
}, }
}; };
} }
}, },
@ -256,7 +272,7 @@ export default {
resetVehicleFromLookup() { resetVehicleFromLookup() {
this.vehicleFromLookup = null; this.vehicleFromLookup = null;
}, },
resetDependentState() {}, resetDependentState() {}
}, }
}; };
</script> </script>

View file

@ -1,14 +1,13 @@
<template> <template>
<textboxQuestion <textboxQuestion
id="vin-question-wrapper"
v-model="vin" v-model="vin"
cmsWidgetName="VinNumberQuestionWidget" cmsWidgetName="VinNumberQuestionWidget"
disableAutoFill disableAutoFill
id="vin-question-wrapper"
inputId="vin-question" inputId="vin-question"
isRequired isRequired
maxLength="17" maxLength="17"
validationRules="vin-required|vin-format" validationRules="vin-required|vin-format" />
/>
</template> </template>
<script> <script>
// Import Other Supporting File(s) // Import Other Supporting File(s)
@ -17,20 +16,20 @@ import { regex, required } from '@/helpers/validation-rules';
import { errorMessages } from '@/constants/error-messages'; import { errorMessages } from '@/constants/error-messages';
// Import Component(s) // Import Component(s)
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue'; import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
defineRule('vin-required', required(errorMessages.VIN_REQUIRED)); defineRule('vin-required', required(errorMessages.VIN_REQUIRED));
defineRule('vin-format', regex(/^[a-hA-Hj-nJ-NpPr-zR-Z0-9]{17}$/, errorMessages.VIN_FORMAT)); defineRule('vin-format', regex(/^[a-hA-Hj-nJ-NpPr-zR-Z0-9]{17}$/, errorMessages.VIN_FORMAT));
export default { export default {
name: 'vin-question', name: 'vin-question',
components: {
textboxQuestion
},
props: { props: {
modelValue: String modelValue: String
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
components: {
textboxQuestion
},
computed: { computed: {
vin: { vin: {
get() { get() {

View file

@ -1,4 +1,4 @@
import welcomePage from '@/layouts/welcome-page/welcome-page.vue'; import welcomePage from '@/layouts/welcome-page/welcome-page';
// Supporting files // Supporting files
// Supporting files // Supporting files
@ -110,13 +110,11 @@ describe('navigation', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES,
navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES,
undefined, undefined,
{}, {},
{}, {},
mockvehicles mockvehicles);
);
}); });
test('if policy is found, but no vehicles, navigate to vehicle-selection page', async () => { test('if policy is found, but no vehicles, navigate to vehicle-selection page', async () => {
// Arrange // Arrange
@ -138,10 +136,8 @@ describe('navigation', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES,
navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES, undefined);
undefined
);
}); });
test('if policy is not found, navigate to policy-holder-details page', async () => { test('if policy is not found, navigate to policy-holder-details page', async () => {
// Arrange // Arrange
@ -158,10 +154,8 @@ describe('navigation', () => {
await wrapper.vm.navigateForward(); await wrapper.vm.navigateForward();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED,
navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED, undefined);
undefined
);
}); });
}); });
@ -172,31 +166,27 @@ function setupMocks({
policies = [], policies = [],
getCoveragePolicyInfoResponse getCoveragePolicyInfoResponse
}) { }) {
useMainStore().getCoveragePolicyInfo = jest.fn().mockImplementation(() => { useMainStore().getCoveragePolicyInfo = jest.fn().mockImplementation(() => Promise.resolve({
return Promise.resolve({ data: getCoveragePolicyInfoResponse || {
data: getCoveragePolicyInfoResponse policies: [
? getCoveragePolicyInfoResponse {
: { insureds: [
policies: [
{ {
insureds: [ customerId: 'TEST_ID'
{ }
customerId: 'TEST_ID' ],
} vehicles: [
], {
vehicles: [ vin: 'TEST_VIN'
{ },
vin: 'TEST_VIN' {
}, vin: 'TEST_VIN2'
{
vin: 'TEST_VIN2'
}
]
} }
] ]
} }
}); ]
}); }
}));
// Mock api responses // Mock api responses
const apiResponses = { const apiResponses = {
@ -213,7 +203,7 @@ function setupMocks({
SiteFooterWidget: pageFooterWidgetCmsContent SiteFooterWidget: pageFooterWidgetCmsContent
}, },
policyLookupResponse: { policyLookupResponse: {
policies: policies policies
} }
}; };

View file

@ -1,79 +1,88 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles welcome"> <div class="page-container-grouped-styles welcome">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" class="mt-4"/> <siteSubHeader
cmsWidgetName="SiteSubHeaderWidget"
class="mt-4" />
<div class="container-fluid px-5"> <div class="container-fluid px-5">
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
ref="policyNumber"
v-model="welcomePageModel.policyNumber"
inputId="policyNumberField" inputId="policyNumberField"
cmsWidgetName="PolicyNumberQuestion" cmsWidgetName="PolicyNumberQuestion"
v-model="welcomePageModel.policyNumber"
isRequired isRequired
ref="policyNumber"
disableAutoFill disableAutoFill
:isDisabled="isPolicyHolderEnabled" :isDisabled="isPolicyHolderEnabled"
:validationRules="rules.policyNumber" /> :validationRules="rules.policyNumber" />
</div> </div>
</div> </div>
<div class="row mt-4" v-if="this.isCoverageEnabled"> <div
v-if="isCoverageEnabled"
class="row mt-4">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
ref="policyZip"
v-model="welcomePageModel.policyZipCode"
inputId="policyZipCode" inputId="policyZipCode"
cmsWidgetName="PolicyZipQuestion" cmsWidgetName="PolicyZipQuestion"
v-model="welcomePageModel.policyZipCode"
isRequired isRequired
ref="policyZip"
:validationRules="rules.policyZip" /> :validationRules="rules.policyZip" />
</div> </div>
</div> </div>
<div class="row mt-4"> <div class="row mt-4">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
ref="dateOfLoss"
v-model="welcomePageModel.dateOfLoss"
type="date" type="date"
cmsWidgetName="DateOfLossQuestion" cmsWidgetName="DateOfLossQuestion"
v-model="welcomePageModel.dateOfLoss"
inputId="dateOfLossField" inputId="dateOfLossField"
isRequired isRequired
ref="dateOfLoss"
disableAutoFill disableAutoFill
:max="new Date().toJSON().slice(0,10)" :max="new Date().toJSON().slice(0,10)"
:min="'1972-12-01'" :min="'1972-12-01'"
:validationRules="rules.lossDate" :validationRules="rules.lossDate" />
/>
</div> </div>
</div> </div>
<div class="row px-4"> <div class="row px-4">
<div class="col px-0"> <div class="col px-0">
<textBlock cmsWidgetName="DamageDateEstimateWidget" typeStyle="small" class="mt-2" /> <textBlock
cmsWidgetName="DamageDateEstimateWidget"
typeStyle="small"
class="mt-2" />
</div> </div>
</div> </div>
<div class="row mt-4"> <div class="row mt-4">
<div class="col"> <div class="col">
<dropdownQuestion <dropdownQuestion
cmsWidgetName="DamageCauseQuestion" id="welcomeDropdown"
v-model="welcomePageModel.damageCause"
ref="damageCause" ref="damageCause"
v-model="welcomePageModel.damageCause"
cmsWidgetName="DamageCauseQuestion"
inputId="damageCauseQuestionField" inputId="damageCauseQuestionField"
:options="DamageCauseOptions" :options="DamageCauseOptions"
disableAutoFill disableAutoFill
:validationRules="rules.damageOption" :validationRules="rules.damageOption"
placeHolderText="Select an option" placeHolderText="Select an option" />
id="welcomeDropdown"
/>
</div> </div>
</div> </div>
<div class="row mt-4 "> <div class="row mt-4 ">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
ref="phoneNumber"
v-model="welcomePageModel.phoneNumber"
inputId="phoneNumberField" inputId="phoneNumberField"
cmsWidgetName="PhoneNumberQuestion" cmsWidgetName="PhoneNumberQuestion"
v-model="welcomePageModel.phoneNumber"
:validationRules="rules.phoneNumber" :validationRules="rules.phoneNumber"
isRequired isRequired
ref="phoneNumber"
mask="###-###-####" mask="###-###-####"
disableAutoFill /> disableAutoFill />
</div> </div>
@ -81,10 +90,10 @@
<div class="row mt-4"> <div class="row mt-4">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
ref="email"
v-model="welcomePageModel.email"
inputId="emailField" inputId="emailField"
cmsWidgetName="EmailAddressQuestion" cmsWidgetName="EmailAddressQuestion"
v-model="welcomePageModel.email"
ref="email"
:validationRules="rules.email" :validationRules="rules.email"
isRequired isRequired
disableAutoFill /> disableAutoFill />
@ -93,40 +102,41 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
v-if="displayDamageCityQuestion"
ref="damageCity"
v-model="welcomePageModel.damageCity"
class="mt-4" class="mt-4"
inputId="damageCityField" inputId="damageCityField"
cmsWidgetName="DamageCityQuestion" cmsWidgetName="DamageCityQuestion"
v-model="welcomePageModel.damageCity"
isRequired isRequired
ref="damageCity"
disableAutoFill disableAutoFill
v-if="this.displayDamageCityQuestion"
:validationRules="rules.lossCity" /> :validationRules="rules.lossCity" />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<dropdownQuestion <dropdownQuestion
v-if="displayDamageStateQuestion"
id="welcomeDropdown"
ref="state"
v-model="welcomePageModel.damageState"
class="mt-4" class="mt-4"
cmsWidgetName="DamageStateQuestion" cmsWidgetName="DamageStateQuestion"
v-model="welcomePageModel.damageState"
ref="state"
inputId="8fdf9dc2e13e430eb57529499dceb3eb" inputId="8fdf9dc2e13e430eb57529499dceb3eb"
:options="getStates" :options="getStates"
:validationRules="rules.lossState" :validationRules="rules.lossState"
isRequired isRequired
disableAutoFill disableAutoFill
v-if="this.displayDamageStateQuestion" placeHolderText="Select an option" />
placeHolderText="Select an option"
id="welcomeDropdown"
/>
</div> </div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<buttonQuestion <buttonQuestion
v-if="displayGlassOnlyQuestion"
ref="glassOnlyDamage"
v-model="welcomePageModel.isDamageGlassOnly"
class="px-0 mt-4" class="px-0 mt-4"
cmsWidgetName="GlassOnlyQuestion" cmsWidgetName="GlassOnlyQuestion"
v-model="welcomePageModel.isDamageGlassOnly"
inputId="isDamageGlassOnly" inputId="isDamageGlassOnly"
:answers="DamageGlassOnlyOptions" :answers="DamageGlassOnlyOptions"
:questionText="DamageGlassOnlyQuestion" :questionText="DamageGlassOnlyQuestion"
@ -134,11 +144,11 @@
:validationRules="rules.damageOption" :validationRules="rules.damageOption"
isRequired isRequired
isSmallQuestionLabelText isSmallQuestionLabelText
ref="glassOnlyDamage" disableAutoFill />
v-if="this.displayGlassOnlyQuestion"
disableAutoFill/>
</div> </div>
<div class="row position-sticky top-100" id="welcomeFooter"> <div
id="welcomeFooter"
class="row position-sticky top-100">
<div class="col"> <div class="col">
<siteFooter <siteFooter
class="mt-3" class="mt-3"
@ -172,7 +182,7 @@ import { required, regex } from '@/helpers/validation-rules';
import { errorMessages } from '@/constants/error-messages'; import { errorMessages } from '@/constants/error-messages';
import BaseFormMixin from '@/mixins/base-form-mixin.js'; import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { states } from '@/constants/states' import { states } from '@/constants/states';
import globalRules from '@/constants/global-rules'; import globalRules from '@/constants/global-rules';
// define validation rules // define validation rules
@ -186,7 +196,45 @@ defineRule('policy-zip-format', regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages
export default { export default {
name: 'welcome-page', name: 'welcome-page',
components: {
siteHeader,
siteSubHeader,
buttonQuestion,
textboxQuestion,
dropdownQuestion,
siteFooter,
textBlock,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
}
];
// use resultMap to populate layout content.
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
setup() {
const mainStore = useMainStore();
// Set order account number from the issConfig.
mainStore.order.accountNumber = mainStore.issConfig.accountNumber;
return { mainStore };
},
data() { data() {
return { return {
welcomePageModel: this.getWelcomePageModelFromStore(), welcomePageModel: this.getWelcomePageModelFromStore(),
@ -203,32 +251,43 @@ export default {
} }
}; };
}, },
setup() { computed: {
const mainStore = useMainStore(); DamageCauseOptions() {
const damageCauseAnswers = this.getCmsContent('DamageCauseQuestion', 'Answers');
// Set order account number from the issConfig. const damageCauseAnswersObj = {};
mainStore.order.accountNumber = mainStore.issConfig.accountNumber; if (damageCauseAnswers) {
for (const answer of Object.values(damageCauseAnswers)) {
return { mainStore }; if (answer?.Name) {
}, damageCauseAnswersObj[answer.Name] = answer.Name;
async beforeRouteEnter(to, from, next) { }
// Call APIs }
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
} }
]; return damageCauseAnswersObj;
},
// use resultMap to populate layout content. DamageGlassOnlyOptions() {
let resultMap = await settleAllPromises(promiseResultMap); return this.getCmsContent('GlassOnlyQuestion', 'Answers');
},
next((vm) => { DamageGlassOnlyQuestion() {
vm.setCmsContent(resultMap.cmsContent); return this.getCmsContent('GlassOnlyQuestion', 'QuestionText');
}); },
displayDamageCityQuestion() {
return !!this.getCmsContent('DamageCityQuestion', 'QuestionText');
},
displayDamageStateQuestion() {
return !!this.getCmsContent('DamageStateQuestion', 'QuestionText');
},
displayGlassOnlyQuestion() {
return !!this.getCmsContent('GlassOnlyQuestion', 'QuestionText');
},
getStates() {
return states;
},
isPolicyHolderEnabled() {
return !!this.mainStore.issConfig.disabledFields.policyNumber;
},
isCoverageEnabled() {
return this.mainStore.issConfig.isCoverageEnabled;
}
}, },
methods: methods:
{ {
@ -282,27 +341,21 @@ export default {
if (policy) { if (policy) {
if (this.vehiclesFound) { if (this.vehiclesFound) {
// if policy lookup is successful and vehicles are found, navigate to policy-vehicles page // if policy lookup is successful and vehicles are found, navigate to policy-vehicles page
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES,
this.navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES,
this.$route, this.$route,
{}, {},
{}, {},
this.vehiclesFound this.vehiclesFound);
);
} else { } else {
// if policy lookup is successful, but no vehicles are associated with the policy // if policy lookup is successful, but no vehicles are associated with the policy
// navigate to vehicle-selection page (manual entry) // navigate to vehicle-selection page (manual entry)
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES,
this.navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES, this.$route);
this.$route
);
} }
} else { } else {
// if policy lookup is unsuccessful, navigate to policy-holder-details page // if policy lookup is unsuccessful, navigate to policy-holder-details page
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED,
this.navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED, this.$route);
this.$route
);
} }
}, },
@ -318,58 +371,10 @@ export default {
phoneNumber: this.mainStore.order.customer.phoneNumber, phoneNumber: this.mainStore.order.customer.phoneNumber,
email: this.mainStore.order.customer.emailAddress, email: this.mainStore.order.customer.emailAddress,
isPolicyNumberDisabled: this.mainStore.order.policy.isPolicyNumberDisabled isPolicyNumberDisabled: this.mainStore.order.policy.isPolicyNumberDisabled
} };
} }
},
computed:{
DamageCauseOptions() {
const damageCauseAnswers = this.getCmsContent('DamageCauseQuestion', 'Answers');
const damageCauseAnswersObj = {};
if (damageCauseAnswers) {
for (let answer of Object.values(damageCauseAnswers)) {
if (answer?.Name) {
damageCauseAnswersObj[answer.Name] = answer.Name;
}
}
}
return damageCauseAnswersObj;
},
DamageGlassOnlyOptions() {
return this.getCmsContent('GlassOnlyQuestion', 'Answers');
},
DamageGlassOnlyQuestion() {
return this.getCmsContent('GlassOnlyQuestion', 'QuestionText');
},
displayDamageCityQuestion() {
return !!this.getCmsContent('DamageCityQuestion', 'QuestionText');
},
displayDamageStateQuestion() {
return !!this.getCmsContent('DamageStateQuestion', 'QuestionText');
},
displayGlassOnlyQuestion() {
return !!this.getCmsContent('GlassOnlyQuestion', 'QuestionText');
},
getStates() {
return states;
},
isPolicyHolderEnabled() {
return !!this.mainStore.issConfig.disabledFields.policyNumber;
},
isCoverageEnabled() {
return this.mainStore.issConfig.isCoverageEnabled;
}
},
components: {
siteHeader,
siteSubHeader,
buttonQuestion,
textboxQuestion,
dropdownQuestion,
siteFooter,
textBlock,
Form,
} }
} };
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -1,6 +1,6 @@
import { createWebHistory, createRouter } from 'vue-router'; import { createWebHistory, createRouter } from 'vue-router';
import { lazyLoadComponent } from '@/router/dynamic-routing/component-loader'; import { lazyLoadComponent } from '@/router/dynamic-routing/component-loader';
import {issPageValues} from '@/router/router-constants/issPage-values'; import { issPageValues } from '@/router/router-constants/issPage-values';
import { routingTable } from '@/router/router-constants/routing-table'; import { routingTable } from '@/router/router-constants/routing-table';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import eventBus from '@/helpers/event-bus/event-bus'; import eventBus from '@/helpers/event-bus/event-bus';
@ -25,7 +25,7 @@ const routes = [
to.query.issPage = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage; to.query.issPage = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
// Do not run these for the main entry page - as it is not part of the user flow. // Do not run these for the main entry page - as it is not part of the user flow.
if (to.query.issPage != issPageValues.ENTRY_PAG ) { if (to.query.issPage !== issPageValues.ENTRY_PAG) {
if (analyticsMixin.methods.noSession()) { if (analyticsMixin.methods.noSession()) {
await analyticsMixin.methods.initSession(); await analyticsMixin.methods.initSession();
} else { } else {
@ -45,7 +45,7 @@ const routes = [
const routeData = await GetRouteInfoFromPageName(to.query.issPage); const routeData = await GetRouteInfoFromPageName(to.query.issPage);
if (routeData[0].name.toLowerCase() === 'error') { if (routeData[0].name.toLowerCase() === 'error') {
throw('Page not found!'); throw new Error('Page not found!');
} }
// Add our dynamic route. // Add our dynamic route.
@ -74,7 +74,7 @@ const router = createRouter({
routes, routes,
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
// always scroll to top // always scroll to top
return { top: 0 } return { top: 0 };
} }
}); });
@ -84,7 +84,7 @@ router.afterEach((to, from) => { /*eslint-disable-line*/
// Update lastPageVisited in the store // Update lastPageVisited in the store
store.updateLastPageVisited(to.name); store.updateLastPageVisited(to.name);
if (to.query.issPage != issPageValues.ENTRY_PAGE) { if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
// Push page view to GA // Push page view to GA
analyticsMixin.methods.pushPageViewToGA(); analyticsMixin.methods.pushPageViewToGA();
@ -99,7 +99,7 @@ async function GetRouteInfoFromPageName(pageName) {
const response = await useMainStore().getRouteInfo(pageName); const response = await useMainStore().getRouteInfo(pageName);
const jsonFromResponse = JSON.parse(response.data.Result); const jsonFromResponse = JSON.parse(response.data.Result);
let routeData = []; const routeData = [];
// Add our route data and return our array. // Add our route data and return our array.
Object.keys(jsonFromResponse).forEach((key) => { Object.keys(jsonFromResponse).forEach((key) => {
@ -114,29 +114,25 @@ async function GetRouteInfoFromPageName(pageName) {
} }
// Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example. // Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example.
router.overrideNavigation = ( router.overrideNavigation = (scenario,
scenario,
currentRoute, currentRoute,
next, next,
isSavingNavigation, isSavingNavigation,
optionalQuery = {}, optionalQuery = {},
optionalParams = {}, optionalParams = {},
optionalPageData optionalPageData) => {
) => { navigate(scenario,
navigate(
scenario,
currentRoute, currentRoute,
isSavingNavigation, isSavingNavigation,
optionalQuery, optionalQuery,
optionalParams, optionalParams,
optionalPageData optionalPageData);
);
next(); next();
}; };
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData); navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
} };
// Navigate to the next route, depending on the scenario. // Navigate to the next route, depending on the scenario.
function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) { /*eslint-disable-line*/ function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) { /*eslint-disable-line*/
@ -156,14 +152,13 @@ function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams =
if (matchingScenarioMap.destinationIssPageValue) { if (matchingScenarioMap.destinationIssPageValue) {
// Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object // Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
const existingPageDataForPage = useMainStore().pageData(matchingScenarioMap.destinationIssPageValue); const existingPageDataForPage = useMainStore().pageData(matchingScenarioMap.destinationIssPageValue);
baseMixin.methods.savePageDataToStore( baseMixin.methods.savePageDataToStore(matchingScenarioMap.destinationIssPageValue,
matchingScenarioMap.destinationIssPageValue,
Object.keys(optionalPageData).length > 0 Object.keys(optionalPageData).length > 0
? optionalPageData ? optionalPageData
: existingPageDataForPage ?? {} : existingPageDataForPage ?? {});
);
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our issPage one. // We're always pushing the same path, just changing query strings.
// Make sure our optional query strings get combined with our issPage one.
router.push({ router.push({
name: 'root', name: 'root',
query: Object.assign(optionalQuery, { query: Object.assign(optionalQuery, {
@ -174,7 +169,7 @@ function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams =
} else if (matchingScenarioMap.destinationUrl) { } else if (matchingScenarioMap.destinationUrl) {
navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery); navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery);
} }
}; }
// Navigate to an external url. // Navigate to an external url.
function navigateToUrl(url, optionalQuery = {}) { function navigateToUrl(url, optionalQuery = {}) {
@ -185,28 +180,25 @@ function navigateToUrl(url, optionalQuery = {}) {
} }
window.location.assign(externalUrl); window.location.assign(externalUrl);
}; }
// Get navigation map depending on the scenario and the current 'page' you're on. // Get navigation map depending on the scenario and the current 'page' you're on.
function getNavigationMap (scenario, currentRoute) { function getNavigationMap(scenario, currentRoute) {
const issPageValue = currentRoute.query.issPage; const issPageValue = currentRoute.query.issPage;
try { try {
const matchedQueryValue = routingTable(useMainStore()) const matchedQueryValue = routingTable(useMainStore())
.filter( .filter((item) =>
(item) => item.issPageValue === issPageValue
item.issPageValue === issPageValue && && item.maps.filter((map) => map.scenario === scenario).length > 0);
item.maps.filter((map) => map.scenario === scenario).length > 0
);
let maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined; const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;
return maps ? maps.filter(x => x.filter === true || x.filter === undefined)[0] : undefined; return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined;
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
return undefined; return undefined;
} }
}; }
function GoToStartOn404(next) { function GoToStartOn404(next) {
const errorPageName = issPageValues.WELCOME_PAGE; const errorPageName = issPageValues.WELCOME_PAGE;
@ -217,22 +209,20 @@ function GoToStartOn404(next) {
}); });
// Put item on the bus // Put item on the bus
eventBus.addEventToBus( eventBus.addEventToBus(globalEvents.Categories.GLOBAL_ALERT,
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND, globalEvents.SubCategories.PAGE_NOT_FOUND,
{ {
isDismissible: true, isDismissible: true,
messageCopy: 'You can get a quote by starting on this page.', messageCopy: 'You can get a quote by starting on this page.',
messageHeadline: "We're sorry, something went wrong.", messageHeadline: "We're sorry, something went wrong.",
type: globalEventTypes.Danger type: globalEventTypes.Danger
} });
);
next({ next({
name: errorPageName, name: errorPageName,
query: {issPage: errorPageName } query: { issPage: errorPageName }
}); });
}; }
// Run SiteEntry and PageEntry triggers for experiments // Run SiteEntry and PageEntry triggers for experiments
async function runExperiments(nextPage) { async function runExperiments(nextPage) {

View file

@ -19,7 +19,7 @@ export const issPageValues = {
REVIEW_PAGE: 'review-page', REVIEW_PAGE: 'review-page',
REVEAL: 'reveal', REVEAL: 'reveal',
SERVICE_LOCATION: 'service-location', SERVICE_LOCATION: 'service-location',
TPA_CONFIRMATION:'tpa-confirmation', TPA_CONFIRMATION: 'tpa-confirmation',
SERVICE_PACKAGES: 'service-packages', SERVICE_PACKAGES: 'service-packages',
SCHEDULE_PAGE: 'schedule-page', SCHEDULE_PAGE: 'schedule-page',
VEHICLE_DAMAGE: 'vehicle-damage', VEHICLE_DAMAGE: 'vehicle-damage',
@ -28,7 +28,6 @@ export const issPageValues = {
VEHICLE_SELECTION: 'vehicle-selection', VEHICLE_SELECTION: 'vehicle-selection',
VIN_LOOKUP: 'vin-lookup', VIN_LOOKUP: 'vin-lookup',
TPA_SUBMIT: 'tpa-submit', TPA_SUBMIT: 'tpa-submit',
BAILOUT_PAGE: 'bailout-page',
TPA_SEARCH: 'tpa-search', TPA_SEARCH: 'tpa-search',
POLICY_VEHICLES:'policy-vehicles' POLICY_VEHICLES: 'policy-vehicles'
}; };

View file

@ -18,7 +18,6 @@ const navigationScenarios = {
SELECTED_STYLE: 'SELECTED_STYLE', SELECTED_STYLE: 'SELECTED_STYLE',
// Policy Holder Details // Policy Holder Details
CLICKED_FORWARD_POLICY_UNVERIFIED: 'CLICKED_FORWARD_POLICY_UNVERIFIED',
CLICKED_FORWARD_POLICY_VERIFIED: 'CLICKED_FORWARD_POLICY_VERIFIED', CLICKED_FORWARD_POLICY_VERIFIED: 'CLICKED_FORWARD_POLICY_VERIFIED',
// Policy Vehicle // Policy Vehicle

View file

@ -2,8 +2,8 @@ import { issPageValues } from '@/router/router-constants/issPage-values';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
// Get store from router/index.js instead of importing it here to get updated values // Get store from router/index.js instead of importing it here to get updated values
const routingTable = function (store) {
return [ const routingTable = () => [
{ {
issPageValue: issPageValues.VEHICLE_SELECTION, issPageValue: issPageValues.VEHICLE_SELECTION,
maps: [ maps: [
@ -556,43 +556,42 @@ const routingTable = function (store) {
} }
] ]
}, },
{
issPageValue: issPageValues.TPA_SUBMIT,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.TPA_SEARCH
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.TPA_CONFIRMATION
}
]
},
{
issPageValue: issPageValues.ORDER_CONFIRMATION,
maps: [
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.WELCOME_PAGE
}
]
},
{
issPageValue: issPageValues.TPA_SEARCH,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.PROVIDER_PREFERENCE
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.TPA_SUBMIT
}
]
}
];
{ export { routingTable };
issPageValue: issPageValues.TPA_SUBMIT,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.TPA_SEARCH
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.TPA_CONFIRMATION
}
]
},
{
issPageValue: issPageValues.ORDER_CONFIRMATION,
maps: [
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.WELCOME_PAGE
}
]
},
{
issPageValue: issPageValues.TPA_SEARCH,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.PROVIDER_PREFERENCE
},
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.TPA_SUBMIT
}
]
}
];
};
export { routingTable };

View file

@ -13,12 +13,12 @@ describe('Router', () => {
}); });
it('Should push next view when navigating locally', () => { it('Should push next view when navigating locally', () => {
let scenario = navigationScenarios.CLICKED_FORWARD; const scenario = navigationScenarios.CLICKED_FORWARD;
let currentRoute = { query: { issPage: issPageValues.WELCOME_PAGE }}; const currentRoute = { query: { issPage: issPageValues.WELCOME_PAGE } };
router.push = jest.fn(); router.push = jest.fn();
router.navigate(scenario, currentRoute); router.navigate(scenario, currentRoute);
expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.POLICY_HOLDER_DETAILS); expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.POLICY_HOLDER_DETAILS);
}); });
}); });