storybook
Version:
Storybook: Develop, document, and test UI components in isolation
1,366 lines (1,270 loc) • 60.5 kB
TypeScript
import React$1, { ComponentProps } from 'react';
import { StorybookTypes, Renderer as Renderer$1, ProjectAnnotations as ProjectAnnotations$1, NormalizedProjectAnnotations, ComponentAnnotations as ComponentAnnotations$1, StoryAnnotations as StoryAnnotations$1, ComposedStoryFn, TestFunction as TestFunction$1 } from 'storybook/internal/types';
import { AddonTypes as AddonTypes$1, StoryContext as StoryContext$1 } from 'storybook/internal/csf';
declare global {
interface SymbolConstructor {
readonly observable: symbol;
}
}
/**
Returns a boolean for whether the two given types are equal.
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
@link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
*/
type IsEqual<T, U> =
(<G>() => G extends T ? 1 : 2) extends
(<G>() => G extends U ? 1 : 2)
? true
: false;
/**
Filter out keys from an object.
Returns `never` if `Exclude` is strictly equal to `Key`.
Returns `never` if `Key` extends `Exclude`.
Returns `Key` otherwise.
@example
```
type Filtered = Filter<'foo', 'foo'>;
//=> never
```
@example
```
type Filtered = Filter<'bar', string>;
//=> never
```
@example
```
type Filtered = Filter<'bar', 'foo'>;
//=> 'bar'
```
@see {Except}
*/
type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : (KeyType extends ExcludeType ? never : KeyType);
/**
Create a type from an object type without certain keys.
This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically.
This type was proposed to the TypeScript team, which declined it, saying they prefer that libraries implement stricter versions of the built-in types ([microsoft/TypeScript#30825](https://github.com/microsoft/TypeScript/issues/30825#issuecomment-523668235)).
@example
```
import type {Except} from 'type-fest';
type Foo = {
a: number;
b: string;
c: boolean;
};
type FooWithoutA = Except<Foo, 'a' | 'c'>;
//=> {b: string};
```
@category Object
*/
type Except<ObjectType, KeysType extends keyof ObjectType> = {
[KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType];
};
/**
@see Simplify
*/
interface SimplifyOptions {
/**
Do the simplification recursively.
@default false
*/
deep?: boolean;
}
// Flatten a type without worrying about the result.
type Flatten<
AnyType,
Options extends SimplifyOptions = {},
> = Options['deep'] extends true
? {[KeyType in keyof AnyType]: Simplify<AnyType[KeyType], Options>}
: {[KeyType in keyof AnyType]: AnyType[KeyType]};
/**
Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
@example
```
import type {Simplify} from 'type-fest';
type PositionProps = {
top: number;
left: number;
};
type SizeProps = {
width: number;
height: number;
};
// In your editor, hovering over `Props` will show a flattened object with all the properties.
type Props = Simplify<PositionProps & SizeProps>;
```
Sometimes it is desired to pass a value as a function argument that has a different type. At first inspection it may seem assignable, and then you discover it is not because the `value`'s type definition was defined as an interface. In the following example, `fn` requires an argument of type `Record<string, unknown>`. If the value is defined as a literal, then it is assignable. And if the `value` is defined as type using the `Simplify` utility the value is assignable. But if the `value` is defined as an interface, it is not assignable because the interface is not sealed and elsewhere a non-string property could be added to the interface.
If the type definition must be an interface (perhaps it was defined in a third-party npm package), then the `value` can be defined as `const value: Simplify<SomeInterface> = ...`. Then `value` will be assignable to the `fn` argument. Or the `value` can be cast as `Simplify<SomeInterface>` if you can't re-declare the `value`.
@example
```
import type {Simplify} from 'type-fest';
interface SomeInterface {
foo: number;
bar?: string;
baz: number | undefined;
}
type SomeType = {
foo: number;
bar?: string;
baz: number | undefined;
};
const literal = {foo: 123, bar: 'hello', baz: 456};
const someType: SomeType = literal;
const someInterface: SomeInterface = literal;
function fn(object: Record<string, unknown>): void {}
fn(literal); // Good: literal object type is sealed
fn(someType); // Good: type is sealed
fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened
fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface` into a `type`
```
@link https://github.com/microsoft/TypeScript/issues/15300
@category Object
*/
type Simplify<
AnyType,
Options extends SimplifyOptions = {},
> = Flatten<AnyType> extends AnyType
? Flatten<AnyType, Options>
: AnyType;
/**
Remove any index signatures from the given object type, so that only explicitly defined properties remain.
Use-cases:
- Remove overly permissive signatures from third-party types.
This type was taken from this [StackOverflow answer](https://stackoverflow.com/a/68261113/420747).
It relies on the fact that an empty object (`{}`) is assignable to an object with just an index signature, like `Record<string, unknown>`, but not to an object with explicitly defined keys, like `Record<'foo' | 'bar', unknown>`.
(The actual value type, `unknown`, is irrelevant and could be any type. Only the key type matters.)
```
const indexed: Record<string, unknown> = {}; // Allowed
const keyed: Record<'foo', unknown> = {}; // Error
// => TS2739: Type '{}' is missing the following properties from type 'Record<"foo" | "bar", unknown>': foo, bar
```
Instead of causing a type error like the above, you can also use a [conditional type](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) to test whether a type is assignable to another:
```
type Indexed = {} extends Record<string, unknown>
? '✅ `{}` is assignable to `Record<string, unknown>`'
: '❌ `{}` is NOT assignable to `Record<string, unknown>`';
// => '✅ `{}` is assignable to `Record<string, unknown>`'
type Keyed = {} extends Record<'foo' | 'bar', unknown>
? "✅ `{}` is assignable to `Record<'foo' | 'bar', unknown>`"
: "❌ `{}` is NOT assignable to `Record<'foo' | 'bar', unknown>`";
// => "❌ `{}` is NOT assignable to `Record<'foo' | 'bar', unknown>`"
```
Using a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#further-exploration), you can then check for each `KeyType` of `ObjectType`...
```
import type {RemoveIndexSignature} from 'type-fest';
type RemoveIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType // Map each key of `ObjectType`...
]: ObjectType[KeyType]; // ...to its original value, i.e. `RemoveIndexSignature<Foo> == Foo`.
};
```
...whether an empty object (`{}`) would be assignable to an object with that `KeyType` (`Record<KeyType, unknown>`)...
```
import type {RemoveIndexSignature} from 'type-fest';
type RemoveIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType
// Is `{}` assignable to `Record<KeyType, unknown>`?
as {} extends Record<KeyType, unknown>
? ... // ✅ `{}` is assignable to `Record<KeyType, unknown>`
: ... // ❌ `{}` is NOT assignable to `Record<KeyType, unknown>`
]: ObjectType[KeyType];
};
```
If `{}` is assignable, it means that `KeyType` is an index signature and we want to remove it. If it is not assignable, `KeyType` is a "real" key and we want to keep it.
```
import type {RemoveIndexSignature} from 'type-fest';
type RemoveIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType
as {} extends Record<KeyType, unknown>
? never // => Remove this `KeyType`.
: KeyType // => Keep this `KeyType` as it is.
]: ObjectType[KeyType];
};
```
@example
```
import type {RemoveIndexSignature} from 'type-fest';
interface Example {
// These index signatures will be removed.
[x: string]: any
[x: number]: any
[x: symbol]: any
[x: `head-${string}`]: string
[x: `${string}-tail`]: string
[x: `head-${string}-tail`]: string
[x: `${bigint}`]: string
[x: `embedded-${number}`]: string
// These explicitly defined keys will remain.
foo: 'bar';
qux?: 'baz';
}
type ExampleWithoutIndexSignatures = RemoveIndexSignature<Example>;
// => { foo: 'bar'; qux?: 'baz' | undefined; }
```
@category Object
*/
type RemoveIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
? never
: KeyType]: ObjectType[KeyType];
};
/**
Create a type that makes the given keys optional. The remaining keys are kept as is. The sister of the `SetRequired` type.
Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are optional.
@example
```
import type {SetOptional} from 'type-fest';
type Foo = {
a: number;
b?: string;
c: boolean;
}
type SomeOptional = SetOptional<Foo, 'b' | 'c'>;
// type SomeOptional = {
// a: number;
// b?: string; // Was already optional and still is.
// c?: boolean; // Is now optional.
// }
```
@category Object
*/
type SetOptional<BaseType, Keys extends keyof BaseType> =
Simplify<
// Pick just the keys that are readonly from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be mutable from the base type and make them mutable.
Partial<Pick<BaseType, Keys>>
>;
/**
Convert a union type to an intersection type using [distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
Inspired by [this Stack Overflow answer](https://stackoverflow.com/a/50375286/2172153).
@example
```
import type {UnionToIntersection} from 'type-fest';
type Union = {the(): void} | {great(arg: string): void} | {escape: boolean};
type Intersection = UnionToIntersection<Union>;
//=> {the(): void; great(arg: string): void; escape: boolean};
```
A more applicable example which could make its way into your library code follows.
@example
```
import type {UnionToIntersection} from 'type-fest';
class CommandOne {
commands: {
a1: () => undefined,
b1: () => undefined,
}
}
class CommandTwo {
commands: {
a2: (argA: string) => undefined,
b2: (argB: string) => undefined,
}
}
const union = [new CommandOne(), new CommandTwo()].map(instance => instance.commands);
type Union = typeof union;
//=> {a1(): void; b1(): void} | {a2(argA: string): void; b2(argB: string): void}
type Intersection = UnionToIntersection<Union>;
//=> {a1(): void; b1(): void; a2(argA: string): void; b2(argB: string): void}
```
@category Type
*/
type UnionToIntersection<Union> = (
// `extends unknown` is always going to be the case and is used to convert the
// `Union` into a [distributive conditional
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
Union extends unknown
// The union type is used as the only argument to a function since the union
// of function arguments is an intersection.
? (distributedUnion: Union) => void
// This won't happen.
: never
// Infer the `Intersection` type since TypeScript represents the positional
// arguments of unions of functions as an intersection of the union.
) extends ((mergedIntersection: infer Intersection) => void)
? Intersection
: never;
interface ComponentSelector {
__emotion_styles: any;
}
/**
* @desc Utility type for getting props type of React component.
* It takes `defaultProps` into an account - making props with defaults optional.
*/
type PropsOf<C extends keyof ReactJSX.IntrinsicElements | React.JSXElementConstructor<any>> = ReactJSX.LibraryManagedAttributes<C, React.ComponentProps<C>>;
interface Theme {
}
type IsPreReact19$1 = 2 extends Parameters<React.FunctionComponent<any>>['length'] ? true : false;
/** @ts-ignore */
type ReactJSXElement = true extends IsPreReact19$1 ? JSX.Element : React.JSX.Element;
/** @ts-ignore */
type ReactJSXElementClass = true extends IsPreReact19$1 ? JSX.ElementClass : React.JSX.ElementClass;
/** @ts-ignore */
type ReactJSXElementAttributesProperty = true extends IsPreReact19$1 ? JSX.ElementAttributesProperty : React.JSX.ElementAttributesProperty;
/** @ts-ignore */
type ReactJSXElementChildrenAttribute = true extends IsPreReact19$1 ? JSX.ElementChildrenAttribute : React.JSX.ElementChildrenAttribute;
/** @ts-ignore */
type ReactJSXLibraryManagedAttributes<C, P> = true extends IsPreReact19$1 ? JSX.LibraryManagedAttributes<C, P> : React.JSX.LibraryManagedAttributes<C, P>;
/** @ts-ignore */
type ReactJSXIntrinsicAttributes = true extends IsPreReact19$1 ? JSX.IntrinsicAttributes : React.JSX.IntrinsicAttributes;
/** @ts-ignore */
type ReactJSXIntrinsicClassAttributes<T> = true extends IsPreReact19$1 ? JSX.IntrinsicClassAttributes<T> : React.JSX.IntrinsicClassAttributes<T>;
/** @ts-ignore */
type ReactJSXIntrinsicElements$1 = true extends IsPreReact19$1 ? JSX.IntrinsicElements : React.JSX.IntrinsicElements;
/** @ts-ignore */
type ReactJSXElementType = true extends IsPreReact19$1 ? string | React.JSXElementConstructor<any> : React.JSX.ElementType;
declare namespace ReactJSX {
type ElementType = ReactJSXElementType;
interface Element extends ReactJSXElement {
}
interface ElementClass extends ReactJSXElementClass {
}
interface ElementAttributesProperty extends ReactJSXElementAttributesProperty {
}
interface ElementChildrenAttribute extends ReactJSXElementChildrenAttribute {
}
type LibraryManagedAttributes<C, P> = ReactJSXLibraryManagedAttributes<C, P>;
interface IntrinsicAttributes extends ReactJSXIntrinsicAttributes {
}
interface IntrinsicClassAttributes<T> extends ReactJSXIntrinsicClassAttributes<T> {
}
type IntrinsicElements = ReactJSXIntrinsicElements$1;
}
type IsPreReact19 = 2 extends Parameters<React.FunctionComponent<any>>['length'] ? true : false;
/** @ts-ignore */
type ReactJSXIntrinsicElements = true extends IsPreReact19 ? JSX.IntrinsicElements : React.JSX.IntrinsicElements;
/**
* @typeparam ComponentProps Props which will be included when withComponent is called
* @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called
*/
interface StyledComponent<ComponentProps extends {}, SpecificComponentProps extends {} = {}, JSXProps extends {} = {}> extends React.FC<ComponentProps & SpecificComponentProps & JSXProps>, ComponentSelector {
withComponent<C extends React.ComponentClass<React.ComponentProps<C>>>(component: C): StyledComponent<ComponentProps & PropsOf<C>, {}, {
ref?: React.Ref<InstanceType<C>>;
}>;
withComponent<C extends React.ComponentType<React.ComponentProps<C>>>(component: C): StyledComponent<ComponentProps & PropsOf<C>>;
withComponent<Tag extends keyof ReactJSXIntrinsicElements>(tag: Tag): StyledComponent<ComponentProps, ReactJSXIntrinsicElements[Tag]>;
}
type IconType = keyof typeof icons;
declare const Svg: StyledComponent<{
theme?: Theme;
as?: React$1.ElementType;
}, React$1.SVGProps<SVGSVGElement>, {}>;
interface IconsProps extends ComponentProps<typeof Svg> {
icon: IconType;
useSymbol?: boolean;
onClick?: () => void;
__suppressDeprecationWarning?: boolean;
}
declare const icons: {
readonly user: "UserIcon";
readonly useralt: "UserAltIcon";
readonly useradd: "UserAddIcon";
readonly users: "UsersIcon";
readonly profile: "ProfileIcon";
readonly facehappy: "FaceHappyIcon";
readonly faceneutral: "FaceNeutralIcon";
readonly facesad: "FaceSadIcon";
readonly accessibility: "AccessibilityIcon";
readonly accessibilityalt: "AccessibilityAltIcon";
readonly arrowup: "ChevronUpIcon";
readonly arrowdown: "ChevronDownIcon";
readonly arrowleft: "ChevronLeftIcon";
readonly arrowright: "ChevronRightIcon";
readonly arrowupalt: "ArrowUpIcon";
readonly arrowdownalt: "ArrowDownIcon";
readonly arrowleftalt: "ArrowLeftIcon";
readonly arrowrightalt: "ArrowRightIcon";
readonly expandalt: "ExpandAltIcon";
readonly collapse: "CollapseIcon";
readonly expand: "ExpandIcon";
readonly unfold: "UnfoldIcon";
readonly transfer: "TransferIcon";
readonly redirect: "RedirectIcon";
readonly undo: "UndoIcon";
readonly reply: "ReplyIcon";
readonly sync: "SyncIcon";
readonly upload: "UploadIcon";
readonly download: "DownloadIcon";
readonly back: "BackIcon";
readonly proceed: "ProceedIcon";
readonly refresh: "RefreshIcon";
readonly globe: "GlobeIcon";
readonly compass: "CompassIcon";
readonly location: "LocationIcon";
readonly pin: "PinIcon";
readonly time: "TimeIcon";
readonly dashboard: "DashboardIcon";
readonly timer: "TimerIcon";
readonly home: "HomeIcon";
readonly admin: "AdminIcon";
readonly info: "InfoIcon";
readonly question: "QuestionIcon";
readonly support: "SupportIcon";
readonly alert: "AlertIcon";
readonly email: "EmailIcon";
readonly phone: "PhoneIcon";
readonly link: "LinkIcon";
readonly unlink: "LinkBrokenIcon";
readonly bell: "BellIcon";
readonly rss: "RSSIcon";
readonly sharealt: "ShareAltIcon";
readonly share: "ShareIcon";
readonly circle: "CircleIcon";
readonly circlehollow: "CircleHollowIcon";
readonly bookmarkhollow: "BookmarkHollowIcon";
readonly bookmark: "BookmarkIcon";
readonly hearthollow: "HeartHollowIcon";
readonly heart: "HeartIcon";
readonly starhollow: "StarHollowIcon";
readonly star: "StarIcon";
readonly certificate: "CertificateIcon";
readonly verified: "VerifiedIcon";
readonly thumbsup: "ThumbsUpIcon";
readonly shield: "ShieldIcon";
readonly basket: "BasketIcon";
readonly beaker: "BeakerIcon";
readonly hourglass: "HourglassIcon";
readonly flag: "FlagIcon";
readonly cloudhollow: "CloudHollowIcon";
readonly edit: "EditIcon";
readonly cog: "CogIcon";
readonly nut: "NutIcon";
readonly wrench: "WrenchIcon";
readonly ellipsis: "EllipsisIcon";
readonly check: "CheckIcon";
readonly form: "FormIcon";
readonly batchdeny: "BatchDenyIcon";
readonly batchaccept: "BatchAcceptIcon";
readonly controls: "ControlsIcon";
readonly plus: "PlusIcon";
readonly closeAlt: "CloseAltIcon";
readonly cross: "CrossIcon";
readonly trash: "TrashIcon";
readonly pinalt: "PinAltIcon";
readonly unpin: "UnpinIcon";
readonly add: "AddIcon";
readonly subtract: "SubtractIcon";
readonly close: "CloseIcon";
readonly delete: "DeleteIcon";
readonly passed: "PassedIcon";
readonly changed: "ChangedIcon";
readonly failed: "FailedIcon";
readonly clear: "ClearIcon";
readonly comment: "CommentIcon";
readonly commentadd: "CommentAddIcon";
readonly requestchange: "RequestChangeIcon";
readonly comments: "CommentsIcon";
readonly lock: "LockIcon";
readonly unlock: "UnlockIcon";
readonly key: "KeyIcon";
readonly outbox: "OutboxIcon";
readonly credit: "CreditIcon";
readonly button: "ButtonIcon";
readonly type: "TypeIcon";
readonly pointerdefault: "PointerDefaultIcon";
readonly pointerhand: "PointerHandIcon";
readonly browser: "BrowserIcon";
readonly tablet: "TabletIcon";
readonly mobile: "MobileIcon";
readonly watch: "WatchIcon";
readonly sidebar: "SidebarIcon";
readonly sidebaralt: "SidebarAltIcon";
readonly sidebaralttoggle: "SidebarAltToggleIcon";
readonly sidebartoggle: "SidebarToggleIcon";
readonly bottombar: "BottomBarIcon";
readonly bottombartoggle: "BottomBarToggleIcon";
readonly cpu: "CPUIcon";
readonly database: "DatabaseIcon";
readonly memory: "MemoryIcon";
readonly structure: "StructureIcon";
readonly box: "BoxIcon";
readonly power: "PowerIcon";
readonly photo: "PhotoIcon";
readonly component: "ComponentIcon";
readonly grid: "GridIcon";
readonly outline: "OutlineIcon";
readonly photodrag: "PhotoDragIcon";
readonly search: "SearchIcon";
readonly zoom: "ZoomIcon";
readonly zoomout: "ZoomOutIcon";
readonly zoomreset: "ZoomResetIcon";
readonly eye: "EyeIcon";
readonly eyeclose: "EyeCloseIcon";
readonly lightning: "LightningIcon";
readonly lightningoff: "LightningOffIcon";
readonly contrast: "ContrastIcon";
readonly switchalt: "SwitchAltIcon";
readonly mirror: "MirrorIcon";
readonly grow: "GrowIcon";
readonly paintbrush: "PaintBrushIcon";
readonly ruler: "RulerIcon";
readonly stop: "StopIcon";
readonly camera: "CameraIcon";
readonly video: "VideoIcon";
readonly speaker: "SpeakerIcon";
readonly play: "PlayIcon";
readonly playback: "PlayBackIcon";
readonly playnext: "PlayNextIcon";
readonly rewind: "RewindIcon";
readonly fastforward: "FastForwardIcon";
readonly stopalt: "StopAltIcon";
readonly sidebyside: "SideBySideIcon";
readonly stacked: "StackedIcon";
readonly sun: "SunIcon";
readonly moon: "MoonIcon";
readonly book: "BookIcon";
readonly document: "DocumentIcon";
readonly copy: "CopyIcon";
readonly category: "CategoryIcon";
readonly folder: "FolderIcon";
readonly print: "PrintIcon";
readonly graphline: "GraphLineIcon";
readonly calendar: "CalendarIcon";
readonly graphbar: "GraphBarIcon";
readonly menu: "MenuIcon";
readonly menualt: "MenuIcon";
readonly filter: "FilterIcon";
readonly docchart: "DocChartIcon";
readonly doclist: "DocListIcon";
readonly markup: "MarkupIcon";
readonly bold: "BoldIcon";
readonly paperclip: "PaperClipIcon";
readonly listordered: "ListOrderedIcon";
readonly listunordered: "ListUnorderedIcon";
readonly paragraph: "ParagraphIcon";
readonly markdown: "MarkdownIcon";
readonly repository: "RepoIcon";
readonly commit: "CommitIcon";
readonly branch: "BranchIcon";
readonly pullrequest: "PullRequestIcon";
readonly merge: "MergeIcon";
readonly apple: "AppleIcon";
readonly linux: "LinuxIcon";
readonly ubuntu: "UbuntuIcon";
readonly windows: "WindowsIcon";
readonly storybook: "StorybookIcon";
readonly azuredevops: "AzureDevOpsIcon";
readonly bitbucket: "BitbucketIcon";
readonly chrome: "ChromeIcon";
readonly chromatic: "ChromaticIcon";
readonly componentdriven: "ComponentDrivenIcon";
readonly discord: "DiscordIcon";
readonly facebook: "FacebookIcon";
readonly figma: "FigmaIcon";
readonly gdrive: "GDriveIcon";
readonly github: "GithubIcon";
readonly gitlab: "GitlabIcon";
readonly google: "GoogleIcon";
readonly graphql: "GraphqlIcon";
readonly medium: "MediumIcon";
readonly redux: "ReduxIcon";
readonly twitter: "TwitterIcon";
readonly youtube: "YoutubeIcon";
readonly vscode: "VSCodeIcon";
};
type ToolbarShortcutType = 'next' | 'previous' | 'reset';
type ToolbarItemType = 'item' | 'reset';
interface ToolbarShortcutConfig {
label: string;
keys: string[];
}
type ToolbarShortcuts = Record<ToolbarShortcutType, ToolbarShortcutConfig>;
interface ToolbarItem {
value?: string;
icon?: IconsProps['icon'];
right?: string;
title?: string;
hideIcon?: boolean;
type?: ToolbarItemType;
}
interface NormalizedToolbarConfig {
/** The label to show for this toolbar item */
title?: string;
/** Choose an icon to show for this toolbar item */
icon?: IconsProps['icon'];
/** Set to true to prevent default update of icon to match any present selected items icon */
preventDynamicIcon?: boolean;
items: ToolbarItem[];
shortcuts?: ToolbarShortcuts;
/** Change title based on selected value */
dynamicTitle?: boolean;
}
type ToolbarConfig = Omit<NormalizedToolbarConfig, 'items'> & {
items: (string | ToolbarItem)[];
};
type ToolbarArgType = {
name?: string;
description?: string;
defaultValue?: any;
toolbar?: ToolbarConfig;
/**
* @deprecated This loose index signature has been added for compatibility with InputType, and
* will be removed in Storybook 11
*/
[key: string]: any;
};
interface SBBaseType {
required?: boolean;
raw?: string;
}
type SBScalarType = SBBaseType & {
name: 'boolean' | 'string' | 'number' | 'function' | 'symbol' | 'date';
};
type SBArrayType = SBBaseType & {
name: 'array';
value: SBType;
};
type SBNodeType = SBBaseType & {
name: 'node';
renderer: string;
};
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 SBLiteralType = SBBaseType & {
name: 'literal';
value: unknown;
};
type SBTupleType = SBBaseType & {
name: 'tuple';
value: SBType[];
};
type SBOtherType = SBBaseType & {
name: 'other';
value: string;
};
type SBType = SBScalarType | SBEnumType | SBArrayType | SBNodeType | SBObjectType | SBIntersectionType | SBUnionType | SBLiteralType | SBTupleType | SBOtherType;
interface ActionsParameters {
/**
* Actions configuration
*
* @see https://storybook.js.org/docs/essentials/actions#parameters
*/
actions?: {
/**
* Create actions for each arg that matches the regex. (**NOT recommended, see below**)
*
* This is quite useful when your component has dozens (or hundreds) of methods and you do not
* want to manually apply the fn utility for each of those methods. However, this is not the
* recommended way of writing actions. That's because automatically inferred args are not
* available as spies in your play function. If you use argTypesRegex and your stories have play
* functions, you will need to also define args with the fn utility to test them in your play
* function.
*
* @example `argTypesRegex: '^on.*'`
*/
argTypesRegex?: string;
/**
* Removes the addon panel and turns off the feature's behavior. If you wish to turn off this
* feature for the entire Storybook, you can set the option in your `main.js|ts` configuration
* file.
*
* @see https://storybook.js.org/docs/essentials/actions#disable
*/
disable?: boolean;
/**
* Binds a standard HTML event handler to the outermost HTML element rendered by your component
* and triggers an action when the event is called for a given selector. The format is
* `<eventname> <selector>`. The selector is optional; it defaults to all elements.
*
* **To enable this feature, you must use the `withActions` decorator.**
*
* @example `handles: ['mouseover', 'click .btn']`
*/
handles?: string[];
/**
* An integer specifying to which level the tree should be initially expanded.
*
* @default 1
*/
expandLevel?: number;
};
}
interface ActionsTypes {
parameters: ActionsParameters;
}
declare const PARAM_KEY = "backgrounds";
interface Background {
name: string;
value: string;
}
type BackgroundMap = Record<string, Background>;
interface GridConfig {
cellAmount: number;
cellSize: number;
opacity: number;
offsetX?: number;
offsetY?: number;
}
type GlobalState$1 = {
value: string | undefined;
grid?: boolean;
};
interface BackgroundsParameters {
/**
* Backgrounds configuration
*
* @see https://storybook.js.org/docs/essentials/backgrounds#parameters
*/
backgrounds?: {
/** Default background color */
default?: string;
/**
* Removes the tool and disables the feature's behavior. If you wish to turn off this feature
* for the entire Storybook, you can set the option in your `main.js|ts` configuration file.
*
* @see https://storybook.js.org/docs/essentials/backgrounds#disable
*/
disable?: boolean;
/** Configuration for the background grid */
grid?: GridConfig;
/** Available background colors */
options?: BackgroundMap;
};
}
interface BackgroundsGlobals {
/**
* Backgrounds configuration
*
* @see https://storybook.js.org/docs/essentials/backgrounds#globals
*/
[PARAM_KEY]?: GlobalState$1 | GlobalState$1['value'];
}
interface BackgroundTypes {
parameters: BackgroundsParameters;
globals: BackgroundsGlobals;
}
interface ControlsParameters {
/**
* Controls configuration
*
* @see https://storybook.js.org/docs/essentials/controls#parameters-1
*/
controls?: {
/**
* Removes the addon panel and turns off the feature's behavior. If you wish to turn off this
* feature for the entire Storybook, you can set the option in your `main.js|ts` configuration
* file.
*
* @see https://storybook.js.org/docs/essentials/controls#disable
*/
disable?: boolean;
/** Disable the ability to create or edit stories from the Controls panel */
disableSaveFromUI?: boolean;
/** Exclude specific properties from the Controls panel */
exclude?: string[] | RegExp;
/**
* Show the full documentation for each property in the Controls addon panel, including the
* description and default value.
*/
expanded?: boolean;
/** Exclude only specific properties in the Controls panel */
include?: string[] | RegExp;
/**
* Custom control type matchers
*
* @see https://storybook.js.org/docs/essentials/controls#custom-control-type-matchers
*/
matchers?: {
date?: RegExp;
color?: RegExp;
};
/**
* Preset color swatches for the color picker control
*
* @example PresetColors: [{ color: '#ff4785', title: 'Coral' }, 'rgba(0, 159, 183, 1)',
* '#fe4a49']
*/
presetColors?: Array<string | {
color: string;
title?: string;
}>;
/** Controls sorting order */
sort?: 'none' | 'alpha' | 'requiredFirst';
};
}
interface ControlsTypes {
parameters: ControlsParameters;
}
interface HighlightTypes {
parameters: HighlightParameters;
}
interface HighlightParameters {
/**
* Highlight configuration
*
* @see https://storybook.js.org/docs/essentials/highlight#parameters
*/
highlight?: {
/**
* Removes the tool and disables the feature's behavior. If you wish to turn off this feature
* for the entire Storybook, you can set the option in your `main.js|ts` configuration file.
*
* @see https://storybook.js.org/docs/essentials/highlight#disable
*/
disable?: boolean;
};
}
interface MeasureParameters {
/**
* Measure configuration
*
* @see https://storybook.js.org/docs/essentials/measure-and-outline#parameters
*/
measure?: {
/**
* Removes the tool and disables the feature's behavior. If you wish to turn off this feature
* for the entire Storybook, you can set the option in your `main.js|ts` configuration file.
*
* @see https://storybook.js.org/docs/essentials/measure-and-outline#disable
*/
disable?: boolean;
};
}
interface MeasureTypes {
parameters: MeasureParameters;
}
interface OutlineParameters {
/**
* Outline configuration
*
* @see https://storybook.js.org/docs/essentials/measure-and-outline#parameters
*/
outline?: {
/**
* Removes the tool and disables the feature's behavior. If you wish to turn off this feature
* for the entire Storybook, you can set the option in your `main.js|ts` configuration file.
*
* @see https://storybook.js.org/docs/essentials/measure-and-outline#disable
*/
disable?: boolean;
};
}
interface OutlineTypes {
parameters: OutlineParameters;
}
interface TestParameters {
test?: {
/** Ignore unhandled errors during test execution */
dangerouslyIgnoreUnhandledErrors?: boolean;
/** Whether to throw exceptions coming from the play function */
throwPlayFunctionExceptions?: boolean;
};
}
interface TestTypes {
parameters: TestParameters;
}
interface Viewport {
name: string;
styles: ViewportStyles;
type?: ViewportType;
}
type ViewportType = 'desktop' | 'mobile' | 'tablet' | 'watch' | 'other';
interface ViewportStyles {
height: string;
width: string;
}
type GlobalState = {
/**
* When set, the viewport is applied and cannot be changed using the toolbar. Must match the key
* of one of the available viewports or follow the format '{width}-{height}', e.g. '320-480' which
* may include a unit (e.g. '100vw' or '100pct').
*/
value: string | undefined;
/**
* When true the viewport applied will be rotated 90°, e.g. it will rotate from portrait to
* landscape orientation.
*/
isRotated?: boolean;
};
interface ViewportParameters {
/**
* Viewport configuration
*
* @see https://storybook.js.org/docs/essentials/viewport#parameters
*/
viewport?: {
/**
* Removes the tool and disables the feature's behavior. If you wish to turn off this feature
* for the entire Storybook, you can set the option in your `main.js|ts` configuration file.
*
* @see https://storybook.js.org/docs/essentials/viewport#disable
*/
disable?: boolean;
/**
* Specify the available viewports. The width and height values must include the unit, e.g.
* '320px'.
*/
options: Record<string, Viewport>;
};
}
interface ViewportGlobals {
/**
* Viewport configuration
*
* @see https://storybook.js.org/docs/essentials/viewport#globals
*/
viewport?: GlobalState | GlobalState['value'];
}
interface ViewportTypes {
parameters: ViewportParameters;
globals: ViewportGlobals;
}
type CoreTypes = StorybookTypes & ActionsTypes & BackgroundTypes & ControlsTypes & HighlightTypes & MeasureTypes & OutlineTypes & TestTypes & ViewportTypes;
declare function getCoreAnnotations(): any[];
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$1 {
[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 | undefined;
detail?: string | undefined;
};
/** @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 | undefined;
detail?: string | undefined;
};
};
/** @see https://storybook.js.org/docs/api/arg-types#type */
type?: SBType | SBScalarType['name'];
/**
* @deprecated Use `table.defaultValue.summary` instead.
* @see https://storybook.js.org/docs/api/arg-types#defaultvalue
*/
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]: ToolbarArgType;
}
/**
* AddonTypes allows addons to extend the type system with additional args, parameters, and globals.
*
* Addons can use `definePreviewAddon<AddonTypes>()` to declare additional types that will be merged
* into the story context. For example, an addon that provides a `theme` arg could declare:
*
* ```ts
* const themeAddon = definePreviewAddon<{ args: { theme: 'light' | 'dark' } }>({
* decorators: [(Story, { args }) => <ThemeProvider theme={args.theme}><Story /></ThemeProvider>]
* });
* ```
*
* When users include this addon in their preview config, the `theme` arg becomes available and
* type-checked across all stories.
*/
interface AddonTypes {
args?: unknown;
parameters?: Record<string, any>;
globals?: Record<string, any>;
}
interface Renderer extends AddonTypes {
/** What is the type of the `component` annotation in this renderer? */
component: any;
/** What does the story function return in this renderer? */
storyResult: any;
/** What type of element does this renderer render to? */
canvasElement: any;
mount(): Promise<Canvas>;
T?: unknown;
args: unknown;
csf4: boolean;
}
/** @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$1;
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: ArgsStoryFn<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 TestFunction<TRenderer extends Renderer = Renderer, TArgs = TRenderer['args']> = (context: StoryContext<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$1 & (TRenderer['csf4'] extends true ? CoreTypes['parameters'] & TRenderer['parameters'] : unknown);
/**
* 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
*/
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;
initialGlobals?: Globals & (TRenderer['csf4'] extends true ? CoreTypes['globals'] & TRenderer['globals'] : unknown);
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:/