@sit-onyx/storybook-utils 0.0.0-20250804145452
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/LICENSE.txt +190 -0
- package/README.md +15 -0
- package/package.json +53 -0
- package/src/actions.spec.ts +29 -0
- package/src/actions.ts +188 -0
- package/src/events.ts +777 -0
- package/src/index.ts +5 -0
- package/src/preview.spec.ts +30 -0
- package/src/preview.ts +234 -0
- package/src/required.ts +35 -0
- package/src/sbType.spec.ts +38 -0
- package/src/sbType.ts +104 -0
- package/src/style.css +161 -0
- package/src/theme.ts +127 -0
- package/src/types.ts +11 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { iconBellRing, iconCalendar, iconPlaceholder } from "@sit-onyx/icons";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { replaceAll, sourceCodeTransformer } from "./preview.js";
|
|
4
|
+
|
|
5
|
+
describe("preview.ts", () => {
|
|
6
|
+
test("should transform source code and add icon/onyx imports", async () => {
|
|
7
|
+
// ACT
|
|
8
|
+
const sourceCode = await sourceCodeTransformer(`<template>
|
|
9
|
+
<OnyxTest icon='${iconPlaceholder}' test='${iconBellRing}' :obj="{foo:'${replaceAll(iconCalendar, '"', "\\'")}'}" />
|
|
10
|
+
<OnyxOtherComponent />
|
|
11
|
+
<OnyxComp>Test</OnyxComp>
|
|
12
|
+
</template>`);
|
|
13
|
+
|
|
14
|
+
// ASSERT
|
|
15
|
+
expect(sourceCode).toBe(`<script lang="ts" setup>
|
|
16
|
+
import { OnyxComp, OnyxOtherComponent, OnyxTest } from "sit-onyx";
|
|
17
|
+
import { iconBellRing, iconCalendar, iconPlaceholder } from "@sit-onyx/icons";
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<OnyxTest
|
|
22
|
+
:icon="iconPlaceholder"
|
|
23
|
+
:test="iconBellRing"
|
|
24
|
+
:obj="{foo:iconCalendar}"
|
|
25
|
+
/>
|
|
26
|
+
<OnyxOtherComponent />
|
|
27
|
+
<OnyxComp>Test</OnyxComp>
|
|
28
|
+
</template>`);
|
|
29
|
+
});
|
|
30
|
+
});
|
package/src/preview.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import type { Preview } from "@storybook/vue3-vite";
|
|
2
|
+
import { DARK_MODE_EVENT_NAME } from "@vueless/storybook-dark-mode";
|
|
3
|
+
import { deepmerge } from "deepmerge-ts";
|
|
4
|
+
import { DOCS_RENDERED } from "storybook/internal/core-events";
|
|
5
|
+
import { addons } from "storybook/internal/preview-api";
|
|
6
|
+
import type { ThemeVars } from "storybook/internal/theming";
|
|
7
|
+
import { enhanceEventArgTypes } from "./actions.js";
|
|
8
|
+
import { requiredGlobalType, withRequired } from "./required.js";
|
|
9
|
+
import { ONYX_BREAKPOINTS, createTheme, type BrandDetails } from "./theme.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a default Storybook preview configuration for 'onyx' with the following features:
|
|
13
|
+
* - Improved controls (sorting and expanded controls so descriptions etc. are also shown in a single story)
|
|
14
|
+
* - Improved Vue-specific code highlighting (e.g. using `@` instead of `v-on:`)
|
|
15
|
+
* - Setup for dark mode (including docs page). Requires addon `@vueless/storybook-dark-mode` to be enabled in .storybook/main.ts file
|
|
16
|
+
* - Custom Storybook theme using onyx colors (light and dark mode)
|
|
17
|
+
* - Configure viewports / breakpoints as defined by onyx
|
|
18
|
+
* - Logs Vue emits as Storybook events
|
|
19
|
+
*
|
|
20
|
+
* @param overrides Custom preview / overrides, will be deep merged with the default preview.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* // .storybook/preview.ts
|
|
25
|
+
* const preview = {
|
|
26
|
+
* // you need to destructure here because as of Storybook 7.6
|
|
27
|
+
* // it can not statically analyze that the `preview` variable is an object
|
|
28
|
+
* ...createPreview({
|
|
29
|
+
* // custom preview / overrides
|
|
30
|
+
* },
|
|
31
|
+
* }),
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* export default preview;
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const createPreview = <T extends Preview = Preview>(
|
|
38
|
+
overrides?: T,
|
|
39
|
+
brandDetails?: BrandDetails,
|
|
40
|
+
) => {
|
|
41
|
+
const themes = {
|
|
42
|
+
light: createTheme("light", brandDetails),
|
|
43
|
+
dark: createTheme("dark", brandDetails),
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
const defaultPreview = {
|
|
47
|
+
argTypesEnhancers: [enhanceEventArgTypes],
|
|
48
|
+
initialGlobals: {
|
|
49
|
+
["requiredMode" satisfies keyof typeof requiredGlobalType]: "required",
|
|
50
|
+
backgrounds: { value: "currentTheme" },
|
|
51
|
+
},
|
|
52
|
+
globalTypes: {
|
|
53
|
+
...requiredGlobalType,
|
|
54
|
+
},
|
|
55
|
+
decorators: [withRequired],
|
|
56
|
+
parameters: {
|
|
57
|
+
controls: {
|
|
58
|
+
matchers: {
|
|
59
|
+
color: /(background|color)$/i,
|
|
60
|
+
date: /Date$/i,
|
|
61
|
+
},
|
|
62
|
+
sort: "requiredFirst",
|
|
63
|
+
// needed to also show props/events descriptions etc. when opening a single story
|
|
64
|
+
expanded: true,
|
|
65
|
+
exclude: ["ref", "ref_for", "ref_key", "class", "style", "key", "$slots"],
|
|
66
|
+
},
|
|
67
|
+
docs: {
|
|
68
|
+
// see: https://github.com/hipstersmoothie/storybook-dark-mode/issues/127#issuecomment-840701971
|
|
69
|
+
get theme(): ThemeVars {
|
|
70
|
+
const isDark = parent.document.body.classList.contains("dark");
|
|
71
|
+
|
|
72
|
+
if (isDark) {
|
|
73
|
+
document.body.classList.remove("light");
|
|
74
|
+
document.body.classList.add("dark");
|
|
75
|
+
} else {
|
|
76
|
+
document.body.classList.remove("dark");
|
|
77
|
+
document.body.classList.add("light");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return isDark ? themes.dark : themes.light;
|
|
81
|
+
},
|
|
82
|
+
source: {
|
|
83
|
+
language: "html",
|
|
84
|
+
/**
|
|
85
|
+
* Use a custom transformer for the story source code to better fit to our
|
|
86
|
+
* Vue.js code because storybook per default does not render it exactly how
|
|
87
|
+
* we want it to look.
|
|
88
|
+
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
89
|
+
*/
|
|
90
|
+
transform: sourceCodeTransformer,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
darkMode: {
|
|
94
|
+
stylePreview: true,
|
|
95
|
+
light: themes.light,
|
|
96
|
+
dark: themes.dark,
|
|
97
|
+
},
|
|
98
|
+
backgrounds: {
|
|
99
|
+
options: {
|
|
100
|
+
currentTheme: { name: "Dynamic", value: "var(--onyx-color-base-background-tinted)" },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
viewport: {
|
|
104
|
+
options: ONYX_BREAKPOINTS,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
} satisfies Preview;
|
|
108
|
+
|
|
109
|
+
const channel = addons.getChannel();
|
|
110
|
+
|
|
111
|
+
// our "workaround" above for dynamically setting the docs theme needs a page-reload after
|
|
112
|
+
// the theme has changed to take effect:
|
|
113
|
+
channel.once(DOCS_RENDERED, () => {
|
|
114
|
+
// the DARK_MODE_EVENT_NAME is emitted once after the docs have been rendered for the initial theme.
|
|
115
|
+
// We only want to reload the page on theme changes, that's why we use .once() to add the event listener
|
|
116
|
+
// only after the initial dark mode change event has been fired. Otherwise we would get an infinite loop.
|
|
117
|
+
channel.once(DARK_MODE_EVENT_NAME, () => {
|
|
118
|
+
channel.on(DARK_MODE_EVENT_NAME, () => {
|
|
119
|
+
window.location.reload();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return deepmerge<[T, typeof defaultPreview]>(overrides ?? ({} as T), defaultPreview);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Custom transformer for the story source code to support improved source code generation.
|
|
129
|
+
* and add imports for all used onyx icons so icon imports are displayed in the source code
|
|
130
|
+
* instead of the the raw SVG content.
|
|
131
|
+
*
|
|
132
|
+
* @see https://storybook.js.org/docs/react/api/doc-block-source
|
|
133
|
+
*/
|
|
134
|
+
export const sourceCodeTransformer = async (originalSourceCode: string): Promise<string> => {
|
|
135
|
+
const ALL_ICONS = await import("@sit-onyx/icons");
|
|
136
|
+
|
|
137
|
+
let code = originalSourceCode;
|
|
138
|
+
|
|
139
|
+
const additionalImports: string[] = [];
|
|
140
|
+
|
|
141
|
+
// add icon imports to the source code for all used onyx icons
|
|
142
|
+
const usedIcons = new Set<string>();
|
|
143
|
+
|
|
144
|
+
Object.entries(ALL_ICONS).forEach(([iconName, iconContent]) => {
|
|
145
|
+
const singleQuotedIconContent = `'${replaceAll(iconContent, '"', "\\'")}'`;
|
|
146
|
+
const escapedIconContent = `"${replaceAll(iconContent, '"', '\\"')}"`;
|
|
147
|
+
|
|
148
|
+
if (code.includes(iconContent)) {
|
|
149
|
+
usedIcons.add(iconName);
|
|
150
|
+
|
|
151
|
+
code = code.replace(
|
|
152
|
+
new RegExp(` (\\S+)=['"]${escapeRegExp(iconContent)}['"]`),
|
|
153
|
+
` :$1="${iconName}"`,
|
|
154
|
+
);
|
|
155
|
+
} else if (code.includes(singleQuotedIconContent)) {
|
|
156
|
+
// support icons inside objects
|
|
157
|
+
usedIcons.add(iconName);
|
|
158
|
+
code = code.replace(singleQuotedIconContent, iconName);
|
|
159
|
+
} else if (code.includes(escapedIconContent)) {
|
|
160
|
+
// support icons inside objects
|
|
161
|
+
usedIcons.add(iconName);
|
|
162
|
+
code = code.replace(escapedIconContent, iconName);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (usedIcons.size > 0) {
|
|
167
|
+
additionalImports.push(
|
|
168
|
+
`import { ${Array.from(usedIcons.values()).sort().join(", ")} } from "@sit-onyx/icons";`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// add imports for all used onyx components
|
|
173
|
+
// Set is used here to only include unique components if they are used multiple times
|
|
174
|
+
const usedOnyxComponents = [
|
|
175
|
+
...new Set(Array.from(code.matchAll(/<(Onyx\w+)(?:\s*\/?)/g)).map((match) => match[1])),
|
|
176
|
+
].sort();
|
|
177
|
+
|
|
178
|
+
if (usedOnyxComponents.length > 0) {
|
|
179
|
+
additionalImports.unshift(`import { ${usedOnyxComponents.join(", ")} } from "sit-onyx";`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (additionalImports.length > 1) {
|
|
183
|
+
if (code.startsWith("<script")) {
|
|
184
|
+
const index = code.indexOf("\n");
|
|
185
|
+
const hasOtherImports = code.includes("import {");
|
|
186
|
+
code =
|
|
187
|
+
code.slice(0, index) +
|
|
188
|
+
additionalImports.join("\n") +
|
|
189
|
+
(!hasOtherImports ? "\n" : "") +
|
|
190
|
+
code.slice(index);
|
|
191
|
+
} else {
|
|
192
|
+
code = `<script lang="ts" setup>
|
|
193
|
+
${additionalImports.join("\n")}
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
${code}`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const { format } = await import("prettier/standalone");
|
|
202
|
+
const parserHtml = await import("prettier/parser-html");
|
|
203
|
+
|
|
204
|
+
code = await format(code, {
|
|
205
|
+
parser: "vue",
|
|
206
|
+
plugins: [parserHtml],
|
|
207
|
+
htmlWhitespaceSensitivity: "ignore",
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// trim code to remove trailing newlines that are added by prettier
|
|
211
|
+
code = code.trim();
|
|
212
|
+
} catch (e) {
|
|
213
|
+
// 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
|
|
214
|
+
console.error("Error while formatting Storybook code snippet:", e);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return code;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Custom String.replaceAll implementation using a RegExp
|
|
222
|
+
* because String.replaceAll() is not available in our specified EcmaScript target in tsconfig.json
|
|
223
|
+
*/
|
|
224
|
+
export const replaceAll = (value: string, searchValue: string | RegExp, replaceValue: string) => {
|
|
225
|
+
return value.replace(new RegExp(searchValue, "gi"), replaceValue);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Escapes the given string value to be used in `new RegExp()`.
|
|
230
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
|
|
231
|
+
*/
|
|
232
|
+
export const escapeRegExp = (string: string) => {
|
|
233
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
|
234
|
+
};
|
package/src/required.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Decorator } from "@storybook/vue3-vite";
|
|
2
|
+
import { ref, watch } from "vue";
|
|
3
|
+
import type { StorybookGlobalType } from "./types.js";
|
|
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
|
+
toolbar: {
|
|
12
|
+
icon: "flag",
|
|
13
|
+
items: [
|
|
14
|
+
{ value: "required", right: "*", title: "Required indicator" },
|
|
15
|
+
{ value: "optional", right: "(optional)", title: "Optional indicator" },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
} satisfies StorybookGlobalType<RequiredIndicator>,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const requiredMode = ref<RequiredIndicator>("required");
|
|
22
|
+
|
|
23
|
+
export const withRequired: Decorator = (Story, context) => {
|
|
24
|
+
watch(
|
|
25
|
+
() => context.globals.requiredMode as RequiredIndicator,
|
|
26
|
+
(newRequiredMode) => (requiredMode.value = newRequiredMode),
|
|
27
|
+
{ immediate: true },
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
components: { Story },
|
|
32
|
+
setup: () => ({ requiredMode }),
|
|
33
|
+
template: `<div :class="{ ['onyx-use-optional']: requiredMode === 'optional' }"> <story /> </div>`,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SBType } from "storybook/internal/types";
|
|
2
|
+
import { describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { walkTree } from "./sbType.js";
|
|
4
|
+
|
|
5
|
+
describe("walkTree", () => {
|
|
6
|
+
test.each<{ input: SBType; expected: SBType["name"][] }>([
|
|
7
|
+
{ input: { name: "array", value: { name: "number" } }, expected: ["array", "number"] },
|
|
8
|
+
{ input: { name: "object", value: { a: { name: "number" } } }, expected: ["object", "number"] },
|
|
9
|
+
{ input: { name: "enum", value: ["a"] }, expected: ["enum"] },
|
|
10
|
+
{
|
|
11
|
+
input: { name: "intersection", value: [{ name: "number" }] },
|
|
12
|
+
expected: ["intersection", "number"],
|
|
13
|
+
},
|
|
14
|
+
{ input: { name: "union", value: [{ name: "number" }] }, expected: ["union", "number"] },
|
|
15
|
+
{ input: { name: "other", value: "a" }, expected: ["other"] },
|
|
16
|
+
])("should execute cb for $input.name correctly", ({ input, expected }) => {
|
|
17
|
+
const spy = vi.fn<(p: SBType) => void>();
|
|
18
|
+
const result = walkTree(input, spy);
|
|
19
|
+
|
|
20
|
+
expect(result).toBeUndefined();
|
|
21
|
+
expect(spy).toHaveBeenCalledTimes(expected.length);
|
|
22
|
+
const nameCalls = spy.mock.calls.map(([{ name }]) => name);
|
|
23
|
+
expect(nameCalls).toMatchObject(expected);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should return value if there is any returned", () => {
|
|
27
|
+
const target: SBType = { name: "number", raw: "here" };
|
|
28
|
+
const overshoot: SBType = { name: "boolean", raw: "here" };
|
|
29
|
+
const parent: SBType = { name: "intersection", value: [target, overshoot] };
|
|
30
|
+
const returned = 42;
|
|
31
|
+
const spy = vi.fn((p: SBType) => (p.raw === "here" ? returned : undefined));
|
|
32
|
+
const result = walkTree({ name: "union", value: [parent] }, spy);
|
|
33
|
+
|
|
34
|
+
expect(spy).toHaveBeenCalledTimes(3);
|
|
35
|
+
expect(spy).toHaveBeenLastCalledWith(target, parent);
|
|
36
|
+
expect(result).toBe(returned);
|
|
37
|
+
});
|
|
38
|
+
});
|
package/src/sbType.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ArgTypesEnhancer,
|
|
3
|
+
InputType,
|
|
4
|
+
SBType,
|
|
5
|
+
StrictInputType,
|
|
6
|
+
} from "storybook/internal/types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Call a function `cb` for every type node in the storybook type tree.
|
|
10
|
+
* @param inputType the root type
|
|
11
|
+
* @param cb the function that is called for every type. If any non-nullish value is returned by `cb` the execution is stopped and this value is returned.
|
|
12
|
+
* @param parent optional, the parent type. Is only used as input for the `cb` function and provided when recursing.
|
|
13
|
+
* @returns the first non-nullish value that is returned by `cb`
|
|
14
|
+
*/
|
|
15
|
+
export const walkTree = <TValue>(
|
|
16
|
+
inputType: SBType,
|
|
17
|
+
cb: (sb: SBType, parent?: SBType) => TValue,
|
|
18
|
+
parent?: SBType,
|
|
19
|
+
): TValue | undefined => {
|
|
20
|
+
const shouldReturn = cb(inputType, parent);
|
|
21
|
+
if (shouldReturn) {
|
|
22
|
+
return shouldReturn;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (inputType.name === "union" || inputType.name === "intersection") {
|
|
26
|
+
return inputType.value.reduce<TValue | undefined>(
|
|
27
|
+
(prev, it) => prev ?? walkTree(it, cb, inputType),
|
|
28
|
+
undefined,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (inputType.name === "array") {
|
|
32
|
+
return walkTree(inputType.value, cb, inputType);
|
|
33
|
+
}
|
|
34
|
+
if (inputType.name === "object") {
|
|
35
|
+
return Object.values(inputType.value).reduce<TValue | undefined>(
|
|
36
|
+
(prev, it) => prev ?? walkTree(it, cb, inputType),
|
|
37
|
+
undefined,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const SB_TYPE_CONTROL_MAP: Partial<Record<SBType["name"], InputType["control"]>> = {
|
|
43
|
+
boolean: { type: "boolean" },
|
|
44
|
+
string: { type: "text" },
|
|
45
|
+
number: { type: "number" },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const getFormInjectedParent = (symbol: string, inputType?: StrictInputType) => {
|
|
49
|
+
if (!inputType?.type || inputType.table?.defaultValue?.summary !== symbol) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return walkTree(inputType.type, (elem, parent) =>
|
|
54
|
+
elem.name === "symbol" || (elem.name === "other" && elem.value === "unique symbol")
|
|
55
|
+
? parent
|
|
56
|
+
: undefined,
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Can be used to create an `ArgTypesEnhancer` which matches a Symbol that is used as default Prop.
|
|
62
|
+
* When it matches the passed description text will be set.
|
|
63
|
+
*
|
|
64
|
+
* @param symbol description of the symbol that should be matched.
|
|
65
|
+
* @param description the description text that should be shown in Storybook for this prop.
|
|
66
|
+
* @returns An `ArgTypesEnhancer` which can be passed to storybook.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { createSymbolArgTypeEnhancer } from "@sit-onyx/storybook-utils";
|
|
71
|
+
*
|
|
72
|
+
* export const enhanceFormInjectedSymbol = createSymbolArgTypeEnhancer(
|
|
73
|
+
* "FORM_INJECTED_SYMBOL",
|
|
74
|
+
* "If no value (or `undefined`) is provided, `FORM_INJECTED_SYMBOL` is the internal default value for this prop.\n" +
|
|
75
|
+
* "In that case the props value will be derived from it's parent form (if it exists).\n",
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export const createSymbolArgTypeEnhancer = (
|
|
80
|
+
symbol: string,
|
|
81
|
+
description: string,
|
|
82
|
+
): ArgTypesEnhancer => {
|
|
83
|
+
return (context) => {
|
|
84
|
+
Object.values(context.argTypes)
|
|
85
|
+
.map((argType) => {
|
|
86
|
+
const parent = getFormInjectedParent(symbol, argType);
|
|
87
|
+
return { argType, parent };
|
|
88
|
+
})
|
|
89
|
+
.filter(({ parent }) => parent)
|
|
90
|
+
.forEach(({ argType, parent }) => {
|
|
91
|
+
const firstAvailableControl = walkTree(
|
|
92
|
+
parent || argType.type!,
|
|
93
|
+
(sb) => SB_TYPE_CONTROL_MAP[sb.name],
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (firstAvailableControl && argType.table?.defaultValue) {
|
|
97
|
+
argType.control = firstAvailableControl;
|
|
98
|
+
argType.table.defaultValue.detail = description;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return context.argTypes;
|
|
103
|
+
};
|
|
104
|
+
};
|
package/src/style.css
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
.onyx-disclaimer {
|
|
2
|
+
border-radius: 0.5rem;
|
|
3
|
+
padding: 1rem;
|
|
4
|
+
line-height: var(--onyx-font-line-height-md);
|
|
5
|
+
margin: 1rem 0;
|
|
6
|
+
|
|
7
|
+
/* same color as VitePress var(--vp-c-tip-soft) */
|
|
8
|
+
background-color: color-mix(in srgb, var(--onyx-color-base-info-400) 25%, transparent);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.onyx-disclaimer__title {
|
|
12
|
+
font-weight: var(--onyx-font-weight-semibold);
|
|
13
|
+
margin-bottom: 0.5rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.onyx-disclaimer p {
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* the Storybook table of content headline is always black so we need to manually set it for the dark mode */
|
|
21
|
+
#storybook-docs .sbdocs-wrapper > aside > nav > h2 {
|
|
22
|
+
/* same as Storybook color "textMuted" inside ./theme.ts */
|
|
23
|
+
color: var(--onyx-color-text-icons-neutral-medium);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* To prevent bg flashing when changing between elements in darkmode */
|
|
27
|
+
.sb-preparing-story,
|
|
28
|
+
.sb-preparing-docs {
|
|
29
|
+
background-color: transparent;
|
|
30
|
+
}
|
|
31
|
+
/* removed placeholder for the same reason */
|
|
32
|
+
.sb-previewBlock,
|
|
33
|
+
.sb-argstableBlock {
|
|
34
|
+
display: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
Copy of ../sit-onyx/src/styles/variables/themes/onyx.css
|
|
39
|
+
TODO: Find a way to automate this
|
|
40
|
+
*/
|
|
41
|
+
:where(:root),
|
|
42
|
+
.onyx-theme-default {
|
|
43
|
+
--onyx-color-neutral-steel-100: #fafbfc;
|
|
44
|
+
--onyx-color-neutral-steel-200: #e3eaf0;
|
|
45
|
+
--onyx-color-neutral-steel-300: #c9d6e0;
|
|
46
|
+
--onyx-color-neutral-steel-400: #9db3c4;
|
|
47
|
+
--onyx-color-neutral-steel-500: #7392aa;
|
|
48
|
+
--onyx-color-neutral-steel-600: #506e84;
|
|
49
|
+
--onyx-color-neutral-steel-700: #3e596e;
|
|
50
|
+
--onyx-color-neutral-steel-800: #31495c;
|
|
51
|
+
--onyx-color-neutral-steel-900: #22384a;
|
|
52
|
+
--onyx-color-neutral-steel-1000: #11212d;
|
|
53
|
+
--onyx-color-neutral-steel-1100: #081723;
|
|
54
|
+
--onyx-color-neutral-steel-1200: #000e19;
|
|
55
|
+
--onyx-color-themed-primary-100: #e8fcfc;
|
|
56
|
+
--onyx-color-themed-primary-200: #bbeaed;
|
|
57
|
+
--onyx-color-themed-primary-300: #79dde2;
|
|
58
|
+
--onyx-color-themed-primary-400: #3dd0d8;
|
|
59
|
+
--onyx-color-themed-primary-500: #00c3cd;
|
|
60
|
+
--onyx-color-themed-primary-600: #00adb5;
|
|
61
|
+
--onyx-color-themed-primary-700: #00969d;
|
|
62
|
+
--onyx-color-themed-primary-800: #008085;
|
|
63
|
+
--onyx-color-themed-primary-900: #00696e;
|
|
64
|
+
--onyx-color-themed-primary-1000: #005356;
|
|
65
|
+
--onyx-color-themed-primary-1100: #003c3e;
|
|
66
|
+
--onyx-color-themed-primary-1200: #002626;
|
|
67
|
+
--onyx-color-themed-secondary-100: var(--onyx-color-themed-primary-100);
|
|
68
|
+
--onyx-color-themed-secondary-200: var(--onyx-color-themed-primary-200);
|
|
69
|
+
--onyx-color-themed-secondary-300: var(--onyx-color-themed-primary-300);
|
|
70
|
+
--onyx-color-themed-secondary-400: var(--onyx-color-themed-primary-400);
|
|
71
|
+
--onyx-color-themed-secondary-500: var(--onyx-color-themed-primary-500);
|
|
72
|
+
--onyx-color-themed-secondary-600: var(--onyx-color-themed-primary-600);
|
|
73
|
+
--onyx-color-themed-secondary-700: var(--onyx-color-themed-primary-700);
|
|
74
|
+
--onyx-color-themed-secondary-800: var(--onyx-color-themed-primary-800);
|
|
75
|
+
--onyx-color-themed-secondary-900: var(--onyx-color-themed-primary-900);
|
|
76
|
+
--onyx-color-themed-secondary-1000: var(--onyx-color-themed-primary-1000);
|
|
77
|
+
--onyx-color-themed-secondary-1100: var(--onyx-color-themed-primary-1100);
|
|
78
|
+
--onyx-color-themed-secondary-1200: var(--onyx-color-themed-primary-1200);
|
|
79
|
+
--onyx-color-universal-grayscale-black: #000000;
|
|
80
|
+
--onyx-color-universal-grayscale-white: #ffffff;
|
|
81
|
+
--onyx-color-universal-green-100: #ecf8f2;
|
|
82
|
+
--onyx-color-universal-green-200: #c6e4d5;
|
|
83
|
+
--onyx-color-universal-green-300: #98d1b3;
|
|
84
|
+
--onyx-color-universal-green-400: #6ebe94;
|
|
85
|
+
--onyx-color-universal-green-500: #44aa75;
|
|
86
|
+
--onyx-color-universal-green-600: #3b9b69;
|
|
87
|
+
--onyx-color-universal-green-700: #328c5e;
|
|
88
|
+
--onyx-color-universal-green-800: #297d52;
|
|
89
|
+
--onyx-color-universal-green-900: #216d46;
|
|
90
|
+
--onyx-color-universal-green-1000: #185e3a;
|
|
91
|
+
--onyx-color-universal-green-1100: #064023;
|
|
92
|
+
--onyx-color-universal-green-1200: #064023;
|
|
93
|
+
--onyx-color-universal-orange-100: #faf6f2;
|
|
94
|
+
--onyx-color-universal-orange-200: #f8e7d8;
|
|
95
|
+
--onyx-color-universal-orange-300: #f6d1b1;
|
|
96
|
+
--onyx-color-universal-orange-400: #f4b57e;
|
|
97
|
+
--onyx-color-universal-orange-500: #f2994a;
|
|
98
|
+
--onyx-color-universal-orange-600: #d98841;
|
|
99
|
+
--onyx-color-universal-orange-700: #bf7737;
|
|
100
|
+
--onyx-color-universal-orange-800: #a6662e;
|
|
101
|
+
--onyx-color-universal-orange-900: #8c5625;
|
|
102
|
+
--onyx-color-universal-orange-1000: #73451c;
|
|
103
|
+
--onyx-color-universal-orange-1100: #593412;
|
|
104
|
+
--onyx-color-universal-orange-1200: #402309;
|
|
105
|
+
--onyx-color-universal-purple-100: #f4f1f8;
|
|
106
|
+
--onyx-color-universal-purple-200: #dbcfea;
|
|
107
|
+
--onyx-color-universal-purple-300: #c2addc;
|
|
108
|
+
--onyx-color-universal-purple-400: #a98ace;
|
|
109
|
+
--onyx-color-universal-purple-500: #9068c0;
|
|
110
|
+
--onyx-color-universal-purple-600: #805aae;
|
|
111
|
+
--onyx-color-universal-purple-700: #704c9b;
|
|
112
|
+
--onyx-color-universal-purple-800: #603e89;
|
|
113
|
+
--onyx-color-universal-purple-900: #513077;
|
|
114
|
+
--onyx-color-universal-purple-1000: #412265;
|
|
115
|
+
--onyx-color-universal-purple-1100: #311452;
|
|
116
|
+
--onyx-color-universal-purple-1200: #210640;
|
|
117
|
+
--onyx-color-universal-quantitatives-100: #005795;
|
|
118
|
+
--onyx-color-universal-quantitatives-200: #ff8a25;
|
|
119
|
+
--onyx-color-universal-quantitatives-300: #e51718;
|
|
120
|
+
--onyx-color-universal-quantitatives-400: #36b16b;
|
|
121
|
+
--onyx-color-universal-quantitatives-500: #56d8fc;
|
|
122
|
+
--onyx-color-universal-quantitatives-600: #ff9990;
|
|
123
|
+
--onyx-color-universal-quantitatives-700: #ff3fd1;
|
|
124
|
+
--onyx-color-universal-quantitatives-800: #3c6475;
|
|
125
|
+
--onyx-color-universal-quantitatives-900: #c4bc81;
|
|
126
|
+
--onyx-color-universal-quantitatives-1000: #c3143f;
|
|
127
|
+
--onyx-color-universal-quantitatives-1100: #a09dfa;
|
|
128
|
+
--onyx-color-universal-quantitatives-1200: #00bcc6;
|
|
129
|
+
--onyx-color-universal-red-100: #fbefee;
|
|
130
|
+
--onyx-color-universal-red-200: #f1d2d1;
|
|
131
|
+
--onyx-color-universal-red-300: #e6a7a5;
|
|
132
|
+
--onyx-color-universal-red-400: #dc716e;
|
|
133
|
+
--onyx-color-universal-red-500: #d1332f;
|
|
134
|
+
--onyx-color-universal-red-600: #bc2d2a;
|
|
135
|
+
--onyx-color-universal-red-700: #a82824;
|
|
136
|
+
--onyx-color-universal-red-800: #93221f;
|
|
137
|
+
--onyx-color-universal-red-900: #7e1d19;
|
|
138
|
+
--onyx-color-universal-red-1000: #691714;
|
|
139
|
+
--onyx-color-universal-red-1100: #400c09;
|
|
140
|
+
--onyx-color-universal-red-1200: #400c09;
|
|
141
|
+
--onyx-number-radius-100: 0.125rem;
|
|
142
|
+
--onyx-number-radius-200: 0.25rem;
|
|
143
|
+
--onyx-number-radius-300: 0.5rem;
|
|
144
|
+
--onyx-number-radius-400: 1rem;
|
|
145
|
+
--onyx-number-radius-500: 2rem;
|
|
146
|
+
--onyx-number-radius-600: 62.5rem;
|
|
147
|
+
--onyx-number-spacing-0: 0rem;
|
|
148
|
+
--onyx-number-spacing-100: 0.125rem;
|
|
149
|
+
--onyx-number-spacing-200: 0.25rem;
|
|
150
|
+
--onyx-number-spacing-250: 0.375rem;
|
|
151
|
+
--onyx-number-spacing-300: 0.5rem;
|
|
152
|
+
--onyx-number-spacing-325: 0.625rem;
|
|
153
|
+
--onyx-number-spacing-350: 0.75rem;
|
|
154
|
+
--onyx-number-spacing-400: 1rem;
|
|
155
|
+
--onyx-number-spacing-500: 1.5rem;
|
|
156
|
+
--onyx-number-spacing-600: 2rem;
|
|
157
|
+
--onyx-number-spacing-700: 3rem;
|
|
158
|
+
--onyx-number-spacing-800: 4rem;
|
|
159
|
+
--onyx-number-spacing-900: 6rem;
|
|
160
|
+
--onyx-number-spacing-950: 8rem;
|
|
161
|
+
}
|