@sit-onyx/storybook-utils 1.0.0-beta.99 → 1.0.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.
- package/package.json +9 -8
- package/src/breakpoints.ts +17 -0
- package/src/events.ts +7 -7
- package/src/preview.spec.ts +15 -12
- package/src/preview.ts +95 -60
- package/src/theme.ts +1 -4
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
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Schwarz IT KG",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -28,20 +28,21 @@
|
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@storybook/vue3-vite": ">= 9.0.0",
|
|
30
30
|
"@vueless/storybook-dark-mode": ">= 9.0.0",
|
|
31
|
+
"prettier": ">= 3.0.0",
|
|
31
32
|
"storybook": ">= 9.0.0",
|
|
32
33
|
"vue-component-type-helpers": ">= 2",
|
|
33
|
-
"@sit-onyx/
|
|
34
|
-
"@sit-onyx/
|
|
34
|
+
"@sit-onyx/flags": "^1.0.0",
|
|
35
|
+
"@sit-onyx/icons": "^1.0.0"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"deepmerge-ts": "^7.1.5"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
|
-
"storybook": "^9.
|
|
41
|
-
"vue": "3.5.
|
|
42
|
-
"vue-component-type-helpers": "^3.0.
|
|
43
|
-
"@sit-onyx/icons": "^1.0.0
|
|
44
|
-
"@sit-onyx/shared": "^1.0
|
|
41
|
+
"storybook": "^9.1.5",
|
|
42
|
+
"vue": "3.5.21",
|
|
43
|
+
"vue-component-type-helpers": "^3.0.6",
|
|
44
|
+
"@sit-onyx/icons": "^1.0.0",
|
|
45
|
+
"@sit-onyx/shared": "^0.1.0"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "tsc --noEmit",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All available onyx breakpoints / viewports.
|
|
3
|
+
* Key = breakpoint name, value = width in pixels.
|
|
4
|
+
*/
|
|
5
|
+
export const ONYX_BREAKPOINTS = {
|
|
6
|
+
"2xs": 320,
|
|
7
|
+
xs: 577,
|
|
8
|
+
sm: 769,
|
|
9
|
+
md: 993,
|
|
10
|
+
lg: 1441,
|
|
11
|
+
xl: 1921,
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
// "string &" is needed to fix a current Vue issue where a warning is logged for invalid property types
|
|
15
|
+
// when this types is used in a union, see:
|
|
16
|
+
// https://github.com/SchwarzIT/onyx/issues/3290
|
|
17
|
+
export type OnyxBreakpoint = string & keyof typeof ONYX_BREAKPOINTS;
|
package/src/events.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Events } from "vue";
|
|
2
2
|
|
|
3
3
|
// prettier-ignore
|
|
4
|
-
type EventName<T extends Event> =
|
|
4
|
+
type EventName<T extends Event> =
|
|
5
5
|
T extends ClipboardEvent ? "ClipboardEvent"
|
|
6
6
|
: T extends WheelEvent ? "WheelEvent"
|
|
7
7
|
: T extends PointerEvent ? "PointerEvent"
|
|
@@ -311,8 +311,8 @@ export const EVENT_DOC_MAP: EventDocMap = {
|
|
|
311
311
|
},
|
|
312
312
|
onAuxclick: {
|
|
313
313
|
constructor: {
|
|
314
|
-
name: "
|
|
315
|
-
url: "https://developer.mozilla.org/en-US/docs/Web/API/
|
|
314
|
+
name: "PointerEvent",
|
|
315
|
+
url: "https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent",
|
|
316
316
|
},
|
|
317
317
|
event: {
|
|
318
318
|
name: "auxclick",
|
|
@@ -321,8 +321,8 @@ export const EVENT_DOC_MAP: EventDocMap = {
|
|
|
321
321
|
},
|
|
322
322
|
onClick: {
|
|
323
323
|
constructor: {
|
|
324
|
-
name: "
|
|
325
|
-
url: "https://developer.mozilla.org/en-US/docs/Web/API/
|
|
324
|
+
name: "PointerEvent",
|
|
325
|
+
url: "https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent",
|
|
326
326
|
},
|
|
327
327
|
event: {
|
|
328
328
|
name: "click",
|
|
@@ -331,8 +331,8 @@ export const EVENT_DOC_MAP: EventDocMap = {
|
|
|
331
331
|
},
|
|
332
332
|
onContextmenu: {
|
|
333
333
|
constructor: {
|
|
334
|
-
name: "
|
|
335
|
-
url: "https://developer.mozilla.org/en-US/docs/Web/API/
|
|
334
|
+
name: "PointerEvent",
|
|
335
|
+
url: "https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent",
|
|
336
336
|
},
|
|
337
337
|
event: {
|
|
338
338
|
name: "contextmenu",
|
package/src/preview.spec.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import placeholder from "@sit-onyx/icons/placeholder.svg?raw";
|
|
1
|
+
import { flagDE } from "@sit-onyx/flags";
|
|
2
|
+
import { iconBellRing, iconCalendar, iconPlaceholder } from "@sit-onyx/icons";
|
|
4
3
|
import { describe, expect, test } from "vitest";
|
|
5
4
|
import { replaceAll, sourceCodeTransformer } from "./preview.js";
|
|
6
5
|
|
|
7
6
|
describe("preview.ts", () => {
|
|
8
|
-
test("should transform source code and add icon/onyx imports", () => {
|
|
7
|
+
test("should transform source code and add icon/onyx imports", async () => {
|
|
9
8
|
// ACT
|
|
10
|
-
const sourceCode = sourceCodeTransformer(`<template>
|
|
11
|
-
<OnyxTest icon='${
|
|
9
|
+
const sourceCode = await sourceCodeTransformer(`<template>
|
|
10
|
+
<OnyxTest icon='${iconPlaceholder}' test='${iconBellRing}' :obj="{foo:'${replaceAll(iconCalendar, '"', "\\'")}'}" flag='${flagDE}' />
|
|
12
11
|
<OnyxOtherComponent />
|
|
13
12
|
<OnyxComp>Test</OnyxComp>
|
|
14
13
|
</template>`);
|
|
@@ -16,15 +15,19 @@ describe("preview.ts", () => {
|
|
|
16
15
|
// ASSERT
|
|
17
16
|
expect(sourceCode).toBe(`<script lang="ts" setup>
|
|
18
17
|
import { OnyxComp, OnyxOtherComponent, OnyxTest } from "sit-onyx";
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import placeholder from "@sit-onyx/icons/placeholder.svg?raw";
|
|
18
|
+
import { iconBellRing, iconCalendar, iconPlaceholder } from "@sit-onyx/icons";
|
|
19
|
+
import { flagDE } from "@sit-onyx/flags";
|
|
22
20
|
</script>
|
|
23
21
|
|
|
24
22
|
<template>
|
|
25
|
-
<OnyxTest
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
<OnyxTest
|
|
24
|
+
:icon="iconPlaceholder"
|
|
25
|
+
:test="iconBellRing"
|
|
26
|
+
:obj="{foo:iconCalendar}"
|
|
27
|
+
:flag="flagDE"
|
|
28
|
+
/>
|
|
29
|
+
<OnyxOtherComponent />
|
|
30
|
+
<OnyxComp>Test</OnyxComp>
|
|
28
31
|
</template>`);
|
|
29
32
|
});
|
|
30
33
|
});
|
package/src/preview.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getIconImportName } from "@sit-onyx/icons/utils";
|
|
2
1
|
import type { Preview } from "@storybook/vue3-vite";
|
|
3
2
|
import { DARK_MODE_EVENT_NAME } from "@vueless/storybook-dark-mode";
|
|
4
3
|
import { deepmerge } from "deepmerge-ts";
|
|
@@ -132,80 +131,116 @@ export const createPreview = <T extends Preview = Preview>(
|
|
|
132
131
|
*
|
|
133
132
|
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
134
133
|
*/
|
|
135
|
-
export const sourceCodeTransformer = (originalSourceCode: string): string => {
|
|
136
|
-
const RAW_ICONS = import.meta.glob("../node_modules/@sit-onyx/icons/src/assets/*.svg", {
|
|
137
|
-
query: "?raw",
|
|
138
|
-
import: "default",
|
|
139
|
-
eager: true,
|
|
140
|
-
});
|
|
141
|
-
|
|
134
|
+
export const sourceCodeTransformer = async (originalSourceCode: string): Promise<string> => {
|
|
142
135
|
let code = originalSourceCode;
|
|
143
136
|
|
|
144
137
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
138
|
+
* A list of additional JavaScript imports to be added at the top of the source code.
|
|
139
|
+
*
|
|
140
|
+
* key = module/package name to import from, value: set of imports to import from the package
|
|
147
141
|
*/
|
|
148
|
-
const
|
|
149
|
-
(obj, [filePath, content]) => {
|
|
150
|
-
obj[filePath.split("/").at(-1)!.replace(".svg", "")] = content as string;
|
|
151
|
-
return obj;
|
|
152
|
-
},
|
|
153
|
-
{},
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const additionalImports: string[] = [];
|
|
157
|
-
|
|
158
|
-
// add icon imports to the source code for all used onyx icons
|
|
159
|
-
Object.entries(ALL_ICONS).forEach(([iconName, iconContent]) => {
|
|
160
|
-
const importName = getIconImportName(iconName);
|
|
161
|
-
const singleQuotedIconContent = `'${replaceAll(iconContent, '"', "\\'")}'`;
|
|
162
|
-
const escapedIconContent = `"${replaceAll(iconContent, '"', '\\"')}"`;
|
|
163
|
-
|
|
164
|
-
if (code.includes(iconContent)) {
|
|
165
|
-
code = code.replace(
|
|
166
|
-
new RegExp(` (\\S+)=['"]${escapeRegExp(iconContent)}['"]`),
|
|
167
|
-
` :$1="${importName}"`,
|
|
168
|
-
);
|
|
169
|
-
additionalImports.push(`import ${importName} from "@sit-onyx/icons/${iconName}.svg?raw";`);
|
|
170
|
-
} else if (code.includes(singleQuotedIconContent)) {
|
|
171
|
-
// support icons inside objects
|
|
172
|
-
code = code.replace(singleQuotedIconContent, importName);
|
|
173
|
-
additionalImports.push(`import ${importName} from "@sit-onyx/icons/${iconName}.svg?raw";`);
|
|
174
|
-
} else if (code.includes(escapedIconContent)) {
|
|
175
|
-
// support icons inside objects
|
|
176
|
-
code = code.replace(escapedIconContent, importName);
|
|
177
|
-
additionalImports.push(`import ${importName} from "@sit-onyx/icons/${iconName}.svg?raw";`);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
142
|
+
const additionalImports = new Map<string, Set<string>>();
|
|
180
143
|
|
|
181
144
|
// add imports for all used onyx components
|
|
182
145
|
// Set is used here to only include unique components if they are used multiple times
|
|
183
|
-
const usedOnyxComponents =
|
|
184
|
-
|
|
185
|
-
|
|
146
|
+
const usedOnyxComponents = new Set(
|
|
147
|
+
Array.from(code.matchAll(/<(Onyx\w+)(?:\s*\/?)/g))
|
|
148
|
+
.map((match) => match[1])
|
|
149
|
+
.filter((i) => i != undefined),
|
|
150
|
+
);
|
|
151
|
+
additionalImports.set("sit-onyx", usedOnyxComponents);
|
|
186
152
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
153
|
+
/**
|
|
154
|
+
* List of npm packages to replace the source code with.
|
|
155
|
+
* The source code will be checked for any import of the package and (if its used), the code will be replaced by the corresponding import.
|
|
156
|
+
*/
|
|
157
|
+
const packagesToReplace = [
|
|
158
|
+
{ name: "@sit-onyx/icons", data: await import("@sit-onyx/icons") },
|
|
159
|
+
{ name: "@sit-onyx/flags", data: await import("@sit-onyx/flags") },
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
packagesToReplace.forEach((_package) => {
|
|
163
|
+
Object.entries(_package.data).forEach(([name, content]) => {
|
|
164
|
+
const singleQuotedContent = `'${replaceAll(content, '"', "\\'")}'`;
|
|
165
|
+
const escapedContent = `"${replaceAll(content, '"', '\\"')}"`;
|
|
190
166
|
|
|
191
|
-
|
|
167
|
+
const imports = additionalImports.get(_package.name) ?? new Set<string>();
|
|
192
168
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
code.
|
|
169
|
+
if (code.includes(content)) {
|
|
170
|
+
imports.add(name);
|
|
171
|
+
|
|
172
|
+
code = code.replace(
|
|
173
|
+
new RegExp(` (\\S+)=['"]${escapeRegExp(content)}['"]`),
|
|
174
|
+
` :$1="${name}"`,
|
|
175
|
+
);
|
|
176
|
+
} else if (code.includes(singleQuotedContent)) {
|
|
177
|
+
// support values inside objects
|
|
178
|
+
imports.add(name);
|
|
179
|
+
code = code.replace(singleQuotedContent, name);
|
|
180
|
+
} else if (code.includes(escapedContent)) {
|
|
181
|
+
// support values inside objects
|
|
182
|
+
imports.add(name);
|
|
183
|
+
code = code.replace(escapedContent, name);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
additionalImports.set(_package.name, imports);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// remove imports without any data so we don't add empty imports
|
|
191
|
+
additionalImports.forEach((value, key) => {
|
|
192
|
+
if (!value.size) additionalImports.delete(key);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// generate the source code for the additional imports and add them to the top of the code snippet
|
|
196
|
+
if (additionalImports.size > 0) {
|
|
197
|
+
const additionalImportsCode = Array.from(additionalImports.entries()).reduce(
|
|
198
|
+
(code, [packageName, imports]) => {
|
|
199
|
+
if (imports.size) {
|
|
200
|
+
code.push(
|
|
201
|
+
`import { ${Array.from(imports.values()).sort().join(", ")} } from "${packageName}";`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return code;
|
|
205
|
+
},
|
|
206
|
+
[] as string[],
|
|
201
207
|
);
|
|
202
|
-
}
|
|
203
208
|
|
|
204
|
-
|
|
205
|
-
|
|
209
|
+
if (code.startsWith("<script")) {
|
|
210
|
+
const index = code.indexOf("\n");
|
|
211
|
+
const hasOtherImports = code.includes("import {");
|
|
212
|
+
code =
|
|
213
|
+
code.slice(0, index) +
|
|
214
|
+
additionalImportsCode.join("\n") +
|
|
215
|
+
(!hasOtherImports ? "\n" : "") +
|
|
216
|
+
code.slice(index);
|
|
217
|
+
} else {
|
|
218
|
+
code = `<script lang="ts" setup>
|
|
219
|
+
${additionalImportsCode.join("\n")}
|
|
206
220
|
</script>
|
|
207
221
|
|
|
208
222
|
${code}`;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const { format } = await import("prettier/standalone");
|
|
228
|
+
const parserHtml = await import("prettier/parser-html");
|
|
229
|
+
|
|
230
|
+
code = await format(code, {
|
|
231
|
+
parser: "vue",
|
|
232
|
+
plugins: [parserHtml],
|
|
233
|
+
htmlWhitespaceSensitivity: "ignore",
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// trim code to remove trailing newlines that are added by prettier
|
|
237
|
+
code = code.trim();
|
|
238
|
+
} catch (e) {
|
|
239
|
+
// eslint-disable-next-line no-console -- if the formatting fails, there is usually an issue with our code so we want to inform the user that the formatting failed
|
|
240
|
+
console.error("Error while formatting Storybook code snippet:", e);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return code;
|
|
209
244
|
};
|
|
210
245
|
|
|
211
246
|
/**
|
package/src/theme.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ONYX_BREAKPOINTS as RAW_ONYX_BREAKPOINTS,
|
|
3
|
-
type OnyxBreakpoint,
|
|
4
|
-
} from "@sit-onyx/shared/breakpoints";
|
|
5
1
|
import { create, type ThemeVars } from "storybook/internal/theming";
|
|
6
2
|
import type { Viewport } from "storybook/internal/viewport";
|
|
3
|
+
import { ONYX_BREAKPOINTS as RAW_ONYX_BREAKPOINTS, type OnyxBreakpoint } from "./breakpoints.js";
|
|
7
4
|
|
|
8
5
|
export type BrandDetails = Pick<ThemeVars, "brandTitle" | "brandImage" | "brandUrl">;
|
|
9
6
|
|