From 5bcec7b72b3ae4170f0038ae63a0228e1a877350 Mon Sep 17 00:00:00 2001 From: TatsuyaYamamoto Date: Wed, 11 Aug 2021 21:45:31 +0900 Subject: [PATCH] feat(runtime-dom): Apply nested component styles when using defineCustomElements --- .../__tests__/customElement.spec.ts | 48 +++++++++++++++++++ packages/runtime-dom/src/apiCustomElement.ts | 29 ++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 042ac68a7af..1318be3a93a 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -300,6 +300,54 @@ describe('defineCustomElement', () => { const style = el.shadowRoot?.querySelector('style')! expect(style.textContent).toBe(`div { color: red; }`) }) + + test('should attach styles recursively to shadow dom', () => { + const SecondDepthChild1 = { + styles: [`div { color: yellow; }`], + render() { + return h('div', '2nd') + } + } + const SecondDepthChild2 = { + styles: [`div { color: green; }`], + render() { + return h('div', '2nd') + } + } + const FirstDepthChild = { + styles: [`div { color: blue; }`], + components: { SecondDepthChild1, SecondDepthChild2 }, + render() { + return h('div', [ + '1st', + h('SecondDepthChild1'), + h('SecondDepthChild2') + ]) + } + } + const RootComponent = { + styles: [`div { color: red; }`], + components: { FirstDepthChild }, + render() { + return h('div', ['root', h('FirstDepthChild')]) + } + } + + const customElement = defineCustomElement(RootComponent) + customElements.define('my-el-with-recursive-styles', customElement) + + container.innerHTML = `` + const el = container.childNodes[0] as VueElement + const styles = el.shadowRoot?.querySelectorAll('style')! + const styleTexts = Array.from(styles).map(style => style.textContent) + + expect(styleTexts).toEqual([ + ...RootComponent.styles, + ...FirstDepthChild.styles, + ...SecondDepthChild1.styles, + ...SecondDepthChild2.styles + ]) + }) }) describe('async', () => { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index ca29a436c72..730edcde417 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -18,6 +18,7 @@ import { defineComponent, nextTick, warn, + Component, ConcreteComponent, ComponentOptions } from '@vue/runtime-core' @@ -221,7 +222,7 @@ export class VueElement extends BaseClass { this._setProp(key, this[key as keyof this]) } } - const { props, styles } = def + const { props } = def // defining getter/setters on prototype const rawKeys = props ? (isArray(props) ? props : Object.keys(props)) : [] for (const key of rawKeys.map(camelize)) { @@ -234,7 +235,7 @@ export class VueElement extends BaseClass { } }) } - this._applyStyles(styles) + this._applyStyles(this._getStylesRecursively(def)) } const asyncDef = (this._def as ComponentOptions).__asyncLoader @@ -341,4 +342,28 @@ export class VueElement extends BaseClass { }) } } + + private _getStylesRecursively( + component: Component & { + components?: Record + styles?: string[] + } + ): string[] { + const customElementStyles: string[] = [] + + if (component.styles) { + customElementStyles.push(...component.styles) + } + + const childComponents = component.components + if (childComponents) { + Object.keys(childComponents).forEach(name => { + const childComponent = childComponents[name] + const styles = this._getStylesRecursively(childComponent) + customElementStyles.push(...styles) + }) + } + + return customElementStyles + } }