From 33216be7e2974cc029d56729ea065ab7a5162b70 Mon Sep 17 00:00:00 2001 From: Markus Haack Date: Fri, 29 May 2026 11:21:37 +0200 Subject: [PATCH 1/4] fix: EW block library title parsing for grouped blocks --- blocks/canvas/ew-panel-extensions/helpers.js | 8 +++++--- blocks/canvas/ew-tool-panel/tool-panel.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/blocks/canvas/ew-panel-extensions/helpers.js b/blocks/canvas/ew-panel-extensions/helpers.js index fa26e040b..fdccbb413 100644 --- a/blocks/canvas/ew-panel-extensions/helpers.js +++ b/blocks/canvas/ew-panel-extensions/helpers.js @@ -135,9 +135,11 @@ function getLibraryMetadata(el) { function transformBlock(block) { const prevSib = block.previousElementSibling; - const item = isHeading(prevSib) && prevSib.textContent - ? { name: prevSib.textContent } - : getBlockName(block.className || ''); + const item = block.dataset.groupheading + ? { name: block.dataset.groupheading } + : isHeading(prevSib) && prevSib.textContent + ? { name: prevSib.textContent } + : getBlockName(block.className || ''); item.dom = block.dataset?.isgroup ? processGroupBlock(block) : getBlockTableHtml(block); const metaEl = block.nextElementSibling?.classList.contains('library-metadata') diff --git a/blocks/canvas/ew-tool-panel/tool-panel.js b/blocks/canvas/ew-tool-panel/tool-panel.js index c5aed2465..8ea5ace9e 100644 --- a/blocks/canvas/ew-tool-panel/tool-panel.js +++ b/blocks/canvas/ew-tool-panel/tool-panel.js @@ -161,7 +161,7 @@ class EwToolPanel extends LitElement { const actions = this._loaded[this.activeId]?.getHeaderActions?.(); if (actions) zone.append(actions); } - +^ _renderDialogIcon(icon) { if (!icon) return nothing; if (icon.startsWith('#')) return html``; From 02798a492e08487f7fe61563328656fb33f97ed7 Mon Sep 17 00:00:00 2001 From: Markus Haack Date: Fri, 29 May 2026 11:25:34 +0200 Subject: [PATCH 2/4] fix: typo --- blocks/canvas/ew-tool-panel/tool-panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/canvas/ew-tool-panel/tool-panel.js b/blocks/canvas/ew-tool-panel/tool-panel.js index 8ea5ace9e..c5aed2465 100644 --- a/blocks/canvas/ew-tool-panel/tool-panel.js +++ b/blocks/canvas/ew-tool-panel/tool-panel.js @@ -161,7 +161,7 @@ class EwToolPanel extends LitElement { const actions = this._loaded[this.activeId]?.getHeaderActions?.(); if (actions) zone.append(actions); } -^ + _renderDialogIcon(icon) { if (!icon) return nothing; if (icon.startsWith('#')) return html``; From c6dcf19141fa842bc7a1d5e8ba5fbfb3c510acb7 Mon Sep 17 00:00:00 2001 From: Markus Haack Date: Fri, 29 May 2026 11:55:45 +0200 Subject: [PATCH 3/4] fix: lint errors --- blocks/canvas/ew-panel-extensions/helpers.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/blocks/canvas/ew-panel-extensions/helpers.js b/blocks/canvas/ew-panel-extensions/helpers.js index fdccbb413..028519921 100644 --- a/blocks/canvas/ew-panel-extensions/helpers.js +++ b/blocks/canvas/ew-panel-extensions/helpers.js @@ -135,11 +135,14 @@ function getLibraryMetadata(el) { function transformBlock(block) { const prevSib = block.previousElementSibling; - const item = block.dataset.groupheading - ? { name: block.dataset.groupheading } - : isHeading(prevSib) && prevSib.textContent - ? { name: prevSib.textContent } - : getBlockName(block.className || ''); + let item; + if (block.dataset.groupheading) { + item = { name: block.dataset.groupheading }; + } else if (isHeading(prevSib) && prevSib.textContent) { + item = { name: prevSib.textContent }; + } else { + item = getBlockName(block.className || ''); + } item.dom = block.dataset?.isgroup ? processGroupBlock(block) : getBlockTableHtml(block); const metaEl = block.nextElementSibling?.classList.contains('library-metadata') From 311064e5265c521ae34e54cf8089156b9ad8cc26 Mon Sep 17 00:00:00 2001 From: Markus Haack Date: Mon, 1 Jun 2026 07:40:11 +0200 Subject: [PATCH 4/4] fix: add test --- test/fixtures/nx/utils/daConfig.js | 3 + .../ew-panel-extensions/helpers.test.js | 181 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 test/fixtures/nx/utils/daConfig.js create mode 100644 test/unit/blocks/canvas/ew-panel-extensions/helpers.test.js diff --git a/test/fixtures/nx/utils/daConfig.js b/test/fixtures/nx/utils/daConfig.js new file mode 100644 index 000000000..6d96bb585 --- /dev/null +++ b/test/fixtures/nx/utils/daConfig.js @@ -0,0 +1,3 @@ +// Mock NX /utils/daConfig.js utils for tests +export const fetchDaConfigs = async () => ({}); +export const getFirstSheet = () => null; diff --git a/test/unit/blocks/canvas/ew-panel-extensions/helpers.test.js b/test/unit/blocks/canvas/ew-panel-extensions/helpers.test.js new file mode 100644 index 000000000..fcf2d0ef8 --- /dev/null +++ b/test/unit/blocks/canvas/ew-panel-extensions/helpers.test.js @@ -0,0 +1,181 @@ +import { expect } from '@esm-bundle/chai'; +import { setNx } from '../../../../../scripts/utils.js'; + +setNx('/test/fixtures/nx', { hostname: 'example.com' }); + +let getBlockVariants; + +before(async () => { + const mod = await import('../../../../../blocks/canvas/ew-panel-extensions/helpers.js'); + getBlockVariants = mod.getBlockVariants; +}); + +describe('EW panel helpers transformBlock', () => { + let savedFetch; + beforeEach(() => { savedFetch = window.fetch; }); + afterEach(() => { window.fetch = savedFetch; }); + + function mockHtml(html) { + window.fetch = () => Promise.resolve(new Response(html, { status: 200 })); + } + + it('Uses data-groupheading as the name for grouped blocks', async () => { + mockHtml(` +
+

My Group

+
+
content
+
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants).to.have.lengthOf(1); + expect(variants[0].name).to.equal('My Group'); + }); + + it('Falls back to the preceding heading text when no groupheading', async () => { + mockHtml(` +
+

Block Title

+
content
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants).to.have.lengthOf(1); + expect(variants[0].name).to.equal('Block Title'); + }); + + it('Falls back to class name when there is no groupheading and no preceding heading', async () => { + mockHtml(` +
+
content
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants).to.have.lengthOf(1); + expect(variants[0].name).to.equal('hero'); + expect(variants[0].variants).to.equal('wide'); + }); + + it('Returns an empty array when the fetch fails', async () => { + window.fetch = () => Promise.resolve(new Response('error', { status: 500 })); + const variants = await getBlockVariants('/mock-path'); + expect(variants).to.deep.equal([]); + }); + + it('Returns a table as item.dom for a regular block', async () => { + mockHtml(` +
+
content
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].dom).to.be.instanceOf(window.HTMLTableElement); + }); + + it('Returns a div as item.dom for a grouped block', async () => { + mockHtml(` +
+

My Group

+
+
content
+
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].dom).to.be.instanceOf(window.HTMLDivElement); + }); + + it('Sets item.tags from searchtags in nextElementSibling library-metadata', async () => { + mockHtml(` +
+
content
+ +
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].tags).to.equal('hero, card'); + }); + + it('Sets item.description from description in nextElementSibling library-metadata', async () => { + mockHtml(` +
+
content
+ +
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].description).to.equal('A hero block'); + }); + + it('Sets item.tags from searchtags in embedded library-metadata', async () => { + mockHtml(` +
+
+
content
+ +
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].tags).to.equal('hero, banner'); + }); + + it('Sets both tags and description when both present in library-metadata', async () => { + mockHtml(` +
+
content
+ +
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].tags).to.equal('hero, card'); + expect(variants[0].description).to.equal('A hero block'); + }); + + it('Does not set tags or description when no library-metadata is present', async () => { + mockHtml(` +
+
content
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].tags).to.be.undefined; + expect(variants[0].description).to.be.undefined; + }); + + it('Sets tags from library-metadata appended after library-container-end in a group', async () => { + mockHtml(` +
+

My Group

+
+
content
+
+ +
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].tags).to.equal('group, hero'); + }); + + it('Group dom contains both a table for the block and cloned non-div siblings', async () => { + mockHtml(` +
+

Hero with text

+
+
content
+

Lorem ipsum

+
+
+ `); + const variants = await getBlockVariants('/mock-path'); + expect(variants[0].name).to.equal('Hero with text'); + const { dom } = variants[0]; + expect(dom).to.be.instanceOf(window.HTMLDivElement); + expect(dom.querySelector('table')).to.not.be.null; + expect(dom.querySelector('p')).to.not.be.null; + }); +});