-
Notifications
You must be signed in to change notification settings - Fork 794
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(runtime): relocate slotted content when slot parent element tag c…
…hanges (#5120) * fix(runtime): re-relocate slot if parent element's tagname changes If a slot is located in an element and that element's tag is dynamically changed (e.g. from `p` to `span`), we need to re-relocate the slot on re-render STENCIL-672: slot element loses its parent reference and disappears when its parent is rendered conditionally Fixes: #4284, #3913 * add e2e tests * code documentation * put changes behind slot fix flag * resolve new SNC * Apply suggestions from code review Co-authored-by: Ryan Waskiewicz <[email protected]> --------- Co-authored-by: Ryan Waskiewicz <[email protected]>
- Loading branch information
1 parent
e78a9a6
commit 4303d6a
Showing
6 changed files
with
182 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Component, h, Prop } from '@stencil/core'; | ||
|
||
@Component({ | ||
tag: 'slot-parent-tag-change-root', | ||
}) | ||
export class SlotParentTagChangeRoot { | ||
@Prop() element = 'p'; | ||
|
||
render() { | ||
return ( | ||
<slot-parent-tag-change element={this.element}> | ||
<slot></slot> | ||
</slot-parent-tag-change> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Component, h, Prop } from '@stencil/core'; | ||
|
||
@Component({ | ||
tag: 'slot-parent-tag-change', | ||
}) | ||
export class SlotParentTagChange { | ||
@Prop() element = 'p'; | ||
|
||
render() { | ||
return ( | ||
<this.element> | ||
<slot></slot> | ||
</this.element> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<!doctype html> | ||
<meta charset="utf8" /> | ||
<script src="./build/testapp.esm.js" type="module"></script> | ||
<script src="./build/testapp.js" nomodule></script> | ||
|
||
<!-- Test "top-level" slot --> | ||
<slot-parent-tag-change id="top-level"> Hello </slot-parent-tag-change> | ||
<!-- Test nested slot --> | ||
<slot-parent-tag-change-root> World </slot-parent-tag-change-root> | ||
|
||
<button type="button" id="top-level-button">Change Top-Level Slot</button> | ||
<button type="button" id="nested-button">Change Nested Slot</button> | ||
|
||
<script> | ||
document.querySelector('#top-level-button').addEventListener('click', () => { | ||
document.querySelector('#top-level').setAttribute('element', 'span'); | ||
}); | ||
document.querySelector('#nested-button').addEventListener('click', () => { | ||
document.querySelector('slot-parent-tag-change-root').setAttribute('element', 'span'); | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { setupDomTests, waitForChanges } from '../util'; | ||
|
||
/** | ||
* Tests the cases where a node is slotted in from the root `index.html` file | ||
* and the slot's parent element dynamically changes (e.g. from `p` to `span`). | ||
*/ | ||
describe('slot-parent-tag-change', () => { | ||
const { setupDom, tearDownDom } = setupDomTests(document); | ||
let app: HTMLElement; | ||
|
||
beforeEach(async () => { | ||
app = await setupDom('/slot-parent-tag-change/index.html'); | ||
}); | ||
|
||
afterEach(tearDownDom); | ||
|
||
describe('direct slot', () => { | ||
it('should relocate the text node to the slot after the parent tag changes', async () => { | ||
const root = app.querySelector('#top-level'); | ||
|
||
expect(root).not.toBeNull(); | ||
expect(root.children.length).toBe(1); | ||
expect(root.children[0].tagName).toBe('P'); | ||
expect(root.children[0].textContent.trim()).toBe('Hello'); | ||
|
||
app.querySelector<HTMLButtonElement>('#top-level-button').click(); | ||
await waitForChanges(); | ||
|
||
expect(root.children.length).toBe(1); | ||
expect(root.children[0].tagName).toBe('SPAN'); | ||
expect(root.children[0].textContent.trim()).toBe('Hello'); | ||
}); | ||
}); | ||
|
||
describe('nested slot', () => { | ||
it('should relocate the text node to the slot after the parent tag changes', async () => { | ||
const root = app.querySelector('slot-parent-tag-change-root slot-parent-tag-change'); | ||
|
||
expect(root).not.toBeNull(); | ||
expect(root.children.length).toBe(1); | ||
expect(root.children[0].tagName).toBe('P'); | ||
expect(root.children[0].textContent.trim()).toBe('World'); | ||
|
||
app.querySelector<HTMLButtonElement>('#nested-button').click(); | ||
await waitForChanges(); | ||
|
||
expect(root.children.length).toBe(1); | ||
expect(root.children[0].tagName).toBe('SPAN'); | ||
expect(root.children[0].textContent.trim()).toBe('World'); | ||
}); | ||
}); | ||
}); |