@sit-onyx/storybook-utils 1.0.0-alpha.6 → 1.0.0-alpha.60
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 +18 -7
- package/src/index.css +1 -1
- package/src/preview.spec.ts +18 -0
- package/src/preview.ts +46 -17
- package/src/required.ts +36 -0
- 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.60",
|
|
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": ">=
|
|
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",
|
|
21
30
|
"storybook-dark-mode": ">= 3",
|
|
22
|
-
"sit-onyx": "^
|
|
31
|
+
"sit-onyx": "^1.0.0-alpha.58"
|
|
23
32
|
},
|
|
24
33
|
"dependencies": {
|
|
25
34
|
"deepmerge-ts": "^5.1.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
|
}
|
package/src/index.css
CHANGED
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
/* the Storybook table of content headline is always black so we need to manually set it for the dark mode */
|
|
23
23
|
#storybook-docs .sbdocs-wrapper > div:nth-child(2) {
|
|
24
24
|
/* same as Storybook color "textMuted" inside ./theme.ts */
|
|
25
|
-
color: var(--onyx-color-text-neutral-medium);
|
|
25
|
+
color: var(--onyx-color-text-icons-neutral-medium);
|
|
26
26
|
}
|
|
@@ -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
|
@@ -3,8 +3,9 @@ import { addons } from "@storybook/preview-api";
|
|
|
3
3
|
import { type ThemeVars } from "@storybook/theming";
|
|
4
4
|
import { type Preview } from "@storybook/vue3";
|
|
5
5
|
import { deepmerge } from "deepmerge-ts";
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import { ONYX_BREAKPOINTS, createTheme } from "./theme";
|
|
8
|
+
import { requiredGlobalType, withRequired } from "./required";
|
|
8
9
|
|
|
9
10
|
const themes = {
|
|
10
11
|
light: createTheme(),
|
|
@@ -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: {
|
|
@@ -59,6 +64,17 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
59
64
|
const isDark = themeParam
|
|
60
65
|
? themeParam === "dark"
|
|
61
66
|
: parent.document.body.classList.contains("dark");
|
|
67
|
+
|
|
68
|
+
if (isDark) {
|
|
69
|
+
document.body.classList.remove("light");
|
|
70
|
+
document.body.classList.add("dark");
|
|
71
|
+
document.documentElement.style.colorScheme = "dark";
|
|
72
|
+
} else {
|
|
73
|
+
document.body.classList.remove("dark");
|
|
74
|
+
document.body.classList.add("light");
|
|
75
|
+
document.documentElement.style.colorScheme = "light";
|
|
76
|
+
}
|
|
77
|
+
|
|
62
78
|
return isDark ? themes.dark : themes.light;
|
|
63
79
|
},
|
|
64
80
|
source: {
|
|
@@ -69,22 +85,7 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
69
85
|
* we want it to look.
|
|
70
86
|
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
71
87
|
*/
|
|
72
|
-
transform:
|
|
73
|
-
const replacements = [
|
|
74
|
-
// replace event bindings with shortcut
|
|
75
|
-
{ searchValue: "v-on:", replaceValue: "@" },
|
|
76
|
-
// remove empty event handlers, e.g. @click="()=>({})" will be removed
|
|
77
|
-
{ searchValue: / @.*['"]\(\)=>\({}\)['"]/g, replaceValue: "" },
|
|
78
|
-
// remove empty v-binds, e.g. v-bind="{}" will be removed
|
|
79
|
-
{ searchValue: / v-bind=['"]{}['"]/g, replaceValue: "" },
|
|
80
|
-
// replace boolean shortcuts for true, e.g. disabled="true" will be changed to just disabled
|
|
81
|
-
{ searchValue: /:(.*)=['"]true['"]/g, replaceValue: "$1" },
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
return replacements.reduce((code, replacement) => {
|
|
85
|
-
return replaceAll(code, replacement.searchValue, replacement.replaceValue);
|
|
86
|
-
}, sourceCode);
|
|
87
|
-
},
|
|
88
|
+
transform: sourceCodeTransformer,
|
|
88
89
|
},
|
|
89
90
|
},
|
|
90
91
|
darkMode: {
|
|
@@ -104,6 +105,11 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
104
105
|
|
|
105
106
|
const channel = addons.getChannel();
|
|
106
107
|
|
|
108
|
+
// TODO: import from storybook-dark-mode instead
|
|
109
|
+
// but this is currently leading to Storybook build errors with Storybook 8
|
|
110
|
+
// import { DARK_MODE_EVENT_NAME } from "storybook-dark-mode";
|
|
111
|
+
const DARK_MODE_EVENT_NAME = "DARK_MODE";
|
|
112
|
+
|
|
107
113
|
// our "workaround" above for dynamically setting the docs theme needs a page-reload after
|
|
108
114
|
// the theme has changed to take effect:
|
|
109
115
|
channel.once(DOCS_RENDERED, () => {
|
|
@@ -120,6 +126,29 @@ export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
|
120
126
|
return deepmerge<[T, typeof defaultPreview]>(overrides ?? ({} as T), defaultPreview);
|
|
121
127
|
};
|
|
122
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Custom transformer for the story source code to better fit to our
|
|
131
|
+
* Vue.js code because storybook per default does not render it exactly how
|
|
132
|
+
* we want it to look.
|
|
133
|
+
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
134
|
+
*/
|
|
135
|
+
export const sourceCodeTransformer = (sourceCode: string): string => {
|
|
136
|
+
const replacements = [
|
|
137
|
+
// replace event bindings with shortcut
|
|
138
|
+
{ searchValue: "v-on:", replaceValue: "@" },
|
|
139
|
+
// remove empty event handlers, e.g. @click="()=>({})" will be removed
|
|
140
|
+
{ searchValue: / @\S*['"]\(\)=>\({}\)['"]/g, replaceValue: "" },
|
|
141
|
+
// // remove empty v-binds, e.g. v-bind="{}" will be removed
|
|
142
|
+
{ searchValue: / v-bind=['"]{}['"]/g, replaceValue: "" },
|
|
143
|
+
// // replace boolean shortcuts for true, e.g. disabled="true" will be changed to just disabled
|
|
144
|
+
{ searchValue: /:?(\S*)=['"]true['"]/g, replaceValue: "$1" },
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
return replacements.reduce((code, replacement) => {
|
|
148
|
+
return replaceAll(code, replacement.searchValue, replacement.replaceValue);
|
|
149
|
+
}, sourceCode);
|
|
150
|
+
};
|
|
151
|
+
|
|
123
152
|
/**
|
|
124
153
|
* Custom String.replaceAll implementation using a RegExp
|
|
125
154
|
* 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/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
|
+
};
|