parent
							
								
									5bd0ac67f3
								
							
						
					
					
						commit
						0cfa2112ce
					
				| @ -136,26 +136,40 @@ describe('defineCustomElement', () => { | ||||
|       const E = defineCustomElement({ | ||||
|         props: { | ||||
|           foo: Number, | ||||
|           bar: Boolean | ||||
|           bar: Boolean, | ||||
|           baz: String | ||||
|         }, | ||||
|         render() { | ||||
|           return [this.foo, typeof this.foo, this.bar, typeof this.bar].join( | ||||
|             ' ' | ||||
|           ) | ||||
|           return [ | ||||
|             this.foo, | ||||
|             typeof this.foo, | ||||
|             this.bar, | ||||
|             typeof this.bar, | ||||
|             this.baz, | ||||
|             typeof this.baz | ||||
|           ].join(' ') | ||||
|         } | ||||
|       }) | ||||
|       customElements.define('my-el-props-cast', E) | ||||
|       container.innerHTML = `<my-el-props-cast foo="1"></my-el-props-cast>` | ||||
|       container.innerHTML = `<my-el-props-cast foo="1" baz="12345"></my-el-props-cast>` | ||||
|       const e = container.childNodes[0] as VueElement | ||||
|       expect(e.shadowRoot!.innerHTML).toBe(`1 number false boolean`) | ||||
|       expect(e.shadowRoot!.innerHTML).toBe( | ||||
|         `1 number false boolean 12345 string` | ||||
|       ) | ||||
| 
 | ||||
|       e.setAttribute('bar', '') | ||||
|       await nextTick() | ||||
|       expect(e.shadowRoot!.innerHTML).toBe(`1 number true boolean`) | ||||
|       expect(e.shadowRoot!.innerHTML).toBe(`1 number true boolean 12345 string`) | ||||
| 
 | ||||
|       e.setAttribute('foo', '2e1') | ||||
|       await nextTick() | ||||
|       expect(e.shadowRoot!.innerHTML).toBe(`20 number true boolean`) | ||||
|       expect(e.shadowRoot!.innerHTML).toBe( | ||||
|         `20 number true boolean 12345 string` | ||||
|       ) | ||||
| 
 | ||||
|       e.setAttribute('baz', '2e1') | ||||
|       await nextTick() | ||||
|       expect(e.shadowRoot!.innerHTML).toBe(`20 number true boolean 2e1 string`) | ||||
|     }) | ||||
| 
 | ||||
|     test('handling properties set before upgrading', () => { | ||||
| @ -392,5 +406,25 @@ describe('defineCustomElement', () => { | ||||
|       e2.msg = 'hello' | ||||
|       expect(e2.shadowRoot!.innerHTML).toBe(`<div>hello</div>`) | ||||
|     }) | ||||
| 
 | ||||
|     test('Number prop casting before resolve', async () => { | ||||
|       const E = defineCustomElement( | ||||
|         defineAsyncComponent(() => { | ||||
|           return Promise.resolve({ | ||||
|             props: { n: Number }, | ||||
|             render(this: any) { | ||||
|               return h('div', this.n + ',' + typeof this.n) | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       ) | ||||
|       customElements.define('my-el-async-3', E) | ||||
|       container.innerHTML = `<my-el-async-3 n="2e1"></my-el-async-3>` | ||||
| 
 | ||||
|       await new Promise(r => setTimeout(r)) | ||||
| 
 | ||||
|       const e = container.childNodes[0] as VueElement | ||||
|       expect(e.shadowRoot!.innerHTML).toBe(`<div>20,number</div>`) | ||||
|     }) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -154,6 +154,7 @@ export class VueElement extends BaseClass { | ||||
| 
 | ||||
|   private _connected = false | ||||
|   private _resolved = false | ||||
|   private _numberProps: Record<string, true> | null = null | ||||
|   private _styles?: HTMLStyleElement[] | ||||
| 
 | ||||
|   constructor( | ||||
| @ -179,19 +180,18 @@ export class VueElement extends BaseClass { | ||||
|       this._setAttr(this.attributes[i].name) | ||||
|     } | ||||
|     // watch future attr changes
 | ||||
|     const observer = new MutationObserver(mutations => { | ||||
|     new MutationObserver(mutations => { | ||||
|       for (const m of mutations) { | ||||
|         this._setAttr(m.attributeName!) | ||||
|       } | ||||
|     }) | ||||
|     observer.observe(this, { attributes: true }) | ||||
|     }).observe(this, { attributes: true }) | ||||
|   } | ||||
| 
 | ||||
|   connectedCallback() { | ||||
|     this._connected = true | ||||
|     if (!this._instance) { | ||||
|       this._resolveDef() | ||||
|       render(this._createVNode(), this.shadowRoot!) | ||||
|       this._update() | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -215,15 +215,33 @@ export class VueElement extends BaseClass { | ||||
| 
 | ||||
|     const resolve = (def: InnerComponentDef) => { | ||||
|       this._resolved = true | ||||
|       const { props, styles } = def | ||||
|       const hasOptions = !isArray(props) | ||||
|       const rawKeys = props ? (hasOptions ? Object.keys(props) : props) : [] | ||||
| 
 | ||||
|       // cast Number-type props set before resolve
 | ||||
|       let numberProps | ||||
|       if (hasOptions) { | ||||
|         for (const key in this._props) { | ||||
|           const opt = props[key] | ||||
|           if (opt === Number || (opt && opt.type === Number)) { | ||||
|             this._props[key] = toNumber(this._props[key]) | ||||
|             ;(numberProps || (numberProps = Object.create(null)))[key] = true | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       if (numberProps) { | ||||
|         this._numberProps = numberProps | ||||
|         this._update() | ||||
|       } | ||||
| 
 | ||||
|       // check if there are props set pre-upgrade or connect
 | ||||
|       for (const key of Object.keys(this)) { | ||||
|         if (key[0] !== '_') { | ||||
|           this._setProp(key, this[key as keyof this]) | ||||
|         } | ||||
|       } | ||||
|       const { props, styles } = def | ||||
|       // defining getter/setters on prototype
 | ||||
|       const rawKeys = props ? (isArray(props) ? props : Object.keys(props)) : [] | ||||
|       for (const key of rawKeys.map(camelize)) { | ||||
|         Object.defineProperty(this, key, { | ||||
|           get() { | ||||
| @ -246,7 +264,11 @@ export class VueElement extends BaseClass { | ||||
|   } | ||||
| 
 | ||||
|   protected _setAttr(key: string) { | ||||
|     this._setProp(camelize(key), toNumber(this.getAttribute(key)), false) | ||||
|     let value = this.getAttribute(key) | ||||
|     if (this._numberProps && this._numberProps[key]) { | ||||
|       value = toNumber(value) | ||||
|     } | ||||
|     this._setProp(camelize(key), value, false) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -263,7 +285,7 @@ export class VueElement extends BaseClass { | ||||
|     if (val !== this._props[key]) { | ||||
|       this._props[key] = val | ||||
|       if (this._instance) { | ||||
|         render(this._createVNode(), this.shadowRoot!) | ||||
|         this._update() | ||||
|       } | ||||
|       // reflect
 | ||||
|       if (shouldReflect) { | ||||
| @ -278,6 +300,10 @@ export class VueElement extends BaseClass { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private _update() { | ||||
|     render(this._createVNode(), this.shadowRoot!) | ||||
|   } | ||||
| 
 | ||||
|   private _createVNode(): VNode<any, any> { | ||||
|     const vnode = createVNode(this._def, extend({}, this._props)) | ||||
|     if (!this._instance) { | ||||
| @ -298,7 +324,7 @@ export class VueElement extends BaseClass { | ||||
|             if (!(this._def as ComponentOptions).__asyncLoader) { | ||||
|               // reload
 | ||||
|               this._instance = null | ||||
|               render(this._createVNode(), this.shadowRoot!) | ||||
|               this._update() | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user