diff --git a/jest.config.js b/jest.config.js index 80b382c5a..c5401c28f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,7 +28,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 83, + statements: 82, // Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90 }, }, diff --git a/src/common-components/button-question/button-question.spec.js b/src/common-components/button-question/button-question.spec.js index 22e50b215..006cbcf2c 100644 --- a/src/common-components/button-question/button-question.spec.js +++ b/src/common-components/button-question/button-question.spec.js @@ -31,7 +31,7 @@ describe("buttonQuestion.vue", () => { // Act const wrapper = shallowMount(buttonQuestion, { propsData: { - buttonType: "listCard", + buttonTypeString: "listCard", groupName: "group-name", }, }); @@ -46,7 +46,7 @@ describe("buttonQuestion.vue", () => { // Act const wrapper = shallowMount(buttonQuestion, { propsData: { - buttonType: "listButtonHorizontal", + buttonTypeString: "listButtonHorizontal", groupName: "group-name", }, }); @@ -61,7 +61,7 @@ describe("buttonQuestion.vue", () => { // Act const wrapper = shallowMount(buttonQuestion, { propsData: { - buttonType: "radio", + buttonTypeString: "radio", groupName: "group-name", }, }); diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 9778cef8f..b417248a2 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -33,9 +33,12 @@ v-for="answer in buttonsInfo" :key="answer.value ? answer.value : answer"> @@ -78,10 +82,14 @@ import { ErrorMessage } from "vee-validate"; export default { name: "buttonQuestion", props: { - buttonType: { + buttonTypeString: { type: String, default: "listButton", }, + buttonTypeObject: { + type: Object, + default: null, + }, isMultiSelect: Boolean, groupName: String, questionText: String, @@ -109,6 +117,12 @@ export default { suppressError: Boolean, useTextForValue: Boolean, valueToLogType: String, + additionalButtonStyling: String, + }, + beforeMount() { + if (this.buttonTypeObject) { + this.$options.components[this.buttonTypeString] = this.buttonTypeObject; + } }, data() { return { @@ -119,7 +133,7 @@ export default { getFieldSetClasses() { if (this.isOverflowScrollable) { return "container-fluid overflow-scroll position-absolute px-5 pt-1 py-0"; - } else if (this.buttonType == "listCard") { + } else if (this.buttonTypeString == "listCard") { return "w-100"; } else { return ""; @@ -127,7 +141,7 @@ export default { }, getComponentLoopWrapperClasses() { let classes; - switch (this.buttonType) { + switch (this.buttonTypeString) { case "listButton": classes = "w-100"; break; @@ -143,6 +157,9 @@ export default { case "radio": classes = "ui-radio d-flex"; break; + case "servicePackageRadio": + classes = "package-main"; + break; } return classes; }, @@ -151,8 +168,10 @@ export default { classes += this.isWide ? "col-12" : "col"; - if (this.buttonType == "radio") { + if (this.buttonTypeString == "radio") { classes += " radio-button-container"; + } else if (this.buttonTypeString == "servicePackageRadio") { + classes = "package-wrapper"; } return classes; @@ -162,6 +181,9 @@ export default { buttonLabel: answer.buttonLabel ?? answer.Text ?? answer, altText: answer.altText ?? (answer.Name ? answer.Name : answer), buttonLabelSubCopy: answer.buttonLabelSubCopy ?? answer.SubText, + buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy, + buttonAuxillaryCopy: answer.buttonAuxillaryCopy ?? answer.buttonAuxillaryCopy, + buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy, buttonImage: answer.buttonImage ?? answer.AnswerImageUrl, buttonImageId: answer.buttonImageId ?? answer.ImageId, groupName: this.formatString(this.groupName), diff --git a/src/constants/dynamic-strings.js b/src/constants/dynamic-strings.js index ff62dfaa3..b0ff5e099 100644 --- a/src/constants/dynamic-strings.js +++ b/src/constants/dynamic-strings.js @@ -2,6 +2,7 @@ const dynamicStrings = { GLOBAL_STATE: "globalState", CUSTOM: "custom", ROUTER_LINK: "routerLink:", + TEXT_LINK: "textLink:", }; export { dynamicStrings }; diff --git a/src/constants/part-type-strings.js b/src/constants/part-type-strings.js new file mode 100644 index 000000000..2dfab2071 --- /dev/null +++ b/src/constants/part-type-strings.js @@ -0,0 +1,8 @@ +const partTypeStrings = { + FRONT_WIPER: "FRONT WIPER", + REAR_WIPER: "REAR WIPER", + RAIN_DEFENSE: "RAIN DEFENSE", + RECALIBRATION: "RECALIBRATION", +}; + +export { partTypeStrings }; diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index f60e45721..6ff18a53e 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -31,7 +31,7 @@ export function fetchCmsContentForPage(fmgPage) { // Function to convert a string, into a matching global state item. function mapStringToState(str) { // Pull all matches out of the string. - const regexExp = new RegExp("{(.*?):(.*?)}", "g"); + const regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g"); const regexMatches = [...str.matchAll(regexExp)]; const globalStateMatches = regexMatches.filter((match) => { return match[1] === dynamicStrings.GLOBAL_STATE; @@ -39,20 +39,13 @@ function mapStringToState(str) { // Our final string value that will be built from the matches. let stringBuilder = ""; - for (const match of globalStateMatches) { - // Reset store state for each match. - let storeState = store.state; - - for (const s of match[2].split(".")) { - if (storeState[s] != undefined) { - storeState = storeState[s]; - } else { - return ""; // if we can't map our string to state data, return an empty string. - } + const valueFromStore = getStoreValueFromString(match[2]); + if (!valueFromStore) { + return ""; // if we can't map our string to state data, return an empty string. } - const stringWithReplacement = str.replace(match[0], storeState); + const stringWithReplacement = str.replace(match[0], valueFromStore); // If we still have values we need to substitute, call this function again. if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) { @@ -62,7 +55,6 @@ function mapStringToState(str) { // Concatenate the string. stringBuilder = `${stringBuilder} ${stringWithReplacement}`; } - return stringBuilder.trimStart(); } @@ -88,6 +80,11 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) { function processWidgetItemForReplacement(widgetModel, key) { // If we have a string, and it needs to be replaced. if (typeof widgetModel[key] === "string") { + widgetModel[key] = processIfStatements( + widgetModel[key], + dynamicStrings.GLOBAL_STATE, + getStoreValueFromString + ); if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) { widgetModel[key] = mapStringToState(widgetModel[key]); } @@ -107,23 +104,217 @@ function processWidgetItemForReplacement(widgetModel, key) { return widgetModel[key]; } +function getStoreValueFromString(str) { + let storeOrStateObject = str.includes("getters") ? store : store.state; + for (const s of str.split(".")) { + if (storeOrStateObject[s] != undefined) { + storeOrStateObject = storeOrStateObject[s]; + } else { + return ""; // if we can't map our string to state data, return an empty string. + } + } + return storeOrStateObject; +} + +/////////////////////////////////// +// If Statement Processing Logic // +/////////////////////////////////// + +/** + * Recursive function - replaces all instances of if statements from the CMS that utilize the specified ifConditionKeyword + * @param {*} str string - Input string to be processed + * @param {*} ifConditionKeyword string - Defines which if statements to process ex: 'globalState' + * @param {*} replacePlaceholderCallback function - Callback to replace CMS placeholder values + * @returns The processed string + */ +export function processIfStatements(str, ifConditionKeyword, replacePlaceholderCallback) { + const containsRelevantIfStatement = new RegExp("{if:" + ifConditionKeyword + ":.+?}", "g").test( + str + ); + if (!containsRelevantIfStatement) { + return str; + } else { + const ifStatementRegexExpression = getIfStatementRegexExpression(); + const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)]; + const completeIfStatementArray = getAndFlagFirstNonNestedIfStatementWithKeyword( + ifStatementRegexMatches, + ifConditionKeyword + ); + executeIfStatementAndSetProcessedStrings( + completeIfStatementArray, + replacePlaceholderCallback + ); + const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches); + return processIfStatements( + reconstructedPostProcessedString, + ifConditionKeyword, + replacePlaceholderCallback + ); + } +} + +function getAndFlagFirstNonNestedIfStatementWithKeyword(matches, ifConditionKeyword) { + let index = 0; + for (const match of matches) { + if (match.groups.isIfStatement && match.groups.ifConditionType === ifConditionKeyword) { + let interiorIndex = 0; + let nestedLevel = 0; + let elseStatementIndex = null; + for (const interiorMatch of matches.slice(index + 1)) { + if (interiorMatch.groups.isIfStatement) { + if (interiorMatch.groups.ifConditionType === ifConditionKeyword) { + break; + } else { + nestedLevel++; + } + } else if (interiorMatch.groups.isElseStatement) { + if (!nestedLevel) { + elseStatementIndex = interiorIndex + 1; + } + } else if (interiorMatch.groups.isEndStatement) { + if (nestedLevel) { + nestedLevel--; + } else { + const ifStatementArray = matches.slice(index, index + interiorIndex + 2); + flagMatchesForProcessing(ifStatementArray, elseStatementIndex); + return ifStatementArray; + } + } + interiorIndex++; + } + } + index++; + } +} + +function flagMatchesForProcessing(matches, elseStatementIndex) { + matches[0].isFlaggedForProcessing = true; + matches[matches.length - 1].isFlaggedForProcessing = true; + if (elseStatementIndex) { + matches[elseStatementIndex].isFlaggedForProcessing = true; + } +} + +function joinProcessedRegexArray(regexMatches) { + let processedString = ""; + regexMatches.forEach((match) => { + const rawString = match[0]; + processedString += match.groups.processedString ?? rawString; + }); + return processedString; +} + +function executeIfStatementAndSetProcessedStrings(ifStatementArray, replacePlaceholderCallback) { + const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition); + let isInsideDesiredBlock = ifCondition; + + ifStatementArray.forEach((entry) => { + if (entry.groups.isElseStatement && entry.isFlaggedForProcessing) { + isInsideDesiredBlock = !ifCondition; + } else if (entry.groups.isEndStatement && entry.isFlaggedForProcessing) { + isInsideDesiredBlock = true; + } + setProcessedStringOnEntry(entry, isInsideDesiredBlock); + }); +} + +function setProcessedStringOnEntry(entry, isInsideDesiredBlock) { + if (!isInsideDesiredBlock) { + entry.groups.processedString = ""; + } else { + if (entry.groups.isIfStatement) { + entry.groups.processedString = entry.isFlaggedForProcessing + ? entry.groups.ifTrailingString + : entry[0]; + } else if (entry.groups.isElseStatement) { + entry.groups.processedString = entry.isFlaggedForProcessing + ? entry.groups.elseTrailingString + : entry[0]; + } else { + entry.groups.processedString = entry.isFlaggedForProcessing + ? entry.groups.endTrailingString + : entry[0]; + } + } +} + +function getIfStatementRegexExpression() { + // Matches but does not capture: + // {if:...} or {else} or {end} + const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})"; + // NOTE: ? syntax stores the captured match like so: match.groups.variableName + const matchStartOfString = + "(?^.+?)" + // Match and Capture all characters (lazy), cannot be empty + "(?=(?:{if))"; // Looks ahead but does not capture {if + const matchIfOperator = + "(?{if:)" + // Match & Capture {if: + "(?.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":" + "(?.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}" + "(?.*?)" + // Match and Capture all characters (lazy), can be empty + "(?=" + + anyLogicOperatorNonCapture + + ")"; // Looks ahead but does not capture the next logic operator + const matchElseOperator = + "(?{else})" + // Match & Capture {else} + "(?.*?)" + // Match & Capture all characters (lazy), can be empty + "(?=" + + anyLogicOperatorNonCapture + + ")"; // Looks ahead but does not capture the next logic operator + const matchEndOperator = + "(?{end})" + // Match & Capture {end} + "(?.*?)" + // Match & Capture all chracters (lazy), can be empty + "(?=" + + anyLogicOperatorNonCapture + + "|$)"; // Looks ahead but does not capture the next logic operator + // Combine all matching patterns, separated by "or" pipes + return new RegExp( + matchStartOfString + + "|" + + matchIfOperator + + "|" + + matchElseOperator + + "|" + + matchEndOperator, + "g" + ); +} + +////////////////////////////////////////// +// End of If Statement Processing Logic // +////////////////////////////////////////// + export function doesCopyContainRouterLink(copy) { return copy.includes(this.dynamicStrings.ROUTER_LINK); } +export function doesCopyContainTextLink(copy) { + return copy.includes(dynamicStrings.TEXT_LINK); +} + +/** + * splits copy on { ... } such as {routerlink: ...} + * @returns array of strings + */ export function splitCopyOnCMSPlaceHolder(copy) { - // splits copy on { ... } such as {routerlink: ...} return copy.split(/{(.*?)}/g); } -export function getRouterLinkRouteFromCopy(copy) { +/** + * Returns string2 of input following this pattern: {string1:string2,string3} + * @returns string + */ +export function getLinkTargetFromCopy(copy) { // sample input: {routerLink:estimate,provide your VIN} // first split would return 'estimate,provide your VIN' // second split would return 'estimate' return copy.split(":")[1].split(",")[0]; } -export function getRouterLinkDisplayTextFromCopy(copy) { +/** + * Returns string3 of input following this pattern: {string1:string2,string3} + * @returns string + */ +export function getLinkDisplayTextFromCopy(copy) { // sample input: {routerLink:estimate,provide your VIN} // first split would return 'estimate,provide your VIN' // second split would return 'provide your VIN' diff --git a/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue b/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue index da5af0819..003556727 100644 --- a/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue +++ b/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue @@ -1,7 +1,7 @@ diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 5e0f6d9a6..63b87fbe7 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -17,72 +17,23 @@ cmsWidgetName="CashOrInsuranceQuestionWidget" v-model="isInsuranceSelected" groupName="CashOrInsuranceQuestion" /> - - -

h1

- -

h2

- -

h3

- -

h4

- -

h5

- -

h6

- -

Body

- -

Label

- -

Small

- -

Caption

+ + + + + { vm.setCmsContent(cmsContent); - vm.isInsuranceSelected = vm.getDefaultisInsuranceSelectedValue(); + vm.availableLineItems = [ + { + partNumber: "001", + Description: "Windshield with Recal", + partType: "Windshield", + Quantity: "1", + BasePartNumber: "001", + Color: "Green", + CanSafeliteRecalibrate: true, + price: 350.22, + }, + { + partNumber: "SBB16", + description: "SAFELITE BEAM BLADE 16", + partType: "FRONT WIPER", + price: 32.64, + }, + { + partNumber: "A DISPOSAL FEE", + partType: "DISPOSAL FEE", + price: 15.0, + }, + { + partNumber: "SBB26", + description: "SAFELITE BEAM BLADE 26", + partType: "FRONT WIPER", + price: 53.04, + }, + { + partNumber: "SBBR12A", + description: "SAFELITE REAR BLADE 12A", + partType: "REAR WIPER", + price: 24.48, + }, + { + partNumber: "RAIN DEFENSE", + description: null, + partType: "RAIN DEFENSE", + price: 35.5, + }, + { + partNumber: "RECAL STATIC", + Description: "Recalibration", + partType: "recalibration", + Quantity: "1", + price: 150.0, + }, + ]; + vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(); }); }, data() { return { pricingRequestResult: null, - isInsurance: null, + isInsuranceSelected: null, + selectedPackage: null, + availableLineItems: null, }; }, methods: { @@ -143,38 +145,27 @@ export default { return true; //return store.getters.order.damage.isRepair || (store.getters.order.lineItems?.glassParts != null && store.getters.order.lineItems.glassParts.length > 0); }, - getDefaultisInsuranceSelectedValue() { - var defaultisInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance; - if (defaultisInsuranceSelectedValue != null) { - return defaultisInsuranceSelectedValue; + getDefaultIsInsuranceSelectedValue() { + const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance; + if (defaultIsInsuranceSelectedValue != null) { + return defaultIsInsuranceSelectedValue; } else { - return this.economyPackagePrice > 600; + return this.availableLineItems + ? baseMixin.methods.getTierOnePackagePrice(this.availableLineItems) > 500 + : null; } }, }, - computed: { - economyPackagePrice() { - //TODO build out the pricing logic CSR-504 - return 0; - }, - selectedChipCountValues: { - get: function () { - return this.modelValue; - }, - set: function (newValue) { - this.$emit("update:modelValue", newValue); - }, - }, - }, components: { funnelHeader, funnelFooter, vehicleBanner, funnelSubHeader, Form, - radioServicePackage, textBlock, cashOrInsuranceQuestion, + servicePackageQuestion, + modal, }, }; diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue new file mode 100644 index 000000000..d92a9bb2e --- /dev/null +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -0,0 +1,244 @@ + + + diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue new file mode 100644 index 000000000..0a6098c97 --- /dev/null +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue index ee9bbeaff..d1743ac00 100644 --- a/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue +++ b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue @@ -5,7 +5,7 @@ isMultiSelect :answers="answersToDisplay" :groupName="groupName" - buttonType="listCard" + buttonTypeString="listCard" isRequired v-model="selectedValues" validationRules="damage-location-required" /> diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue index a812d7dab..ff2290664 100644 --- a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue +++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue @@ -11,7 +11,7 @@ :isMultiSelect="isMultiSelect" :answers="answersToDisplay" :groupName="groupName" - buttonType="listCard" + buttonTypeString="listCard" v-model="selectedValues" :validationRules="validationRules" :suppressError="suppressError" diff --git a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue index a54c7f0c9..b645c1ecd 100644 --- a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue +++ b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue @@ -10,7 +10,7 @@ isMultiSelect :answers="answersToDisplay" :groupName="groupName" - buttonType="listCard" + buttonTypeString="listCard" v-model="selectedDoorSidesValues" validationRules="damage-side-required" isRequired /> diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue b/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue index b01878e70..48b813f2d 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue +++ b/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue @@ -5,7 +5,7 @@ :questionText="questionText" :answers="answersFromCms" :groupName="groupName" - buttonType="listButtonHorizontal" + buttonTypeString="listButtonHorizontal" useTextForValue v-model="selectedValue" :validationRules="validationRules" diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue index 75d9d05cb..01e146080 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue +++ b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue @@ -5,7 +5,7 @@ :questionText="questionText" :answers="answersFromCms" :groupName="groupName" - buttonType="listCard" + buttonTypeString="listCard" v-model="selectedValues" :suppressError="suppressError" :validationRules="validationRules" diff --git a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue index bf8c0ec60..65730f695 100644 --- a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue @@ -8,7 +8,7 @@ { + const partType = lineItem.partType.toUpperCase(); + if ( + partType != partTypeStrings.FRONT_WIPER && + partType != partTypeStrings.REAR_WIPER && + partType != partTypeStrings.RAIN_DEFENSE + ) { + totalPrice += lineItem.price; + } + }); + return totalPrice; + }, }, computed: { storeActions() { diff --git a/src/mixins/input-button-wrapper-mixin.js b/src/mixins/input-button-wrapper-mixin.js index 1ee0977a8..1d34d6520 100644 --- a/src/mixins/input-button-wrapper-mixin.js +++ b/src/mixins/input-button-wrapper-mixin.js @@ -9,6 +9,9 @@ export default { ...inputButtonProps, buttonLabel: [Number, String], buttonLabelSubCopy: String, + buttonBodyCopy: String, + buttonAuxillaryCopy: String, + buttonFooterCopy: String, buttonImage: String, buttonImageId: String, altText: { @@ -18,6 +21,7 @@ export default { textPosition: String, screenReaderOnlyText: String, isWide: Boolean, + additionalButtonStyling: String, }, computed: { selectedValue: { diff --git a/src/store/index.js b/src/store/index.js index 6ff878aeb..34ec8a8a9 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -392,6 +392,12 @@ export const getters = { }, eventBus: (state) => state.applicationUser.eventBus, damage: (state) => state.order.damage, + hasAnyNonWindshieldGlassParts: (state) => { + const nonWindshieldItems = state.order.damage.glassToReplace.filter( + (glassToReplace) => glassToReplace.glassLocation != "Windshield" + ); + return !!nonWindshieldItems.length; + }, lineItems: (state) => state.order.lineItems, pageData: (state) => (page) => { return state.applicationUser.pageData[page]; diff --git a/src/ux-components/alert/alert.vue b/src/ux-components/alert/alert.vue index d0fe33ace..ca5ea7f45 100644 --- a/src/ux-components/alert/alert.vue +++ b/src/ux-components/alert/alert.vue @@ -19,10 +19,10 @@ {{ getRouterLinkDisplayTextFromCopy(copy) }}{{ getLinkDisplayTextFromCopy(copy) }} @@ -41,8 +41,8 @@ import { doesCopyContainRouterLink, splitCopyOnCMSPlaceHolder, - getRouterLinkRouteFromCopy, - getRouterLinkDisplayTextFromCopy, + getLinkTargetFromCopy, + getLinkDisplayTextFromCopy, splitCMSCopyOnParagraphTag, } from "@/helpers/cms-content-helper"; @@ -92,8 +92,8 @@ export default { methods: { doesCopyContainRouterLink, splitCopyOnCMSPlaceHolder, - getRouterLinkRouteFromCopy, - getRouterLinkDisplayTextFromCopy, + getLinkTargetFromCopy, + getLinkDisplayTextFromCopy, ensureAlertIsInViewPort() { if (this.shouldScrollToOnMount && this.$el.style.display != "none") { var footerHeight = this.getFooterInfoBoxHeight(); diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js index 09a63b349..432717817 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js @@ -52,12 +52,12 @@ describe("list-button-horizontal.vue", () => { expect(input.attributes()["aria-required"]).toEqual("true"); }); - it("is cash or insurance button => has 'radio-fancy' class", () => { + it("is cash or insurance button => has 'strong' class", () => { // Arrange/Act const { wrapper } = setupMocks({ mockData: { propsData: { - isCashOrInsurance: true, + additionalButtonStyling: "listButtonHorizontalStrong", }, }, }); @@ -65,7 +65,7 @@ describe("list-button-horizontal.vue", () => { // Assert const label = wrapper.find("label"); - expect(label.classes()).toContain("radio-fancy"); + expect(label.classes()).toContain("strong"); }); test("has buttonLabel => displays buttonLabel", () => { diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index 1b4232c4c..7d7f52b4e 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -3,7 +3,7 @@ v-bind="$props" :buttonWrapperClasses="[ 'list-group list-button-horizontal d-flex flex-column w-100', - { 'radio-fancy': isCashOrInsurance }, + { strong: isStrongStyling }, ]" v-model="selectedValue">
-
-
- - -
-
- - -
-
- - -
- - - - -
- - - - -