├── .editorconfig
├── .gitignore
├── .release-it.json
├── .travis.yml
├── LICENSE
├── README.md
├── demo
└── index.html
├── index.js
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── HauntedLitElement.js
└── component.js
└── test
├── haunted-lit-element.test.js
└── library.test.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
23 | [*.json]
24 | indent_size = 2
25 |
26 | [*.{html,js,md}]
27 | block_comment_start = /**
28 | block_comment = *
29 | block_comment_end = */
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## editors
2 | /.idea
3 | /.vscode
4 |
5 | ## system files
6 | .DS_Store
7 |
8 | ## npm
9 | /node_modules/
10 | /npm-debug.log
11 |
12 | ## testing
13 | /coverage/
14 |
15 | ## temp folders
16 | /.tmp/
17 |
18 | # build
19 | /_site/
20 | /dist/
21 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "changelog": "git log --pretty=format:'* %s (%h)' [REV_RANGE]"
4 | },
5 | "github": {
6 | "release": true,
7 | "releaseName": "Release %s",
8 | "tokenRef": "GITHUB_TOKEN"
9 | },
10 | "publishConfig": {
11 | "access": "public"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: node
3 | addons:
4 | chrome: stable
5 | script:
6 | - npm run lint && npm run test
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 haunted-lit-element
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Haunted Lit Element
2 |
3 | [](https://travis-ci.com/jdin/haunted-lit-element)
4 | [](https://www.npmjs.com/package/haunted-lit-element)
5 | [](https://bundlephobia.com/result?p=haunted-lit-element)
6 | [](https://www.npmjs.com/package/haunted-lit-element)
7 |
8 | A missing connection between [Haunted](https://github.com/matthewp/haunted) and [LitElement](https://github.com/polymer/lit-element).
9 |
10 | It makes it possible to use LitElement's features like
11 | [properties](https://lit-element.polymer-project.org/guide/properties)
12 | and [styles](https://lit-element.polymer-project.org/guide/styles) in Haunted.
13 |
14 | > This project follows the [open-wc](https://github.com/open-wc/open-wc) recommendation.
15 |
16 | ## Installation
17 | ```bash
18 | npm i haunted-lit-element
19 | ```
20 |
21 | ## Usage
22 |
23 | This library provides `component` function that is made in the way as it is in `Haunted`.
24 |
25 | ### `component(MyEl)`
26 |
27 | Similar to `haunted` but the base class is `LitElement`:
28 |
29 | ```javascript
30 | import {html} from 'lit-html';
31 | import {component} from 'haunted-lit-element';
32 | window.customElements.define('my-el', component(() => html`hello world`));
33 | ```
34 |
35 | ### `component(MyEl, optsOrBaseCls)`
36 |
37 | The second parameter in `component` function can be `options` or a `base class`
38 | which should be derived from `HauntedLitElement`.
39 |
40 | The `options` in most cases are [properties](https://lit-element.polymer-project.org/guide/properties)
41 | and [styles](https://lit-element.polymer-project.org/guide/styles) from `LitElement`.
42 | But it can actually be anything as at the end it is just a static field in the base class.
43 | It is done in that way because there are `LitElement` extensions that use similar approach with their own configuration.
44 |
45 | Example of defining `options` as second argument:
46 | ```javascript
47 | import {css} from 'lit-element';
48 | import {component} from 'haunted-lit-element';
49 |
50 | const MyEl = () => { /*...*/ };
51 |
52 | const properties = {myParam: {type: String}, /*...*/};
53 | const styles = css`/* my css styles */`;
54 |
55 | window.customElements.define('my-el', component(MyEl, {properties, styles}));
56 | ```
57 |
58 | Example of defining `base class` as second argument:
59 | ```javascript
60 | import {component, HauntedLitElement} from 'haunted-lit-element';
61 |
62 | class MyExtHauntedLitElement extends HauntedLitElement {
63 | // ... my own stuff
64 | }
65 |
66 | const MyEl = () => { /*...*/ };
67 |
68 | window.customElements.define('my-el', component(MyEl, MyExtHauntedLitElement));
69 | ```
70 |
71 | ### `component(myEl, baseCls, opts)`
72 |
73 | If you want to use options and a base class than the base class is the second argument and options are the third.
74 |
75 | Example of using LitElement's [properties](https://lit-element.polymer-project.org/guide/properties)
76 | and [styles](https://lit-element.polymer-project.org/guide/styles) helper with a custom base class.
77 |
78 | ```html
79 |
${count}
60 | 61 | `; 62 | }; 63 | register('test-el-3', renderer); 64 | const el = await fixture( 65 | html` 66 |0
`); 70 | el.shadowRoot.querySelector('button').click(); 71 | await runEffects(); 72 | expect(el).shadowDom.to.equal(`1
`); 73 | }); 74 | 75 | it('checks pure useEffect works', async () => { 76 | let isCleanedUp = false; 77 | const renderer = () => { 78 | useEffect(() => { 79 | console.log('use effect'); 80 | isCleanedUp = false; 81 | return () => { 82 | console.log('clean up'); 83 | isCleanedUp = true; 84 | }; 85 | }, []); 86 | return html` 87 | ... 88 | `; 89 | }; 90 | register('test-el-4', renderer); 91 | await fixture( 92 | html` 93 |${val}
115 | 116 | `; 117 | }; 118 | register('test-el-5', renderer); 119 | const el = await fixture( 120 | html` 121 |...
`); 125 | expect(isCleanedUp).to.equal(false); 126 | el.shadowRoot.querySelector('button').click(); 127 | await runEffects(); 128 | expect(el).shadowDom.to.equal(`loaded
`); 129 | expect(isCleanedUp).to.equal(false); 130 | fixtureCleanup(); 131 | expect(isCleanedUp).to.equal(true); 132 | }); 133 | 134 | it('checks pure useLayoutEffect works', async () => { 135 | let isCleanedUp = false; 136 | const renderer = () => { 137 | useLayoutEffect(() => { 138 | console.log('use effect'); 139 | isCleanedUp = false; 140 | return () => { 141 | console.log('clean up'); 142 | isCleanedUp = true; 143 | }; 144 | }, []); 145 | return html` 146 | ... 147 | `; 148 | }; 149 | register('test-el-6', renderer); 150 | await fixture( 151 | html` 152 |...
`); 184 | expect(isCleanedUp).to.equal(false); 185 | el.shadowRoot.querySelector('button').click(); 186 | await runEffects(); 187 | expect(el).shadowDom.to.equal(`loaded
`); 188 | expect(isCleanedUp).to.equal(false); 189 | fixtureCleanup(); 190 | expect(isCleanedUp).to.equal(true); 191 | }); 192 | 193 | it('checks useMemo works', async () => { 194 | const renderer = () => { 195 | const a = useMemo(() => 25, []); 196 | return html` 197 | ${a} 198 | `; 199 | }; 200 | register('test-el-8', renderer); 201 | const el = await fixture( 202 | html` 203 |${a}
216 | 217 | `; 218 | }; 219 | register('test-el-9', renderer); 220 | const el = await fixture( 221 | html` 222 |true
`); 226 | el.shadowRoot.querySelector('button').click(); 227 | await runEffects(); 228 | expect(el).shadowDom.to.equal(`false
`); 229 | }); 230 | }); 231 | -------------------------------------------------------------------------------- /test/library.test.js: -------------------------------------------------------------------------------- 1 | import { expect, html } from '@open-wc/testing'; 2 | import { css } from 'lit-element'; 3 | import compDefault, { HauntedLitElement, component } from '../index.js'; 4 | 5 | class MyCls extends HauntedLitElement {} 6 | 7 | describe('library tests', () => { 8 | it('can create basic component', () => { 9 | const comp = compDefault(() => html``); 10 | expect(comp).to.be.not.null; 11 | window.customElements.define('my-el', comp); 12 | const C = window.customElements.get('my-el'); 13 | expect(new C() instanceof HauntedLitElement).to.be.true; 14 | }); 15 | 16 | it('can create basic component with props, styles and custom stuff', () => { 17 | const properties = { a: { type: String } }; 18 | const styles = css` 19 | :host { 20 | display: block; 21 | } 22 | `; 23 | const custom = { a: 'b' }; 24 | const comp = compDefault(() => html``, { properties, styles, custom }); 25 | expect(comp.properties).to.equal(properties); 26 | expect(comp.styles).to.equal(styles); 27 | expect(comp.custom).to.equal(custom); 28 | }); 29 | 30 | it('can create basic comp with custom class and props', () => { 31 | const properties = { a: { type: String } }; 32 | const comp = compDefault(() => html``, MyCls, { properties }); 33 | window.customElements.define('my-custom-el', comp); 34 | const C = window.customElements.get('my-custom-el'); 35 | expect(new C() instanceof MyCls).to.be.true; 36 | expect(comp.properties).to.equal(properties); 37 | }); 38 | 39 | it('can create basic comp with custom class as a second param', () => { 40 | const comp = compDefault(() => html``, MyCls); 41 | window.customElements.define('my-custom-el-2', comp); 42 | const C = window.customElements.get('my-custom-el-2'); 43 | expect(new C() instanceof MyCls).to.be.true; 44 | }); 45 | 46 | it('can create basic comp using component', () => { 47 | const comp = component(() => html``); 48 | window.customElements.define('my-el-3', comp); 49 | const C = window.customElements.get('my-el-3'); 50 | expect(new C() instanceof HauntedLitElement).to.be.true; 51 | }); 52 | }); 53 | --------------------------------------------------------------------------------