From 5344fdeb66fecbd74ee7c7ffc72d9dd257c9f337 Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Thu, 16 Jan 2025 22:24:13 +0100 Subject: [PATCH] fix(ui/core): rerender direct host child with right slot attr when root is switched --- .changeset/great-pumas-rush.md | 5 ++ packages/ui/components/core/src/SlotMixin.js | 15 ++++++ .../ui/components/core/test/SlotMixin.test.js | 50 +++++++++++++++++++ .../src/LionInputTelDropdown.js | 2 +- 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 .changeset/great-pumas-rush.md diff --git a/.changeset/great-pumas-rush.md b/.changeset/great-pumas-rush.md new file mode 100644 index 000000000..033efd1ae --- /dev/null +++ b/.changeset/great-pumas-rush.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[core] rerender direct host child with right slot attr when root is switched diff --git a/packages/ui/components/core/src/SlotMixin.js b/packages/ui/components/core/src/SlotMixin.js index 0acafb5e9..d56d936c5 100644 --- a/packages/ui/components/core/src/SlotMixin.js +++ b/packages/ui/components/core/src/SlotMixin.js @@ -182,6 +182,21 @@ const SlotMixinImplementation = superclass => // Providing all options breaks Safari: we keep host and creationScope const { creationScope, host } = this.renderOptions; render(template, rerenderTarget, { creationScope, host, renderBefore }); + + // Assume we had this config: + // `'my-slot': () => ({ template: myBool ? html`
` : html``, renderAsDirectHostChild: true })` + // If myBool started as true,
would be rendered in first render above, a slot would be applied, + // resulting in
+ // However, when myBool changes to false, the would be rendered as root instead... + // We need to make sure that this "replaced root" gets the slot applied as well => + const isRerenderingRootOfTemplate = + renderAsDirectHostChild && + renderBefore.previousElementSibling && + !renderBefore.previousElementSibling.slot; + + if (isRerenderingRootOfTemplate) { + renderBefore.previousElementSibling.slot = slotName; + } } /** diff --git a/packages/ui/components/core/test/SlotMixin.test.js b/packages/ui/components/core/test/SlotMixin.test.js index c6bb70779..2f664ae05 100644 --- a/packages/ui/components/core/test/SlotMixin.test.js +++ b/packages/ui/components/core/test/SlotMixin.test.js @@ -345,6 +345,56 @@ describe('SlotMixin', () => { expect(isActiveElement(el._focusableNode._buttonNode, { deep: true })).to.be.true; }); + it('allows for switching template root in slot as a direct child', async () => { + const tagName = defineCE( + // @ts-expect-error + class extends SlotMixin(LitElement) { + static properties = { isSwitched: Boolean }; + + constructor() { + super(); + this.isSwitched = false; + } + + get slots() { + return { + ...super.slots, + 'my-root-switcher-node': () => ({ + template: this.isSwitched + ? html`
` + : html` `, + renderAsDirectHostChild: true, + }), + }; + } + + render() { + return html``; + } + + get _myRootSwitcherNode() { + return /** @type HTMLSpanElement */ ( + Array.from(this.children).find(elm => elm.slot === 'my-root-switcher-node') + ); + } + }, + ); + const el = /** @type {* & SlotHost} */ (await fixture(`<${tagName}>`)); + + expect(el._myRootSwitcherNode.id).to.equal('is-not-switched'); + expect(el.innerHTML).to.equal( + ` `, + ); + + el.isSwitched = true; + await el.updateComplete; + + expect(el._myRootSwitcherNode.id).to.equal('is-switched'); + expect(el.innerHTML).to.equal( + `
`, + ); + }); + describe('firstRenderOnConnected (for backwards compatibility)', () => { it('does render on connected when firstRenderOnConnected:true', async () => { // Start with elem that does not render on connectedCallback diff --git a/packages/ui/components/input-tel-dropdown/src/LionInputTelDropdown.js b/packages/ui/components/input-tel-dropdown/src/LionInputTelDropdown.js index 603382a49..ca7057b61 100644 --- a/packages/ui/components/input-tel-dropdown/src/LionInputTelDropdown.js +++ b/packages/ui/components/input-tel-dropdown/src/LionInputTelDropdown.js @@ -181,7 +181,7 @@ export class LionInputTelDropdown extends LionInputTel { return { template: templates.dropdown(this._templateDataDropdown), - renderAsDirectHostChild: Boolean, + renderAsDirectHostChild: true, }; }, };