Merge branch 'develop' into feature/CSR-335

This commit is contained in:
FrankRua 2022-02-24 08:48:16 -05:00
commit 64bcb2d395
7 changed files with 226 additions and 80 deletions

View file

@ -4,7 +4,7 @@
<span :class="[buttonType === 'radio' ? ['mb-0', 'text-left', 'mt-2' ] : ['mb-4', 'mt-6', 'text-center'], 'fs-6 fw-bold w-100' ]">{{ questionText }}</span> <span :class="[buttonType === 'radio' ? ['mb-0', 'text-left', 'mt-2' ] : ['mb-4', 'mt-6', 'text-center'], 'fs-6 fw-bold w-100' ]">{{ questionText }}</span>
</div> </div>
<div class="w-100 d-flex justify-content-center"> <div class="w-100 d-flex justify-content-center">
<fieldset class="w-100" :class="getFieldSetClasses" role="radiogroup" :aria-labelledby="groupName ? groupName + '-radio-group' : ''"> <fieldset class="w-100" :class="getFieldSetClasses" :role="isMultiSelect ? 'group' : 'radiogroup'" :aria-labelledby="groupName ? groupName + '-radio-group' : ''">
<legend class="sr-only">{{groupName}}</legend> <legend class="sr-only">{{groupName}}</legend>
<div :class="getComponentWrapperClasses"> <div :class="getComponentWrapperClasses">
<component <component

View file

@ -2,7 +2,7 @@
<div class="dropdown-question"> <div class="dropdown-question">
<label :for="inputId" class="form-label">{{labelText}}</label> <label :for="inputId" class="form-label">{{labelText}}</label>
<select class="form-select" aria-label="Default select example" :id="inputId" :aria-disabled="isDisabled" :disabled="isDisabled" :aria-required="isRequired"> <select class="form-select" aria-label="Default select example" :id="inputId" :aria-disabled="isDisabled" :disabled="isDisabled" :aria-required="isRequired">
<option selected>Open this select menu</option> <option selected>Placeholder</option>
<option value="1">One</option> <option value="1">One</option>
<option value="2">Two</option> <option value="2">Two</option>
<option value="3">Three</option> <option value="3">Three</option>
@ -30,6 +30,7 @@ export default {
margin-bottom: .25rem; margin-bottom: .25rem;
} }
.form-select { .form-select {
color: $gray-500;
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='%231474a2'/%3e%3c/svg%3e"); 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='%231474a2'/%3e%3c/svg%3e");
border: 1px solid $gray-500; border: 1px solid $gray-500;
border-radius: .5rem; border-radius: .5rem;
@ -46,7 +47,6 @@ export default {
&.disabled { &.disabled {
background-color: $gray-100; background-color: $gray-100;
filter: grayscale(100%); filter: grayscale(100%);
opacity: .65;
&:hover { &:hover {
box-shadow: 0 0 0 4px transparent; box-shadow: 0 0 0 4px transparent;
border: 1px solid $gray-500; border: 1px solid $gray-500;

View file

@ -1,76 +1,124 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question"; import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import store from "@/store"; import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true}); jest.mock("@/store", () => { return {}; }, {virtual: true});
describe("replace-options-question.vue", () => { describe("replace-options-question.vue", () => {
test("Selected damage option is emitted upon selection.", async () => { test("Selected damage option is emitted upon selection.", async () => {
//Arrange
const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] });
const damageToSelect = ["Backseat"];
//Act
wrapper.setValue({ modelValue: damageToSelect });
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]);
});
});
describe("replace-options-question.vue", () => { //Arrange
test("Answers to display filtered by data from api.", async () => { const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] });
const damageToSelect = ["Backseat"];
//Arrange
const { wrapper, cmsContent, replaceOptions } = setupMocks({ dataFromStoreApi: ["Windshield", "FrontDoor"], filterByVehicleCategory: true});
//Act
replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group");
//Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'Windshield' }, { Name: 'FrontDoor' } ])
});
});
function setupMocks({ //Act
modelValueProp = ["Windshield"], wrapper.setValue({ modelValue: damageToSelect });
isAvailale = true, await wrapper.vm.$nextTick();
isMultiSelect = false,
filterByVehicleCategory = false, //Assert
groupName = "damageQuestion", expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]);
cmsQuestionText = "CMS text goes here", });
cmsAnswers = [{Name: "car-Windshield"}, {Name: "car-BackDoor"}, {Name: "car-FrontDoor"}], });
dataFromStoreApi = [],
}) { describe("replace-options-question.vue", () => {
test("Answers to display filtered by data from api.", async () => {
//Mock store
store.dispatch = jest.fn(() => dataFromStoreApi); //Arrange
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} }; const { wrapper, cmsContent, replaceOptions } = setupMocks({ dataFromStoreApi: ["Windshield", "FrontDoor"], filterByVehicleCategory: true});
const mountOptions = getMountOptions({
store: { //Act
dispatch: store.dispatch, replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group");
getters: store.getters,
//Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'Windshield' }, { Name: 'FrontDoor' } ])
});
});
describe("replace-options-question.vue", () => {
test("when updateSelectedValues method is called with a single answerToDisplay it will call to update this.selectedValues", async () => {
//Arrange
const { wrapper, cmsContent, replaceOptions
} = setupMocks({
modelValueProp: [],
});
wrapper.setData({answersFromCms: [
{
"Name": "Stationary",
}, },
}); {
"Name": "Slider",
}
]});
wrapper.setData({replaceOptions: ["Stationary"]});
//Act
wrapper.vm.$options.methods.updateSelectedValues.call(wrapper.vm);
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([['Stationary']]);
});
});
describe("replace-options-question.vue", () => {
test("when isAvailable is true, will run updateSelectedValues method", async () => {
//Arrange
const { wrapper, cmsContent, replaceOptions } = setupMocks({ isAvailable: false, methodsToMock: ["updateSelectedValues"] });
//Act
wrapper.vm.$options.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group");
wrapper.vm.$options.watch.isAvailable.call(wrapper.vm, true);
//Assert
expect(replaceOptionsQuestion.methods.updateSelectedValues).toHaveBeenCalled();
wrapper.unmount();
});
});
function setupMocks({
modelValueProp = ["Windshield"],
isAvailable = true,
isMultiSelect = false,
filterByVehicleCategory = false,
groupName = "damageQuestion",
cmsQuestionText = "CMS text goes here",
cmsAnswers = [{Name: "car-Windshield"}, {Name: "car-BackDoor"}, {Name: "car-FrontDoor"}],
dataFromStoreApi = [],
methodsToMock = [],
}) {
//Mock props //Mock store
mountOptions.propsData = { store.dispatch = jest.fn(() => dataFromStoreApi);
modelValue: modelValueProp, store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
isAvailable: isAvailale, const mountOptions = getMountOptions({
isMultiSelect: isMultiSelect, store: {
filterByVehicleCategory: filterByVehicleCategory dispatch: store.dispatch,
}; getters: store.getters,
},
const wrapper = shallowMount(replaceOptionsQuestion, mountOptions); });
//Mock props
mountOptions.propsData = {
modelValue: modelValueProp,
isAvailable: isAvailable,
isMultiSelect: isMultiSelect,
filterByVehicleCategory: filterByVehicleCategory
};
//Mock methods
methodsToMock.forEach((methodName) => {
replaceOptionsQuestion.methods[methodName] = jest.fn();
});
//Mock CMS content const wrapper = shallowMount(replaceOptionsQuestion, mountOptions);
const cmsContent = {
groupName: groupName, //Mock CMS content
QuestionText: cmsQuestionText, const cmsContent = {
Answers: cmsAnswers, groupName: groupName,
}; QuestionText: cmsQuestionText,
const replaceOptions = dataFromStoreApi; Answers: cmsAnswers,
return { wrapper, cmsContent, replaceOptions }; };
} const replaceOptions = dataFromStoreApi;
return { wrapper, cmsContent, replaceOptions };
}

View file

@ -4,7 +4,7 @@
<buttonQuestion <buttonQuestion
isWide isWide
:questionText="questionText" :questionText="questionText"
isMultiSelect :isMultiSelect="isMultiSelect"
:answers="answersToDisplay" :answers="answersToDisplay"
:groupName="groupName" :groupName="groupName"
buttonType="listCard" buttonType="listCard"
@ -42,6 +42,7 @@ export default ({
filterByVehicleCategory: Boolean, filterByVehicleCategory: Boolean,
groupName: String, groupName: String,
modelValue: Array, modelValue: Array,
isMultiSelect: Boolean,
}, },
methods: { methods: {
initializeComponent(cmsContent, replaceOptions){ initializeComponent(cmsContent, replaceOptions){
@ -49,6 +50,12 @@ export default ({
this.answersFromCms = cmsContent.Answers; this.answersFromCms = cmsContent.Answers;
this.replaceOptions = replaceOptions; this.replaceOptions = replaceOptions;
}, },
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: { computed: {
selectedValues: { selectedValues: {
@ -75,11 +82,10 @@ export default ({
}); });
}, },
}, },
mounted(){ watch: {
if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1 && this.selectedValues) { isAvailable(val) {
const newSelectedValues = this.selectedValues; // CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE
newSelectedValues.push(this.answersToDisplay[0].Name); val && this.updateSelectedValues();
this.selectedValues = newSelectedValues;
} }
}, },
components: { components: {

View file

@ -7,6 +7,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options"; import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question"; import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options"; import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
// Supporting Files // Supporting Files
import { settleAllPromises } from "@/helpers/layout-helper.js"; import { settleAllPromises } from "@/helpers/layout-helper.js";
@ -166,9 +167,57 @@ describe("vehicle-damage.vue", () => {
}); });
}); });
describe("vehicle-damage.vue", () => {
test("BackButtonAction triggers a router.navigate change", async () => {
//Arrange
const { wrapper } = setupMocks({});
//Act
vehicleDamage.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-damage" } },
undefined,
(c) => c(wrapper.vm)
);
wrapper.vm.backButtonAction();
//Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
});
});
describe("vehicle-damage.vue", () => {
test("isRearWindowDamageLocation is true if Rear Window damage is selected", async () => {
//Arrange
const { wrapper } = setupMocks({});
//Act
vehicleDamage.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-damage" } },
undefined,
(c) => c(wrapper.vm)
);
wrapper.vm.selectedDamageLocations = ["RearWindow"];
//Assert
expect(wrapper.vm.isRearWindowDamageLocation).toEqual(true);
});
});
function setupMocks({ function setupMocks({
pageHeaderWidgetHeaderText = {}, pageHeaderWidgetHeaderText = {},
mountOptionsMockData = {}, mountOptionsMockData = {
router: {
navigate: jest.fn(),
},
},
}) { }) {
//Mock api responses //Mock api responses
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn();
@ -194,6 +243,9 @@ function setupMocks({
windshieldOptions: { windshieldOptions: {
availableReplacementOptions: ["Single", "Driver", "Passenger"], availableReplacementOptions: ["Single", "Driver", "Passenger"],
}, },
backGlassOptions: {
availableReplacementOptions: ["Front", "Back", "Side"],
},
}, },
}; };
@ -227,6 +279,10 @@ function setupMocks({
initializeComponent: jest.fn(), initializeComponent: jest.fn(),
}; };
replaceOptionsQuestion.methods = {
initializeComponent: jest.fn(),
};
funnelFooter.methods = { funnelFooter.methods = {
initializeComponent: jest.fn(), initializeComponent: jest.fn(),
} }
@ -259,6 +315,12 @@ function setupMocks({
funnelSubHeaderWrapper.vm.initializeComponent = funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent; funnelSubHeader.methods.initializeComponent;
const backGlassOptionsWrapper = wrapper.findComponent({
name: "replaceOptionsQuestion",
});
backGlassOptionsWrapper.vm.initializeComponent =
replaceOptionsQuestion.methods.initializeComponent;
const damageLocationQuestionWrapper = wrapper.findComponent({ const damageLocationQuestionWrapper = wrapper.findComponent({
name: "damageLocationQuestion", name: "damageLocationQuestion",
}); });

View file

@ -3,12 +3,28 @@
<funnelHeader ref="funnelHeader" /> <funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false /> <vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
<funnelSubHeader ref="funnelSubHeader" /> <funnelSubHeader ref="funnelSubHeader" />
<damageLocationQuestion ref="damageLocation" v-model="selectedDamageLocations" groupName="DamageLocationQuestion" /> <damageLocationQuestion
<windshieldOptions ref="windshieldOptions" ref="damageLocation"
v-model="selectedDamageLocations"
groupName="DamageLocationQuestion"
/>
<windshieldOptions
ref="windshieldOptions"
v-model="selectedWindshieldOptions" v-model="selectedWindshieldOptions"
:selectedDamageLocations="selectedDamageLocations" :selectedDamageLocations="selectedDamageLocations"
/> />
<sideDoorOptions ref="sideDoorOptions" groupName="SideDoorSideQuestion" v-model="sideDoorOptionsData" :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" /> <funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
</div> </div>
</template> </template>
@ -22,6 +38,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options"; import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question"; import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options"; import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -77,11 +94,22 @@ export default {
resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion, resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion,
resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions
); );
vm.$refs.backGlassOptions.initializeComponent(
resultMap.cmsContent.RearReplaceOptionsQuestion, resultMap.damageOptions.backGlassOptions.availableReplacementOptions
);
vm.$refs.funnelFooter.initializeComponent( vm.$refs.funnelFooter.initializeComponent(
resultMap.cmsContent.FunnelFooterWidget resultMap.cmsContent.FunnelFooterWidget
); );
}); });
}, },
computed: {
isRearWindowDamageLocation() {
return this.selectedDamageLocations.some(selectedDamages =>
{
return selectedDamages.toUpperCase() === "REARWINDOW";
});
},
},
data(){ data(){
return { return {
selectedDamageLocations: [], selectedDamageLocations: [],
@ -90,7 +118,8 @@ export default {
selectedDriverSideReplacOptions: [], selectedDriverSideReplacOptions: [],
selectedPassengerSideReplaceOptions: [] selectedPassengerSideReplaceOptions: []
}, },
selectedWindshieldOptions: [] selectedWindshieldOptions: [],
selectedRearReplaceOptions: [],
} }
}, },
methods: { methods: {
@ -117,6 +146,7 @@ export default {
sideDoorOptions, sideDoorOptions,
damageLocationQuestion, damageLocationQuestion,
windshieldOptions, windshieldOptions,
replaceOptionsQuestion,
}, },
}; };
</script> </script>

View file

@ -1,10 +1,10 @@
.has-error { .has-error {
&.list-button, &.list-button,
&.list-card { &.list-card {
border: 1px solid transparent; border: 1px solid $red;
color: $red; color: $red;
label { label {
border: 1px solid $red; box-shadow: 0 0 1px $red;
border-radius: .5rem; border-radius: .5rem;
} }
} }