Skip to content

Commit

Permalink
fix(ui/core): rerender direct host child with right slot attr when ro…
Browse files Browse the repository at this point in the history
…ot is switched
  • Loading branch information
tlouisse committed Jan 21, 2025
1 parent 81e2a1d commit 5344fde
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/great-pumas-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lion/ui': patch
---

[core] rerender direct host child with right slot attr when root is switched
15 changes: 15 additions & 0 deletions packages/ui/components/core/src/SlotMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`<div id=a></div>` : html`<span id=b></span>`, renderAsDirectHostChild: true })`
// If myBool started as true, <div id=a></div> would be rendered in first render above, a slot would be applied,
// resulting in <div id=a slot=my-slot></div>
// However, when myBool changes to false, the <span id=b></span> would be rendered as root instead...
// We need to make sure that this "replaced root" gets the slot applied as well => <span id=b slot=my-slot></span>
const isRerenderingRootOfTemplate =
renderAsDirectHostChild &&
renderBefore.previousElementSibling &&
!renderBefore.previousElementSibling.slot;

if (isRerenderingRootOfTemplate) {
renderBefore.previousElementSibling.slot = slotName;
}
}

/**
Expand Down
50 changes: 50 additions & 0 deletions packages/ui/components/core/test/SlotMixin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`<div id="is-switched"></div>`
: html`<span id="is-not-switched"> </span> `,
renderAsDirectHostChild: true,
}),
};
}

render() {
return html`<slot name="my-root-switcher-node"></slot>`;
}

get _myRootSwitcherNode() {
return /** @type HTMLSpanElement */ (
Array.from(this.children).find(elm => elm.slot === 'my-root-switcher-node')
);
}
},
);
const el = /** @type {* & SlotHost} */ (await fixture(`<${tagName}></${tagName}>`));

expect(el._myRootSwitcherNode.id).to.equal('is-not-switched');
expect(el.innerHTML).to.equal(
`<!--_start_slot_my-root-switcher-node_--><!----><span id="is-not-switched" slot="my-root-switcher-node"> </span> <!--_end_slot_my-root-switcher-node_-->`,
);

el.isSwitched = true;
await el.updateComplete;

expect(el._myRootSwitcherNode.id).to.equal('is-switched');
expect(el.innerHTML).to.equal(
`<!--_start_slot_my-root-switcher-node_--><!----><div id="is-switched" slot="my-root-switcher-node"></div><!--_end_slot_my-root-switcher-node_-->`,
);
});

describe('firstRenderOnConnected (for backwards compatibility)', () => {
it('does render on connected when firstRenderOnConnected:true', async () => {
// Start with elem that does not render on connectedCallback
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export class LionInputTelDropdown extends LionInputTel {

return {
template: templates.dropdown(this._templateDataDropdown),
renderAsDirectHostChild: Boolean,
renderAsDirectHostChild: true,
};
},
};
Expand Down

0 comments on commit 5344fde

Please sign in to comment.