@repobuddy/storybook 0.9.3 → 0.10.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.
Files changed (40) hide show
  1. package/esm/index.d.ts +3 -1
  2. package/esm/index.js +3 -1
  3. package/esm/manager/tag_badges.js +3 -3
  4. package/esm/parameters/define_parameters.d.ts +4 -2
  5. package/esm/parameters/story_sort.d.ts +27 -0
  6. package/esm/parameters/story_sort.js +1 -0
  7. package/esm/storybook-dark-mode/dark_mode_docs_container.d.ts +26 -0
  8. package/esm/storybook-dark-mode/dark_mode_docs_container.js +32 -0
  9. package/esm/storybook-dark-mode/define_dark_mode.d.ts +38 -0
  10. package/esm/storybook-dark-mode/define_dark_mode.js +18 -0
  11. package/esm/storybook-dark-mode/index.d.ts +3 -0
  12. package/esm/storybook-dark-mode/index.js +3 -0
  13. package/esm/storybook-dark-mode/with_story_root.d.ts +15 -0
  14. package/esm/storybook-dark-mode/with_story_root.js +61 -0
  15. package/package.json +25 -4
  16. package/readme.md +40 -0
  17. package/src/index.ts +3 -1
  18. package/src/manager/tag_badges.ts +3 -3
  19. package/src/parameters/define_parameters.ts +2 -3
  20. package/src/parameters/story_sort.ts +29 -0
  21. package/src/storybook-dark-mode/dark_mode_docs_container.mdx +33 -0
  22. package/src/storybook-dark-mode/dark_mode_docs_container.tsx +43 -0
  23. package/src/storybook-dark-mode/define_dark_mode.ts +40 -0
  24. package/src/storybook-dark-mode/index.ts +3 -0
  25. package/src/storybook-dark-mode/with_story_root.tsx +97 -0
  26. package/esm/parameters/define_story_sort.d.ts +0 -55
  27. package/esm/parameters/define_story_sort.js +0 -28
  28. package/esm/react/index.d.ts +0 -2
  29. package/esm/react/index.js +0 -2
  30. package/src/parameters/define_story_sort.ts +0 -58
  31. package/src/react/index.ts +0 -2
  32. /package/esm/{react/decorators → decorators}/show_doc_source.d.ts +0 -0
  33. /package/esm/{react/decorators → decorators}/show_doc_source.js +0 -0
  34. /package/esm/{react/decorators → decorators}/when_running_in_test.d.ts +0 -0
  35. /package/esm/{react/decorators → decorators}/when_running_in_test.js +0 -0
  36. /package/esm/{react/decorators → decorators}/when_running_in_text.ctx.d.ts +0 -0
  37. /package/esm/{react/decorators → decorators}/when_running_in_text.ctx.js +0 -0
  38. /package/src/{react/decorators → decorators}/show_doc_source.tsx +0 -0
  39. /package/src/{react/decorators → decorators}/when_running_in_test.tsx +0 -0
  40. /package/src/{react/decorators → decorators}/when_running_in_text.ctx.ts +0 -0
package/esm/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export * from '@repobuddy/test';
2
+ export * from './decorators/show_doc_source.tsx';
3
+ export * from './decorators/when_running_in_test.tsx';
2
4
  export * from './parameters/define_actions_param.ts';
3
5
  export * from './parameters/define_backgrounds_param.ts';
4
6
  export * from './parameters/define_docs_param.ts';
5
7
  export * from './parameters/define_layout_param.ts';
6
8
  export * from './parameters/define_parameters.ts';
7
- export * from './parameters/define_story_sort.ts';
8
9
  export * from './parameters/define_test_param.ts';
9
10
  export * from './parameters/define_viewport_param.ts';
11
+ export * from './parameters/story_sort.ts';
package/esm/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  export * from '@repobuddy/test';
2
+ export * from "./decorators/show_doc_source.js";
3
+ export * from "./decorators/when_running_in_test.js";
2
4
  export * from "./parameters/define_actions_param.js";
3
5
  export * from "./parameters/define_backgrounds_param.js";
4
6
  export * from "./parameters/define_docs_param.js";
5
7
  export * from "./parameters/define_layout_param.js";
6
8
  export * from "./parameters/define_parameters.js";
7
- export * from "./parameters/define_story_sort.js";
8
9
  export * from "./parameters/define_test_param.js";
9
10
  export * from "./parameters/define_viewport_param.js";
11
+ export * from "./parameters/story_sort.js";
@@ -127,16 +127,16 @@ export const integrationBadge = {
127
127
  }
128
128
  };
129
129
  export const tagBadges = [
130
+ editorBadge,
131
+ unitBadge,
132
+ integrationBadge,
130
133
  newBadge,
131
134
  betaBadge,
132
135
  deprecatedBadge,
133
136
  outdatedBadge,
134
137
  dangerBadge,
135
138
  todoBadge,
136
- editorBadge,
137
139
  codeOnlyBadge,
138
140
  snapshotBadge,
139
- unitBadge,
140
- integrationBadge,
141
141
  versionBadge
142
142
  ];
@@ -1,10 +1,12 @@
1
1
  import type { BackgroundsParam, GlobalApiBackgroundsParam } from './define_backgrounds_param.ts';
2
2
  import type { DocsParam } from './define_docs_param.ts';
3
3
  import type { LayoutParam } from './define_layout_param.ts';
4
- import type { StorySortParam } from './define_story_sort.ts';
5
4
  import type { TestParam } from './define_test_param.ts';
6
5
  import type { ViewportParam } from './define_viewport_param.ts';
7
- export type StorybookBuiltInParams = Partial<BackgroundsParam | GlobalApiBackgroundsParam> & Partial<DocsParam> & Partial<LayoutParam> & Partial<StorySortParam> & Partial<TestParam> & Partial<ViewportParam>;
6
+ import type { StorySortParam } from './story_sort.ts';
7
+ export type StorybookBuiltInParams = Partial<BackgroundsParam | GlobalApiBackgroundsParam> & Partial<DocsParam> & Partial<LayoutParam> & Partial<TestParam> & Partial<ViewportParam> & {
8
+ options?: StorySortParam & Record<string, any>;
9
+ } & Record<string, any>;
8
10
  /**
9
11
  * Defines parameters for Storybook stories, combining built-in parameters with custom ones.
10
12
  *
@@ -0,0 +1,27 @@
1
+ type StorySortConfig = {
2
+ includeNames?: boolean;
3
+ locales?: string;
4
+ method?: 'alphabetical' | 'alphabetical-by-kind' | 'custom';
5
+ order?: string[];
6
+ [k: string]: unknown;
7
+ };
8
+ type Story = {
9
+ id: string;
10
+ importPath: string;
11
+ name: string;
12
+ title: string;
13
+ };
14
+ type StorySortFn = (a: Story, b: Story) => number;
15
+ /**
16
+ * Interface for story sorting parameters in Storybook.
17
+ * Used to define how stories should be sorted in the navigation sidebar.
18
+ */
19
+ export interface StorySortParam {
20
+ /**
21
+ * Configuration for story sorting. Can be either:
22
+ * - A StorySortConfig object specifying sort method and options
23
+ * - A custom sorting function that takes two stories and returns their sort order
24
+ */
25
+ storySort: StorySortConfig | StorySortFn;
26
+ }
27
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { type DocsContextProps } from '@storybook/blocks';
2
+ import { type ThemeVars } from '@storybook/theming';
3
+ import { type PropsWithChildren } from 'react';
4
+ /**
5
+ * Creates a `DocsContainer` for `storybook` that works with `storybook-dark-mode`.
6
+ *
7
+ * @see https://github.com/hipstersmoothie/storybook-dark-mode/issues/282
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // .storybook/preview.tsx
12
+ * const preview: Preview = {
13
+ * parameters: {
14
+ * docs: {
15
+ * container: createDarkModeDocsContainer()
16
+ * }
17
+ * }
18
+ * }
19
+ * ```
20
+ */
21
+ export declare function createDarkModeDocsContainer(customThemes?: {
22
+ light: ThemeVars;
23
+ dark: ThemeVars;
24
+ }): (props: PropsWithChildren<{
25
+ context: DocsContextProps;
26
+ }>) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { DocsContainer } from '@storybook/blocks';
3
+ import { themes } from '@storybook/theming';
4
+ import { useEffect, useState } from 'react';
5
+ import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
6
+ /**
7
+ * Creates a `DocsContainer` for `storybook` that works with `storybook-dark-mode`.
8
+ *
9
+ * @see https://github.com/hipstersmoothie/storybook-dark-mode/issues/282
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // .storybook/preview.tsx
14
+ * const preview: Preview = {
15
+ * parameters: {
16
+ * docs: {
17
+ * container: createDarkModeDocsContainer()
18
+ * }
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+ export function createDarkModeDocsContainer(customThemes = themes) {
24
+ return function DarkModeDocsContainer(props) {
25
+ const [isDark, setDark] = useState(true);
26
+ useEffect(() => {
27
+ props.context.channel.on(DARK_MODE_EVENT_NAME, setDark);
28
+ return () => props.context.channel.removeListener(DARK_MODE_EVENT_NAME, setDark);
29
+ }, [props.context.channel]);
30
+ return (_jsx(DocsContainer, { ...props, theme: isDark ? customThemes.dark : customThemes.light, children: props.children }));
31
+ };
32
+ }
@@ -0,0 +1,38 @@
1
+ import type { ThemeVars } from '@storybook/theming';
2
+ /**
3
+ * Configuration parameters for `storybook-dark-mode`.
4
+ */
5
+ export interface DarkModeParam {
6
+ /** The current theme ('dark' or 'light') */
7
+ current?: 'dark' | 'light';
8
+ /** CSS class(es) to apply in dark mode */
9
+ darkClass?: string | string[];
10
+ /** CSS class(es) to apply in light mode */
11
+ lightClass?: string | string[];
12
+ /** Dark theme variables */
13
+ dark?: ThemeVars;
14
+ /** Light theme variables */
15
+ light?: ThemeVars;
16
+ /** Element to apply theme classes to ('html' or 'body') */
17
+ classTarget?: 'html' | 'body';
18
+ /** Whether to apply theme styles to preview iframe */
19
+ stylePreview?: boolean;
20
+ }
21
+ /**
22
+ * Defines `storybook-dark-mode` parameters for Storybook stories.
23
+ *
24
+ * @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
+ * @returns An object containing the dark mode parameter configuration
35
+ */
36
+ export declare function defineDarkModeParam(darkMode: DarkModeParam): {
37
+ darkMode: DarkModeParam;
38
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Defines `storybook-dark-mode` parameters for Storybook stories.
3
+ *
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
+ * @returns An object containing the dark mode parameter configuration
15
+ */
16
+ export function defineDarkModeParam(darkMode) {
17
+ return { darkMode };
18
+ }
@@ -0,0 +1,3 @@
1
+ export * from './dark_mode_docs_container.tsx';
2
+ export * from './define_dark_mode.ts';
3
+ export * from './with_story_root.tsx';
@@ -0,0 +1,3 @@
1
+ export * from "./dark_mode_docs_container.js";
2
+ export * from "./define_dark_mode.js";
3
+ export * from "./with_story_root.js";
@@ -0,0 +1,15 @@
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 {};
@@ -0,0 +1,61 @@
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repobuddy/storybook",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "Storybook repo buddy",
5
5
  "keywords": [
6
6
  "storybook",
@@ -23,9 +23,9 @@
23
23
  "types": "./esm/manager/index.d.ts",
24
24
  "default": "./esm/manager/index.js"
25
25
  },
26
- "./react": {
27
- "types": "./esm/react/index.d.ts",
28
- "default": "./esm/react/index.js"
26
+ "./storybook-dark-mode": {
27
+ "types": "./esm/storybook-dark-mode/index.d.ts",
28
+ "default": "./esm/storybook-dark-mode/index.js"
29
29
  },
30
30
  "./testing": {
31
31
  "types": "./esm/testing.d.ts",
@@ -38,6 +38,7 @@
38
38
  "!**/*.{spec,test,unit,accept,integrate,system,perf,stress,study,stories}.*"
39
39
  ],
40
40
  "dependencies": {
41
+ "@just-web/css": "^0.4.0",
41
42
  "@repobuddy/test": "^1.0.0"
42
43
  },
43
44
  "devDependencies": {
@@ -66,6 +67,26 @@
66
67
  "vite": "^6.3.4",
67
68
  "vitest": "^3.1.2"
68
69
  },
70
+ "peerDependencies": {
71
+ "@storybook/blocks": ">= 8.6.12",
72
+ "@storybook/theming": ">= 8.6.12",
73
+ "storybook-addon-tag-badges": ">= 1.4.0",
74
+ "storybook-dark-mode": ">= 4.0.2"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "@storybook/blocks": {
78
+ "optional": true
79
+ },
80
+ "@storybook/theming": {
81
+ "optional": true
82
+ },
83
+ "storybook-addon-tag-badges": {
84
+ "optional": true
85
+ },
86
+ "storybook-dark-mode": {
87
+ "optional": true
88
+ }
89
+ },
69
90
  "scripts": {
70
91
  "build": "tsc",
71
92
  "clean": "rimraf .turbo coverage esm storybook-static *.tsbuildinfo",
package/readme.md CHANGED
@@ -86,5 +86,45 @@ import { addons } from '@storybook/manager-api'
86
86
  addons.setConfig({ tagBadges })
87
87
  ```
88
88
 
89
+ ### `storybook-dark-mode` support
90
+
91
+ [`@repobuddy/storybook`][`@repobuddy/storybook`] provides a few utilities to work with `storybook-dark-mode`.
92
+
93
+ ```ts
94
+ // .storybook/preview.tsx
95
+ import { defineDarkModeParam, withStoryRoot, createDarkModeDocsContainer } from '@repobuddy/storybook/storybook-dark-mode'
96
+
97
+ export const preview: Preview = {
98
+ parameters: {
99
+ docs: {
100
+ container: createDarkModeDocsContainer()
101
+ },
102
+ darkMode: defineDarkModeParam({
103
+ classTarget: 'html',
104
+ darkClass: 'dark',
105
+ stylePreview: true
106
+ })
107
+ },
108
+ decorators: [withStoryRoot({
109
+ classTarget: 'html',
110
+ dark: {
111
+ className: 'dark:bg-black dark:text-white'
112
+ }
113
+ })]
114
+ }
115
+ ```
116
+
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
+
89
129
  [`@repobuddy/storybook`]: https://github.com/repobuddy/storybook
90
130
  [`storybook-addon-tag-badges`]: https://github.com/Sidnioulz/storybook-addon-tag-badges
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export * from '@repobuddy/test'
2
+ export * from './decorators/show_doc_source.tsx'
3
+ export * from './decorators/when_running_in_test.tsx'
2
4
  export * from './parameters/define_actions_param.ts'
3
5
  export * from './parameters/define_backgrounds_param.ts'
4
6
  export * from './parameters/define_docs_param.ts'
5
7
  export * from './parameters/define_layout_param.ts'
6
8
  export * from './parameters/define_parameters.ts'
7
- export * from './parameters/define_story_sort.ts'
8
9
  export * from './parameters/define_test_param.ts'
9
10
  export * from './parameters/define_viewport_param.ts'
11
+ export * from './parameters/story_sort.ts'
@@ -140,16 +140,16 @@ export const integrationBadge = {
140
140
  }
141
141
 
142
142
  export const tagBadges: TagBadgeParameters = [
143
+ editorBadge,
144
+ unitBadge,
145
+ integrationBadge,
143
146
  newBadge,
144
147
  betaBadge,
145
148
  deprecatedBadge,
146
149
  outdatedBadge,
147
150
  dangerBadge,
148
151
  todoBadge,
149
- editorBadge,
150
152
  codeOnlyBadge,
151
153
  snapshotBadge,
152
- unitBadge,
153
- integrationBadge,
154
154
  versionBadge
155
155
  ]
@@ -1,16 +1,15 @@
1
1
  import type { BackgroundsParam, GlobalApiBackgroundsParam } from './define_backgrounds_param.ts'
2
2
  import type { DocsParam } from './define_docs_param.ts'
3
3
  import type { LayoutParam } from './define_layout_param.ts'
4
- import type { StorySortParam } from './define_story_sort.ts'
5
4
  import type { TestParam } from './define_test_param.ts'
6
5
  import type { ViewportParam } from './define_viewport_param.ts'
6
+ import type { StorySortParam } from './story_sort.ts'
7
7
 
8
8
  export type StorybookBuiltInParams = Partial<BackgroundsParam | GlobalApiBackgroundsParam> &
9
9
  Partial<DocsParam> &
10
10
  Partial<LayoutParam> &
11
- Partial<StorySortParam> &
12
11
  Partial<TestParam> &
13
- Partial<ViewportParam>
12
+ Partial<ViewportParam> & { options?: StorySortParam & Record<string, any> } & Record<string, any>
14
13
 
15
14
  /**
16
15
  * Defines parameters for Storybook stories, combining built-in parameters with custom ones.
@@ -0,0 +1,29 @@
1
+ type StorySortConfig = {
2
+ includeNames?: boolean
3
+ locales?: string
4
+ method?: 'alphabetical' | 'alphabetical-by-kind' | 'custom'
5
+ order?: string[]
6
+ [k: string]: unknown
7
+ }
8
+
9
+ type Story = {
10
+ id: string
11
+ importPath: string
12
+ name: string
13
+ title: string
14
+ }
15
+
16
+ type StorySortFn = (a: Story, b: Story) => number
17
+
18
+ /**
19
+ * Interface for story sorting parameters in Storybook.
20
+ * Used to define how stories should be sorted in the navigation sidebar.
21
+ */
22
+ export interface StorySortParam {
23
+ /**
24
+ * Configuration for story sorting. Can be either:
25
+ * - A StorySortConfig object specifying sort method and options
26
+ * - A custom sorting function that takes two stories and returns their sort order
27
+ */
28
+ storySort: StorySortConfig | StorySortFn
29
+ }
@@ -0,0 +1,33 @@
1
+ import { Meta } from '@storybook/blocks'
2
+
3
+ # `createDarkModeDocsContainer`
4
+
5
+ <Meta title="storybook-dark-mode/createDarkModeDocsContainer" />
6
+
7
+ Creates a `DocsContainer` for `storybook` that works with `storybook-dark-mode`.
8
+
9
+ ## Usage
10
+
11
+ It can be called with no arguments to use the default themes from `@storybook/theming`.
12
+
13
+ ```tsx
14
+ // .storybook/preview.tsx
15
+ import { createDarkModeDocsContainer } from '@repobuddy/storybook/react'
16
+
17
+ export const preview: Preview = {
18
+ parameters: {
19
+ docs: {
20
+ container: createDarkModeDocsContainer()
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ It can also be called with a custom themes.
27
+
28
+ ```tsx
29
+ container: createDarkModeDocsContainer({
30
+ light: customThemes.light,
31
+ dark: customThemes.dark
32
+ })
33
+ ```
@@ -0,0 +1,43 @@
1
+ import { DocsContainer, type DocsContextProps } from '@storybook/blocks'
2
+ import { type ThemeVars, themes } from '@storybook/theming'
3
+ import { type PropsWithChildren, useEffect, useState } from 'react'
4
+ import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode'
5
+
6
+ /**
7
+ * Creates a `DocsContainer` for `storybook` that works with `storybook-dark-mode`.
8
+ *
9
+ * @see https://github.com/hipstersmoothie/storybook-dark-mode/issues/282
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // .storybook/preview.tsx
14
+ * const preview: Preview = {
15
+ * parameters: {
16
+ * docs: {
17
+ * container: createDarkModeDocsContainer()
18
+ * }
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+ export function createDarkModeDocsContainer(
24
+ customThemes: {
25
+ light: ThemeVars
26
+ dark: ThemeVars
27
+ } = themes
28
+ ) {
29
+ return function DarkModeDocsContainer(props: PropsWithChildren<{ context: DocsContextProps }>) {
30
+ const [isDark, setDark] = useState(true)
31
+
32
+ useEffect(() => {
33
+ props.context.channel.on(DARK_MODE_EVENT_NAME, setDark)
34
+
35
+ return () => props.context.channel.removeListener(DARK_MODE_EVENT_NAME, setDark)
36
+ }, [props.context.channel])
37
+ return (
38
+ <DocsContainer {...props} theme={isDark ? customThemes.dark : customThemes.light}>
39
+ {props.children}
40
+ </DocsContainer>
41
+ )
42
+ }
43
+ }
@@ -0,0 +1,40 @@
1
+ import type { ThemeVars } from '@storybook/theming'
2
+
3
+ /**
4
+ * Configuration parameters for `storybook-dark-mode`.
5
+ */
6
+ export interface DarkModeParam {
7
+ /** The current theme ('dark' or 'light') */
8
+ current?: 'dark' | 'light'
9
+ /** CSS class(es) to apply in dark mode */
10
+ darkClass?: string | string[]
11
+ /** CSS class(es) to apply in light mode */
12
+ lightClass?: string | string[]
13
+ /** Dark theme variables */
14
+ dark?: ThemeVars
15
+ /** Light theme variables */
16
+ light?: ThemeVars
17
+ /** Element to apply theme classes to ('html' or 'body') */
18
+ classTarget?: 'html' | 'body'
19
+ /** Whether to apply theme styles to preview iframe */
20
+ stylePreview?: boolean
21
+ }
22
+
23
+ /**
24
+ * Defines `storybook-dark-mode` parameters for Storybook stories.
25
+ *
26
+ * @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
+ * @returns An object containing the dark mode parameter configuration
37
+ */
38
+ export function defineDarkModeParam(darkMode: DarkModeParam) {
39
+ return { darkMode }
40
+ }
@@ -0,0 +1,3 @@
1
+ export * from './dark_mode_docs_container.tsx'
2
+ export * from './define_dark_mode.ts'
3
+ export * from './with_story_root.tsx'
@@ -0,0 +1,97 @@
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
+ }
@@ -1,55 +0,0 @@
1
- type StorySortConfig = {
2
- includeNames?: boolean;
3
- locales?: string;
4
- method?: 'alphabetical' | 'alphabetical-by-kind' | 'custom';
5
- order?: string[];
6
- [k: string]: unknown;
7
- };
8
- type Story = {
9
- id: string;
10
- importPath: string;
11
- name: string;
12
- title: string;
13
- };
14
- type StorySortFn = (a: Story, b: Story) => number;
15
- /**
16
- * Interface for story sorting parameters in Storybook.
17
- * Used to define how stories should be sorted in the navigation sidebar.
18
- */
19
- export interface StorySortParam {
20
- /**
21
- * Configuration for story sorting. Can be either:
22
- * - A StorySortConfig object specifying sort method and options
23
- * - A custom sorting function that takes two stories and returns their sort order
24
- */
25
- storySort: StorySortConfig | StorySortFn;
26
- }
27
- /**
28
- * Defines story sorting parameters for Storybook navigation.
29
- *
30
- * @see https://storybook.js.org/docs/api/parameters#optionsstorysort
31
- *
32
- * @param storySort - Configuration for how stories should be sorted. Can be either:
33
- * - A configuration object specifying sort method and options
34
- * - A custom sorting function that takes two stories and returns their sort order
35
- * @returns An object containing the story sort configuration
36
- *
37
- * @example
38
- * // Alphabetical sorting
39
- * defineStorySort({ method: 'alphabetical' })
40
- *
41
- * @example
42
- * // Custom order
43
- * defineStorySort({
44
- * method: 'custom',
45
- * order: ['Introduction', 'Components', '*']
46
- * })
47
- *
48
- * @example
49
- * // Custom sort function
50
- * defineStorySort((a, b) => a.title.localeCompare(b.title))
51
- */
52
- export declare function defineStorySort(storySort: StorySortParam['storySort']): {
53
- storySort: StorySortConfig | StorySortFn;
54
- };
55
- export {};
@@ -1,28 +0,0 @@
1
- /**
2
- * Defines story sorting parameters for Storybook navigation.
3
- *
4
- * @see https://storybook.js.org/docs/api/parameters#optionsstorysort
5
- *
6
- * @param storySort - Configuration for how stories should be sorted. Can be either:
7
- * - A configuration object specifying sort method and options
8
- * - A custom sorting function that takes two stories and returns their sort order
9
- * @returns An object containing the story sort configuration
10
- *
11
- * @example
12
- * // Alphabetical sorting
13
- * defineStorySort({ method: 'alphabetical' })
14
- *
15
- * @example
16
- * // Custom order
17
- * defineStorySort({
18
- * method: 'custom',
19
- * order: ['Introduction', 'Components', '*']
20
- * })
21
- *
22
- * @example
23
- * // Custom sort function
24
- * defineStorySort((a, b) => a.title.localeCompare(b.title))
25
- */
26
- export function defineStorySort(storySort) {
27
- return { storySort };
28
- }
@@ -1,2 +0,0 @@
1
- export * from './decorators/show_doc_source.tsx';
2
- export * from './decorators/when_running_in_test.tsx';
@@ -1,2 +0,0 @@
1
- export * from "./decorators/show_doc_source.js";
2
- export * from "./decorators/when_running_in_test.js";
@@ -1,58 +0,0 @@
1
- type StorySortConfig = {
2
- includeNames?: boolean
3
- locales?: string
4
- method?: 'alphabetical' | 'alphabetical-by-kind' | 'custom'
5
- order?: string[]
6
- [k: string]: unknown
7
- }
8
-
9
- type Story = {
10
- id: string
11
- importPath: string
12
- name: string
13
- title: string
14
- }
15
-
16
- type StorySortFn = (a: Story, b: Story) => number
17
-
18
- /**
19
- * Interface for story sorting parameters in Storybook.
20
- * Used to define how stories should be sorted in the navigation sidebar.
21
- */
22
- export interface StorySortParam {
23
- /**
24
- * Configuration for story sorting. Can be either:
25
- * - A StorySortConfig object specifying sort method and options
26
- * - A custom sorting function that takes two stories and returns their sort order
27
- */
28
- storySort: StorySortConfig | StorySortFn
29
- }
30
-
31
- /**
32
- * Defines story sorting parameters for Storybook navigation.
33
- *
34
- * @see https://storybook.js.org/docs/api/parameters#optionsstorysort
35
- *
36
- * @param storySort - Configuration for how stories should be sorted. Can be either:
37
- * - A configuration object specifying sort method and options
38
- * - A custom sorting function that takes two stories and returns their sort order
39
- * @returns An object containing the story sort configuration
40
- *
41
- * @example
42
- * // Alphabetical sorting
43
- * defineStorySort({ method: 'alphabetical' })
44
- *
45
- * @example
46
- * // Custom order
47
- * defineStorySort({
48
- * method: 'custom',
49
- * order: ['Introduction', 'Components', '*']
50
- * })
51
- *
52
- * @example
53
- * // Custom sort function
54
- * defineStorySort((a, b) => a.title.localeCompare(b.title))
55
- */
56
- export function defineStorySort(storySort: StorySortParam['storySort']) {
57
- return { storySort }
58
- }
@@ -1,2 +0,0 @@
1
- export * from './decorators/show_doc_source.tsx'
2
- export * from './decorators/when_running_in_test.tsx'