import { tmpl, domBackend, composedBackend, shadowBackend, execWithWarn } from '../base/env' import { virtual as matchElementWithDom } from '../base/match' import * as glassEasel from '../../src' const domHtml = (elem: glassEasel.Element): string => { const domElem = elem.getBackendElement() as unknown as Element return domElem.innerHTML } const testCases = (testBackend: glassEasel.GeneralBackendContext) => { const componentSpace = new glassEasel.ComponentSpace() componentSpace.updateComponentOptions({ writeFieldsToNode: true, writeIdToDOM: true, }) componentSpace.defineComponent({ is: '', }) describe('dynamic slot', () => { describe('core', () => { test('should support duplicate slot', () => { const ops: Array<[number, string]> = [] const comp = componentSpace .define() .property('s', String) .lifetime('attached', function () { ops.push([-1, this.data.s.toString()]) }) .lifetime('detached', function () { ops.push([-2, this.data.s.toString()]) }) .lifetime('moved', function () { ops.push([-3, this.data.s.toString()]) }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child, comp }) .definition({ template: tmpl(` A B C D `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! const a = childElem.$.a as glassEasel.Element const slot1 = childElem.getShadowRoot()!.createVirtualNode('slot') glassEasel.Element.setSlotName(slot1, '') const slotA1 = childElem.getShadowRoot()!.createVirtualNode('slot') glassEasel.Element.setSlotName(slotA1, 'a') const slotA2 = childElem.getShadowRoot()!.createVirtualNode('slot') glassEasel.Element.setSlotName(slotA2, 'a') const slotB = childElem.getShadowRoot()!.createVirtualNode('slot') glassEasel.Element.setSlotName(slotB, 'b') glassEasel.Element.pretendAttached(parentElem) expect(childElem.childNodes.length).toBe(0) expect(ops).toEqual([]) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) a.appendChild(slotA1) expect(childElem.childNodes.length).toBe(4) expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes) expect(domHtml(parentElem)).toBe('A') expect(ops).toEqual([[-1, 'A']]) matchElementWithDom(parentElem) a.appendChild(slotA2) expect(childElem.childNodes.length).toBe(8) expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4)) expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8)) expect(domHtml(parentElem)).toBe( 'AA', ) expect(ops).toEqual([ [-1, 'A'], [-1, 'A'], ]) matchElementWithDom(parentElem) a.insertBefore(slotB, slotA2) expect(childElem.childNodes.length).toBe(12) expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4)) expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8)) expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(8, 12)) expect(domHtml(parentElem)).toBe( 'ABA', ) expect(ops).toEqual([ [-1, 'A'], [-1, 'A'], [-1, 'B'], ]) matchElementWithDom(parentElem) a.removeChild(slotA1) expect(childElem.childNodes.length).toBe(8) expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4)) expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8)) expect(domHtml(parentElem)).toBe( 'BA', ) expect(ops).toEqual([ [-1, 'A'], [-1, 'A'], [-1, 'B'], [-2, 'A'], ]) matchElementWithDom(parentElem) a.appendChild(slotA1) expect(childElem.childNodes.length).toBe(12) expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4)) expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8)) expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(8, 12)) expect(domHtml(parentElem)).toBe( 'BAA', ) expect(ops).toEqual([ [-1, 'A'], [-1, 'A'], [-1, 'B'], [-2, 'A'], [-1, 'A'], ]) matchElementWithDom(parentElem) a.appendChild(slot1) expect(childElem.childNodes.length).toBe(16) expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4)) expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8)) expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(8, 12)) expect(slot1.getComposedChildren()).toEqual(childElem.childNodes.slice(12, 16)) expect(domHtml(parentElem)).toBe( 'BAAD', ) expect(ops).toEqual([ [-1, 'A'], [-1, 'A'], [-1, 'B'], [-2, 'A'], [-1, 'A'], [-1, 'D'], ]) matchElementWithDom(parentElem) }) test('should support slot element modification', () => { const child = componentSpace .define() .options({ dynamicSlots: true }) .definition({ template: tmpl(' '), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .definition({ template: tmpl(''), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! const slotA = childElem.getShadowRoot()!.childNodes[0]! as glassEasel.Element const slotB = (childElem.getShadowRoot()!.childNodes[1]! as glassEasel.Element) .childNodes[0]! as glassEasel.Element const slotC = childElem.getShadowRoot()!.childNodes[2]! as glassEasel.Element const contentA = parentElem.getShadowRoot()!.createVirtualNode('virtual') contentA.appendChild(parentElem.getShadowRoot()!.createTextNode('A')) const contentB = parentElem.getShadowRoot()!.createVirtualNode('virtual') glassEasel.Element.setInheritSlots(contentB) const textB = parentElem.getShadowRoot()!.createTextNode('B') contentB.appendChild(textB) glassEasel.Element.pretendAttached(parentElem) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) childElem.appendChild(contentA) expect(contentA.getComposedParent()).toBe(null) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) glassEasel.Element.setSlotElement(contentA, slotA) expect(contentA.getComposedParent()).toBe(slotA) expect(slotA.getComposedChildren()).toEqual([contentA]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('A') matchElementWithDom(parentElem) glassEasel.Element.setSlotElement(contentA, slotB) expect(contentA.getComposedParent()).toBe(slotB) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([contentA]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('A') matchElementWithDom(parentElem) childElem.removeChild(contentA) expect(contentA.getComposedParent()).toBe(null) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) glassEasel.Element.setSlotElement(contentA, slotC) childElem.appendChild(contentA) expect(contentA.getComposedParent()).toBe(slotC) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([contentA]) expect(domHtml(parentElem)).toBe('A') matchElementWithDom(parentElem) childElem.appendChild(contentB) expect(contentA.getComposedParent()).toBe(slotC) expect(contentB.getComposedParent()).toBe(null) expect(textB.getComposedParent()).toBe(null) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([contentA]) expect(domHtml(parentElem)).toBe('A') matchElementWithDom(parentElem) glassEasel.Element.setSlotElement(contentB, slotC) expect(contentA.getComposedParent()).toBe(slotC) expect(contentB.getComposedParent()).toBe(slotC) expect(textB.getComposedParent()).toBe(slotC) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([contentA, contentB, textB]) expect(domHtml(parentElem)).toBe('AB') matchElementWithDom(parentElem) childElem.appendChild(contentA) expect(contentA.getComposedParent()).toBe(slotC) expect(contentB.getComposedParent()).toBe(slotC) expect(textB.getComposedParent()).toBe(slotC) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([contentB, textB, contentA]) expect(domHtml(parentElem)).toBe('BA') matchElementWithDom(parentElem) glassEasel.Element.setSlotElement(contentB, slotB) expect(contentA.getComposedParent()).toBe(slotC) expect(contentB.getComposedParent()).toBe(slotB) expect(textB.getComposedParent()).toBe(slotB) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([contentB, textB]) expect(slotC.getComposedChildren()).toEqual([contentA]) expect(domHtml(parentElem)).toBe('BA') matchElementWithDom(parentElem) childElem.removeChild(contentB) expect(contentA.getComposedParent()).toBe(slotC) expect(contentB.getComposedParent()).toBe(null) expect(textB.getComposedParent()).toBe(null) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([contentA]) expect(domHtml(parentElem)).toBe('A') matchElementWithDom(parentElem) glassEasel.Element.setSlotElement(contentB, slotA) childElem.appendChild(contentB) expect(contentA.getComposedParent()).toBe(slotC) expect(contentB.getComposedParent()).toBe(slotA) expect(textB.getComposedParent()).toBe(slotA) expect(slotA.getComposedChildren()).toEqual([contentB, textB]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([contentA]) expect(domHtml(parentElem)).toBe('BA') matchElementWithDom(parentElem) childElem.removeChild(contentA) expect(contentA.getComposedParent()).toBe(null) expect(contentB.getComposedParent()).toBe(slotA) expect(textB.getComposedParent()).toBe(slotA) expect(slotA.getComposedChildren()).toEqual([contentB, textB]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('B') matchElementWithDom(parentElem) childElem.removeChild(contentB) expect(contentA.getComposedParent()).toBe(null) expect(contentB.getComposedParent()).toBe(null) expect(textB.getComposedParent()).toBe(null) expect(slotA.getComposedChildren()).toEqual([]) expect(slotB.getComposedChildren()).toEqual([]) expect(slotC.getComposedChildren()).toEqual([]) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) }) }) describe('tmpl', () => { test('should support slot value usages', () => { const child = componentSpace .define() .options({ dynamicSlots: true, virtualHost: true }) .definition({ data: { v: 123, }, template: tmpl(` `), }) .data(() => ({ sn: 'abc', })) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .definition({ template: tmpl(`
{{someText}}
`), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('
123
') matchElementWithDom(parentElem) childElem.setData({ v: 456 }) expect(domHtml(parentElem)).toBe('
456
') matchElementWithDom(parentElem) }) test('should support slot content modification', () => { const child = componentSpace .define() .options({ dynamicSlots: true }) .definition({ template: tmpl(` `), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .data(() => ({ slotName1: 'a', slotContent1: 'A', slotName2: 'a', slotContent2: 'B', })) .definition({ template: tmpl(` {{slotContent1}} {{slotContent2}} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('ABABAB') matchElementWithDom(parentElem) parentElem.setData({ slotContent1: 'AA' }) expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('AABAABAAB') matchElementWithDom(parentElem) parentElem.setData({ slotName1: 'b' }) expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('BBB') matchElementWithDom(parentElem) parentElem.setData({ slotContent1: 'C', slotName1: 'a' }) expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('CBCBCB') matchElementWithDom(parentElem) parentElem.setData({ slotContent2: 'D' }) expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('CDCDCD') matchElementWithDom(parentElem) parentElem.setData({ slotName2: 'c' }) expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('CCC') matchElementWithDom(parentElem) parentElem.setData({ slotName1: 'b', slotName2: 'c' }) expect(childElem.childNodes.length).toBe(6) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) }) test('should support slot name modification', () => { const child = componentSpace .define() .options({ dynamicSlots: true }) .definition({ template: tmpl(` `), }) .data(() => ({ sn: 'abc', })) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .definition({ template: tmpl(` 456
{{someText}}
`), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('
123
') matchElementWithDom(parentElem) childElem.setData({ sn: '' }) expect(domHtml(parentElem)).toBe('456') matchElementWithDom(parentElem) childElem.setData({ sn: 'def' }) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) childElem.setData({ sn: 'abc' }) expect(domHtml(parentElem)).toBe('
123
') matchElementWithDom(parentElem) }) test('nested inside dynamic slots', () => { let ops: Array<[number, string]> = [] const x = componentSpace .define() .property('s', String) .lifetime('attached', function () { ops.push([-1, this.data.s.toString()]) }) .lifetime('detached', function () { ops.push([-2, this.data.s.toString()]) }) .lifetime('moved', function () { ops.push([-3, this.data.s.toString()]) }) .registerComponent() const c1 = componentSpace .define() .property('enableA1', Boolean) .property('enableA2', Boolean) .definition({ template: tmpl( ` `, ), }) .registerComponent() const c2 = componentSpace .define() .options({ multipleSlots: true }) .property('enableA1', Boolean) .property('enableA2', Boolean) .definition({ template: tmpl( ` `, ), }) .registerComponent() const c3 = componentSpace .define() .options({ dynamicSlots: true }) .property('enableA1', Boolean) .property('enableA2', Boolean) .definition({ template: tmpl( ` `, ), }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .property('duplicateSlots', Boolean) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .data(() => ({ slotName1: 'a1', slotContent1: 'A', slotName2: 'a1', slotContent2: 'B', enableA1: false, enableA2: false, duplicateSlots: false, })) .usingComponents({ child, c1, c2, c3, x, }) .definition({ template: tmpl( ` {{slotContent1}} {{slotContent2}} {{slotContent1}} {{slotContent2}} {{slotContent1}} {{slotContent2}} `, ), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! glassEasel.Element.pretendAttached(parentElem) expect(domHtml(parentElem)).toBe( '', ) expect(ops).toEqual([ [-1, '1:A'], [-1, '1:B'], [-1, '2:A'], [-1, '2:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA1: true }) expect(domHtml(parentElem)).toBe( 'ABABAB', ) expect(ops).toEqual([ [-1, '3:A'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotName2: 'a2' }) expect(domHtml(parentElem)).toBe( 'ABAA', ) expect(ops).toEqual([[-2, '3:B']]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA2: true }) expect(domHtml(parentElem)).toBe( 'ABABABB', ) expect(ops).toEqual([ [-1, '3:B'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA1: false }) expect(domHtml(parentElem)).toBe( 'ABBBB', ) expect(ops).toEqual([[-2, '3:A']]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ duplicateSlots: true }) expect(domHtml(parentElem)).toBe( 'ABBBBABBBB', ) expect(ops).toEqual([ [-1, '1:A'], [-1, '1:B'], [-1, '2:A'], [-1, '2:B'], [-1, '3:B'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotName1: 'a2' }) expect(domHtml(parentElem)).toBe( 'ABABABABABABABAB', ) expect(ops).toEqual([ [-1, '3:A'], [-1, '3:A'], [-1, '3:A'], [-1, '3:A'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA2: false }) expect(domHtml(parentElem)).toBe( '', ) expect(ops).toEqual([ [-2, '3:A'], [-2, '3:B'], [-2, '3:A'], [-2, '3:B'], [-2, '3:A'], [-2, '3:B'], [-2, '3:A'], [-2, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA1: true }) expect(domHtml(parentElem)).toBe( 'ABAB', ) expect(ops).toEqual([]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotName1: 'a1' }) expect(domHtml(parentElem)).toBe( 'ABAAABAA', ) expect(ops).toEqual([ [-1, '3:A'], [-1, '3:A'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotContent1: 'C' }) expect(domHtml(parentElem)).toBe( 'CBCCCBCC', ) expect(ops).toEqual([]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ duplicateSlots: false }) expect(domHtml(parentElem)).toBe( 'CBCC', ) expect(ops).toEqual([ [-2, '1:C'], [-2, '1:B'], [-2, '2:C'], [-2, '2:B'], [-2, '3:C'], ]) matchElementWithDom(parentElem) }) test('nested outside dynamic slots', () => { let ops: Array<[number, string]> = [] const x = componentSpace .define() .property('s', String) .lifetime('attached', function () { ops.push([-1, this.data.s.toString()]) }) .lifetime('detached', function () { ops.push([-2, this.data.s.toString()]) }) .lifetime('moved', function () { ops.push([-3, this.data.s.toString()]) }) .registerComponent() const c1 = componentSpace .define() .property('enableA1', Boolean) .property('enableA2', Boolean) .definition({ template: tmpl( ` `, ), }) .registerComponent() const c2 = componentSpace .define() .options({ multipleSlots: true }) .property('enableA1', Boolean) .property('enableA2', Boolean) .definition({ template: tmpl( ` `, ), }) .registerComponent() const c3 = componentSpace .define() .options({ dynamicSlots: true }) .property('enableA1', Boolean) .property('enableA2', Boolean) .definition({ template: tmpl( ` `, ), }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .property('duplicateSlots', Boolean) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .data(() => ({ slotName1: 'b1', slotContent1: 'A', slotName2: 'b1', slotContent2: 'B', enableA1: false, enableA2: false, childSlotName: 'a1', })) .usingComponents({ child, c1, c2, c3, x, }) .definition({ template: tmpl( ` {{slotContent1}} {{slotContent2}} {{slotContent1}} {{slotContent2}} {{slotContent1}} {{slotContent2}} `, ), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! glassEasel.Element.pretendAttached(parentElem) expect(domHtml(parentElem)).toBe('') expect(ops).toEqual([ [-1, '1:A'], [-1, '1:B'], [-1, '2:A'], [-1, '2:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA1: true }) expect(domHtml(parentElem)).toBe( 'ABABAB', ) expect(ops).toEqual([ [-1, '3:A'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotName2: 'b2' }) expect(domHtml(parentElem)).toBe( 'ABBABBABB', ) expect(ops).toEqual([ [-2, '1:B'], [-1, '1:B'], [-1, '1:B'], [-2, '2:B'], [-1, '2:B'], [-1, '2:B'], [-2, '3:B'], [-1, '3:B'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA2: true }) expect(domHtml(parentElem)).toBe( 'ABBABBABB', ) expect(ops).toEqual([]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ childSlotName: 'a2' }) expect(domHtml(parentElem)).toBe( 'ABBABBABBABB', ) expect(ops).toEqual([ [-2, '3:A'], [-2, '3:B'], [-2, '3:B'], [-1, '3:A'], [-1, '3:B'], [-1, '3:B'], [-1, '3:A'], [-1, '3:B'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotName1: 'b2' }) expect(domHtml(parentElem)).toBe( 'ABABABABABABABAB', ) expect(ops).toEqual([ [-2, '1:A'], [-1, '1:A'], [-1, '1:A'], [-2, '2:A'], [-1, '2:A'], [-1, '2:A'], [-2, '3:A'], [-1, '3:A'], [-1, '3:A'], [-2, '3:A'], [-1, '3:A'], [-1, '3:A'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ slotContent1: 'C' }) expect(domHtml(parentElem)).toBe( 'CBCBCBCBCBCBCBCB', ) expect(ops).toEqual([]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA2: false, childSlotName: 'a1' }) expect(domHtml(parentElem)).toBe( 'CBCBCBCBCBCB', ) expect(ops).toEqual([ [-2, '3:C'], [-2, '3:B'], [-2, '3:C'], [-2, '3:B'], [-2, '3:C'], [-2, '3:B'], [-2, '3:C'], [-2, '3:B'], [-1, '3:C'], [-1, '3:B'], [-1, '3:C'], [-1, '3:B'], ]) matchElementWithDom(parentElem) ops = [] parentElem.setData({ enableA1: false }) expect(domHtml(parentElem)).toBe('') expect(ops).toEqual([ [-2, '3:C'], [-2, '3:B'], [-2, '3:C'], [-2, '3:B'], ]) matchElementWithDom(parentElem) }) test('should support slot props', () => { let updateCount = 0 const subComp = componentSpace .define() .property('propA', Number) .observer('propA', () => { updateCount += 1 }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ a1: { b: [10, 100], }, a2: { b: [20, 200], }, c: 0, })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child, 'x-c': subComp }) .definition({ template: tmpl(` {{ foo.b[bar] }} {{c}} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe( '100200', ) expect(updateCount).toBe(2) matchElementWithDom(parentElem) childElem.replaceDataOnPath(['a1', 'b', 0], 11) childElem.applyDataUpdates() expect(domHtml(parentElem)).toBe( '110200', ) expect(updateCount).toBe(3) matchElementWithDom(parentElem) childElem.replaceDataOnPath(['a1', 'b', 0], 12) childElem.replaceDataOnPath(['a2', 'b', 0], 21) childElem.applyDataUpdates() expect(domHtml(parentElem)).toBe( '120210', ) expect(updateCount).toBe(5) matchElementWithDom(parentElem) childElem.replaceDataOnPath(['a2', 'b', 1], 201) childElem.applyDataUpdates() expect(domHtml(parentElem)).toBe( '120210', ) expect(updateCount).toBe(6) matchElementWithDom(parentElem) childElem.setData({ c: 1 }) expect(domHtml(parentElem)).toBe( '10012011', ) expect(updateCount).toBe(8) matchElementWithDom(parentElem) }) test('should support slot props with wx:for', () => { const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ arr: [1, 2], })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .definition({ template: tmpl(` {{c}} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('12') matchElementWithDom(parentElem) childElem.setData({ arr: [2, 1] }) expect(domHtml(parentElem)).toBe('21') matchElementWithDom(parentElem) childElem.spliceArrayDataOnPath(['arr'], 1, 0, [3, 4]) childElem.spliceArrayDataOnPath(['arr'], 4, 0, [5]) childElem.applyDataUpdates() expect(domHtml(parentElem)).toBe('23415') matchElementWithDom(parentElem) childElem.spliceArrayDataOnPath(['arr'], 0, 1, []) childElem.spliceArrayDataOnPath(['arr'], 2, 1, []) childElem.applyDataUpdates() expect(domHtml(parentElem)).toBe('345') matchElementWithDom(parentElem) }) test('should support slot props of array', () => { const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ c: [123, 456, 789], })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .definition({ template: tmpl(` {{item}} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('123456789') matchElementWithDom(parentElem) childElem.setData({ c: [456] }) expect(domHtml(parentElem)).toBe('456') matchElementWithDom(parentElem) }) test('should support slot props inside wx:for and proper updates', () => { let updateCount = 0 const itemChild = componentSpace .define() .property('p', Number) .observer('p', () => { updateCount += 1 }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ c: 123, })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .options({ writeIdToDOM: false }) .usingComponents({ child, item: itemChild }) .data(() => ({ list: [1], })) .definition({ template: tmpl(` {{ item }} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const childElem = parentElem.getShadowRoot()!.getElementById('a')!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('1') matchElementWithDom(parentElem) expect(updateCount).toBe(1) childElem.setData({ c: 456 }) expect(domHtml(parentElem)).toBe('1') matchElementWithDom(parentElem) expect(updateCount).toBe(1) parentElem.setData({ list: [2] }) expect(domHtml(parentElem)).toBe('2') matchElementWithDom(parentElem) expect(updateCount).toBe(2) childElem.setData({ c: 789 }) expect(domHtml(parentElem)).toBe('2') matchElementWithDom(parentElem) expect(updateCount).toBe(2) }) test('should support slot props with let-var', () => { const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ c: 123, })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .definition({ template: tmpl(` {{d}} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('123') matchElementWithDom(parentElem) childElem.setData({ c: 456 }) expect(domHtml(parentElem)).toBe('456') matchElementWithDom(parentElem) }) test('should support slot props inside let-var and proper updates', () => { let updateCount = 0 const itemChild = componentSpace .define() .template(tmpl(`{{ p }}`)) .property('p', Number) .observer('p', () => { updateCount += 1 }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ c: 123, })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .options({ writeIdToDOM: false }) .usingComponents({ child, item: itemChild }) .data(() => ({ list: [1], })) .definition({ template: tmpl(` `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const childElem = parentElem.getShadowRoot()!.getElementById('a')!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('1') matchElementWithDom(parentElem) expect(updateCount).toBe(1) childElem.setData({ c: 456 }) expect(domHtml(parentElem)).toBe('1') matchElementWithDom(parentElem) expect(updateCount).toBe(1) parentElem.setData({ list: [2] }) expect(domHtml(parentElem)).toBe('2') matchElementWithDom(parentElem) expect(updateCount).toBe(2) childElem.setData({ c: 789 }) expect(domHtml(parentElem)).toBe('2') matchElementWithDom(parentElem) expect(updateCount).toBe(2) }) test('should support nested slot props and proper updates', () => { let updateCount = 0 const itemChild = componentSpace .define() .template(tmpl(`{{ p }}`)) .property('p', Number) .observer('p', () => { updateCount += 1 }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true, virtualHost: true }) .data(() => ({ c: 123, })) .definition({ template: tmpl(''), }) .registerComponent() const parent = componentSpace .define() .options({ writeIdToDOM: false }) .usingComponents({ child, item: itemChild }) .definition({ template: tmpl(` `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ).asInstanceOf(parent)! const outer = parentElem.getShadowRoot()!.getElementById('a')!.asInstanceOf(child)! const inner = parentElem.getShadowRoot()!.getElementById('b')!.asInstanceOf(child)! expect(domHtml(parentElem)).toBe('123') matchElementWithDom(parentElem) expect(updateCount).toBe(1) inner.setData({ c: 456 }) expect(domHtml(parentElem)).toBe('123') matchElementWithDom(parentElem) expect(updateCount).toBe(1) outer.setData({ c: 654 }) expect(domHtml(parentElem)).toBe('654') matchElementWithDom(parentElem) expect(updateCount).toBe(2) inner.setData({ c: 789 }) expect(domHtml(parentElem)).toBe('654') matchElementWithDom(parentElem) expect(updateCount).toBe(2) }) test('should support duplicate slot props', () => { let ops: Array<[number, string]> = [] const itemComp = componentSpace .define() .property('s', String) .observer('s', (s: string) => { ops.push([0, s.toString()]) }) .lifetime('attached', function () { ops.push([-1, this.data.s.toString()]) }) .lifetime('detached', function () { ops.push([-2, this.data.s.toString()]) }) .lifetime('moved', function () { ops.push([-3, this.data.s.toString()]) }) .registerComponent() const child = componentSpace .define() .options({ dynamicSlots: true }) .data(() => ({ items: [] as number[], })) .definition({ template: tmpl( '', ), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ child, 'x-c': itemComp }) .data(() => ({ a: { foo: 'foo', }, bar: undefined as string | undefined, })) .definition({ template: tmpl(` {{index}}:{{item}} `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext('root', parent, testBackend) glassEasel.Element.pretendAttached(parentElem) const childElem = parentElem.getShadowRoot()!.childNodes[1]!.asInstanceOf(child)! parentElem.replaceDataOnPath(['a', 'foo'], 'oops') expect(domHtml(parentElem)).toBe('') expect(ops).toEqual([ [0, 'foo'], [-1, 'foo'], ]) matchElementWithDom(parentElem) ops = [] childElem.setData({ items: [1, 2, 3] }) expect(domHtml(parentElem)).toBe( '0:11:22:3', ) expect(ops).toEqual([ [0, '0:1'], [0, '1:2'], [0, '2:3'], [-1, '0:1'], [-1, '1:2'], [-1, '2:3'], ]) matchElementWithDom(parentElem) ops = [] childElem.setData({ items: [4, 5, 6] }) expect(ops).toEqual([ [-2, '0:1'], [-2, '1:2'], [-2, '2:3'], [0, '0:4'], [0, '1:5'], [0, '2:6'], [-1, '0:4'], [-1, '1:5'], [-1, '2:6'], ]) expect(domHtml(parentElem)).toBe( '0:41:52:6', ) matchElementWithDom(parentElem) ops = [] childElem.setData({ items: [1, 4, 6, 3, 5, 2] }) expect(ops).toEqual([ [0, '0:1'], [-1, '0:1'], [0, '1:4'], [0, '3:3'], [-1, '3:3'], [0, '4:5'], [0, '5:2'], [-1, '5:2'], ]) expect(domHtml(parentElem)).toBe( '0:11:42:63:34:55:2', ) matchElementWithDom(parentElem) ops = [] childElem.setData({ items: [2, 5, 1, 4, 6, 3] }) expect(ops).toEqual([ [0, '0:2'], [0, '1:5'], [0, '2:1'], [0, '3:4'], [0, '4:6'], [0, '5:3'], ]) expect(domHtml(parentElem)).toBe( '0:21:52:13:44:65:3', ) matchElementWithDom(parentElem) ops = [] childElem.setData({ items: [1, 4, 6, 3, 5, 2] }) expect(ops).toEqual([ [0, '0:1'], [0, '1:4'], [0, '2:6'], [0, '3:3'], [0, '4:5'], [0, '5:2'], ]) expect(domHtml(parentElem)).toBe( '0:11:42:63:34:55:2', ) matchElementWithDom(parentElem) ops = [] childElem.spliceArrayDataOnPath(['items'], 2, 1, []) childElem.applyDataUpdates() expect(ops).toEqual([ [-2, '2:6'], [0, '2:3'], [0, '3:5'], [0, '4:2'], ]) expect(domHtml(parentElem)).toBe( '0:11:42:33:54:2', ) matchElementWithDom(parentElem) ops = [] childElem.spliceArrayDataOnPath(['items'], 2, 0, [6]) childElem.applyDataUpdates() expect(ops).toEqual([ [0, '2:6'], [-1, '2:6'], [0, '3:3'], [0, '4:5'], [0, '5:2'], ]) expect(domHtml(parentElem)).toBe( '0:11:42:63:34:55:2', ) matchElementWithDom(parentElem) ops = [] childElem.setData({ items: [] }) expect(ops).toEqual([ [-2, '1:4'], [-2, '4:5'], [-2, '0:1'], [-2, '3:3'], [-2, '5:2'], [-2, '2:6'], ]) expect(domHtml(parentElem)).toBe('') matchElementWithDom(parentElem) ops = [] parentElem.setData({ bar: '1' }) expect(ops).toEqual([[0, 'oops']]) }) test('should support different deep copy strategies', () => { const none = componentSpace .define() .options({ dynamicSlots: true, dataDeepCopy: glassEasel.DeepCopyKind.None, propertyPassingDeepCopy: glassEasel.DeepCopyKind.None, }) .definition({ template: tmpl(` `), }) .data(() => ({ sp: { text: 123, }, })) .registerComponent() const rec = componentSpace .define() .options({ dynamicSlots: true, dataDeepCopy: glassEasel.DeepCopyKind.SimpleWithRecursion, propertyPassingDeepCopy: glassEasel.DeepCopyKind.SimpleWithRecursion, }) .definition({ template: tmpl(` `), }) .data(() => ({ sp: { text: 'abc', }, // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment recObj: {} as any, })) .registerComponent() const parent = componentSpace .define() .usingComponents({ none, rec }) .definition({ template: tmpl(`
{{p.text}}
{{p.text}}
`), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) const noneElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(none)! const recElem = parentElem.getShadowRoot()!.childNodes[1]!.asInstanceOf(rec)! expect(domHtml(parentElem)).toBe('
123
abc
') matchElementWithDom(parentElem) noneElem.setData({ 'sp.text': 456 }) expect(domHtml(parentElem)).toBe('
123
abc
') matchElementWithDom(parentElem) noneElem.setData({ sp: { text: 789 } }) expect(domHtml(parentElem)).toBe('
789
abc
') matchElementWithDom(parentElem) const recObj = {} as { r: any } recObj.r = recObj recElem.setData({ sp: { text: 'def' }, recObj }) expect(domHtml(parentElem)).toBe('
789
def
') matchElementWithDom(parentElem) }) test('should support placeholders', () => { const placeholder = componentSpace .define() .options({ dynamicSlots: true }) .definition({ template: tmpl(`
`), }) .data(() => ({ sp: { text: 123, }, })) .registerComponent() const parent = componentSpace .define() .usingComponents({ impl: 'impl', placeholder, }) .placeholders({ impl: 'placeholder', }) .definition({ template: tmpl(`
{{text}}
`), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) expect(domHtml(parentElem)).toBe('
123
') matchElementWithDom(parentElem) componentSpace .define('impl') .options({ dynamicSlots: true }) .definition({ template: tmpl(` `), }) .data(() => ({ sp: { text: 123, }, })) .registerComponent() expect(domHtml(parentElem)).toBe('
456
') matchElementWithDom(parentElem) }) test('should support slot as slot content', () => { const comp = componentSpace .define() .options({ multipleSlots: true, }) .template( tmpl(`
comp
`), ) .registerComponent() const child = componentSpace .define() .options({ multipleSlots: true, }) .usingComponents({ comp }) .template( tmpl(`
child
`), ) .property('s', String) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .template( tmpl(`
content
`), ) .data(() => ({ s: '', })) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) expect(domHtml(parentElem)).toBe('
comp
') parentElem.setData({ s: 'comp-slot', }) expect(domHtml(parentElem)).toBe( '
comp
content
', ) parentElem.setData({ s: 'invalid', }) expect(domHtml(parentElem)).toBe('
comp
') }) test('should support slot as slot content with virtual host and placeholder', () => { const placeholder = componentSpace .define() .options({ multipleSlots: true, virtualHost: true, }) .template( tmpl(`
placeholder
`), ) .registerComponent() const child = componentSpace .define() .options({ multipleSlots: true, virtualHost: true, }) .usingComponents({ comp: 'comp', placeholder }) .placeholders({ comp: 'placeholder' }) .template( tmpl(`
child
`), ) .registerComponent() const parent = componentSpace .define() .usingComponents({ child }) .template( tmpl(`
content
`), ) .registerComponent() const parentElem = glassEasel.Component.createWithContext( 'root', parent.general(), testBackend, ) expect(domHtml(parentElem)).toBe('
placeholder
content
') componentSpace .define('comp') .options({ multipleSlots: true, }) .template( tmpl(`
comp
`), ) .registerComponent() expect(domHtml(parentElem)).toBe('
comp
content
') }) test('should support splice update', () => { const li = componentSpace.define().property('s', String).registerComponent() const listDef = componentSpace .define() .options({ dynamicSlots: true, }) .property('list', Array) .data(() => ({ displayList: [] as number[], })) .template(tmpl('')) .registerComponent() const listArray = new Array(100).fill(0).map((_, i) => i) const parent = componentSpace .define() .usingComponents({ list: listDef, li }) .data(() => ({ listArray, })) .template( tmpl(`
  • {{item}}
  • `), ) .registerComponent() const root = glassEasel.Component.createWithContext('root', parent, testBackend) const list = root.getShadowRoot()!.childNodes[0] as glassEasel.GeneralComponent expect(domHtml(root)).toEqual('') matchElementWithDom(root) list.setData({ displayList: [0, 1, 2], }) expect(domHtml(root)).toEqual('
  • 0
  • 1
  • 2
  • ') matchElementWithDom(root) list.spliceArrayDataOnPath(['displayList'], undefined, undefined, [3, 4]) list.applyDataUpdates() expect(list.data.displayList).toEqual([0, 1, 2, 3, 4]) expect(domHtml(root)).toEqual( '
  • 0
  • 1
  • 2
  • 3
  • 4
  • ', ) matchElementWithDom(root) list.spliceArrayDataOnPath(['displayList'], 0, 2, []) list.applyDataUpdates() expect(list.data.displayList).toEqual([2, 3, 4]) expect(domHtml(root)).toEqual('
  • 2
  • 3
  • 4
  • ') matchElementWithDom(root) }) }) }) describe('slot utilities', () => { test('get slot element in shadow root', () => { const single = componentSpace .define() .definition({ template: tmpl('
    '), }) .registerComponent() const multi = componentSpace .define() .options({ multipleSlots: true }) .definition({ template: tmpl('
    '), }) .registerComponent() const dynamic = componentSpace .define() .options({ dynamicSlots: true }) .definition({ template: tmpl('
    '), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ single, multi, dynamic }) .definition({ template: tmpl(`
    S
    M
    D `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext('root', parent, testBackend) const singleElem = (parentElem.$.s as glassEasel.Element).asInstanceOf(single)! const multiElem = (parentElem.$.m as glassEasel.Element).asInstanceOf(multi)! const dynamicElem = (parentElem.$.d as glassEasel.Element).asInstanceOf(dynamic)! const singleSlot = (singleElem.$.a as glassEasel.Element).childNodes[0]!.asElement()! const multiSlot = (multiElem.$.a as glassEasel.Element).childNodes[0]!.asElement()! const dynamicSlot = (dynamicElem.$.a as glassEasel.Element).childNodes[0]!.asElement()! expect(parentElem.getShadowRoot()!.getSlotElementFromName('')).toBe(null) expect(singleElem.getShadowRoot()!.getSlotElementFromName('')).toBe(singleSlot) expect(multiElem.getShadowRoot()!.getSlotElementFromName('')).toBe(null) expect(dynamicElem.getShadowRoot()!.getSlotElementFromName('')).toStrictEqual([]) expect(parentElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(null) expect(singleElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(singleSlot) expect(multiElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(multiSlot) expect(dynamicElem.getShadowRoot()!.getSlotElementFromName('a')).toStrictEqual([dynamicSlot]) parentElem.getShadowRoot()!.forEachSlot(() => { throw new Error() }) singleElem.getShadowRoot()!.forEachSlot((slot) => { expect(slot).toBe(singleSlot) }) multiElem.getShadowRoot()!.forEachSlot((slot) => { expect(slot).toBe(multiSlot) }) dynamicElem.getShadowRoot()!.forEachSlot((slot) => { expect(slot).toBe(dynamicSlot) }) const checkSlotContentMethods = ( sr: glassEasel.ShadowRoot, slot: glassEasel.Element, expectedContent: glassEasel.Node[], expectedNode: glassEasel.Node[], ) => { const contentArr = sr.getSlotContentArray(slot)! expect(contentArr?.length).toBe(expectedContent.length) for (let i = 0; i < contentArr?.length; i += 1) { expect(contentArr[i]).toBe(expectedContent[i]) } let cur = 0 sr.forEachNodeInSlot((node, s) => { if (!s) { if (node.asTextNode()) { expect(node.asTextNode()!.textContent).toBe('M') expect(node.asVirtualNode()).toBe(null) } else { expect(node.asVirtualNode()).toBeTruthy() } return } expect(s).toBe(slot) expect(node).toBe(expectedContent[cur]) cur += 1 }) expect(cur).toBe(expectedContent.length) cur = 0 sr.forEachNodeInSpecifiedSlot(slot, (node) => { expect(node).toBe(expectedContent[cur]) cur += 1 }) expect(cur).toBe(expectedContent.length) cur = 0 sr.forEachSlotContentInSlot((node, s) => { if (!s) { if (node.asTextNode()) { expect(node.asTextNode()!.textContent).toBe('M') expect(node.asVirtualNode()).toBe(null) } else { expect(node.asVirtualNode()).toBeTruthy() } return } expect(s).toBe(slot) expect(node).toBe(expectedNode[cur]) cur += 1 }) expect(cur).toBe(expectedNode.length) cur = 0 sr.forEachSlotContentInSpecifiedSlot(slot, (node) => { expect(node).toBe(expectedNode[cur]) cur += 1 }) expect(cur).toBe(expectedNode.length) } checkSlotContentMethods( singleElem.getShadowRoot()!, singleSlot, [ singleElem.childNodes[0]!, (singleElem.childNodes[0] as glassEasel.Element).childNodes[0]!, singleElem.childNodes[1]!, ], [ (singleElem.childNodes[0] as glassEasel.Element).childNodes[0]!, singleElem.childNodes[1]!, ], ) checkSlotContentMethods( multiElem.getShadowRoot()!, multiSlot, [(multiElem.childNodes[0] as glassEasel.Element).childNodes[0]!], [(multiElem.childNodes[0] as glassEasel.Element).childNodes[0]!], ) checkSlotContentMethods( dynamicElem.getShadowRoot()!, dynamicSlot, [ dynamicElem.childNodes[0]!, (dynamicElem.childNodes[0] as glassEasel.Element).childNodes[0]!, dynamicElem.childNodes[1]!, ], [ (dynamicElem.childNodes[0] as glassEasel.Element).childNodes[0]!, dynamicElem.childNodes[1]!, ], ) }) test('fallback to possible event bindings', () => { const callOrder: number[] = [] const single = componentSpace .define() .definition({ template: tmpl('
    '), methods: { fAbc() { callOrder.push(1) }, }, }) .registerComponent() const parent = componentSpace .define() .usingComponents({ single }) .definition({ template: tmpl(`
    `), }) .registerComponent() const parentElem = execWithWarn(1, () => glassEasel.Component.createWithContext('root', parent, testBackend), ) const a = parentElem.getShadowRoot()!.getElementById('a')! a.triggerEvent('aBc', null, { bubbles: true, composed: true }) expect(callOrder).toStrictEqual([1]) }) test('fallback to possible event bindings (template script)', () => { const single = componentSpace .define() .definition({ template: tmpl(` exports.fA = function (ev) { ev.target._test = 123 }
    `), }) .registerComponent() const parent = componentSpace .define() .usingComponents({ single }) .definition({ template: tmpl(`
    `), }) .registerComponent() const parentElem = glassEasel.Component.createWithContext('root', parent, testBackend) const a = parentElem.getShadowRoot()!.getElementById('a')! a.triggerEvent('aBc', null, { bubbles: true, composed: true }) expect((a.getComposedParent() as unknown as { _test: number })._test).toStrictEqual(123) }) }) } describe('slot (DOM backend)', () => testCases(domBackend)) describe('slot (shadow backend)', () => testCases(shadowBackend)) describe('slot (composed backend)', () => testCases(composedBackend))