@storybook/csf
Version:
Component Story Format (CSF) utilities
542 lines (536 loc) • 21.6 kB
TypeScript
import { Simplify, RemoveIndexSignature, UnionToIntersection } from 'type-fest';
interface SBBaseType {
required?: boolean;
raw?: string;
}
type SBScalarType = SBBaseType & {
name: 'boolean' | 'string' | 'number' | 'function' | 'symbol';
};
type SBArrayType = SBBaseType & {
name: 'array';
value: SBType;
};
type SBObjectType = SBBaseType & {
name: 'object';
value: Record<string, SBType>;
};
type SBEnumType = SBBaseType & {
name: 'enum';
value: (string | number)[];
};
type SBIntersectionType = SBBaseType & {
name: 'intersection';
value: SBType[];
};
type SBUnionType = SBBaseType & {
name: 'union';
value: SBType[];
};
type SBOtherType = SBBaseType & {
name: 'other';
value: string;
};
type SBType = SBScalarType | SBEnumType | SBArrayType | SBObjectType | SBIntersectionType | SBUnionType | SBOtherType;
type StoryId = string;
type ComponentId = string;
type ComponentTitle = string;
type StoryName = string;
/** @deprecated */
type StoryKind = ComponentTitle;
type Tag = string;
interface StoryIdentifier {
componentId: ComponentId;
title: ComponentTitle;
/** @deprecated */
kind: ComponentTitle;
id: StoryId;
name: StoryName;
/** @deprecated */
story: StoryName;
tags: Tag[];
}
interface Parameters {
[name: string]: any;
}
interface StrictParameters {
[name: string]: unknown;
}
type ControlType = 'object' | 'boolean' | 'check' | 'inline-check' | 'radio' | 'inline-radio' | 'select' | 'multi-select' | 'number' | 'range' | 'file' | 'color' | 'date' | 'text';
type ConditionalTest = {
truthy?: boolean;
} | {
exists: boolean;
} | {
eq: any;
} | {
neq: any;
};
type ConditionalValue = {
arg: string;
} | {
global: string;
};
type Conditional = ConditionalValue & ConditionalTest;
interface ControlBase {
[key: string]: any;
/**
* @see https://storybook.js.org/docs/api/arg-types#controltype
*/
type?: ControlType;
disable?: boolean;
}
interface Report {
type: string;
version?: number;
result: unknown;
status: 'failed' | 'passed' | 'warning';
}
interface ReportingAPI {
reports: Report[];
addReport: (report: Report) => void;
}
type Control = ControlType | false | (ControlBase & (ControlBase | {
type: 'color';
/**
* @see https://storybook.js.org/docs/api/arg-types#controlpresetcolors
*/
presetColors?: string[];
} | {
type: 'file';
/**
* @see https://storybook.js.org/docs/api/arg-types#controlaccept
*/
accept?: string;
} | {
type: 'inline-check' | 'radio' | 'inline-radio' | 'select' | 'multi-select';
/**
* @see https://storybook.js.org/docs/api/arg-types#controllabels
*/
labels?: {
[options: string]: string;
};
} | {
type: 'number' | 'range';
/**
* @see https://storybook.js.org/docs/api/arg-types#controlmax
*/
max?: number;
/**
* @see https://storybook.js.org/docs/api/arg-types#controlmin
*/
min?: number;
/**
* @see https://storybook.js.org/docs/api/arg-types#controlstep
*/
step?: number;
}));
interface InputType {
/**
* @see https://storybook.js.org/docs/api/arg-types#control
*/
control?: Control;
/**
* @see https://storybook.js.org/docs/api/arg-types#description
*/
description?: string;
/**
* @see https://storybook.js.org/docs/api/arg-types#if
*/
if?: Conditional;
/**
* @see https://storybook.js.org/docs/api/arg-types#mapping
*/
mapping?: {
[key: string]: any;
};
/**
* @see https://storybook.js.org/docs/api/arg-types#name
*/
name?: string;
/**
* @see https://storybook.js.org/docs/api/arg-types#options
*/
options?: readonly any[];
/**
* @see https://storybook.js.org/docs/api/arg-types#table
*/
table?: {
[key: string]: unknown;
/**
* @see https://storybook.js.org/docs/api/arg-types#tablecategory
*/
category?: string;
/**
* @see https://storybook.js.org/docs/api/arg-types#tabledefaultvalue
*/
defaultValue?: {
summary?: string;
detail?: string;
};
/**
* @see https://storybook.js.org/docs/api/arg-types#tabledisable
*/
disable?: boolean;
/**
* @see https://storybook.js.org/docs/api/arg-types#tablesubcategory
*/
subcategory?: string;
/**
* @see https://storybook.js.org/docs/api/arg-types#tabletype
*/
type?: {
summary?: string;
detail?: string;
};
};
/**
* @see https://storybook.js.org/docs/api/arg-types#type
*/
type?: SBType | SBScalarType['name'];
/**
* @see https://storybook.js.org/docs/api/arg-types#defaultvalue
*
* @deprecated Use `table.defaultValue.summary` instead.
*/
defaultValue?: any;
[key: string]: any;
}
interface StrictInputType extends InputType {
name: string;
type?: SBType;
}
interface Args {
[name: string]: any;
}
interface StrictArgs {
[name: string]: unknown;
}
/**
* @see https://storybook.js.org/docs/api/arg-types#argtypes
*/
type ArgTypes<TArgs = Args> = {
[name in keyof TArgs]: InputType;
};
type StrictArgTypes<TArgs = Args> = {
[name in keyof TArgs]: StrictInputType;
};
interface Globals {
[name: string]: any;
}
interface GlobalTypes {
[name: string]: InputType;
}
interface StrictGlobalTypes {
[name: string]: StrictInputType;
}
interface Renderer {
/** What is the type of the `component` annotation in this renderer? */
component: unknown;
/** What does the story function return in this renderer? */
storyResult: unknown;
/** What type of element does this renderer render to? */
canvasElement: unknown;
mount(): Promise<Canvas>;
T?: unknown;
}
/** @deprecated - use `Renderer` */
type AnyFramework = Renderer;
interface StoryContextForEnhancers<TRenderer extends Renderer = Renderer, TArgs = Args> extends StoryIdentifier {
component?: (TRenderer & {
T: any;
})['component'];
subcomponents?: Record<string, (TRenderer & {
T: any;
})['component']>;
parameters: Parameters;
initialArgs: TArgs;
argTypes: StrictArgTypes<TArgs>;
}
type ArgsEnhancer<TRenderer extends Renderer = Renderer, TArgs = Args> = (context: StoryContextForEnhancers<TRenderer, TArgs>) => TArgs;
type ArgTypesEnhancer<TRenderer extends Renderer = Renderer, TArgs = Args> = ((context: StoryContextForEnhancers<TRenderer, TArgs>) => StrictArgTypes<TArgs>) & {
secondPass?: boolean;
};
interface StoryContextUpdate<TArgs = Args> {
args?: TArgs;
globals?: Globals;
[key: string]: any;
}
type ViewMode = 'story' | 'docs';
type LoaderFunction<TRenderer extends Renderer = Renderer, TArgs = Args> = (context: StoryContextForLoaders<TRenderer, TArgs>) => Promise<Record<string, any> | void> | Record<string, any> | void;
type Awaitable<T> = T | PromiseLike<T>;
type CleanupCallback = () => Awaitable<unknown>;
type BeforeAll = () => Awaitable<CleanupCallback | void>;
type BeforeEach<TRenderer extends Renderer = Renderer, TArgs = Args> = (context: StoryContext<TRenderer, TArgs>) => Awaitable<CleanupCallback | void>;
type AfterEach<TRenderer extends Renderer = Renderer, TArgs = Args> = (context: StoryContext<TRenderer, TArgs>) => Awaitable<void>;
interface Canvas {
}
interface StoryContext<TRenderer extends Renderer = Renderer, TArgs = Args> extends StoryContextForEnhancers<TRenderer, TArgs>, Required<StoryContextUpdate<TArgs>> {
loaded: Record<string, any>;
abortSignal: AbortSignal;
canvasElement: TRenderer['canvasElement'];
hooks: unknown;
originalStoryFn: StoryFn<TRenderer>;
viewMode: ViewMode;
step: StepFunction<TRenderer, TArgs>;
context: this;
canvas: Canvas;
mount: TRenderer['mount'];
reporting: ReportingAPI;
}
/** @deprecated Use {@link StoryContext} instead. */
interface StoryContextForLoaders<TRenderer extends Renderer = Renderer, TArgs = Args> extends StoryContext<TRenderer, TArgs> {
}
/** @deprecated Use {@link StoryContext} instead. */
interface PlayFunctionContext<TRenderer extends Renderer = Renderer, TArgs = Args> extends StoryContext<TRenderer, TArgs> {
}
type StepLabel = string;
type StepFunction<TRenderer extends Renderer = Renderer, TArgs = Args> = (label: StepLabel, play: PlayFunction<TRenderer, TArgs>) => Promise<void> | void;
type PlayFunction<TRenderer extends Renderer = Renderer, TArgs = Args> = (context: PlayFunctionContext<TRenderer, TArgs>) => Promise<void> | void;
type PartialStoryFn<TRenderer extends Renderer = Renderer, TArgs = Args> = (update?: StoryContextUpdate<Partial<TArgs>>) => TRenderer['storyResult'];
type LegacyStoryFn<TRenderer extends Renderer = Renderer, TArgs = Args> = (context: StoryContext<TRenderer, TArgs>) => TRenderer['storyResult'];
type ArgsStoryFn<TRenderer extends Renderer = Renderer, TArgs = Args> = (args: TArgs, context: StoryContext<TRenderer, TArgs>) => (TRenderer & {
T: TArgs;
})['storyResult'];
type StoryFn<TRenderer extends Renderer = Renderer, TArgs = Args> = LegacyStoryFn<TRenderer, TArgs> | ArgsStoryFn<TRenderer, TArgs>;
type DecoratorFunction<TRenderer extends Renderer = Renderer, TArgs = Args> = (fn: PartialStoryFn<TRenderer, TArgs>, c: StoryContext<TRenderer, TArgs>) => TRenderer['storyResult'];
type DecoratorApplicator<TRenderer extends Renderer = Renderer, TArgs = Args> = (storyFn: LegacyStoryFn<TRenderer, TArgs>, decorators: DecoratorFunction<TRenderer, TArgs>[]) => LegacyStoryFn<TRenderer, TArgs>;
type StepRunner<TRenderer extends Renderer = Renderer, TArgs = Args> = (label: StepLabel, play: PlayFunction<TRenderer, TArgs>, context: StoryContext<TRenderer, TArgs>) => Promise<void>;
interface BaseAnnotations<TRenderer extends Renderer = Renderer, TArgs = Args> {
/**
* Wrapper components or Storybook decorators that wrap a story.
*
* Decorators defined in Meta will be applied to every story variation.
* @see [Decorators](https://storybook.js.org/docs/writing-stories/decorators)
*/
decorators?: DecoratorFunction<TRenderer, Simplify<TArgs>>[] | DecoratorFunction<TRenderer, Simplify<TArgs>>;
/**
* Custom metadata for a story.
* @see [Parameters](https://storybook.js.org/docs/writing-stories/parameters)
*/
parameters?: Parameters;
/**
* Dynamic data that are provided (and possibly updated by) Storybook and its addons.
* @see [Args](https://storybook.js.org/docs/writing-stories/args)
*/
args?: Partial<TArgs>;
/**
* ArgTypes encode basic metadata for args, such as `name`, `description`, `defaultValue` for an arg. These get automatically filled in by Storybook Docs.
* @see [ArgTypes](https://storybook.js.org/docs/api/arg-types)
*/
argTypes?: Partial<ArgTypes<TArgs>>;
/**
* Asynchronous functions which provide data for a story.
* @see [Loaders](https://storybook.js.org/docs/writing-stories/loaders)
*/
loaders?: LoaderFunction<TRenderer, TArgs>[] | LoaderFunction<TRenderer, TArgs>;
/**
* Function to be called before each story. When the function is async, it will be awaited.
*
* `beforeEach` can be added to preview, the default export and to a specific story.
* They are run (and awaited) in the order: preview, default export, story
*
* A cleanup function can be returned.
*/
beforeEach?: BeforeEach<TRenderer, TArgs>[] | BeforeEach<TRenderer, TArgs>;
/**
* Function to be called after each play function for post-test assertions.
* Don't use this function for cleaning up state.
* You can use the return callback of `beforeEach` for that, which is run when switching stories.
* When the function is async, it will be awaited.
*
* `afterEach` can be added to preview, the default export and to a specific story.
* They are run (and awaited) reverse order: preview, default export, story
*/
experimental_afterEach?: AfterEach<TRenderer, TArgs>[] | AfterEach<TRenderer, TArgs>;
/**
* Define a custom render function for the story(ies). If not passed, a default render function by the renderer will be used.
*/
render?: ArgsStoryFn<TRenderer, TArgs>;
/**
* Named tags for a story, used to filter stories in different contexts.
*/
tags?: Tag[];
mount?: (context: StoryContext<TRenderer, TArgs>) => TRenderer['mount'];
}
interface ProjectAnnotations<TRenderer extends Renderer = Renderer, TArgs = Args> extends BaseAnnotations<TRenderer, TArgs> {
argsEnhancers?: ArgsEnhancer<TRenderer, Args>[];
argTypesEnhancers?: ArgTypesEnhancer<TRenderer, Args>[];
/**
* Lifecycle hook which runs once, before any loaders, decorators or stories, and may rerun when configuration changes or when reinitializing (e.g. between test runs).
* The function may be synchronous or asynchronous, and may return a cleanup function which may also be synchronous or asynchronous.
* The cleanup function is not guaranteed to run (e.g. when the browser closes), but runs when configuration changes or when reinitializing.
* This hook may only be defined globally (i.e. not on component or story level).
* When multiple hooks are specified, they are to be executed sequentially (and awaited) in the following order:
* - Addon hooks (in order of addons array in e.g. .storybook/main.js)
* - Annotation hooks (in order of previewAnnotations array in e.g. .storybook/main.js)
* - Preview hook (via e.g. .storybook/preview.js)
* Cleanup functions are executed sequentially in reverse order of initialization.
*/
beforeAll?: BeforeAll;
/**
* @deprecated Project `globals` renamed to `initiaGlobals`
*/
globals?: Globals;
initialGlobals?: Globals;
globalTypes?: GlobalTypes;
applyDecorators?: DecoratorApplicator<TRenderer, Args>;
runStep?: StepRunner<TRenderer, TArgs>;
}
type StoryDescriptor$1 = string[] | RegExp;
interface ComponentAnnotations<TRenderer extends Renderer = Renderer, TArgs = Args> extends BaseAnnotations<TRenderer, TArgs> {
/**
* Title of the component which will be presented in the navigation. **Should be unique.**
*
* Components can be organized in a nested structure using "/" as a separator.
*
* Since CSF 3.0 this property is optional -- it can be inferred from the filesystem path
*
* @example
* export default {
* ...
* title: 'Design System/Atoms/Button'
* }
*
* @see [Story Hierarchy](https://storybook.js.org/docs/writing-stories/naming-components-and-hierarchy#structure-and-hierarchy)
*/
title?: ComponentTitle;
/**
* Id of the component (prefix of the story id) which is used for URLs.
*
* By default is inferred from sanitizing the title
*
* @see [Permalink to stories](https://storybook.js.org/docs/configure/sidebar-and-urls#permalink-to-stories)
*/
id?: ComponentId;
/**
* Used to only include certain named exports as stories. Useful when you want to have non-story exports such as mock data or ignore a few stories.
* @example
* includeStories: ['SimpleStory', 'ComplexStory']
* includeStories: /.*Story$/
*
* @see [Non-story exports](https://storybook.js.org/docs/api/csf#non-story-exports)
*/
includeStories?: StoryDescriptor$1;
/**
* Used to exclude certain named exports. Useful when you want to have non-story exports such as mock data or ignore a few stories.
* @example
* excludeStories: ['simpleData', 'complexData']
* excludeStories: /.*Data$/
*
* @see [Non-story exports](https://storybook.js.org/docs/api/csf#non-story-exports)
*/
excludeStories?: StoryDescriptor$1;
/**
* The primary component for your story.
*
* Used by addons for automatic prop table generation and display of other component metadata.
*/
component?: (TRenderer & {
T: Record<string, unknown> extends Required<TArgs> ? any : TArgs;
})['component'];
/**
* Auxiliary subcomponents that are part of the stories.
*
* Used by addons for automatic prop table generation and display of other component metadata.
*
* @example
* import { Button, ButtonGroup } from './components';
*
* export default {
* ...
* subcomponents: { Button, ButtonGroup }
* }
*
* By defining them each component will have its tab in the args table.
*/
subcomponents?: Record<string, (TRenderer & {
T: any;
})['component']>;
/**
* Function that is executed after the story is rendered.
*/
play?: PlayFunction<TRenderer, TArgs>;
/**
* Override the globals values for all stories in this component
*/
globals?: Globals;
}
type StoryAnnotations<TRenderer extends Renderer = Renderer, TArgs = Args, TRequiredArgs = Partial<TArgs>> = BaseAnnotations<TRenderer, TArgs> & {
/**
* Override the display name in the UI (CSF v3)
*/
name?: StoryName;
/**
* Override the display name in the UI (CSF v2)
*/
storyName?: StoryName;
/**
* Function that is executed after the story is rendered.
*/
play?: PlayFunction<TRenderer, TArgs>;
/**
* Override the globals values for this story
*/
globals?: Globals;
/** @deprecated */
story?: Omit<StoryAnnotations<TRenderer, TArgs>, 'story'>;
} & ({} extends TRequiredArgs ? {
args?: TRequiredArgs;
} : {
args: TRequiredArgs;
});
type LegacyAnnotatedStoryFn<TRenderer extends Renderer = Renderer, TArgs = Args> = StoryFn<TRenderer, TArgs> & StoryAnnotations<TRenderer, TArgs>;
type LegacyStoryAnnotationsOrFn<TRenderer extends Renderer = Renderer, TArgs = Args> = LegacyAnnotatedStoryFn<TRenderer, TArgs> | StoryAnnotations<TRenderer, TArgs>;
type AnnotatedStoryFn<TRenderer extends Renderer = Renderer, TArgs = Args> = ArgsStoryFn<TRenderer, TArgs> & StoryAnnotations<TRenderer, TArgs>;
type StoryAnnotationsOrFn<TRenderer extends Renderer = Renderer, TArgs = Args> = AnnotatedStoryFn<TRenderer, TArgs> | StoryAnnotations<TRenderer, TArgs>;
type ArgsFromMeta<TRenderer extends Renderer, Meta> = Meta extends {
render?: ArgsStoryFn<TRenderer, infer RArgs>;
loaders?: (infer Loaders)[] | infer Loaders;
decorators?: (infer Decorators)[] | infer Decorators;
} ? Simplify<RemoveIndexSignature<RArgs & DecoratorsArgs<TRenderer, Decorators> & LoaderArgs<TRenderer, Loaders>>> : unknown;
type DecoratorsArgs<TRenderer extends Renderer, Decorators> = UnionToIntersection<Decorators extends DecoratorFunction<TRenderer, infer TArgs> ? TArgs : unknown>;
type LoaderArgs<TRenderer extends Renderer, Loaders> = UnionToIntersection<Loaders extends LoaderFunction<TRenderer, infer TArgs> ? TArgs : unknown>;
/**
* Helper function to include/exclude an arg based on the value of other other args
* aka "conditional args"
*/
declare const includeConditionalArg: (argType: InputType, args: Args, globals: Globals) => any;
/**
* Remove punctuation and illegal characters from a story ID.
*
* See https://gist.github.com/davidjrice/9d2af51100e41c6c4b4a
*/
declare const sanitize: (string: string) => string;
/**
* Generate a storybook ID from a component/kind and story name.
*/
declare const toId: (kind: string, name?: string) => string;
/**
* Transform a CSF named export into a readable story name
*/
declare const storyNameFromExport: (key: string) => string;
type StoryDescriptor = string[] | RegExp;
interface IncludeExcludeOptions {
includeStories?: StoryDescriptor;
excludeStories?: StoryDescriptor;
}
/**
* Does a named export match CSF inclusion/exclusion options?
*/
declare function isExportStory(key: string, { includeStories, excludeStories }: IncludeExcludeOptions): boolean | null;
interface SeparatorOptions {
rootSeparator: string | RegExp;
groupSeparator: string | RegExp;
}
/**
* Parse out the component/kind name from a path, using the given separator config.
*/
declare const parseKind: (kind: string, { rootSeparator, groupSeparator }: SeparatorOptions) => {
root: string | null;
groups: string[];
};
/**
* Combine a set of project / meta / story tags, removing duplicates and handling negations.
*/
declare const combineTags: (...tags: string[]) => string[];
export { AfterEach, AnnotatedStoryFn, AnyFramework, ArgTypes, ArgTypesEnhancer, Args, ArgsEnhancer, ArgsFromMeta, ArgsStoryFn, BaseAnnotations, BeforeAll, BeforeEach, Canvas, CleanupCallback, ComponentAnnotations, ComponentId, ComponentTitle, Conditional, DecoratorApplicator, DecoratorFunction, GlobalTypes, Globals, IncludeExcludeOptions, InputType, LegacyAnnotatedStoryFn, LegacyStoryAnnotationsOrFn, LegacyStoryFn, LoaderFunction, Parameters, PartialStoryFn, PlayFunction, PlayFunctionContext, ProjectAnnotations, Renderer, SBArrayType, SBEnumType, SBIntersectionType, SBObjectType, SBOtherType, SBScalarType, SBType, SBUnionType, SeparatorOptions, StepFunction, StepLabel, StepRunner, StoryAnnotations, StoryAnnotationsOrFn, StoryContext, StoryContextForEnhancers, StoryContextForLoaders, StoryContextUpdate, StoryFn, StoryId, StoryIdentifier, StoryKind, StoryName, StrictArgTypes, StrictArgs, StrictGlobalTypes, StrictInputType, StrictParameters, Tag, ViewMode, combineTags, includeConditionalArg, isExportStory, parseKind, sanitize, storyNameFromExport, toId };