commit
5a3850948a
12 changed files with 490 additions and 128 deletions
|
|
@ -19,3 +19,67 @@ describe("buttonQuestion.vue", () => {
|
||||||
expect(wrapper.props().modelValue).toBe("2020");
|
expect(wrapper.props().modelValue).toBe("2020");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("buttonQuestion.vue", () => {
|
||||||
|
it("Should show overflow classes on fieldset if isOverflowScrollable is true", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(buttonQuestion);
|
||||||
|
await wrapper.setProps({
|
||||||
|
isOverflowScrollable: true,
|
||||||
|
});
|
||||||
|
// Assert
|
||||||
|
const fieldSet = wrapper.find('fieldset');
|
||||||
|
expect(fieldSet.classes()).toContain("overflow-scroll");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buttonQuestion.vue", () => {
|
||||||
|
it("Should show appropriate classes for button type", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(buttonQuestion);
|
||||||
|
await wrapper.setProps({
|
||||||
|
buttonType: "listCard",
|
||||||
|
});
|
||||||
|
// Assert
|
||||||
|
const Div = wrapper.find('fieldset div');
|
||||||
|
expect(Div.classes()).toContain("row");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buttonQuestion.vue", () => {
|
||||||
|
it("Should trigger event modelValue change on select", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(buttonQuestion);
|
||||||
|
await wrapper.setData({
|
||||||
|
modelValueAnswers: ["Windshield"],
|
||||||
|
chosenAnswer: "Windshield"
|
||||||
|
});
|
||||||
|
wrapper.setValue({ answer: wrapper.vm.chosenAnswer });
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{answer: "Windshield"}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buttonQuestion.vue", () => {
|
||||||
|
it("Should trigger event modelValue change with an array of string values on select if checked is true and multiple options are chosen", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(buttonQuestion);
|
||||||
|
await wrapper.setData({
|
||||||
|
modelValueAnswers: ["Windshield", "BackDoor"]
|
||||||
|
});
|
||||||
|
wrapper.vm.isItemChecked(true);
|
||||||
|
// Assert
|
||||||
|
expect(typeof wrapper.emitted()["update:modelValue"][0]).toEqual('object');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buttonQuestion.vue", () => {
|
||||||
|
it("Should not trigger event modelValue change on select if checked is false", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(buttonQuestion);
|
||||||
|
wrapper.vm.isItemChecked(false);
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([undefined]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -4,40 +4,37 @@
|
||||||
<span class="text-center fs-6 fw-bold w-100">{{ questionText }}</span>
|
<span class="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
|
<fieldset class="w-100" :class="getFieldSetClasses" role="radiogroup" :aria-labelledby="groupName ? groupName + '-radio-group' : ''">
|
||||||
class="w-100"
|
<legend class="sr-only">{{groupName}}</legend>
|
||||||
:class="getFieldSetClasses"
|
<div :class="getComponentWrapperClasses">
|
||||||
role="radiogroup"
|
<component
|
||||||
:aria-labelledby="groupName ? groupName + '-radio-group' : ''"
|
:is="buttonType"
|
||||||
>
|
v-for="answer in answers"
|
||||||
<legend class="sr-only">{{ groupName }}</legend>
|
:key="answer.Name ? answer.Name : answer"
|
||||||
<div :class="getComponentWrapperClasses">
|
@mouseup="chooseAnswer(answer.Name ? answer.Name : answer)"
|
||||||
<component
|
@keyup.space="chooseAnswer(answer.Name ? answer.Name : answer)"
|
||||||
:is="buttonType"
|
@isChecked="isItemChecked"
|
||||||
v-for="answer in answers"
|
:buttonID="answer.Name ? answer.Name : answer"
|
||||||
:key="answer"
|
:value="answer.Name ? answer.Name : answer"
|
||||||
@mouseup="chooseAnswer(answer.Name ? answer.Name : answer)"
|
:buttonLabel="answer.Text ? answer.Text : answer"
|
||||||
@keyup.space="chooseAnswer(answer.Name ? answer.Name : answer)"
|
:buttonLabelSubCopy="answer.SubText"
|
||||||
:buttonID="answer.Name ? answer.Name : answer"
|
:textPosition="textPosition"
|
||||||
:buttonLabel="answer.Text ? answer.Text : answer"
|
:isMultiSelect="isMultiSelect"
|
||||||
:buttonLabelSubCopy="answer.SubText"
|
:groupName="groupName"
|
||||||
:textPosition="textPosition"
|
:loaderEnabled="loaderEnabled"
|
||||||
:isMultiSelect="isMultiSelect"
|
:loaderColor="loaderColor"
|
||||||
:groupName="groupName"
|
:loaderPosition="loaderPosition"
|
||||||
:loaderEnabled="loaderEnabled"
|
:sizeInRem="sizeInRem"
|
||||||
:loaderColor="loaderColor"
|
:isWide="isWide"
|
||||||
:loaderPosition="loaderPosition"
|
:isRequired="isRequired"
|
||||||
:sizeInRem="sizeInRem"
|
:buttonImage="answer.AnswerImageUrl"
|
||||||
:isWide="isWide"
|
:buttonImageId="answer.ImageId"
|
||||||
:isRequired="isRequired"
|
:altText="answer.Name ? answer.Name : answer"
|
||||||
:buttonImage="answer.AnswerImageUrl"
|
screenReaderOnlyText="(opens new window)"
|
||||||
:buttonImageId="answer.ImageId"
|
:colLength="this.answers.length < 3 ? '' : '-4'"
|
||||||
:altText="answer.Name ? answer.Name : answer"
|
data-test="button"
|
||||||
screenReaderOnlyText="(opens new window)"
|
/>
|
||||||
:value="modelValue"
|
</div>
|
||||||
data-test="button"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -79,7 +76,7 @@ export default {
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
isOverflowScrollable: Boolean,
|
isOverflowScrollable: Boolean,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
modelValue: String,
|
modelValue: [Array, String],
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getFieldSetClasses() {
|
getFieldSetClasses() {
|
||||||
|
|
@ -96,16 +93,34 @@ export default {
|
||||||
case "listButtonHorizontal":
|
case "listButtonHorizontal":
|
||||||
classes = "d-flex flex-row p-0";
|
classes = "d-flex flex-row p-0";
|
||||||
break;
|
break;
|
||||||
case "listCard":
|
case 'listCard':
|
||||||
classes = "row g-2";
|
classes = 'row justify-content-center g-2'
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
modelValueAnswers: [],
|
||||||
|
chosenAnswer: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
if(Array.isArray(this.answers) && this.answers.length === 1) {
|
||||||
|
const onlyAnswer = typeof(this.answers[0]) === 'object' ? this.answers[0].Name : this.answers[0];
|
||||||
|
this.$emit("update:modelValue", onlyAnswer);
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
chooseAnswer(answer) {
|
chooseAnswer(answer) {
|
||||||
this.$emit("update:modelValue", answer);
|
this.chosenAnswer = answer;
|
||||||
|
},
|
||||||
|
isItemChecked(val) {
|
||||||
|
// Add or remove item to array of data to emit
|
||||||
|
val ? this.modelValueAnswers.push(this.chosenAnswer) : this.modelValueAnswers.splice(this.modelValueAnswers.indexOf(this.chosenAnswer), 1);
|
||||||
|
|
||||||
|
this.$emit("update:modelValue", this.modelValueAnswers.length ? this.modelValueAnswers : undefined);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import store from "@/store";
|
||||||
|
jest.mock("@/store", () => { return {}; }, {virtual: true});
|
||||||
|
|
||||||
|
describe("replace-options-question.vue", () => {
|
||||||
|
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", () => {
|
||||||
|
test("Answers to display filtered by data from api.", async () => {
|
||||||
|
|
||||||
|
//Arrange
|
||||||
|
const { wrapper, cmsContent, replaceOptions } = setupMocks({ dataFromStoreApi: ["Windshield", "FrontDoor"]});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group");
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'car-Windshield' }, { Name: 'car-FrontDoor' } ])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({
|
||||||
|
modelValueProp = ["Windshield"],
|
||||||
|
isAvailale = true,
|
||||||
|
isMultiSelect = false,
|
||||||
|
filterByVehicleCategory = false,
|
||||||
|
groupName = "damageQuestion",
|
||||||
|
cmsQuestionText = "CMS text goes here",
|
||||||
|
cmsAnswers = [{Name: "car-Windshield"}, {Name: "car-BackDoor"}, {Name: "car-FrontDoor"}],
|
||||||
|
dataFromStoreApi = [],
|
||||||
|
}) {
|
||||||
|
|
||||||
|
//Mock store
|
||||||
|
store.dispatch = jest.fn(() => dataFromStoreApi);
|
||||||
|
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
|
||||||
|
const mountOptions = getMountOptions({
|
||||||
|
store: {
|
||||||
|
dispatch: store.dispatch,
|
||||||
|
getters: store.getters,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//Mock props
|
||||||
|
mountOptions.propsData = {
|
||||||
|
modelValue: modelValueProp,
|
||||||
|
isAvailable: isAvailale,
|
||||||
|
isMultiSelect: isMultiSelect,
|
||||||
|
filterByVehicleCategory: filterByVehicleCategory
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = shallowMount(replaceOptionsQuestion, mountOptions);
|
||||||
|
|
||||||
|
//Mock CMS content
|
||||||
|
const cmsContent = {
|
||||||
|
groupName: groupName,
|
||||||
|
QuestionText: cmsQuestionText,
|
||||||
|
Answers: cmsAnswers,
|
||||||
|
};
|
||||||
|
const replaceOptions = dataFromStoreApi;
|
||||||
|
return { wrapper, cmsContent, replaceOptions };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<transition name="fade">
|
||||||
|
<div class="replace-options-question">
|
||||||
|
<buttonQuestion
|
||||||
|
v-if="isAvailable && answersToDisplay.length > 1"
|
||||||
|
:questionText="questionText"
|
||||||
|
:isMultiSelect="isMultiSelect"
|
||||||
|
:answers="answersToDisplay"
|
||||||
|
:groupName="groupName"
|
||||||
|
buttonType="listCard"
|
||||||
|
v-model="modelValue"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
name: "replaceOptionsQuestion",
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
questionText: String,
|
||||||
|
answersFromCms: Array,
|
||||||
|
replaceOptions: Array,
|
||||||
|
groupName: String,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isMultiSelect: Boolean,
|
||||||
|
isAvailable: Boolean,
|
||||||
|
filterByVehicleCategory: Boolean,
|
||||||
|
modelValue: Array,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initializeComponent(cmsContent, replaceOptions, groupName){
|
||||||
|
this.groupName = groupName;
|
||||||
|
this.questionText = cmsContent.QuestionText;
|
||||||
|
this.answersFromCms = cmsContent.Answers;
|
||||||
|
this.replaceOptions = replaceOptions;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
answersToDisplay(){
|
||||||
|
return Array.isArray(this.answersFromCms)
|
||||||
|
? this.answersFromCms.filter(ans =>
|
||||||
|
{
|
||||||
|
const name = ans.Name.split('-');
|
||||||
|
return this.filterByVehicleCategory ? name[0].toUpperCase() === store.getters.vehicle.category : name[0].toUpperCase() === store.getters.vehicle.category && this.replaceOptions.includes(name[1])
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(val) {
|
||||||
|
this.$emit("update:modelValue", val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
buttonQuestion,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -3,6 +3,7 @@ import vehicleDamage from "@/layouts/vehicle-damage/vehicle-damage.vue";
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||||
|
|
@ -144,7 +145,9 @@ function setupMocks({
|
||||||
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn();
|
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn();
|
||||||
baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation(() => {
|
baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation(() => {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: ["Front Window", "Back Window", "Side Window"],
|
driverSideOptions: {
|
||||||
|
availableReplacementOptions: ["Front", "Back", "Side"],
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const apiResponses = {
|
const apiResponses = {
|
||||||
|
|
@ -159,7 +162,11 @@ function setupMocks({
|
||||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
damageOptions: ["Front Window", "Back Window", "Side Window"],
|
damageOptions: {
|
||||||
|
driverSideOptions: {
|
||||||
|
availableReplacementOptions: ["Front", "Back", "Side"],
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiPromise = Promise.resolve(apiResponses);
|
const apiPromise = Promise.resolve(apiResponses);
|
||||||
|
|
@ -180,6 +187,11 @@ function setupMocks({
|
||||||
initializeComponent: jest.fn(),
|
initializeComponent: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const driverSideOptions = replaceOptionsQuestion
|
||||||
|
driverSideOptions.methods = {
|
||||||
|
initializeComponent: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||||
const wrapper = shallowMount(vehicleDamage, mountOptions);
|
const wrapper = shallowMount(vehicleDamage, mountOptions);
|
||||||
|
|
||||||
|
|
@ -197,5 +209,11 @@ function setupMocks({
|
||||||
funnelSubHeaderWrapper.vm.initializeComponent =
|
funnelSubHeaderWrapper.vm.initializeComponent =
|
||||||
funnelSubHeader.methods.initializeComponent;
|
funnelSubHeader.methods.initializeComponent;
|
||||||
|
|
||||||
|
const driverSideOptionsWrapper = wrapper.findComponent({
|
||||||
|
name: "replaceOptionsQuestion",
|
||||||
|
});
|
||||||
|
driverSideOptionsWrapper.vm.initializeComponent =
|
||||||
|
replaceOptionsQuestion.methods.initializeComponent;
|
||||||
|
|
||||||
return { wrapper, apiPromise };
|
return { wrapper, apiPromise };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container-fluid shadow rounded-3 p-0 position-relative">
|
<div class="container-fluid shadow rounded-3 p-2 position-relative">
|
||||||
<funnelHeader ref="funnelHeader" />
|
<funnelHeader ref="funnelHeader" />
|
||||||
<vehicleBanner ref="vehicleBanner" />
|
<vehicleBanner ref="vehicleBanner" />
|
||||||
<funnelSubHeader ref="funnelSubHeader" />
|
<funnelSubHeader
|
||||||
</div>
|
ref="funnelSubHeader"
|
||||||
|
/>
|
||||||
|
<replaceOptionsQuestion ref="driverSideOptions" isAvailable isMultiSelect />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -11,6 +14,7 @@
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
|
@ -54,7 +58,11 @@ export default {
|
||||||
vm.$refs.funnelSubHeader.initializeComponent(
|
vm.$refs.funnelSubHeader.initializeComponent(
|
||||||
resultMap.cmsContent.FunnelSubHeaderWidget
|
resultMap.cmsContent.FunnelSubHeaderWidget
|
||||||
);
|
);
|
||||||
// use resultMap.damageOptions for damage options
|
vm.$refs.driverSideOptions.initializeComponent(
|
||||||
|
resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, 'DriverSideReplaceOptionsQuestion'
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log(resultMap) for damageOptions and cms content data
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -66,6 +74,7 @@ export default {
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
vehicleBanner,
|
vehicleBanner,
|
||||||
funnelSubHeader,
|
funnelSubHeader,
|
||||||
|
replaceOptionsQuestion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
},
|
},
|
||||||
|
emits: ['update:modelValue'],
|
||||||
components: {
|
components: {
|
||||||
buttonQuestion,
|
buttonQuestion,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ const navigationScenarios = {
|
||||||
SELECTED_MODEL: "SELECTED_MODEL",
|
SELECTED_MODEL: "SELECTED_MODEL",
|
||||||
SELECTED_MAKE: "SELECTED_MAKE",
|
SELECTED_MAKE: "SELECTED_MAKE",
|
||||||
SELECTED_STYLE: "SELECTED_STYLE",
|
SELECTED_STYLE: "SELECTED_STYLE",
|
||||||
VEHICLE_DAMAGE: "VEHICLE_DAMAGE",
|
|
||||||
CLICKED_BACK: "CLICKED_BACK",
|
CLICKED_BACK: "CLICKED_BACK",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
:value="buttonID"
|
:value="buttonID"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
:data-focus-target="groupName"
|
:data-focus-target="groupName"
|
||||||
|
v-model="modelValue"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
|
@ -18,16 +19,25 @@
|
||||||
:aria-labelledby="buttonID"
|
:aria-labelledby="buttonID"
|
||||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||||
>
|
>
|
||||||
<span class="m-0" :class="textPosition">{{ buttonLabel }}</span>
|
<span
|
||||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">{{
|
class="m-0"
|
||||||
buttonLabelSubCopy
|
:class="textPosition"
|
||||||
}}</span>
|
>
|
||||||
<span v-if="screenReaderOnlyText" class="sr-only">{{
|
{{buttonLabel}}
|
||||||
screenReaderOnlyText
|
</span>
|
||||||
}}</span>
|
<span
|
||||||
|
v-if="buttonLabelSubCopy"
|
||||||
|
class="m-0 small"
|
||||||
|
:class="textPosition"
|
||||||
|
>
|
||||||
|
{{buttonLabelSubCopy}}
|
||||||
|
</span>
|
||||||
|
<span v-if="screenReaderOnlyText" class="sr-only">
|
||||||
|
{{screenReaderOnlyText}}
|
||||||
|
</span>
|
||||||
<loader
|
<loader
|
||||||
v-if="isLoaderDisplayed && !isMultiSelect"
|
v-if="isLoaderDisplayed && !isMultiSelect"
|
||||||
:style="{ width: `${sizeInRem}rem`, height: `${sizeInRem}rem` }"
|
:style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}"
|
||||||
:class="[loaderColor, loaderPosition]"
|
:class="[loaderColor, loaderPosition]"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -59,6 +69,7 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
modelValue: false,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -76,6 +87,11 @@ export default {
|
||||||
this.handleChange(value);
|
this.handleChange(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(){
|
||||||
|
this.$emit('isChecked', this.modelValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
loader,
|
loader,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,33 @@
|
||||||
:value="buttonID"
|
:value="buttonID"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
:data-focus-target="groupName"
|
:data-focus-target="groupName"
|
||||||
/>
|
v-model="modelValue"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
:for="buttonID"
|
:for="buttonID"
|
||||||
:aria-labelledby="buttonID"
|
:aria-labelledby="buttonID"
|
||||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||||
>
|
>
|
||||||
<span class="m-0" :class="textPosition">{{ buttonLabel }}</span>
|
<span
|
||||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">{{
|
class="m-0"
|
||||||
buttonLabelSubCopy
|
:class="textPosition"
|
||||||
}}</span>
|
>
|
||||||
<span v-if="screenReaderOnlyText" class="sr-only">{{
|
{{ buttonLabel }}
|
||||||
screenReaderOnlyText
|
</span>
|
||||||
}}</span>
|
<span
|
||||||
|
v-if="buttonLabelSubCopy"
|
||||||
|
class="m-0 small"
|
||||||
|
:class="textPosition"
|
||||||
|
>
|
||||||
|
{{ buttonLabelSubCopy }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="screenReaderOnlyText"
|
||||||
|
class="sr-only"
|
||||||
|
>
|
||||||
|
{{ screenReaderOnlyText }}
|
||||||
|
</span>
|
||||||
<loader
|
<loader
|
||||||
v-if="isLoaderDisplayed && !isMultiSelect"
|
v-if="isLoaderDisplayed && !isMultiSelect"
|
||||||
:style="{ width: `${sizeInRem}rem`, height: `${sizeInRem}rem` }"
|
:style="{ width: `${sizeInRem}rem`, height: `${sizeInRem}rem` }"
|
||||||
|
|
@ -52,12 +65,13 @@ export default {
|
||||||
loaderEnabled: Boolean,
|
loaderEnabled: Boolean,
|
||||||
loaderColor: String,
|
loaderColor: String,
|
||||||
loaderPosition: String,
|
loaderPosition: String,
|
||||||
sizeInRem: [Number, String],
|
sizeInRem: [Number,String],
|
||||||
value: {
|
value: {
|
||||||
// Field initial value
|
// Field initial value
|
||||||
type: String,
|
type: [String, Number],
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
modelValue: false
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -75,6 +89,11 @@ export default {
|
||||||
this.handleChange(value);
|
this.handleChange(value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(){
|
||||||
|
this.$emit('isChecked', this.modelValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
loader,
|
loader,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -119,4 +119,67 @@ describe("list-card.vue", () => {
|
||||||
|
|
||||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should return flex row classes if isWide is true", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(listCard, {
|
||||||
|
propsData: {
|
||||||
|
isRadioHorizontal: true,
|
||||||
|
buttonLabel: "Windshield",
|
||||||
|
buttonID: "List Card Checkbox",
|
||||||
|
groupID: "radio-demo-1",
|
||||||
|
groupName: "radio 1",
|
||||||
|
buttonImage: "windshield-damage.svg",
|
||||||
|
isRequired: true,
|
||||||
|
isWide: true,
|
||||||
|
buttonLabelSubCopy: ""
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const label = wrapper.find("label");
|
||||||
|
expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-row", "py-r", "ps-3", "pe-8"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return flex row classes if isWide is true and checkboxTop if buttonLabelSubCopy is true", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(listCard, {
|
||||||
|
propsData: {
|
||||||
|
isRadioHorizontal: true,
|
||||||
|
buttonLabel: "Windshield",
|
||||||
|
buttonID: "List Card Checkbox",
|
||||||
|
groupID: "radio-demo-1",
|
||||||
|
groupName: "radio 1",
|
||||||
|
buttonImage: "windshield-damage.svg",
|
||||||
|
isRequired: true,
|
||||||
|
isWide: true,
|
||||||
|
buttonLabelSubCopy: "Button Subcopy"
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const label = wrapper.find("label");
|
||||||
|
expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-row", "py-r", "ps-3", "pe-8", "checkboxTop"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return flex column classes if isWide is false", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(listCard, {
|
||||||
|
propsData: {
|
||||||
|
isRadioHorizontal: true,
|
||||||
|
buttonLabel: "Windshield",
|
||||||
|
buttonID: "List Card Checkbox",
|
||||||
|
groupID: "radio-demo-1",
|
||||||
|
groupName: "radio 1",
|
||||||
|
buttonImage: "windshield-damage.svg",
|
||||||
|
isRequired: true,
|
||||||
|
isWide: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const label = wrapper.find("label");
|
||||||
|
expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-column", "pt-4", "pb-2"]);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- Heavily documented below -->
|
<!-- Heavily documented below -->
|
||||||
<div class="col">
|
<div :class="'col' + colLength">
|
||||||
<div
|
<div class="list-card w-100 rounded-3 d-flex align-items-center h-100"
|
||||||
class="list-card w-100 rounded-3 d-flex align-items-center h-100"
|
:class="isWide ? 'horizontal' : ''"
|
||||||
:class="isWide ? 'horizontal' : ''"
|
@mouseup="handleChange(value)"
|
||||||
@mouseup="handleClick(value)"
|
@keyup.space="handleChange(value)">
|
||||||
@keyup.space="handleClick(value)"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
||||||
:id="buttonID"
|
:id="buttonID"
|
||||||
|
|
@ -14,28 +12,28 @@
|
||||||
:value="buttonID"
|
:value="buttonID"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
:data-focus-target="groupName"
|
:data-focus-target="groupName"
|
||||||
/>
|
v-model="modelValue"
|
||||||
|
/>
|
||||||
<label
|
<label
|
||||||
:for="buttonID"
|
:for="buttonID"
|
||||||
class="d-flex w-100 align-items-center px-2 h-100"
|
class="d-flex w-100 align-items-center px-2 h-100"
|
||||||
:class="getLabelClasses"
|
:class="getLabelClasses" tabindex="-1"
|
||||||
tabindex="-1"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:id="buttonImageId"
|
:id="buttonImageId"
|
||||||
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
|
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
|
||||||
v-bind:src="require(`@/assets/img/icons/${buttonImage}`)"
|
:src="buttonImage"
|
||||||
:alt="altText"
|
:alt="altText"
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
v-if="!isWide"
|
v-if="!isWide"
|
||||||
class="small order-3"
|
class="small order-3"
|
||||||
:class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"
|
:class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"
|
||||||
>
|
>
|
||||||
{{ buttonLabel }}
|
{{buttonLabel}}
|
||||||
</p>
|
</p>
|
||||||
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4">
|
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4">
|
||||||
{{ buttonLabelSubCopy }}
|
{{buttonLabelSubCopy}}
|
||||||
</p>
|
</p>
|
||||||
<div v-if="isWide" class="order-2">
|
<div v-if="isWide" class="order-2">
|
||||||
<p class="m-0 small">{{ buttonLabel }}</p>
|
<p class="m-0 small">{{ buttonLabel }}</p>
|
||||||
|
|
@ -72,6 +70,8 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
colLength: String,
|
||||||
|
modelValue: false,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getLabelClasses() {
|
getLabelClasses() {
|
||||||
|
|
@ -86,6 +86,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue(){
|
||||||
|
this.$emit('isChecked', this.modelValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { groupName, value } = toRefs(props);
|
const { groupName, value } = toRefs(props);
|
||||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue