expect(e.shadowRoot!.innerHTML).toBe(`<div>fooValue</div>`)
app.unmount()
})
+
+ // #11276
+ test('delete prop on attr removal', async () => {
+ const E = defineCustomElement({
+ props: {
+ boo: {
+ type: Boolean,
+ },
+ },
+ render() {
+ return this.boo + ',' + typeof this.boo
+ },
+ })
+ customElements.define('el-attr-removal', E)
+ container.innerHTML = '<el-attr-removal boo>'
+ const e = container.childNodes[0] as VueElement
+ expect(e.shadowRoot!.innerHTML).toBe(`true,boolean`)
+ e.removeAttribute('boo')
+ await nextTick()
+ expect(e.shadowRoot!.innerHTML).toBe(`false,boolean`)
+ })
})
} from '@vue/shared'
import { createApp, createSSRApp, render } from '.'
+// marker for attr removal
+const REMOVAL = {}
+
export type VueElementConstructor<P = {}> = {
new (initialProps?: Record<string, any>): VueElement & P
}
protected _setAttr(key: string) {
if (key.startsWith('data-v-')) return
- let value = this.hasAttribute(key) ? this.getAttribute(key) : undefined
+ const has = this.hasAttribute(key)
+ let value = has ? this.getAttribute(key) : REMOVAL
const camelKey = camelize(key)
- if (this._numberProps && this._numberProps[camelKey]) {
+ if (has && this._numberProps && this._numberProps[camelKey]) {
value = toNumber(value)
}
this._setProp(camelKey, value, false, true)
*/
_setProp(key: string, val: any, shouldReflect = true, shouldUpdate = false) {
if (val !== this._props[key]) {
- this._props[key] = val
+ if (val === REMOVAL) {
+ delete this._props[key]
+ } else {
+ this._props[key] = val
+ }
if (shouldUpdate && this._instance) {
this._update()
}