Merge remote-tracking branch 'origin/develop' into feature/CSR-77
This commit is contained in:
commit
21c5316950
23 changed files with 532 additions and 233 deletions
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<button
|
||||
v-if="backButtonAction"
|
||||
@click="backButtonAction"
|
||||
class="back-button-wrapper p-2"
|
||||
@click="handleClick"
|
||||
class="back-button-wrapper p-3"
|
||||
:aria-label="backButtonAccessibleText"
|
||||
>
|
||||
<div class="button-back">
|
||||
|
|
@ -20,12 +19,13 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
backButtonAction: {
|
||||
type: Function,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.$emit('click-event');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ describe("buttonQuestion.vue", () => {
|
|||
wrapper.vm.chooseAnswer("2021");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.find(".needed_car_info-text").text()).toEqual(
|
||||
expect(wrapper.find(".text-center").text()).toEqual(
|
||||
"Question Text"
|
||||
);
|
||||
const buttonButtons = wrapper.findAllComponents('[data-test="button"]');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="button_question">
|
||||
<div class="needed_car_info mt-6 mb-4 d-flex">
|
||||
<span class="text-center fs-6 fw-bold w-100 needed_car_info-text">{{
|
||||
<div class="mt-6 mb-4 d-flex">
|
||||
<span class="text-center fs-6 fw-bold w-100">{{
|
||||
questionText
|
||||
}}</span>
|
||||
</div>
|
||||
|
|
@ -32,6 +32,7 @@ import listButton from "@/ux-components/list-button/list-button";
|
|||
export default {
|
||||
name: "buttonQuestion",
|
||||
props: {
|
||||
isMultiSelect: Boolean,
|
||||
questionText: String,
|
||||
answers: Array,
|
||||
modelValue: String,
|
||||
|
|
@ -58,4 +59,4 @@ export default {
|
|||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -6,16 +6,17 @@
|
|||
{{ text }}
|
||||
</span>
|
||||
<buttonBack
|
||||
v-if="hasBackButton"
|
||||
:backButtonAction="backButtonAction"
|
||||
/>
|
||||
</h5>
|
||||
v-if="hasBackButton"
|
||||
:backButtonAccessibleText="backButtonAccessibleText"
|
||||
@click-event="clickEvent"
|
||||
/>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import buttonBack from "@/ux-components/button-back/button-back";
|
||||
import buttonBack from "@/common-components/button-back/button-back";
|
||||
|
||||
export default {
|
||||
name: "FunnelSubHeader",
|
||||
|
|
@ -26,14 +27,15 @@ export default {
|
|||
},
|
||||
props: {
|
||||
hasBackButton: Boolean,
|
||||
backButtonUrl: String,
|
||||
backButtonAccessibleText: String,
|
||||
backButtonAction: Function,
|
||||
},
|
||||
components: {
|
||||
buttonBack,
|
||||
},
|
||||
methods: {
|
||||
clickEvent() {
|
||||
this.$emit('click-event');
|
||||
},
|
||||
initializeComponent(cmsContent) {
|
||||
this.text = cmsContent.HeaderText;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,20 @@ export function fetchCmsContentForPage(fmgPage) {
|
|||
|
||||
response.data.Result.forEach((widget) => {
|
||||
|
||||
let widgetWithReplacements = findAndReplaceGlobalStateValues(widget.Model, widget.Type);
|
||||
let widgetWithReplacements = findAndReplaceGlobalStateValues(widget.Model, widget.Name);
|
||||
|
||||
if (Object.values(widgetNames).includes(widgetWithReplacements.Type)) {
|
||||
// If we already have this widget, push it on the collection
|
||||
if (widgetWithReplacements.Type in pageDataFromCms) {
|
||||
pageDataFromCms[widgetWithReplacements.Type].push(widgetWithReplacements.Model);
|
||||
return;
|
||||
}
|
||||
// If we already have this widget, push it on the collection
|
||||
if (widgetWithReplacements.Name in pageDataFromCms) {
|
||||
pageDataFromCms[widgetWithReplacements.Name].push(widgetWithReplacements.Model);
|
||||
return;
|
||||
}
|
||||
|
||||
pageDataFromCms[widgetWithReplacements.Type] = [widgetWithReplacements.Model];
|
||||
pageDataFromCms[widgetWithReplacements.Name] = [widgetWithReplacements.Model];
|
||||
});
|
||||
|
||||
Object.keys(pageDataFromCms).forEach((key) => {
|
||||
if (pageDataFromCms[key].length === 1){
|
||||
pageDataFromCms[key] = pageDataFromCms[key][0];
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -66,10 +70,10 @@ function mapStringToState(str) {
|
|||
|
||||
// Parent function for processWidgetItemForReplacement. This will loop through the parent
|
||||
// object and pass any objects that need additional processing to the processWidgetItemForReplacement function.
|
||||
function findAndReplaceGlobalStateValues(widgetModel, widgetType) {
|
||||
function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
|
||||
|
||||
const objWithReplacements = {
|
||||
Type: widgetType,
|
||||
Name: widgetName,
|
||||
Model: {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ describe("cms-content-helper.js", () => {
|
|||
const cmsMockData = {
|
||||
Result: [
|
||||
{
|
||||
Type: "FunnelSubHeaderWidget",
|
||||
Name: "FunnelHeaderWidget",
|
||||
Model: {
|
||||
HeaderText: "Select a year to get started",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "FunnelSubHeaderWidget",
|
||||
Name: "FunnelSubHeaderWidget",
|
||||
Model: {
|
||||
HeaderText: "Select a model",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "RadioQuestionWidget",
|
||||
Name: "VehicleYearQuestion",
|
||||
Model: {
|
||||
QuestionText: "What year is your vehicle?",
|
||||
},
|
||||
|
|
@ -40,7 +40,7 @@ describe("cms-content-helper.js", () => {
|
|||
// Act
|
||||
fetchCmsContentForPage("testPage").then((response) => {
|
||||
// Assert
|
||||
expect(response.FunnelSubHeaderWidget[0].HeaderText).toEqual(
|
||||
expect(response.FunnelHeaderWidget.HeaderText).toEqual(
|
||||
"Select a year to get started"
|
||||
);
|
||||
});
|
||||
|
|
@ -53,7 +53,7 @@ describe("cms-content-helper.js", () => {
|
|||
const cmsMockData = {
|
||||
Result: [
|
||||
{
|
||||
Type: "FunnelSubHeaderWidget",
|
||||
Name: "FunnelSubHeaderWidget",
|
||||
Model: {
|
||||
HeaderText: "{globalState:order.vehicle.year}",
|
||||
},
|
||||
|
|
@ -65,7 +65,7 @@ describe("cms-content-helper.js", () => {
|
|||
|
||||
fetchCmsContentForPage("testPage").then((response) => {
|
||||
// Assert
|
||||
expect(response.FunnelSubHeaderWidget[0].HeaderText).toEqual(
|
||||
expect(response.FunnelSubHeaderWidget.HeaderText).toEqual(
|
||||
"2019"
|
||||
);
|
||||
});
|
||||
|
|
@ -78,13 +78,13 @@ describe("cms-content-helper.js", () => {
|
|||
const cmsMockData = {
|
||||
Result: [
|
||||
{
|
||||
Type: "FunnelSubHeaderWidget",
|
||||
Name: "FunnelSubHeaderWidget",
|
||||
Model: {
|
||||
HeaderText: "{globalState:order.vehicle.year}",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "RadioQuestionWidget",
|
||||
Name: "VehicleYearQuestion",
|
||||
Model: {
|
||||
ExampleText: "My widget value!",
|
||||
},
|
||||
|
|
@ -96,8 +96,8 @@ describe("cms-content-helper.js", () => {
|
|||
|
||||
fetchCmsContentForPage("testPage").then((response) => {
|
||||
// Assert
|
||||
expect(response.FunnelSubHeaderWidget[0].HeaderText).toEqual("2019");
|
||||
expect(response.RadioQuestionWidget[0].ExampleText).toEqual("My widget value!");
|
||||
expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019");
|
||||
expect(response.VehicleYearQuestion.ExampleText).toEqual("My widget value!");
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -109,13 +109,13 @@ describe("cms-content-helper.js", () => {
|
|||
const cmsMockData = {
|
||||
Result: [
|
||||
{
|
||||
Type: "FunnelSubHeaderWidget",
|
||||
Name: "FunnelSubHeaderWidget",
|
||||
Model: {
|
||||
HeaderText: "{globalState:order.vehicle.year}",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "RadioQuestionWidget",
|
||||
Name: "VehicleMakeQuestion",
|
||||
Model: {
|
||||
OtherObjectInside: {
|
||||
ExampleText: "{globalState:order.vehicle.make}",
|
||||
|
|
@ -130,8 +130,8 @@ describe("cms-content-helper.js", () => {
|
|||
fetchCmsContentForPage("testPage").then((response) => {
|
||||
// Assert
|
||||
|
||||
expect(response.FunnelSubHeaderWidget[0].HeaderText).toEqual("2019");
|
||||
expect(response.RadioQuestionWidget[0].OtherObjectInside.ExampleText).toEqual("Acura");
|
||||
expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019");
|
||||
expect(response.VehicleMakeQuestion.OtherObjectInside.ExampleText).toEqual("Acura");
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -185,24 +185,6 @@
|
|||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<h6 class="mx-2 my-4">With error</h6>
|
||||
<fieldset>
|
||||
<legend class="sr-only">Radio button with error state</legend>
|
||||
<listCard
|
||||
isRadioHorizontal
|
||||
buttonImage="windshield-damage.svg"
|
||||
buttonLabel="Windshield"
|
||||
altText=""
|
||||
buttonID="Horizontal Radio Button Error State"
|
||||
groupID="radio-demo-5"
|
||||
buttonLabelSubCopy="Description"
|
||||
hasError
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-4">
|
||||
<div class="col">
|
||||
<h4 class="m-0 p-2 bg-light rounded">List Button - Single-Line</h4>
|
||||
|
|
@ -227,8 +209,6 @@
|
|||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
screenReaderOnlyText=" opens new window"
|
||||
hasError
|
||||
errorMessage="Error"
|
||||
/>
|
||||
<listButton
|
||||
isMultiSelect
|
||||
|
|
@ -495,7 +475,6 @@
|
|||
groupName="demo-4"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="1"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
|
|
@ -509,7 +488,6 @@
|
|||
groupName="demo-4"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="2"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
|
|
@ -523,7 +501,6 @@
|
|||
groupName="demo-4"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="3"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
|
|
@ -546,8 +523,8 @@
|
|||
groupName="demo-4-checkbox"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="4"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderEnabled=true
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
|
|
@ -559,8 +536,8 @@
|
|||
groupName="demo-4-checkbox"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="5"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderEnabled=true
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
|
|
@ -572,8 +549,8 @@
|
|||
groupName="demo-4-checkbox"
|
||||
ariaLabelBy="vehicle-model"
|
||||
buttonID="6"
|
||||
buttonLabelSubCopy=""
|
||||
textPosition="text-center"
|
||||
loaderEnabled=true
|
||||
loaderColor="blue"
|
||||
loaderPosition="right"
|
||||
sizeInRem="1"
|
||||
|
|
@ -739,7 +716,7 @@
|
|||
<script>
|
||||
import buttonPrimary from "@/ux-components/button-primary/button-primary";
|
||||
import buttonSecondary from "@/ux-components/button-secondary/button-secondary";
|
||||
import buttonBack from "@/ux-components/button-back/button-back";
|
||||
import buttonBack from "@/common-components/button-back/button-back";
|
||||
import listCard from "@/ux-components/list-card/list-card";
|
||||
import listButton from "@/ux-components/list-button/list-button";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ describe("vehicle-make.vue", () => {
|
|||
test("Make question component is initized with api data", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const radioQuestionCmsContent = { QuestionText: "What make is your vehicle?" };
|
||||
const vehicleMakeQuestionCmsContent = { QuestionText: "What make is your vehicle?" };
|
||||
const makeQuestionInitialData = ["honda", "ford", "dodge"];
|
||||
const { wrapper, apiPromise } = setupMocks( {
|
||||
radioQuestionCmsContent: radioQuestionCmsContent,
|
||||
vehicleMakeQuestionCmsContent: vehicleMakeQuestionCmsContent,
|
||||
makeQuestionInitialData: makeQuestionInitialData,
|
||||
} );
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ describe("vehicle-make.vue", () => {
|
|||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(makeQuestion.methods.initializeComponent).toHaveBeenCalledWith(radioQuestionCmsContent, makeQuestionInitialData);
|
||||
expect(makeQuestion.methods.initializeComponent).toHaveBeenCalledWith(vehicleMakeQuestionCmsContent, makeQuestionInitialData);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
@ -122,7 +122,7 @@ describe("vehicle-make.vue", () => {
|
|||
});
|
||||
|
||||
function setupMocks({
|
||||
radioQuestionCmsContent = {},
|
||||
vehicleMakeQuestionCmsContent = {},
|
||||
makeQuestionInitialData = {},
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {},
|
||||
|
|
@ -131,20 +131,18 @@ function setupMocks({
|
|||
//Mock api responses
|
||||
const apiResponses = {
|
||||
cmsContent: {
|
||||
FunnelSubHeaderWidget: [pageHeaderWidgetHeaderText],
|
||||
RadioQuestionWidget: [radioQuestionCmsContent],
|
||||
VehicleBannerWidget: [
|
||||
FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
|
||||
VehicleMakeQuestion: vehicleMakeQuestionCmsContent,
|
||||
VehicleBannerWidget:
|
||||
{
|
||||
GenericVehicleImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||
},
|
||||
],
|
||||
FunnelHeaderWidget: [
|
||||
FunnelHeaderWidget:
|
||||
{
|
||||
LogoImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||
},
|
||||
],
|
||||
},
|
||||
makeQuestionInitialData: makeQuestionInitialData,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
ref="funnelSubHeader"
|
||||
:hasBackButton="true"
|
||||
backButtonAccessibleText="Change Vehicle Year"
|
||||
:backButtonAction="backButtonAction"
|
||||
class="Header"
|
||||
@click-event="backButtonAction"
|
||||
class="Header"
|
||||
/>
|
||||
<makeQuestion v-model="selectedMake" ref="makeQuestion" />
|
||||
</div>
|
||||
|
|
@ -55,16 +55,16 @@ export default {
|
|||
settleAllPromises(promiseResultMap).then((resultMap) => {
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget[0]);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget[0]);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget[0]);
|
||||
vm.$refs.makeQuestion.initializeComponent(resultMap.cmsContent.RadioQuestionWidget[0], resultMap.makeQuestionInitialData);
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget);
|
||||
vm.$refs.makeQuestion.initializeComponent(resultMap.cmsContent.VehicleMakeQuestion, resultMap.makeQuestionInitialData);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
backButtonAction: function () {
|
||||
backButtonAction() {
|
||||
// route to move backwards
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
|
||||
export default {
|
||||
name: "model-question",
|
||||
mixins: [baseMixin],
|
||||
data() {
|
||||
return {
|
||||
questionText: null,
|
||||
|
|
|
|||
|
|
@ -7,44 +7,35 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||
import { nextTick } from "vue";
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
settleAllPromises: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("vehicle-model.vue", () => {
|
||||
test("Model question component is initized with api data", async (done) => {
|
||||
|
||||
//Arange
|
||||
const radioQuestionCmsContent = { QuestionText: "What model is your vehicle?" };
|
||||
const buttonQuestionContent = { QuestionText: "What model is your vehicle?" };
|
||||
const modelQuestionInitialData = ["accord", "civic", "insight"];
|
||||
const { wrapper, apiPromise } = setupMocks( {
|
||||
radioQuestionCmsContent: radioQuestionCmsContent,
|
||||
buttonQuestionContent: buttonQuestionContent,
|
||||
modelQuestionInitialData: modelQuestionInitialData,
|
||||
} );
|
||||
|
||||
//Act
|
||||
vehicleModel.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-model" } }, undefined, (c) => c(wrapper.vm));
|
||||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(modelQuestion.methods.initializeComponent).toHaveBeenCalledWith(radioQuestionCmsContent, modelQuestionInitialData);
|
||||
expect(modelQuestion.methods.initializeComponent).toHaveBeenCalledWith(buttonQuestionContent, modelQuestionInitialData);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-model.vue", () => {
|
||||
test("Page header is initailized with api data", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const pageHeaderWidgetHeaderText = "Select a model to get started";
|
||||
const { wrapper, apiPromise } = setupMocks( { pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText });
|
||||
|
||||
//Act
|
||||
vehicleModel.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-model" } }, undefined, (c) => c(wrapper.vm));
|
||||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(pageHeaderWidgetHeaderText);
|
||||
|
|
@ -52,20 +43,16 @@ describe("vehicle-model.vue", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-model.vue", () => {
|
||||
test("Page logo image is initailized with api data", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const SiteHeaderWidget = {
|
||||
LogoImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||
}
|
||||
const { wrapper, apiPromise } = setupMocks( { SiteHeaderWidget: SiteHeaderWidget});
|
||||
|
||||
//Act
|
||||
vehicleModel.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-model" } }, undefined, (c) => c(wrapper.vm));
|
||||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(SiteHeaderWidget);
|
||||
|
|
@ -73,20 +60,16 @@ describe("vehicle-model.vue", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-model.vue", () => {
|
||||
test("Vehicle image is initailized with api data", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const VehicleBannerWidget = {
|
||||
GenericVehicleImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||
}
|
||||
const { wrapper, apiPromise } = setupMocks( { VehicleBannerWidget: VehicleBannerWidget});
|
||||
|
||||
//Act
|
||||
vehicleModel.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-model" } }, undefined, (c) => c(wrapper.vm));
|
||||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(VehicleBannerWidget);
|
||||
|
|
@ -94,10 +77,8 @@ describe("vehicle-model.vue", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-model.vue", () => {
|
||||
test("BackButtonAction triggers a router.navigate change", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper, apiPromise } = setupMocks( {
|
||||
pageHeaderWidgetHeaderText: "Select a model to get started",
|
||||
|
|
@ -107,12 +88,10 @@ describe("vehicle-model.vue", () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleModel.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-model" } }, undefined, (c) => c(wrapper.vm));
|
||||
wrapper.vm.backButtonAction();
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
|
|
@ -120,37 +99,32 @@ describe("vehicle-model.vue", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({
|
||||
radioQuestionCmsContent = {},
|
||||
buttonQuestionContent = {},
|
||||
modelQuestionInitialData = {},
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {},
|
||||
}) {
|
||||
|
||||
//Mock api responses
|
||||
const apiResponses = {
|
||||
cmsContent: {
|
||||
FunnelSubHeaderWidget: [pageHeaderWidgetHeaderText],
|
||||
RadioQuestionWidget: [radioQuestionCmsContent],
|
||||
VehicleBannerWidget: [
|
||||
FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
|
||||
VehicleModelQuestion: buttonQuestionContent,
|
||||
VehicleBannerWidget:
|
||||
{
|
||||
GenericVehicleImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||
},
|
||||
],
|
||||
FunnelHeaderWidget: [
|
||||
FunnelHeaderWidget:
|
||||
{
|
||||
LogoImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||
},
|
||||
],
|
||||
},
|
||||
modelQuestionInitialData: modelQuestionInitialData,
|
||||
};
|
||||
const apiPromise = Promise.resolve(apiResponses);
|
||||
settleAllPromises.mockImplementation(() => apiPromise);
|
||||
|
||||
//Mock model question methods
|
||||
modelQuestion.methods = {
|
||||
loadInitialData: jest.fn(),
|
||||
|
|
@ -175,6 +149,5 @@ function setupMocks({
|
|||
vehicleBannerWrapper.vm.initializeComponent = vehicleBanner.methods.initializeComponent;
|
||||
const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader" });
|
||||
funnelSubHeaderWrapper.vm.initializeComponent = funnelSubHeader.methods.initializeComponent;
|
||||
|
||||
return { wrapper, apiPromise };
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
ref="funnelSubHeader"
|
||||
:hasBackButton="true"
|
||||
backButtonAccessibleText="Change Vehicle Make"
|
||||
:backButtonAction="backButtonAction"
|
||||
@click-event="backButtonAction"
|
||||
class="Header"
|
||||
/>
|
||||
<modelQuestion v-model="selectedModel" ref="modelQuestion" />
|
||||
|
|
@ -55,16 +55,16 @@ export default {
|
|||
settleAllPromises(promiseResultMap).then((resultMap) => {
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget[0]);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget[0]);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget[0]);
|
||||
vm.$refs.modelQuestion.initializeComponent(resultMap.cmsContent.RadioQuestionWidget[0], resultMap.modelQuestionInitialData);
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget);
|
||||
vm.$refs.modelQuestion.initializeComponent(resultMap.cmsContent.VehicleModelQuestion, resultMap.modelQuestionInitialData);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
backButtonAction: function () {
|
||||
backButtonAction() {
|
||||
// route to move backwards
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ describe("vehicle-style.vue", () => {
|
|||
test("Style question component is initized with api data", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const radioQuestionCmsContent = { QuestionText: "What style is your vehicle?" };
|
||||
const vehicleStyleQuestionCmsContent = { QuestionText: "What style is your vehicle?" };
|
||||
const styleQuestionInitialData = ["2 Door", "4 Door"];
|
||||
const { wrapper, apiPromise } = setupMocks( {
|
||||
radioQuestionCmsContent: radioQuestionCmsContent,
|
||||
vehicleStyleQuestionCmsContent: vehicleStyleQuestionCmsContent,
|
||||
styleQuestionInitialData: styleQuestionInitialData,
|
||||
} );
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ describe("vehicle-style.vue", () => {
|
|||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(styleQuestion.methods.initializeComponent).toHaveBeenCalledWith(radioQuestionCmsContent, styleQuestionInitialData);
|
||||
expect(styleQuestion.methods.initializeComponent).toHaveBeenCalledWith(vehicleStyleQuestionCmsContent, styleQuestionInitialData);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
@ -156,7 +156,7 @@ describe("vehicle-style.vue", () => {
|
|||
});
|
||||
|
||||
function setupMocks({
|
||||
radioQuestionCmsContent = {},
|
||||
vehicleStyleQuestionCmsContent = {},
|
||||
styleQuestionInitialData = {},
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {},
|
||||
|
|
@ -165,20 +165,18 @@ function setupMocks({
|
|||
//Mock api responses
|
||||
const apiResponses = {
|
||||
cmsContent: {
|
||||
FunnelSubHeaderWidget: [pageHeaderWidgetHeaderText],
|
||||
RadioQuestionWidget: [radioQuestionCmsContent],
|
||||
VehicleBannerWidget: [
|
||||
FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
|
||||
VehicleStyleQuestion: vehicleStyleQuestionCmsContent,
|
||||
VehicleBannerWidget:
|
||||
{
|
||||
GenericVehicleImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||
},
|
||||
],
|
||||
FunnelHeaderWidget: [
|
||||
FunnelHeaderWidget:
|
||||
{
|
||||
LogoImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||
},
|
||||
],
|
||||
},
|
||||
styleQuestionInitialData: styleQuestionInitialData,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
ref="funnelSubHeader"
|
||||
:hasBackButton="true"
|
||||
backButtonAccessibleText="Change Vehicle Model"
|
||||
:backButtonAction="backButtonAction"
|
||||
@click-event="backButtonAction"
|
||||
class="Header"
|
||||
/>
|
||||
<styleQuestion v-model="selectedStyle" ref="styleQuestion" />
|
||||
|
|
@ -55,16 +55,16 @@ export default {
|
|||
settleAllPromises(promiseResultMap).then((resultMap) => {
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget[0]);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget[0]);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget[0]);
|
||||
vm.$refs.styleQuestion.initializeComponent(resultMap.cmsContent.RadioQuestionWidget[0], resultMap.styleQuestionInitialData);
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget);
|
||||
vm.$refs.styleQuestion.initializeComponent(resultMap.cmsContent.VehicleStyleQuestion, resultMap.styleQuestionInitialData);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
backButtonAction: function () {
|
||||
backButtonAction() {
|
||||
// route to move backwards
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ describe("vehicle-year.vue", () => {
|
|||
test("Year question component is initized with api data", async (done) => {
|
||||
|
||||
//Arrange
|
||||
const radioQuestionCmsContent = { QuestionText: "What year is your vehicle?" };
|
||||
const vehicleYearQuestionCmsContent = { QuestionText: "What year is your vehicle?" };
|
||||
const yearQuestionInitialData = ["2023", "2022", "2021"];
|
||||
const { wrapper, apiPromise } = setupMocks( {
|
||||
radioQuestionCmsContent: radioQuestionCmsContent,
|
||||
vehicleYearQuestionCmsContent: vehicleYearQuestionCmsContent,
|
||||
yearQuestionInitialData: yearQuestionInitialData,
|
||||
} );
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ describe("vehicle-year.vue", () => {
|
|||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(yearQuestion.methods.initializeComponent).toHaveBeenCalledWith(radioQuestionCmsContent, yearQuestionInitialData);
|
||||
expect(yearQuestion.methods.initializeComponent).toHaveBeenCalledWith(vehicleYearQuestionCmsContent, yearQuestionInitialData);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
@ -78,7 +78,7 @@ describe("vehicle-year.vue", () => {
|
|||
});
|
||||
|
||||
function setupMocks({
|
||||
radioQuestionCmsContent = {},
|
||||
vehicleYearQuestionCmsContent = {},
|
||||
yearQuestionInitialData = {},
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {},
|
||||
|
|
@ -87,20 +87,18 @@ function setupMocks({
|
|||
//Mock api responses
|
||||
const apiResponses = {
|
||||
cmsContent: {
|
||||
FunnelSubHeaderWidget: [{ HeaderText: pageHeaderWidgetHeaderText }],
|
||||
RadioQuestionWidget: [radioQuestionCmsContent],
|
||||
VehicleBannerWidget: [
|
||||
FunnelSubHeaderWidget: { HeaderText: pageHeaderWidgetHeaderText },
|
||||
VehicleYearQuestion: vehicleYearQuestionCmsContent,
|
||||
VehicleBannerWidget:
|
||||
{
|
||||
GenericVehicleImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||
},
|
||||
],
|
||||
FunnelHeaderWidget: [
|
||||
FunnelHeaderWidget:
|
||||
{
|
||||
LogoImage:
|
||||
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||
},
|
||||
],
|
||||
},
|
||||
yearQuestionInitialData: yearQuestionInitialData,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ export default {
|
|||
settleAllPromises(promiseResultMap).then((resultMap) => {
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget[0]);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget[0]);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget[0]);
|
||||
vm.$refs.yearQuestion.initializeComponent(resultMap.cmsContent.RadioQuestionWidget[0], resultMap.yearQuestionInitialData);
|
||||
vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget);
|
||||
vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget);
|
||||
vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget);
|
||||
vm.$refs.yearQuestion.initializeComponent(resultMap.cmsContent.VehicleYearQuestion, resultMap.yearQuestionInitialData);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,46 +1,219 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import listButtonHorizontal from "./list-button-horizontal";
|
||||
import { nextTick } from "vue";
|
||||
|
||||
describe("list-button.vue", () => {
|
||||
it("Should render the 'buttonID' prop value as the label and id value as well as the button value, groupName as the name value, and fire a click even that sets the display data attribute to true.", async () => {
|
||||
|
||||
it("Should return input type checkbox if isMultiSelect is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
buttonID: "2023",
|
||||
groupName: "TestGroup",
|
||||
loaderColor: "blue",
|
||||
loaderPosition: "right",
|
||||
sizeInRem: "1.5",
|
||||
errorMessage: "null",
|
||||
isMultiSelect: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
const label = wrapper.find("label");
|
||||
const paragraph = wrapper.find("span");
|
||||
|
||||
await label.trigger("click");
|
||||
|
||||
expect(input.attributes()).toEqual({
|
||||
id: "2023",
|
||||
type: "radio",
|
||||
value: "2023",
|
||||
name: "TestGroup",
|
||||
"aria-required": "true",
|
||||
});
|
||||
|
||||
expect(label.attributes()).toEqual({
|
||||
tabindex: "-1",
|
||||
for: "2023",
|
||||
class: "d-flex flex-column justify-content-center py-3 px-4 last-item",
|
||||
"aria-checked": "false",
|
||||
});
|
||||
|
||||
expect(label.text()).toEqual("2023");
|
||||
|
||||
expect(paragraph.text()).toEqual("2023");
|
||||
|
||||
expect(wrapper.vm.isLoaderDisplayed).toBe(true);
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
|
||||
it("Should return input type radio if isMultiSelect is false or not specified", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
});
|
||||
|
||||
it("Should return primary label text (buttonID)", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
buttonID: "List Card Checkbox"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const label = wrapper.find("label");
|
||||
|
||||
expect(label.attributes().for).toEqual("List Card Checkbox");
|
||||
|
||||
});
|
||||
|
||||
it("Should return screen reader text", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
screenReaderOnlyText: "Screen Reader Only Text"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.sr-only");
|
||||
|
||||
expect(paragraph.text()).toEqual("Screen Reader Only Text");
|
||||
|
||||
});
|
||||
|
||||
it("Should return text alignment class", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
textPosition: "text-center"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.m-0");
|
||||
|
||||
expect(paragraph.attributes('class')).toContain("text-center");
|
||||
|
||||
});
|
||||
|
||||
it("Should return aria-required state", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isRequired: true
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
|
||||
});
|
||||
|
||||
it("Should return loader enabled true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
loaderEnabled: true
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleClick();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.exists()).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
it("Should return loader color", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
loaderColor: "blue",
|
||||
loaderEnabled: true
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleClick();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.attributes('class')).toContain("blue");
|
||||
|
||||
});
|
||||
|
||||
it("Should return loader position", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
loaderEnabled: true
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleClick();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.attributes('class')).toContain("right");
|
||||
|
||||
});
|
||||
|
||||
it("Should return loader size in rem", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
sizeInRem: 1,
|
||||
loaderEnabled: true
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleClick();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.attributes('style')).toContain("1rem");
|
||||
|
||||
});
|
||||
|
||||
it("Should return first item if first item in group", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
positionInGroup: 1
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
expect(label.attributes('class')).toContain("first-item");
|
||||
|
||||
});
|
||||
|
||||
it("Should return last item if last item in group", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
positionInGroup: 3,
|
||||
totalInGroup: 3
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
expect(label.attributes('class')).toContain("last-item");
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,42 +3,39 @@
|
|||
<!-- IMPORTANT: Refrain from using more than 4 horizontal buttons on desktop, 3 on mobile. -->
|
||||
<div v-if="isMultiSelect" class="list-group list-button-horizontal d-flex flex-column w-100 mb-2">
|
||||
<input type="checkbox" :id="buttonID" :name="groupName" :value="buttonID" :aria-required="isRequired">
|
||||
<label tabindex="-1" aria-checked="false" :for="buttonID" class="d-flex flex-column justify-content-center py-3 px-4" :class="isFirstOrLastButton">
|
||||
<label tabindex="-1" aria-checked="false" :for="buttonID" :aria-labelledby="buttonID" class="d-flex flex-column justify-content-center py-3 px-4" :class="isFirstOrLastButton">
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="[this.textPosition]">{{buttonLabelSubCopy}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
</label>
|
||||
<p class="small">{{errorMessage}}</p>
|
||||
</div>
|
||||
<div v-else class="col list-group list-button-horizontal d-flex flex-column mb-2">
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonID" aria-required="true" @keyup.space="displayLoader()"/>
|
||||
<label tabindex="-1" aria-checked="false" :for="buttonID" class="d-flex flex-column justify-content-center py-3 px-4" :class="isFirstOrLastButton" @click="displayLoader()">
|
||||
<span class="m-0" :class="[this.textPosition]">{{buttonID}}</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="[this.textPosition]">{{buttonLabelSubCopy}}</span>
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonID" aria-required="true" @keyup.space="handleClick()" @click="handleClick()" />
|
||||
<label tabindex="-1" aria-checked="false" :for="buttonID" :aria-labelledby="buttonID" class="d-flex flex-column justify-content-center py-3 px-4" :class="isFirstOrLastButton">
|
||||
<span class="m-0" :class="textPosition">{{buttonID}}</span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{screenReaderOnlyText}}</span>
|
||||
<loader v-if="isLoaderDisplayed" :style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}" :class="[this.loaderColor, this.loaderPosition]" />
|
||||
<loader v-if="isLoaderDisplayed" :style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}" :class="[loaderColor, loaderPosition]" />
|
||||
</label>
|
||||
<p class="small">{{ errorMessage }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
|
||||
export default {
|
||||
name: "listButtonHorizontal",
|
||||
props: {
|
||||
isMultiSelect: Boolean,
|
||||
isMultiSelect: Boolean, /* Defines use as checkbox */
|
||||
groupName: String, /* Required, unique for each button GROUP */
|
||||
buttonID: String, /* Required, unique for each button. Used for button id, label and <label for> */
|
||||
buttonLabelSubCopy: String, /* Optional, used for multi-line buttons */
|
||||
screenReaderOnlyText: String, /* Optional, copy to be read by screenreader */
|
||||
textPosition: String, /* Optional, use Bootstrap classes: text-start, text-center, text-end. Default (empty) is text-start */
|
||||
errorMessage: String, /* Optional, if there is an error message to be displayed */
|
||||
loaderEnabled: Boolean, /* Optional, need to include this for loader to be used at all */
|
||||
loaderColor: String, /* Specify color of loader/spinner. Options are blue, red, green, white, black. Default is blue */
|
||||
loaderPosition: String, /* Specify horizontal position of loader/spinner. Options are center, right, left */
|
||||
sizeInRem: [Number,String], /* Specify size of loader/spinner in rem. Example: 1.5 (equals 24px (16x1.5)) */
|
||||
sizeInRem: Number, /* Specify size of loader/spinner in rem. Example: 1.5 (equals 24px (16x1.5)) */
|
||||
totalInGroup: Number, /* Required, total number of buttons in group. Used to tell first and last in group to apply border radius. */
|
||||
positionInGroup: Number /* Rquired, position of button in group. Example, 1,2,3 */
|
||||
positionInGroup: Number, /* Required, position of button in group. Example, 1,2,3 */
|
||||
isRequired: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -49,6 +46,9 @@ export default {
|
|||
displayLoader() {
|
||||
this.isLoaderDisplayed = true;
|
||||
},
|
||||
handleClick() {
|
||||
this.loaderEnabled && this.displayLoader();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
loader,
|
||||
|
|
@ -66,6 +66,7 @@ export default {
|
|||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.list-button-horizontal {
|
||||
input[type="radio"],
|
||||
|
|
@ -101,13 +102,6 @@ export default {
|
|||
+ p {
|
||||
display: none;
|
||||
}
|
||||
&.error {
|
||||
border: 1px solid $danger;
|
||||
+ p {
|
||||
display: flex;
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
&.first-item {
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
border-top-left-radius: 0.5rem;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ describe("list-button.vue", () => {
|
|||
loaderColor: "blue",
|
||||
loaderPosition: "right",
|
||||
sizeInRem: "1.5",
|
||||
errorMessage: "null",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -24,14 +24,13 @@ import loader from "@/ux-components/loader/loader";
|
|||
export default {
|
||||
name: "listButton",
|
||||
props: {
|
||||
isMultiSelect: Boolean,
|
||||
isMultiSelect: Boolean, /* Defines use as checkbox */
|
||||
groupName: String, /* Required, unique for each button GROUP */
|
||||
buttonID: [Number,String], /* Required, unique for each button. Used for button id, label and <label for> */
|
||||
isRequired: Boolean, /* Optional, default is false */
|
||||
screenReaderOnlyText: String, /* Optional, copy to be read by screenreader */
|
||||
buttonLabelSubCopy: String, /* Optional, used for multi-line buttons */
|
||||
textPosition: String, /* Optional, use Bootstrap classes: text-start, text-center, text-end. Default (empty) is text-start */
|
||||
errorMessage: String, /* Optional, if there is an error message to be displayed */
|
||||
loaderColor: String, /* Specify color of loader/spinner. Options are blue, red, green, white, black. Default is blue */
|
||||
loaderPosition: String, /* Specify horizontal position of loader/spinner. Options are center, right, left */
|
||||
sizeInRem: [Number,String], /* Specify size of loader/spinner in rem. Example: 1.5 (equals 24px (16x1.5)) */
|
||||
|
|
|
|||
|
|
@ -1 +1,188 @@
|
|||
test.todo("some test to be written in the future");
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import listCard from "./list-card";
|
||||
|
||||
describe("list-card.vue", () => {
|
||||
|
||||
it("Should return input type checkbox if isMultiSelect is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listCard, {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
buttonLabel: "Windshield",
|
||||
buttonID: "List Card Checkbox",
|
||||
groupID: "checkbox-demo-1",
|
||||
groupName: "Checkbox 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
|
||||
it("Should return input type radio if isRadio is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listCard, {
|
||||
propsData: {
|
||||
isRadio: true,
|
||||
buttonLabel: "Windshield",
|
||||
buttonID: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
});
|
||||
|
||||
it("Should return input type checkbox if isMultiSelectHorizontal is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listCard, {
|
||||
propsData: {
|
||||
isMultiSelectHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
buttonID: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
|
||||
});
|
||||
|
||||
it("Should return input type radio if isRadioHorizontal 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"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
|
||||
});
|
||||
|
||||
it("Should return primary label text", 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"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("p");
|
||||
|
||||
expect(paragraph.text()).toEqual("Windshield");
|
||||
|
||||
});
|
||||
|
||||
it("Should return secondary (sub) label text", 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",
|
||||
buttonLabelSubCopy: "Test"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("p:nth-of-type(2)");
|
||||
|
||||
expect(paragraph.text()).toEqual("Test");
|
||||
|
||||
});
|
||||
|
||||
it("Should return value used for various text settings including the label 'for' and input id", 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",
|
||||
buttonLabelSubCopy: "Test"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const label = wrapper.find("label");
|
||||
|
||||
expect(label.attributes().for).toEqual("List Card Checkbox");
|
||||
|
||||
});
|
||||
|
||||
it("Should return input group name used for radio or checkbox", 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",
|
||||
buttonLabelSubCopy: "Test"
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().name).toEqual("radio 1");
|
||||
|
||||
});
|
||||
|
||||
it("Should return aria-required state", 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
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
<template>
|
||||
<!-- Heavily documented below -->
|
||||
<div v-if="isMultiSelect" class="list-card w-100 rounded-3 d-flex align-items-center" :class="{'error': hasError}">
|
||||
<div v-if="isMultiSelect" class="list-card w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="checkbox" :id="buttonID" :name="groupName" :value="buttonLabel" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-column w-100 align-items-center pt-4 pb-2" tabindex="1">
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<p class="small m-0 order-3">{{buttonLabel}}</p>
|
||||
<p class="fs-7 m-0 order-4">{{buttonLabelSubCopy}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 order-4">{{buttonLabelSubCopy}}</p>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isRadio" class="list-card w-100 rounded-3 d-flex align-items-center" :class="{'error': hasError}">
|
||||
<div v-else-if="isRadio" class="list-card w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonID" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-column w-100 align-items-center pt-4 pb-2" tabindex="1">
|
||||
<img class="order-1" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<p class="small mt-2 mb-0 order-2">{{buttonLabel}}</p>
|
||||
<p class="fs-7 m-0 order-3">{{buttonLabelSubCopy}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="fs-7 m-0 order-3">{{buttonLabelSubCopy}}</p>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isMultiSelectHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center" :class="{'error': hasError}">
|
||||
<div v-else-if="isMultiSelectHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="checkbox" :id="buttonID" :name="groupName" :value="buttonLabel" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-row w-100 align-items-center py-3 ps-3 pe-8" tabindex="1" :class="{'checkboxTop': buttonLabelSubCopy}">
|
||||
<img class="ms-auto order-3" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0 small">{{buttonLabel}}</p>
|
||||
<p class="m-0 fs-7">{{buttonLabelSubCopy}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="m-0 fs-7">{{buttonLabelSubCopy}}</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else-if="isRadioHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center" :class="{'error': hasError}">
|
||||
<div v-else-if="isRadioHorizontal" class="list-card horizontal w-100 rounded-3 d-flex align-items-center">
|
||||
<input type="radio" :id="buttonID" :name="groupName" :value="buttonLabel" :aria-required="isRequired" />
|
||||
<label :for="buttonID" class="d-flex flex-row w-100 align-items-center py-3 ps-3 pe-8" tabindex="1" :class="{'checkboxTop': buttonLabelSubCopy}">
|
||||
<img class="ms-auto order-3" v-bind:src="require(`@/assets/img/icons/${buttonImage}`)" v-bind:alt="altText" />
|
||||
<div class="order-2">
|
||||
<p class="m-0 small">{{buttonLabel}}</p>
|
||||
<p class="m-0 fs-7">{{buttonLabelSubCopy}}</p>
|
||||
<p v-if="buttonLabelSubCopy" class="m-0 fs-7">{{buttonLabelSubCopy}}</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -43,27 +43,24 @@ export default {
|
|||
name: "listCard",
|
||||
props: {
|
||||
//Must choose one of the following four options
|
||||
isMultiSelect: Boolean,//Defines use as checkbox
|
||||
isRadio: Boolean,//Defines use as radio button
|
||||
isMultiSelectHorizontal: Boolean,//Defines use as horizontal checkbox
|
||||
isRadioHorizontal: Boolean,//Defines use as horizontal radio button
|
||||
isMultiSelect: Boolean, //Defines use as checkbox
|
||||
isRadio: Boolean, //Defines use as radio button
|
||||
isMultiSelectHorizontal: Boolean, //Defines use as horizontal checkbox
|
||||
isRadioHorizontal: Boolean, //Defines use as horizontal radio button
|
||||
//end must choose
|
||||
buttonImage: String,//Optional: File name of image
|
||||
buttonImage: String,//Required: File name of image
|
||||
buttonLabel: String,//Required: Label text
|
||||
isRequired: Boolean, //Required: is aria-required required or not?
|
||||
altText: String,//Leave empty. Screen readers read the buttonLabel text. If alt has content, it will repeat unnecessarily.
|
||||
buttonID: String,//Required: Unique
|
||||
groupName: String,//Rquired: Unique
|
||||
buttonLabelSubCopy: String,//Optional: sub text
|
||||
hasError: Boolean//Used to show error state
|
||||
groupName: String,//Required: Unique
|
||||
buttonLabelSubCopy: String,//Optional: sub tex
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.list-card {
|
||||
&.error {
|
||||
border: 1px solid $red !important;
|
||||
}
|
||||
border: 1px solid $gray-500;
|
||||
&.invalid { //Red border if invalid
|
||||
border: 1px solid $red;
|
||||
|
|
|
|||
Loading…
Reference in a new issue