CSR-731 | Refactor cms-content-helper.js

Also fixed a looping issue
This commit is contained in:
Scott Kiener 2022-10-18 14:32:10 -04:00
parent be9f6a008c
commit 8e2e5eb5bd
3 changed files with 147 additions and 101 deletions

View file

@ -40,7 +40,8 @@ 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 regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g");
const regexMatches = [...str.matchAll(regexExp)];
const globalStateMatches = regexMatches.filter(match => {
return match[1] === dynamicStrings.GLOBAL_STATE;
@ -93,11 +94,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] = checkForIfStatements(widgetModel[key], dynamicStrings.GLOBAL_STATE, getStoreValueFromString);
widgetModel[key] = processIfStatements(widgetModel[key], dynamicStrings.GLOBAL_STATE, getStoreValueFromString);
if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
widgetModel[key] = mapStringToState(widgetModel[key]);
}
return reconstructPageLevelIfStatements(widgetModel[key]);
return widgetModel[key];
}
// If we have an object. array, etc
@ -116,75 +117,6 @@ function processWidgetItemForReplacement(widgetModel, key) {
return widgetModel[key];
}
export function checkForIfStatements(str, ifConditionString, replaceStringCallback) {
// Pattern to split off each {if:...}, {else}, {end} and their trailing strings
const regexExp = new RegExp("(?<cleanString>^.+?)(?=(?:{if))|(?<ifOperator>{if:)(?<ifConditionType>.*?:)(?<ifCondition>.*?)}(?<ifTrailingString>.*?)(?=(?:{(?:end|else|if:.*?)}))|(?<elseOperator>{else})(?<elseTrailingString>.*?)(?=(?:{(?:end|else|if:.*?)}))|(?<endOperator>{end})(?<endTrailingString>.*?)(?=(?:{(?:end|else|if:.*?)})|$)", "g");
const regexMatches = [...str.matchAll(regexExp)];
// Find and evaluate full globalState if statements
if (regexMatches.length) {
regexMatches.forEach((match, index) => {
if(match.groups.ifOperator) {
if(regexMatches[index+1].groups.endOperator) {
processIfStatement(regexMatches.slice(index, index+2), ifConditionString, replaceStringCallback) + regexMatches[index+1].groups.endTrailingString;
}
else if(regexMatches[index+2].groups.endOperator) {
processIfStatement(regexMatches.slice(index, index+3), ifConditionString, replaceStringCallback)+ regexMatches[index+2].groups.endTrailingString;
}
}
});
const processedString = joinProcessedRegexArray(regexMatches);
return checkForIfStatements(processedString, ifConditionString, replaceStringCallback);
} else {
return str;
}
}
function reconstructPageLevelIfStatements(str) {
return str.replaceAll("(((", "{").replaceAll(")))","}");
}
function joinProcessedRegexArray(regexMatches) {
let processedString = "";
regexMatches.forEach((match) => {
processedString += match.groups.cleanString ?? match[0];
});
return processedString;
}
function processIfStatement(ifStatementArray, ifConditionString, replaceStringCallback) {
if(ifStatementArray[0].groups.ifConditionType.includes(ifConditionString)) {
//replace the if condition with a real value
const ifCondition = replaceStringCallback(ifStatementArray[0].groups.ifCondition);
//convert the entire if statement
let processedIfStatementString;
if (ifCondition) {
processedIfStatementString = ifStatementArray[0].groups.ifTrailingString;
} else {
processedIfStatementString = ifStatementArray[1].groups.elseTrailingString ?? "";
}
ifStatementArray.forEach((entry) => {
if (entry.groups.ifOperator) {
entry.groups.cleanString = processedIfStatementString;
} else if (entry.groups.endOperator) {
entry.groups.cleanString = entry.groups.endTrailingString;
} else {
entry.groups.cleanString = "";
}
})
} else {
// stow the if statement to reconstruct later when passing to the page
ifStatementArray.forEach((entry) => {
const groups = entry.groups;
if (groups.ifOperator)
groups.cleanString = "(((if:" + groups.ifConditionType + groups.ifCondition + ")))" + groups.ifTrailingString;
else if (groups.elseOperator)
groups.cleanString = "(((else)))" + groups.elseTrailingString;
else
groups.cleanString = "(((end)))" + groups.endTrailingString;
});
}
}
function getStoreValueFromString(str) {
let storeOrStateObject = str.includes('getters') ? store :store.state;
for (const s of str.split(".")) {
@ -197,8 +129,121 @@ function getStoreValueFromString(str) {
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 = getFirstNonNestedIfStatement(ifStatementRegexMatches, ifConditionKeyword);
const processedIfStatementString = executeIfStatementAndGetProcessedString(completeIfStatementArray, replacePlaceholderCallback);
replaceIfStatementWithProcessedString(completeIfStatementArray, processedIfStatementString);
const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches);
return processIfStatements(reconstructedPostProcessedString, ifConditionKeyword, replacePlaceholderCallback);
}
}
function getFirstNonNestedIfStatement(matches, ifConditionKeyword) {
let index = 0;
for(const match of matches) {
if (match.groups.ifOperator && match.groups.ifConditionType === ifConditionKeyword) {
let interiorIndex = 0;
let nestedLevel = 0;
for (const interiorMatch of matches.slice(index+1)) {
if (interiorMatch.groups.ifOperator) {
if(interiorMatch.groups.ifConditionType === ifConditionKeyword) {
break;
} else {
nestedLevel++;
}
} else if (interiorMatch.groups.endOperator) {
if (nestedLevel) {
nestedLevel--;
} else {
return matches.slice(index, index + interiorIndex+2);
}
}
interiorIndex++;
}
}
index++;
}
}
function joinProcessedRegexArray(regexMatches) {
let processedString = "";
regexMatches.forEach((match) => {
processedString += match.groups.cleanString ?? match[0];
});
return processedString;
}
function executeIfStatementAndGetProcessedString(ifStatementArray, replacePlaceholderCallback) {
const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition);
if (ifCondition) {
return ifStatementArray[0].groups.ifTrailingString;
} else {
return ifStatementArray[1].groups.elseTrailingString ?? "";
}
}
function replaceIfStatementWithProcessedString(ifStatementArray, processedIfStatementString) {
ifStatementArray.forEach((entry) => {
if (entry.groups.ifOperator) {
entry.groups.cleanString = processedIfStatementString;
} else if (entry.groups.endOperator) {
entry.groups.cleanString = entry.groups.endTrailingString;
} else {
entry.groups.cleanString = "";
}
})
}
function getIfStatementRegexExpression() {
// Matches but does not capture:
// {if:...} or {else} or {end}
const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})";
// NOTE: ?<variableName> syntax stores the captured match like so: match.groups.variableName
const matchStartOfString = (
"(?<cleanString>^.+?)" + // Match and Capture all characters (lazy), cannot be empty
"(?=(?:{if))" // Looks ahead but does not capture {if
);
const matchIfOperator = (
"(?<ifOperator>{if:)" + // Match & Capture {if:
"(?<ifConditionType>.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":"
"(?<ifCondition>.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}"
"(?<ifTrailingString>.*?)" + // Match and Capture all characters (lazy), can be empty
"(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator
);
const matchElseOperator = (
"(?<elseOperator>{else})" + // Match & Capture {else}
"(?<elseTrailingString>.*?)" + // Match & Capture all characters (lazy), can be empty
"(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator
);
const matchEndOperator = (
"(?<endOperator>{end})" + // Match & Capture {end}
"(?<endTrailingString>.*?)" + // 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);

View file

@ -24,6 +24,7 @@
groupName="CashOrInsuranceQuestion"
/>
<servicePackageQuestion
v-if="lineItems"
ref="servicePackage"
cashCmsWidgetName="CashServicePackageQuestionWidget"
insuranceCmsWidgetName="InsuranceServicePackageQuestionWidget"
@ -113,37 +114,37 @@ export default {
CanSafeliteRecalibrate: true,
price : 350.22
},
// {
// "partNumber": "SBB16",
// "description": "SAFELITE BEAM BLADE 16",
// "partType": "PASSENGER FRONT WIPER",
// "price": 32.64,
// },
// {
// "partNumber": "SBB26",
// "description": "SAFELITE BEAM BLADE 26",
// "partType": "DRIVER FRONT WIPER",
// "price": 53.04,
// },
// {
// "partNumber": "SBBR12A",
// "description": "SAFELITE REAR BLADE 12A",
// "partType": "REAR WIPER",
// "price": 24.48,
// },
{
"partNumber": "SBB16",
"description": "SAFELITE BEAM BLADE 16",
"partType": "PASSENGER FRONT WIPER",
"price": 32.64,
},
{
"partNumber": "SBB26",
"description": "SAFELITE BEAM BLADE 26",
"partType": "DRIVER 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.50,
},
// {
// PartNumber : "005",
// Description : "Recalibration",
// partType : "recalibration",
// Quantity : "1",
// price : 150.00,
// },
{
PartNumber : "005",
Description : "Recalibration",
partType : "recalibration",
Quantity : "1",
price : 150.00,
},
];
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue();
});

View file

@ -36,7 +36,7 @@ import baseInputButton from "@/common-components/base-input-button/base-input-bu
import textLink from "@/ux-components/text-link/text-link";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
import baseMixin from "@/mixins/base-mixin.js";
import { splitCopyOnCMSPlaceHolder, checkForIfStatements } from "@/helpers/cms-content-helper";
import { splitCopyOnCMSPlaceHolder, processIfStatements } from "@/helpers/cms-content-helper";
import { dynamicStrings } from "@/constants/dynamic-strings";
export default {
@ -55,11 +55,11 @@ export default {
},
getBodyTextFromCms(){
const bodyText = this.getCmsContent(this.value, 'BodyText');
return this.checkForIfStatements(bodyText, "custom", this.getCustomValueFromString);
return this.processIfStatements(bodyText, "custom", this.getCustomValueFromString);
},
getFooterTextFromCms(){
const footerText = this.getCmsContent(this.value, 'FooterText');
return this.checkForIfStatements(footerText, "custom", this.getCustomValueFromString);
return this.processIfStatements(footerText, "custom", this.getCustomValueFromString);
},
getPackagePriceString() {
const formattedPriceFloat = parseFloat(this.getPackagePrice()).toFixed(2);
@ -127,7 +127,7 @@ export default {
},
methods: {
splitCopyOnCMSPlaceHolder,
checkForIfStatements,
processIfStatements,
stripUlTagFromCopy(copy) {
const regex = /(?:<ul(?:.*?)>)|(?:<\/ul>)/g;
return copy.replace(regex, '');