From 960366d7454d407b17a48c81d8b21ae470f0607c Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 20 Feb 2024 17:45:27 -0800 Subject: [PATCH 1/2] feature: rewrite markup during block decoration pipeline to ensure all block div's contain appropriate wrapper paragraph tags --- dist/aem.js | 44 +++++++ src/block-loader.js | 3 +- src/dom-utils.js | 34 +++++ src/index.js | 1 + test/dom-utils/wrapTextNodes.test.html | 176 +++++++++++++++++++++++++ 5 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 test/dom-utils/wrapTextNodes.test.html diff --git a/dist/aem.js b/dist/aem.js index 6e92dd7..2f647b5 100644 --- a/dist/aem.js +++ b/dist/aem.js @@ -372,6 +372,48 @@ function decorateTemplateAndTheme() { if (theme) addClasses(document.body, theme); } +/** + * Wrap inline text content of block cells within a

tag. + * @param {Element} block the block element + */ +function wrapTextNodes(block) { + const validWrappers = [ + 'P', + 'PRE', + 'UL', + 'OL', + 'PICTURE', + 'TABLE', + 'H1', + 'H2', + 'H3', + 'H4', + 'H5', + 'H6', + ]; + + const wrap = (el, tag = 'p') => { + const wrapper = document.createElement(tag); + wrapper.append(...el.childNodes); + el.append(wrapper); + }; + + block.querySelectorAll(':scope > div > div').forEach((blockColumn) => { + if (blockColumn.hasChildNodes()) { + const hasWrapper = !!blockColumn.firstElementChild + && validWrappers.some((tagName) => blockColumn.firstElementChild.tagName === tagName); + if (!hasWrapper) { + wrap(blockColumn); + } else if ( + blockColumn.firstElementChild.tagName === 'PICTURE' + && (blockColumn.children.length > 1 || !!blockColumn.textContent.trim()) + ) { + wrap(blockColumn); + } + } + }); +} + /** * Decorates paragraphs containing a single link as buttons. * @param {Element} element container element @@ -635,6 +677,7 @@ function decorateBlock(block) { block.classList.add('block'); block.dataset.blockName = shortBlockName; block.dataset.blockStatus = 'initialized'; + wrapTextNodes(block); const blockWrapper = block.parentElement; blockWrapper.classList.add(`${shortBlockName}-wrapper`); const section = block.closest('.section'); @@ -723,4 +766,5 @@ export { toClassName, updateSectionsStatus, waitForLCP, + wrapTextNodes, }; diff --git a/src/block-loader.js b/src/block-loader.js index 2c23788..8186bb7 100644 --- a/src/block-loader.js +++ b/src/block-loader.js @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import { loadCSS } from './dom-utils.js'; +import { loadCSS, wrapTextNodes } from './dom-utils.js'; /** * Updates all section status in a container element. @@ -124,6 +124,7 @@ export function decorateBlock(block) { block.classList.add('block'); block.dataset.blockName = shortBlockName; block.dataset.blockStatus = 'initialized'; + wrapTextNodes(block); const blockWrapper = block.parentElement; blockWrapper.classList.add(`${shortBlockName}-wrapper`); const section = block.closest('.section'); diff --git a/src/dom-utils.js b/src/dom-utils.js index 6b5e8d6..6383440 100644 --- a/src/dom-utils.js +++ b/src/dom-utils.js @@ -124,3 +124,37 @@ export function decorateTemplateAndTheme() { const theme = getMetadata('theme'); if (theme) addClasses(document.body, theme); } + +/** + * Wrap inline text content of block cells within a

tag. + * @param {Element} block the block element + */ +export function wrapTextNodes(block) { + const validWrappers = [ + 'P', + 'PRE', + 'UL', 'OL', + 'PICTURE', + 'TABLE', + 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', + ]; + + const wrap = (el, tag = 'p') => { + const wrapper = document.createElement(tag); + wrapper.append(...el.childNodes); + el.append(wrapper); + }; + + block.querySelectorAll(':scope > div > div').forEach((blockColumn) => { + if (blockColumn.hasChildNodes()) { + const hasWrapper = !!blockColumn.firstElementChild + && validWrappers.some((tagName) => blockColumn.firstElementChild.tagName === tagName); + if (!hasWrapper) { + wrap(blockColumn); + } else if (blockColumn.firstElementChild.tagName === 'PICTURE' + && (blockColumn.children.length > 1 || !!blockColumn.textContent.trim())) { + wrap(blockColumn); + } + } + }); +} diff --git a/src/index.js b/src/index.js index 8bb9e6d..3f22bb2 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,7 @@ export { getMetadata, createOptimizedPicture, decorateTemplateAndTheme, + wrapTextNodes, } from './dom-utils.js'; export { decorateButtons, decorateIcons, decorateSections } from './decorate.js'; export { sampleRUM } from '@adobe/helix-rum-js'; diff --git a/test/dom-utils/wrapTextNodes.test.html b/test/dom-utils/wrapTextNodes.test.html new file mode 100644 index 0000000..b51b64f --- /dev/null +++ b/test/dom-utils/wrapTextNodes.test.html @@ -0,0 +1,176 @@ + + + + + + + + +

+
+
+
+
This will get a wrapping p
+

This will get a not get wrapping p since it already has one

+
+
+
+
+
+

This will get a wrapping p

+

This will get a not get wrapping p since it already has one

+

+
+
+
+
+
it also works when the icon comes first
+
+
+
+
+
+

it also works when the icon comes first

+

+
+
+
+
+
code blocks also get wrapped
+
text with some inline code block also get wrapped
+
inline code block also get wrapped with text after it, too
+
and for completeness inline code block also get wrapped with text before and after it!
+
+
+
pre is a block level element so it doens't get a wrapper
+
+
+
+
+

code blocks also get wrapped

+

text with some inline code block also get wrapped

+

inline code block also get wrapped with text after it, too

+

and for completeness inline code block also get wrapped with text before and after it!

+
+
+
pre is a block level element so it doens't get a wrapper
+
+
+
+
+
+

headings will not be wrapped

+
+
+

headings of all levels will not be wrapped

+
+
+
+
+
+
+

headings will not be wrapped

+
+
+

headings of all levels will not be wrapped

+
+
+
+
+
+
+
+

+

a picture with some text

+
+
a picture with inline text
+
inline text with a picture
+ +
+
+
+
+
+
+

+

a picture with some text

+
+

a picture with inline text

+

inline text with a picture

+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + \ No newline at end of file From c0bc73416258b0ded2ddda4d5b7bee94dbc493b6 Mon Sep 17 00:00:00 2001 From: Sean Steimer Date: Tue, 12 Mar 2024 14:27:17 -0700 Subject: [PATCH 2/2] remove unused tag parameter in wrap function --- dist/aem.js | 4 ++-- src/dom-utils.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/aem.js b/dist/aem.js index 2f647b5..3aef990 100644 --- a/dist/aem.js +++ b/dist/aem.js @@ -392,8 +392,8 @@ function wrapTextNodes(block) { 'H6', ]; - const wrap = (el, tag = 'p') => { - const wrapper = document.createElement(tag); + const wrap = (el) => { + const wrapper = document.createElement('p'); wrapper.append(...el.childNodes); el.append(wrapper); }; diff --git a/src/dom-utils.js b/src/dom-utils.js index 6383440..e219065 100644 --- a/src/dom-utils.js +++ b/src/dom-utils.js @@ -139,8 +139,8 @@ export function wrapTextNodes(block) { 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', ]; - const wrap = (el, tag = 'p') => { - const wrapper = document.createElement(tag); + const wrap = (el) => { + const wrapper = document.createElement('p'); wrapper.append(...el.childNodes); el.append(wrapper); };