@sit-onyx/storybook-utils 1.0.0-alpha.11 → 1.0.0-alpha.111
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 +6 -0
- package/package.json +20 -9
- package/src/preview.spec.ts +18 -0
- package/src/preview.ts +33 -24
- package/src/required.ts +36 -0
- package/src/theme.ts +16 -43
- package/src/types.ts +10 -0
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sit-onyx/storybook-utils",
|
|
3
3
|
"description": "Storybook utilities for Vue",
|
|
4
|
-
"version": "1.0.0-alpha.
|
|
4
|
+
"version": "1.0.0-alpha.111",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Schwarz IT KG",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -13,18 +13,29 @@
|
|
|
13
13
|
".": "./src/index.ts",
|
|
14
14
|
"./style.css": "./src/index.css"
|
|
15
15
|
},
|
|
16
|
+
"homepage": "https://onyx.schwarz/development/packages/storybook-utils.html",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/SchwarzIT/onyx",
|
|
20
|
+
"directory": "packages/storybook-utils"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/SchwarzIT/onyx/issues"
|
|
24
|
+
},
|
|
16
25
|
"peerDependencies": {
|
|
17
|
-
"@storybook/core-events": ">=
|
|
18
|
-
"@storybook/preview-api": ">=
|
|
19
|
-
"@storybook/theming": ">=
|
|
20
|
-
"@storybook/vue3": ">=
|
|
21
|
-
"storybook-dark-mode": ">=
|
|
22
|
-
"sit-onyx": "^1.0.0-alpha.
|
|
26
|
+
"@storybook/core-events": ">= 8.0.0",
|
|
27
|
+
"@storybook/preview-api": ">= 8.0.0",
|
|
28
|
+
"@storybook/theming": ">= 8.0.0",
|
|
29
|
+
"@storybook/vue3": ">= 8.0.0",
|
|
30
|
+
"storybook-dark-mode": ">= 4",
|
|
31
|
+
"sit-onyx": "^1.0.0-alpha.108"
|
|
23
32
|
},
|
|
24
33
|
"dependencies": {
|
|
25
|
-
"deepmerge-ts": "^
|
|
34
|
+
"deepmerge-ts": "^7.0.0"
|
|
26
35
|
},
|
|
27
36
|
"scripts": {
|
|
28
|
-
"build": "tsc --noEmit"
|
|
37
|
+
"build": "tsc --noEmit",
|
|
38
|
+
"test": "vitest",
|
|
39
|
+
"test:coverage": "vitest run --coverage"
|
|
29
40
|
}
|
|
30
41
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { sourceCodeTransformer } from "./preview";
|
|
3
|
+
|
|
4
|
+
describe("preview.ts", () => {
|
|
5
|
+
test("should transform source code", () => {
|
|
6
|
+
// ARRANGE
|
|
7
|
+
// mix double and single quotes to test that both are supported
|
|
8
|
+
const originalSourceCode = `<OnyxTest v-on:test="someFunction" @empty='()=>({})' v-bind="{}" :disabled='true' :readonly="false" test="true" />`;
|
|
9
|
+
|
|
10
|
+
const expectedOutput = `<OnyxTest @test="someFunction" disabled :readonly="false" test />`;
|
|
11
|
+
|
|
12
|
+
// ACT
|
|
13
|
+
const output = sourceCodeTransformer(originalSourceCode);
|
|
14
|
+
|
|
15
|
+
// ASSERT
|
|
16
|
+
expect(output).toBe(expectedOutput);
|
|
17
|
+
});
|
|
18
|
+
});
|
package/src/preview.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { type ThemeVars } from "@storybook/theming";
|
|
|
4
4
|
import { type Preview } from "@storybook/vue3";
|
|
5
5
|
import { deepmerge } from "deepmerge-ts";
|
|
6
6
|
import { DARK_MODE_EVENT_NAME } from "storybook-dark-mode";
|
|
7
|
+
|
|
8
|
+
import { requiredGlobalType, withRequired } from "./required";
|
|
7
9
|
import { ONYX_BREAKPOINTS, createTheme } from "./theme";
|
|
8
10
|
|
|
9
11
|
const themes = {
|
|
@@ -17,7 +19,6 @@ const themes = {
|
|
|
17
19
|
* - Improved Vue-specific code highlighting (e.g. using `@` instead of `v-on:`)
|
|
18
20
|
* - Setup for dark mode (including docs page). Requires addon `storybook-dark-mode` to be enabled in .storybook/main.ts file
|
|
19
21
|
* - Custom Storybook theme using onyx colors (light and dark mode)
|
|
20
|
-
* - Support for setting the light/dark mode when Storybook is embedded as an iframe (via query parameter, e.g. `?theme=dark`)
|
|
21
22
|
* - Configure viewports / breakpoints as defined by onyx
|
|
22
23
|
*
|
|
23
24
|
* @param overrides Custom preview / overrides, will be deep merged with the default preview.
|
|
@@ -39,6 +40,10 @@ const themes = {
|
|
|
39
40
|
*/
|
|
40
41
|
export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
41
42
|
const defaultPreview = {
|
|
43
|
+
globalTypes: {
|
|
44
|
+
...requiredGlobalType,
|
|
45
|
+
},
|
|
46
|
+
decorators: [withRequired],
|
|
42
47
|
parameters: {
|
|
43
48
|
controls: {
|
|
44
49
|
matchers: {
|
|
@@ -52,20 +57,16 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
52
57
|
docs: {
|
|
53
58
|
// see: https://github.com/hipstersmoothie/storybook-dark-mode/issues/127#issuecomment-840701971
|
|
54
59
|
get theme(): ThemeVars {
|
|
55
|
-
|
|
56
|
-
const params = new URLSearchParams(window.location.search);
|
|
57
|
-
const themeParam = params.get("theme");
|
|
58
|
-
|
|
59
|
-
const isDark = themeParam
|
|
60
|
-
? themeParam === "dark"
|
|
61
|
-
: parent.document.body.classList.contains("dark");
|
|
60
|
+
const isDark = parent.document.body.classList.contains("dark");
|
|
62
61
|
|
|
63
62
|
if (isDark) {
|
|
64
63
|
document.body.classList.remove("light");
|
|
65
64
|
document.body.classList.add("dark");
|
|
65
|
+
document.documentElement.style.colorScheme = "dark";
|
|
66
66
|
} else {
|
|
67
67
|
document.body.classList.remove("dark");
|
|
68
68
|
document.body.classList.add("light");
|
|
69
|
+
document.documentElement.style.colorScheme = "light";
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
return isDark ? themes.dark : themes.light;
|
|
@@ -78,22 +79,7 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
78
79
|
* we want it to look.
|
|
79
80
|
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
80
81
|
*/
|
|
81
|
-
transform:
|
|
82
|
-
const replacements = [
|
|
83
|
-
// replace event bindings with shortcut
|
|
84
|
-
{ searchValue: "v-on:", replaceValue: "@" },
|
|
85
|
-
// remove empty event handlers, e.g. @click="()=>({})" will be removed
|
|
86
|
-
{ searchValue: / @.*['"]\(\)=>\({}\)['"]/g, replaceValue: "" },
|
|
87
|
-
// remove empty v-binds, e.g. v-bind="{}" will be removed
|
|
88
|
-
{ searchValue: / v-bind=['"]{}['"]/g, replaceValue: "" },
|
|
89
|
-
// replace boolean shortcuts for true, e.g. disabled="true" will be changed to just disabled
|
|
90
|
-
{ searchValue: /:(.*)=['"]true['"]/g, replaceValue: "$1" },
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
return replacements.reduce((code, replacement) => {
|
|
94
|
-
return replaceAll(code, replacement.searchValue, replacement.replaceValue);
|
|
95
|
-
}, sourceCode);
|
|
96
|
-
},
|
|
82
|
+
transform: sourceCodeTransformer,
|
|
97
83
|
},
|
|
98
84
|
},
|
|
99
85
|
darkMode: {
|
|
@@ -129,6 +115,29 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
129
115
|
return deepmerge<[T, typeof defaultPreview]>(overrides ?? ({} as T), defaultPreview);
|
|
130
116
|
};
|
|
131
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Custom transformer for the story source code to better fit to our
|
|
120
|
+
* Vue.js code because storybook per default does not render it exactly how
|
|
121
|
+
* we want it to look.
|
|
122
|
+
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
123
|
+
*/
|
|
124
|
+
export const sourceCodeTransformer = (sourceCode: string): string => {
|
|
125
|
+
const replacements = [
|
|
126
|
+
// replace event bindings with shortcut
|
|
127
|
+
{ searchValue: "v-on:", replaceValue: "@" },
|
|
128
|
+
// remove empty event handlers, e.g. @click="()=>({})" will be removed
|
|
129
|
+
{ searchValue: / @\S*['"]\(\)=>\({}\)['"]/g, replaceValue: "" },
|
|
130
|
+
// // remove empty v-binds, e.g. v-bind="{}" will be removed
|
|
131
|
+
{ searchValue: / v-bind=['"]{}['"]/g, replaceValue: "" },
|
|
132
|
+
// // replace boolean shortcuts for true, e.g. disabled="true" will be changed to just disabled
|
|
133
|
+
{ searchValue: /:?(\S*)=['"]true['"]/g, replaceValue: "$1" },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
return replacements.reduce((code, replacement) => {
|
|
137
|
+
return replaceAll(code, replacement.searchValue, replacement.replaceValue);
|
|
138
|
+
}, sourceCode);
|
|
139
|
+
};
|
|
140
|
+
|
|
132
141
|
/**
|
|
133
142
|
* Custom String.replaceAll implementation using a RegExp
|
|
134
143
|
* because String.replaceAll() is not available in our specified EcmaScript target in tsconfig.json
|
package/src/required.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Decorator } from "@storybook/vue3";
|
|
2
|
+
import { ref, watch } from "vue";
|
|
3
|
+
import type { StorybookGlobalType } from "./types";
|
|
4
|
+
|
|
5
|
+
type RequiredIndicator = "required" | "optional";
|
|
6
|
+
|
|
7
|
+
export const requiredGlobalType = {
|
|
8
|
+
requiredMode: {
|
|
9
|
+
name: "Required mode",
|
|
10
|
+
description: "Switch between 'required' and 'optional' indicator",
|
|
11
|
+
defaultValue: "required",
|
|
12
|
+
toolbar: {
|
|
13
|
+
icon: "flag",
|
|
14
|
+
items: [
|
|
15
|
+
{ value: "required", right: "*", title: "Required indicator" },
|
|
16
|
+
{ value: "optional", right: "(optional)", title: "Optional indicator" },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
} satisfies StorybookGlobalType<RequiredIndicator>,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const requiredMode = ref<RequiredIndicator>("required");
|
|
23
|
+
|
|
24
|
+
export const withRequired: Decorator = (Story, context) => {
|
|
25
|
+
watch(
|
|
26
|
+
() => context.globals.requiredMode as RequiredIndicator,
|
|
27
|
+
(newRequiredMode) => (requiredMode.value = newRequiredMode),
|
|
28
|
+
{ immediate: true },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
components: { Story },
|
|
33
|
+
setup: () => ({ requiredMode }),
|
|
34
|
+
template: `<div :class="{ ['onyx-use-optional']: requiredMode === 'optional' }"> <story /> </div>`,
|
|
35
|
+
};
|
|
36
|
+
};
|
package/src/theme.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ThemeVars, ThemeVarsPartial } from "@storybook/theming";
|
|
2
2
|
import { create } from "@storybook/theming/create";
|
|
3
3
|
import onyxVariables from "sit-onyx/src/styles/variables-onyx.json";
|
|
4
|
+
import { ONYX_BREAKPOINTS as RAW_ONYX_BREAKPOINTS, type OnyxBreakpoint } from "sit-onyx/types";
|
|
4
5
|
import onyxLogo from "./assets/logo-onyx.svg";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -90,50 +91,22 @@ const defineTheme = (colors: {
|
|
|
90
91
|
};
|
|
91
92
|
|
|
92
93
|
/** All available Storybook breakpoints / viewports supported by onyx. */
|
|
93
|
-
export const ONYX_BREAKPOINTS =
|
|
94
|
-
|
|
95
|
-
name
|
|
96
|
-
styles: {
|
|
97
|
-
|
|
98
|
-
height: "100%",
|
|
99
|
-
},
|
|
94
|
+
export const ONYX_BREAKPOINTS = Object.entries(RAW_ONYX_BREAKPOINTS).reduce(
|
|
95
|
+
(obj, [name, width]) => {
|
|
96
|
+
const breakpoint = name as OnyxBreakpoint;
|
|
97
|
+
obj[breakpoint] = { name: breakpoint, styles: { width: `${width}px`, height: "100%" } };
|
|
98
|
+
return obj;
|
|
100
99
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
width: "768px",
|
|
112
|
-
height: "100%",
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
md: {
|
|
116
|
-
name: "md",
|
|
117
|
-
styles: {
|
|
118
|
-
width: "992px",
|
|
119
|
-
height: "100%",
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
lg: {
|
|
123
|
-
name: "lg",
|
|
124
|
-
styles: {
|
|
125
|
-
width: "1440px",
|
|
126
|
-
height: "100%",
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
xl: {
|
|
130
|
-
name: "xl",
|
|
131
|
-
styles: {
|
|
132
|
-
width: "1920px",
|
|
133
|
-
height: "100%",
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
} as const;
|
|
100
|
+
{} as Record<OnyxBreakpoint, StorybookBreakpoint>,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export type StorybookBreakpoint = {
|
|
104
|
+
name: OnyxBreakpoint;
|
|
105
|
+
styles: {
|
|
106
|
+
width: string;
|
|
107
|
+
height: string;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
137
110
|
|
|
138
111
|
/**
|
|
139
112
|
* Converts a rem string into a numeric value with a rem base of 16.
|
package/src/types.ts
CHANGED
|
@@ -46,3 +46,13 @@ export type DefineStorybookActionsAndVModelsOptions<T> = Meta<T> & {
|
|
|
46
46
|
component: NonNullable<T>;
|
|
47
47
|
events: ExtractVueEventNames<T>[];
|
|
48
48
|
};
|
|
49
|
+
|
|
50
|
+
export type StorybookGlobalType<TValue> = {
|
|
51
|
+
name: string;
|
|
52
|
+
description: string;
|
|
53
|
+
defaultValue: TValue;
|
|
54
|
+
toolbar: {
|
|
55
|
+
icon: string;
|
|
56
|
+
items: { value: TValue; right: string; title: string }[];
|
|
57
|
+
};
|
|
58
|
+
};
|