a post-component library for building user-interfaces on the web.
1import { html } from 'dhtml'
2import { assert, assert_deep_eq, assert_eq, test } from '../../../scripts/test/test.ts'
3import { setup } from './setup.ts'
4
5test('regular attributes', () => {
6 const { root, el } = setup()
7
8 root.render(html`<h1 style=${'color: red'}>Hello, world!</h1>`)
9 assert_eq(el.querySelector('h1')!.getAttribute('style'), 'color: red;')
10})
11
12test('can toggle attributes', () => {
13 const { root, el } = setup()
14
15 const template = (hidden: unknown) => html`<h1 data-hidden=${hidden}>Hello, world!</h1>`
16
17 root.render(template(false))
18 assert(!el.querySelector('h1')!.hasAttribute('data-hidden'))
19
20 root.render(template(true))
21 assert(el.querySelector('h1')!.hasAttribute('data-hidden'))
22
23 root.render(template(null))
24 assert(!el.querySelector('h1')!.hasAttribute('data-hidden'))
25})
26
27test('supports property attributes', () => {
28 const { root, el } = setup()
29
30 root.render(html`<details open=${true}></details>`)
31 assert(el.querySelector('details')!.open)
32
33 root.render(html`<details open=${false}></details>`)
34 assert(!el.querySelector('details')!.open)
35})
36
37test('maintains the case of properties', () => {
38 const { root, el } = setup()
39
40 const innerHTML = '<h1>Hello, world!</h1>'
41
42 root.render(html`<div innerhtml=${innerHTML}></div>`)
43 // @ts-expect-error -- no such property
44 assert_eq(el.querySelector('div')!.innerhtml, innerHTML)
45
46 root.render(html`<span innerHTML=${innerHTML}></span>`)
47 assert_eq(el.querySelector('span')!.innerHTML, innerHTML)
48})
49
50test('does not maintain the case of attributes', () => {
51 const { root, el } = setup()
52
53 root.render(html`<div theThing="hello"></div>`)
54 assert_deep_eq(el.querySelector('div')!.getAttributeNames(), ['thething'])
55})
56
57test('treats class/for specially', () => {
58 const { root, el } = setup()
59
60 root.render(html`<h1 class=${'foo'}>Hello, world!</h1>`)
61 assert_eq(el.querySelector('h1')!.className, 'foo')
62
63 root.render(html`<label for=${'foo'}>Hello, world!</label>`)
64 assert_eq(el.querySelector('label')!.htmlFor, 'foo')
65})
66
67test('handles data attributes', () => {
68 const { root, el } = setup()
69
70 root.render(html`<h1 data-foo=${'bar'}>Hello, world!</h1>`)
71 assert_eq(el.querySelector('h1')!.dataset.foo, 'bar')
72})
73
74test('supports events', () => {
75 const { root, el } = setup()
76
77 let clicks = 0
78 root.render(html`
79 <button
80 onclick=${() => {
81 clicks++
82 }}
83 >
84 Click me
85 </button>
86 `)
87
88 assert_eq(clicks, 0)
89 el.querySelector('button')!.click()
90 assert_eq(clicks, 1)
91 el.querySelector('button')!.click()
92 assert_eq(clicks, 2)
93})
94
95test('supports event handlers that change', () => {
96 const { root, el } = setup()
97
98 const template = (handler: ((event: Event) => void) | null) => html`<input onblur=${handler}>Click me</input>`
99
100 const calls: Event[] = []
101 root.render(template(event => calls.push(event)))
102 assert_eq(calls.length, 0)
103
104 const event = new Event('blur')
105 el.querySelector('input')!.dispatchEvent(event)
106 assert_eq(calls.length, 1)
107 assert_eq(calls[0], event)
108
109 root.render(template(null))
110 el.querySelector('input')!.dispatchEvent(new Event('blur'))
111 assert_eq(calls.length, 1)
112})