Skip to content

Commit b805cef

Browse files
committed
fix(custom-element): fix that boolean prop with default true can't updated to falsy value (#12214)
1 parent 657603d commit b805cef

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

packages/runtime-dom/__tests__/customElement.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,38 @@ describe('defineCustomElement', () => {
396396
expect(e.value).toBe('hi')
397397
})
398398

399+
// #12214
400+
test('Boolean prop with default true', async () => {
401+
const E = defineCustomElement({
402+
props: {
403+
foo: {
404+
type: Boolean,
405+
default: true,
406+
},
407+
},
408+
render() {
409+
return String(this.foo)
410+
},
411+
})
412+
customElements.define('my-el-default-true', E)
413+
container.innerHTML = `<my-el-default-true></my-el-default-true>`
414+
const e = container.childNodes[0] as HTMLElement & { foo: any },
415+
shadowRoot = e.shadowRoot as ShadowRoot
416+
expect(shadowRoot.innerHTML).toBe('true')
417+
e.foo = undefined
418+
await nextTick()
419+
expect(shadowRoot.innerHTML).toBe('true')
420+
e.foo = false
421+
await nextTick()
422+
expect(shadowRoot.innerHTML).toBe('false')
423+
e.foo = null
424+
await nextTick()
425+
expect(shadowRoot.innerHTML).toBe('null')
426+
e.foo = ''
427+
await nextTick()
428+
expect(shadowRoot.innerHTML).toBe('true')
429+
})
430+
399431
test('support direct setup function syntax with extra options', () => {
400432
const E = defineCustomElement(
401433
props => {

packages/runtime-dom/src/apiCustomElement.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ export class VueElement
242242
private _childStyles?: Map<string, HTMLStyleElement[]>
243243
private _ob?: MutationObserver | null = null
244244
private _slots?: Record<string, Node[]>
245+
private _skipRemoveSet = new Set<string>()
245246

246247
constructor(
247248
/**
@@ -466,8 +467,12 @@ export class VueElement
466467
protected _setAttr(key: string): void {
467468
if (key.startsWith('data-v-')) return
468469
const has = this.hasAttribute(key)
469-
let value = has ? this.getAttribute(key) : REMOVAL
470470
const camelKey = camelize(key)
471+
let value = has ? this.getAttribute(key) : REMOVAL
472+
if (this._skipRemoveSet.has(camelKey)) {
473+
if (value === REMOVAL) return
474+
else this._skipRemoveSet.delete(camelKey)
475+
}
471476
if (has && this._numberProps && this._numberProps[camelKey]) {
472477
value = toNumber(value)
473478
}
@@ -510,6 +515,7 @@ export class VueElement
510515
} else if (typeof val === 'string' || typeof val === 'number') {
511516
this.setAttribute(hyphenate(key), val + '')
512517
} else if (!val) {
518+
this._skipRemoveSet.add(key)
513519
this.removeAttribute(hyphenate(key))
514520
}
515521
}

0 commit comments

Comments
 (0)