Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs changes for on demand store #16009

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@
"@storybook/components": "6.4.0-alpha.34",
"@storybook/core": "6.4.0-alpha.34",
"@storybook/core-events": "6.4.0-alpha.34",
"@storybook/csf": "0.0.1",
"@storybook/csf": "0.0.2--canary.b1d5348.0",
"@storybook/csf-tools": "6.4.0-alpha.34",
"@storybook/node-logger": "6.4.0-alpha.34",
"@storybook/postinstall": "6.4.0-alpha.34",
"@storybook/preview-web": "6.4.0-alpha.34",
"@storybook/source-loader": "6.4.0-alpha.34",
"@storybook/store": "6.4.0-alpha.34",
"@storybook/theming": "6.4.0-alpha.34",
"acorn": "^7.4.1",
"acorn-jsx": "^5.3.1",
Expand Down Expand Up @@ -106,6 +108,7 @@
"@emotion/core": "^10.1.1",
"@emotion/styled": "^10.0.27",
"@storybook/angular": "6.4.0-alpha.34",
"@storybook/html": "6.4.0-alpha.34",
"@storybook/react": "6.4.0-alpha.34",
"@storybook/vue": "6.4.0-alpha.34",
"@storybook/web-components": "6.4.0-alpha.34",
Expand Down Expand Up @@ -139,6 +142,8 @@
},
"peerDependencies": {
"@storybook/angular": "6.4.0-alpha.34",
"@storybook/html": "6.4.0-alpha.34",
tmeasday marked this conversation as resolved.
Show resolved Hide resolved
"@storybook/react": "6.4.0-alpha.34",
"@storybook/vue": "6.4.0-alpha.34",
"@storybook/vue3": "6.4.0-alpha.34",
"@storybook/web-components": "6.4.0-alpha.34",
Expand All @@ -155,6 +160,12 @@
"@storybook/angular": {
"optional": true
},
"@storybook/html": {
"optional": true
},
"@storybook/react": {
"optional": true
},
"@storybook/vue": {
"optional": true
},
Expand Down
100 changes: 56 additions & 44 deletions addons/docs/src/blocks/ArgsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
/* eslint-disable no-underscore-dangle */
import React, { FC, useContext, useEffect, useState, useCallback } from 'react';
import mapValues from 'lodash/mapValues';
import {
ArgsTable as PureArgsTable,
ArgsTableProps as PureArgsTableProps,
ArgsTableError,
ArgTypes,
SortType,
TabbedArgsTable,
} from '@storybook/components';
import { Args } from '@storybook/addons';
import { StoryStore, filterArgTypes } from '@storybook/client-api';
import type { PropDescriptor } from '@storybook/client-api';
import { addons } from '@storybook/addons';
import { filterArgTypes, PropDescriptor } from '@storybook/store';
import Events from '@storybook/core-events';
import { StrictArgTypes, Args, AnyFramework } from '@storybook/csf';

import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, CURRENT_SELECTION, PRIMARY_STORY } from './types';
import { getComponentName, getDocsStories } from './utils';
import { getComponentName } from './utils';
import { ArgTypesExtractor } from '../lib/docgen/types';
import { lookupStoryId } from './Story';
import { useStory } from './useStory';

interface BaseProps {
include?: PropDescriptor;
Expand All @@ -45,42 +44,46 @@ type ArgsTableProps = BaseProps | OfProps | ComponentsProps | StoryProps;

const useArgs = (
storyId: string,
storyStore: StoryStore
context: DocsContextProps
): [Args, (args: Args) => void, (argNames?: string[]) => void] => {
const story = storyStore.fromId(storyId);
const channel = addons.getChannel();

const story = context.storyById(storyId);
if (!story) {
throw new Error(`Unknown story: ${storyId}`);
}

const { args: initialArgs } = story;
const [args, setArgs] = useState(initialArgs);
const storyContext = context.getStoryContext(story);

const [args, setArgs] = useState(storyContext.args);
useEffect(() => {
const cb = (changed: { storyId: string; args: Args }) => {
if (changed.storyId === storyId) {
setArgs(changed.args);
}
};
storyStore._channel.on(Events.STORY_ARGS_UPDATED, cb);
return () => storyStore._channel.off(Events.STORY_ARGS_UPDATED, cb);
channel.on(Events.STORY_ARGS_UPDATED, cb);
return () => channel.off(Events.STORY_ARGS_UPDATED, cb);
}, [storyId]);
const updateArgs = useCallback((newArgs) => storyStore.updateStoryArgs(storyId, newArgs), [
storyId,
]);
const updateArgs = useCallback(
(updatedArgs) => channel.emit(Events.UPDATE_STORY_ARGS, { storyId, updatedArgs }),
[storyId]
);
const resetArgs = useCallback(
(argNames?: string[]) => storyStore.resetStoryArgs(storyId, argNames),
(argNames?: string[]) => channel.emit(Events.RESET_STORY_ARGS, { storyId, argNames }),
[storyId]
);
return [args, updateArgs, resetArgs];
};

export const extractComponentArgTypes = (
component: Component,
{ parameters }: DocsContextProps,
{ id, storyById }: DocsContextProps,
include?: PropDescriptor,
exclude?: PropDescriptor
): ArgTypes => {
const params = parameters || {};
const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = params.docs || {};
): StrictArgTypes => {
const { parameters } = storyById(id);
const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = parameters.docs || {};
if (!extractArgTypes) {
throw new Error(ArgsTableError.ARGS_UNSUPPORTED);
}
Expand All @@ -94,11 +97,13 @@ const isShortcut = (value?: string) => {
return value && [CURRENT_SELECTION, PRIMARY_STORY].includes(value);
};

export const getComponent = (props: ArgsTableProps = {}, context: DocsContextProps): Component => {
export const getComponent = (
props: ArgsTableProps = {},
{ id, storyById }: DocsContextProps
): Component => {
const { of } = props as OfProps;
const { story } = props as StoryProps;
const { parameters = {} } = context;
const { component } = parameters;
const { component } = storyById(id);
if (isShortcut(of) || isShortcut(story)) {
return component || null;
}
Expand Down Expand Up @@ -127,47 +132,50 @@ export const StoryTable: FC<
StoryProps & { component: Component; subcomponents: Record<string, Component> }
> = (props) => {
const context = useContext(DocsContext);
const { id: currentId, componentStories } = context;
const {
id: currentId,
parameters: { argTypes },
storyStore,
} = context;
const { story, component, subcomponents, showComponent, include, exclude, sort } = props;
let storyArgTypes;
story: storyName,
component,
subcomponents,
showComponent,
include,
exclude,
sort,
} = props;
try {
let storyId;
switch (story) {
switch (storyName) {
case CURRENT_SELECTION: {
storyId = currentId;
storyArgTypes = argTypes;
break;
}
case PRIMARY_STORY: {
const primaryStory = getDocsStories(context)[0];
const primaryStory = componentStories()[0];
storyId = primaryStory.id;
storyArgTypes = primaryStory.parameters.argTypes;
break;
}
default: {
storyId = lookupStoryId(story, context);
const data = storyStore.fromId(storyId);
storyArgTypes = data.parameters.argTypes;
storyId = lookupStoryId(storyName, context);
}
}
storyArgTypes = filterArgTypes(storyArgTypes, include, exclude);
const story = useStory(storyId, context);
if (!story) {
return <div>Loading...</div>;
}

const argTypes = filterArgTypes(story.argTypes, include, exclude);

const mainLabel = getComponentName(component) || 'Story';

// eslint-disable-next-line prefer-const
let [args, updateArgs, resetArgs] = useArgs(storyId, storyStore);
let tabs = { [mainLabel]: { rows: storyArgTypes, args, updateArgs, resetArgs } } as Record<
let [args, updateArgs, resetArgs] = useArgs(storyId, context);
let tabs = { [mainLabel]: { rows: argTypes, args, updateArgs, resetArgs } } as Record<
string,
PureArgsTableProps
>;

// Use the dynamically generated component tabs if there are no controls
const storyHasArgsWithControls =
storyArgTypes && Object.values(storyArgTypes).find((v) => !!v?.control);
const storyHasArgsWithControls = argTypes && Object.values(argTypes).find((v) => !!v?.control);

if (!storyHasArgsWithControls) {
updateArgs = null;
Expand Down Expand Up @@ -203,15 +211,19 @@ export const ComponentsTable: FC<ComponentsProps> = (props) => {

export const ArgsTable: FC<ArgsTableProps> = (props) => {
const context = useContext(DocsContext);
const { parameters: { subcomponents, controls } = {} } = context;
const { id, storyById } = context;
const {
parameters: { controls },
subcomponents,
} = storyById(id);

const { include, exclude, components, sort: sortProp } = props as ComponentsProps;
const { story } = props as StoryProps;
const { story: storyName } = props as StoryProps;

const sort = sortProp || controls?.sort;

const main = getComponent(props, context);
if (story) {
if (storyName) {
return <StoryTable {...(props as StoryProps)} component={main} {...{ subcomponents, sort }} />;
}

Expand Down
8 changes: 4 additions & 4 deletions addons/docs/src/blocks/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC, ReactElement, ReactNode, ReactNodeArray, useContext } from 'react';
import { MDXProvider } from '@mdx-js/react';
import { toId, storyNameFromExport } from '@storybook/csf';
import { toId, storyNameFromExport, AnyFramework } from '@storybook/csf';
import {
resetComponents,
Preview as PurePreview,
Expand All @@ -19,10 +19,10 @@ type CanvasProps = PurePreviewProps & {

const getPreviewProps = (
{ withSource, mdxSource, children, ...props }: CanvasProps & { children?: ReactNode },
docsContext: DocsContextProps,
docsContext: DocsContextProps<AnyFramework>,
sourceContext: SourceContextProps
): PurePreviewProps => {
const { mdxComponentMeta, mdxStoryNameToKey } = docsContext;
const { mdxComponentAnnotations, mdxStoryNameToKey } = docsContext;
let sourceState = withSource;
if (sourceState === SourceState.NONE) {
return props;
Expand All @@ -41,7 +41,7 @@ const getPreviewProps = (
(s) =>
s.props.id ||
toId(
mdxComponentMeta.id || mdxComponentMeta.title,
mdxComponentAnnotations.id || mdxComponentAnnotations.title,
storyNameFromExport(mdxStoryNameToKey[s.props.name])
)
);
Expand Down
7 changes: 4 additions & 3 deletions addons/docs/src/blocks/Description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ const noDescription = (component?: Component): string | null => null;

export const getDescriptionProps = (
{ of, type, markdown, children }: DescriptionProps,
{ parameters }: DocsContextProps
{ id, storyById }: DocsContextProps<any>
): PureDescriptionProps => {
const { component, parameters } = storyById(id);
if (children || markdown) {
return { markdown: children || markdown };
}
const { component, notes, info, docs } = parameters;
const { notes, info, docs } = parameters;
const { extractComponentDescription = noDescription, description } = docs || {};
const target = of === CURRENT_SELECTION ? component : of;

Expand All @@ -63,7 +64,7 @@ ${extractComponentDescription(target) || ''}
case DescriptionType.DOCGEN:
case DescriptionType.AUTO:
default:
return { markdown: extractComponentDescription(target, parameters) };
return { markdown: extractComponentDescription(target, { component, ...parameters }) };
}
};

Expand Down
11 changes: 7 additions & 4 deletions addons/docs/src/blocks/DocsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dedent from 'ts-dedent';
import { MDXProvider } from '@mdx-js/react';
import { ThemeProvider, ensure as ensureTheme } from '@storybook/theming';
import { DocsWrapper, DocsContent, components as htmlComponents } from '@storybook/components';
import { AnyFramework } from '@storybook/csf';
import { DocsContextProps, DocsContext } from './DocsContext';
import { anchorBlockIdFromId } from './Anchor';
import { storyBlockIdFromId } from './Story';
Expand All @@ -14,8 +15,8 @@ import { scrollToElement } from './utils';

const { document, window: globalWindow } = global;

export interface DocsContainerProps {
context: DocsContextProps;
export interface DocsContainerProps<TFramework extends AnyFramework = AnyFramework> {
context: DocsContextProps<TFramework>;
}

const defaultComponents = {
Expand All @@ -35,8 +36,10 @@ const warnOptionsTheme = deprecate(
);

export const DocsContainer: FunctionComponent<DocsContainerProps> = ({ context, children }) => {
const { id: storyId = null, parameters = {} } = context || {};
const { options = {}, docs = {} } = parameters;
const { id: storyId, storyById } = context;
const {
parameters: { options = {}, docs = {} },
} = storyById(storyId);
let themeVars = docs.theme;
if (!themeVars && options.theme) {
warnOptionsTheme();
Expand Down
20 changes: 4 additions & 16 deletions addons/docs/src/blocks/DocsContext.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
import { Context, createContext } from 'react';
import { window as globalWindow } from 'global';

export interface DocsContextProps {
id?: string;
kind?: string;
name?: string;
import { DocsContextProps } from '@storybook/preview-web';
import { AnyFramework } from '@storybook/csf';

/**
* mdxStoryNameToKey is an MDX-compiler-generated mapping of an MDX story's
* display name to its story key for ID generation. It's used internally by the `<Story>`
* and `Preview` doc blocks.
*/
mdxStoryNameToKey?: Record<string, string>;
mdxComponentMeta?: any;
parameters?: any;
storyStore?: any;
forceRender?: () => void;
}
export type { DocsContextProps };

// We add DocsContext to window. The reason is that in case DocsContext.ts is
// imported multiple times (maybe once directly, and another time from a minified bundle)
Expand All @@ -29,4 +17,4 @@ if (globalWindow.__DOCS_CONTEXT__ === undefined) {
globalWindow.__DOCS_CONTEXT__.displayName = 'DocsContext';
}

export const DocsContext: Context<DocsContextProps> = globalWindow.__DOCS_CONTEXT__;
export const DocsContext: Context<DocsContextProps<AnyFramework>> = globalWindow.__DOCS_CONTEXT__;
7 changes: 3 additions & 4 deletions addons/docs/src/blocks/DocsPage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { extractTitle } from './Title';

describe('defaultTitleSlot', () => {
it('splits on last /', () => {
const parameters = {};
expect(extractTitle({ kind: 'a/b/c', parameters })).toBe('c');
expect(extractTitle({ kind: 'a|b', parameters })).toBe('a|b');
expect(extractTitle({ kind: 'a/b/c.d', parameters })).toBe('c.d');
expect(extractTitle({ title: 'a/b/c' } as any)).toBe('c');
expect(extractTitle({ title: 'a|b' } as any)).toBe('a|b');
expect(extractTitle({ title: 'a/b/c.d' } as any)).toBe('c.d');
});
});
8 changes: 3 additions & 5 deletions addons/docs/src/blocks/Meta.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import React, { FC, useContext } from 'react';
import global from 'global';
import { Args, BaseAnnotations, BaseMeta } from '@storybook/addons';
import { AnyFramework, BaseAnnotations } from '@storybook/csf';
import { Anchor } from './Anchor';
import { DocsContext, DocsContextProps } from './DocsContext';
import { getDocsStories } from './utils';
import { Component } from './types';

const { document } = global;

type MetaProps = BaseMeta<Component> & BaseAnnotations<Args, any>;
type MetaProps = BaseAnnotations;

function getFirstStoryId(docsContext: DocsContextProps): string {
const stories = getDocsStories(docsContext);
const stories = docsContext.componentStories();

return stories.length > 0 ? stories[0].id : null;
}
Expand Down
Loading