diff --git a/packages/runtime-vapor/__tests__/dom/patchProp.spec.ts b/packages/runtime-vapor/__tests__/dom/patchProp.spec.ts index 539364a3c..e8866ad01 100644 --- a/packages/runtime-vapor/__tests__/dom/patchProp.spec.ts +++ b/packages/runtime-vapor/__tests__/dom/patchProp.spec.ts @@ -5,6 +5,7 @@ import { setAttr, setClass, setDOMProp, + setDynamicProps, setHtml, setStyle, setText, @@ -414,6 +415,54 @@ describe('patchProp', () => { test.todo('should be able to set something on SVG') }) + describe('setDynamicProps', () => { + test('basic set dynamic props', () => { + const el = document.createElement('div') + setDynamicProps(el, { foo: 'val' }, { bar: 'val' }) + expect(el.getAttribute('foo')).toBe('val') + expect(el.getAttribute('bar')).toBe('val') + }) + + test('should merge props', () => { + const el = document.createElement('div') + setDynamicProps(el, { foo: 'val' }, { foo: 'newVal' }) + expect(el.getAttribute('foo')).toBe('newVal') + }) + + test('should reset old props', () => { + const el = document.createElement('div') + + setDynamicProps(el, { foo: 'val' }) + expect(el.attributes.length).toBe(1) + expect(el.getAttribute('foo')).toBe('val') + + setDynamicProps(el, { bar: 'val' }) + expect(el.attributes.length).toBe(1) + expect(el.getAttribute('bar')).toBe('val') + expect(el.getAttribute('foo')).toBeNull() + }) + + test('should reset old modifier props', () => { + const el = document.createElement('div') + + setDynamicProps(el, { ['.foo']: 'val' }) + expect((el as any).foo).toBe('val') + + setDynamicProps(el, { ['.bar']: 'val' }) + expect((el as any).bar).toBe('val') + expect((el as any).foo).toBe('') + + setDynamicProps(el, { ['^foo']: 'val' }) + expect(el.attributes.length).toBe(1) + expect(el.getAttribute('foo')).toBe('val') + + setDynamicProps(el, { ['^bar']: 'val' }) + expect(el.attributes.length).toBe(1) + expect(el.getAttribute('bar')).toBe('val') + expect(el.getAttribute('foo')).toBeNull() + }) + }) + describe('setText', () => { test('should set textContent', () => { const el = document.createElement('div') diff --git a/packages/runtime-vapor/src/dom/prop.ts b/packages/runtime-vapor/src/dom/prop.ts index 638a64cbc..67912f261 100644 --- a/packages/runtime-vapor/src/dom/prop.ts +++ b/packages/runtime-vapor/src/dom/prop.ts @@ -9,20 +9,28 @@ import { normalizeStyle, toDisplayString, } from '@vue/shared' -import { currentInstance } from '../component' +import { type ElementMetadata, currentInstance } from '../component' import { warn } from '../warning' import { setStyle } from './style' -export function recordPropMetadata(el: Node, key: string, value: any): any { +function getMetadata(el: Node): ElementMetadata { + const EMPTY_METADATA = { props: {} } + if (!currentInstance) { // TODO implement error handling if (__DEV__) throw new Error('cannot be used out of component') - return + return EMPTY_METADATA } + let metadata = currentInstance.metadata.get(el) if (!metadata) { - currentInstance.metadata.set(el, (metadata = { props: {} })) + currentInstance.metadata.set(el, (metadata = EMPTY_METADATA)) } + return metadata +} + +export function recordPropMetadata(el: Node, key: string, value: any): any { + const metadata = getMetadata(el) const prev = metadata.props[key] metadata.props[key] = value return prev @@ -140,9 +148,21 @@ export function setDynamicProp(el: Element, key: string, value: any) { } export function setDynamicProps(el: Element, ...args: any) { + const oldProps = getMetadata(el).props const props = args.length > 1 ? mergeProps(...args) : args[0] - // TODO remove all of old props before set new props since there is containing dynamic key + for (const key in oldProps) { + // TODO should these keys be allowed as dynamic keys? The current logic of the runtime-core will throw an error + if (key === 'textContent' || key === 'innerHTML') { + continue + } + + const hasNewValue = props[key] || props['.' + key] || props['^' + key] + if (oldProps[key] && !hasNewValue) { + setDynamicProp(el, key, null) + } + } + for (const key in props) { setDynamicProp(el, key, props[key]) } diff --git a/playground/src/v-bind.vue b/playground/src/prop.vue similarity index 86% rename from playground/src/v-bind.vue rename to playground/src/prop.vue index 54cc2bc06..c715fe80b 100644 --- a/playground/src/v-bind.vue +++ b/playground/src/prop.vue @@ -11,7 +11,7 @@ const handleClick = () => {