@storybook-tiny/webcomponent 1.0.0 → 1.1.1
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/README.md +1 -1
- package/bin/storybook-tiny.js +1 -1
- package/package.json +2 -1
- package/src/Storybook.js +102 -71
- package/stories/some.stories.js +21 -17
- package/src/WcElement.js +0 -145
package/README.md
CHANGED
package/bin/storybook-tiny.js
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url'
|
|
|
5
5
|
|
|
6
6
|
const config = {
|
|
7
7
|
rootDir: fileURLToPath(new URL('..', import.meta.url)),
|
|
8
|
-
devDependencies: ['@storybook-tiny/webcomponent', 'vite'],
|
|
8
|
+
devDependencies: ['@storybook-tiny/webcomponent', 'mi-element', 'vite'],
|
|
9
9
|
files: ['stories/*', 'vite.config.js'],
|
|
10
10
|
post: [
|
|
11
11
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook-tiny/webcomponent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A tiny storybook for webcompoments",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storybook",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"vite.config.js"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"mi-element": "^0.1.0",
|
|
36
37
|
"@storybook-tiny/setup": "1.0.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
package/src/Storybook.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import styles from './Storybook.module.css'
|
|
2
|
-
import {
|
|
2
|
+
import { MiElement, define, refsBySelector, esc, refsById } from 'mi-element'
|
|
3
3
|
|
|
4
4
|
const getLocationHash = () => decodeURIComponent(location.hash.substring(1))
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`
|
|
6
|
+
/**
|
|
7
|
+
* convert number to css unit
|
|
8
|
+
* @param {numbers|string} unit
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
const cssUnit = (unit) => {
|
|
12
|
+
const n = Number(unit)
|
|
13
|
+
return isNaN(n) ? unit : `${n}px`
|
|
14
|
+
}
|
|
16
15
|
|
|
17
16
|
const defaultStory = `
|
|
18
17
|
<p class="${styles.storybookSectionP}">
|
|
19
|
-
The tiny storybook for
|
|
18
|
+
The tiny storybook for
|
|
20
19
|
<a
|
|
21
20
|
href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components"
|
|
22
21
|
target="_blanc"
|
|
@@ -27,50 +26,71 @@ const defaultStory = `
|
|
|
27
26
|
</p>
|
|
28
27
|
`
|
|
29
28
|
|
|
30
|
-
class Storybook extends
|
|
31
|
-
$ = {}
|
|
29
|
+
class Storybook extends MiElement {
|
|
32
30
|
state = {}
|
|
33
31
|
|
|
34
|
-
static
|
|
35
|
-
header: 'Storybook Tiny',
|
|
36
|
-
href: '/stories/index.html',
|
|
37
|
-
width: 130,
|
|
38
|
-
stories: []
|
|
39
|
-
}
|
|
32
|
+
static shadowRootOptions = null
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.addEventListener(window, 'hashchange', () => this._renderStory())
|
|
49
|
-
this.render()
|
|
34
|
+
static get attributes() {
|
|
35
|
+
return {
|
|
36
|
+
header: 'Storybook Tiny',
|
|
37
|
+
href: '/stories/index.html',
|
|
38
|
+
width: 130,
|
|
39
|
+
stories: []
|
|
40
|
+
}
|
|
50
41
|
}
|
|
51
42
|
|
|
43
|
+
static template = `
|
|
44
|
+
<main class="${styles.storybook}">
|
|
45
|
+
<aside>
|
|
46
|
+
<h4><a></a></h4>
|
|
47
|
+
<nav></nav>
|
|
48
|
+
</aside>
|
|
49
|
+
<section class="stories">
|
|
50
|
+
</section>
|
|
51
|
+
</main>
|
|
52
|
+
`
|
|
53
|
+
|
|
52
54
|
render() {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
this.refs = refsBySelector(this.renderRoot, {
|
|
56
|
+
aside: 'main > aside',
|
|
57
|
+
h4: 'main > aside h4 a',
|
|
58
|
+
nav: 'main > aside nav',
|
|
59
|
+
story: 'main > section'
|
|
60
|
+
})
|
|
61
|
+
this.on('hashchange', () => this._updateStory(), window)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
update() {
|
|
65
|
+
const { refs } = this
|
|
66
|
+
refs.h4.textContent = this.header
|
|
67
|
+
refs.h4.href = this.href
|
|
68
|
+
refs.aside.style.flexBasis = cssUnit(this.width)
|
|
69
|
+
refs.nav.innerHTML = ''
|
|
58
70
|
for (const story of this.stories) {
|
|
59
71
|
const $el = document.createElement('storybook-tiny-story')
|
|
60
72
|
$el.story = story
|
|
61
|
-
|
|
73
|
+
refs.nav.appendChild($el)
|
|
62
74
|
}
|
|
63
|
-
this.
|
|
75
|
+
this._updateStory()
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
_updateStory() {
|
|
79
|
+
const { refs } = this
|
|
67
80
|
const locHash = getLocationHash()
|
|
81
|
+
|
|
68
82
|
if (this.state.title === locHash) {
|
|
69
83
|
return
|
|
70
84
|
}
|
|
71
85
|
|
|
86
|
+
// update active state on nav
|
|
87
|
+
for (const $el of refs.nav.childNodes) {
|
|
88
|
+
$el.active = $el.story?.title === locHash
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
let renderStory = defaultStory
|
|
73
92
|
|
|
93
|
+
// find active story
|
|
74
94
|
for (const story of this.stories) {
|
|
75
95
|
if (typeof story === 'object') {
|
|
76
96
|
const { title, component } = story
|
|
@@ -81,15 +101,15 @@ class Storybook extends WcElement {
|
|
|
81
101
|
}
|
|
82
102
|
}
|
|
83
103
|
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
// try rendering the story
|
|
105
|
+
refs.story.innerHTML = ''
|
|
86
106
|
try {
|
|
87
107
|
switch (typeof renderStory) {
|
|
88
108
|
case 'string':
|
|
89
|
-
|
|
109
|
+
refs.story.innerHTML = renderStory
|
|
90
110
|
break
|
|
91
111
|
case 'function':
|
|
92
|
-
|
|
112
|
+
refs.story.appendChild(renderStory())
|
|
93
113
|
break
|
|
94
114
|
default:
|
|
95
115
|
throw new Error(
|
|
@@ -101,55 +121,66 @@ class Storybook extends WcElement {
|
|
|
101
121
|
const error = document.createElement('storybook-tiny-error')
|
|
102
122
|
error.message = err.message
|
|
103
123
|
error.stack = err.stack
|
|
104
|
-
|
|
124
|
+
refs.story.appendChild(error)
|
|
105
125
|
}
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
define('storybook-tiny', Storybook)
|
|
110
130
|
|
|
111
|
-
class Story extends
|
|
112
|
-
|
|
131
|
+
class Story extends MiElement {
|
|
132
|
+
static shadowRootOptions = null
|
|
133
|
+
|
|
134
|
+
static get attributes() {
|
|
135
|
+
return {
|
|
136
|
+
active: false,
|
|
137
|
+
story: ''
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
update() {
|
|
113
142
|
if (typeof this.story === 'string') {
|
|
114
|
-
this.innerHTML = this.story
|
|
143
|
+
this.renderRoot.innerHTML = this.story
|
|
115
144
|
return
|
|
116
145
|
}
|
|
117
146
|
|
|
118
|
-
this.addEventListener(window, 'hashchange', () => this.render())
|
|
119
147
|
const { title } = this.story
|
|
120
|
-
this.innerHTML = `
|
|
121
|
-
<div>
|
|
122
|
-
<a
|
|
123
|
-
href="#${title}"
|
|
124
|
-
tabindex="0"
|
|
125
|
-
role="button"
|
|
126
|
-
>${title}</a>
|
|
127
|
-
</div>
|
|
128
|
-
`
|
|
129
|
-
this.render()
|
|
130
|
-
}
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
this.renderRoot.innerHTML = esc`
|
|
150
|
+
<div>
|
|
151
|
+
<a href="#${title}">${title}</a>
|
|
152
|
+
</div>
|
|
153
|
+
`
|
|
154
|
+
this.refs = { a: this.querySelector('a') }
|
|
155
|
+
this.refs.a.className = this.active ? styles.active : ''
|
|
136
156
|
}
|
|
137
157
|
}
|
|
138
158
|
|
|
139
159
|
define('storybook-tiny-story', Story)
|
|
140
160
|
|
|
141
|
-
class StoryError extends
|
|
142
|
-
static
|
|
161
|
+
class StoryError extends MiElement {
|
|
162
|
+
static shadowRootOptions = null
|
|
163
|
+
|
|
164
|
+
static get attributes() {
|
|
165
|
+
return { message: '', stack: '' }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static template = `
|
|
169
|
+
<div class="${styles.error}">
|
|
170
|
+
<h2>Error</h2>
|
|
171
|
+
<p id="message"></p>
|
|
172
|
+
<p> </p>
|
|
173
|
+
<pre style="white-space: pre-wrap" id="stack"></pre>
|
|
174
|
+
</div>
|
|
175
|
+
`
|
|
143
176
|
|
|
144
177
|
render() {
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
</div>
|
|
152
|
-
`
|
|
178
|
+
this.refs = refsById(this.renderRoot)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
update() {
|
|
182
|
+
this.refs.message.textContent = this.message
|
|
183
|
+
this.refs.stack.textContent = this.stack
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
186
|
|
package/stories/some.stories.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { refsBySelector } from "mi-element/refs"
|
|
2
|
+
|
|
1
3
|
// define some custom elements
|
|
2
4
|
window.customElements.define(
|
|
3
5
|
'x-button',
|
|
4
6
|
class extends HTMLElement {
|
|
5
7
|
connectedCallback() {
|
|
6
|
-
this.
|
|
8
|
+
this.renderRoot = this.attachShadow({ mode: 'open' })
|
|
7
9
|
this.$ = document.createElement('button')
|
|
8
10
|
this.$.addEventListener('click', () => alert('Hi'))
|
|
9
|
-
this.
|
|
11
|
+
this.renderRoot.appendChild(this.$)
|
|
10
12
|
this.$.textContent = this.childNodes?.[0]?.textContent || 'Click Me'
|
|
11
13
|
}
|
|
12
14
|
}
|
|
@@ -15,7 +17,6 @@ window.customElements.define(
|
|
|
15
17
|
window.customElements.define(
|
|
16
18
|
'x-counter',
|
|
17
19
|
class extends HTMLElement {
|
|
18
|
-
$ = {}
|
|
19
20
|
init = 0
|
|
20
21
|
|
|
21
22
|
static observedAttributes = ['init']
|
|
@@ -29,8 +30,8 @@ window.customElements.define(
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
connectedCallback() {
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
33
|
+
this.renderRoot = this.attachShadow({ mode: 'closed' })
|
|
34
|
+
this.renderRoot.innerHTML = `
|
|
34
35
|
<style>
|
|
35
36
|
:host {
|
|
36
37
|
font-size: 1.5em;
|
|
@@ -50,18 +51,21 @@ window.customElements.define(
|
|
|
50
51
|
<div>
|
|
51
52
|
<span class="count"></span>
|
|
52
53
|
<span class="heart"></span>
|
|
53
|
-
<button>👍</button>
|
|
54
|
-
<button>👎</button>
|
|
54
|
+
<button id="up">👍</button>
|
|
55
|
+
<button id="down">👎</button>
|
|
55
56
|
</div>
|
|
56
|
-
|
|
57
|
-
this
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
`
|
|
58
|
+
this.refs = refsBySelector(this.renderRoot, {
|
|
59
|
+
count: '.count',
|
|
60
|
+
heart: '.heart',
|
|
61
|
+
up: '#up',
|
|
62
|
+
down: '#down'
|
|
63
|
+
})
|
|
64
|
+
this.refs.up.addEventListener('click', () => {
|
|
61
65
|
this.init += 1
|
|
62
66
|
this.render()
|
|
63
67
|
})
|
|
64
|
-
this
|
|
68
|
+
this.refs.down.addEventListener('click', () => {
|
|
65
69
|
this.init -= 1
|
|
66
70
|
this.render()
|
|
67
71
|
})
|
|
@@ -69,8 +73,8 @@ window.customElements.define(
|
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
render() {
|
|
72
|
-
this
|
|
73
|
-
this
|
|
76
|
+
this.refs.count.textContent = this.init
|
|
77
|
+
this.refs.heart.textContent =
|
|
74
78
|
this.init === 0 ? '🤍' : this.init > 0 ? '❤️' : '💔'
|
|
75
79
|
}
|
|
76
80
|
}
|
|
@@ -86,8 +90,8 @@ export const storyCounter = {
|
|
|
86
90
|
component: () => document.createElement('x-counter')
|
|
87
91
|
}
|
|
88
92
|
export const storyError = {
|
|
89
|
-
title: 'Error',
|
|
93
|
+
title: 'Hello 🌍 Error',
|
|
90
94
|
component: () => {
|
|
91
|
-
throw new Error('
|
|
95
|
+
throw new Error('Hello 🌍 Error')
|
|
92
96
|
}
|
|
93
97
|
}
|
package/src/WcElement.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* class extening HTMLElement to enable deferred rendering on attribute changes
|
|
3
|
-
* either via `setAttribute(name, value)` or `this[name] = value`.
|
|
4
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
|
|
5
|
-
* @example
|
|
6
|
-
* class Example extends WcElement {
|
|
7
|
-
* // internal references go here
|
|
8
|
-
* $ = {}
|
|
9
|
-
* // define all observed attributes.
|
|
10
|
-
* // attributes are accessible via `this[prop]`
|
|
11
|
-
* // avoid using attributes which are HTMLElement properties
|
|
12
|
-
* static attributes = {
|
|
13
|
-
* text: 'Hi'
|
|
14
|
-
* }
|
|
15
|
-
* // as with HTMLElement
|
|
16
|
-
* connectedCallback() {
|
|
17
|
-
* // do initial mount of component here
|
|
18
|
-
* this._shadow = this.shadow = this.attachShadow({ mode: 'closed' })
|
|
19
|
-
* this._shadow.innerHTML = `<div></div>`
|
|
20
|
-
* this.$.div = this._shadow.querySelector('div')
|
|
21
|
-
* this.render()
|
|
22
|
-
* }
|
|
23
|
-
* // render method called every time an attribute changes
|
|
24
|
-
* render() {
|
|
25
|
-
* this.$.div.textContent = this.text
|
|
26
|
-
* }
|
|
27
|
-
* }
|
|
28
|
-
* // create a custom element with the `define` function (see below)
|
|
29
|
-
* define('x-example', Example)
|
|
30
|
-
* // create a DOM node and re-render via attribute or property changes
|
|
31
|
-
* const elem = document.createElement('x-example')
|
|
32
|
-
* elem.setAttribute('text', 'set attribute')
|
|
33
|
-
* elem.text = 'set property'
|
|
34
|
-
*/
|
|
35
|
-
export class WcElement extends HTMLElement {
|
|
36
|
-
_attr = {}
|
|
37
|
-
_removers = []
|
|
38
|
-
|
|
39
|
-
constructor() {
|
|
40
|
-
super()
|
|
41
|
-
this._attr = structuredClone(this.constructor.attributes || {})
|
|
42
|
-
observedAttributes(this)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* set attribute and re-render
|
|
47
|
-
* @param {string} name
|
|
48
|
-
* @param {any} value
|
|
49
|
-
*/
|
|
50
|
-
setAttribute(name, value) {
|
|
51
|
-
this._attr[name] = value
|
|
52
|
-
this.isConnected && this.deferredRender()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* component must implement render()!
|
|
57
|
-
*/
|
|
58
|
-
connectedCallback() {
|
|
59
|
-
this.render()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @param {string} name change attribute
|
|
64
|
-
* @param {any} _oldValue
|
|
65
|
-
* @param {any} newValue new value
|
|
66
|
-
*/
|
|
67
|
-
attributeChangedCallback(name, _oldValue, newValue) {
|
|
68
|
-
this._attr[name] = newValue
|
|
69
|
-
this.deferredRender()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* helper function to remove event listeners when component gets disconnected
|
|
74
|
-
* @param {HTMLElement} node
|
|
75
|
-
* @param {string} event
|
|
76
|
-
* @param {function} fn
|
|
77
|
-
*/
|
|
78
|
-
addEventListener(node, event, fn) {
|
|
79
|
-
node.addEventListener(event, fn)
|
|
80
|
-
this._removers.push(() => node.removeEventListener(event, fn))
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* remove possible event listeners
|
|
85
|
-
*/
|
|
86
|
-
disconnectedCallback() {
|
|
87
|
-
this._removers.forEach((remover) => remover())
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* deferred render
|
|
92
|
-
*/
|
|
93
|
-
deferredRender() {
|
|
94
|
-
window.requestAnimationFrame(() => {
|
|
95
|
-
this.render()
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* re-render component when property changes
|
|
102
|
-
* @param {HTMLElement} elem
|
|
103
|
-
*/
|
|
104
|
-
const observedAttributes = (elem) => {
|
|
105
|
-
for (const prop of Object.keys(elem._attr)) {
|
|
106
|
-
Object.defineProperty(elem, prop, {
|
|
107
|
-
get() {
|
|
108
|
-
return elem._attr[prop]
|
|
109
|
-
},
|
|
110
|
-
set(newValue) {
|
|
111
|
-
const oldValue = elem._attr[prop]
|
|
112
|
-
if (oldValue === newValue) return
|
|
113
|
-
elem._attr[prop] = newValue
|
|
114
|
-
elem.isConnected && elem.deferredRender()
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* defines a custom element adding observedAttributes from default static
|
|
122
|
-
* attributes
|
|
123
|
-
* @param {string} name custom element tag
|
|
124
|
-
* @param {HTMLElement} Element
|
|
125
|
-
*/
|
|
126
|
-
export const define = (name, Element) => {
|
|
127
|
-
Element.observedAttributes =
|
|
128
|
-
Element.observedAttributes || Object.keys(Element.attributes || [])
|
|
129
|
-
window.customElements.define(name, Element)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const toNumber = (n) => {
|
|
133
|
-
const _n = Number(n)
|
|
134
|
-
return !isNaN(_n) ? _n : undefined
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* convert number to css unit
|
|
139
|
-
* @param {numbers|string} unit
|
|
140
|
-
* @returns {string}
|
|
141
|
-
*/
|
|
142
|
-
export const cssUnit = (unit) => {
|
|
143
|
-
const n = toNumber(unit)
|
|
144
|
-
return typeof n === 'number' ? `${n}px` : unit
|
|
145
|
-
}
|