@sit-onyx/storybook-utils 1.0.0-beta.40 → 1.0.0-beta.41
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 +1 -1
- package/src/actions.ts +39 -63
- package/src/preview.ts +3 -0
- package/src/types.ts +0 -49
package/package.json
CHANGED
package/src/actions.ts
CHANGED
|
@@ -1,85 +1,61 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { deepmerge } from "deepmerge-ts";
|
|
1
|
+
import type { Decorator } from "@storybook/vue3";
|
|
3
2
|
import { useArgs } from "storybook/internal/preview-api";
|
|
3
|
+
import type { ArgTypesEnhancer, StrictInputType } from "storybook/internal/types";
|
|
4
4
|
import { isReactive, reactive, watch } from "vue";
|
|
5
|
-
import type { DefineStorybookActionsAndVModelsOptions, ExtractVueEventNames } from ".";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
* the given events as well as implementing v-model handlers so that the Storybook controls are updated when you interact with the component.
|
|
10
|
-
* Should be preferred over manually defining argTypes for *.stories.ts files.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* // Input.stories.ts
|
|
15
|
-
* import { defineStorybookActionsAndVModels } from '@sit-onyx/storybook-utils';
|
|
16
|
-
* import type { Meta } from '@storybook/vue3';
|
|
17
|
-
* import Input from './Input.vue';
|
|
18
|
-
*
|
|
19
|
-
* const meta: Meta<typeof Input> = {
|
|
20
|
-
* title: 'components/Input',
|
|
21
|
-
* ...defineStorybookActionsAndVModels({
|
|
22
|
-
* component: Input,
|
|
23
|
-
* events: ['update:modelValue', 'change'],
|
|
24
|
-
* }),
|
|
25
|
-
* };
|
|
26
|
-
* ```
|
|
7
|
+
* Adds actions for all argTypes of the 'event' category, so that they are logged via the actions plugin.
|
|
27
8
|
*/
|
|
28
|
-
export const
|
|
29
|
-
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
9
|
+
export const enhanceEventArgTypes: ArgTypesEnhancer = ({ argTypes }) => {
|
|
10
|
+
Object.values(argTypes)
|
|
11
|
+
.filter(({ table }) => table?.category === "events")
|
|
12
|
+
.forEach(({ name }) => {
|
|
13
|
+
const eventName = `on${capitalizeFirstLetter(name)}`;
|
|
14
|
+
if (eventName in argTypes) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
argTypes[eventName] = {
|
|
18
|
+
name: eventName,
|
|
19
|
+
table: { disable: true },
|
|
20
|
+
action: eventName,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
return argTypes;
|
|
40
24
|
};
|
|
41
25
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
* otherwise we would use it like "@on-click="..."" which is redundant.
|
|
49
|
-
*
|
|
50
|
-
* So this utility will remove the on[eventName] entry from the Storybook panel/table
|
|
51
|
-
* and register the correct eventName as action so it is logged in the "Actions" tab.
|
|
52
|
-
*
|
|
53
|
-
* @example defineActions(["click", "input"])
|
|
54
|
-
*/
|
|
55
|
-
export const defineActions = <T>(events: ExtractVueEventNames<T>[]): ArgTypes => {
|
|
56
|
-
return events.reduce<ArgTypes>((argTypes, eventName) => {
|
|
57
|
-
argTypes[`on${capitalizeFirstLetter(eventName)}`] = {
|
|
58
|
-
table: { disable: true },
|
|
59
|
-
action: eventName,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
argTypes[eventName] = { control: false };
|
|
63
|
-
return argTypes;
|
|
64
|
-
}, {});
|
|
26
|
+
export type WithVModelDecoratorOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* The matcher for the v-model events.
|
|
29
|
+
* @default /^update:/
|
|
30
|
+
*/
|
|
31
|
+
filter: (argType: StrictInputType) => boolean;
|
|
65
32
|
};
|
|
66
33
|
|
|
67
34
|
/**
|
|
68
|
-
* Defines a custom decorator that will implement event handlers for all v-models
|
|
69
|
-
* so that the Storybook controls are updated live when the user interacts with the component
|
|
35
|
+
* Defines a custom decorator that will implement event handlers for all v-models,
|
|
36
|
+
* so that the Storybook controls are updated live when the user interacts with the component.
|
|
37
|
+
* This ensures that the story and component props stay in sync.
|
|
70
38
|
*
|
|
71
39
|
* @example
|
|
72
40
|
* ```ts
|
|
73
|
-
*
|
|
41
|
+
* // .storybook/preview.ts
|
|
74
42
|
*
|
|
75
43
|
* {
|
|
76
|
-
* decorators: [withVModelDecorator
|
|
44
|
+
* decorators: [withVModelDecorator()]
|
|
77
45
|
* }
|
|
78
46
|
* ```
|
|
79
47
|
*/
|
|
80
|
-
|
|
48
|
+
|
|
49
|
+
export const withVModelDecorator = (options?: WithVModelDecoratorOptions): Decorator => {
|
|
81
50
|
return (story, ctx) => {
|
|
82
|
-
const
|
|
51
|
+
const vModelFilter =
|
|
52
|
+
options?.filter ||
|
|
53
|
+
(({ table, name }) => table?.category === "events" && name.startsWith("update:"));
|
|
54
|
+
|
|
55
|
+
const vModelEvents = Object.values(ctx.argTypes)
|
|
56
|
+
.filter(vModelFilter)
|
|
57
|
+
.map(({ name }) => name);
|
|
58
|
+
|
|
83
59
|
if (!vModelEvents.length) return story();
|
|
84
60
|
|
|
85
61
|
const [args, updateArgs] = useArgs();
|
package/src/preview.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { DARK_MODE_EVENT_NAME } from "storybook-dark-mode";
|
|
|
5
5
|
import { DOCS_RENDERED } from "storybook/internal/core-events";
|
|
6
6
|
import { addons } from "storybook/internal/preview-api";
|
|
7
7
|
import type { ThemeVars } from "storybook/internal/theming";
|
|
8
|
+
import { enhanceEventArgTypes } from "./actions";
|
|
8
9
|
import { requiredGlobalType, withRequired } from "./required";
|
|
9
10
|
import { generateSourceCode } from "./source-code-generator";
|
|
10
11
|
import { ONYX_BREAKPOINTS, createTheme } from "./theme";
|
|
@@ -21,6 +22,7 @@ const themes = {
|
|
|
21
22
|
* - Setup for dark mode (including docs page). Requires addon `storybook-dark-mode` to be enabled in .storybook/main.ts file
|
|
22
23
|
* - Custom Storybook theme using onyx colors (light and dark mode)
|
|
23
24
|
* - Configure viewports / breakpoints as defined by onyx
|
|
25
|
+
* - Logs Vue emits as Storybook events
|
|
24
26
|
*
|
|
25
27
|
* @param overrides Custom preview / overrides, will be deep merged with the default preview.
|
|
26
28
|
*
|
|
@@ -41,6 +43,7 @@ const themes = {
|
|
|
41
43
|
*/
|
|
42
44
|
export const createPreview = <T extends Preview = Preview>(overrides?: T) => {
|
|
43
45
|
const defaultPreview = {
|
|
46
|
+
argTypesEnhancers: [enhanceEventArgTypes],
|
|
44
47
|
globalTypes: {
|
|
45
48
|
...requiredGlobalType,
|
|
46
49
|
},
|
package/src/types.ts
CHANGED
|
@@ -1,52 +1,3 @@
|
|
|
1
|
-
import type { ComponentPropsAndSlots, Meta } from "@storybook/vue3";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extracts all event names defined by e.g. `defineEmits()` from the given Vue component.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* import Input from "./Input.vue";
|
|
9
|
-
* type InputEvents = ExtractVueEventNames<typeof Input>; // e.g. "input" | "change"
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
export type ExtractVueEventNames<VueComponent> =
|
|
13
|
-
Extract<
|
|
14
|
-
// extract all props/events of the vue component that are functions
|
|
15
|
-
ExtractKeysByValueType<
|
|
16
|
-
// this generic type will extract ALL props and events from the given Vue component
|
|
17
|
-
ComponentPropsAndSlots<VueComponent>,
|
|
18
|
-
// emits are declared as functions, so we only take props/events that are functions and ignore the rest
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- We must use any here to match the type defined by Vue
|
|
20
|
-
((...args: any) => any) | undefined
|
|
21
|
-
>,
|
|
22
|
-
// filter out potential function properties by just picking events that start with "on"
|
|
23
|
-
`on${string}`
|
|
24
|
-
> extends `on${infer EventName}`
|
|
25
|
-
? // until now the extracted event names still start with "on" but we want to have the plain event name
|
|
26
|
-
// so we will remove the "on" prefix and uncapitalized the first letter so e.g. "onClick" becomes "click"
|
|
27
|
-
Uncapitalize<EventName>
|
|
28
|
-
: never;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Extracts only the keys from T whose value type satisfies U.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```ts
|
|
35
|
-
* type Test = ExtractKeysByValueType<{ a: boolean, b: number, c: boolean }, boolean>
|
|
36
|
-
* // result: "a" | "c"
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
export type ExtractKeysByValueType<T, U> = { [P in keyof T]: T[P] extends U ? P : never }[keyof T] &
|
|
40
|
-
keyof T;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Options for defining Storybook actions and v-models.
|
|
44
|
-
*/
|
|
45
|
-
export type DefineStorybookActionsAndVModelsOptions<T> = Meta<T> & {
|
|
46
|
-
component: NonNullable<T>;
|
|
47
|
-
events: ExtractVueEventNames<T>[];
|
|
48
|
-
};
|
|
49
|
-
|
|
50
1
|
export type StorybookGlobalType<TValue> = {
|
|
51
2
|
name: string;
|
|
52
3
|
description: string;
|