@repobuddy/storybook 0.10.0 → 0.11.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.
@@ -1,2 +1 @@
1
1
  export * from './brand_title.ts';
2
- export * from './tag_badges.ts';
@@ -1,2 +1 @@
1
1
  export * from "./brand_title.js";
2
- export * from "./tag_badges.js";
@@ -0,0 +1 @@
1
+ export * from './tag_badges.ts';
@@ -0,0 +1 @@
1
+ export * from "./tag_badges.js";
@@ -0,0 +1,35 @@
1
+ import { type TagBadgeParameters } from 'storybook-addon-tag-badges';
2
+ type TagBadgeParameter = TagBadgeParameters[0];
3
+ /**
4
+ * Configuration for story tag badges that appear in the Storybook sidebar.
5
+ * Each badge is associated with a specific tag and displays an emoji with a tooltip.
6
+ *
7
+ * The badges help visually identify stories with certain characteristics:
8
+ * - ✏️ Editor - Stories with live editor
9
+ * - 🆕 New - Recently added stories
10
+ * - 🅱️ Beta - Stories for features in beta
11
+ * - 🪦 Deprecated - Stories for deprecated features
12
+ * - ⚠️ Outdated - Stories that need updating
13
+ * - 🚨 Danger - Stories demonstrating dangerous patterns
14
+ * - 📋 Todo - Stories marked as todo/incomplete
15
+ * - 📝 Code Only - Stories without visual examples
16
+ * - 📸 Snapshot - Stories with snapshot tests
17
+ * - 🧪 Unit - Stories with unit tests
18
+ * - 🔗 Integration - Stories with integration tests
19
+ *
20
+ * Also includes the default version badge from `storybook-addon-tag-badges`.
21
+ */
22
+ export declare const editorBadge: TagBadgeParameter;
23
+ export declare const newBadge: TagBadgeParameter;
24
+ export declare const betaBadge: TagBadgeParameter;
25
+ export declare const propsBadge: TagBadgeParameter;
26
+ export declare const deprecatedBadge: TagBadgeParameter;
27
+ export declare const outdatedBadge: TagBadgeParameter;
28
+ export declare const dangerBadge: TagBadgeParameter;
29
+ export declare const todoBadge: TagBadgeParameter;
30
+ export declare const codeOnlyBadge: TagBadgeParameter;
31
+ export declare const snapshotBadge: TagBadgeParameter;
32
+ export declare const unitBadge: TagBadgeParameter;
33
+ export declare const integrationBadge: TagBadgeParameter;
34
+ export declare const tagBadges: TagBadgeParameters;
35
+ export {};
@@ -50,6 +50,15 @@ export const betaBadge = {
50
50
  tooltip: 'Beta'
51
51
  }
52
52
  };
53
+ export const propsBadge = {
54
+ tags: 'props',
55
+ badge: {
56
+ text: '🔧',
57
+ bgColor: 'transparent',
58
+ borderColor: 'transparent',
59
+ tooltip: 'Props'
60
+ }
61
+ };
53
62
  export const deprecatedBadge = {
54
63
  tags: 'deprecated',
55
64
  badge: {
@@ -104,7 +113,7 @@ export const snapshotBadge = {
104
113
  tooltip: 'Snapshot Test'
105
114
  },
106
115
  display: {
107
- sidebar: ['story'],
116
+ sidebar: false,
108
117
  toolbar: ['story']
109
118
  }
110
119
  };
@@ -115,6 +124,9 @@ export const unitBadge = {
115
124
  bgColor: 'transparent',
116
125
  borderColor: 'transparent',
117
126
  tooltip: 'Unit Test'
127
+ },
128
+ display: {
129
+ sidebar: false
118
130
  }
119
131
  };
120
132
  export const integrationBadge = {
@@ -124,6 +136,9 @@ export const integrationBadge = {
124
136
  bgColor: 'transparent',
125
137
  borderColor: 'transparent',
126
138
  tooltip: 'Integration Test'
139
+ },
140
+ display: {
141
+ sidebar: false
127
142
  }
128
143
  };
129
144
  export const tagBadges = [
@@ -135,6 +150,7 @@ export const tagBadges = [
135
150
  deprecatedBadge,
136
151
  outdatedBadge,
137
152
  dangerBadge,
153
+ propsBadge,
138
154
  todoBadge,
139
155
  codeOnlyBadge,
140
156
  snapshotBadge,
@@ -19,8 +19,8 @@ import { type PropsWithChildren } from 'react';
19
19
  * ```
20
20
  */
21
21
  export declare function createDarkModeDocsContainer(customThemes?: {
22
- light: ThemeVars;
23
- dark: ThemeVars;
24
- }): (props: PropsWithChildren<{
22
+ light?: ThemeVars | undefined;
23
+ dark?: ThemeVars | undefined;
24
+ } | undefined): (props: PropsWithChildren<{
25
25
  context: DocsContextProps;
26
26
  }>) => import("react/jsx-runtime").JSX.Element;
@@ -20,13 +20,13 @@ import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
20
20
  * }
21
21
  * ```
22
22
  */
23
- export function createDarkModeDocsContainer(customThemes = themes) {
23
+ export function createDarkModeDocsContainer(customThemes) {
24
24
  return function DarkModeDocsContainer(props) {
25
25
  const [isDark, setDark] = useState(true);
26
26
  useEffect(() => {
27
27
  props.context.channel.on(DARK_MODE_EVENT_NAME, setDark);
28
28
  return () => props.context.channel.removeListener(DARK_MODE_EVENT_NAME, setDark);
29
29
  }, [props.context.channel]);
30
- return (_jsx(DocsContainer, { ...props, theme: isDark ? customThemes.dark : customThemes.light, children: props.children }));
30
+ return (_jsx(DocsContainer, { ...props, theme: isDark ? (customThemes?.dark ?? themes.dark) : (customThemes?.light ?? themes.light), children: props.children }));
31
31
  };
32
32
  }
@@ -1,37 +1,53 @@
1
- import type { ThemeVars } from '@storybook/theming';
1
+ import type { CSSProperties } from '@just-web/css';
2
+ import type { ThemeVars } from 'storybook/internal/theming';
2
3
  /**
3
4
  * Configuration parameters for `storybook-dark-mode`.
4
5
  */
5
6
  export interface DarkModeParam {
6
7
  /** The current theme ('dark' or 'light') */
7
- current?: 'dark' | 'light';
8
- /** CSS class(es) to apply in dark mode */
9
- darkClass?: string | string[];
8
+ current?: 'dark' | 'light' | undefined;
9
+ /**
10
+ * CSS class(es) to apply in dark mode.
11
+ */
12
+ darkClass?: string | string[] | undefined;
13
+ /**
14
+ * CSS style(s) to apply in dark mode.
15
+ *
16
+ * This only works when using `withStoryRoot`.
17
+ */
18
+ darkStyle?: CSSProperties | undefined;
10
19
  /** CSS class(es) to apply in light mode */
11
- lightClass?: string | string[];
20
+ lightClass?: string | string[] | undefined;
21
+ /**
22
+ * CSS style(s) to apply in light mode.
23
+ *
24
+ * This only works when using `withStoryRoot`.
25
+ */
26
+ lightStyle?: CSSProperties | undefined;
12
27
  /** Dark theme variables */
13
28
  dark?: ThemeVars;
14
29
  /** Light theme variables */
15
30
  light?: ThemeVars;
16
31
  /** Element to apply theme classes to ('html' or 'body') */
17
- classTarget?: 'html' | 'body';
32
+ classTarget?: 'html' | 'body' | undefined;
18
33
  /** Whether to apply theme styles to preview iframe */
19
- stylePreview?: boolean;
34
+ stylePreview?: boolean | undefined;
20
35
  }
21
36
  /**
22
37
  * Defines `storybook-dark-mode` parameters for Storybook stories.
23
38
  *
24
39
  * @see https://storybook.js.org/addons/@storybook/addon-themes#dark-mode
25
- *
26
- * @param darkMode - Configuration for dark mode parameters
27
- * @param darkMode.current - The current theme ('dark' or 'light')
28
- * @param darkMode.darkClass - CSS class(es) to apply in dark mode
29
- * @param darkMode.lightClass - CSS class(es) to apply in light mode
30
- * @param darkMode.dark - Dark theme variables
31
- * @param darkMode.light - Light theme variables
32
- * @param darkMode.classTarget - Element to apply theme classes to ('html' or 'body')
33
- * @param darkMode.stylePreview - Whether to apply theme styles to preview iframe
34
40
  * @returns An object containing the dark mode parameter configuration
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * // ./storybook/preview.tsx
45
+ *
46
+ * export const preview = {
47
+ * parameters: {
48
+ * ...defineDarkModeParam({ darkClass: 'dark' })
49
+ * }
50
+ * }
35
51
  */
36
52
  export declare function defineDarkModeParam(darkMode: DarkModeParam): {
37
53
  darkMode: DarkModeParam;
@@ -2,16 +2,17 @@
2
2
  * Defines `storybook-dark-mode` parameters for Storybook stories.
3
3
  *
4
4
  * @see https://storybook.js.org/addons/@storybook/addon-themes#dark-mode
5
- *
6
- * @param darkMode - Configuration for dark mode parameters
7
- * @param darkMode.current - The current theme ('dark' or 'light')
8
- * @param darkMode.darkClass - CSS class(es) to apply in dark mode
9
- * @param darkMode.lightClass - CSS class(es) to apply in light mode
10
- * @param darkMode.dark - Dark theme variables
11
- * @param darkMode.light - Light theme variables
12
- * @param darkMode.classTarget - Element to apply theme classes to ('html' or 'body')
13
- * @param darkMode.stylePreview - Whether to apply theme styles to preview iframe
14
5
  * @returns An object containing the dark mode parameter configuration
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // ./storybook/preview.tsx
10
+ *
11
+ * export const preview = {
12
+ * parameters: {
13
+ * ...defineDarkModeParam({ darkClass: 'dark' })
14
+ * }
15
+ * }
15
16
  */
16
17
  export function defineDarkModeParam(darkMode) {
17
18
  return { darkMode };
@@ -1,3 +1,3 @@
1
1
  export * from './dark_mode_docs_container.tsx';
2
2
  export * from './define_dark_mode.ts';
3
- export * from './with_story_root.tsx';
3
+ export * from './with_dark_mode.tsx';
@@ -1,3 +1,3 @@
1
1
  export * from "./dark_mode_docs_container.js";
2
2
  export * from "./define_dark_mode.js";
3
- export * from "./with_story_root.js";
3
+ export * from "./with_dark_mode.js";
@@ -0,0 +1,22 @@
1
+ import type { CSSProperties } from '@just-web/css';
2
+ import type { DecoratorFunction } from 'storybook/internal/types';
3
+ /**
4
+ * Applies additional dark mode behavior.
5
+ *
6
+ * @see https://storybook.js.org/addons/@storybook/addon-themes#dark-mode
7
+ * @returns A decorator function that applies additional dark mode behavior.
8
+ */
9
+ export declare function withDarkMode(options?: {
10
+ /**
11
+ * CSS class(es) to apply to the body element.
12
+ *
13
+ * Useful when using Tailwind CSS and `classTarget: 'html'`
14
+ */
15
+ bodyClass?: string | string[] | undefined;
16
+ /**
17
+ * CSS style(s) to apply to the body element.
18
+ *
19
+ * Useful when using Tailwind CSS and `classTarget: 'html'`
20
+ */
21
+ bodyStyle?: CSSProperties | undefined;
22
+ } | undefined): DecoratorFunction<any, any>;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { toDOMStyle } from '@just-web/css';
3
+ import { useDarkMode } from 'storybook-dark-mode';
4
+ /**
5
+ * Applies additional dark mode behavior.
6
+ *
7
+ * @see https://storybook.js.org/addons/@storybook/addon-themes#dark-mode
8
+ * @returns A decorator function that applies additional dark mode behavior.
9
+ */
10
+ export function withDarkMode(options) {
11
+ return function darkModeDecorator(Story, { parameters }) {
12
+ const darkMode = parameters.darkMode;
13
+ if (!darkMode)
14
+ return _jsx(Story, {});
15
+ const dark = useDarkMode();
16
+ const target = darkMode.classTarget === 'html' ? document.documentElement : document.body;
17
+ if (dark) {
18
+ removeClass(target, darkMode.lightClass);
19
+ addClass(target, darkMode.darkClass);
20
+ removeStyle(target, darkMode.lightStyle);
21
+ addStyle(target, darkMode.darkStyle);
22
+ }
23
+ else {
24
+ removeClass(target, darkMode.darkClass);
25
+ addClass(target, darkMode.lightClass);
26
+ removeStyle(target, darkMode.darkStyle);
27
+ addStyle(target, darkMode.lightStyle);
28
+ }
29
+ if (options?.bodyClass) {
30
+ addClass(document.body, options.bodyClass);
31
+ }
32
+ if (options?.bodyStyle) {
33
+ addStyle(document.body, options.bodyStyle);
34
+ }
35
+ return _jsx(Story, {});
36
+ };
37
+ }
38
+ function addClass(target, className) {
39
+ if (!className)
40
+ return;
41
+ if (typeof className === 'string') {
42
+ target.classList.add(className);
43
+ }
44
+ else if (Array.isArray(className)) {
45
+ target.classList.add(...className);
46
+ }
47
+ }
48
+ function removeClass(target, className) {
49
+ if (!className)
50
+ return;
51
+ if (typeof className === 'string') {
52
+ target.classList.remove(className);
53
+ }
54
+ else if (Array.isArray(className)) {
55
+ target.classList.remove(...className);
56
+ }
57
+ }
58
+ function addStyle(target, style) {
59
+ if (style) {
60
+ for (const [key, value] of Object.entries(toDOMStyle(style))) {
61
+ target.style.setProperty(key, value);
62
+ }
63
+ }
64
+ }
65
+ function removeStyle(target, style) {
66
+ if (style) {
67
+ for (const key of Object.keys(toDOMStyle(style))) {
68
+ target.style.removeProperty(key);
69
+ }
70
+ }
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repobuddy/storybook",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Storybook repo buddy",
5
5
  "keywords": [
6
6
  "storybook",
@@ -23,6 +23,10 @@
23
23
  "types": "./esm/manager/index.d.ts",
24
24
  "default": "./esm/manager/index.js"
25
25
  },
26
+ "./storybook-addon-tag-badges": {
27
+ "types": "./esm/storybook-addon-tag-badges/index.d.ts",
28
+ "default": "./esm/storybook-addon-tag-badges/index.js"
29
+ },
26
30
  "./storybook-dark-mode": {
27
31
  "types": "./esm/storybook-dark-mode/index.d.ts",
28
32
  "default": "./esm/storybook-dark-mode/index.js"
package/readme.md CHANGED
@@ -23,7 +23,7 @@ For example:
23
23
  import { defineLayoutParam } from '@repobuddy/storybook'
24
24
 
25
25
  export const MyStory: StoryObj = {
26
- parameters: defineLayoutParam('fullscreen')
26
+ parameters: defineLayoutParam('fullscreen')
27
27
  }
28
28
  ```
29
29
 
@@ -34,13 +34,13 @@ and also allow you to specify additional parameter types.
34
34
  import { defineParameters, type ActionsParam } from '@repobuddy/storybook'
35
35
 
36
36
  export const MyStory: StoryObj = {
37
- parameters: defineParameters<ActionsParam>({
38
- layout: 'fullscreen',
39
- // this is typed
40
- actions: {
41
- disable: true
42
- }
43
- })
37
+ parameters: defineParameters<ActionsParam>({
38
+ layout: 'fullscreen',
39
+ // this is typed
40
+ actions: {
41
+ disable: true
42
+ }
43
+ })
44
44
  }
45
45
  ```
46
46
 
@@ -59,7 +59,7 @@ addons.setConfig({
59
59
  })
60
60
  ```
61
61
 
62
- ### Tag Badges
62
+ ### `storybook-addon-tag-badges`
63
63
 
64
64
  If you use [`storybook-addon-tag-badges`][`storybook-addon-tag-badges`],
65
65
  we provide a different set of badges that uses emojis:
@@ -80,51 +80,50 @@ we provide a different set of badges that uses emojis:
80
80
  To use them, add them to your `.storybook/manager.ts`:
81
81
 
82
82
  ```ts
83
- import { tagBadges } from '@repobuddy/storybook/manager'
83
+ import { tagBadges } from '@repobuddy/storybook/storybook-addon-tag-badges'
84
84
  import { addons } from '@storybook/manager-api'
85
85
 
86
86
  addons.setConfig({ tagBadges })
87
87
  ```
88
88
 
89
- ### `storybook-dark-mode` support
89
+ You can also import only the one you need:
90
+
91
+ ```ts
92
+ import { newBadge } from '@repobuddy/storybook/storybook-addon-tag-badges'
93
+ import { defaultConfig } from 'storybook-addon-tag-badges'
94
+
95
+ addons.setConfig({ tagBadges: [newBadge, ...defaultConfig] })
96
+ ```
97
+
98
+ ### `storybook-dark-mode`
90
99
 
91
- [`@repobuddy/storybook`][`@repobuddy/storybook`] provides a few utilities to work with `storybook-dark-mode`.
100
+ [`@repobuddy/storybook`][`@repobuddy/storybook`] provides a few utilities to work with [`storybook-dark-mode`][`storybook-dark-mode`].
92
101
 
93
102
  ```ts
94
103
  // .storybook/preview.tsx
95
- import { defineDarkModeParam, withStoryRoot, createDarkModeDocsContainer } from '@repobuddy/storybook/storybook-dark-mode'
104
+ import {
105
+ createDarkModeDocsContainer,
106
+ defineDarkModeParam,
107
+ withDarkMode
108
+ } from '@repobuddy/storybook/storybook-dark-mode'
96
109
 
97
110
  export const preview: Preview = {
98
- parameters: {
99
- docs: {
100
- container: createDarkModeDocsContainer()
101
- },
111
+ parameters: {
112
+ docs: {
113
+ container: createDarkModeDocsContainer()
114
+ },
102
115
  darkMode: defineDarkModeParam({
103
116
  classTarget: 'html',
104
117
  darkClass: 'dark',
105
118
  stylePreview: true
106
119
  })
107
- },
108
- decorators: [withStoryRoot({
109
- classTarget: 'html',
110
- dark: {
111
- className: 'dark:bg-black dark:text-white'
112
- }
120
+ },
121
+ decorators: [withDarkMode({
122
+ bodyClass: 'dark:bg-black dark:text-white'
113
123
  })]
114
124
  }
115
125
  ```
116
126
 
117
- #### `withStoryRoot`
118
-
119
- The `withStoryRoot` decorator allows you to use `storybook-dark-mode` to change the background color of the story.
120
-
121
- ```ts
122
- import { withStoryRoot } from '@repobuddy/storybook/storybook-dark-mode'
123
-
124
- export const MyStory: StoryObj = {
125
- decorators: [withStoryRoot()]
126
- }
127
- ```
128
-
129
127
  [`@repobuddy/storybook`]: https://github.com/repobuddy/storybook
130
128
  [`storybook-addon-tag-badges`]: https://github.com/Sidnioulz/storybook-addon-tag-badges
129
+ [`storybook-dark-mode`]: https://github.com/hipstersmoothie/storybook-dark-mode
@@ -1,2 +1 @@
1
1
  export * from './brand_title.ts'
2
- export * from './tag_badges.ts'
@@ -0,0 +1 @@
1
+ export * from './tag_badges.ts'
@@ -2,6 +2,8 @@ import { type TagBadgeParameters, defaultConfig } from 'storybook-addon-tag-badg
2
2
 
3
3
  const [, , , , , , versionBadge] = defaultConfig
4
4
 
5
+ type TagBadgeParameter = TagBadgeParameters[0]
6
+
5
7
  /**
6
8
  * Configuration for story tag badges that appear in the Storybook sidebar.
7
9
  * Each badge is associated with a specific tag and displays an emoji with a tooltip.
@@ -21,7 +23,7 @@ const [, , , , , , versionBadge] = defaultConfig
21
23
  *
22
24
  * Also includes the default version badge from `storybook-addon-tag-badges`.
23
25
  */
24
- export const editorBadge = {
26
+ export const editorBadge: TagBadgeParameter = {
25
27
  tags: 'editor',
26
28
  badge: {
27
29
  text: '✏️',
@@ -35,7 +37,7 @@ export const editorBadge = {
35
37
  }
36
38
  }
37
39
 
38
- export const newBadge = {
40
+ export const newBadge: TagBadgeParameter = {
39
41
  tags: 'new',
40
42
  badge: {
41
43
  text: '🆕',
@@ -45,7 +47,7 @@ export const newBadge = {
45
47
  }
46
48
  }
47
49
 
48
- export const betaBadge = {
50
+ export const betaBadge: TagBadgeParameter = {
49
51
  tags: 'beta',
50
52
  badge: {
51
53
  text: '🅱️',
@@ -55,7 +57,17 @@ export const betaBadge = {
55
57
  }
56
58
  }
57
59
 
58
- export const deprecatedBadge = {
60
+ export const propsBadge: TagBadgeParameter = {
61
+ tags: 'props',
62
+ badge: {
63
+ text: '🔧',
64
+ bgColor: 'transparent',
65
+ borderColor: 'transparent',
66
+ tooltip: 'Props'
67
+ }
68
+ }
69
+
70
+ export const deprecatedBadge: TagBadgeParameter = {
59
71
  tags: 'deprecated',
60
72
  badge: {
61
73
  text: '🪦',
@@ -65,7 +77,7 @@ export const deprecatedBadge = {
65
77
  }
66
78
  }
67
79
 
68
- export const outdatedBadge = {
80
+ export const outdatedBadge: TagBadgeParameter = {
69
81
  tags: 'outdated',
70
82
  badge: {
71
83
  text: '⚠️',
@@ -75,7 +87,7 @@ export const outdatedBadge = {
75
87
  }
76
88
  }
77
89
 
78
- export const dangerBadge = {
90
+ export const dangerBadge: TagBadgeParameter = {
79
91
  tags: 'danger',
80
92
  badge: {
81
93
  text: '🚨',
@@ -85,7 +97,7 @@ export const dangerBadge = {
85
97
  }
86
98
  }
87
99
 
88
- export const todoBadge = {
100
+ export const todoBadge: TagBadgeParameter = {
89
101
  tags: 'todo',
90
102
  badge: {
91
103
  text: '📋',
@@ -95,7 +107,7 @@ export const todoBadge = {
95
107
  }
96
108
  }
97
109
 
98
- export const codeOnlyBadge = {
110
+ export const codeOnlyBadge: TagBadgeParameter = {
99
111
  tags: 'code-only',
100
112
  badge: {
101
113
  text: '📝',
@@ -105,7 +117,7 @@ export const codeOnlyBadge = {
105
117
  }
106
118
  }
107
119
 
108
- export const snapshotBadge = {
120
+ export const snapshotBadge: TagBadgeParameter = {
109
121
  tags: 'snapshot',
110
122
  badge: {
111
123
  text: '📸',
@@ -114,28 +126,34 @@ export const snapshotBadge = {
114
126
  tooltip: 'Snapshot Test'
115
127
  },
116
128
  display: {
117
- sidebar: ['story'],
129
+ sidebar: false,
118
130
  toolbar: ['story']
119
131
  }
120
132
  }
121
133
 
122
- export const unitBadge = {
134
+ export const unitBadge: TagBadgeParameter = {
123
135
  tags: 'unit',
124
136
  badge: {
125
137
  text: '🧪',
126
138
  bgColor: 'transparent',
127
139
  borderColor: 'transparent',
128
140
  tooltip: 'Unit Test'
141
+ },
142
+ display: {
143
+ sidebar: false
129
144
  }
130
145
  }
131
146
 
132
- export const integrationBadge = {
147
+ export const integrationBadge: TagBadgeParameter = {
133
148
  tags: 'integration',
134
149
  badge: {
135
150
  text: '🔗',
136
151
  bgColor: 'transparent',
137
152
  borderColor: 'transparent',
138
153
  tooltip: 'Integration Test'
154
+ },
155
+ display: {
156
+ sidebar: false
139
157
  }
140
158
  }
141
159
 
@@ -148,6 +166,7 @@ export const tagBadges: TagBadgeParameters = [
148
166
  deprecatedBadge,
149
167
  outdatedBadge,
150
168
  dangerBadge,
169
+ propsBadge,
151
170
  todoBadge,
152
171
  codeOnlyBadge,
153
172
  snapshotBadge,
@@ -21,10 +21,12 @@ import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode'
21
21
  * ```
22
22
  */
23
23
  export function createDarkModeDocsContainer(
24
- customThemes: {
25
- light: ThemeVars
26
- dark: ThemeVars
27
- } = themes
24
+ customThemes?:
25
+ | {
26
+ light?: ThemeVars | undefined
27
+ dark?: ThemeVars | undefined
28
+ }
29
+ | undefined
28
30
  ) {
29
31
  return function DarkModeDocsContainer(props: PropsWithChildren<{ context: DocsContextProps }>) {
30
32
  const [isDark, setDark] = useState(true)
@@ -35,7 +37,10 @@ export function createDarkModeDocsContainer(
35
37
  return () => props.context.channel.removeListener(DARK_MODE_EVENT_NAME, setDark)
36
38
  }, [props.context.channel])
37
39
  return (
38
- <DocsContainer {...props} theme={isDark ? customThemes.dark : customThemes.light}>
40
+ <DocsContainer
41
+ {...props}
42
+ theme={isDark ? (customThemes?.dark ?? themes.dark) : (customThemes?.light ?? themes.light)}
43
+ >
39
44
  {props.children}
40
45
  </DocsContainer>
41
46
  )
@@ -1,39 +1,55 @@
1
- import type { ThemeVars } from '@storybook/theming'
1
+ import type { CSSProperties } from '@just-web/css'
2
+ import type { ThemeVars } from 'storybook/internal/theming'
2
3
 
3
4
  /**
4
5
  * Configuration parameters for `storybook-dark-mode`.
5
6
  */
6
7
  export interface DarkModeParam {
7
8
  /** The current theme ('dark' or 'light') */
8
- current?: 'dark' | 'light'
9
- /** CSS class(es) to apply in dark mode */
10
- darkClass?: string | string[]
9
+ current?: 'dark' | 'light' | undefined
10
+ /**
11
+ * CSS class(es) to apply in dark mode.
12
+ */
13
+ darkClass?: string | string[] | undefined
14
+ /**
15
+ * CSS style(s) to apply in dark mode.
16
+ *
17
+ * This only works when using `withStoryRoot`.
18
+ */
19
+ darkStyle?: CSSProperties | undefined
11
20
  /** CSS class(es) to apply in light mode */
12
- lightClass?: string | string[]
21
+ lightClass?: string | string[] | undefined
22
+ /**
23
+ * CSS style(s) to apply in light mode.
24
+ *
25
+ * This only works when using `withStoryRoot`.
26
+ */
27
+ lightStyle?: CSSProperties | undefined
13
28
  /** Dark theme variables */
14
29
  dark?: ThemeVars
15
30
  /** Light theme variables */
16
31
  light?: ThemeVars
17
32
  /** Element to apply theme classes to ('html' or 'body') */
18
- classTarget?: 'html' | 'body'
33
+ classTarget?: 'html' | 'body' | undefined
19
34
  /** Whether to apply theme styles to preview iframe */
20
- stylePreview?: boolean
35
+ stylePreview?: boolean | undefined
21
36
  }
22
37
 
23
38
  /**
24
39
  * Defines `storybook-dark-mode` parameters for Storybook stories.
25
40
  *
26
41
  * @see https://storybook.js.org/addons/@storybook/addon-themes#dark-mode
27
- *
28
- * @param darkMode - Configuration for dark mode parameters
29
- * @param darkMode.current - The current theme ('dark' or 'light')
30
- * @param darkMode.darkClass - CSS class(es) to apply in dark mode
31
- * @param darkMode.lightClass - CSS class(es) to apply in light mode
32
- * @param darkMode.dark - Dark theme variables
33
- * @param darkMode.light - Light theme variables
34
- * @param darkMode.classTarget - Element to apply theme classes to ('html' or 'body')
35
- * @param darkMode.stylePreview - Whether to apply theme styles to preview iframe
36
42
  * @returns An object containing the dark mode parameter configuration
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * // ./storybook/preview.tsx
47
+ *
48
+ * export const preview = {
49
+ * parameters: {
50
+ * ...defineDarkModeParam({ darkClass: 'dark' })
51
+ * }
52
+ * }
37
53
  */
38
54
  export function defineDarkModeParam(darkMode: DarkModeParam) {
39
55
  return { darkMode }
@@ -1,3 +1,3 @@
1
1
  export * from './dark_mode_docs_container.tsx'
2
2
  export * from './define_dark_mode.ts'
3
- export * from './with_story_root.tsx'
3
+ export * from './with_dark_mode.tsx'
@@ -0,0 +1,90 @@
1
+ import type { CSSProperties } from '@just-web/css'
2
+ import { toDOMStyle } from '@just-web/css'
3
+ import { useDarkMode } from 'storybook-dark-mode'
4
+ import type { DecoratorFunction } from 'storybook/internal/types'
5
+ import type { DarkModeParam } from './define_dark_mode.ts'
6
+
7
+ /**
8
+ * Applies additional dark mode behavior.
9
+ *
10
+ * @see https://storybook.js.org/addons/@storybook/addon-themes#dark-mode
11
+ * @returns A decorator function that applies additional dark mode behavior.
12
+ */
13
+ export function withDarkMode(
14
+ options?:
15
+ | {
16
+ /**
17
+ * CSS class(es) to apply to the body element.
18
+ *
19
+ * Useful when using Tailwind CSS and `classTarget: 'html'`
20
+ */
21
+ bodyClass?: string | string[] | undefined
22
+ /**
23
+ * CSS style(s) to apply to the body element.
24
+ *
25
+ * Useful when using Tailwind CSS and `classTarget: 'html'`
26
+ */
27
+ bodyStyle?: CSSProperties | undefined
28
+ }
29
+ | undefined
30
+ ): DecoratorFunction<any, any> {
31
+ return function darkModeDecorator(Story, { parameters }) {
32
+ const darkMode = parameters.darkMode as DarkModeParam | undefined
33
+ if (!darkMode) return <Story />
34
+
35
+ const dark = useDarkMode()
36
+ const target = darkMode.classTarget === 'html' ? document.documentElement : document.body
37
+ if (dark) {
38
+ removeClass(target, darkMode.lightClass)
39
+ addClass(target, darkMode.darkClass)
40
+ removeStyle(target, darkMode.lightStyle)
41
+ addStyle(target, darkMode.darkStyle)
42
+ } else {
43
+ removeClass(target, darkMode.darkClass)
44
+ addClass(target, darkMode.lightClass)
45
+ removeStyle(target, darkMode.darkStyle)
46
+ addStyle(target, darkMode.lightStyle)
47
+ }
48
+ if (options?.bodyClass) {
49
+ addClass(document.body, options.bodyClass)
50
+ }
51
+ if (options?.bodyStyle) {
52
+ addStyle(document.body, options.bodyStyle)
53
+ }
54
+ return <Story />
55
+ }
56
+ }
57
+
58
+ function addClass(target: HTMLElement, className: string | string[] | undefined) {
59
+ if (!className) return
60
+ if (typeof className === 'string') {
61
+ target.classList.add(className)
62
+ } else if (Array.isArray(className)) {
63
+ target.classList.add(...className)
64
+ }
65
+ }
66
+
67
+ function removeClass(target: HTMLElement, className: string | string[] | undefined) {
68
+ if (!className) return
69
+ if (typeof className === 'string') {
70
+ target.classList.remove(className)
71
+ } else if (Array.isArray(className)) {
72
+ target.classList.remove(...className)
73
+ }
74
+ }
75
+
76
+ function addStyle(target: HTMLElement, style: CSSProperties | undefined) {
77
+ if (style) {
78
+ for (const [key, value] of Object.entries(toDOMStyle(style)!)) {
79
+ target.style.setProperty(key, value as any)
80
+ }
81
+ }
82
+ }
83
+
84
+ function removeStyle(target: HTMLElement, style: CSSProperties | undefined) {
85
+ if (style) {
86
+ for (const key of Object.keys(toDOMStyle(style)!)) {
87
+ target.style.removeProperty(key)
88
+ }
89
+ }
90
+ }
@@ -1,128 +0,0 @@
1
- import { type TagBadgeParameters } from 'storybook-addon-tag-badges';
2
- /**
3
- * Configuration for story tag badges that appear in the Storybook sidebar.
4
- * Each badge is associated with a specific tag and displays an emoji with a tooltip.
5
- *
6
- * The badges help visually identify stories with certain characteristics:
7
- * - ✏️ Editor - Stories with live editor
8
- * - 🆕 New - Recently added stories
9
- * - 🅱️ Beta - Stories for features in beta
10
- * - 🪦 Deprecated - Stories for deprecated features
11
- * - ⚠️ Outdated - Stories that need updating
12
- * - 🚨 Danger - Stories demonstrating dangerous patterns
13
- * - 📋 Todo - Stories marked as todo/incomplete
14
- * - 📝 Code Only - Stories without visual examples
15
- * - 📸 Snapshot - Stories with snapshot tests
16
- * - 🧪 Unit - Stories with unit tests
17
- * - 🔗 Integration - Stories with integration tests
18
- *
19
- * Also includes the default version badge from `storybook-addon-tag-badges`.
20
- */
21
- export declare const editorBadge: {
22
- tags: string;
23
- badge: {
24
- text: string;
25
- bgColor: string;
26
- borderColor: string;
27
- tooltip: string;
28
- };
29
- display: {
30
- sidebar: string[];
31
- toolbar: string[];
32
- };
33
- };
34
- export declare const newBadge: {
35
- tags: string;
36
- badge: {
37
- text: string;
38
- bgColor: string;
39
- borderColor: string;
40
- tooltip: string;
41
- };
42
- };
43
- export declare const betaBadge: {
44
- tags: string;
45
- badge: {
46
- text: string;
47
- bgColor: string;
48
- borderColor: string;
49
- tooltip: string;
50
- };
51
- };
52
- export declare const deprecatedBadge: {
53
- tags: string;
54
- badge: {
55
- text: string;
56
- bgColor: string;
57
- borderColor: string;
58
- tooltip: string;
59
- };
60
- };
61
- export declare const outdatedBadge: {
62
- tags: string;
63
- badge: {
64
- text: string;
65
- bgColor: string;
66
- borderColor: string;
67
- tooltip: string;
68
- };
69
- };
70
- export declare const dangerBadge: {
71
- tags: string;
72
- badge: {
73
- text: string;
74
- bgColor: string;
75
- borderColor: string;
76
- tooltip: string;
77
- };
78
- };
79
- export declare const todoBadge: {
80
- tags: string;
81
- badge: {
82
- text: string;
83
- bgColor: string;
84
- borderColor: string;
85
- tooltip: string;
86
- };
87
- };
88
- export declare const codeOnlyBadge: {
89
- tags: string;
90
- badge: {
91
- text: string;
92
- bgColor: string;
93
- borderColor: string;
94
- tooltip: string;
95
- };
96
- };
97
- export declare const snapshotBadge: {
98
- tags: string;
99
- badge: {
100
- text: string;
101
- bgColor: string;
102
- borderColor: string;
103
- tooltip: string;
104
- };
105
- display: {
106
- sidebar: string[];
107
- toolbar: string[];
108
- };
109
- };
110
- export declare const unitBadge: {
111
- tags: string;
112
- badge: {
113
- text: string;
114
- bgColor: string;
115
- borderColor: string;
116
- tooltip: string;
117
- };
118
- };
119
- export declare const integrationBadge: {
120
- tags: string;
121
- badge: {
122
- text: string;
123
- bgColor: string;
124
- borderColor: string;
125
- tooltip: string;
126
- };
127
- };
128
- export declare const tagBadges: TagBadgeParameters;
@@ -1,15 +0,0 @@
1
- import type { CSSProperties } from '@just-web/css';
2
- import type { DecoratorFunction } from 'storybook/internal/types';
3
- interface StoryRootOptions {
4
- classTarget?: 'html' | 'body' | undefined;
5
- dark?: {
6
- className?: string | string[] | undefined;
7
- style?: CSSProperties | undefined;
8
- } | undefined;
9
- light?: {
10
- className?: string | string[] | undefined;
11
- style?: CSSProperties | undefined;
12
- } | undefined;
13
- }
14
- export declare function withStoryRoot(param: StoryRootOptions): DecoratorFunction<any, any>;
15
- export {};
@@ -1,61 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { toDOMStyle } from '@just-web/css';
3
- import { useDarkMode } from 'storybook-dark-mode';
4
- export function withStoryRoot(param) {
5
- return function storyRootDecorator(Story) {
6
- const dark = useDarkMode();
7
- if (param.classTarget === 'html') {
8
- if (dark) {
9
- removeClass(param.light?.className);
10
- addClass(param.dark?.className);
11
- removeStyle(param.light?.style);
12
- addStyle(param.dark?.style);
13
- }
14
- else {
15
- removeClass(param.dark?.className);
16
- addClass(param.light?.className);
17
- removeStyle(param.dark?.style);
18
- addStyle(param.light?.style);
19
- }
20
- return _jsx(Story, {});
21
- }
22
- if (dark) {
23
- return (_jsx("div", { className: typeof param.dark?.className === 'string' ? param.dark.className : param.dark?.className?.join(' '), style: param.dark?.style, children: _jsx(Story, {}) }));
24
- }
25
- return (_jsx("div", { className: typeof param.light?.className === 'string' ? param.light.className : param.light?.className?.join(' '), style: param.light?.style, children: _jsx(Story, {}) }));
26
- };
27
- }
28
- function addClass(className) {
29
- if (!className)
30
- return;
31
- if (typeof className === 'string') {
32
- document.body.classList.add(...className.split(' '));
33
- }
34
- else if (Array.isArray(className)) {
35
- document.body.classList.add(...className);
36
- }
37
- }
38
- function removeClass(className) {
39
- if (!className)
40
- return;
41
- if (typeof className === 'string') {
42
- document.body.classList.remove(...className.split(' '));
43
- }
44
- else if (Array.isArray(className)) {
45
- document.body.classList.remove(...className);
46
- }
47
- }
48
- function addStyle(style) {
49
- if (style) {
50
- for (const [key, value] of Object.entries(toDOMStyle(style))) {
51
- document.body.style.setProperty(key, value);
52
- }
53
- }
54
- }
55
- function removeStyle(style) {
56
- if (style) {
57
- for (const key of Object.keys(toDOMStyle(style))) {
58
- document.body.style.removeProperty(key);
59
- }
60
- }
61
- }
@@ -1,97 +0,0 @@
1
- import type { CSSProperties } from '@just-web/css'
2
- import { toDOMStyle } from '@just-web/css'
3
- import { useDarkMode } from 'storybook-dark-mode'
4
- import type { DecoratorFunction } from 'storybook/internal/types'
5
-
6
- interface StoryRootOptions {
7
- classTarget?: 'html' | 'body' | undefined
8
- dark?:
9
- | {
10
- className?: string | string[] | undefined
11
- style?: CSSProperties | undefined
12
- }
13
- | undefined
14
- light?:
15
- | {
16
- className?: string | string[] | undefined
17
- style?: CSSProperties | undefined
18
- }
19
- | undefined
20
- }
21
-
22
- export function withStoryRoot(param: StoryRootOptions): DecoratorFunction<any, any> {
23
- return function storyRootDecorator(Story) {
24
- const dark = useDarkMode()
25
- if (param.classTarget === 'html') {
26
- if (dark) {
27
- removeClass(param.light?.className)
28
- addClass(param.dark?.className)
29
- removeStyle(param.light?.style)
30
- addStyle(param.dark?.style)
31
- } else {
32
- removeClass(param.dark?.className)
33
- addClass(param.light?.className)
34
- removeStyle(param.dark?.style)
35
- addStyle(param.light?.style)
36
- }
37
- return <Story />
38
- }
39
-
40
- if (dark) {
41
- return (
42
- <div
43
- className={
44
- typeof param.dark?.className === 'string' ? param.dark.className : param.dark?.className?.join(' ')
45
- }
46
- style={param.dark?.style}
47
- >
48
- <Story />
49
- </div>
50
- )
51
- }
52
- return (
53
- <div
54
- className={
55
- typeof param.light?.className === 'string' ? param.light.className : param.light?.className?.join(' ')
56
- }
57
- style={param.light?.style}
58
- >
59
- <Story />
60
- </div>
61
- )
62
- }
63
- }
64
-
65
- function addClass(className: string | string[] | undefined) {
66
- if (!className) return
67
- if (typeof className === 'string') {
68
- document.body.classList.add(...className.split(' '))
69
- } else if (Array.isArray(className)) {
70
- document.body.classList.add(...className)
71
- }
72
- }
73
-
74
- function removeClass(className: string | string[] | undefined) {
75
- if (!className) return
76
- if (typeof className === 'string') {
77
- document.body.classList.remove(...className.split(' '))
78
- } else if (Array.isArray(className)) {
79
- document.body.classList.remove(...className)
80
- }
81
- }
82
-
83
- function addStyle(style: CSSProperties | undefined) {
84
- if (style) {
85
- for (const [key, value] of Object.entries(toDOMStyle(style)!)) {
86
- document.body.style.setProperty(key, value as any)
87
- }
88
- }
89
- }
90
-
91
- function removeStyle(style: CSSProperties | undefined) {
92
- if (style) {
93
- for (const key of Object.keys(toDOMStyle(style)!)) {
94
- document.body.style.removeProperty(key)
95
- }
96
- }
97
- }