move all functions to their own files

Co-authored-by: Kristján Oddsson <koddsson@gmail.com>
This commit is contained in:
Keith Cirkel 2022-03-23 10:19:16 +00:00 коммит произвёл GitHub
Родитель 2237f9047b
Коммит 7455cb0486
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 381 добавлений и 361 удалений

10
src/document-fragment.ts Normal file
Просмотреть файл

@ -0,0 +1,10 @@
import {NodeTemplatePart} from '@github/template-parts'
import type {TemplatePart} from '@github/template-parts'
export function processDocumentFragment(part: TemplatePart, value: unknown): boolean {
if (value instanceof DocumentFragment && part instanceof NodeTemplatePart) {
if (value.childNodes.length) part.replace(...value.childNodes)
return true
}
return false
}

Просмотреть файл

@ -1,111 +1,6 @@
import {
TemplateInstance,
NodeTemplatePart,
createProcessor,
processPropertyIdentity,
processBooleanAttribute
} from '@github/template-parts'
import {processDirective} from './directive.js'
import {processEvent} from './events.js'
import type {TemplatePart, TemplateTypeInit} from '@github/template-parts'
import {processor} from './processor.js'
import {TemplateResult} from './template-result.js'
function processSubTemplate(part: TemplatePart, value: unknown): boolean {
if (value instanceof TemplateResult && part instanceof NodeTemplatePart) {
value.renderInto(part)
return true
}
return false
}
function processDocumentFragment(part: TemplatePart, value: unknown): boolean {
if (value instanceof DocumentFragment && part instanceof NodeTemplatePart) {
if (value.childNodes.length) part.replace(...value.childNodes)
return true
}
return false
}
function isIterable(value: unknown): value is Iterable<unknown> {
return typeof value === 'object' && Symbol.iterator in ((value as unknown) as Record<symbol, unknown>)
}
function processIterable(part: TemplatePart, value: unknown): boolean {
if (!isIterable(value)) return false
if (part instanceof NodeTemplatePart) {
const nodes = []
for (const item of value) {
if (item instanceof TemplateResult) {
const fragment = document.createDocumentFragment()
item.renderInto(fragment)
nodes.push(...fragment.childNodes)
} else if (item instanceof DocumentFragment) {
nodes.push(...item.childNodes)
} else {
nodes.push(String(item))
}
}
if (nodes.length) part.replace(...nodes)
return true
} else {
part.value = Array.from(value).join(' ')
return true
}
}
export function processPart(part: TemplatePart, value: unknown): void {
processDirective(part, value) ||
processBooleanAttribute(part, value) ||
processEvent(part, value) ||
processSubTemplate(part, value) ||
processDocumentFragment(part, value) ||
processIterable(part, value) ||
processPropertyIdentity(part, value)
}
const templates = new WeakMap<TemplateStringsArray, HTMLTemplateElement>()
const renderedTemplates = new WeakMap<Node | NodeTemplatePart, HTMLTemplateElement>()
const renderedTemplateInstances = new WeakMap<Node | NodeTemplatePart, TemplateInstance>()
export class TemplateResult {
constructor(
public readonly strings: TemplateStringsArray,
public readonly values: unknown[],
public readonly processor: TemplateTypeInit
) {}
get template(): HTMLTemplateElement {
if (templates.has(this.strings)) {
return templates.get(this.strings)!
} else {
const template = document.createElement('template')
const end = this.strings.length - 1
template.innerHTML = this.strings.reduce((str, cur, i) => str + cur + (i < end ? `{{ ${i} }}` : ''), '')
templates.set(this.strings, template)
return template
}
}
renderInto(element: Node | NodeTemplatePart): void {
const template = this.template
if (renderedTemplates.get(element) !== template) {
renderedTemplates.set(element, template)
const instance = new TemplateInstance(template, this.values, this.processor)
renderedTemplateInstances.set(element, instance)
if (element instanceof NodeTemplatePart) {
element.replace(...instance.children)
} else {
element.appendChild(instance)
}
return
}
renderedTemplateInstances.get(element)!.update((this.values as unknown) as Record<string, unknown>)
}
}
const defaultProcessor = createProcessor(processPart)
export function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult {
return new TemplateResult(strings, values, defaultProcessor)
}
export function render(result: TemplateResult, element: Node | NodeTemplatePart): void {
result.renderInto(element)
return new TemplateResult(strings, values, processor)
}

Просмотреть файл

@ -1,4 +1,7 @@
export {TemplateResult, html, render} from './html.js'
export {TemplateResult} from './template-result.js'
export {render} from './render.js'
export {processor} from './processor.js'
export {html} from './html.js'
export {isDirective, directive} from './directive.js'
export {until} from './until.js'
export {unsafeHTML} from './unsafe-html.js'

30
src/iterable.ts Normal file
Просмотреть файл

@ -0,0 +1,30 @@
import {TemplateResult} from './template-result.js'
import {NodeTemplatePart} from '@github/template-parts'
import type {TemplatePart} from '@github/template-parts'
function isIterable(value: unknown): value is Iterable<unknown> {
return typeof value === 'object' && Symbol.iterator in ((value as unknown) as Record<symbol, unknown>)
}
export function processIterable(part: TemplatePart, value: unknown): boolean {
if (!isIterable(value)) return false
if (part instanceof NodeTemplatePart) {
const nodes = []
for (const item of value) {
if (item instanceof TemplateResult) {
const fragment = document.createDocumentFragment()
item.renderInto(fragment)
nodes.push(...fragment.childNodes)
} else if (item instanceof DocumentFragment) {
nodes.push(...item.childNodes)
} else {
nodes.push(String(item))
}
}
if (nodes.length) part.replace(...nodes)
return true
} else {
part.value = Array.from(value).join(' ')
return true
}
}

20
src/processor.ts Normal file
Просмотреть файл

@ -0,0 +1,20 @@
import {createProcessor, processPropertyIdentity, processBooleanAttribute} from '@github/template-parts'
import type {TemplatePart} from '@github/template-parts'
import {processDirective} from './directive.js'
import {processEvent} from './events.js'
import {processIterable} from './iterable.js'
import {processDocumentFragment} from './document-fragment.js'
import {processSubTemplate} from './sub-template.js'
export function processPart(part: TemplatePart, value: unknown): void {
processDirective(part, value) ||
processBooleanAttribute(part, value) ||
processEvent(part, value) ||
processSubTemplate(part, value) ||
processDocumentFragment(part, value) ||
processIterable(part, value) ||
processPropertyIdentity(part, value)
}
export const processor = createProcessor(processPart)

7
src/render.ts Normal file
Просмотреть файл

@ -0,0 +1,7 @@
import type {NodeTemplatePart} from '@github/template-parts'
import {TemplateResult} from './template-result.js'
export function render(result: TemplateResult, element: Node | NodeTemplatePart): void {
result.renderInto(element)
}

12
src/sub-template.ts Normal file
Просмотреть файл

@ -0,0 +1,12 @@
import {NodeTemplatePart} from '@github/template-parts'
import type {TemplatePart} from '@github/template-parts'
import {TemplateResult} from './template-result.js'
export function processSubTemplate(part: TemplatePart, value: unknown): boolean {
if (value instanceof TemplateResult && part instanceof NodeTemplatePart) {
value.renderInto(part)
return true
}
return false
}

41
src/template-result.ts Normal file
Просмотреть файл

@ -0,0 +1,41 @@
import {TemplateInstance, NodeTemplatePart} from '@github/template-parts'
import type {TemplateTypeInit} from '@github/template-parts'
const templates = new WeakMap<TemplateStringsArray, HTMLTemplateElement>()
const renderedTemplates = new WeakMap<Node | NodeTemplatePart, HTMLTemplateElement>()
const renderedTemplateInstances = new WeakMap<Node | NodeTemplatePart, TemplateInstance>()
export class TemplateResult {
constructor(
public readonly strings: TemplateStringsArray,
public readonly values: unknown[],
public readonly processor: TemplateTypeInit
) {}
get template(): HTMLTemplateElement {
if (templates.has(this.strings)) {
return templates.get(this.strings)!
} else {
const template = document.createElement('template')
const end = this.strings.length - 1
template.innerHTML = this.strings.reduce((str, cur, i) => str + cur + (i < end ? `{{ ${i} }}` : ''), '')
templates.set(this.strings, template)
return template
}
}
renderInto(element: Node | NodeTemplatePart): void {
const template = this.template
if (renderedTemplates.get(element) !== template) {
renderedTemplates.set(element, template)
const instance = new TemplateInstance(template, this.values, this.processor)
renderedTemplateInstances.set(element, instance)
if (element instanceof NodeTemplatePart) {
element.replace(...instance.children)
} else {
element.appendChild(instance)
}
return
}
renderedTemplateInstances.get(element)!.update((this.values as unknown) as Record<string, unknown>)
}
}

Просмотреть файл

@ -1,7 +1,8 @@
import {processPart} from './html.js'
import {directive} from './directive.js'
import type {TemplatePart} from '@github/template-parts'
import {processPart} from './processor.js'
import {directive} from './directive.js'
const untils: WeakMap<TemplatePart, {i: number}> = new WeakMap()
export const until = directive((...promises: unknown[]) => (part: TemplatePart) => {
if (!untils.has(part)) untils.set(part, {i: promises.length})

Просмотреть файл

@ -1,4 +1,4 @@
import {html, render, directive} from '../lib/index.js'
import {html} from '../lib/index.js'
describe('html', () => {
it('creates new TemplateResults with each call', () => {
@ -9,252 +9,3 @@ describe('html', () => {
expect(other()).to.not.equal(other())
})
})
describe('render', () => {
let surface
beforeEach(() => {
surface = document.createElement('section')
})
it('calls `createCallback` on first render', () => {
const main = (x = null) => html`<div class="${x}"></div>`
let called = false
const instance = main()
instance.processor = {
processCallback() {
throw new Error('Expected processCallback to not be called')
},
createCallback() {
called = true
}
}
instance.renderInto(surface)
expect(called).to.equal(true)
})
it('memoizes by TemplateResult#template, updating old templates with new values', () => {
const main = (x = null) => html`<div class="${x}"></div>`
render(main('foo'), surface)
expect(surface.innerHTML).to.equal('<div class="foo"></div>')
render(main('bar'), surface)
expect(surface.innerHTML).to.equal('<div class="bar"></div>')
})
describe('nesting', () => {
it('supports nested html calls', () => {
const main = child => html`<div>${child}</div>`
const child = message => html`<span>${message}</span>`
render(main(child('Hello')), surface)
expect(surface.innerHTML).to.equal('<div><span>Hello</span></div>')
})
it('updates nodes from repeat calls', () => {
const main = child => html`<div>${child}</div>`
const child = message => html`<span>${message}</span>`
render(main(child('Hello')), surface)
expect(surface.innerHTML).to.equal('<div><span>Hello</span></div>')
render(main(child('Goodbye')), surface)
expect(surface.innerHTML).to.equal('<div><span>Goodbye</span></div>')
})
it('can nest document fragments and text nodes', () => {
const main = frag => html`<span>${frag}</span>`
const fragment = document.createDocumentFragment()
fragment.append(new Text('Hello World'))
render(main(fragment), surface)
expect(surface.innerHTML).to.equal('<span>Hello World</span>')
fragment.append(document.createTextNode('Hello Universe!'))
render(main(fragment), surface)
expect(surface.innerHTML).to.equal('<span>Hello Universe!</span>')
})
it('renders DocumentFragments nested in sub templates nested in arrays', () => {
const sub = () => {
const frag = document.createDocumentFragment()
frag.appendChild(document.createElement('div'))
return html`<span>${frag}</span>`
}
const main = () => html`<div>${[sub(), sub()]}</div>`
render(main(), surface)
expect(surface.innerHTML).to.contain('<div><span><div></div></span><span><div></div></span></div>')
})
})
describe('iterables', () => {
it('supports arrays of strings in nodes', () => {
const main = list => html`<div>${list}</div>`
render(main(['one', 'two', 'three']), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
render(main(['four', 'five', 'six']), surface)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports iterables of Sub Templates with text nodes', () => {
const main = list => html`<div>${list}</div>`
let fragments = ['one', 'two', 'three'].map(text => html`${text}`)
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
fragments = ['four', 'five', 'six'].map(text => html`${text}`)
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports iterables of fragments with text nodes', () => {
const main = list => html`<div>${list}</div>`
let fragments = ['one', 'two', 'three'].map(text => {
const fragment = document.createDocumentFragment()
fragment.append(new Text(text))
return fragment
})
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
fragments = ['four', 'five', 'six'].map(text => {
const fragment = document.createDocumentFragment()
fragment.append(new Text(text))
return fragment
})
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports other strings iterables in nodes', () => {
const main = list => html`<div>${list}</div>`
render(main(new Set(['one', 'two', 'three'])), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
render(
main(
new Map([
[4, 'four'],
[5, 'five'],
[6, 'six']
]).values()
),
surface
)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports iterables of strings in attributes', () => {
const main = list => html`<div class="${list}"></div>`
render(main(['one', 'two', 'three']), surface)
expect(surface.innerHTML).to.equal('<div class="one two three"></div>')
render(main(new Set(['four', 'five', 'six'])), surface)
expect(surface.innerHTML).to.equal('<div class="four five six"></div>')
})
it('supports nested iterables of document fragments', () => {
// prettier-ignore
const main = list => html`<ul>${list}</ul>`
render(
main(
['One', 'Two'].map(text => {
const f = document.createDocumentFragment()
const li = document.createElement('li')
li.textContent = text
f.append(li)
return f
})
),
surface
)
expect(surface.innerHTML).to.equal('<ul><li>One</li><li>Two</li></ul>')
})
it('supports nested iterables of templates', () => {
const child = item => html`<li>${item.name}</li>`
// prettier-ignore
const main = list => html`<ul>${list.map(child)}</ul>`
render(main([{name: 'One'}, {name: 'Two'}, {name: 'Three'}]), surface)
expect(surface.innerHTML).to.equal('<ul><li>One</li><li>Two</li><li>Three</li></ul>')
render(main([{name: 'Two'}, {name: 'Three'}, {name: 'Four'}]), surface)
expect(surface.innerHTML).to.equal('<ul><li>Two</li><li>Three</li><li>Four</li></ul>')
})
})
describe('directives', () => {
it('handles directives differently', () => {
const setAsFoo = directive(() => part => {
part.value = 'foo'
})
const main = () => html`<div class="${setAsFoo()}"></div>`
render(main(), surface)
expect(surface.innerHTML).to.equal('<div class="foo"></div>')
})
})
describe('event listeners', () => {
it('handles event listeners properly', () => {
let i = 0
const main = () => html`<div onclick="${() => (i += 1)}"></div>`
render(main(), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(i).to.equal(2)
})
it('does not rebind event listeners multiple times', () => {
let i = 0
const main = () => html`<div onclick="${() => (i += 1)}"></div>`
render(main(), surface)
render(main(), surface)
render(main(), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(i).to.equal(2)
})
it('allows events to be driven by params', () => {
let i = 0
const main = amt => html`<div onclick="${() => (i += amt)}"></div>`
render(main(1), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
render(main(4), surface)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(i).to.equal(5)
})
it('will unbind event listeners by passing null', () => {
let i = 0
const main = listener => html`<div onclick="${listener}"></div>`
render(
main(() => (i += 1)),
surface
)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
render(main(null), surface)
surface.children[0].click()
surface.children[0].click()
surface.children[0].click()
expect(i).to.equal(1)
})
it('binds event handler objects', () => {
const handler = {
i: 0,
handleEvent() {
this.i += 1
}
}
const main = () => html`<div onclick="${handler}"></div>`
render(main(), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(handler.i).to.equal(0)
surface.children[0].click()
expect(handler.i).to.equal(1)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(handler.i).to.equal(2)
})
})
})

250
test/render.ts Normal file
Просмотреть файл

@ -0,0 +1,250 @@
import {html, render, directive} from '../lib/index.js'
describe('render', () => {
let surface
beforeEach(() => {
surface = document.createElement('section')
})
it('calls `createCallback` on first render', () => {
const main = (x = null) => html`<div class="${x}"></div>`
let called = false
const instance = main()
instance.processor = {
processCallback() {
throw new Error('Expected processCallback to not be called')
},
createCallback() {
called = true
}
}
instance.renderInto(surface)
expect(called).to.equal(true)
})
it('memoizes by TemplateResult#template, updating old templates with new values', () => {
const main = (x = null) => html`<div class="${x}"></div>`
render(main('foo'), surface)
expect(surface.innerHTML).to.equal('<div class="foo"></div>')
render(main('bar'), surface)
expect(surface.innerHTML).to.equal('<div class="bar"></div>')
})
describe('nesting', () => {
it('supports nested html calls', () => {
const main = child => html`<div>${child}</div>`
const child = message => html`<span>${message}</span>`
render(main(child('Hello')), surface)
expect(surface.innerHTML).to.equal('<div><span>Hello</span></div>')
})
it('updates nodes from repeat calls', () => {
const main = child => html`<div>${child}</div>`
const child = message => html`<span>${message}</span>`
render(main(child('Hello')), surface)
expect(surface.innerHTML).to.equal('<div><span>Hello</span></div>')
render(main(child('Goodbye')), surface)
expect(surface.innerHTML).to.equal('<div><span>Goodbye</span></div>')
})
it('can nest document fragments and text nodes', () => {
const main = frag => html`<span>${frag}</span>`
const fragment = document.createDocumentFragment()
fragment.append(new Text('Hello World'))
render(main(fragment), surface)
expect(surface.innerHTML).to.equal('<span>Hello World</span>')
fragment.append(document.createTextNode('Hello Universe!'))
render(main(fragment), surface)
expect(surface.innerHTML).to.equal('<span>Hello Universe!</span>')
})
it('renders DocumentFragments nested in sub templates nested in arrays', () => {
const sub = () => {
const frag = document.createDocumentFragment()
frag.appendChild(document.createElement('div'))
return html`<span>${frag}</span>`
}
const main = () => html`<div>${[sub(), sub()]}</div>`
render(main(), surface)
expect(surface.innerHTML).to.contain('<div><span><div></div></span><span><div></div></span></div>')
})
})
describe('iterables', () => {
it('supports arrays of strings in nodes', () => {
const main = list => html`<div>${list}</div>`
render(main(['one', 'two', 'three']), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
render(main(['four', 'five', 'six']), surface)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports iterables of Sub Templates with text nodes', () => {
const main = list => html`<div>${list}</div>`
let fragments = ['one', 'two', 'three'].map(text => html`${text}`)
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
fragments = ['four', 'five', 'six'].map(text => html`${text}`)
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports iterables of fragments with text nodes', () => {
const main = list => html`<div>${list}</div>`
let fragments = ['one', 'two', 'three'].map(text => {
const fragment = document.createDocumentFragment()
fragment.append(new Text(text))
return fragment
})
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
fragments = ['four', 'five', 'six'].map(text => {
const fragment = document.createDocumentFragment()
fragment.append(new Text(text))
return fragment
})
render(main(fragments), surface)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports other strings iterables in nodes', () => {
const main = list => html`<div>${list}</div>`
render(main(new Set(['one', 'two', 'three'])), surface)
expect(surface.innerHTML).to.equal('<div>onetwothree</div>')
render(
main(
new Map([
[4, 'four'],
[5, 'five'],
[6, 'six']
]).values()
),
surface
)
expect(surface.innerHTML).to.equal('<div>fourfivesix</div>')
})
it('supports iterables of strings in attributes', () => {
const main = list => html`<div class="${list}"></div>`
render(main(['one', 'two', 'three']), surface)
expect(surface.innerHTML).to.equal('<div class="one two three"></div>')
render(main(new Set(['four', 'five', 'six'])), surface)
expect(surface.innerHTML).to.equal('<div class="four five six"></div>')
})
it('supports nested iterables of document fragments', () => {
// prettier-ignore
const main = list => html`<ul>${list}</ul>`
render(
main(
['One', 'Two'].map(text => {
const f = document.createDocumentFragment()
const li = document.createElement('li')
li.textContent = text
f.append(li)
return f
})
),
surface
)
expect(surface.innerHTML).to.equal('<ul><li>One</li><li>Two</li></ul>')
})
it('supports nested iterables of templates', () => {
const child = item => html`<li>${item.name}</li>`
// prettier-ignore
const main = list => html`<ul>${list.map(child)}</ul>`
render(main([{name: 'One'}, {name: 'Two'}, {name: 'Three'}]), surface)
expect(surface.innerHTML).to.equal('<ul><li>One</li><li>Two</li><li>Three</li></ul>')
render(main([{name: 'Two'}, {name: 'Three'}, {name: 'Four'}]), surface)
expect(surface.innerHTML).to.equal('<ul><li>Two</li><li>Three</li><li>Four</li></ul>')
})
})
describe('directives', () => {
it('handles directives differently', () => {
const setAsFoo = directive(() => part => {
part.value = 'foo'
})
const main = () => html`<div class="${setAsFoo()}"></div>`
render(main(), surface)
expect(surface.innerHTML).to.equal('<div class="foo"></div>')
})
})
describe('event listeners', () => {
it('handles event listeners properly', () => {
let i = 0
const main = () => html`<div onclick="${() => (i += 1)}"></div>`
render(main(), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(i).to.equal(2)
})
it('does not rebind event listeners multiple times', () => {
let i = 0
const main = () => html`<div onclick="${() => (i += 1)}"></div>`
render(main(), surface)
render(main(), surface)
render(main(), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(i).to.equal(2)
})
it('allows events to be driven by params', () => {
let i = 0
const main = amt => html`<div onclick="${() => (i += amt)}"></div>`
render(main(1), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
render(main(4), surface)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(i).to.equal(5)
})
it('will unbind event listeners by passing null', () => {
let i = 0
const main = listener => html`<div onclick="${listener}"></div>`
render(
main(() => (i += 1)),
surface
)
expect(surface.innerHTML).to.equal('<div></div>')
expect(i).to.equal(0)
surface.children[0].click()
expect(i).to.equal(1)
render(main(null), surface)
surface.children[0].click()
surface.children[0].click()
surface.children[0].click()
expect(i).to.equal(1)
})
it('binds event handler objects', () => {
const handler = {
i: 0,
handleEvent() {
this.i += 1
}
}
const main = () => html`<div onclick="${handler}"></div>`
render(main(), surface)
expect(surface.innerHTML).to.equal('<div></div>')
expect(handler.i).to.equal(0)
surface.children[0].click()
expect(handler.i).to.equal(1)
surface.children[0].dispatchEvent(new CustomEvent('click'))
expect(handler.i).to.equal(2)
})
})
})