From d0c533f1149ada312d41c850fe6b30f85ff9a4ae Mon Sep 17 00:00:00 2001 From: dobri1408 <50819975+dobri1408@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:25:34 +0300 Subject: [PATCH] Backport #6309 to Volto 17 (#6384) --- news/6384.feature | 1 + src/components/manage/Diff/DiffField.jsx | 206 ++++++++++++++---- .../Diff/__snapshots__/Diff.test.jsx.snap | 21 +- .../__snapshots__/DiffField.test.jsx.snap | 60 ++--- 4 files changed, 193 insertions(+), 95 deletions(-) create mode 100644 news/6384.feature diff --git a/news/6384.feature b/news/6384.feature new file mode 100644 index 0000000000..446af0edf9 --- /dev/null +++ b/news/6384.feature @@ -0,0 +1 @@ +improve DiffField.jsx with better support for displaying HTML elements such as images @dobri1408 diff --git a/src/components/manage/Diff/DiffField.jsx b/src/components/manage/Diff/DiffField.jsx index 23582788d3..a9bad2afb2 100644 --- a/src/components/manage/Diff/DiffField.jsx +++ b/src/components/manage/Diff/DiffField.jsx @@ -4,7 +4,6 @@ */ import React from 'react'; -// import { diffWords as dWords } from 'diff'; import { join, map } from 'lodash'; import PropTypes from 'prop-types'; import { Grid } from 'semantic-ui-react'; @@ -13,20 +12,128 @@ import { Provider } from 'react-intl-redux'; import { createBrowserHistory } from 'history'; import { ConnectedRouter } from 'connected-react-router'; import { useSelector } from 'react-redux'; - +import config from '@plone/volto/registry'; import { Api } from '@plone/volto/helpers'; import configureStore from '@plone/volto/store'; -import { DefaultView } from '@plone/volto/components/'; +import { RenderBlocks } from '@plone/volto/components'; import { serializeNodes } from '@plone/volto-slate/editor/render'; - import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable'; -/** - * Enhanced diff words utility - * @function diffWords - * @param oneStr Field one - * @param twoStr Field two - */ +const isHtmlTag = (str) => { + // Match complete HTML tags, including: + // 1. Opening tags like
, , ... + // 2. Self-closing tags like ,
+ // 3. Closing tags like
+ return /^<([a-zA-Z]+[0-9]*)\b[^>]*>|^<\/([a-zA-Z]+[0-9]*)\b[^>]*>$|^<([a-zA-Z]+[0-9]*)\b[^>]*\/>$/.test( + str, + ); +}; + +const splitWords = (str) => { + if (typeof str !== 'string') return str; + if (!str) return []; + + const result = []; + let currentWord = ''; + let insideTag = false; + let insideSpecialTag = false; + let tagBuffer = ''; + + // Special tags that should not be split (e.g., , ... ) + const specialTags = ['img', 'svg']; + + for (let i = 0; i < str.length; i++) { + const char = str[i]; + + // Start of an HTML tag + if (char === '<') { + if (currentWord) { + result.push(currentWord); // Push text before the tag + currentWord = ''; + } + insideTag = true; + tagBuffer += char; + } + // End of an HTML tag + else if (char === '>') { + tagBuffer += char; + insideTag = false; + + // Check if the tagBuffer contains a special tag + const tagNameMatch = tagBuffer.match(/^<\/?([a-zA-Z]+[0-9]*)\b/); + if (tagNameMatch && specialTags.includes(tagNameMatch[1])) { + insideSpecialTag = + tagNameMatch[0].startsWith('<') && !tagNameMatch[0].startsWith(' { + if (!isHtmlTag(value)) { + if (part.removed && (side === 'left' || side === 'unified')) { + return `${value}`; + } else if (part.removed) return ''; + else if (part.added && (side === 'right' || side === 'unified')) { + return `${value}`; + } else if (part.added) return ''; + return value; + } else { + if (side === 'unified' && part.added) return value; + else if (side === 'unified' && part.removed) return ''; + if (part.removed && side === 'left') { + return value; + } else if (part.removed) return ''; + else if (part.added && side === 'right') { + return value; + } else if (part.added) return ''; + return value; + } +}; /** * Diff field component. @@ -36,6 +143,7 @@ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable'; * @param {Object} schema Field schema * @returns {string} Markup of the component. */ + const DiffField = ({ one, two, @@ -51,7 +159,10 @@ const DiffField = ({ timeStyle: 'short', }; const diffWords = (oneStr, twoStr) => { - return diffLib.diffWords(String(oneStr), String(twoStr)); + return diffLib.diffArrays( + splitWords(String(oneStr)), + splitWords(String(twoStr)), + ); }; let parts, oneArray, twoArray; @@ -78,14 +189,14 @@ const DiffField = ({ ReactDOMServer.renderToStaticMarkup( - + , ), ReactDOMServer.renderToStaticMarkup( - + , ), @@ -116,7 +227,30 @@ const DiffField = ({ } case 'textarea': default: - parts = diffWords(one, two); + const Widget = config.widgets?.views?.widget?.[schema.widget]; + + if (Widget) { + const api = new Api(); + const history = createBrowserHistory(); + const store = configureStore(window.__data, history, api); + parts = diffWords( + ReactDOMServer.renderToStaticMarkup( + + + + + , + ), + ReactDOMServer.renderToStaticMarkup( + + + + + , + ), + ); + } else parts = diffWords(one, two); + break; } } else if (schema.type === 'object') { @@ -128,6 +262,7 @@ const DiffField = ({ } else { parts = diffWords(one?.title || one, two?.title || two); } + return ( @@ -140,14 +275,12 @@ const DiffField = ({ - (part.removed && - `${part.value}`) || - (!part.added && `${part.value}`) || - '', - ), + map(parts, (part) => { + let combined = (part.value || []).reduce((acc, value) => { + return acc + formatDiffPart(part, value, 'left'); + }, ''); + return combined; + }), '', ), }} @@ -157,14 +290,12 @@ const DiffField = ({ - (part.added && - `${part.value}`) || - (!part.removed && `${part.value}`) || - '', - ), + map(parts, (part) => { + let combined = (part.value || []).reduce((acc, value) => { + return acc + formatDiffPart(part, value, 'right'); + }, ''); + return combined; + }), '', ), }} @@ -178,15 +309,12 @@ const DiffField = ({ - (part.removed && - `${part.value}`) || - (part.added && - `${part.value}`) || - (!part.added && `${part.value}`), - ), + map(parts, (part) => { + let combined = (part.value || []).reduce((acc, value) => { + return acc + formatDiffPart(part, value, 'unified'); + }, ''); + return combined; + }), '', ), }} diff --git a/src/components/manage/Diff/__snapshots__/Diff.test.jsx.snap b/src/components/manage/Diff/__snapshots__/Diff.test.jsx.snap index 65383def7a..2421b47acb 100644 --- a/src/components/manage/Diff/__snapshots__/Diff.test.jsx.snap +++ b/src/components/manage/Diff/__snapshots__/Diff.test.jsx.snap @@ -232,16 +232,17 @@ exports[`Diff renders a diff component 1`] = ` class="top aligned six wide column" > - - My - + My old - - title + + + title @@ -249,21 +250,17 @@ exports[`Diff renders a diff component 1`] = ` class="top aligned six wide column" > - - My - + My new - - title - + - , + title, diff --git a/src/components/manage/Diff/__snapshots__/DiffField.test.jsx.snap b/src/components/manage/Diff/__snapshots__/DiffField.test.jsx.snap index 2bbf810620..0871f46671 100644 --- a/src/components/manage/Diff/__snapshots__/DiffField.test.jsx.snap +++ b/src/components/manage/Diff/__snapshots__/DiffField.test.jsx.snap @@ -103,16 +103,14 @@ exports[`DiffField renders a datetime field 1`] = ` - Tuesday + Tuesday, - Monday - - - , April 25, + Monday, + April 25, @@ -123,9 +121,7 @@ exports[`DiffField renders a datetime field 1`] = ` > 2016 - - at 2:14 PM - + at 2:14 PM @@ -155,34 +151,26 @@ exports[`DiffField renders a diff field in split mode 1`] = ` class="top aligned six wide column" > - - My - + My old - - string - + string
- - My - + My new - - string - + string
@@ -212,9 +200,7 @@ exports[`DiffField renders a diff field in unified mode 1`] = ` class="top aligned sixteen wide column" > - - My - + My @@ -225,9 +211,7 @@ exports[`DiffField renders a diff field in unified mode 1`] = ` > new - - string - + string @@ -257,9 +241,7 @@ exports[`DiffField renders a richtext field 1`] = ` class="top aligned sixteen wide column" > - - My - + My @@ -270,9 +252,7 @@ exports[`DiffField renders a richtext field 1`] = ` > new - - string - + string @@ -302,9 +282,7 @@ exports[`DiffField renders a textarea field 1`] = ` class="top aligned sixteen wide column" > - - My - + My @@ -315,9 +293,7 @@ exports[`DiffField renders a textarea field 1`] = ` > new - - string - + string @@ -347,9 +323,7 @@ exports[`DiffField renders an array field 1`] = ` class="top aligned sixteen wide column" > - - one, - + one, @@ -389,9 +363,7 @@ exports[`DiffField renders an array of objects field 1`] = ` class="top aligned sixteen wide column" > - - one, - + one,