Skip to content

Commit

Permalink
fix(Designer): Updated HTML Editor to support newline characters (for…
Browse files Browse the repository at this point in the history
… dynamic content) (#4635)

* Fixed HTML Editor newline bug -> made sure unit tests work properly despite changes

* Added support for multiple consecutive newlines in HTML Editor
  • Loading branch information
spanvalkar committed Apr 18, 2024
1 parent 5f05c44 commit f4e1f8b
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 10 deletions.
30 changes: 28 additions & 2 deletions libs/designer-ui/src/lib/editor/base/utils/editorToSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const getChildrenNodesToSegments = (node: ElementNode, segments: ValueSegment[],
export const convertStringToSegments = (
value: string,
nodeMap: Map<string, ValueSegment>,
options?: SegmentParserOptions
options?: SegmentParserOptions,
convertSpaceToNewline?: boolean
): ValueSegment[] => {
if (!value) {
return [];
Expand Down Expand Up @@ -82,7 +83,28 @@ export const convertStringToSegments = (
segmentSoFar += currChar;

if (!isInQuotedString && currChar === '}' && currSegmentType === ValueSegmentType.TOKEN) {
const token = nodeMap.get(segmentSoFar);
let token: ValueSegment | undefined = undefined;

// removes formatting compatibility issues between nodemap and HTML text in the editor
// when opening an action with an HTML editor
if (convertSpaceToNewline) {
// modifiedSegmentSoFar -> in segmentSoFar, replace spaces with no space
const modifiedSegmentSoFar = removeNewlinesAndSpaces(segmentSoFar);
// for each key in nodeMap
for (const key of nodeMap.keys()) {
// keyNoNewline = key, but replace all newlines with no space
const keyNoNewline = removeNewlinesAndSpaces(key);
// if the nodemap key and modified HTML segment match,
// take the corresponding HTML node in the nodemap
if (keyNoNewline === modifiedSegmentSoFar) {
token = nodeMap.get(key);
break;
}
}
} else {
token = nodeMap.get(segmentSoFar);
}

if (token) {
returnSegments.push(token);
currSegmentType = ValueSegmentType.LITERAL;
Expand Down Expand Up @@ -117,3 +139,7 @@ const collapseLiteralSegments = (segments: ValueSegment[]): void => {
index++;
}
};

const removeNewlinesAndSpaces = (inputStr: string): string => {
return inputStr.replace(/\s+/g, '').replaceAll(/\n/g, '');
};
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const appendChildrenNode = (
const childNodeFormat = childNode.getFormat();

if (tokensEnabled && nodeMap) {
const contentAsParameter = convertStringToSegments(decodedTextContent, nodeMap, options);
const contentAsParameter = convertStringToSegments(decodedTextContent, nodeMap, options, true);
contentAsParameter.forEach((segment) => {
const tokenNode = createTokenNodeFromSegment(segment, options, nodeMap);
if (tokenNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
encodeStringSegmentTokensInLexicalContext,
} from '../../../../editor/base/utils/parsesegments';
import {
canReplaceSpanWithId,
cleanHtmlString,
cleanStyleAttribute,
encodeSegmentValueInLexicalContext,
Expand Down Expand Up @@ -74,6 +75,7 @@ export const convertEditorState = (
// Create a temporary DOM element to parse the HTML string
const tempElement = getDomFromHtmlEditorString(htmlEditorString, nodeMap);

let idValue = '';
// Loop through all elements and remove unwanted attributes
const elements = tempElement.querySelectorAll('*');
// biome-ignore lint/style/useForOf: Node List isn't iterable
Expand All @@ -87,7 +89,7 @@ export const convertEditorState = (
}
if (attribute.name === 'id' && !isValuePlaintext) {
// If we're in the rich HTML editor, encoding occurs at the element level since they are all wrapped in <span>.
const idValue = element.getAttribute('id') ?? ''; // e.g., "@{concat('&lt;', '"')}"
idValue = element.getAttribute('id') ?? ''; // e.g., "@{concat('&lt;', '"')}"
const encodedIdValue = encodeSegmentValueInLexicalContext(idValue); // e.g., "@{concat('%26lt;', '%22')}"
element.setAttribute('id', encodedIdValue);
continue;
Expand All @@ -106,12 +108,11 @@ export const convertEditorState = (

// Replace `<span id="..."></span>` with the captured `id` value if it is found in the viable IDs map.
const spanIdPattern = /<span id="(.*?)"><\/span>/g;
const noTokenSpansString = decodedLexicalString.replace(spanIdPattern, (match, idValue) => {
if (nodeMap.get(idValue)) {
return idValue;
}
return match;
});
let noTokenSpansString = decodedLexicalString;
const decodedLexicalStringWithoutNewlines = decodedLexicalString.replace(/\n/g, '');
if (canReplaceSpanWithId(idValue, nodeMap)) {
noTokenSpansString = decodedLexicalStringWithoutNewlines.replace(spanIdPattern, idValue);
}
const valueSegments: ValueSegment[] = convertStringToSegments(noTokenSpansString, nodeMap, { tokensEnabled: true });
resolve(valueSegments);
});
Expand Down
5 changes: 5 additions & 0 deletions libs/designer-ui/src/lib/html/plugins/toolbar/helper/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export const cleanHtmlString = (html: string): string => {
return cleanedHtmlString;
};

// If we can find the id in the nodemap, return true.
export const canReplaceSpanWithId = (idValue: string, nodeMap: Map<string, ValueSegment>): boolean => {
return nodeMap.get(idValue) !== undefined;
};

export const cleanStyleAttribute = (styleAttributeValue: string): string | undefined => {
const newValue = styleAttributeValue.replace('white-space: pre-wrap;', '').trim();
return newValue.length ? newValue : undefined;
Expand Down

0 comments on commit f4e1f8b

Please sign in to comment.