@pxsig/storybook-addon 0.2.5 → 0.3.2
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/manager.mjs +13 -17
- package/package.json +5 -1
- package/panel-style.mjs +50 -0
package/manager.mjs
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
import React from 'react';
|
|
22
22
|
import { addons, experimental_getStatusStore, types, useStorybookState } from 'storybook/manager-api';
|
|
23
23
|
import { AddonPanel } from 'storybook/internal/components';
|
|
24
|
+
import { useTheme } from 'storybook/theming';
|
|
25
|
+
import { buttonStyle, dashboardUrl, panelColors, themeMode } from './panel-style.mjs';
|
|
24
26
|
|
|
25
27
|
const ADDON = 'pxsig';
|
|
26
28
|
const PANEL = `${ADDON}/panel`;
|
|
@@ -121,20 +123,14 @@ addons.register(ADDON, () => {
|
|
|
121
123
|
});
|
|
122
124
|
|
|
123
125
|
const h = React.createElement;
|
|
124
|
-
const btn = (label, onClick, disabled) =>
|
|
125
|
-
h(
|
|
126
|
-
'button',
|
|
127
|
-
{
|
|
128
|
-
type: 'button',
|
|
129
|
-
onClick,
|
|
130
|
-
disabled,
|
|
131
|
-
style: { font: 'inherit', fontSize: 12, padding: '4px 10px', borderRadius: 4, border: '1px solid rgba(128,128,128,.4)', background: 'transparent', color: 'inherit', cursor: disabled ? 'default' : 'pointer', opacity: disabled ? 0.5 : 1 },
|
|
132
|
-
},
|
|
133
|
-
label
|
|
134
|
-
);
|
|
135
126
|
|
|
136
127
|
function Panel() {
|
|
137
128
|
const { storyId } = useStorybookState();
|
|
129
|
+
// Storybook's theme: the buttons follow it, and the inspector and dashboard open in its mode.
|
|
130
|
+
const theme = useTheme();
|
|
131
|
+
const mode = themeMode(theme);
|
|
132
|
+
const colors = panelColors(theme);
|
|
133
|
+
const btn = (label, onClick, disabled) => h('button', { type: 'button', onClick, disabled, style: buttonStyle(theme, disabled) }, label);
|
|
138
134
|
const [, force] = React.useReducer((n) => n + 1, 0);
|
|
139
135
|
React.useEffect(() => {
|
|
140
136
|
bus.addEventListener('change', force);
|
|
@@ -146,17 +142,17 @@ function Panel() {
|
|
|
146
142
|
if (!state.up)
|
|
147
143
|
return h(
|
|
148
144
|
'div',
|
|
149
|
-
{ style: { padding: 16, fontSize: 13, lineHeight: 1.6 } },
|
|
145
|
+
{ style: { padding: 16, fontSize: 13, lineHeight: 1.6, color: colors.text } },
|
|
150
146
|
h('strong', null, 'The pxsig server is not running.'),
|
|
151
147
|
h('div', null, 'Start it from the repo root, then this panel connects on its own:'),
|
|
152
148
|
h('code', { style: { display: 'block', marginTop: 8 } }, 'npx pxsig serve --watch')
|
|
153
149
|
);
|
|
154
150
|
return h(
|
|
155
151
|
'div',
|
|
156
|
-
{ style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },
|
|
152
|
+
{ style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, color: colors.text } },
|
|
157
153
|
h(
|
|
158
154
|
'div',
|
|
159
|
-
{ style: { display: 'flex', gap: 8, alignItems: 'center', padding: '6px 10px', borderBottom:
|
|
155
|
+
{ style: { display: 'flex', gap: 8, alignItems: 'center', padding: '6px 10px', borderBottom: `1px solid ${colors.border}`, fontSize: 12 } },
|
|
160
156
|
h('strong', null, st ? `pxsig: ${st.status}` : 'pxsig: no result yet'),
|
|
161
157
|
st ? h('span', { style: { opacity: 0.7 } }, st.entries.map((e) => `${e.mode} ${e.state}`).join(' · ')) : null,
|
|
162
158
|
ig && ig.status !== 'ok'
|
|
@@ -173,12 +169,12 @@ function Panel() {
|
|
|
173
169
|
state.running ? h('span', { style: { opacity: 0.7 } }, 'Checking…') : null,
|
|
174
170
|
btn('Check story', () => post({ story: storyId }), state.running),
|
|
175
171
|
btn('Check all', () => post({ all: true }), state.running),
|
|
176
|
-
btn('Open dashboard', () => window.open(
|
|
172
|
+
btn('Open dashboard', () => window.open(dashboardUrl(SERVER, { project: st?.project ?? null, story: storyId, hash: `inspect=${encodeURIComponent(st?.entries[0]?.id ?? storyId)}` }, mode), '_blank'))
|
|
177
173
|
),
|
|
178
174
|
h('iframe', {
|
|
179
|
-
key: `${storyId}`,
|
|
175
|
+
key: `${storyId}:${mode}`,
|
|
180
176
|
title: 'pxsig inspector',
|
|
181
|
-
src:
|
|
177
|
+
src: dashboardUrl(SERVER, { embed: 1, story: storyId }, mode),
|
|
182
178
|
style: { flex: 1, minHeight: 0, border: 0, width: '100%' },
|
|
183
179
|
})
|
|
184
180
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pxsig/storybook-addon",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "pxsig in Storybook: a visual-regression panel and sidebar statuses, fed by the pxsig server.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -17,9 +17,13 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"preset.cjs",
|
|
19
19
|
"manager.mjs",
|
|
20
|
+
"panel-style.mjs",
|
|
20
21
|
"README.md"
|
|
21
22
|
],
|
|
22
23
|
"peerDependencies": {
|
|
23
24
|
"storybook": ">=9"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "node --test test/*.test.mjs"
|
|
24
28
|
}
|
|
25
29
|
}
|
package/panel-style.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel's look, from Storybook's own theme (`useTheme()` from storybook/theming), so the
|
|
3
|
+
* toolbar and its buttons follow Storybook's light or dark theme, and the embedded inspector and
|
|
4
|
+
* the dashboard it opens start in the same one. Plain functions: no Storybook import here.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** 'dark' or 'light': the theme's base, else read from its content background. */
|
|
8
|
+
export function themeMode(theme) {
|
|
9
|
+
if (theme?.base === 'dark' || theme?.base === 'light') return theme.base;
|
|
10
|
+
const bg = String(theme?.background?.content ?? theme?.appContentBg ?? '');
|
|
11
|
+
const m = bg.match(/^#([0-9a-f]{6})$/i);
|
|
12
|
+
if (!m) return 'light';
|
|
13
|
+
const n = Number.parseInt(m[1], 16);
|
|
14
|
+
const lum = 0.2126 * ((n >> 16) & 255) + 0.7152 * ((n >> 8) & 255) + 0.0722 * (n & 255);
|
|
15
|
+
return lum < 128 ? 'dark' : 'light';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Text and border colours for the panel, with neutral fallbacks for a theme that lacks them. */
|
|
19
|
+
export function panelColors(theme) {
|
|
20
|
+
return {
|
|
21
|
+
text: theme?.color?.defaultText ?? theme?.textColor ?? 'inherit',
|
|
22
|
+
muted: theme?.barTextColor ?? theme?.textMutedColor ?? 'inherit',
|
|
23
|
+
border: theme?.appBorderColor ?? 'rgba(128,128,128,.4)',
|
|
24
|
+
background: theme?.background?.content ?? theme?.appContentBg ?? 'transparent',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A toolbar button styled like Storybook's inputs in the current theme. */
|
|
29
|
+
export function buttonStyle(theme, disabled = false) {
|
|
30
|
+
const c = panelColors(theme);
|
|
31
|
+
return {
|
|
32
|
+
font: 'inherit',
|
|
33
|
+
fontSize: 12,
|
|
34
|
+
padding: '4px 10px',
|
|
35
|
+
borderRadius: theme?.input?.borderRadius ?? theme?.appBorderRadius ?? 4,
|
|
36
|
+
border: `1px solid ${theme?.input?.border ?? c.border}`,
|
|
37
|
+
background: theme?.input?.background ?? 'transparent',
|
|
38
|
+
color: theme?.input?.color ?? c.text,
|
|
39
|
+
cursor: disabled ? 'default' : 'pointer',
|
|
40
|
+
opacity: disabled ? 0.5 : 1,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A dashboard URL on `server` with Storybook's theme mode added (`theme=dark|light`). */
|
|
45
|
+
export function dashboardUrl(server, params, mode) {
|
|
46
|
+
const q = new URLSearchParams();
|
|
47
|
+
for (const [k, v] of Object.entries(params)) if (v != null && k !== 'hash') q.set(k, String(v));
|
|
48
|
+
q.set('theme', mode);
|
|
49
|
+
return `${server}/report/?${q.toString()}${params.hash ? `#${params.hash}` : ''}`;
|
|
50
|
+
}
|