commit
0359697212
6 changed files with 206 additions and 4 deletions
|
|
@ -4,8 +4,14 @@
|
|||
:modalId="ModalName"
|
||||
:footerButtonText="ModalCloseButtonText"
|
||||
@footer-button-event="footerButtonClick">
|
||||
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
|
||||
<h5 class="mb-4" v-html="ModalHeadline"></h5>
|
||||
<div v-if="ModalName == 'RecalModal'">
|
||||
<h5 class="mb-4 recal-header" v-html="ModalHeadline"></h5>
|
||||
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
|
||||
<h5 class="mb-4" v-html="ModalHeadline"></h5>
|
||||
</div>
|
||||
<p class="fw-bold mb-2 subheader-text" v-html="ModalSubheadertext"></p>
|
||||
<p class="mb-0" v-html="ModalBodyText"></p>
|
||||
<p
|
||||
|
|
@ -78,6 +84,10 @@ export default {
|
|||
.modal-dialog {
|
||||
.modal-content {
|
||||
.modal-body {
|
||||
.recal-header {
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.modal-sub-body {
|
||||
color: $gray-600;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-h
|
|||
import { nextTick } from "vue";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { containsLineItemWithPartType } from "../../helpers/service-package-helper";
|
||||
|
||||
jest.mock("@/store", () => ({
|
||||
commit: jest.fn(),
|
||||
|
|
@ -22,6 +23,9 @@ jest.mock("@/helpers/cms-content-helper", () => ({
|
|||
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||
navigateToHeritageFunnel: jest.fn(),
|
||||
}));
|
||||
jest.mock("@/helpers/service-package-helper", () => ({
|
||||
containsLineItemWithPartType: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("@/helpers/promotions-helper", () => ({
|
||||
revalidatePromosAndValidateQueryStringPromo: jest.fn(() => {
|
||||
|
|
|
|||
|
|
@ -36,12 +36,19 @@
|
|||
isRequired />
|
||||
|
||||
<afterpayModalBanner
|
||||
v-if="!isInsuranceSelected"
|
||||
v-if="!isInsuranceSelected && !isRecalibrationOnOrder"
|
||||
cmsWidgetName="AfterpayModalWidget"
|
||||
:activePromos="lineItems.promos"
|
||||
:availableLineItems="availableLineItems"
|
||||
:lineItems="lineItems" />
|
||||
|
||||
<recalDisclaimer
|
||||
class="my-5"
|
||||
cmsWidgetName="RecalDisclaimerWidget"
|
||||
v-if="isRecalibrationOnOrder"
|
||||
v-on="{ textLinkClicked: openModalAction }"
|
||||
alertClass="alert-info" />
|
||||
|
||||
<promoModalQuestion
|
||||
class="small mt-4"
|
||||
v-model="lineItems"
|
||||
|
|
@ -86,6 +93,7 @@ import textBlock from "@/digital-components/text-block/text-block";
|
|||
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
|
||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||
import afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-modal-banner";
|
||||
import recalDisclaimer from "@/layouts/quote/recal-disclaimer/recal-disclaimer.vue";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";
|
||||
|
|
@ -108,6 +116,8 @@ import { queryStrings } from "@/constants/query-strings";
|
|||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||
import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||
import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
|
||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
|
|
@ -256,6 +266,12 @@ export default {
|
|||
lineItemsCloneForWatcher() {
|
||||
return Object.assign({}, this.lineItems);
|
||||
},
|
||||
isRecalibrationOnOrder() {
|
||||
return containsLineItemWithPartType(
|
||||
partTypeStrings.RECALIBRATION,
|
||||
this?.availableLineItems
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModalAction(modalName) {
|
||||
|
|
@ -405,6 +421,7 @@ export default {
|
|||
loadingModal,
|
||||
afterpayModalBanner,
|
||||
promoModalQuestion,
|
||||
recalDisclaimer,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
51
src/layouts/quote/recal-disclaimer/recal-disclaimer.spec.js
Normal file
51
src/layouts/quote/recal-disclaimer/recal-disclaimer.spec.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { mount } from "@vue/test-utils";
|
||||
import recalDisclamer from "./recal-disclaimer";
|
||||
|
||||
describe("recal-disclaimer.vue", () => {
|
||||
it("Should display header text when HeaderText is defined in the CMS", async () => {
|
||||
const wrapper = mount(recalDisclamer, {
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
cmsWidgetName: "test",
|
||||
},
|
||||
attachTo: document.body,
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
|
||||
});
|
||||
|
||||
it("Should insert image url when Image is defined in the CMS", async () => {
|
||||
const wrapper = mount(recalDisclamer, {
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
cmsWidgetName: "test",
|
||||
},
|
||||
attachTo: document.body,
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"]));
|
||||
});
|
||||
|
||||
it("Should display subheader text when SubheaderText is defined in the CMS", async () => {
|
||||
const wrapper = mount(recalDisclamer, {
|
||||
mixins: [mockMixin],
|
||||
props: {
|
||||
cmsWidgetName: "test",
|
||||
},
|
||||
attachTo: document.body,
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"]));
|
||||
});
|
||||
});
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||
return mockCmsContent[cmsFieldName];
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const mockCmsContent = {
|
||||
HeaderText: "Sample header text here.",
|
||||
SubheaderText: "Sample subheader text here.",
|
||||
Image: "https://www.sampleImage.sample",
|
||||
};
|
||||
115
src/layouts/quote/recal-disclaimer/recal-disclaimer.vue
Normal file
115
src/layouts/quote/recal-disclaimer/recal-disclaimer.vue
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<template>
|
||||
<div class="alert fade show alert-info recal-alert" role="alert">
|
||||
<div class="alert-heading">
|
||||
<span>
|
||||
<img v-if="hasImage" :src="imageUrl" />
|
||||
<span class="px-2"> {{ recalHeaderText }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span v-for="copy in recalBody" :key="copy">
|
||||
<span v-if="doesCopyContainTextLink(copy)" class="recal-link">
|
||||
<textLink
|
||||
linkType="text"
|
||||
:text="getRouterLinkDisplayTextFromCopy(copy)"
|
||||
href="#!"
|
||||
@click-event="$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))"
|
||||
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)" />
|
||||
</span>
|
||||
<span v-else v-html="copy" class="alert-body"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import textLink from "@/ux-components/text-link/text-link";
|
||||
|
||||
// Supporting files
|
||||
import {
|
||||
doesCopyContainTextLink,
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
getRouterLinkRouteFromCopy,
|
||||
getRouterLinkDisplayTextFromCopy,
|
||||
getExternalLink,
|
||||
} from "@/helpers/cms-content-helper";
|
||||
|
||||
function getInlineAltText(token) {
|
||||
const innerTokens = token.split(",");
|
||||
|
||||
return innerTokens[1] ?? "";
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "recal-disclaimer",
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
getInlineAltText,
|
||||
doesCopyContainTextLink,
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
getRouterLinkRouteFromCopy,
|
||||
getRouterLinkDisplayTextFromCopy,
|
||||
getExternalLink,
|
||||
},
|
||||
computed: {
|
||||
imageUrl() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "Image");
|
||||
},
|
||||
hasImage() {
|
||||
return !!this.imageUrl;
|
||||
},
|
||||
recalHeaderText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||
},
|
||||
recalBody() {
|
||||
return splitCopyOnCMSPlaceHolder(
|
||||
this.getCmsContent(this.cmsWidgetName, "SubheaderText")
|
||||
);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
textLink,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.alert {
|
||||
text-align: center;
|
||||
&.recal-alert {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
&.alert-info {
|
||||
background-color: #e5f1fa;
|
||||
color: #525656;
|
||||
.alert-heading {
|
||||
color: #0070d1;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 17.3px;
|
||||
}
|
||||
svg {
|
||||
fill: #0070d1;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
& .alert-heading {
|
||||
font-size: 14px;
|
||||
}
|
||||
.alert-body {
|
||||
font-size: 14px;
|
||||
font-weight: 400px;
|
||||
}
|
||||
.recal-link {
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -234,10 +234,15 @@ export default {
|
|||
},
|
||||
getPackagePrice(packageName, { discountedPrice = false, servicePackageDiscount = false }) {
|
||||
var lineItemsToPrice = [...this.nullSafeAvailableLineItems];
|
||||
if (this.isRecalibrationOnOrder) {
|
||||
lineItemsToPrice = this.nullSafeAvailableLineItems.filter((item) => {
|
||||
return item.partType != partTypeStrings.RECALIBRATION;
|
||||
});
|
||||
}
|
||||
|
||||
//remove service package discount part
|
||||
if (this.isServicePackageDiscountOnOrder) {
|
||||
lineItemsToPrice = this.nullSafeAvailableLineItems.filter((item) => {
|
||||
lineItemsToPrice = lineItemsToPrice.filter((item) => {
|
||||
return item.partType != this.servicePackageDiscountParts[0].partType;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue