custom-elements-ts 0.0.16 → 0.1.0
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.
- package/.eslintrc.json +46 -0
- package/.github/workflows/ci.yml +49 -0
- package/.prettierrc +7 -0
- package/LICENSE +20 -0
- package/README.md +426 -168
- package/demos/counter/counter.element.html +1 -0
- package/demos/counter/counter.element.scss +234 -0
- package/demos/counter/counter.element.ts +68 -0
- package/demos/counter/index.html +205 -0
- package/demos/counter/index.ts +1 -0
- package/demos/site/code-example/code-example.element.scss +168 -0
- package/demos/site/code-example/code-example.element.ts +88 -0
- package/demos/site/event-log/event-log.element.scss +179 -0
- package/demos/site/event-log/event-log.element.ts +134 -0
- package/demos/site/favicon.svg +14 -0
- package/demos/site/index.html +346 -0
- package/demos/site/index.ts +13 -0
- package/demos/site/message/message.element.scss +75 -0
- package/demos/site/message/message.element.ts +76 -0
- package/demos/site/og-image.png +0 -0
- package/demos/site/styles/site.css +1023 -0
- package/demos/site/styles/tokens.css +56 -0
- package/demos/site/toast/toast.element.scss +110 -0
- package/demos/site/toast/toast.element.ts +63 -0
- package/demos/todo-dashboard/index.html +141 -0
- package/demos/todo-dashboard/index.ts +4 -0
- package/demos/todo-dashboard/todo-dashboard.element.scss +1145 -0
- package/demos/todo-dashboard/todo-dashboard.element.ts +332 -0
- package/demos/todo-dashboard/todo-filters.element.ts +54 -0
- package/demos/todo-dashboard/todo-item.element.ts +126 -0
- package/demos/todo-dashboard/todo-stats.element.ts +189 -0
- package/package.json +73 -29
- package/src/custom-element.ts +206 -0
- package/{index.d.ts → src/index.ts} +2 -0
- package/src/listen.ts +70 -0
- package/src/prop.ts +92 -0
- package/src/state.ts +129 -0
- package/src/template-runtime.ts +435 -0
- package/src/toggle.ts +66 -0
- package/src/tsconfig.json +24 -0
- package/src/util.ts +33 -0
- package/src/watch.ts +14 -0
- package/tests/basic.spec.ts +70 -0
- package/tests/custom-element.spec.ts +77 -0
- package/tests/dispatch.spec.ts +52 -0
- package/tests/init.spec.ts +94 -0
- package/tests/listen.spec.ts +118 -0
- package/tests/prop.spec.ts +118 -0
- package/tests/templating-runtime.spec.ts +575 -0
- package/tests/toggle.spec.ts +92 -0
- package/tests/watch.spec.ts +183 -0
- package/tools/build.js +119 -0
- package/tools/bundle.js +167 -0
- package/tools/rollup-config.js +70 -0
- package/tools/start.js +188 -0
- package/tsconfig.json +38 -0
- package/vite.config.mts +30 -0
- package/bundles/custom-elements-ts.umd.js +0 -315
- package/bundles/custom-elements-ts.umd.js.map +0 -1
- package/custom-element.d.ts +0 -12
- package/esm2015/custom-elements-ts.js +0 -260
- package/esm2015/custom-elements-ts.js.map +0 -1
- package/esm5/custom-elements-ts.js +0 -298
- package/esm5/custom-elements-ts.js.map +0 -1
- package/listen.d.ts +0 -17
- package/prop.d.ts +0 -2
- package/toggle.d.ts +0 -1
- package/util.d.ts +0 -4
- package/watch.d.ts +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
2
|
+
import { CustomElement } from 'custom-elements-ts';
|
|
3
|
+
|
|
4
|
+
@CustomElement({
|
|
5
|
+
tag: 'ce-a',
|
|
6
|
+
template: '<div id="t">X</div>',
|
|
7
|
+
style: ':host{display:block}',
|
|
8
|
+
shadow: true,
|
|
9
|
+
})
|
|
10
|
+
class A extends HTMLElement {
|
|
11
|
+
_watchArgs: any = undefined;
|
|
12
|
+
watched(args: any) {
|
|
13
|
+
this._watchArgs = args;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@CustomElement({
|
|
18
|
+
tag: 'ce-b',
|
|
19
|
+
template: '<span id="light">L</span>',
|
|
20
|
+
shadow: false,
|
|
21
|
+
})
|
|
22
|
+
class B extends HTMLElement {}
|
|
23
|
+
|
|
24
|
+
describe('CustomElement decorator', () => {
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
document.body.innerHTML = '';
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders template and style into shadowRoot by default', () => {
|
|
30
|
+
const el = document.createElement('ce-a') as A;
|
|
31
|
+
document.body.appendChild(el);
|
|
32
|
+
expect(el.shadowRoot).toBeTruthy();
|
|
33
|
+
expect(el.shadowRoot!.querySelector('#t')!.textContent).toBe('X');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('renders into light DOM when shadow=false', () => {
|
|
37
|
+
const el = document.createElement('ce-b') as B;
|
|
38
|
+
document.body.appendChild(el);
|
|
39
|
+
expect(el.shadowRoot).toBeNull();
|
|
40
|
+
expect(el.querySelector('#light')!.textContent).toBe('L');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('observedAttributes are derived from propsInit and kebab-cased', () => {
|
|
44
|
+
(A as any).propsInit = { myAttr: null, anotherAttr: null };
|
|
45
|
+
const observed = (A as any).observedAttributes as string[];
|
|
46
|
+
expect(observed).toContain('my-attr');
|
|
47
|
+
expect(observed).toContain('another-attr');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('attributeChangedCallback delegates to onAttributeChange and sets camelCase prop', () => {
|
|
51
|
+
(A as any).propsInit = { fooBar: null };
|
|
52
|
+
const el = document.createElement('ce-a') as A;
|
|
53
|
+
const spy = vi.spyOn(el as any, 'onAttributeChange');
|
|
54
|
+
document.body.appendChild(el);
|
|
55
|
+
// Simulate platform callback directly since observedAttributes may be empty at define time
|
|
56
|
+
(el as any).attributeChangedCallback('foo-bar', null as any, 'v1');
|
|
57
|
+
expect(spy).toHaveBeenCalled();
|
|
58
|
+
// property should be set via camelCase mapping
|
|
59
|
+
expect((el as any).fooBar).toBe('v1');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('onAttributeChange triggers watch method only when connected', () => {
|
|
63
|
+
(A as any).watchAttributes = { 'my-attr': 'watched' };
|
|
64
|
+
(A as any).propsInit = { myAttr: null };
|
|
65
|
+
const el = document.createElement('ce-a') as A;
|
|
66
|
+
// not connected: should not call watched
|
|
67
|
+
(el as any).onAttributeChange('my-attr', null as any, 'x');
|
|
68
|
+
expect(el._watchArgs).toBeUndefined();
|
|
69
|
+
|
|
70
|
+
// connected: should call watched
|
|
71
|
+
document.body.appendChild(el);
|
|
72
|
+
el._watchArgs = undefined;
|
|
73
|
+
// Simulate platform callback directly to trigger watcher
|
|
74
|
+
(el as any).onAttributeChange('my-attr', null as any, 'y');
|
|
75
|
+
expect(el._watchArgs).toEqual({ old: null, new: 'y' });
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { CustomElement, Dispatch, DispatchEmitter, Listen } from 'custom-elements-ts';
|
|
3
|
+
@CustomElement({
|
|
4
|
+
tag: 'btn-dispatch',
|
|
5
|
+
template: `<button>Test</button>`,
|
|
6
|
+
})
|
|
7
|
+
class DispatchElement extends HTMLElement {
|
|
8
|
+
@Dispatch() btnClick!: DispatchEmitter;
|
|
9
|
+
@Dispatch('btn.namedClick') btnClickNamed!: DispatchEmitter;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Listen('click')
|
|
16
|
+
btnHandler() {
|
|
17
|
+
this.shadowRoot!.querySelector('button')!.innerHTML = 'Hello';
|
|
18
|
+
this.btnClick.emit({ detail: 'Hello' });
|
|
19
|
+
this.btnClickNamed.emit({ detail: 'Hello from named click' });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('dispatch decorators', () => {
|
|
24
|
+
let element: any;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
const btnElement = document.createElement('btn-dispatch');
|
|
28
|
+
element = document.body.appendChild(btnElement);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
document.body.innerHTML = '';
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should trigger a btn.click DispatchEmitter', async () => {
|
|
36
|
+
const eventPromise = new Promise<CustomEvent>((resolve) => {
|
|
37
|
+
element.addEventListener('btn.click', (e: CustomEvent) => resolve(e));
|
|
38
|
+
});
|
|
39
|
+
element.click();
|
|
40
|
+
const e = await eventPromise;
|
|
41
|
+
expect(e.detail).toBe('Hello');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should trigger a btn.namedClick DispatchEmitter', async () => {
|
|
45
|
+
const eventPromise = new Promise<CustomEvent>((resolve) => {
|
|
46
|
+
element.addEventListener('btn.namedClick', (e: CustomEvent) => resolve(e));
|
|
47
|
+
});
|
|
48
|
+
element.click();
|
|
49
|
+
const e = await eventPromise;
|
|
50
|
+
expect(e.detail).toBe('Hello from named click');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { CustomElement, Toggle, Prop, Watch } from 'custom-elements-ts';
|
|
3
|
+
|
|
4
|
+
@CustomElement({})
|
|
5
|
+
class InitElement extends HTMLElement {
|
|
6
|
+
@Toggle() disabled = true;
|
|
7
|
+
@Prop() color = 'blue';
|
|
8
|
+
@Prop() icon: any;
|
|
9
|
+
|
|
10
|
+
setIcon = false;
|
|
11
|
+
@Watch('icon')
|
|
12
|
+
changeIcon(_value: any) {
|
|
13
|
+
this.setIcon = true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
connectedCallback() {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('init state', () => {
|
|
24
|
+
let myElement: any;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
myElement = document.createElement('init-element');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
document.body.innerHTML = '';
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should set attribute based on default prop value on init', () => {
|
|
35
|
+
const element = document.body.appendChild(myElement);
|
|
36
|
+
expect(element.getAttribute('color')).toBe('blue');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should set default prop value on init', () => {
|
|
40
|
+
const element = document.body.appendChild(myElement);
|
|
41
|
+
expect(element.color).toBe('blue');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should set attribute based on default toggle value on init', () => {
|
|
45
|
+
const element = document.body.appendChild(myElement);
|
|
46
|
+
expect(element.hasAttribute('disabled')).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should set default toggle value on init', () => {
|
|
50
|
+
const element = document.body.appendChild(myElement);
|
|
51
|
+
expect(element.disabled).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should reflect attribute to props on init', () => {
|
|
55
|
+
myElement.setAttribute('color', 'red');
|
|
56
|
+
const element = document.body.appendChild(myElement);
|
|
57
|
+
expect(element.color).toBe('red');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should not set attribute on second element instance', () => {
|
|
61
|
+
document.body.innerHTML = `
|
|
62
|
+
<init-element icon="awesome" disabled="false"></init-element>
|
|
63
|
+
<init-element></init-element>
|
|
64
|
+
<init-element></init-element>
|
|
65
|
+
`;
|
|
66
|
+
const initElements: any = document.body.querySelectorAll('init-element');
|
|
67
|
+
expect(initElements[1].icon).toBeFalsy();
|
|
68
|
+
expect(initElements[2].icon).toBeFalsy();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should reflect has attribute to toggle on init', () => {
|
|
72
|
+
myElement.setAttribute('disabled', '');
|
|
73
|
+
const element = document.body.appendChild(myElement);
|
|
74
|
+
expect(element.disabled).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should reflect true attribute to toggle on init', () => {
|
|
78
|
+
myElement.setAttribute('disabled', 'true');
|
|
79
|
+
const element = document.body.appendChild(myElement);
|
|
80
|
+
expect(element.disabled).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should reflect false attribute to toggle on init', () => {
|
|
84
|
+
myElement.setAttribute('disabled', 'false');
|
|
85
|
+
const element = document.body.appendChild(myElement);
|
|
86
|
+
expect(element.disabled).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should execute watch of attribute on init', () => {
|
|
90
|
+
myElement.setAttribute('icon', 'test');
|
|
91
|
+
const element = document.body.appendChild(myElement);
|
|
92
|
+
expect(element.setIcon).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { CustomElement, Dispatch, DispatchEmitter, Listen } from 'custom-elements-ts';
|
|
3
|
+
|
|
4
|
+
@CustomElement({
|
|
5
|
+
tag: 'btn-listen-dispatch',
|
|
6
|
+
template: `<button>Test</button>`,
|
|
7
|
+
})
|
|
8
|
+
class ButtonElement extends HTMLElement {
|
|
9
|
+
@Dispatch() btnClick!: DispatchEmitter;
|
|
10
|
+
@Dispatch('btn.namedClick') btnClickNamed!: DispatchEmitter;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Listen('click')
|
|
17
|
+
btnHandler() {
|
|
18
|
+
this.shadowRoot!.querySelector('button')!.innerHTML = 'Hello';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Listen('click', 'button')
|
|
22
|
+
btnInnerClick() {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('listen decorator', () => {
|
|
26
|
+
let element: any;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
const btnElement = document.createElement('btn-listen-dispatch');
|
|
30
|
+
element = document.body.appendChild(btnElement);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
document.body.innerHTML = '';
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should call method decorated @Listen', () => {
|
|
38
|
+
const btnHandlerSpy = vi.spyOn(element.btnHandler, 'call');
|
|
39
|
+
element.click();
|
|
40
|
+
expect(btnHandlerSpy).toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should call method decorated @Listen on children element click', () => {
|
|
44
|
+
const btnHandlerSpy = vi.spyOn(element.btnHandler, 'call');
|
|
45
|
+
element.shadowRoot.querySelector('button').click();
|
|
46
|
+
expect(btnHandlerSpy).toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should call method decorated @Listen on selector click', () => {
|
|
50
|
+
const btnInnerSpy = vi.spyOn(element.btnInnerClick, 'call');
|
|
51
|
+
element.shadowRoot.querySelector('button').click();
|
|
52
|
+
expect(btnInnerSpy).toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should execute inner method decorated @Listen', () => {
|
|
56
|
+
element.click();
|
|
57
|
+
expect(element.shadowRoot.querySelector('button').innerHTML).toEqual('Hello');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
62
|
+
@CustomElement({
|
|
63
|
+
tag: 'btn-listen-shadow-false',
|
|
64
|
+
template: `<button>Test</button>`,
|
|
65
|
+
shadow: false,
|
|
66
|
+
})
|
|
67
|
+
class ShadowFalseButtonElement extends HTMLElement {
|
|
68
|
+
@Dispatch() btnClick!: DispatchEmitter;
|
|
69
|
+
@Dispatch('btn.namedClick') btnClickNamed!: DispatchEmitter;
|
|
70
|
+
|
|
71
|
+
constructor() {
|
|
72
|
+
super();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Listen('click')
|
|
76
|
+
btnHandler() {
|
|
77
|
+
this.querySelector('button')!.innerHTML = 'Hello';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Listen('click', 'button')
|
|
81
|
+
btnInnerClick() {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe('listen decorator no shadowroot', () => {
|
|
85
|
+
let element: any;
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
const btnElement = document.createElement('btn-listen-shadow-false');
|
|
89
|
+
element = document.body.appendChild(btnElement);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
afterEach(() => {
|
|
93
|
+
document.body.innerHTML = '';
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should call method decorated @Listen', () => {
|
|
97
|
+
const btnHandlerSpy = vi.spyOn(element.btnHandler, 'call');
|
|
98
|
+
element.click();
|
|
99
|
+
expect(btnHandlerSpy).toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should call method decorated @Listen on children element click', () => {
|
|
103
|
+
const btnHandlerSpy = vi.spyOn(element.btnHandler, 'call');
|
|
104
|
+
element.querySelector('button').click();
|
|
105
|
+
expect(btnHandlerSpy).toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should call method decorated @Listen on selector click', () => {
|
|
109
|
+
const btnInnerSpy = vi.spyOn(element.btnInnerClick, 'call');
|
|
110
|
+
element.querySelector('button').click();
|
|
111
|
+
expect(btnInnerSpy).toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should execute inner method decorated @Listen', () => {
|
|
115
|
+
element.click();
|
|
116
|
+
expect(element.querySelector('button').innerHTML).toEqual('Hello');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { CustomElement, Prop } from 'custom-elements-ts';
|
|
3
|
+
|
|
4
|
+
@CustomElement({
|
|
5
|
+
tag: 'prop-element',
|
|
6
|
+
template: '<span>my element</span>',
|
|
7
|
+
style: ':host{border:0}',
|
|
8
|
+
})
|
|
9
|
+
class PropElement extends HTMLElement {
|
|
10
|
+
@Prop() name: any;
|
|
11
|
+
@Prop() maxFileSize: any;
|
|
12
|
+
|
|
13
|
+
@Prop() objectProp: any;
|
|
14
|
+
@Prop() init = 'blue';
|
|
15
|
+
|
|
16
|
+
@Prop() kebabCase = 'kebab';
|
|
17
|
+
|
|
18
|
+
@Prop() fnProp: any;
|
|
19
|
+
@Prop() classProp: any;
|
|
20
|
+
|
|
21
|
+
@Prop() zero = 0;
|
|
22
|
+
@Prop() enabled = false;
|
|
23
|
+
@Prop() empty = '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('prop decorator', () => {
|
|
27
|
+
let myElementInstance: any;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
const myElement = document.createElement('prop-element');
|
|
31
|
+
myElementInstance = document.body.appendChild(myElement);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
document.body.innerHTML = '';
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should reflect as property', () => {
|
|
39
|
+
myElementInstance.setAttribute('name', 'Mario');
|
|
40
|
+
expect(myElementInstance.getAttribute('name')).toEqual('Mario');
|
|
41
|
+
myElementInstance.setAttribute('name', 'Luigi');
|
|
42
|
+
expect(myElementInstance.name).toEqual('Luigi');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should reflect as attribute', () => {
|
|
46
|
+
myElementInstance.name = 'Aivan';
|
|
47
|
+
expect(myElementInstance.getAttribute('name')).toEqual('Aivan');
|
|
48
|
+
myElementInstance.name = 'Custom Elements';
|
|
49
|
+
expect(myElementInstance.getAttribute('name')).toEqual('Custom Elements');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should not reflect as attribute', () => {
|
|
53
|
+
myElementInstance.name = {};
|
|
54
|
+
expect(myElementInstance.getAttribute('name')).toBeFalsy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should properly set camelcase properties', () => {
|
|
58
|
+
myElementInstance.maxFileSize = 1;
|
|
59
|
+
expect(myElementInstance.maxFileSize).toEqual(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should properly get camelcase properties as kebabcase attributes', () => {
|
|
63
|
+
myElementInstance.maxFileSize = 1;
|
|
64
|
+
expect(myElementInstance.getAttribute('max-file-size')).toEqual('1');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should properly set kebabcase attributes as camelcase properties', () => {
|
|
68
|
+
myElementInstance.setAttribute('max-file-size', 2);
|
|
69
|
+
expect(myElementInstance.maxFileSize).toEqual(2);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should evaluate property', () => {
|
|
73
|
+
myElementInstance.objectProp = { name: 'Name' };
|
|
74
|
+
expect(myElementInstance.objectProp.name).toEqual('Name');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should change attribute on attribute change', () => {
|
|
78
|
+
myElementInstance.setAttribute('init', 'red');
|
|
79
|
+
expect(myElementInstance.init).toEqual('red');
|
|
80
|
+
expect(myElementInstance.getAttribute('init')).toEqual('red');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should change property via kebab attribute', () => {
|
|
84
|
+
myElementInstance.setAttribute('kebab-case', 'shawarma');
|
|
85
|
+
expect(myElementInstance.kebabCase).toEqual('shawarma');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not reflect function values as attributes and preserve reference', () => {
|
|
89
|
+
const fn = () => 42;
|
|
90
|
+
myElementInstance.fnProp = fn;
|
|
91
|
+
expect(myElementInstance.getAttribute('fn-prop')).toBeFalsy();
|
|
92
|
+
expect(typeof myElementInstance.fnProp).toEqual('function');
|
|
93
|
+
expect(myElementInstance.fnProp()).toEqual(42);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should not reflect class/constructor values as attributes and preserve reference', () => {
|
|
97
|
+
class Foo {}
|
|
98
|
+
myElementInstance.classProp = Foo;
|
|
99
|
+
expect(myElementInstance.getAttribute('class-prop')).toBeFalsy();
|
|
100
|
+
expect(myElementInstance.classProp).toBe(Foo);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should initialize falsy default property values', () => {
|
|
104
|
+
expect(myElementInstance.zero).toBe(0);
|
|
105
|
+
expect(myElementInstance.enabled).toBe(false);
|
|
106
|
+
expect(myElementInstance.empty).toBe('');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should preserve boolean prop type when reflected through attributes', () => {
|
|
110
|
+
myElementInstance.enabled = true;
|
|
111
|
+
expect(myElementInstance.enabled).toBe(true);
|
|
112
|
+
expect(myElementInstance.getAttribute('enabled')).toBe('true');
|
|
113
|
+
|
|
114
|
+
myElementInstance.enabled = false;
|
|
115
|
+
expect(myElementInstance.enabled).toBe(false);
|
|
116
|
+
expect(myElementInstance.getAttribute('enabled')).toBe('false');
|
|
117
|
+
});
|
|
118
|
+
});
|