Merge branch 'develop' into feature/CSR-96
This commit is contained in:
commit
09530d8338
45 changed files with 1258 additions and 453 deletions
|
|
@ -1,7 +1,7 @@
|
|||
<!-- Documented in confluence https://safelite.atlassian.net/wiki/spaces/DC/pages/76644418/Button+Question+Component -->
|
||||
<template>
|
||||
<div :class="isOverflowScrollable ? 'button-question button-question-overflow' : 'button-question'">
|
||||
<div v-if="questionText" class="mt-5 mb-4 d-flex">
|
||||
<div v-if="questionText" :class="[this.buttonType === 'radio' ? ['mt-2', 'mb-0'] : ['mt-5', 'mb-4'], 'd-flex']">
|
||||
<span class="fs-5 fw-bold w-100" :class="this.buttonType === 'radio' ? 'text-start' : 'text-center'">{{ questionText }}</span>
|
||||
</div>
|
||||
<div class="w-100 d-flex justify-content-center">
|
||||
|
|
|
|||
|
|
@ -1 +1,18 @@
|
|||
test.todo("some test to be written in the future");
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import vinInformation from "@/common-components/vin-information/vin-information";
|
||||
|
||||
describe("vinInformation.vue", () => {
|
||||
it("Should return input class active if isActive is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(vinInformation);
|
||||
|
||||
await wrapper.setData({
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.toggleClass()
|
||||
|
||||
expect(wrapper.vm.isActive).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<textLink :text="HeadlineText" linkType="text" href="#!" />
|
||||
</div>
|
||||
<div class="vin-info">
|
||||
<div class="mt-3" v-html="BodyText"></div>
|
||||
<div class="mt-2" v-html="BodyText"></div>
|
||||
<img :src="OptionalImage" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
const damageLocationsParts = {
|
||||
WINDSHIELD: "Windshield",
|
||||
DRIVER: "Driver",
|
||||
PASSENGER: "Passenger",
|
||||
REAR: "Rear",
|
||||
};
|
||||
|
||||
export { damageLocationsParts };
|
||||
21
src/constants/damage-locations-selected.js
Normal file
21
src/constants/damage-locations-selected.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const damageLocationsSelected = {
|
||||
WINDSHIELD: "Windshield",
|
||||
SIDEDOOR: "SideDoor",
|
||||
REARWINDOW: "RearWindow",
|
||||
REPAIR: "Repair",
|
||||
REPLACE: "Replace",
|
||||
DRIVER: "Driver",
|
||||
PASSENGER: "Passenger",
|
||||
FRONT: "Front",
|
||||
REAR: "Rear",
|
||||
BACK: "Back",
|
||||
QUARTER: "Quarter",
|
||||
VENT: "Vent",
|
||||
SINGLE: "Single",
|
||||
DRIVERSIDE: "DriverSide",
|
||||
PASSENGERSIDE: "PassengerSide",
|
||||
STATIONARY: "Stationary",
|
||||
SLIDER: "Slider"
|
||||
};
|
||||
|
||||
export { damageLocationsSelected };
|
||||
|
|
@ -10,7 +10,7 @@ const customMappings = {
|
|||
{ key: 'Rear Stationary', transformedValue: 'rear window' },
|
||||
{ key: 'Rear Slider', transformedValue: 'rear window' },
|
||||
{ key: 'Driver Front', transformedValue: 'driver side front door' },
|
||||
{ key: 'Driver Back ', transformedValue: 'driver side back door' },
|
||||
{ key: 'Driver Back', transformedValue: 'driver side back door' },
|
||||
{ key: 'Driver Vent', transformedValue: 'driver side vent glass' },
|
||||
{ key: 'Driver Quarter', transformedValue: 'driver side quarter panel' },
|
||||
{ key: 'Driver SideDoor', transformedValue: 'driver side sliding door' },
|
||||
|
|
|
|||
12
src/constants/error-messages.js
Normal file
12
src/constants/error-messages.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const errorMessages = {
|
||||
DAMAGE_LOCATION_REQUIRED: "Please select damage location",
|
||||
DAMAGE_SIDE_REQUIRED: "Please select vehicle side",
|
||||
DRIVER_SIDE_OPTIONS_REQUIRED: "Please select window",
|
||||
PASSENGER_SIDE_OPTIONS_REQUIRED: "Please select window",
|
||||
WINDSHIELD_DAMAGE_TYPE_REQUIRED: "Please select windshield damage",
|
||||
WINDSHIELD_CHIP_COUNT_REQUIRED: "Please select chip(s)",
|
||||
WINSHIELD_REPLACE_OPTIONS_REQUIRED: "Please select windshield part",
|
||||
REPLACE_OPTIONS_REQUIRED: "Please select rear window type",
|
||||
};
|
||||
|
||||
export { errorMessages };
|
||||
|
|
@ -14,10 +14,10 @@ const storeActions = {
|
|||
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
|
||||
|
||||
// DEPENDENCY MUTATIONS
|
||||
RESET_VEHICLE_STATE: "resetVehicleAndDependencies",
|
||||
RESET_DAMAGE_STATE: "resetDamageAndDependencies",
|
||||
RESET_REGISTRATION_STATE: "resetRegistrationAndDependencies",
|
||||
RESET_PARTS_STATE: "resetPartsAndDependencies",
|
||||
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",
|
||||
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
||||
RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies",
|
||||
RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies",
|
||||
};
|
||||
|
||||
export { storeActions };
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const tintMap = {
|
|||
{ name: "gray tint privacy", src: "Glass-NoShade-Privacy.svg" },
|
||||
|
||||
// No shade or tint
|
||||
{ name: "no shade, no tint", src: "Glass-NoShade-NoTint.svg" }, // ???
|
||||
{ name: "clear", src: "Glass-NoShade-NoTint.svg" }
|
||||
],
|
||||
|
||||
windshield: [
|
||||
|
|
@ -70,7 +70,7 @@ const tintMap = {
|
|||
{ name: "gray tint privacy", src: "Windshield-NoShade-Privacy.svg" },
|
||||
|
||||
// No shade or tint
|
||||
{ name: "no shade, no tint", src: "Windshield-NoShade-NoTint.svg" }, // ???
|
||||
{ name: "clear", src: "Windshield-NoShade-NoTint.svg" },
|
||||
|
||||
]
|
||||
}
|
||||
|
|
|
|||
8
src/helpers/validation-rules.js
Normal file
8
src/helpers/validation-rules.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export function required(errorMessage) {
|
||||
return (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return errorMessage;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
29
src/helpers/validation-rules.spec.js
Normal file
29
src/helpers/validation-rules.spec.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { required } from "@/helpers/validation-rules";
|
||||
|
||||
describe("validation-rules.vue", () => {
|
||||
test("required rules should return error if value missing", () => {
|
||||
|
||||
//Arrange
|
||||
const testFn = required("an error");
|
||||
|
||||
//Act
|
||||
const testResponse = testFn();
|
||||
|
||||
//Assert
|
||||
expect(testResponse).toBe("an error");
|
||||
});
|
||||
});
|
||||
|
||||
describe("validation-rules.vue", () => {
|
||||
test("required rules should return true if value present", () => {
|
||||
|
||||
//Arrange
|
||||
const testFn = required("an error");
|
||||
|
||||
//Act
|
||||
const testResponse = testFn('some value');
|
||||
|
||||
//Assert
|
||||
expect(testResponse).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -15,27 +15,38 @@
|
|||
groupName="DamageLocationQuestion"
|
||||
/>
|
||||
<windshieldOptions
|
||||
ref="windshieldOptions"
|
||||
:showWindshieldDamageTypeQuestion="values.DamageLocationQuestion && values.DamageLocationQuestion.toString().includes('-Windshield')"
|
||||
:suppressError="errors.windshieldDamageTypeQuestion && errors.windshieldDamageTypeQuestion.startsWith('checkForRepairAndReplace')"
|
||||
ref="windshieldOptions"
|
||||
v-model="selectedWindshieldOptions"
|
||||
:hasRepairReplaceConflict="hasRepairReplaceConflict"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<alert
|
||||
class="my-3"
|
||||
v-if="errors.windshieldDamageTypeQuestion && errors.windshieldDamageTypeQuestion.startsWith('checkForRepairAndReplace')"
|
||||
v-show="hasRepairReplaceConflict"
|
||||
alertClass="alert-danger"
|
||||
alertHeadline="You'll need to schedule separate appointments"
|
||||
alertCopy="Vehicle service requiring both glass repair and replacement must be scheduled separately, as they're performed by different technicians."
|
||||
alertCopy="Vehicle service requiring both glass repair and replacement must be scheduled separately, as they're performed by different technicians. Continue scheduling your first service now, and then come back to schedule the second service."
|
||||
:isDismissible="false"
|
||||
/>
|
||||
<sideDoorOptions
|
||||
ref="sideDoorOptions"
|
||||
groupName="SideDoorSideQuestion"
|
||||
v-model="sideDoorOptionsData"
|
||||
v-show="!hasRepairReplaceConflict"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<replaceOptionsQuestion
|
||||
ref="driverSideOptions"
|
||||
isAvailable
|
||||
v-model="driverSideOptionsData"
|
||||
groupName="DriverSideReplaceOptionsQuestion"
|
||||
ref="backGlassOptions"
|
||||
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict"
|
||||
v-model="selectedRearReplaceOptions"
|
||||
groupName="BackGlassReplaceOptionsQuestion"
|
||||
validationRules="replace-options-required"
|
||||
/>
|
||||
<funnel-footer
|
||||
ref="funnelFooter"
|
||||
:isDisabled="!meta.valid"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
/>
|
||||
|
||||
<!-- KEEP TEMPORARILY FOR TROUBLESHOOTING VALIDATION -->
|
||||
|
|
@ -60,19 +71,23 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
|
||||
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
|
||||
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
|
||||
import { Form } from 'vee-validate';
|
||||
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "form-test",
|
||||
|
|
@ -110,12 +125,19 @@ export default {
|
|||
vm.$refs.funnelSubHeader.initializeComponent(
|
||||
resultMap.cmsContent.FunnelSubHeaderWidget
|
||||
);
|
||||
vm.$refs.driverSideOptions.initializeComponent(
|
||||
resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.damageLocation.initializeComponent(
|
||||
resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions
|
||||
);
|
||||
vm.$refs.sideDoorOptions.initializeComponent(
|
||||
resultMap.cmsContent.SideDoorSideQuestion, resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.cmsContent.PassengerSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.windshieldOptions.initializeComponent(
|
||||
resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion,
|
||||
resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.backGlassOptions.initializeComponent(
|
||||
resultMap.cmsContent.RearReplaceOptionsQuestion, resultMap.damageOptions.backGlassOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.funnelFooter.initializeComponent(
|
||||
resultMap.cmsContent.FunnelFooterWidget
|
||||
);
|
||||
|
|
@ -124,27 +146,21 @@ export default {
|
|||
data(){
|
||||
return {
|
||||
selectedDamageLocations: [],
|
||||
driverSideOptionsData: [],
|
||||
sideDoorOptionsData: {
|
||||
selectedDoorSides: [],
|
||||
selectedDriverSideReplaceOptions: [],
|
||||
selectedPassengerSideReplaceOptions: []
|
||||
},
|
||||
selectedWindshieldOptions: [],
|
||||
selectedRearReplaceOptions: [],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Form,
|
||||
funnelHeader,
|
||||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
damageLocationQuestion,
|
||||
replaceOptionsQuestion,
|
||||
funnelFooter,
|
||||
windshieldOptions,
|
||||
alert,
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.vehicle.carId !== null;
|
||||
},
|
||||
resetDependentState() {
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_PARTS_STATE);
|
||||
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
},
|
||||
onSubmit(values) {
|
||||
window.alert('Success! Submitted values: ' + JSON.stringify(values, null ,2))
|
||||
|
|
@ -165,5 +181,64 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
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() {
|
||||
if (!this.isWindshieldDamageLocation) return false;
|
||||
|
||||
return this.selectedWindshieldOptions.selectedWindshieldDamageType && this.selectedWindshieldOptions.selectedWindshieldDamageType.some(selectedDamageType =>
|
||||
{
|
||||
return selectedDamageType.toUpperCase() === "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;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
funnelHeader,
|
||||
funnelFooter,
|
||||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
sideDoorOptions,
|
||||
damageLocationQuestion,
|
||||
windshieldOptions,
|
||||
replaceOptionsQuestion,
|
||||
Form,
|
||||
alert,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage="false" />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
<h1>Part Questions Page Placeholder</h1>
|
||||
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
|
||||
</div>
|
||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage="false" />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
<h1>Part Questions Page Placeholder</h1>
|
||||
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -16,11 +16,22 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
|
|||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||
|
||||
// Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import {storeMutations} from "@/constants/store-mutations";
|
||||
import {storeActions} from "@/constants/store-actions";
|
||||
import {
|
||||
fetchCmsContentForPage
|
||||
} from "@/helpers/cms-content-helper";
|
||||
import {
|
||||
settleAllPromises
|
||||
} from "@/helpers/layout-helper";
|
||||
import {
|
||||
storeMutations
|
||||
} from "@/constants/store-mutations";
|
||||
import {
|
||||
storeActions
|
||||
} from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
import {
|
||||
fmgPageValues
|
||||
} from "@/router/router-constants/fmgPage-values";
|
||||
|
||||
export default {
|
||||
name: "part-questions",
|
||||
|
|
@ -61,7 +72,10 @@ export default {
|
|||
);
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.lineItems.glassParts !== null;
|
||||
if (Object.keys(store.getters.pageData(fmgPageValues.PART_QUESTIONS)).length !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
resetDependentState() {
|
||||
// Set
|
||||
|
|
@ -71,8 +85,7 @@ export default {
|
|||
store.dispatch(storeActions.RESET_PARTS_AND_DEPS);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
},
|
||||
data() {},
|
||||
components: {
|
||||
funnelHeader,
|
||||
vehicleBanner,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
);
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.lineItems.glassParts !== null;
|
||||
return true;
|
||||
},
|
||||
resetDependentState() {
|
||||
// Set
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
v-model="selectedValues"
|
||||
validationRules="damage-location-required"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -17,14 +16,11 @@
|
|||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import store from "@/store";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("damage-location-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select damage location";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
defineRule("damage-location-required", required(errorMessages.DAMAGE_LOCATION_REQUIRED));
|
||||
|
||||
export default ({
|
||||
name: "damageLocationQuestion",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
:groupName="groupName"
|
||||
buttonType="listCard"
|
||||
v-model="selectedValues"
|
||||
validationRules="replace-options-required"
|
||||
:validationRules="validationRules"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
|
|
@ -18,15 +18,6 @@
|
|||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import store from "@/store";
|
||||
import { defineRule } from "vee-validate";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("replace-options-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select window";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
export default ({
|
||||
name: "replaceOptionsQuestion",
|
||||
|
|
@ -43,6 +34,7 @@ export default ({
|
|||
groupName: String,
|
||||
modelValue: Array,
|
||||
isMultiSelect: Boolean,
|
||||
validationRules: String,
|
||||
},
|
||||
methods: {
|
||||
initializeComponent(cmsContent, replaceOptions){
|
||||
|
|
|
|||
|
|
@ -9,13 +9,28 @@
|
|||
:groupName="groupName"
|
||||
buttonType="listCard"
|
||||
v-model="selectedDoorSidesValues"
|
||||
validationRules="damage-side-required"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<replaceOptionsQuestion ref="driverSideOptions" :isAvailable="isDriverSideReplaceOptionsQuestionAvailable" groupName="driverSideOptions" filterByVehicleCategory isMultiSelect v-model="selectedDriverSideReplaceOptionsValues" />
|
||||
|
||||
<replaceOptionsQuestion ref="passengerSideOptions" :isAvailable="isPassengerSideReplaceOptionsQuestionAvailable" groupName="passengerSideOptions" filterByVehicleCategory isMultiSelect v-model="selectedPassengerSideReplaceOptionsValues" />
|
||||
<replaceOptionsQuestion
|
||||
ref="driverSideOptions"
|
||||
:isAvailable="isDriverSideReplaceOptionsQuestionAvailable"
|
||||
groupName="driverSideOptions"
|
||||
isMultiSelect
|
||||
filterByVehicleCategory
|
||||
v-model="selectedDriverSideReplaceOptionsValues"
|
||||
validationRules="driver-side-options-required"
|
||||
/>
|
||||
<replaceOptionsQuestion
|
||||
ref="passengerSideOptions"
|
||||
:isAvailable="isPassengerSideReplaceOptionsQuestionAvailable"
|
||||
groupName="passengerSideOptions"
|
||||
isMultiSelect
|
||||
filterByVehicleCategory
|
||||
v-model="selectedPassengerSideReplaceOptionsValues"
|
||||
validationRules="passenger-side-options-required"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -23,6 +38,14 @@
|
|||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||
import store from "@/store";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("damage-side-required", required(errorMessages.DAMAGE_SIDE_REQUIRED));
|
||||
defineRule("driver-side-options-required", required(errorMessages.DRIVER_SIDE_OPTIONS_REQUIRED));
|
||||
defineRule("passenger-side-options-required", required(errorMessages.PASSENGER_SIDE_OPTIONS_REQUIRED));
|
||||
|
||||
export default ({
|
||||
name: "sideDoorOptions",
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-que
|
|||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { nextTick } from "vue";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import store from "@/store";
|
||||
import { validate } from "vee-validate";
|
||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
|
|
@ -37,6 +39,11 @@ jest.mock("@/store", () => ({
|
|||
getters: {
|
||||
vehicle: {
|
||||
carId: "C00000000",
|
||||
image: "test.jpg",
|
||||
},
|
||||
eventBusItem: jest.fn(),
|
||||
damage: {
|
||||
glassToReplace: []
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
@ -212,6 +219,11 @@ describe("vehicle-damage.vue", () => {
|
|||
mountOptionsMockData: {
|
||||
router: { navigateAfterSave: jest.fn(), },
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],
|
||||
store: {
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -252,7 +264,7 @@ describe("vehicle-damage.vue", () => {
|
|||
});
|
||||
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("Windshield replace with single part on ForwardButtonAction triggers a router.navigateAfterSave and saves selections to store", async () => {
|
||||
test("Windshield replace with single part on ForwardButtonAction triggers a router.navigate and saves selections to store", async () => {
|
||||
//Arrange
|
||||
const partsData = { partsOrQuestions: [
|
||||
{
|
||||
|
|
@ -272,8 +284,14 @@ describe("vehicle-damage.vue", () => {
|
|||
const { wrapper } = setupMocks({
|
||||
pageHeaderWidgetHeaderText: "",
|
||||
mountOptionsMockData: {
|
||||
router: { navigateAfterSave: jest.fn(), },
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],},
|
||||
router: { navigate: jest.fn(), },
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],
|
||||
store: {
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.selectedDamageLocations = ["Windshield"];
|
||||
|
|
@ -296,7 +314,7 @@ describe("vehicle-damage.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace);
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, false);
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_TO_REPLACE, expectedGlassToReplace);
|
||||
|
|
@ -357,7 +375,13 @@ describe("vehicle-damage.vue", () => {
|
|||
pageHeaderWidgetHeaderText: "",
|
||||
mountOptionsMockData: {
|
||||
router: { navigateAfterSave: jest.fn(), },
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],},
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],
|
||||
store: {
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.selectedDamageLocations = ["Windshield"];
|
||||
|
|
@ -389,14 +413,20 @@ describe("vehicle-damage.vue", () => {
|
|||
|
||||
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("Windshield repair on ForwardButtonAction triggers a router.navigateAfterSave and saves selection to store", async () => {
|
||||
test("Windshield repair on ForwardButtonAction triggers a router.navigate and saves selection to store", async () => {
|
||||
//Arrange
|
||||
const partsData = { partsOrQuestions: []};
|
||||
const { wrapper } = setupMocks({
|
||||
pageHeaderWidgetHeaderText: "",
|
||||
mountOptionsMockData: {
|
||||
router: { navigateAfterSave: jest.fn(), },
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],},
|
||||
router: { navigate: jest.fn(), },
|
||||
actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],
|
||||
store: {
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.selectedDamageLocations = ["Windshield"];
|
||||
|
|
@ -416,7 +446,7 @@ describe("vehicle-damage.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, true);
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_NUMBER_OF_CHIPS, 2);
|
||||
});
|
||||
|
|
@ -591,17 +621,245 @@ describe("vehicle-damage.vue", () => {
|
|||
wrapper.vm.resetDependentState();
|
||||
|
||||
//Assert
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_PARTS_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES)
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
const damageLocations = [["Windshield", [damageLocationsSelected.WINDSHIELD]],
|
||||
["Driver", [damageLocationsSelected.SIDEDOOR]],
|
||||
["Passenger", [damageLocationsSelected.SIDEDOOR]],
|
||||
["Rear", [damageLocationsSelected.REARWINDOW]]];
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test.each(damageLocations)("getDamageLocationsFromStore for %s returns expected %s", async (damageLocation, expectedGlass) => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{location: damageLocation}] }, isRepair: true};
|
||||
|
||||
var glassSelections = wrapper.vm.getDamageLocationsFromStore();
|
||||
|
||||
//Assert
|
||||
expect(glassSelections).toEqual(expectedGlass);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
const storeWindshieldOptions = [[1, false, "Windshield", "Single", { selectedWindshieldDamageType: [damageLocationsSelected.REPLACE],
|
||||
selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: [damageLocationsSelected.SINGLE]}],
|
||||
[2, false, "Windshield", "Driver", { selectedWindshieldDamageType: [damageLocationsSelected.REPLACE],
|
||||
selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: [damageLocationsSelected.DRIVER]}],
|
||||
[3, false, "Windshield", "Passenger", { selectedWindshieldDamageType: [damageLocationsSelected.REPLACE],
|
||||
selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: [damageLocationsSelected.PASSENGER]}],
|
||||
[4, true, "", "", { selectedWindshieldDamageType: [damageLocationsSelected.REPAIR],
|
||||
selectedWindshieldChipCount: [2], selectedWindshieldReplaceOptions: []}]
|
||||
];
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test.each(storeWindshieldOptions)("getWindshieldOptionsFromStore test #%s", async (testNum, isRepair, damageLocation, damageName, expectedWindshieldOptions) => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
store.getters = {
|
||||
vehicle:
|
||||
{ carId: "C0000000" },
|
||||
eventBusItem: jest.fn(),
|
||||
damage:
|
||||
{
|
||||
glassToReplace: [{location: damageLocation, name: damageName}],
|
||||
isRepair: isRepair,
|
||||
numberOfChips: 2
|
||||
},
|
||||
};
|
||||
|
||||
var windshieldSelections = wrapper.vm.getWindshieldOptionsFromStore();
|
||||
|
||||
//Assert
|
||||
expect(windshieldSelections).toEqual(expectedWindshieldOptions);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
const driverDoorSides = [["Driver", "Front", [damageLocationsSelected.FRONT]],
|
||||
["Driver", "Back", [damageLocationsSelected.BACK]],
|
||||
["Driver", "Vent", [damageLocationsSelected.VENT]],
|
||||
["Driver", "Quarter", [damageLocationsSelected.QUARTER]]
|
||||
];
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test.each(driverDoorSides)("getDriverSideReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{location: damageLocation, name: damageName}] }, isRepair: true};
|
||||
|
||||
var glassSelections = wrapper.vm.getDriverSideReplaceOptionsFromStore();
|
||||
|
||||
//Assert
|
||||
expect(glassSelections).toEqual(expectedGlass);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
const passengerDoorSides = [["Passenger", "Front", [damageLocationsSelected.FRONT]],
|
||||
["Passenger", "Back", [damageLocationsSelected.BACK]],
|
||||
["Passenger", "Vent", [damageLocationsSelected.VENT]],
|
||||
["Passenger", "Quarter", [damageLocationsSelected.QUARTER]]
|
||||
];
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test.each(passengerDoorSides)("getPassengerSideReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{location: damageLocation, name: damageName}] }, isRepair: true};
|
||||
|
||||
var glassSelections = wrapper.vm.getPassengerSideReplaceOptionsFromStore();
|
||||
|
||||
//Assert
|
||||
expect(glassSelections).toEqual(expectedGlass);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
const rearReplaceOptions = [["Rear", "Stationary", [damageLocationsSelected.STATIONARY]],
|
||||
["Rear", "Slider", [damageLocationsSelected.SLIDER]]
|
||||
];
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test.each(rearReplaceOptions)("getRearReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{location: damageLocation, name: damageName}] }, isRepair: true};
|
||||
|
||||
var glassSelections = wrapper.vm.getRearReplaceOptionsFromStore();
|
||||
|
||||
//Assert
|
||||
expect(glassSelections).toEqual(expectedGlass);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("when onInvalidSubmit is triggered with errors focus will be put on the first element with an error", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const mockedValidationPayload = {
|
||||
values: {},
|
||||
errors: {
|
||||
driverSideOptions: 'Please select window',
|
||||
passengerSideOptions: 'Please select window'
|
||||
},
|
||||
results: {},
|
||||
}
|
||||
const newObj = document.createElement('input');
|
||||
newObj.setAttribute("id", "testInput");
|
||||
newObj.setAttribute("data-focus-target", "driverSideOptions");
|
||||
document.body.appendChild(newObj);
|
||||
const testInputElement = document.getElementById("testInput");
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
wrapper.vm.onInvalidSubmit(mockedValidationPayload);
|
||||
await nextTick();
|
||||
const focusedEl = document.activeElement;
|
||||
|
||||
//Assert
|
||||
expect(testInputElement).toBe(focusedEl);
|
||||
});
|
||||
});
|
||||
|
||||
// THE FOLLOWING TEST IS NOT NECESSARILY REQUIRED FOR COVERAGE
|
||||
// BUT KEEP FOR AN EXAMPLE OF A VALIDATION TEST
|
||||
//
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("when validation rules are set they should validate correctly", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
const testNull = validate( "", "replace-options-required");
|
||||
const testString = validate( "sldfj", "replace-options-required");
|
||||
await flushPromises();
|
||||
|
||||
//Assert
|
||||
testNull.then(function(data) {
|
||||
expect(data.valid).toEqual(false);
|
||||
});
|
||||
testString.then(function(data) {
|
||||
expect(data.valid).toEqual(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function setupMocks({
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
store: {
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}) {
|
||||
//Mock api responses
|
||||
|
|
@ -666,6 +924,7 @@ function setupMocks({
|
|||
|
||||
replaceOptionsQuestion.methods = {
|
||||
initializeComponent: jest.fn(),
|
||||
updateSelectedValues: jest.fn(),
|
||||
};
|
||||
|
||||
funnelFooter.methods = {
|
||||
|
|
@ -673,7 +932,9 @@ function setupMocks({
|
|||
}
|
||||
|
||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||
const wrapper = shallowMount(vehicleDamage, mountOptions);
|
||||
mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods
|
||||
|
||||
const wrapper = mount(vehicleDamage, mountOptions);
|
||||
|
||||
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
|
||||
funnelHeaderWrapper.vm.initializeComponent =
|
||||
|
|
@ -687,36 +948,25 @@ function setupMocks({
|
|||
sideDoorOptionsWrapper.vm.initializeComponent =
|
||||
sideDoorOptions.methods.initializeComponent;
|
||||
|
||||
const windshieldOptionsWrapper = wrapper.findComponent({
|
||||
name: "windshieldOptions",
|
||||
});
|
||||
|
||||
const windshieldOptionsWrapper = wrapper.findComponent({ name: "windshieldOptions" });
|
||||
windshieldOptionsWrapper.vm.initializeComponent =
|
||||
windshieldOptions.methods.initializeComponent;
|
||||
|
||||
const funnelSubHeaderWrapper = wrapper.findComponent({
|
||||
name: "funnelSubHeader",
|
||||
});
|
||||
const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader" });
|
||||
funnelSubHeaderWrapper.vm.initializeComponent =
|
||||
funnelSubHeader.methods.initializeComponent;
|
||||
|
||||
const backGlassOptionsWrapper = wrapper.findComponent({
|
||||
name: "replaceOptionsQuestion",
|
||||
});
|
||||
const backGlassOptionsWrapper = wrapper.findComponent({ name: "replaceOptionsQuestion" });
|
||||
backGlassOptionsWrapper.vm.initializeComponent =
|
||||
replaceOptionsQuestion.methods.initializeComponent;
|
||||
|
||||
const damageLocationQuestionWrapper = wrapper.findComponent({
|
||||
name: "damageLocationQuestion",
|
||||
});
|
||||
const damageLocationQuestionWrapper = wrapper.findComponent({ name: "damageLocationQuestion" });
|
||||
damageLocationQuestionWrapper.vm.initializeComponent =
|
||||
damageLocationQuestion.methods.initializeComponent;
|
||||
|
||||
const funnelFooterWrapper = wrapper.findComponent({
|
||||
name: "funnelFooter",
|
||||
});
|
||||
|
||||
funnelFooterWrapper.vm.initializeComponent = funnelFooter.methods.initializeComponent;
|
||||
const funnelFooterWrapper = wrapper.findComponent({ name: "funnelFooter" });
|
||||
funnelFooterWrapper.vm.initializeComponent =
|
||||
funnelFooter.methods.initializeComponent;
|
||||
|
||||
return { wrapper, apiPromise };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,52 @@
|
|||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
<damageLocationQuestion
|
||||
ref="damageLocation"
|
||||
v-model="selectedDamageLocations"
|
||||
groupName="DamageLocationQuestion"
|
||||
/>
|
||||
<windshieldOptions
|
||||
ref="windshieldOptions"
|
||||
v-model="selectedWindshieldOptions"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<sideDoorOptions
|
||||
ref="sideDoorOptions"
|
||||
groupName="SideDoorSideQuestion"
|
||||
v-model="sideDoorOptionsData"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<replaceOptionsQuestion
|
||||
ref="backGlassOptions"
|
||||
:isAvailable="isRearWindowDamageLocation"
|
||||
v-model="selectedRearReplaceOptions"
|
||||
groupName="BackGlassReplaceOptionsQuestion"
|
||||
/>
|
||||
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction" />
|
||||
<Form
|
||||
@submit="onSubmit"
|
||||
@invalid-submit="onInvalidSubmit"
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
>
|
||||
<damageLocationQuestion
|
||||
ref="damageLocation"
|
||||
v-model="selectedDamageLocations"
|
||||
groupName="DamageLocationQuestion"
|
||||
/>
|
||||
<windshieldOptions
|
||||
ref="windshieldOptions"
|
||||
v-model="selectedWindshieldOptions"
|
||||
:hasRepairReplaceConflict="hasRepairReplaceConflict"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<alert
|
||||
class="my-3"
|
||||
v-show="hasRepairReplaceConflict"
|
||||
alertClass="alert-danger"
|
||||
alertHeadline="You'll need to schedule separate appointments"
|
||||
alertCopy="Vehicle service requiring both glass repair and replacement must be scheduled separately, as they're performed by different technicians. Continue scheduling your first service now, and then come back to schedule the second service."
|
||||
:isDismissible="false"
|
||||
/>
|
||||
<sideDoorOptions
|
||||
ref="sideDoorOptions"
|
||||
groupName="SideDoorSideQuestion"
|
||||
v-model="sideDoorOptionsData"
|
||||
v-show="!hasRepairReplaceConflict"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<replaceOptionsQuestion
|
||||
ref="backGlassOptions"
|
||||
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict"
|
||||
v-model="selectedRearReplaceOptions"
|
||||
groupName="BackGlassReplaceOptionsQuestion"
|
||||
validationRules="replace-options-required"
|
||||
/>
|
||||
<funnel-footer
|
||||
ref="funnelFooter"
|
||||
:isDisabled="!meta.valid"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -39,6 +62,7 @@ import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-doo
|
|||
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
|
||||
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
|
||||
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -46,9 +70,14 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
|||
import { storeActions } from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { damageLocationsParts } from "@/constants/damage-locations-parts.js";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "vehicle-damage",
|
||||
|
|
@ -106,14 +135,14 @@ export default {
|
|||
},
|
||||
data(){
|
||||
return {
|
||||
selectedDamageLocations: [],
|
||||
selectedDamageLocations: this.getDamageLocationsFromStore(),
|
||||
sideDoorOptionsData: {
|
||||
selectedDoorSides: [],
|
||||
selectedDriverSideReplaceOptions: [],
|
||||
selectedPassengerSideReplaceOptions: []
|
||||
selectedDoorSides: this.getDoorSidesFromStore(),
|
||||
selectedDriverSideReplaceOptions: this.getDriverSideReplaceOptionsFromStore(),
|
||||
selectedPassengerSideReplaceOptions: this.getPassengerSideReplaceOptionsFromStore()
|
||||
},
|
||||
selectedWindshieldOptions: [],
|
||||
selectedRearReplaceOptions: [],
|
||||
selectedWindshieldOptions: this.getWindshieldOptionsFromStore(),
|
||||
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -121,7 +150,7 @@ export default {
|
|||
return store.getters.vehicle.carId !== null;
|
||||
},
|
||||
resetDependentState() {
|
||||
store.dispatch(storeActions.RESET_PARTS_STATE);
|
||||
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
},
|
||||
backButtonAction() {
|
||||
// route to move backwards
|
||||
|
|
@ -130,20 +159,133 @@ export default {
|
|||
this.$route
|
||||
);
|
||||
},
|
||||
onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior
|
||||
onInvalidSubmit({ values, errors, results }) {
|
||||
// identify the first error field and put focus on it
|
||||
// get error names array
|
||||
const errorNames = errors ? Object.keys(errors) : [];
|
||||
const firstErrorEl = errorNames[0];
|
||||
if (firstErrorEl) {
|
||||
const qsString = "[data-focus-target='" + firstErrorEl + "']";
|
||||
const el = document.querySelector(qsString);
|
||||
el && el.focus();
|
||||
}
|
||||
},
|
||||
|
||||
async forwardButtonAction() {
|
||||
var windshieldChipCount = this.selectedWindshieldOptions.selectedWindshieldChipCount && this.isWindshieldDamageLocation ?
|
||||
this.selectedWindshieldOptions.selectedWindshieldChipCount[0] : null;
|
||||
getDamageLocationsFromStore() {
|
||||
var glassSelections = [];
|
||||
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD }) ||
|
||||
store.getters.damage.isRepair) {
|
||||
glassSelections.push(damageLocationsSelected.WINDSHIELD);
|
||||
}
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.DRIVER ||
|
||||
glass.location === damageLocationsSelected.PASSENGER })) {
|
||||
glassSelections.push(damageLocationsSelected.SIDEDOOR);
|
||||
}
|
||||
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.REAR })) {
|
||||
glassSelections.push(damageLocationsSelected.REARWINDOW);
|
||||
}
|
||||
|
||||
return glassSelections;
|
||||
},
|
||||
|
||||
getWindshieldOptionsFromStore() {
|
||||
var windShieldOptions = { selectedWindshieldDamageType: [], selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: []};
|
||||
|
||||
if (store.getters.damage.isRepair === undefined) return windshieldOptions;
|
||||
|
||||
if (!store.getters.damage.isRepair) {
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
||||
glass.name === damageLocationsSelected.SINGLE })) {
|
||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
||||
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE);
|
||||
}
|
||||
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
||||
glass.name === damageLocationsSelected.DRIVER })) {
|
||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
||||
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER);
|
||||
}
|
||||
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD &&
|
||||
glass.name === damageLocationsSelected.PASSENGER })) {
|
||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPLACE);
|
||||
windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER);
|
||||
}
|
||||
}
|
||||
|
||||
if (store.getters.damage.isRepair) {
|
||||
windShieldOptions.selectedWindshieldDamageType.push(damageLocationsSelected.REPAIR);
|
||||
windShieldOptions.selectedWindshieldChipCount.push(store.getters.damage.numberOfChips);
|
||||
}
|
||||
|
||||
return windShieldOptions;
|
||||
|
||||
},
|
||||
|
||||
getDoorSidesFromStore() {
|
||||
var doorSides = [];
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.DRIVER })){
|
||||
doorSides.push(damageLocationsSelected.DRIVERSIDE);
|
||||
}
|
||||
|
||||
if (store.getters.damage.glassToReplace.some(glass => { return glass.location === damageLocationsSelected.PASSENGER })){
|
||||
doorSides.push(damageLocationsSelected.PASSENGERSIDE);
|
||||
}
|
||||
|
||||
return doorSides;
|
||||
},
|
||||
|
||||
getDriverSideReplaceOptionsFromStore() {
|
||||
var driverSideReplaceOptions = [];
|
||||
|
||||
store.getters.damage.glassToReplace.forEach(glass => {
|
||||
if (glass.location === damageLocationsSelected.DRIVER){
|
||||
driverSideReplaceOptions.push(glass.name);
|
||||
}
|
||||
});
|
||||
|
||||
return driverSideReplaceOptions;
|
||||
},
|
||||
|
||||
getPassengerSideReplaceOptionsFromStore() {
|
||||
var passengerSideReplaceOptions = [];
|
||||
|
||||
store.getters.damage.glassToReplace.forEach(glass => {
|
||||
if (glass.location === damageLocationsSelected.PASSENGER){
|
||||
passengerSideReplaceOptions.push(glass.name);
|
||||
}
|
||||
});
|
||||
|
||||
return passengerSideReplaceOptions;
|
||||
},
|
||||
|
||||
getRearReplaceOptionsFromStore(){
|
||||
var rearReplaceOptions = [];
|
||||
|
||||
store.getters.damage.glassToReplace.forEach(glass => {
|
||||
if (glass.location === damageLocationsSelected.REAR){
|
||||
rearReplaceOptions.push(glass.name);
|
||||
}
|
||||
});
|
||||
|
||||
return rearReplaceOptions;
|
||||
},
|
||||
|
||||
async forwardButtonAction() {
|
||||
store.commit(this.storeMutations.UPDATE_IS_REPAIR, this.isWindshieldRepair);
|
||||
|
||||
if (windshieldChipCount){
|
||||
store.commit(this.storeMutations.UPDATE_NUMBER_OF_CHIPS, parseInt(windshieldChipCount));
|
||||
if (this.isWindshieldRepair){
|
||||
store.commit(this.storeMutations.UPDATE_NUMBER_OF_CHIPS, parseInt(this.selectedWindshieldOptions.selectedWindshieldChipCount));
|
||||
} else {
|
||||
store.commit(this.storeMutations.UPDATE_NUMBER_OF_CHIPS, null);
|
||||
}
|
||||
|
||||
store.commit(this.storeMutations.UPDATE_GLASS_TO_REPLACE, this.selectedGlassToReplace());
|
||||
|
||||
var partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(this.storeActions.GET_PARTS_OR_QUESTIONS,
|
||||
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(this.storeActions.GET_PARTS_OR_QUESTIONS,
|
||||
{ carId: store.getters.vehicle.carId, glassArray: this.selectedGlassToReplace()}, false);
|
||||
|
||||
this.navigateForward(partsData);
|
||||
|
|
@ -158,14 +300,12 @@ export default {
|
|||
|
||||
//found multiple parts for a single glass location (Windshield, Driver, Passenger, Rear)
|
||||
if (partsData.data.partsOrQuestions){
|
||||
var multiParts = false;
|
||||
partsData.data.partsOrQuestions.forEach(pq => {
|
||||
if (pq.parts != null && pq.parts.length > 1){
|
||||
for (var i = 0; i < partsData.data.partsOrQuestions.length; i++){
|
||||
if (partsData.data.partsOrQuestions[i].parts != null && partsData.data.partsOrQuestions[i].parts.length > 1){
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data);
|
||||
multiParts = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (multiParts) return;
|
||||
}
|
||||
}
|
||||
|
||||
//if not a repair then there should only be 1 part and no problem questions at this point so save the part to the store.
|
||||
|
|
@ -173,39 +313,39 @@ export default {
|
|||
store.commit(this.storeMutations.UPDATE_PARTS, partsData.data.partsOrQuestions[0].parts);
|
||||
}
|
||||
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART, this.$route);
|
||||
this.$router.navigate(this.navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART, this.$route);
|
||||
},
|
||||
|
||||
selectedGlassToReplace() {
|
||||
var selectedGlassToReplace = [];
|
||||
const selectedGlassToReplace = [];
|
||||
if (this.isWindshieldDamageLocation && !this.isWindshieldRepair){
|
||||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach(wsItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsParts.WINDSHIELD, name: wsItem});
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.WINDSHIELD, name: wsItem});
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isDriverSideReplace){
|
||||
this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach(driverItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsParts.DRIVER, name: driverItem});
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.DRIVER, name: driverItem});
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isPassengerSideReplace){
|
||||
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach(passengerItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsParts.PASSENGER, name: passengerItem});
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.PASSENGER, name: passengerItem});
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isRearWindowDamageLocation) {
|
||||
this.selectedRearReplaceOptions.forEach(rearItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsParts.REAR, name: rearItem});
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.REAR, name: rearItem});
|
||||
})
|
||||
}
|
||||
|
||||
return selectedGlassToReplace;
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
computed: {
|
||||
isWindshieldDamageLocation() {
|
||||
return this.selectedDamageLocations.some(selectedDamages =>
|
||||
{
|
||||
|
|
@ -227,7 +367,7 @@ export default {
|
|||
isWindshieldRepair() {
|
||||
if (!this.isWindshieldDamageLocation) return false;
|
||||
|
||||
return this.selectedWindshieldOptions.selectedWindshieldDamageType.some(selectedDamageType =>
|
||||
return this.selectedWindshieldOptions.selectedWindshieldDamageType && this.selectedWindshieldOptions.selectedWindshieldDamageType.some(selectedDamageType =>
|
||||
{
|
||||
return selectedDamageType.toUpperCase() === "REPAIR";
|
||||
});
|
||||
|
|
@ -247,7 +387,10 @@ export default {
|
|||
{
|
||||
return selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE;
|
||||
});
|
||||
}
|
||||
},
|
||||
hasRepairReplaceConflict() {
|
||||
return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair;
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
|
|
@ -259,6 +402,8 @@ export default {
|
|||
damageLocationQuestion,
|
||||
windshieldOptions,
|
||||
replaceOptionsQuestion,
|
||||
Form,
|
||||
alert,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
buttonType="listButtonHorizontal"
|
||||
useTextForValue
|
||||
v-model="selectedChipCountValues"
|
||||
:validationRules="validationRules"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
|
|
@ -28,12 +29,19 @@ export default ({
|
|||
modelValue: Array,
|
||||
groupName: String,
|
||||
isAvailable: Boolean,
|
||||
validationRules: String,
|
||||
},
|
||||
methods: {
|
||||
initializeComponent(cmsContent){
|
||||
this.questionText = cmsContent.QuestionText;
|
||||
this.answersFromCms = cmsContent.Answers;
|
||||
}
|
||||
},
|
||||
updateSelectedValues() {
|
||||
// UPDATE SELECTEDVALUES IF ONLY ONE ANSWER
|
||||
if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1 && this.selectedValues) {
|
||||
this.selectedValues = [this.answersToDisplay[0].Name];
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
selectedChipCountValues: {
|
||||
|
|
@ -45,6 +53,12 @@ export default ({
|
|||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isAvailable(val) {
|
||||
// CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE
|
||||
val && this.updateSelectedValues();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
:groupName="groupName"
|
||||
buttonType="listCard"
|
||||
v-model="selectedValues"
|
||||
:suppressError="suppressError"
|
||||
:validationRules="validationRules"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
|
|
@ -14,24 +16,7 @@
|
|||
|
||||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
//import { defineRule } from "vee-validate";
|
||||
|
||||
//DEFINE VALIDATION RULES
|
||||
// add these to the button component above:
|
||||
// validationRules="windshield-damage-required|checkForRepairAndReplace:@DamageLocationQuestion"
|
||||
// :suppressError="suppressError"
|
||||
// defineRule("windshield-damage-required", (value) => {
|
||||
// if (!value || value.length < 1) {
|
||||
// return "Please select windshield damage";
|
||||
// }
|
||||
// return true;
|
||||
// });
|
||||
// defineRule("checkForRepairAndReplace", (value, [other]) => {
|
||||
// if (value === "Windshield-Chip" && other.toString().includes("Windshield") && other.length > 1) {
|
||||
// return "checkForRepairAndReplace error"
|
||||
// }
|
||||
// return true;
|
||||
// });
|
||||
import store from "@/store";
|
||||
|
||||
export default ({
|
||||
name: "windshieldDamageTypeQuestion",
|
||||
|
|
@ -46,12 +31,13 @@ export default ({
|
|||
groupName: String,
|
||||
isAvailable: Boolean,
|
||||
suppressError: Boolean,
|
||||
validationRules: String,
|
||||
},
|
||||
methods: {
|
||||
initializeComponent(cmsContent){
|
||||
this.questionText = cmsContent.QuestionText;
|
||||
this.answersFromCms = cmsContent.Answers;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
selectedValues: {
|
||||
|
|
|
|||
|
|
@ -2,20 +2,23 @@
|
|||
<div class="windshield-options">
|
||||
<windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion"
|
||||
:isAvailable=isWindshieldDamageLocation
|
||||
:suppressError="suppressError"
|
||||
:suppressError="hasRepairReplaceConflict"
|
||||
groupName="WindshieldDamageTypeQuestion"
|
||||
v-model="selectedWindshieldDamageTypeValues"
|
||||
v-model="selectedWindshieldDamageTypeValues"
|
||||
validationRules="windshield-damage-type-required|checkForRepairAndReplace:@DamageLocationQuestion"
|
||||
/>
|
||||
<windshieldChipCountQuestion ref="windshieldChipCountQuestion"
|
||||
:isAvailable=isRepairOptionSelected
|
||||
:isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict"
|
||||
groupName="WindshieldChipCountQuestion"
|
||||
v-model="selectedWindshieldChipCountValues"
|
||||
v-model="selectedWindshieldChipCountValues"
|
||||
validationRules="windshield-chip-count-required"
|
||||
/>
|
||||
<replaceOptionsQuestion ref="replaceOptionsQuestion"
|
||||
:isAvailable=isReplaceOptionSelected
|
||||
isMultiSelect
|
||||
groupName="WindshieldReplaceOptions"
|
||||
v-model="selectedWindshieldReplaceOptionsValues"
|
||||
validationRules="windshield-replace-options-required"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -24,15 +27,29 @@
|
|||
import windshieldDamageTypeQuestion from"@/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 { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("windshield-damage-type-required", required(errorMessages.WINDSHIELD_DAMAGE_TYPE_REQUIRED));
|
||||
defineRule("windshield-chip-count-required", required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED));
|
||||
defineRule("windshield-replace-options-required", required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED));
|
||||
|
||||
defineRule("checkForRepairAndReplace", (value, [other]) => {
|
||||
if (value.toString().toUpperCase().includes("REPAIR") && other.toString().toUpperCase().includes("WINDSHIELD") && other.length > 1) {
|
||||
return "checkForRepairAndReplace error"
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
export default ({
|
||||
name: "windshieldOptions",
|
||||
|
||||
props: {
|
||||
modelValue: Array,
|
||||
selectedDamageLocations: Array,
|
||||
selectedChipCount: Array,
|
||||
selectedReplaceOptions: Array,
|
||||
suppressError: Boolean
|
||||
hasRepairReplaceConflict: Boolean
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -70,7 +87,7 @@ export default ({
|
|||
},
|
||||
selectedWindshieldChipCountValues: {
|
||||
get: function() {
|
||||
return this.selectedValues.selectedChipCount;
|
||||
return this.selectedValues.selectedWindshieldChipCount;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.selectedValues = this.getWindshieldOptions(this.selectedWindshieldDamageTypeValues, newValue, null);
|
||||
|
|
|
|||
|
|
@ -223,8 +223,8 @@ describe("vehicle-make.vue", () => {
|
|||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null)
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null)
|
||||
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES)
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ export default {
|
|||
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
|
||||
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE);
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -208,8 +208,8 @@ describe("vehicle-model.vue", () => {
|
|||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null)
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null)
|
||||
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES)
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ export default {
|
|||
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
|
||||
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE);
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass
|
|||
import store from "@/store";
|
||||
|
||||
jest.mock("@/store", () => { return {}; }, { virtual: true });
|
||||
store.getters = {
|
||||
pageData: jest.fn()
|
||||
};
|
||||
|
||||
const featureListData = {
|
||||
colorAnswersProp: [
|
||||
|
|
@ -93,9 +96,9 @@ describe("glass-part-question.vue", () => {
|
|||
|
||||
//Act
|
||||
await wrapper.vm.$nextTick();
|
||||
wrapper.setData({selectedTint: {"Rear-Stationary" : 'Green Tint' } });
|
||||
wrapper.setData({ selectedTint: { "Rear-Stationary": 'Green Tint' } });
|
||||
|
||||
expect(wrapper.vm.selectedTint).toStrictEqual({"Rear-Stationary": 'Green Tint'});
|
||||
expect(wrapper.vm.selectedTint).toStrictEqual({ "Rear-Stationary": 'Green Tint' });
|
||||
|
||||
await wrapper.vm.ResetTintAndPartSelections();
|
||||
expect(wrapper.vm.selectedTint).toStrictEqual({});
|
||||
|
|
@ -107,7 +110,19 @@ describe("glass-part-question.vue", () => {
|
|||
function setupMocks({ glassNameProp, glassLocationProp, colorAnswersProp, modelValueProp }) {
|
||||
|
||||
//Mock store
|
||||
const mountOptions = getMountOptions({});
|
||||
store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: {}});
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
const mountOptions = getMountOptions({
|
||||
store: {
|
||||
getters: store.getters,
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
fmgPage: 'vehicle-parts',
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
mountOptions.propsData = {
|
||||
glassName: glassNameProp,
|
||||
|
|
|
|||
|
|
@ -1,33 +1,62 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<p class="mb-0">{{ colorQuestionText }}</p>
|
||||
<p class="mb-0">{{ colorQuestionText }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container nested-radio" v-for="(value, name, index) in featureListData" :key="index">
|
||||
<div
|
||||
class="container nested-radio"
|
||||
v-for="(value, name, index) in featureListData"
|
||||
:key="index"
|
||||
>
|
||||
<div class="row my-2">
|
||||
<div class="col">
|
||||
<listCard v-model="selectedTint[name]" :isRadio="true" :isWide="true" :buttonImage="
|
||||
<div class="col">
|
||||
<listCard
|
||||
v-model="selectedTint[name]"
|
||||
:value="`${glassLocation}-${glassName}-${name}`"
|
||||
:isRadio="true"
|
||||
:isWide="true"
|
||||
:buttonImage="
|
||||
require(`@/assets/img/tints/${getTintSourceImage(
|
||||
glassLocation,
|
||||
name
|
||||
)}`)
|
||||
" :buttonLabel="name" altText="" :buttonID="`${glassLocation}-${glassName}-${name}`" :groupName="`${glassLocation}-${glassName}`" @isCheckedChanged="ResetTintAndPartSelections()" />
|
||||
</div>
|
||||
"
|
||||
:buttonLabel="name"
|
||||
altText=""
|
||||
:buttonID="`${glassLocation}-${glassName}-${name}`"
|
||||
:groupName="`${glassLocation}-${glassName}`"
|
||||
@isCheckedChanged="ResetTintAndPartSelections()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div v-if="
|
||||
selectedTint[name] != undefined &&
|
||||
selectedTint[name].buttonId === `${glassLocation}-${glassName}-${name}`
|
||||
" class="row my-2" aria-live="polite">
|
||||
<div class="col">
|
||||
<buttonQuestion v-model="selectedPart[glassLocation]" buttonType="radio" class="radioQuestion" :questionText="glassFeatureQuestion" :answers="value" textPosition="text-start" :loaderEnabled="false" :isRequired="true" :groupName="`${glassLocation}-${glassName}-${name}`" />
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
selectedTint[name] != undefined &&
|
||||
selectedTint[name].buttonId ===
|
||||
`${glassLocation}-${glassName}-${name}`
|
||||
"
|
||||
class="row my-2"
|
||||
aria-live="polite"
|
||||
>
|
||||
<div class="col">
|
||||
<buttonQuestion
|
||||
v-model="selectedPart[glassLocation]"
|
||||
buttonType="radio"
|
||||
class="radioQuestion"
|
||||
:questionText="glassFeatureQuestion"
|
||||
:answers="value"
|
||||
textPosition="text-start"
|
||||
:loaderEnabled="false"
|
||||
:isRequired="true"
|
||||
:groupName="`${glassLocation}-${glassName}-${name}`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -36,108 +65,165 @@ import listCard from "@/ux-components/list-card/list-card";
|
|||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
|
||||
// Supporting files
|
||||
import {
|
||||
getTintImage
|
||||
} from "@/constants/tint-mapper";
|
||||
import {
|
||||
getCustomTransformValue
|
||||
} from "@/constants/dynamictext-mapper";
|
||||
import { getTintImage } from "@/constants/tint-mapper";
|
||||
import { getCustomTransformValue } from "@/constants/dynamictext-mapper";
|
||||
|
||||
export default {
|
||||
name: "glass-part-question",
|
||||
inheritAttrs: false,
|
||||
data() {
|
||||
return {
|
||||
glassColorQuestion: '',
|
||||
glassFeatureQuestion: '',
|
||||
selectedTint: {}, // v-model for the list card selections
|
||||
};
|
||||
name: "glass-part-question",
|
||||
inheritAttrs: false,
|
||||
data() {
|
||||
return {
|
||||
glassColorQuestion: "",
|
||||
glassFeatureQuestion: "",
|
||||
selectedTint: {}, // v-model for the list card selections
|
||||
};
|
||||
},
|
||||
props: {
|
||||
glassName: String,
|
||||
glassLocation: String,
|
||||
colorAnswers: Array,
|
||||
modelValue: Object,
|
||||
},
|
||||
mounted() {
|
||||
this.LoadPreselectedValues();
|
||||
},
|
||||
components: {
|
||||
listCard,
|
||||
buttonQuestion,
|
||||
},
|
||||
computed: {
|
||||
colorQuestionText() {
|
||||
return getCustomTransformValue(
|
||||
this.glassColorQuestion,
|
||||
`${this.glassLocation} ${this.glassName}`
|
||||
);
|
||||
},
|
||||
props: {
|
||||
glassName: String,
|
||||
glassLocation: String,
|
||||
colorAnswers: Array,
|
||||
modelValue: Object
|
||||
|
||||
selectedPart: {
|
||||
get: function () {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
listCard,
|
||||
buttonQuestion,
|
||||
|
||||
// Creates a map of the feature list data in the correct Name/Value
|
||||
// format for the button-question component.
|
||||
featureListData() {
|
||||
const tintMapByColor = this.colorAnswers.reduce((arr, item) => {
|
||||
// Create the key
|
||||
const itemColor = item.ColorAnswerText;
|
||||
arr[itemColor] = arr[itemColor] || [];
|
||||
|
||||
// Map the data to Name/Text object for ButtonQuestion
|
||||
const mappedItem = item.FeatureAnswers.reduce((featureArr, item) => {
|
||||
featureArr["Text"] = item.FeatureAnswerText; // Display to User
|
||||
featureArr["Name"] = item.PartNumber; // Backing Value
|
||||
|
||||
return featureArr;
|
||||
}, {});
|
||||
|
||||
// Add onto the final object
|
||||
arr[itemColor].push(mappedItem);
|
||||
|
||||
return arr;
|
||||
}, {});
|
||||
|
||||
return tintMapByColor;
|
||||
},
|
||||
computed: {
|
||||
colorQuestionText() {
|
||||
return getCustomTransformValue(this.glassColorQuestion, `${this.glassLocation} ${this.glassName}`);
|
||||
},
|
||||
|
||||
selectedPart: {
|
||||
get: function () {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
},
|
||||
|
||||
// Creates a map of the feature list data in the correct Name/Value
|
||||
// format for the button-question component.
|
||||
featureListData() {
|
||||
const tintMapByColor = this.colorAnswers.reduce((arr, item) => {
|
||||
// Create the key
|
||||
const itemColor = item.ColorAnswerText;
|
||||
arr[itemColor] = arr[itemColor] || [];
|
||||
PartDataFromApi() {
|
||||
return this.$store.getters.pageData(this.$route.query.fmgPage);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// Initialize the component data
|
||||
initializeComponent(cmsContent) {
|
||||
this.glassColorQuestion = cmsContent.ColorQuestionWidget.QuestionText;
|
||||
this.glassFeatureQuestion = cmsContent.FeatureQuestionWidget.QuestionText;
|
||||
},
|
||||
|
||||
// Map the data to Name/Text object for ButtonQuestion
|
||||
const mappedItem = item.FeatureAnswers.reduce((featureArr, item) => {
|
||||
featureArr["Text"] = item.FeatureAnswerText; // Display to User
|
||||
featureArr["Name"] = item.PartNumber; // Backing Value
|
||||
// Gets tint images based on the glass type, and tint name.
|
||||
// Returns an empty string if the src or object is undefined.
|
||||
getTintSourceImage(glassLocation, tintColor) {
|
||||
const tintSourceObject = getTintImage(glassLocation, tintColor);
|
||||
|
||||
return featureArr;
|
||||
}, {});
|
||||
if (tintSourceObject === undefined || tintSourceObject.src == undefined) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Add onto the final object
|
||||
arr[itemColor].push(mappedItem);
|
||||
return tintSourceObject.src;
|
||||
},
|
||||
|
||||
return arr;
|
||||
}, {});
|
||||
|
||||
return tintMapByColor;
|
||||
// Reset selections when tint changes for the same glass to ensure proper selection.
|
||||
// Also checks if only a single part is present for the tint.
|
||||
ResetTintAndPartSelections() {
|
||||
this.selectedTint = {};
|
||||
this.selectedPart = {};
|
||||
this.AutoSelectIfSinglePart();
|
||||
},
|
||||
|
||||
// Check if only a single part is present for the tint and set the v-model if it is.
|
||||
AutoSelectIfSinglePart() {
|
||||
// Check if the selected tint only has a single feature
|
||||
Object.keys(this.PartDataFromApi.partsOrQuestions).forEach((key) => {
|
||||
const currentGlassSelection =
|
||||
this.PartDataFromApi.partsOrQuestions[key];
|
||||
|
||||
if (
|
||||
currentGlassSelection.glassName === this.glassName &&
|
||||
currentGlassSelection.glassLocation === this.glassLocation
|
||||
) {
|
||||
if (currentGlassSelection.parts.length === 1) {
|
||||
this.selectedPart = {
|
||||
[currentGlassSelection.glassLocation]: [
|
||||
currentGlassSelection.parts[0].partNumber,
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
initializeComponent(cmsContent) {
|
||||
this.glassColorQuestion = cmsContent.ColorQuestionWidget.QuestionText;
|
||||
this.glassFeatureQuestion = cmsContent.FeatureQuestionWidget.QuestionText;
|
||||
},
|
||||
|
||||
// Gets tint images based on the glass type, and tint name.
|
||||
// Returns an empty string if the src or object is undefined.
|
||||
getTintSourceImage(glassLocation, tintColor) {
|
||||
const tintSourceObject = getTintImage(glassLocation, tintColor);
|
||||
// Loads the preselected values from the store.
|
||||
LoadPreselectedValues() {
|
||||
this.$nextTick(() => {
|
||||
if (this.modelValue !== undefined) {
|
||||
// Populate button-question model-value if parts data already exists in VueX
|
||||
const alreadyPopulatedPartsData =
|
||||
this.$store.getters.lineItems.glassParts;
|
||||
|
||||
if (tintSourceObject === undefined || tintSourceObject.src == undefined) {
|
||||
return "";
|
||||
}
|
||||
Object.keys(alreadyPopulatedPartsData).forEach((key) => {
|
||||
const partNumber = alreadyPopulatedPartsData[key].partNumber;
|
||||
const tintColor = alreadyPopulatedPartsData[key].color;
|
||||
|
||||
return tintSourceObject.src;
|
||||
},
|
||||
|
||||
// Reset selections when tint changes for the same glass to ensure proper selection.
|
||||
ResetTintAndPartSelections() {
|
||||
this.selectedTint = {};
|
||||
this.selectedPart = {};
|
||||
Object.keys(this.modelValue).forEach((key) => {
|
||||
if (this.modelValue[key][0] === partNumber) {
|
||||
this.selectedTint[tintColor] = {
|
||||
buttonId: `${this.glassLocation}-${this.glassName}-${tintColor}`,
|
||||
checkValue: "",
|
||||
value: `${this.glassLocation}-${this.glassName}-${tintColor}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.nested-radio {
|
||||
.ui-radio {
|
||||
flex-direction: column;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
.ui-radio {
|
||||
flex-direction: column;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
p {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -8,13 +8,10 @@ import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass
|
|||
|
||||
// Supporting Files
|
||||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { nextTick } from "vue";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import store from "@/store";
|
||||
|
||||
// Mock our module for promises.
|
||||
|
|
@ -70,33 +67,8 @@ describe("vehicle-parts.vue", () => {
|
|||
|
||||
test("Page header is initialized with api data", async (done) => {
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce({
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassName: "Stationary",
|
||||
glassLocation: "Rear",
|
||||
parts: [
|
||||
{
|
||||
partNumber: "DB12209GTYN",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Green Tint",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
childParts: null
|
||||
},
|
||||
{
|
||||
partNumber: "DB12209YPYNOEM",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Gray Tint Privacy",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
childParts: null
|
||||
}
|
||||
],
|
||||
partQuestions: null
|
||||
}
|
||||
]
|
||||
});
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
const pageHeaderWidgetHeaderText = "Select Parts";
|
||||
const { wrapper, apiPromise } = setupMocks(
|
||||
|
|
@ -117,6 +89,7 @@ describe("vehicle-parts.vue", () => {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-parts" } },
|
||||
|
|
@ -139,6 +112,7 @@ describe("vehicle-parts.vue", () => {
|
|||
};
|
||||
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
const { wrapper, apiPromise } = setupMocks(
|
||||
{
|
||||
|
|
@ -181,6 +155,7 @@ describe("vehicle-parts.vue", () => {
|
|||
};
|
||||
|
||||
store.getters.pageData.mockReturnValue(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
const { wrapper, apiPromise } = setupMocks(
|
||||
{
|
||||
|
|
@ -219,6 +194,8 @@ describe("vehicle-parts.vue", () => {
|
|||
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
|
|
@ -250,10 +227,48 @@ describe("vehicle-parts.vue", () => {
|
|||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
|
||||
test("Initial data, should populate this.glassParts", async () => {
|
||||
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: { 0: { partNumber: 'DB12209YPYNOEM'} } }
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigateAfterSave: jest.fn()
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
fmgPage: 'vehicle-parts',
|
||||
}
|
||||
},
|
||||
store: {
|
||||
getters: store.getters
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.glassParts).toEqual({ "Rear-Stationary": { "Rear": ['DB12209YPYNOEM'] } });
|
||||
});
|
||||
|
||||
test("BackButtonAction triggers a router.navigate change", async () => {
|
||||
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
|
|
@ -290,6 +305,9 @@ describe("vehicle-parts.vue", () => {
|
|||
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: {} }
|
||||
|
||||
|
||||
store.commit = jest.fn();
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<hr v-if="i > 0" />
|
||||
</div>
|
||||
|
||||
<glassPartQuestion :ref="`${refPrefix}-${item.glassLocation}`" v-model="glassParts[item.glassLocation + '-' + item.glassName]" :glassLocation="item.glassLocation" :glassName="item.glassName" :colorAnswers="item.colorAnswers" />
|
||||
<glassPartQuestion :ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`" v-model="glassParts[item.glassLocation + '-' + item.glassName]" :glassLocation="item.glassLocation" :glassName="item.glassName" :colorAnswers="item.colorAnswers" />
|
||||
</div>
|
||||
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction" />
|
||||
</div>
|
||||
|
|
@ -75,7 +75,7 @@ export default {
|
|||
// Glass Part Question dynamic component
|
||||
|
||||
Object.keys(vm.$refs)
|
||||
.filter((r) => r.includes(vm.refPrefix) && vm.$refs[r][0] !== undefined)
|
||||
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
|
||||
.forEach((c) =>
|
||||
vm.$refs[c][0].initializeComponent({
|
||||
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
|
||||
|
|
@ -87,15 +87,13 @@ export default {
|
|||
// initializeComponent here.
|
||||
vm.alertWidgetData = {
|
||||
copy: resultMap.cmsContent.AlertWidget.BodyText,
|
||||
headline: resultMap.cmsContent.AlertWidget.HeadlineText
|
||||
headline: resultMap.cmsContent.AlertWidget.HeadlineText,
|
||||
};
|
||||
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
glassParts: {},
|
||||
refPrefix: "partQuestion",
|
||||
alertWidgetData: Object,
|
||||
};
|
||||
},
|
||||
|
|
@ -109,10 +107,11 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
PartsForQuestions() {
|
||||
const partsData = this.$store.getters.pageData(this.$route.query.fmgPage);
|
||||
const partsData = this.PartsFromApi;
|
||||
|
||||
// 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,
|
||||
glassLocation: g.glassLocation,
|
||||
|
|
@ -120,13 +119,13 @@ export default {
|
|||
arr.push({
|
||||
ColorAnswerText: p.color,
|
||||
FeatureAnswers: [{
|
||||
FeatureAnswerText: p.description === '' ? p.color : p.description,
|
||||
PartNumber: p.partNumber
|
||||
}]
|
||||
FeatureAnswerText: p.description === "" ? p.color : p.description,
|
||||
PartNumber: p.partNumber,
|
||||
}, ],
|
||||
});
|
||||
return arr;
|
||||
}, [])
|
||||
}
|
||||
}, []),
|
||||
};
|
||||
});
|
||||
|
||||
return mappedData;
|
||||
|
|
@ -134,23 +133,27 @@ export default {
|
|||
|
||||
PartsFromApi() {
|
||||
return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
||||
}
|
||||
},
|
||||
|
||||
RefPrefix() {
|
||||
return "partQuestion";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||
if (store.getters.damage.isRepair !== null && Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS)).length !== 0) {
|
||||
if (
|
||||
store.getters.damage.isRepair !== null &&
|
||||
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
|
||||
.length !== 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_BACK,
|
||||
this.$route
|
||||
);
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
const selectedGlassPartNumbers = [];
|
||||
|
|
@ -164,10 +167,15 @@ export default {
|
|||
}
|
||||
|
||||
// Match them to the parts from the API.
|
||||
for (let [key, value] of Object.entries(this.PartsFromApi.partsOrQuestions)) {
|
||||
for (let [key, value] of Object.entries(
|
||||
this.PartsFromApi.partsOrQuestions
|
||||
)) {
|
||||
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
||||
const currentPart = this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||
const isMatched = selectedGlassPartNumbers.some(p => p === currentPart.partNumber);
|
||||
const currentPart =
|
||||
this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||
const isMatched = selectedGlassPartNumbers.some(
|
||||
(p) => p === currentPart.partNumber
|
||||
);
|
||||
|
||||
if (isMatched) {
|
||||
matchedParts.push(currentPart);
|
||||
|
|
@ -184,9 +192,39 @@ export default {
|
|||
store.commit(storeMutations.UPDATE_PARTS, matchedParts);
|
||||
|
||||
// Navigate to the next page.
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_PARTS, this.$route);
|
||||
}
|
||||
this.$router.navigateAfterSave(
|
||||
this.navigationScenarios.SELECTED_PARTS,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
|
||||
resetDependentState() {
|
||||
// Nothing additional to reset here: The page save is already fully resetting all the line-items on the order
|
||||
},
|
||||
|
||||
LoadInitialPartsData() {
|
||||
const partsData = this.PartsFromApi;
|
||||
const alreadyPopulatedPartsData =
|
||||
this.$store.getters.lineItems.glassParts === null ? {} :
|
||||
this.$store.getters.lineItems.glassParts;
|
||||
|
||||
partsData.partsOrQuestions.map((g) => {
|
||||
// If the part is already populated, use the value from the store and populate the v-model.
|
||||
Object.keys(alreadyPopulatedPartsData).forEach((key) => {
|
||||
const partNumber = alreadyPopulatedPartsData[key].partNumber;
|
||||
g.parts.forEach((p) => {
|
||||
if (p.partNumber === partNumber) {
|
||||
this.glassParts[g.glassLocation + "-" + g.glassName] = {
|
||||
[g.glassLocation]: [partNumber],
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.LoadInitialPartsData();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -186,12 +186,7 @@ describe("vehicle-style.vue", () => {
|
|||
store: {
|
||||
commit: jest.fn(),
|
||||
getters: {
|
||||
vehicle: {
|
||||
year: 2020,
|
||||
make: "Honda",
|
||||
model: "Civic",
|
||||
style: "2 Door",
|
||||
},
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
actionList: [
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
|||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
|
||||
export default {
|
||||
name: "vehicle-style",
|
||||
|
|
@ -99,8 +100,11 @@ export default {
|
|||
},
|
||||
resetDependentState() {
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE);
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, null);
|
||||
store.commit(storeMutations.UPDATE_NUMBER_OF_CHIPS, null);
|
||||
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, []);
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -163,8 +163,8 @@ describe("vehicle-year.vue", () => {
|
|||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null)
|
||||
expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null)
|
||||
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES)
|
||||
expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES)
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -93,10 +93,11 @@ export default {
|
|||
store.commit(storeMutations.UPDATE_STYLE, null);
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, null);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
|
||||
|
||||
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE);
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
store.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -61,19 +61,14 @@ const routingTable = [
|
|||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
|
||||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
fmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -85,20 +80,7 @@ const routingTable = [
|
|||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_PARTS,
|
||||
destinationFmgPageValue: fmgPageValues.QUOTE,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
fmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ export const state = {
|
|||
glassToReplace: null,
|
||||
},
|
||||
lineItems:{
|
||||
glassParts: null,
|
||||
otherParts: null
|
||||
glassParts: {},
|
||||
otherParts: {}
|
||||
}
|
||||
},
|
||||
applicationUser: {
|
||||
|
|
@ -122,9 +122,8 @@ export const mutations = {
|
|||
|
||||
},
|
||||
resetPartsState(state) {
|
||||
state.glassParts = null;
|
||||
state.childParts = null;
|
||||
state.otherParts = null;
|
||||
state.order.lineItems.glassParts = null;
|
||||
state.order.lineItems.otherParts = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.fade-enter-active{
|
||||
transition: opacity 0.6s ease;
|
||||
.fade-enter-active {
|
||||
transition: opacity 0.8s ease;
|
||||
}
|
||||
|
||||
.fade-leave-active {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@
|
|||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
select {
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.75rem center;
|
||||
background-size: 16px 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
|
||||
:class="hasError ? 'has-error' : ''"
|
||||
@mouseup="handleClick(value)"
|
||||
@keyup.space="handleClick(value)"
|
||||
:class="[errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"
|
||||
>
|
||||
<input
|
||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
||||
|
|
@ -12,6 +10,7 @@
|
|||
:value="value"
|
||||
:aria-required="isRequired"
|
||||
:data-focus-target="groupName"
|
||||
@click="handleClick(value)"
|
||||
v-model="checkValue"
|
||||
@change="!selectingInitiatesLoad ? handleCheckChange() : ''"
|
||||
/>
|
||||
|
|
@ -46,7 +45,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from "vue";
|
||||
import { useField } from "vee-validate";
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
|
||||
|
|
@ -69,6 +67,7 @@ export default {
|
|||
type: String,
|
||||
default: "",
|
||||
},
|
||||
validationRules: String,
|
||||
selectedValues: [Array, String],
|
||||
hasError: Boolean,
|
||||
},
|
||||
|
|
@ -105,20 +104,25 @@ export default {
|
|||
loader,
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange, errorMessage } = useField(
|
||||
groupName,
|
||||
undefined,
|
||||
{
|
||||
type: inputType,
|
||||
checkedValue: value,
|
||||
}
|
||||
);
|
||||
const fieldOptions = {
|
||||
type: inputType,
|
||||
checkedValue: props.value,
|
||||
};
|
||||
|
||||
if (Array.isArray(props.selectedValues) && props.selectedValues.length == 1) {
|
||||
fieldOptions['initialValue'] = fieldOptions.checkedValue;
|
||||
}
|
||||
const {
|
||||
checked,
|
||||
handleChange,
|
||||
errors,
|
||||
} = useField(props.groupName, props.validationRules, fieldOptions);
|
||||
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
errorMessage,
|
||||
errors,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
@ -142,6 +146,7 @@ export default {
|
|||
&:checked + label {
|
||||
background: $blue-100;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
outline: none;
|
||||
z-index: 2;
|
||||
}
|
||||
&:checked + label p:first-child {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="list-group list-button d-flex flex-column w-100 mb-2"
|
||||
:class="hasError ? 'has-error' : ''"
|
||||
@mouseup="handleClick(value)"
|
||||
@keyup.space="handleClick(value)"
|
||||
:class="[errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"
|
||||
>
|
||||
<input
|
||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
||||
|
|
@ -12,6 +10,7 @@
|
|||
:value="value"
|
||||
:aria-required="isRequired"
|
||||
:data-focus-target="groupName"
|
||||
@click="handleClick(value)"
|
||||
v-model="checkValue"
|
||||
@change="!selectingInitiatesLoad ? handleCheckChange() : ''"
|
||||
>
|
||||
|
|
@ -49,9 +48,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { toRefs } from "vue";
|
||||
import { useField } from "vee-validate";
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
|
||||
export default {
|
||||
name: "listButton",
|
||||
props: {
|
||||
|
|
@ -71,6 +70,7 @@ export default {
|
|||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
validationRules: String,
|
||||
selectedValues: [Array, String],
|
||||
hasError: Boolean,
|
||||
},
|
||||
|
|
@ -107,20 +107,25 @@ export default {
|
|||
loader,
|
||||
},
|
||||
setup(props) {
|
||||
const { groupName, value } = toRefs(props);
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
const { checked, handleChange, errorMessage } = useField(
|
||||
groupName,
|
||||
undefined,
|
||||
{
|
||||
type: inputType,
|
||||
checkedValue: value,
|
||||
}
|
||||
);
|
||||
const fieldOptions = {
|
||||
type: inputType,
|
||||
checkedValue: props.value,
|
||||
};
|
||||
|
||||
if (Array.isArray(props.selectedValues) && props.selectedValues.length == 1) {
|
||||
fieldOptions['initialValue'] = fieldOptions.checkedValue;
|
||||
}
|
||||
const {
|
||||
checked,
|
||||
handleChange,
|
||||
errors,
|
||||
} = useField(props.groupName, props.validationRules, fieldOptions);
|
||||
|
||||
return {
|
||||
checked,
|
||||
handleChange,
|
||||
errorMessage,
|
||||
errors,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ describe("list-card.vue", () => {
|
|||
});
|
||||
wrapper.vm.handleCheckChange();
|
||||
// Assert
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean, buttonId: 'list-card-id'}]);
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: [Boolean, String], buttonId: 'list-card-id'}]);
|
||||
});
|
||||
|
||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@
|
|||
<div :class="'col' + colLength">
|
||||
<div
|
||||
class="list-card w-100 rounded-3 d-flex align-items-center h-100"
|
||||
:class="[isWide ? 'horizontal' : '', errors.length > 0 ? 'has-error' : '', hasError ? 'has-error' : '']"
|
||||
:class="[
|
||||
isWide ? 'horizontal' : '',
|
||||
errors.length > 0 ? 'has-error' : '',
|
||||
hasError ? 'has-error' : '',
|
||||
]"
|
||||
>
|
||||
<input
|
||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
||||
|
|
@ -13,12 +17,13 @@
|
|||
:data-focus-target="groupName"
|
||||
@click="handleChange(value)"
|
||||
v-model="checkValue"
|
||||
@change="handleCheckChange()"
|
||||
@change="handleCheckChange(value)"
|
||||
/>
|
||||
<label
|
||||
:for="buttonID"
|
||||
class="d-flex w-100 align-items-center px-2 h-100"
|
||||
:class="getLabelClasses" tabindex="-1"
|
||||
:class="getLabelClasses"
|
||||
tabindex="-1"
|
||||
>
|
||||
<img
|
||||
:id="buttonImageId"
|
||||
|
|
@ -31,10 +36,13 @@
|
|||
class="small order-3"
|
||||
:class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"
|
||||
>
|
||||
{{buttonLabel}}
|
||||
{{ buttonLabel }}
|
||||
</p>
|
||||
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4 sub-copy">
|
||||
{{buttonLabelSubCopy}}
|
||||
<p
|
||||
v-if="buttonLabelSubCopy && !isWide"
|
||||
class="fs-7 m-0 order-4 sub-copy"
|
||||
>
|
||||
{{ buttonLabelSubCopy }}
|
||||
</p>
|
||||
<div v-if="isWide" class="order-2">
|
||||
<p class="m-0 small">{{ buttonLabel }}</p>
|
||||
|
|
@ -70,18 +78,30 @@ export default {
|
|||
colLength: String,
|
||||
validationRules: String,
|
||||
selectedValues: [Array, String],
|
||||
modelValue: Object,
|
||||
hasError: Boolean,
|
||||
},
|
||||
data(){
|
||||
data() {
|
||||
return {
|
||||
checkValue: Boolean,
|
||||
checkValue: [Boolean, String],
|
||||
}
|
||||
},
|
||||
created(){
|
||||
if(Array.isArray(this.selectedValues)){
|
||||
this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0];
|
||||
created() {
|
||||
if (Array.isArray(this.selectedValues)) {
|
||||
this.checkValue = this.isMultiSelect
|
||||
? this.selectedValues.includes(this.value)
|
||||
: this.selectedValues[0];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// Changing this will impact pre-selection data loads on vehicle-parts.
|
||||
// If changed, please regression test that vehicle-parts data still loads correctly with previous selections.
|
||||
modelValue(newVal) {
|
||||
if (newVal !== undefined) {
|
||||
this.checkValue = newVal.value;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
getLabelClasses() {
|
||||
if (this.isWide) {
|
||||
|
|
@ -96,26 +116,36 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
handleCheckChange(newValue, oldValue){
|
||||
const isInitialization = typeof(oldValue) === 'function';
|
||||
handleCheckChange(newValue, oldValue) {
|
||||
const isInitialization = typeof oldValue === "function";
|
||||
if (!isInitialization) {
|
||||
const emitEvent = { checkValue: this.checkValue, value: this.value.toString(), buttonId: this.buttonID.toString() };
|
||||
this.$emit('isCheckedChanged', emitEvent);
|
||||
this.$emit('update:modelValue', emitEvent);
|
||||
const emitEvent = {
|
||||
checkValue: this.checkValue,
|
||||
value: this.value.toString(),
|
||||
buttonId: this.buttonID.toString(),
|
||||
};
|
||||
this.$emit("isCheckedChanged", emitEvent);
|
||||
this.$emit("update:modelValue", emitEvent);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
const {
|
||||
value: inputValue,
|
||||
handleChange,
|
||||
errors,
|
||||
} = useField(props.groupName, props.validationRules,
|
||||
{
|
||||
|
||||
const fieldOptions = {
|
||||
type: inputType,
|
||||
checkedValue: props.value,
|
||||
});
|
||||
};
|
||||
|
||||
if (Array.isArray(props.selectedValues) && props.selectedValues.length == 1) {
|
||||
fieldOptions['initialValue'] = fieldOptions.checkedValue;
|
||||
}
|
||||
|
||||
const {
|
||||
handleChange,
|
||||
errors,
|
||||
} = useField(props.groupName, props.validationRules, fieldOptions);
|
||||
|
||||
return {
|
||||
handleChange,
|
||||
errors,
|
||||
|
|
@ -127,10 +157,12 @@ export default {
|
|||
<style lang="scss">
|
||||
.list-card {
|
||||
border: 1px solid $gray-500;
|
||||
|
||||
&.invalid {
|
||||
//Red border if invalid
|
||||
border: 1px solid $red;
|
||||
}
|
||||
|
||||
img {
|
||||
// svg's should be constructed on the same canvas size/viewbox to ensure they occupy the same space in the DOM. This will allow easy/proper alignment of elements. See exisitng svg's for examples.
|
||||
height: auto;
|
||||
|
|
@ -138,50 +170,61 @@ export default {
|
|||
margin-bottom: 3rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include media-breakpoint-up(sm) {
|
||||
box-shadow: 0px 0px 0px 4px $blue-300;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
+ label {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $gray-600;
|
||||
text-align: center;
|
||||
|
||||
&.sub-copy {
|
||||
color: $gray-550;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus + label {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
&:checked + label {
|
||||
background: $blue-100;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
&:checked + label {
|
||||
p {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sub-copy {
|
||||
color: $gray-600;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
+ label::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
@ -197,15 +240,19 @@ export default {
|
|||
flex-shrink: 0;
|
||||
color: $gray-600;
|
||||
}
|
||||
|
||||
+ label.checkboxTop::before {
|
||||
margin: -1.25rem 0.5rem 0 0 !important;
|
||||
}
|
||||
|
||||
+ label.checkboxTop::after {
|
||||
margin: -1.5rem 0.5rem 0 0 !important;
|
||||
}
|
||||
|
||||
&:checked + label::before {
|
||||
background: $blue;
|
||||
}
|
||||
|
||||
&:checked + label::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
@ -217,26 +264,31 @@ export default {
|
|||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
+ label::before {
|
||||
content: "";
|
||||
display: none;
|
||||
}
|
||||
|
||||
+ label::after {
|
||||
content: "";
|
||||
display: none;
|
||||
}
|
||||
|
||||
+ label {
|
||||
img {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.horizontal {
|
||||
img {
|
||||
margin-bottom: 0;
|
||||
width: 5.5rem;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
+ label::before {
|
||||
|
|
@ -245,30 +297,37 @@ export default {
|
|||
margin: 0 0.5rem 0 0;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
&:checked + label::after {
|
||||
content: "";
|
||||
margin: -0.25rem 0 0 0;
|
||||
left: 0.875rem;
|
||||
}
|
||||
|
||||
&:checked + label {
|
||||
p {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sub-copy {
|
||||
color: $gray-600;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
+ label {
|
||||
min-height: 48px;
|
||||
color: $gray-600;
|
||||
|
||||
img {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $gray-600;
|
||||
text-align: left;
|
||||
|
||||
&.sub-copy {
|
||||
color: $gray-550;
|
||||
}
|
||||
|
|
@ -277,4 +336,4 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ describe("radio.vue", () => {
|
|||
});
|
||||
wrapper.vm.handleCheckChange();
|
||||
// Assert
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: Boolean}]);
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: false}]);
|
||||
});
|
||||
|
||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ export default {
|
|||
created() {
|
||||
if (this.selectedValues) {
|
||||
this.checkValue = this.selectedValues[0] === this.value;
|
||||
}else{
|
||||
this.checkValue = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
a {
|
||||
display: inline-flex !important;
|
||||
color: $blue;
|
||||
text-underline-offset: 0.5em;
|
||||
line-height: 26px;
|
||||
|
|
@ -42,7 +43,6 @@ a {
|
|||
&.navigation-link {
|
||||
color: $black;
|
||||
line-height: 26px;
|
||||
display: inline-flex;
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue