Merge pull request #1086 from Safelite/feature/CSR-1112
Feature/csr 1112
This commit is contained in:
commit
55b8c5061f
11 changed files with 1143 additions and 8 deletions
|
|
@ -90,6 +90,10 @@ const endpoints = {
|
|||
url: "/parts/api/v1/parts/supporting-items",
|
||||
method: "POST",
|
||||
},
|
||||
GetProviderLocations: {
|
||||
url: "/location/api/v1/location/providers",
|
||||
method: "GET",
|
||||
},
|
||||
GetCapabilityQuestions: {
|
||||
url: "/parts/api/v1/parts/capability-questions",
|
||||
method: "GET",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ const storeActions = {
|
|||
GET_MOLDING_QUESTIONS: "getMoldingQuestions",
|
||||
GET_MOBILE_FEE_PART: "getMobileFeePart",
|
||||
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
||||
GET_PROVIDER_LOCATIONS: "getProviderLocations",
|
||||
SAVE_SESSION: "saveSession",
|
||||
LOAD_SESSION: "loadSession",
|
||||
UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE: "updateStoreWithSaveSessionResponse",
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ export default {
|
|||
valueToLogType: String,
|
||||
additionalButtonStyling: String,
|
||||
isSmallQuestionText: Boolean,
|
||||
availability: String,
|
||||
customButtonQuestionId: String,
|
||||
logDisplayedValuesEvent: {
|
||||
type: Boolean,
|
||||
|
|
@ -255,8 +256,10 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.resetField();
|
||||
modelValue(newValue) {
|
||||
this.resetField({
|
||||
value: newValue,
|
||||
});
|
||||
},
|
||||
answers() {
|
||||
//once we get the answers to display from parent, see if we need a GA event to log what we showed
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<transition name="fade" mode="out-in">
|
||||
<div class="appointment-type-question" aria-live="polite">
|
||||
<buttonQuestion
|
||||
ref="buttonQuestion"
|
||||
customButtonQuestionId="appointmentTypeQuestion"
|
||||
:questionText="questionText"
|
||||
:answers="answersToDisplay"
|
||||
|
|
@ -82,8 +83,6 @@ export default {
|
|||
handler(newValue) {
|
||||
if (newValue) {
|
||||
this.selectedValues = "Mobile";
|
||||
} else {
|
||||
this.selectedValues = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -190,6 +190,8 @@ describe("service-location.vue", () => {
|
|||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
const mobileFeePart = {
|
||||
partNumber: "MOBILE FEE",
|
||||
description: "MOBILE FEE",
|
||||
|
|
@ -585,6 +587,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -611,6 +615,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -637,6 +643,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -663,6 +671,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -689,6 +699,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -716,6 +728,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -743,6 +757,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -770,6 +786,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -797,6 +815,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -824,6 +844,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -851,6 +873,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -878,6 +902,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -905,6 +931,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -932,6 +960,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -959,6 +989,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -989,6 +1021,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1015,6 +1049,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1041,6 +1077,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1068,6 +1106,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1095,6 +1135,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1122,6 +1164,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1151,6 +1195,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1178,6 +1224,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1205,6 +1253,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1232,6 +1282,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1259,6 +1311,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1286,6 +1340,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1313,6 +1369,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
@ -1342,6 +1400,8 @@ describe("service-location.vue", () => {
|
|||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.$refs.shopQuestion.initializeComponent = jest.fn();
|
||||
|
||||
// Act
|
||||
await serviceLocation.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
alertClass="alert-warning" />
|
||||
<appointmentTypeQuestion
|
||||
v-model="selectedAppointmentType"
|
||||
v-if="!displayNoShopsAlert"
|
||||
v-show="!displayNoShopsAlert"
|
||||
:isServiceableMobile="isServiceableMobile"
|
||||
:isServiceableInshop="isServiceableInshop"
|
||||
ref="appointmentTypeQuestion"
|
||||
|
|
@ -65,6 +65,19 @@
|
|||
ref="mobileLocationQuestions"
|
||||
linkWidgetName="MobileLocationLinkWidget"
|
||||
modalWidgetName="MobileLocationModalWidget" />
|
||||
<Transition name="fade" mode="out-in">
|
||||
<shopQuestion
|
||||
ref="shopQuestion"
|
||||
v-show="
|
||||
selectedAppointmentType === 'Inshop' ||
|
||||
selectedAppointmentType === 'Dropoff'
|
||||
"
|
||||
v-model="providerNumber"
|
||||
:serviceZipCode="zipCode"
|
||||
:selectedAppointmentType="selectedAppointmentType"
|
||||
:shopQuestionData="shopQuestionInitialData"
|
||||
cmsWidgetName="ShopQuestionWidget" />
|
||||
</Transition>
|
||||
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
|
|
@ -82,6 +95,8 @@ import alert from "@/ux-components/alert/alert";
|
|||
import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question";
|
||||
import mobileLocationModalQuestions from "@/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions";
|
||||
import appointmentTypeQuestion from "@/layouts/service-location/appointment-type-question/appointment-type-question";
|
||||
import shopQuestion from "@/layouts/service-location/shop-question/shop-question";
|
||||
|
||||
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
|
||||
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer";
|
||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||
|
|
@ -133,10 +148,10 @@ export default {
|
|||
isRecalibrationServiceableInshop: null,
|
||||
isGlassServiceableMobile: null,
|
||||
isRecalibrationServiceableMobile: null,
|
||||
selectedAppointmentType: null,
|
||||
providerNumber: null,
|
||||
mobileFeePart: null,
|
||||
zipContainsMilitaryBase: false,
|
||||
selectedAppointmentType: "",
|
||||
providerNumber: null,
|
||||
};
|
||||
},
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
|
|
@ -150,6 +165,8 @@ export default {
|
|||
|
||||
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode);
|
||||
|
||||
const shopQuestionInitialDataPromise = shopQuestion.methods.loadInitialData();
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
|
|
@ -168,6 +185,10 @@ export default {
|
|||
resultKey: "zipCodeData",
|
||||
promise: getZipCodeData,
|
||||
},
|
||||
{
|
||||
resultKey: "shopQuestionInitialData",
|
||||
promise: shopQuestionInitialDataPromise,
|
||||
},
|
||||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
|
@ -180,6 +201,7 @@ export default {
|
|||
resultMap.serviceabilityDetails,
|
||||
resultMap.mobileFeePart
|
||||
);
|
||||
vm.$refs.shopQuestion.initializeComponent(resultMap.shopQuestionInitialData);
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -274,6 +296,12 @@ export default {
|
|||
displayNoShopsAlert() {
|
||||
return !this.isServiceableInshop && !this.isServiceableMobile;
|
||||
},
|
||||
isDropoff() {
|
||||
return this.selectedAppointmentType === "Dropoff";
|
||||
},
|
||||
isMobile() {
|
||||
return this.selectedAppointmentType === "Mobile";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
|
|
@ -283,7 +311,7 @@ export default {
|
|||
store.getters.payment.isInsurance !== null
|
||||
);
|
||||
},
|
||||
setData(zipCodeData, serviceabilityDetails, mobileFeePart) {
|
||||
setData(zipCodeData, serviceabilityDetails, mobileFeePart, shopQuestionData) {
|
||||
if (zipCodeData) {
|
||||
this.zipContainsMilitaryBase = zipCodeData.containsMilitaryBase;
|
||||
}
|
||||
|
|
@ -355,6 +383,7 @@ export default {
|
|||
Form,
|
||||
loadingModal,
|
||||
contentGroupModal,
|
||||
shopQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import { mount } from "@vue/test-utils";
|
||||
import shopListButton from "./shop-list-button";
|
||||
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||
|
||||
describe("service-package-radio.vue", () => {
|
||||
it("Should include buttonLabel in html", async () => {
|
||||
// Arrange
|
||||
let { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
propsData: mockProps,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const outputHtml = wrapper.html();
|
||||
|
||||
// Assert
|
||||
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabel"]));
|
||||
});
|
||||
|
||||
it("Should include buttonLabelSubCopy in html", async () => {
|
||||
// Arrange
|
||||
let { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
propsData: mockProps,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const outputHtml = wrapper.html();
|
||||
|
||||
// Assert
|
||||
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelSubCopy"]));
|
||||
});
|
||||
|
||||
it("Should include buttonLabelAuxillaryCopy in html", async () => {
|
||||
// Arrange
|
||||
let { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
propsData: mockProps,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const outputHtml = wrapper.html();
|
||||
|
||||
// Assert
|
||||
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelAuxillaryCopy"]));
|
||||
});
|
||||
});
|
||||
|
||||
const mockProps = {
|
||||
buttonLabel: "buttonLabel test copy",
|
||||
buttonLabelSubCopy: "buttonLabelSubCopy test copy",
|
||||
buttonBodyCopy:
|
||||
"<ul><li>buttonBodyCopy test copy</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>",
|
||||
buttonLabelAuxillaryCopy: "buttonLabelAuxillaryCopy test copy",
|
||||
value: 0,
|
||||
modelValue: 0,
|
||||
groupName: "mockGroup",
|
||||
};
|
||||
|
||||
function setupMocks({ mountOptionsMockData = {} }) {
|
||||
const wrapper = mount(shopListButton, {
|
||||
...mountOptionsMockData,
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
});
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<baseInputButton
|
||||
v-bind="$props"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2"
|
||||
v-model="selectedValue">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
<div class="row-one">
|
||||
<span class="m-0 button-label-copy" :class="textPosition">{{
|
||||
buttonLabel
|
||||
}}</span>
|
||||
<span class="m-0 button-label-sub-copy" :class="textPosition">{{
|
||||
buttonLabelSubCopy
|
||||
}}</span>
|
||||
<div
|
||||
class="availability-indicator"
|
||||
:class="availability === 'high' ? 'green' : 'red'">
|
||||
<span class="m-0 button-auxillary-copy">{{ buttonAuxillaryCopy }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
v-if="buttonBodyCopy"
|
||||
class="m-0 button-label-sub-copy small"
|
||||
:class="textPosition"
|
||||
v-html="buttonBodyCopy"></span>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">
|
||||
{{ screenReaderOnlyText }}
|
||||
</span>
|
||||
<loader
|
||||
v-if="isLoaderDisplayed && selectingInitiatesLoad"
|
||||
:class="[this.loaderColor, this.loaderPosition]" />
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import loader from "@/ux-components/loader/loader";
|
||||
import baseInputButton from "@/digital-components/base-input-button/base-input-button";
|
||||
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
name: "shopListButton",
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
props: {
|
||||
loaderColor: String,
|
||||
loaderPosition: {
|
||||
type: String,
|
||||
default: "right",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoaderDisplayed: false,
|
||||
availability: "low",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
displayLoader() {
|
||||
this.isLoaderDisplayed = true;
|
||||
},
|
||||
preHandleAnswerChange() {
|
||||
if (this.selectingInitiatesLoad) {
|
||||
this.displayLoader();
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
loader,
|
||||
baseInputButton,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loader {
|
||||
position: absolute;
|
||||
}
|
||||
.list-button {
|
||||
outline: none;
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
position: static; //override bootstrap
|
||||
|
||||
&:focus-visible + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content {
|
||||
color: $black;
|
||||
font-weight: 500;
|
||||
background: $blue-100;
|
||||
box-shadow: 0 0 0 1px $blue;
|
||||
}
|
||||
&:checked:focus + .list-button-content {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
&:checked + .list-button-content p,
|
||||
&:checked + .list-button-content span {
|
||||
font-weight: 500;
|
||||
}
|
||||
&:checked + .list-button-content span:nth-child(2) {
|
||||
font-weight: 400;
|
||||
color: $gray-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-button-content {
|
||||
color: $gray-600;
|
||||
position: relative;
|
||||
background: $white;
|
||||
transition: all 150ms linear;
|
||||
border-radius: $border-radius-lg;
|
||||
border: 1px solid $gray-500;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
|
||||
span {
|
||||
&.small {
|
||||
font-size: 0.75rem;
|
||||
color: $gray-550;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button-content {
|
||||
.row-one {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.25rem !important;
|
||||
|
||||
.button-label-copy {
|
||||
flex-grow: 0;
|
||||
line-height: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.button-label-sub-copy {
|
||||
flex-grow: 1;
|
||||
line-height: 1.25rem !important;
|
||||
font-weight: 400;
|
||||
font-size: 0.75rem;
|
||||
color: #727676;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
|
||||
.availability-indicator {
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.125rem 1.5rem;
|
||||
gap: 0.25rem;
|
||||
background: #e3f2ea;
|
||||
border-radius: 4.5rem;
|
||||
|
||||
.button-auxillary-copy {
|
||||
justify-content: right;
|
||||
line-height: 1.25rem !important;
|
||||
font-weight: 400;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: #006a36;
|
||||
background: #e3f2ea;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #ac160b;
|
||||
background: #e3f2ea;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
554
src/layouts/service-location/shop-question/shop-question.spec.js
Normal file
554
src/layouts/service-location/shop-question/shop-question.spec.js
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import shopQuestion from "./shop-question";
|
||||
|
||||
jest.mock("@/mixins/base-mixin", () => ({
|
||||
methods: {
|
||||
dispatchStoreAction(action, items, encode) {
|
||||
if (action === mockGetProviderLocationsStoreAction) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(mockNewShopList);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const mockGetProviderLocationsStoreAction = storeActions.GET_PROVIDER_LOCATIONS;
|
||||
const mockNewShopList = {
|
||||
data: [
|
||||
{
|
||||
city: "Far",
|
||||
country: "United States",
|
||||
distance: 100,
|
||||
providerNumber: "129",
|
||||
state: "OH",
|
||||
streetAddress: "555 First Capital Ln",
|
||||
zipCode: "45601",
|
||||
},
|
||||
{
|
||||
city: "Farther",
|
||||
country: "United States",
|
||||
distance: 200,
|
||||
providerNumber: "130",
|
||||
state: "OH",
|
||||
streetAddress: "5486 N Grove Rd",
|
||||
zipCode: "43215",
|
||||
},
|
||||
{
|
||||
city: "Farthest (Ever)",
|
||||
country: "United States",
|
||||
distance: 380.5,
|
||||
providerNumber: "131",
|
||||
state: "OH",
|
||||
streetAddress: "1670 Bongo Ave D",
|
||||
zipCode: "43223",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockCmsContent = {
|
||||
QuestionText: "Select a shop:",
|
||||
};
|
||||
|
||||
const cmsWidgetName = "AppointmentTypeQuestionWidget";
|
||||
const shopQuestionInitialData = [
|
||||
{
|
||||
city: "Worthington",
|
||||
country: "United States",
|
||||
distance: 1.5,
|
||||
providerNumber: "123",
|
||||
state: "OH",
|
||||
streetAddress: "760 Dearborn Park Ln",
|
||||
zipCode: "43085",
|
||||
},
|
||||
{
|
||||
city: "Columbus",
|
||||
country: "United States",
|
||||
distance: 4.5,
|
||||
providerNumber: "124",
|
||||
state: "OH",
|
||||
streetAddress: "5486 N Hamilton Rd",
|
||||
zipCode: "43230",
|
||||
},
|
||||
{
|
||||
city: "Powell",
|
||||
country: "United States",
|
||||
distance: 7,
|
||||
providerNumber: "125",
|
||||
state: "OH",
|
||||
streetAddress: "1670 Harmon Ave C",
|
||||
zipCode: "43223",
|
||||
},
|
||||
{
|
||||
city: "Chillicothe",
|
||||
country: "United States",
|
||||
distance: 41.5,
|
||||
providerNumber: "126",
|
||||
state: "OH",
|
||||
streetAddress: "555 First Capital Ln",
|
||||
zipCode: "45601",
|
||||
},
|
||||
{
|
||||
city: "Grove City",
|
||||
country: "United States",
|
||||
distance: 4.5,
|
||||
providerNumber: "127",
|
||||
state: "OH",
|
||||
streetAddress: "5486 N Grove Rd",
|
||||
zipCode: "43215",
|
||||
},
|
||||
{
|
||||
city: "Dayton",
|
||||
country: "United States",
|
||||
distance: 80.5,
|
||||
providerNumber: "128",
|
||||
state: "OH",
|
||||
streetAddress: "1670 Bongo Ave D",
|
||||
zipCode: "43223",
|
||||
},
|
||||
{
|
||||
city: "Far",
|
||||
country: "United States",
|
||||
distance: 100,
|
||||
providerNumber: "129",
|
||||
state: "OH",
|
||||
streetAddress: "555 First Capital Ln",
|
||||
zipCode: "45601",
|
||||
},
|
||||
{
|
||||
city: "Farther",
|
||||
country: "United States",
|
||||
distance: 200,
|
||||
providerNumber: "130",
|
||||
state: "OH",
|
||||
streetAddress: "5486 N Grove Rd",
|
||||
zipCode: "43215",
|
||||
},
|
||||
{
|
||||
city: "Farthest (Ever)",
|
||||
country: "United States",
|
||||
distance: 380.5,
|
||||
providerNumber: "131",
|
||||
state: "OH",
|
||||
streetAddress: "1670 Bongo Ave D",
|
||||
zipCode: "43223",
|
||||
},
|
||||
];
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||
if (widgetName === cmsWidgetName) {
|
||||
return mockCmsContent[cmsFieldName];
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
describe("shop-question.vue", () => {
|
||||
it("Should display first three shops when an appointment type has already been selected", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: null,
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.answers).toEqual([
|
||||
{
|
||||
Name: "123",
|
||||
buttonLabel: "Worthington",
|
||||
buttonLabelSubCopy: "1.5 mi",
|
||||
buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085",
|
||||
},
|
||||
{
|
||||
Name: "124",
|
||||
buttonLabel: "Columbus",
|
||||
buttonLabelSubCopy: "4.5 mi",
|
||||
buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230",
|
||||
},
|
||||
{
|
||||
Name: "125",
|
||||
buttonLabel: "Powell",
|
||||
buttonLabelSubCopy: "7 mi",
|
||||
buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("Should display the 'Show more locations' link when there are more than three locations to chose from", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: null,
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
||||
|
||||
// Assert
|
||||
expect(showMoreShopsLink.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("Should not display the 'Show more locations' link when there are fewer than three locations to chose from", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const alsoShopQuestionInitialData = [
|
||||
{
|
||||
city: "Worthington",
|
||||
country: "United States",
|
||||
distance: 1.5,
|
||||
providerNumber: "123",
|
||||
state: "OH",
|
||||
streetAddress: "760 Dearborn Park Ln",
|
||||
zipCode: "43085",
|
||||
},
|
||||
{
|
||||
city: "Columbus",
|
||||
country: "United States",
|
||||
distance: 4.5,
|
||||
providerNumber: "124",
|
||||
state: "OH",
|
||||
streetAddress: "5486 N Hamilton Rd",
|
||||
zipCode: "43230",
|
||||
},
|
||||
{
|
||||
city: "Powell",
|
||||
country: "United States",
|
||||
distance: 7,
|
||||
providerNumber: "125",
|
||||
state: "OH",
|
||||
streetAddress: "1670 Harmon Ave C",
|
||||
zipCode: "43223",
|
||||
},
|
||||
];
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: null,
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.initializeComponent(alsoShopQuestionInitialData);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
||||
|
||||
// Assert
|
||||
expect(showMoreShopsLink.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("Should display the next three shops when the 'Show more location' link is clicked", async () => {
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const displayedAnswers = [
|
||||
{
|
||||
Name: "123",
|
||||
buttonLabel: "Worthington",
|
||||
buttonLabelSubCopy: "1.5 mi",
|
||||
buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085",
|
||||
},
|
||||
{
|
||||
Name: "124",
|
||||
buttonLabel: "Columbus",
|
||||
buttonLabelSubCopy: "4.5 mi",
|
||||
buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230",
|
||||
},
|
||||
{
|
||||
Name: "125",
|
||||
buttonLabel: "Powell",
|
||||
buttonLabelSubCopy: "7 mi",
|
||||
buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223",
|
||||
},
|
||||
];
|
||||
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: null,
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
|
||||
wrapper.vm.shops = shopQuestionInitialData;
|
||||
wrapper.vm.answers = displayedAnswers;
|
||||
wrapper.vm.shopIndex = 3;
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
||||
|
||||
showMoreShopsLink.trigger("click");
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.answers.length).toBe(6);
|
||||
expect(wrapper.vm.answers).toEqual([
|
||||
{
|
||||
Name: "123",
|
||||
buttonLabel: "Worthington",
|
||||
buttonLabelSubCopy: "1.5 mi",
|
||||
buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085",
|
||||
},
|
||||
{
|
||||
Name: "124",
|
||||
buttonLabel: "Columbus",
|
||||
buttonLabelSubCopy: "4.5 mi",
|
||||
buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230",
|
||||
},
|
||||
{
|
||||
Name: "125",
|
||||
buttonLabel: "Powell",
|
||||
buttonLabelSubCopy: "7 mi",
|
||||
buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223",
|
||||
},
|
||||
{
|
||||
Name: "126",
|
||||
buttonLabel: "Chillicothe",
|
||||
buttonLabelSubCopy: "41.5 mi",
|
||||
buttonBodyCopy: "555 First Capital Ln, Chillicothe, OH 45601",
|
||||
},
|
||||
{
|
||||
Name: "127",
|
||||
buttonLabel: "Grove City",
|
||||
buttonLabelSubCopy: "4.5 mi",
|
||||
buttonBodyCopy: "5486 N Grove Rd, Grove City, OH 43215",
|
||||
},
|
||||
{
|
||||
Name: "128",
|
||||
buttonLabel: "Dayton",
|
||||
buttonLabelSubCopy: "80.5 mi",
|
||||
buttonBodyCopy: "1670 Bongo Ave D, Dayton, OH 43223",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("Should display the number of shops necessary to show a previously selected shop", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: "127",
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.answers.length).toEqual(5);
|
||||
});
|
||||
|
||||
it("Should reset the answers when the selected appointment type changes", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: "127",
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
||||
|
||||
wrapper.setProps({
|
||||
selectedAppointmentType: "Inshop",
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.answers.length).toEqual(3);
|
||||
});
|
||||
|
||||
it("Should reload the shops when the service zip code changes", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: "127",
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.shops = shopQuestionInitialData;
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
wrapper.vm.$options.methods.loadInitialData = jest.fn().mockImplementation(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve(newShopList);
|
||||
});
|
||||
});
|
||||
await wrapper.vm.$options.watch.serviceZipCode.handler.call(wrapper.vm, "43054");
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.shops.length).toEqual(3);
|
||||
expect(wrapper.vm.shops).toEqual(mockNewShopList.data);
|
||||
});
|
||||
|
||||
it("Should clear the existing answers when the service zip code changes", async () => {
|
||||
// Arrange/Act
|
||||
const container = document.createElement("div");
|
||||
container.scrollTo = jest.fn();
|
||||
|
||||
container.classList.add("page-container-grouped-styles");
|
||||
document.body.appendChild(container);
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
modelValue: "127",
|
||||
serviceZipCode: "43081",
|
||||
selectedAppointmentType: "Dropoff",
|
||||
cmsWidgetName: cmsWidgetName,
|
||||
},
|
||||
mountOptions: {
|
||||
attachTo: document.body,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
|
||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
||||
|
||||
wrapper.setProps({
|
||||
selectedAppointmentType: "Inshop",
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.answers.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({ mountOptions, mixins, props, isShallowMount = true }) {
|
||||
const resultingMountOptions = getMountOptions({
|
||||
...mountOptions,
|
||||
mixins,
|
||||
});
|
||||
|
||||
if (props) resultingMountOptions.propsData = props;
|
||||
|
||||
const wrapper = isShallowMount
|
||||
? shallowMount(shopQuestion, resultingMountOptions)
|
||||
: mount(shopQuestion, resultingMountOptions);
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
228
src/layouts/service-location/shop-question/shop-question.vue
Normal file
228
src/layouts/service-location/shop-question/shop-question.vue
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div class="shop-question" aria-live="polite">
|
||||
<alert
|
||||
ref="alertDropoffInformation"
|
||||
v-if="displayDropoffInformation"
|
||||
class="mb-4 drop-off-alert"
|
||||
cmsWidgetName="AlertDropoffInformationWidget"
|
||||
alertClass="alert-info"
|
||||
v-bind:isDismissible="false" />
|
||||
<buttonQuestion
|
||||
ref="buttonQuestion"
|
||||
buttonTypeString="shopListButton"
|
||||
:buttonTypeObject="shopListButton"
|
||||
class="radioQuestion"
|
||||
:questionText="questionText"
|
||||
:answers="answers"
|
||||
groupName="chooseShop"
|
||||
textPosition="text-start"
|
||||
v-model="selectedValue"
|
||||
isRequired
|
||||
validationRules="option-required" />
|
||||
<textLink
|
||||
v-if="displaySeeMoreLocationsLink"
|
||||
ref="showMoreShopsLink"
|
||||
class="show-more-shops-link"
|
||||
id="showMoreShopsId"
|
||||
cmsWidgetName="ShowMoreShopsLinkWidget"
|
||||
linkType="text"
|
||||
:text="showMoreShopsLinkText"
|
||||
href="#!"
|
||||
@click-event="getNextShopsFromList"
|
||||
:aria-label="showMoreShopsLinkText" />
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
import buttonQuestion from "@/digital-components/button-question/button-question";
|
||||
import shopListButton from "./shop-list-button/shop-list-button";
|
||||
import textLink from "@/ux-components/text-link/text-link";
|
||||
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
|
||||
// Supporting files
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
|
||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "shop-question",
|
||||
mixins: [baseMixin],
|
||||
data() {
|
||||
return {
|
||||
shops: [],
|
||||
shopListButton: shopListButton,
|
||||
availability: "high",
|
||||
answers: [],
|
||||
shopIndex: 0,
|
||||
displaySeeMoreLocationsLink: false,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
serviceZipCode: String,
|
||||
selectedAppointmentType: String,
|
||||
cmsWidgetName: String,
|
||||
validationRules: String,
|
||||
},
|
||||
computed: {
|
||||
questionText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||
},
|
||||
selectedValue: {
|
||||
get: function () {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
displayDropoffInformation() {
|
||||
return this.selectedAppointmentType == "Dropoff";
|
||||
},
|
||||
showMoreShopsLinkText() {
|
||||
return this.getCmsContent("ShowMoreShopsLinkWidget", "Text");
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadInitialData() {
|
||||
return baseMixin.methods.dispatchStoreAction(storeActions.GET_PROVIDER_LOCATIONS, {
|
||||
serviceZipCode: this.serviceZipCode,
|
||||
});
|
||||
},
|
||||
initializeComponent(shopQuestionInitialData) {
|
||||
this.shops = shopQuestionInitialData;
|
||||
},
|
||||
async getNextShopsFromList(numberToGet = 3) {
|
||||
const shopIterator = (array, n) => {
|
||||
let l = array.length;
|
||||
return () => {
|
||||
var end = this.shopIndex + n;
|
||||
var part = array.slice(this.shopIndex, end);
|
||||
this.shopIndex = end < l ? end : this.shops.length;
|
||||
return part;
|
||||
};
|
||||
};
|
||||
|
||||
const nextShop = shopIterator(this.shops, numberToGet);
|
||||
|
||||
// Map API result data
|
||||
const mappedData = nextShop().map((shop) => {
|
||||
return {
|
||||
Name: shop.providerNumber,
|
||||
buttonLabel: shop.city,
|
||||
buttonLabelSubCopy: `${shop.distance} mi`,
|
||||
buttonBodyCopy: `${shop.streetAddress}, ${shop.city}, ${shop.state} ${shop.zipCode}`,
|
||||
//buttonAuxillaryCopy: `foo`,
|
||||
};
|
||||
});
|
||||
|
||||
if (this.answers.length === 0) {
|
||||
this.answers = mappedData;
|
||||
} else {
|
||||
mappedData.forEach((shop) => {
|
||||
this.answers.push(shop);
|
||||
});
|
||||
}
|
||||
|
||||
await this.$nextTick();
|
||||
|
||||
if (this.shopIndex == this.shops.length) {
|
||||
this.displaySeeMoreLocationsLink = false;
|
||||
} else {
|
||||
this.displaySeeMoreLocationsLink = true;
|
||||
}
|
||||
|
||||
await this.$nextTick();
|
||||
|
||||
this.scrollToBottom();
|
||||
},
|
||||
resetShopList() {
|
||||
this.answers = [];
|
||||
this.shopIndex = 0;
|
||||
this.selectedValue = "";
|
||||
this.$refs.buttonQuestion.resetField();
|
||||
},
|
||||
scrollToBottom() {
|
||||
const container = document.getElementsByClassName("page-container-grouped-styles")[0];
|
||||
container.scrollTo({ top: container.scrollHeight, left: 0, behavior: "smooth" });
|
||||
},
|
||||
async reloadShopData(serviceZipCode) {
|
||||
const result = await this.loadInitialData(serviceZipCode);
|
||||
this.initializeComponent(result.data);
|
||||
this.resetShopList();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
serviceZipCode: {
|
||||
async handler(newValue) {
|
||||
await this.reloadShopData(newValue);
|
||||
},
|
||||
},
|
||||
selectedAppointmentType: {
|
||||
async handler(newValue) {
|
||||
// If the validation has been previously triggered, clear it before displaying the component
|
||||
this.$refs.buttonQuestion.resetField();
|
||||
|
||||
this.resetShopList();
|
||||
await this.getNextShopsFromList();
|
||||
|
||||
await this.$nextTick();
|
||||
|
||||
this.scrollToBottom();
|
||||
},
|
||||
},
|
||||
shops: {
|
||||
handler(newValue) {
|
||||
this.$refs.buttonQuestion.resetField();
|
||||
const selectedShopIndex = newValue.findIndex(
|
||||
(provider) => provider.providerNumber == this.modelValue
|
||||
);
|
||||
|
||||
if (selectedShopIndex >= 3) {
|
||||
this.getNextShopsFromList(selectedShopIndex + 1);
|
||||
} else {
|
||||
this.getNextShopsFromList();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
alert,
|
||||
buttonQuestion,
|
||||
textLink,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.shop-question {
|
||||
margin-top: 1rem !important;
|
||||
text-align: center !important;
|
||||
|
||||
.button-question {
|
||||
.question-text {
|
||||
margin-top: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drop-off-alert {
|
||||
.alert-heading {
|
||||
text-align: left;
|
||||
font-size: 0.75rem !important;
|
||||
line-height: 1.25rem !important;
|
||||
}
|
||||
|
||||
margin-top: 0.5rem !important;
|
||||
padding-left: 1.5rem !important;
|
||||
padding-right: 0.5rem !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -980,6 +980,14 @@ export const actions = {
|
|||
});
|
||||
},
|
||||
|
||||
getProviderLocations(context, { serviceZipCode }) {
|
||||
return globalMethods.callMockHttpClient({
|
||||
method: endpoints.GetProviderLocations.method,
|
||||
//TODO: Remove Mocky Endpoints
|
||||
endpoint: "https://run.mocky.io/v3/abf2fa63-8287-4e46-b169-f5bec65c8dff",
|
||||
});
|
||||
},
|
||||
|
||||
getSupportingItems(context) {
|
||||
const glassPartsArray = context.getters.lineItems.glassParts ?? [];
|
||||
const carId = context.getters.vehicle.carId;
|
||||
|
|
|
|||
Loading…
Reference in a new issue