From 2de24dd2efa1b3db9f5c2365ecb2ef1d9c3b3abf Mon Sep 17 00:00:00 2001 From: raffobaffo <37933664+raffobaffo@users.noreply.github.com> Date: Mon, 27 Sep 2021 21:34:17 +0200 Subject: [PATCH] fix(customElementDependencyStyles): Recursively add styles from imported components. Missing feature, store available styles to avoid dupes. close #4662 --- .../__tests__/customElement.spec.ts | 43 ++++++++++++++++++ packages/runtime-dom/src/apiCustomElement.ts | 45 +++++++++++++++++-- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 7e69ec28ef2..a086b31a2f1 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -1,6 +1,7 @@ import { defineAsyncComponent, defineCustomElement, + defineComponent, h, inject, nextTick, @@ -314,6 +315,48 @@ describe('defineCustomElement', () => { const style = el.shadowRoot?.querySelector('style')! expect(style.textContent).toBe(`div { color: red; }`) }) + + test('should attach styles of children components to shadow dom', () => { + const Bar = defineComponent({ + styles: [`.green-color { color: green; }`], + render() { + return h( + 'h1', + { + attrs: { class: 'green-color' } + }, + 'hello' + ) + } + }) + const Foo = defineComponent({ + components: { Bar }, + styles: [`.blue-back { color: blue; }`], + render() { + return h( + 'span', + { + attrs: { class: 'blue-back' } + }, + '' + ) + } + }) + + const FooBar = defineCustomElement({ + components: { Foo }, + styles: [`div { color: red; }`], + render() { + return h('div', '') + } + }) + customElements.define('my-el-with-nested-styles', FooBar) + container.innerHTML = `` + const el = container.childNodes[0] as VueElement + const style = el.shadowRoot?.querySelectorAll('style')! + expect(style[0].textContent).toBe(`.green-color { color: green; }`) + expect(style[1].textContent).toBe(`.blue-back { color: blue; }`) + }) }) describe('async', () => { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index f72b8765f6d..d0fdb9c09a9 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -19,7 +19,8 @@ import { nextTick, warn, ConcreteComponent, - ComponentOptions + ComponentOptions, + Component } from '@vue/runtime-core' import { camelize, extend, hyphenate, isArray, toNumber } from '@vue/shared' import { hydrate, render } from '.' @@ -215,7 +216,7 @@ export class VueElement extends BaseClass { const resolve = (def: InnerComponentDef) => { this._resolved = true - const { props, styles } = def + const { props } = def const hasOptions = !isArray(props) const rawKeys = props ? (hasOptions ? Object.keys(props) : props) : [] @@ -252,7 +253,7 @@ export class VueElement extends BaseClass { } }) } - this._applyStyles(styles) + this._applyStyles(this._getChildrenComponentsStyles(def)) } const asyncDef = (this._def as ComponentOptions).__asyncLoader @@ -367,4 +368,42 @@ export class VueElement extends BaseClass { }) } } + + private _getChildrenComponentsStyles( + component: Component & { + components?: Record + styles?: string[] + } + ): string[] { + let componentStyles: string[] = [] + + if (component.components) { + componentStyles = Object.values(component.components).reduce( + ( + aggregatedStyles: string[], + nestedComponent: Component & { + components?: Record + styles?: string[] + } + ) => { + if (nestedComponent?.components) { + aggregatedStyles = [ + ...aggregatedStyles, + ...this._getChildrenComponentsStyles(nestedComponent) + ] + } + return nestedComponent.styles + ? [...aggregatedStyles, ...nestedComponent.styles] + : aggregatedStyles + }, + [] as string[] + ) + } + + if (component.styles) { + componentStyles.push(...component.styles) + } + + return componentStyles + } }