diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts new file mode 100644 index 00000000..b3407b34 --- /dev/null +++ b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts @@ -0,0 +1,80 @@ +import { + render, + useCSSVars, + h, + reactive, + nextTick, + ComponentOptions +} from '@vue/runtime-dom' + +describe('useCssVars', () => { + async function assertCssVars( + getApp: (state: any) => ComponentOptions, + scopeId?: string + ) { + const state = reactive({ color: 'red' }) + const App = getApp(state) + const root = document.createElement('div') + const prefix = scopeId ? `${scopeId}-` : `` + + render(h(App), root) + for (const c of [].slice.call(root.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--${prefix}color`)) + } + + state.color = 'green' + await nextTick() + for (const c of [].slice.call(root.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--${prefix}color`)) + } + } + + test('basic', async () => { + await assertCssVars(state => ({ + setup() { + // test receiving render context + useCSSVars((ctx: any) => ({ + color: ctx.color + })) + return state + }, + render() { + return h('div') + } + })) + }) + + test('on fragment root', async () => { + await assertCssVars(state => ({ + setup() { + useCSSVars(() => state) + return () => [h('div'), h('div')] + } + })) + }) + + test('on HOCs', async () => { + const Child = () => [h('div'), h('div')] + + await assertCssVars(state => ({ + setup() { + useCSSVars(() => state) + return () => h(Child) + } + })) + }) + + test('with scopeId', async () => { + const id = 'v-12345' + + await assertCssVars( + state => ({ + setup() { + useCSSVars(() => state, id) + return () => h('div') + } + }), + id + ) + }) +}) diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index bd2a7c6c..480598ae 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -10,7 +10,8 @@ import { import { ShapeFlags } from '@vue/shared/src' export function useCSSVars( - getter: (ctx: ComponentPublicInstance) => Record + getter: (ctx: ComponentPublicInstance) => Record, + scopeId?: string ) { const instance = getCurrentInstance() if (!instance) { @@ -20,22 +21,27 @@ export function useCSSVars( } onMounted(() => { watchEffect(() => { - setVarsOnVNode(instance.vnode, getter(instance.proxy!)) + setVarsOnVNode(instance.vnode, getter(instance.proxy!), scopeId) }) }) } -function setVarsOnVNode(vnode: VNode, vars: Record) { +function setVarsOnVNode( + vnode: VNode, + vars: Record, + scopeId?: string +) { // drill down HOCs until it's a non-component vnode while (vnode.component) { vnode = vnode.component.subTree } if (vnode.shapeFlag & ShapeFlags.ELEMENT && vnode.el) { const style = vnode.el.style + const prefix = scopeId ? scopeId + '-' : '' for (const key in vars) { - style.setProperty(`--${key}`, vars[key]) + style.setProperty(`--${prefix}${key}`, vars[key]) } } else if (vnode.type === Fragment) { - ;(vnode.children as VNode[]).forEach(c => setVarsOnVNode(c, vars)) + ;(vnode.children as VNode[]).forEach(c => setVarsOnVNode(c, vars, scopeId)) } }