diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 6b9f7d1391e..df438d47eee 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -396,6 +396,38 @@ describe('defineCustomElement', () => { expect(e.value).toBe('hi') }) + // #12214 + test('Boolean prop with default true', async () => { + const E = defineCustomElement({ + props: { + foo: { + type: Boolean, + default: true, + }, + }, + render() { + return String(this.foo) + }, + }) + customElements.define('my-el-default-true', E) + container.innerHTML = `` + const e = container.childNodes[0] as HTMLElement & { foo: any }, + shadowRoot = e.shadowRoot as ShadowRoot + expect(shadowRoot.innerHTML).toBe('true') + e.foo = undefined + await nextTick() + expect(shadowRoot.innerHTML).toBe('true') + e.foo = false + await nextTick() + expect(shadowRoot.innerHTML).toBe('false') + e.foo = null + await nextTick() + expect(shadowRoot.innerHTML).toBe('null') + e.foo = '' + await nextTick() + expect(shadowRoot.innerHTML).toBe('true') + }) + test('support direct setup function syntax with extra options', () => { const E = defineCustomElement( props => { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 6ddaf897130..c90c5725e17 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -232,6 +232,8 @@ export class VueElement private _styleChildren = new WeakSet() private _pendingResolve: Promise | undefined private _parent: VueElement | undefined + private _removedAttributes = new Set() + /** * dev only */ @@ -466,8 +468,12 @@ export class VueElement protected _setAttr(key: string): void { if (key.startsWith('data-v-')) return const has = this.hasAttribute(key) - let value = has ? this.getAttribute(key) : REMOVAL const camelKey = camelize(key) + if (this._removedAttributes.has(camelKey)) { + if (!has) return + else this._removedAttributes.delete(camelKey) + } + let value = has ? this.getAttribute(key) : REMOVAL if (has && this._numberProps && this._numberProps[camelKey]) { value = toNumber(value) } @@ -511,6 +517,7 @@ export class VueElement this.setAttribute(hyphenate(key), val + '') } else if (!val) { this.removeAttribute(hyphenate(key)) + this._removedAttributes.add(key) } } }