@joist/element 4.0.0-next.2 → 4.0.0-next.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +110 -25
  2. package/package.json +5 -4
  3. package/src/lib/attr.test.ts +137 -80
  4. package/src/lib/attr.ts +24 -32
  5. package/src/lib/element.test.ts +96 -45
  6. package/src/lib/element.ts +90 -28
  7. package/src/lib/lifecycle.test.ts +31 -0
  8. package/src/lib/lifecycle.ts +9 -0
  9. package/src/lib/listen.test.ts +88 -0
  10. package/src/lib/listen.ts +28 -5
  11. package/src/lib/metadata.ts +18 -6
  12. package/src/lib/query.test.ts +28 -0
  13. package/src/lib/query.ts +31 -0
  14. package/src/lib/result.ts +2 -26
  15. package/src/lib/tags.ts +22 -64
  16. package/src/lib/template.test.ts +123 -0
  17. package/src/lib/template.ts +118 -0
  18. package/src/lib.ts +3 -2
  19. package/target/lib/attr.d.ts +5 -1
  20. package/target/lib/attr.js +19 -24
  21. package/target/lib/attr.js.map +1 -1
  22. package/target/lib/attr.test.js +333 -214
  23. package/target/lib/attr.test.js.map +1 -1
  24. package/target/lib/element.d.ts +15 -5
  25. package/target/lib/element.js +71 -18
  26. package/target/lib/element.js.map +1 -1
  27. package/target/lib/element.test.js +195 -111
  28. package/target/lib/element.test.js.map +1 -1
  29. package/target/lib/lifecycle.d.ts +1 -0
  30. package/target/lib/lifecycle.js +8 -0
  31. package/target/lib/lifecycle.js.map +1 -0
  32. package/target/lib/lifecycle.test.js +48 -0
  33. package/target/lib/lifecycle.test.js.map +1 -0
  34. package/target/lib/listen.d.ts +2 -1
  35. package/target/lib/listen.js +21 -3
  36. package/target/lib/listen.js.map +1 -1
  37. package/target/lib/listen.test.js +159 -0
  38. package/target/lib/listen.test.js.map +1 -0
  39. package/target/lib/metadata.d.ts +16 -6
  40. package/target/lib/metadata.js +3 -2
  41. package/target/lib/metadata.js.map +1 -1
  42. package/target/lib/query.d.ts +9 -0
  43. package/target/lib/query.js +19 -0
  44. package/target/lib/query.js.map +1 -0
  45. package/target/lib/query.test.d.ts +1 -0
  46. package/target/lib/query.test.js +41 -0
  47. package/target/lib/query.test.js.map +1 -0
  48. package/target/lib/result.d.ts +2 -8
  49. package/target/lib/result.js +1 -19
  50. package/target/lib/result.js.map +1 -1
  51. package/target/lib/tags.d.ts +10 -20
  52. package/target/lib/tags.js +17 -29
  53. package/target/lib/tags.js.map +1 -1
  54. package/target/lib/template.d.ts +11 -0
  55. package/target/lib/template.js +87 -0
  56. package/target/lib/template.js.map +1 -0
  57. package/target/lib/template.test.d.ts +1 -0
  58. package/target/lib/template.test.js +91 -0
  59. package/target/lib/template.test.js.map +1 -0
  60. package/target/lib.d.ts +3 -2
  61. package/target/lib.js +3 -2
  62. package/target/lib.js.map +1 -1
  63. package/src/lib/shadow.test.ts +0 -40
  64. package/src/lib/shadow.ts +0 -16
  65. package/src/lib/tags.test.ts +0 -28
  66. package/target/lib/shadow.d.ts +0 -2
  67. package/target/lib/shadow.js +0 -10
  68. package/target/lib/shadow.js.map +0 -1
  69. package/target/lib/shadow.test.js +0 -69
  70. package/target/lib/shadow.test.js.map +0 -1
  71. package/target/lib/tags.test.js +0 -23
  72. package/target/lib/tags.test.js.map +0 -1
  73. /package/target/lib/{shadow.test.d.ts → lifecycle.test.d.ts} +0 -0
  74. /package/target/lib/{tags.test.d.ts → listen.test.d.ts} +0 -0
package/README.md CHANGED
@@ -1,41 +1,126 @@
1
1
  # Element
2
2
 
3
- Create a shadow root and apply styles and html as defined
3
+ Utilities for building web compnennts. Especially targeted at
4
4
 
5
- #### Installation:
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Custom Element](#custom-element)
9
+ - [Attributes](#attributes)
10
+ - [Styles](#styles)
11
+ - [Listeners](#listeners)
12
+ - [Queries](#queries)
13
+
14
+ ## Installation
6
15
 
7
16
  ```BASH
8
- npm i @joist/element
17
+ npm i @joist/element@next
9
18
  ```
10
19
 
11
- #### Example:
20
+ ## Custom Element
12
21
 
13
- ```TS
14
- import { tagName, shadow, css, html, attr, listen, element } from '@joist/element';
22
+ To define a custom element decorate your custom element class and add a tagName
15
23
 
24
+ ```ts
25
+ @element({
26
+ tagName: 'my-element'
27
+ })
28
+ export class MyElement extends HTMLElement {}
29
+ ```
30
+
31
+ ## Attributes
32
+
33
+ Attributes can be managed using the `@attr` decorator. This decorator will read attribute values and and write properties back to attributes;
34
+
35
+ ```ts
16
36
  @element({
17
37
  tagName: 'my-element'
18
38
  })
19
39
  export class MyElement extends HTMLElement {
20
- // apply styles to shadow dom
21
- @shadow styles = css`
22
- :host {
23
- display: block;
24
- color: red;
25
- }
26
- `;
27
-
28
- // apply html to shadow dom
29
- @shadow template = html`
30
- <slot></slot>
31
- `;
32
-
33
- // define attributes
34
- @attr() accessor value = 0;
35
-
36
- // listen for events
37
- @listen('click') onClick() {
38
- console.log('clicked!')
40
+ @attr()
41
+ accessor greeting = 'Hello World';
42
+ }
43
+ ```
44
+
45
+ ## HTML and CSS
46
+
47
+ HTML templates can be applied by passing the result of the `html` tag to the shaodw list.
48
+ CSS can be applied by passing the result of the `css` tag to the shadow list.
49
+
50
+ ```ts
51
+ @element({
52
+ tagName: 'my-element',
53
+ shadow: [
54
+ css`
55
+ h1 {
56
+ color: red;
57
+ }
58
+ `,
59
+ html`<h1>Hello World</h1>`
60
+ ]
61
+ })
62
+ export class MyElement extends HTMLElement {}
63
+ ```
64
+
65
+ ## Listeners
66
+
67
+ The `@listen` decorator allows you to easy setup event listeners. By default the listener will be attached to the shadow root if it exists or the host element if it doesn't. This can be customized by pass a selector function to the decorator
68
+
69
+ ```ts
70
+ @element({
71
+ tagName: 'my-element',
72
+ shadow: []
73
+ })
74
+ export class MyElement extends HTMLElement {
75
+ @listen('eventname')
76
+ onEventName1() {
77
+ // all listener to the shadow root
78
+ }
79
+
80
+ @listen('eventname', (host) => host)
81
+ onEventName2() {
82
+ // all listener to the host element
83
+ }
84
+
85
+ @listen('eventname', (host) => host.querySelector('button'))
86
+ onEventName3() {
87
+ // add listener to a button found in the light dom
88
+ }
89
+
90
+ @listen('eventname', '#test')
91
+ onEventName4() {
92
+ // add listener to element with the id of "test" that is found in the shadow dom
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## Queries
98
+
99
+ The `query` function will query for a particular element and allow you to easily patch that element with new properties.
100
+
101
+ ```ts
102
+ @element({
103
+ tagName: 'my-element',
104
+ shadow: [
105
+ html`
106
+ <label for="my-input">
107
+ <slot></slot>
108
+ </label>
109
+
110
+ <input id="my-input" />
111
+ `
112
+ ]
113
+ })
114
+ export class MyElement extends HTMLElement {
115
+ @observe()
116
+ value: string;
117
+
118
+ #input = query('input');
119
+
120
+ @effect()
121
+ onChange() {
122
+ const input = this.#input();
123
+ input.value = this.value;
39
124
  }
40
125
  }
41
126
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joist/element",
3
- "version": "4.0.0-next.2",
3
+ "version": "4.0.0-next.20",
4
4
  "type": "module",
5
5
  "main": "./target/lib.js",
6
6
  "module": "./target/lib.js",
@@ -9,7 +9,7 @@
9
9
  "import": "./target/lib.js"
10
10
  },
11
11
  "./*": {
12
- "import": "./target/lib/*.js"
12
+ "import": "./target/lib"
13
13
  }
14
14
  },
15
15
  "files": [
@@ -20,7 +20,7 @@
20
20
  "description": "Intelligently apply styles to WebComponents",
21
21
  "repository": {
22
22
  "type": "git",
23
- "url": "git+https://github.com/deebloo/joist.git"
23
+ "url": "git+https://github.com/joist-framework/joist.git"
24
24
  },
25
25
  "keywords": [
26
26
  "TypeScript",
@@ -31,7 +31,7 @@
31
31
  "author": "deebloo",
32
32
  "license": "MIT",
33
33
  "bugs": {
34
- "url": "https://github.com/deebloo/joist/issues"
34
+ "url": "https://github.com/joist-framework/joist/issues"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -57,6 +57,7 @@
57
57
  "test": {
58
58
  "command": "wtr --config wtr.config.mjs",
59
59
  "files": [
60
+ "vitest.config.js",
60
61
  "target/**"
61
62
  ],
62
63
  "output": [],
@@ -1,113 +1,170 @@
1
- import { expect, fixture, html } from '@open-wc/testing';
1
+ import { expect } from 'chai';
2
2
 
3
3
  import { attr } from './attr.js';
4
4
  import { element } from './element.js';
5
5
 
6
- describe('@attr()', () => {
7
- it('should read and parse the correct values', async () => {
8
- @element({
9
- tagName: 'attr-test-1'
10
- })
11
- class MyElement extends HTMLElement {
12
- @attr()
13
- accessor value1 = 100; // no attribute
6
+ it('should read and parse the correct values', () => {
7
+ @element({
8
+ tagName: 'attr-test-1'
9
+ })
10
+ class MyElement extends HTMLElement {
11
+ @attr()
12
+ accessor value1 = 100; // no attribute
14
13
 
15
- @attr()
16
- accessor value2 = 0; // number
14
+ @attr()
15
+ accessor value2 = 0; // number
17
16
 
18
- @attr()
19
- accessor value3 = false; // boolean
17
+ @attr()
18
+ accessor value3 = false; // boolean
20
19
 
21
- @attr()
22
- accessor value4 = 'hello'; // string
23
- }
20
+ @attr()
21
+ accessor value4 = 'hello'; // string
22
+ }
24
23
 
25
- const el = await fixture<MyElement>(
26
- html`<attr-test-1 value2="2" value3 value4="world"></attr-test-1>`
27
- );
24
+ const container = document.createElement('div');
25
+ container.innerHTML = /*html*/ `
26
+ <attr-test-1 value2="2" value3 value4="world"></attr-test-1>
27
+ `;
28
28
 
29
- expect(el.value1).to.equal(100);
30
- expect(el.value2).to.equal(2);
31
- expect(el.value3).to.equal(true);
32
- expect(el.value4).to.equal('world');
33
- });
29
+ document.body.append(container);
34
30
 
35
- it('should not write falsy props to attributes', async () => {
36
- @element({
37
- tagName: 'attr-test-2'
38
- })
39
- class MyElement extends HTMLElement {
40
- @attr()
41
- accessor value1 = undefined;
31
+ const el = document.querySelector('attr-test-1') as MyElement;
42
32
 
43
- @attr()
44
- accessor value2 = null;
33
+ expect(el.value1).to.equal(100);
34
+ expect(el.value2).to.equal(2);
35
+ expect(el.value3).to.equal(true);
36
+ expect(el.value4).to.equal('world');
45
37
 
46
- @attr()
47
- accessor value3 = '';
48
- }
38
+ container.remove();
39
+ });
49
40
 
50
- const el = await fixture<MyElement>(html`<attr-test-2></attr-test-2>`);
41
+ it('should not write falsy props to attributes', async () => {
42
+ @element({
43
+ tagName: 'attr-test-2'
44
+ })
45
+ class MyElement extends HTMLElement {
46
+ @attr()
47
+ accessor value1 = undefined;
51
48
 
52
- expect(el.hasAttribute('value1')).to.be.false;
53
- expect(el.hasAttribute('value2')).to.be.false;
54
- expect(el.hasAttribute('value3')).to.be.false;
55
- });
49
+ @attr()
50
+ accessor value2 = null;
56
51
 
57
- it('should update attributes when props are changed', async () => {
58
- @element({
59
- tagName: 'attr-test-3'
60
- })
61
- class MyElement extends HTMLElement {
62
- @attr()
63
- accessor value1 = 'hello'; // no attribute
52
+ @attr()
53
+ accessor value3 = '';
54
+ }
64
55
 
65
- @attr()
66
- accessor value2 = 0; // number
56
+ const el = new MyElement();
67
57
 
68
- @attr()
69
- accessor value3 = true; // boolean
58
+ expect(el.hasAttribute('value1')).to.be.false;
59
+ expect(el.hasAttribute('value2')).to.be.false;
60
+ expect(el.hasAttribute('value3')).to.be.false;
61
+ });
70
62
 
71
- @attr()
72
- accessor value4 = false; // boolean
73
- }
63
+ it('should update attributes when props are changed', async () => {
64
+ @element({
65
+ tagName: 'attr-test-3'
66
+ })
67
+ class MyElement extends HTMLElement {
68
+ @attr()
69
+ accessor value1 = 'hello'; // no attribute
70
+
71
+ @attr()
72
+ accessor value2 = 0; // number
73
+
74
+ @attr()
75
+ accessor value3 = true; // boolean
76
+
77
+ @attr()
78
+ accessor value4 = false; // boolean
79
+ }
80
+
81
+ const el = new MyElement();
82
+
83
+ el.value1 = 'world';
84
+ el.value2 = 100;
85
+ el.value3 = false;
86
+ el.value4 = true;
87
+
88
+ expect(el.getAttribute('value1')).to.equal('world');
89
+ expect(el.getAttribute('value2')).to.equal('100');
90
+ expect(el.hasAttribute('value3')).to.be.false;
91
+ expect(el.hasAttribute('value4')).to.be.true;
92
+ });
93
+
94
+ it('should normalize attribute names', async () => {
95
+ const value3 = Symbol('Value from SYMBOL');
96
+
97
+ @element({
98
+ tagName: 'attr-test-4'
99
+ })
100
+ class MyElement extends HTMLElement {
101
+ @attr()
102
+ accessor Value1 = 'hello';
103
+
104
+ @attr()
105
+ accessor ['Value 2'] = 0;
106
+
107
+ @attr()
108
+ accessor [value3] = true;
109
+ }
74
110
 
75
- const el = await fixture<MyElement>(html`<attr-test-3></attr-test-3>`);
111
+ const el = new MyElement();
76
112
 
77
- el.value1 = 'world';
78
- el.value2 = 100;
79
- el.value3 = false;
80
- el.value4 = true;
113
+ document.body.append(el);
81
114
 
82
- expect(el.getAttribute('value1')).to.equal('world');
83
- expect(el.getAttribute('value2')).to.equal('100');
84
- expect(el.hasAttribute('value3')).to.be.false;
85
- expect(el.hasAttribute('value4')).to.be.true;
86
- });
115
+ expect([...el.attributes].map((attr) => attr.name)).to.deep.equal([
116
+ 'value1',
117
+ 'value-2',
118
+ 'value-from-symbol'
119
+ ]);
87
120
 
88
- it('should normalize attribute names', async () => {
89
- const value3 = Symbol('Value from SYMBOL');
121
+ el.remove();
122
+ });
123
+
124
+ it('should throw an error for symbols with no description', async () => {
125
+ expect(() => {
126
+ const value = Symbol();
90
127
 
91
128
  @element({
92
129
  tagName: 'attr-test-4'
93
130
  })
94
131
  class MyElement extends HTMLElement {
95
132
  @attr()
96
- accessor Value1 = 'hello';
133
+ accessor [value] = true;
134
+ }
97
135
 
98
- @attr()
99
- accessor ['Value 2'] = 0;
136
+ new MyElement();
137
+ }).to.throw('Cannot handle Symbol property without description');
138
+ });
100
139
 
101
- @attr()
102
- accessor [value3] = true;
103
- }
140
+ it('should not reflect property to attribute', async () => {
141
+ @element({
142
+ tagName: 'attr-test-5'
143
+ })
144
+ class MyElement extends HTMLElement {
145
+ @attr({ reflect: false })
146
+ accessor value = 'foo';
147
+ }
148
+
149
+ const el = new MyElement();
150
+ el.value = 'bar';
151
+
152
+ expect(el.value).to.equal('bar');
153
+
154
+ expect(el.hasAttribute('value')).to.be.false;
155
+ });
156
+
157
+ it('non reflective attributes should still read new attribute values', async () => {
158
+ @element({
159
+ tagName: 'attr-test-6'
160
+ })
161
+ class MyElement extends HTMLElement {
162
+ @attr({ reflect: false })
163
+ accessor value = 'foo';
164
+ }
104
165
 
105
- const el = await fixture<MyElement>(html`<attr-test-4></attr-test-4>`);
166
+ const el = new MyElement();
167
+ el.setAttribute('value', 'bar');
106
168
 
107
- expect([...el.attributes].map((attr) => attr.name)).to.deep.equal([
108
- 'value1',
109
- 'value-2',
110
- 'value-from-symbol'
111
- ]);
112
- });
169
+ expect(el.value).to.equal('bar');
113
170
  });
package/src/lib/attr.ts CHANGED
@@ -1,48 +1,40 @@
1
1
  import { metadataStore } from './metadata.js';
2
2
 
3
- export function attr() {
4
- return <This extends HTMLElement>(
3
+ export interface AttrOpts {
4
+ observed?: boolean;
5
+ reflect?: boolean;
6
+ }
7
+
8
+ export function attr(opts?: AttrOpts) {
9
+ return function attrDecorator<This extends HTMLElement>(
5
10
  { get, set }: ClassAccessorDecoratorTarget<This, unknown>,
6
11
  ctx: ClassAccessorDecoratorContext<This>
7
- ): ClassAccessorDecoratorResult<This, any> => {
12
+ ): ClassAccessorDecoratorResult<This, any> {
8
13
  const attrName = parseAttrName(ctx.name);
9
14
  const meta = metadataStore.read(ctx.metadata);
15
+ const reflect = opts?.reflect ?? true;
10
16
 
11
- meta.attrs.push({ propName: ctx.name, attrName });
17
+ meta.attrs.set(attrName, {
18
+ propName: ctx.name,
19
+ observe: opts?.observed ?? true,
20
+ reflect,
21
+ getPropValue: get,
22
+ setPropValue: set
23
+ });
12
24
 
13
25
  return {
14
26
  set(value: unknown) {
15
- if (value === true) {
16
- this.setAttribute(attrName, '');
17
- } else if (value === false) {
18
- this.removeAttribute(attrName);
19
- } else {
20
- this.setAttribute(attrName, String(value));
21
- }
22
-
23
- set.call(this, value);
24
- },
25
- get() {
26
- const ogValue = get.call(this);
27
- const attr = this.getAttribute(attrName);
28
-
29
- if (attr !== null) {
30
- // treat as boolean
31
- if (attr === '') {
32
- return true;
33
- }
34
-
35
- // treat as number
36
- if (typeof ogValue === 'number') {
37
- return Number(attr);
27
+ if (reflect) {
28
+ if (value === true) {
29
+ this.setAttribute(attrName, '');
30
+ } else if (value === false) {
31
+ this.removeAttribute(attrName);
32
+ } else {
33
+ this.setAttribute(attrName, String(value));
38
34
  }
39
-
40
- // treat as string
41
- return attr;
42
35
  }
43
36
 
44
- // no readable value return original
45
- return ogValue;
37
+ set.call(this, value);
46
38
  }
47
39
  };
48
40
  };
@@ -1,50 +1,101 @@
1
- import { expect, fixture, html } from '@open-wc/testing';
1
+ import { expect } from 'chai';
2
2
 
3
3
  import { attr } from './attr.js';
4
4
  import { element } from './element.js';
5
+ import { css, html } from './tags.js';
5
6
 
6
- describe('@element()', () => {
7
- it('should write default value to attribute', async () => {
8
- @element({
9
- tagName: 'element-1'
10
- })
11
- class MyElement extends HTMLElement {
12
- @attr()
13
- accessor value1 = 'hello'; // no attribute
14
-
15
- @attr()
16
- accessor value2 = 0; // number
17
-
18
- @attr()
19
- accessor value3 = true; // boolean
20
- }
21
-
22
- const el = await fixture<MyElement>(html`<element-1></element-1>`);
23
-
24
- expect(el.getAttribute('value1')).to.equal('hello');
25
- expect(el.getAttribute('value2')).to.equal('0');
26
- expect(el.getAttribute('value3')).to.equal('');
27
- });
28
-
29
- it('should register attributes', async () => {
30
- @element({
31
- tagName: 'element-2'
32
- })
33
- class MyElement extends HTMLElement {
34
- @attr()
35
- accessor value1 = 'hello'; // no attribute
36
-
37
- @attr()
38
- accessor value2 = 0; // number
39
-
40
- @attr()
41
- accessor value3 = true; // boolean
42
- }
43
-
44
- expect(Reflect.get(MyElement, 'observedAttributes')).to.deep.equal([
45
- 'value1',
46
- 'value2',
47
- 'value3'
48
- ]);
49
- });
7
+ it('should write default value to attribute', async () => {
8
+ @element({
9
+ tagName: 'element-1'
10
+ })
11
+ class MyElement extends HTMLElement {
12
+ @attr()
13
+ accessor value1 = 'hello'; // no attribute
14
+
15
+ @attr()
16
+ accessor value2 = 0; // number
17
+
18
+ @attr()
19
+ accessor value3 = true; // boolean
20
+
21
+ @attr({ reflect: false })
22
+ accessor value4 = 'foo';
23
+ }
24
+
25
+ const el = new MyElement();
26
+
27
+ document.body.append(el);
28
+
29
+ expect(el.getAttribute('value1')).to.equal('hello');
30
+ expect(el.getAttribute('value2')).to.equal('0');
31
+ expect(el.getAttribute('value3')).to.equal('');
32
+ expect(el.getAttribute('value4')).to.equal(null);
33
+
34
+ el.remove();
35
+ });
36
+
37
+ it('should register attributes', async () => {
38
+ @element({
39
+ tagName: 'element-2'
40
+ })
41
+ class MyElement extends HTMLElement {
42
+ @attr()
43
+ accessor value1 = 'hello'; // no attribute
44
+
45
+ @attr()
46
+ accessor value2 = 0; // number
47
+
48
+ @attr()
49
+ accessor value3 = true; // boolean
50
+
51
+ @attr({ observed: false }) // should be filtered out
52
+ accessor value4 = 'hello world';
53
+ }
54
+
55
+ expect(Reflect.get(MyElement, 'observedAttributes')).to.deep.equal([
56
+ 'value1',
57
+ 'value2',
58
+ 'value3'
59
+ ]);
60
+ });
61
+
62
+ it('should attach shadow root when the shadow property exists', async () => {
63
+ @element({
64
+ tagName: 'element-3',
65
+ shadow: []
66
+ })
67
+ class MyElement extends HTMLElement {}
68
+
69
+ const el = new MyElement();
70
+
71
+ expect(el.shadowRoot).to.be.instanceOf(ShadowRoot);
72
+ });
73
+
74
+ it('should apply html and css', async () => {
75
+ @element({
76
+ tagName: 'element-4',
77
+ shadow: [
78
+ css`
79
+ :host {
80
+ display: contents;
81
+ }
82
+ `,
83
+ html`<slot></slot>`,
84
+ {
85
+ apply(el) {
86
+ const div = document.createElement('div');
87
+ div.innerHTML = 'hello world';
88
+
89
+ el.append(div);
90
+ }
91
+ }
92
+ ]
93
+ })
94
+ class MyElement extends HTMLElement {}
95
+
96
+ const el = new MyElement();
97
+
98
+ expect(el.shadowRoot!.adoptedStyleSheets.length).to.equal(1);
99
+ expect(el.shadowRoot!.innerHTML).to.equal(`<slot></slot>`);
100
+ expect(el.innerHTML).to.equal(`<div>hello world</div>`);
50
101
  });