diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index df438d47eee..ac9cb814d8b 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -708,6 +708,31 @@ describe('defineCustomElement', () => { `
changedA! changedB!
`, ) }) + + test('should resolve correct parent when element is slotted in shadow DOM', async () => { + const GrandParent = defineCustomElement({ + provide: { + foo: ref('GrandParent'), + }, + render() { + return h('my-parent-in-shadow', h('slot')) + }, + }) + const Parent = defineCustomElement({ + provide: { + foo: ref('Parent'), + }, + render() { + return h('slot') + }, + }) + customElements.define('my-grand-parent', GrandParent) + customElements.define('my-parent-in-shadow', Parent) + container.innerHTML = `` + const grandParent = container.childNodes[0] as VueElement, + consumer = grandParent.firstElementChild as VueElement + expect(consumer.shadowRoot!.textContent).toBe('Parent') + }) }) describe('styles', () => { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index aeeaeec9b9f..edec8855ded 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -288,7 +288,12 @@ export class VueElement // locate nearest Vue custom element parent for provide/inject let parent: Node | null = this while ( - (parent = parent && (parent.parentNode || (parent as ShadowRoot).host)) + (parent = + parent && + // #12479 should check assignedSlot first to get correct parent + ((parent as Element).assignedSlot || + parent.parentNode || + (parent as ShadowRoot).host)) ) { if (parent instanceof VueElement) { this._parent = parent