Skip to content

Commit

Permalink
Got docs working (first pass)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmeasday committed Aug 3, 2021
1 parent 5b6d4b2 commit bb9f36e
Show file tree
Hide file tree
Showing 18 changed files with 149 additions and 123 deletions.
59 changes: 35 additions & 24 deletions addons/docs/src/blocks/ArgsTable.tsx
Expand Up @@ -16,7 +16,7 @@ import Events from '@storybook/core-events';

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';

Expand Down Expand Up @@ -75,12 +75,12 @@ const useArgs = (

export const extractComponentArgTypes = (
component: Component,
{ parameters }: DocsContextProps,
{ id, storyById }: DocsContextProps<any>,
include?: PropDescriptor,
exclude?: PropDescriptor
): ArgTypes => {
const params = parameters || {};
const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = params.docs || {};
const { parameters } = storyById(id);
const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = parameters.docs || {};
if (!extractArgTypes) {
throw new Error(ArgsTableError.ARGS_UNSUPPORTED);
}
Expand All @@ -94,11 +94,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<any>
): 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 All @@ -111,7 +113,7 @@ export const getComponent = (props: ArgsTableProps = {}, context: DocsContextPro
const addComponentTabs = (
tabs: Record<string, PureArgsTableProps>,
components: Record<string, Component>,
context: DocsContextProps,
context: DocsContextProps<any>,
include?: PropDescriptor,
exclude?: PropDescriptor,
sort?: SortType
Expand All @@ -127,39 +129,44 @@ export const StoryTable: FC<
StoryProps & { component: Component; subcomponents: Record<string, Component> }
> = (props) => {
const context = useContext(DocsContext);
const { id: currentId, storyById, 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;
const { argTypes, parameters } = storyById(currentId);
let storyArgTypes: ArgTypes;
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;
storyArgTypes = primaryStory.argTypes;
break;
}
default: {
storyId = lookupStoryId(story, context);
const data = storyStore.fromId(storyId);
storyArgTypes = data.parameters.argTypes;
storyId = lookupStoryId(storyName, context);
storyArgTypes = storyById(storyId).argTypes;
}
}
storyArgTypes = filterArgTypes(storyArgTypes, include, exclude);

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

// TODO -- how to get the current args and channel?
// eslint-disable-next-line prefer-const
let [args, updateArgs, resetArgs] = useArgs(storyId, storyStore);
let [args, updateArgs, resetArgs] = [storyById(storyId).initialArgs, () => 0, () => 0];
let tabs = { [mainLabel]: { rows: storyArgTypes, args, updateArgs, resetArgs } } as Record<
string,
PureArgsTableProps
Expand Down Expand Up @@ -203,15 +210,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
2 changes: 1 addition & 1 deletion addons/docs/src/blocks/Canvas.tsx
Expand Up @@ -19,7 +19,7 @@ type CanvasProps = PurePreviewProps & {

const getPreviewProps = (
{ withSource, mdxSource, children, ...props }: CanvasProps & { children?: ReactNode },
docsContext: DocsContextProps,
docsContext: DocsContextProps<any>,
sourceContext: SourceContextProps
): PurePreviewProps => {
const { mdxComponentMeta, mdxStoryNameToKey } = docsContext;
Expand Down
3 changes: 2 additions & 1 deletion addons/docs/src/blocks/Description.tsx
Expand Up @@ -31,8 +31,9 @@ const noDescription = (component?: Component): string | null => null;

export const getDescriptionProps = (
{ of, type, markdown, children }: DescriptionProps,
{ parameters }: DocsContextProps
{ id, storyById }: DocsContextProps<any>
): PureDescriptionProps => {
const { parameters } = storyById(id);
if (children || markdown) {
return { markdown: children || markdown };
}
Expand Down
15 changes: 10 additions & 5 deletions addons/docs/src/blocks/DocsContainer.tsx
Expand Up @@ -14,8 +14,8 @@ import { scrollToElement } from './utils';

const { document, window: globalWindow } = global;

export interface DocsContainerProps {
context: DocsContextProps;
export interface DocsContainerProps<StoryFnReturnType> {
context: DocsContextProps<StoryFnReturnType>;
}

const defaultComponents = {
Expand All @@ -34,9 +34,14 @@ const warnOptionsTheme = deprecate(
`
);

export const DocsContainer: FunctionComponent<DocsContainerProps> = ({ context, children }) => {
const { id: storyId = null, parameters = {} } = context || {};
const { options = {}, docs = {} } = parameters;
export const DocsContainer: FunctionComponent<DocsContainerProps<any>> = ({
context,
children,
}) => {
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
@@ -1,22 +1,9 @@
import { Context, createContext } from 'react';
import { window as globalWindow } from 'global';

export interface DocsContextProps {
id?: string;
kind?: string;
name?: string;
import { DocsContextProps } from '@storybook/client-api/dist/ts3.9/new/types';

/**
* 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 +16,5 @@ if (globalWindow.__DOCS_CONTEXT__ === undefined) {
globalWindow.__DOCS_CONTEXT__.displayName = 'DocsContext';
}

export const DocsContext: Context<DocsContextProps> = globalWindow.__DOCS_CONTEXT__;
// TODO -- how to parameterize this by <StoryFnReturnType>
export const DocsContext: Context<DocsContextProps<any>> = globalWindow.__DOCS_CONTEXT__;
5 changes: 2 additions & 3 deletions addons/docs/src/blocks/Meta.tsx
Expand Up @@ -3,15 +3,14 @@ import global from 'global';
import { Args, BaseAnnotations, BaseMeta } from '@storybook/addons';
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>;

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

return stories.length > 0 ? stories[0].id : null;
}
Expand Down
5 changes: 2 additions & 3 deletions addons/docs/src/blocks/Primary.tsx
@@ -1,15 +1,14 @@
import React, { useContext, FC } from 'react';
import { DocsContext } from './DocsContext';
import { DocsStory } from './DocsStory';
import { getDocsStories } from './utils';

interface PrimaryProps {
name?: string;
}

export const Primary: FC<PrimaryProps> = ({ name }) => {
const context = useContext(DocsContext);
const componentStories = getDocsStories(context);
const { componentStories: getComponentStories } = useContext(DocsContext);
const componentStories = getComponentStories();
let story;
if (componentStories) {
story = name ? componentStories.find((s) => s.name === name) : componentStories[0];
Expand Down
42 changes: 15 additions & 27 deletions addons/docs/src/blocks/Source.tsx
Expand Up @@ -7,6 +7,7 @@ import {
import { StoryId } from '@storybook/api';
import { logger } from '@storybook/client-logger';
import { StoryContext } from '@storybook/addons';
import { Story } from '@storybook/client-api/dist/ts3.9/new/types';

import { DocsContext, DocsContextProps } from './DocsContext';
import { SourceContext, SourceContextProps } from './SourceContainer';
Expand Down Expand Up @@ -43,25 +44,11 @@ type NoneProps = CommonProps;

type SourceProps = SingleSourceProps | MultiSourceProps | CodeProps | NoneProps;

const getStoryContext = (storyId: StoryId, docsContext: DocsContextProps): StoryContext | null => {
const { storyStore } = docsContext;
const storyContext = storyStore?.fromId(storyId);

if (!storyContext) {
// Fallback if we can't get the story data for this story
logger.warn(`Unable to find information for story ID '${storyId}'`);
return null;
}

return storyContext;
};

const getSourceState = (storyIds: string[], docsContext: DocsContextProps) => {
const getSourceState = (storyIds: string[], docsContext: DocsContextProps<any>) => {
const states = storyIds
.map((storyId) => {
const storyContext = getStoryContext(storyId, docsContext);
if (!storyContext) return null;
return storyContext.parameters.docs?.source?.state;
const story = docsContext.storyById(storyId);
return story.parameters.docs?.source?.state;
})
.filter(Boolean);

Expand All @@ -77,12 +64,12 @@ const getStorySource = (storyId: StoryId, sourceContext: SourceContextProps): st
return sources?.[storyId] || '';
};

const getSnippet = (snippet: string, storyContext?: StoryContext): string => {
if (!storyContext) {
const getSnippet = (snippet: string, story?: Story<any>): string => {
if (!story) {
return snippet;
}

const { parameters } = storyContext;
const { parameters } = story;
// eslint-disable-next-line no-underscore-dangle
const isArgsStory = parameters.__isArgsStory;
const type = parameters.docs?.source?.type || SourceType.AUTO;
Expand All @@ -95,27 +82,28 @@ const getSnippet = (snippet: string, storyContext?: StoryContext): string => {

// if user has explicitly set this as dynamic, use snippet
if (type === SourceType.DYNAMIC) {
return parameters.docs?.transformSource?.(snippet, storyContext) || snippet;
return parameters.docs?.transformSource?.(snippet, story) || snippet;
}

// if this is an args story and there's a snippet
if (type === SourceType.AUTO && snippet && isArgsStory) {
return parameters.docs?.transformSource?.(snippet, storyContext) || snippet;
return parameters.docs?.transformSource?.(snippet, story) || snippet;
}

// otherwise, use the source code logic
const enhanced = enhanceSource(storyContext) || parameters;
const enhanced = enhanceSource(story) || parameters;
return enhanced?.docs?.source?.code || '';
};

type SourceStateProps = { state: SourceState };

export const getSourceProps = (
props: SourceProps,
docsContext: DocsContextProps,
docsContext: DocsContextProps<any>,
sourceContext: SourceContextProps
): PureSourceProps & SourceStateProps => {
const { id: currentId, parameters = {} } = docsContext;
const { id: currentId, storyById } = docsContext;
const { parameters } = storyById(currentId);

const codeProps = props as CodeProps;
const singleProps = props as SingleSourceProps;
Expand All @@ -131,8 +119,8 @@ export const getSourceProps = (
source = targetIds
.map((storyId) => {
const storySource = getStorySource(storyId, sourceContext);
const storyContext = getStoryContext(storyId, docsContext);
return getSnippet(storySource, storyContext);
const story = docsContext.storyById(storyId);
return getSnippet(storySource, story);
})
.join('\n\n');
}
Expand Down
6 changes: 2 additions & 4 deletions addons/docs/src/blocks/Stories.tsx
Expand Up @@ -2,7 +2,6 @@ import React, { useContext, FunctionComponent } from 'react';
import { DocsContext } from './DocsContext';
import { DocsStory } from './DocsStory';
import { Heading } from './Heading';
import { getDocsStories } from './utils';
import { DocsStoryProps } from './types';

interface StoriesProps {
Expand All @@ -11,10 +10,9 @@ interface StoriesProps {
}

export const Stories: FunctionComponent<StoriesProps> = ({ title, includePrimary = false }) => {
const context = useContext(DocsContext);
const componentStories = getDocsStories(context);
const { componentStories } = useContext(DocsContext);

let stories: DocsStoryProps[] = componentStories;
let stories: DocsStoryProps[] = componentStories();
if (!includePrimary) stories = stories.slice(1);

if (!stories || stories.length === 0) {
Expand Down

0 comments on commit bb9f36e

Please sign in to comment.