Updating ways special strings are parsed

This commit is contained in:
Max 2022-04-19 10:26:31 -04:00
parent 7150f5fd9b
commit 18710d1469
9 changed files with 75 additions and 124 deletions

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="textbox-question" :class="(errors.length > 0 || hasError) ? 'has-error' : ''"> <div class="textbox-question" :class="(hasError) ? 'has-error' : ''">
<label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label> <label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label>
<input v-model="value" <input v-model="value"
v-maska="mask" v-maska="mask"

View file

@ -0,0 +1,7 @@
const magicStrings = {
GLOBAL_STATE: "globalState",
CUSTOM: "custom",
ROUTER_LINK: "routerLink"
};
export { magicStrings };

View file

@ -1,5 +1,6 @@
import { storeActions } from "@/constants/store-actions.js"; import { storeActions } from "@/constants/store-actions.js";
import store from "@/store"; import store from "@/store";
import { magicStrings } from "../constants/magic-strings";
export function fetchCmsContentForPage(fmgPage) { export function fetchCmsContentForPage(fmgPage) {
return store return store
@ -42,7 +43,7 @@ function mapStringToState(str) {
const regexExp = new RegExp("{(.*?):(.*?)}", "g"); const regexExp = new RegExp("{(.*?):(.*?)}", "g");
const regexMatches = [...str.matchAll(regexExp)]; const regexMatches = [...str.matchAll(regexExp)];
const globalStateMatches = regexMatches.filter(match => { const globalStateMatches = regexMatches.filter(match => {
return match[1] === "globalState"; return match[1] === magicStrings.GLOBAL_STATE;
}) })
// Our final string value that will be built from the matches. // Our final string value that will be built from the matches.
@ -63,7 +64,7 @@ function mapStringToState(str) {
const stringWithReplacement = str.replace(match[0], storeState); const stringWithReplacement = str.replace(match[0], storeState);
// If we still have values we need to substitute, call this function again. // If we still have values we need to substitute, call this function again.
if (stringWithReplacement.includes("{globalState:")) { if (stringWithReplacement.includes(magicStrings.GLOBAL_STATE)) {
return mapStringToState(stringWithReplacement); return mapStringToState(stringWithReplacement);
} }
@ -99,7 +100,7 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
function processWidgetItemForReplacement(widgetModel, key) { function processWidgetItemForReplacement(widgetModel, key) {
// If we have a string, and it needs to be replaced. // If we have a string, and it needs to be replaced.
if (typeof widgetModel[key] === "string") { if (typeof widgetModel[key] === "string") {
if (widgetModel[key].includes("{globalState:")) { if (widgetModel[key].includes(magicStrings.GLOBAL_STATE)) {
widgetModel[key] = mapStringToState(widgetModel[key]); widgetModel[key] = mapStringToState(widgetModel[key]);
} }
return widgetModel[key]; return widgetModel[key];
@ -119,4 +120,4 @@ function processWidgetItemForReplacement(widgetModel, key) {
// If we have something else like a number, boolean, etc. just return it // If we have something else like a number, boolean, etc. just return it
return widgetModel[key]; return widgetModel[key];
} }

View file

@ -0,0 +1,5 @@
import store from "@/store";
export function getDamageString() {
return store.getters.damage.glassToReplace.length > 1 ? "match" : store.getters.damage.glassToReplace[0].location;
}

View file

@ -43,10 +43,10 @@ export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHerita
return 'vehicle-damage' return 'vehicle-damage'
} else { } else {
if (store.getters.vehicle.vin) { if (store.getters.vehicle.vin) {
return 'vehicle-damage'; return 'license-plate-lookup';
//return "vin-lookup"; (uncomment) //return "vin-lookup"; (uncomment)
} else { } else {
return 'vehicle-damage'; return 'license-plate-lookup';
//return "estimate" (uncomment) //return "estimate" (uncomment)
} }
} }
@ -66,6 +66,9 @@ export async function navigateToHeritageFunnel() {
{ {
corid: store.getters.order.referralCorrelationId, corid: store.getters.order.referralCorrelationId,
src: "concept-funnel", src: "concept-funnel",
// TODO CSR-28, remove this
cns: "all",
experiments: "RemoveServiceAreaPage=ServAreaRemoval_V7=NoShowPackages_CONTROL=true,ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_TEST=true"
} }
); );
} }

View file

@ -26,22 +26,21 @@
</div> </div>
<alert <alert
class="my-3" class="my-3"
cmsWidgetName="NoServiceZipWidget" :manualHeadline="NoServiceZipHeader"
v-model="customAlertData" :manualCopy="NoServiceZipBody"
v-if="newServiceZipRequired" v-if="newServiceZipRequired"
alertClass="alert-danger" alertClass="alert-danger"
/> />
<alert <alert
class="my-3" class="my-3"
cmsWidgetName="NoMatchAlertWidget" cmsWidgetName="NoMatchAlertWidget"
v-model="customAlertData"
v-if="vinNotValid" v-if="vinNotValid"
alertClass="alert-danger" alertClass="alert-danger"
/> />
<alert <alert
class="my-3" class="my-3"
cmsWidgetName="MatchedDifferentVehicleAlertWidget" :manualHeadline="MatchedDifferentVehicleAlertHeader"
v-model="customAlertData" :manualCopy="MatchedDifferentVehicleAlertBody"
v-if="vinDoesNotMatchCarId" v-if="vinDoesNotMatchCarId"
alertClass="alert-warning" alertClass="alert-warning"
/> />
@ -78,6 +77,7 @@ import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations"; import { storeMutations } from "@/constants/store-mutations";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
import { getDamageString } from "@/helpers/damage-helper";
import { required, regex } from "@/helpers/validation-rules"; import { required, regex } from "@/helpers/validation-rules";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
@ -125,6 +125,35 @@ export default {
customAlertData: {} customAlertData: {}
}; };
}, },
computed: {
MatchedDifferentVehicleAlertHeader(){
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "HeadlineText");
// Replace all instances where custom data is implemented
text = text.replaceAll("{custom:damage}", getDamageString());
return text;
},
MatchedDifferentVehicleAlertBody(){
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "BodyText");
// Replace all instances where custom data is implemented
text = text.replaceAll("{custom:damage}", getDamageString());
text = text.replaceAll("{custom:plateLookupYear}", this.customAlertData?.vehicleInfo?.year);
text = text.replaceAll("{custom:plateLookupMake}", this.customAlertData?.vehicleInfo?.make);
text = text.replaceAll("{custom:plateLookupModel}", this.customAlertData?.vehicleInfo?.model);
return text;
},
NoServiceZipHeader(){
let text = this.getCmsContent("NoServiceZipWidget", "HeadlineText");
// Replace all instances where custom data is implemented
text = text.replaceAll("{custom:zip}", this.zip);
return text;
},
NoServiceZipBody(){
return this.getCmsContent("NoServiceZipWidget", "BodyText");
}
},
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return store.getters.vehicle.carId !== null; return store.getters.vehicle.carId !== null;
@ -171,7 +200,7 @@ export default {
vehicleInfo = store.getters.vehicle; vehicleInfo = store.getters.vehicle;
} }
this.updateStore(vin, vehicleInfo, zipValidation.data.state); this.updateCustomerInfo(vin, vehicleInfo, zipValidation.data.state);
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction( const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
this.storeActions.GET_PARTS_OR_QUESTIONS, this.storeActions.GET_PARTS_OR_QUESTIONS,
@ -209,7 +238,7 @@ export default {
{ licensePlate: plate, licenseState: state } { licensePlate: plate, licenseState: state }
); );
}, },
updateStore(vin, vehicleInfo, registrationState) { updateCustomerInfo(vin, vehicleInfo, registrationState) {
// if(vehicleDamage){ // if(vehicleDamage){
// store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); // store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
// } // }

View file

@ -5,7 +5,7 @@
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<h1>Part Questions Page Placeholder</h1> <h1>Part Questions Page Placeholder</h1>
<funnel-footer <funnel-footer
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget" @back-clicked="backButtonAction"
/> />
</div> </div>
</template> </template>

View file

@ -1,5 +1,4 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import { createStore } from 'vuex'
import alert from "./alert"; import alert from "./alert";
describe("alert.vue", () => { describe("alert.vue", () => {
@ -11,7 +10,7 @@ describe("alert.vue", () => {
isDismissible: true isDismissible: true
}, },
computed: { computed: {
alertCopy: { splitAlertCopyForLink: {
get() { get() {
return "TEST"; return "TEST";
}, },
@ -33,7 +32,7 @@ describe("alert.vue", () => {
alertClass: 'warning' alertClass: 'warning'
}, },
computed: { computed: {
alertCopy: { splitAlertCopyForLink: {
get() { get() {
return "TEST"; return "TEST";
}, },
@ -48,9 +47,10 @@ describe("alert.vue", () => {
expect(wrapperDiv.classes()).toContain('warning') expect(wrapperDiv.classes()).toContain('warning')
}); });
it("Should return original string if no custom string notations added", async () => { it("Should update headline when manual headline added", async () => {
// Arrange // Arrange
const wrapper = shallowMount(alert, { const wrapper = shallowMount(alert, {
computed: { computed: {
alertCopy: { alertCopy: {
get() { get() {
@ -61,69 +61,11 @@ describe("alert.vue", () => {
mixins: [mockMixin] mixins: [mockMixin]
}); });
const stringWithNoCustomAtrributes = wrapper.vm.checkForCustomStrings("Test string") await wrapper.vm.$nextTick();
const wrapperDiv = wrapper.find('.text-body');
// Assert // Assert
expect(stringWithNoCustomAtrributes).toEqual("Test string") expect(wrapperDiv.text()).toEqual('TEST')
});
it("Should return link when custom string notation with 'routerLink' included", async () => {
// Arrange
const wrapper = shallowMount(alert, {
computed: {
alertCopy: {
get() {
return "TEST";
},
}
},
mixins: [mockMixin]
});
const stringWithNoCustomAtrributes = wrapper.vm.checkForCustomStrings("{routerLink:clickedChangeVinLookupMethod,provide your VIN}")
// Assert
expect(stringWithNoCustomAtrributes).toEqual("{routerLink:clickedChangeVinLookupMethod,provide your VIN}")
});
it("Should return zip when custom string notation with 'custom' for zip is called.", async () => {
// Arrange
const wrapper = shallowMount(alert, {
propsData: {modelValue: {zip: "12345"}},
computed: {
alertCopy: {
get() {
return "TEST";
},
}
},
mixins: [mockMixin]
});
const stringWithNoCustomAtrributes = wrapper.vm.checkForCustomStrings("{custom:zip}")
// Assert
expect(stringWithNoCustomAtrributes).toEqual("12345")
});
it("Should return custom datum specified when custom string notation with 'custom' for lookup is called.", async () => {
// Arrange
const wrapper = shallowMount(alert, {
propsData: {modelValue: {vehicleInfo: {year: "2020"}}},
computed: {
alertCopy: {
get() {
return "TEST";
},
}
},
mixins: [mockMixin]
});
const stringWithNoCustomAtrributes = wrapper.vm.checkForCustomStrings("{custom:plateLookupYear}")
// Assert
expect(stringWithNoCustomAtrributes).toEqual("2020")
}); });
}); });

View file

@ -4,9 +4,9 @@
role="alert" role="alert"
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]" :class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]"
> >
<p class="m-0 fw-bold small alert-heading" v-html="alertHeadline"></p> <p class="m-0 fw-bold small alert-heading">{{ alertHeadline }}</p>
<p v-if="alertCopy.includes('routerLink:')"> <p v-if="splitAlertCopyForLink.length">
<template v-for="copy in alertCopy.split(/{(.*?)}/g)" :key="copy"> <template v-for="copy in splitAlertCopyForLink" :key="copy">
<span v-if="copy.includes('routerLink:')" class="m-0 text-body small"> <span v-if="copy.includes('routerLink:')" class="m-0 text-body small">
<router-link :to="{query: {fmgPage: `${copy.split(':')[1].split(',')[0]}`}, name: 'root'}">{{ copy.split(':')[1].split(',')[1] }}</router-link> <router-link :to="{query: {fmgPage: `${copy.split(':')[1].split(',')[0]}`}, name: 'root'}">{{ copy.split(':')[1].split(',')[1] }}</router-link>
</span> </span>
@ -34,7 +34,6 @@
</template> </template>
<script> <script>
import store from "@/store";
export default { export default {
name: "alert", name: "alert",
@ -51,52 +50,17 @@ export default {
cmsWidgetName: String, cmsWidgetName: String,
manualHeadline: String, manualHeadline: String,
manualCopy: String, manualCopy: String,
modelValue: {}
}, },
computed: { computed: {
alertHeadline(){ alertHeadline(){
return this.cmsWidgetName ? this.checkForCustomStrings(this.getCmsContent(this.cmsWidgetName, 'HeadlineText')) : this.manualHeadline; return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'HeadlineText') : this.manualHeadline;
}, },
alertCopy(){ alertCopy(){
return this.cmsWidgetName ? this.checkForCustomStrings(this.getCmsContent(this.cmsWidgetName, 'BodyText')) : this.manualCopy; return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'BodyText') : this.manualCopy;
}, },
}, splitAlertCopyForLink(){
methods: { // Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
checkForCustomStrings(str){ return this.alertCopy.split(/{(.*?)}/g);
let returnElement = "";
if(str.includes("{custom:")){
const regexExp = new RegExp("{(.*?)}", "g");
const splitOnSpecialData = str.split(regexExp);
for(const item of splitOnSpecialData){
let itemToAdd;
if(item.includes("custom:")){
const splitItem = item.split(":");
const customDataAttribute = splitItem[1].includes("plateLookup") ? splitItem[1].split("plateLookup")[1].toLowerCase() : splitItem[1];
switch(customDataAttribute){
case "damage":
itemToAdd = store.getters.damage.glassToReplace.length > 1 ? "match" : store.getters.damage.glassToReplace[0].location;
break;
case "zip":
itemToAdd = this.modelValue.zip;
break;
default:
itemToAdd = this.modelValue.vehicleInfo[customDataAttribute];
}
} else if(item.includes("routerLink:")){
itemToAdd = `{${item}}`;
}
else {
itemToAdd = item;
}
returnElement += itemToAdd;
}
} else {
returnElement = str;
}
return returnElement;
} }
}, },
}; };