Skip to content

Commit

Permalink
Merge pull request #18514 from storybookjs/tom/sb-416-switch-meta-to-…
Browse files Browse the repository at this point in the history
…receive-all-exports

Addon-docs: Switch Meta block to receive all module exports
  • Loading branch information
shilman authored Jun 20, 2022
2 parents 3803656 + 3dae5e2 commit f33375b
Show file tree
Hide file tree
Showing 15 changed files with 109 additions and 84 deletions.
24 changes: 14 additions & 10 deletions addons/docs/src/blocks/DocsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ const warnOptionsTheme = deprecate(
);

export const DocsContainer: FunctionComponent<DocsContainerProps> = ({ context, children }) => {
const { id: storyId, storyById } = context;
const {
parameters: { options = {}, docs = {} },
} = storyById(storyId);
let themeVars = docs.theme;
if (!themeVars && options.theme) {
warnOptionsTheme();
themeVars = options.theme;
const { id: storyId, type, storyById } = context;
const allComponents = { ...defaultComponents };
let theme = ensureTheme(null);
if (type === 'legacy') {
const {
parameters: { options = {}, docs = {} },
} = storyById(storyId);
let themeVars = docs.theme;
if (!themeVars && options.theme) {
warnOptionsTheme();
themeVars = options.theme;
}
theme = ensureTheme(themeVars);
Object.assign(allComponents, docs.components);
}
const theme = ensureTheme(themeVars);
const allComponents = { ...defaultComponents, ...docs.components };

useEffect(() => {
let url;
Expand Down
11 changes: 2 additions & 9 deletions addons/docs/src/blocks/DocsRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DocsRenderFunction } from '@storybook/preview-web';

import { DocsContainer } from './DocsContainer';
import { DocsPage } from './DocsPage';
import { DocsContext, DocsContextProps } from './DocsContext';
import { DocsContextProps } from './DocsContext';

export class DocsRenderer<TFramework extends AnyFramework> {
public render: DocsRenderFunction<TFramework>;
Expand Down Expand Up @@ -33,15 +33,8 @@ async function renderDocsAsync<TFramework extends AnyFramework>(
docsParameters: Parameters,
element: HTMLElement
) {
// FIXME -- use DocsContainer, make it work for modern
const SimpleContainer = ({ children }: any) => (
<DocsContext.Provider value={docsContext}>{children} </DocsContext.Provider>
);

const Container: ComponentType<{ context: DocsContextProps<TFramework> }> =
docsParameters.container ||
(await docsParameters.getContainer?.()) ||
(docsContext.type === 'legacy' ? DocsContainer : SimpleContainer);
docsParameters.container || (await docsParameters.getContainer?.()) || DocsContainer;

const Page: ComponentType = docsParameters.page || (await docsParameters.getPage?.()) || DocsPage;

Expand Down
7 changes: 4 additions & 3 deletions addons/docs/src/blocks/ExternalDocsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import { ThemeProvider, themes, ensure } from '@storybook/theming';
import { DocsContextProps } from '@storybook/preview-web';
import { ModuleExport, Story } from '@storybook/store';
import { ModuleExport, ModuleExports, Story } from '@storybook/store';
import { AnyFramework, StoryId } from '@storybook/csf';

import { DocsContext } from './DocsContext';
Expand All @@ -28,8 +28,8 @@ export const ExternalDocsContainer: React.FC<{ projectAnnotations: any }> = ({
title: 'External',
name: 'Docs',

storyIdByModuleExport: (storyExport: ModuleExport) => {
return preview.storyIdByModuleExport(storyExport, pageMeta);
storyIdByModuleExport: (storyExport: ModuleExport, metaExport: ModuleExports) => {
return preview.storyIdByModuleExport(storyExport, metaExport || pageMeta);
},

storyById: (id: StoryId) => {
Expand All @@ -41,6 +41,7 @@ export const ExternalDocsContainer: React.FC<{ projectAnnotations: any }> = ({
},

componentStories: () => {
// TODO: could implement in a very similar way to in DocsRender. (TODO: How to share code?)
throw new Error('not implemented');
},

Expand Down
21 changes: 14 additions & 7 deletions addons/docs/src/blocks/ExternalPreview.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StoryId } from '@storybook/csf';
import { ExternalPreview } from './ExternalPreview';

const projectAnnotations = { render: jest.fn(), renderToDOM: jest.fn() };
Expand All @@ -18,7 +19,10 @@ describe('ExternalPreview', () => {
it('handles csf files with titles', async () => {
const preview = new ExternalPreview(projectAnnotations);

const storyId = preview.storyIdByModuleExport(csfFileWithTitle.one, csfFileWithTitle.default);
const storyId = preview.storyIdByModuleExport(
csfFileWithTitle.one,
csfFileWithTitle
) as StoryId;
const story = preview.storyById(storyId);

expect(story).toMatchObject({
Expand All @@ -30,10 +34,13 @@ describe('ExternalPreview', () => {
it('returns consistent story ids and objects', () => {
const preview = new ExternalPreview(projectAnnotations);

const storyId = preview.storyIdByModuleExport(csfFileWithTitle.one, csfFileWithTitle.default);
const storyId = preview.storyIdByModuleExport(
csfFileWithTitle.one,
csfFileWithTitle
) as StoryId;
const story = preview.storyById(storyId);

expect(preview.storyIdByModuleExport(csfFileWithTitle.one, csfFileWithTitle.default)).toEqual(
expect(preview.storyIdByModuleExport(csfFileWithTitle.one, csfFileWithTitle)).toEqual(
storyId
);
expect(preview.storyById(storyId)).toBe(story);
Expand All @@ -43,11 +50,11 @@ describe('ExternalPreview', () => {
const preview = new ExternalPreview(projectAnnotations);

preview.storyById(
preview.storyIdByModuleExport(csfFileWithTitle.one, csfFileWithTitle.default)
preview.storyIdByModuleExport(csfFileWithTitle.one, csfFileWithTitle) as StoryId
);

const story = preview.storyById(
preview.storyIdByModuleExport(csfFileWithTitle.two, csfFileWithTitle.default)
preview.storyIdByModuleExport(csfFileWithTitle.two, csfFileWithTitle) as StoryId
);
expect(story).toMatchObject({
title: 'Component',
Expand All @@ -60,8 +67,8 @@ describe('ExternalPreview', () => {

const storyId = preview.storyIdByModuleExport(
csfFileWithoutTitle.one,
csfFileWithoutTitle.default
);
csfFileWithoutTitle
) as StoryId;
const story = preview.storyById(storyId);

expect(story).toMatchObject({
Expand Down
23 changes: 10 additions & 13 deletions addons/docs/src/blocks/ExternalPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Path, ModuleExports, StoryIndex, ModuleExport } from '@storybook/store'
import { toId, AnyFramework, ComponentTitle, StoryId, ProjectAnnotations } from '@storybook/csf';

type StoryExport = ModuleExport;
type MetaExport = ModuleExport;
type MetaExport = ModuleExports;
type ExportName = string;

class ConstantMap<TKey, TValue extends string> {
Expand All @@ -27,8 +27,6 @@ export class ExternalPreview<TFramework extends AnyFramework> extends Preview<TF

private titles = new ConstantMap<MetaExport, ComponentTitle>('title-');

private exportNames = new ConstantMap<StoryExport, ExportName>('story-');

public storyIds = new Map<StoryExport, StoryId>();

private storyIndex: StoryIndex = { v: 4, entries: {} };
Expand All @@ -46,18 +44,17 @@ export class ExternalPreview<TFramework extends AnyFramework> extends Preview<TF

addStoryFromExports(storyExport: StoryExport, meta: MetaExport) {
const importPath = this.importPaths.get(meta);
const title = meta.title || this.titles.get(meta);
this.moduleExportsByImportPath[importPath] = meta;

const exportName = this.exportNames.get(storyExport);
const storyId = toId(title, exportName);
this.storyIds.set(storyExport, storyId);
const title = meta.default.title || this.titles.get(meta);

// We need to be sure to create a new object each time here to bust caches
this.moduleExportsByImportPath[importPath] = {
...this.moduleExportsByImportPath[importPath],
default: meta,
[exportName]: storyExport,
};
const exportEntry = Object.entries(meta).find(
([_, moduleExport]) => moduleExport === storyExport
);
if (!exportEntry)
throw new Error(`Didn't find \`of\` used in Story block in the provided CSF exports`);
const storyId = toId(title, exportEntry[0]);
this.storyIds.set(storyExport, storyId);

this.storyIndex.entries[storyId] = {
id: storyId,
Expand Down
4 changes: 3 additions & 1 deletion addons/docs/src/blocks/Meta.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { FC, useContext } from 'react';
import global from 'global';
import { BaseAnnotations } from '@storybook/csf';
import type { ModuleExports } from '@storybook/store';

import { Anchor } from './Anchor';
import { DocsContext, DocsContextProps } from './DocsContext';

const { document } = global;

type MetaProps = BaseAnnotations & { of?: any };
type MetaProps = BaseAnnotations & { of?: ModuleExports };

function getFirstStoryId(docsContext: DocsContextProps): string {
const stories = docsContext.componentStories();
Expand Down
9 changes: 5 additions & 4 deletions addons/docs/src/blocks/Story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import React, {
import { MDXProvider } from '@mdx-js/react';
import { resetComponents, Story as PureStory, StorySkeleton } from '@storybook/components';
import { StoryId, toId, storyNameFromExport, StoryAnnotations, AnyFramework } from '@storybook/csf';
import type { Story as StoryType } from '@storybook/store';
import type { ModuleExport, ModuleExports, Story as StoryType } from '@storybook/store';

import { CURRENT_SELECTION } from './types';
import { DocsContext, DocsContextProps } from './DocsContext';
Expand All @@ -33,7 +33,8 @@ type StoryDefProps = {

type StoryRefProps = {
id?: string;
of?: any;
of?: ModuleExport;
meta?: ModuleExports;
};

type StoryImportProps = {
Expand All @@ -53,10 +54,10 @@ export const lookupStoryId = (
);

export const getStoryId = (props: StoryProps, context: DocsContextProps): StoryId => {
const { id, of } = props as StoryRefProps;
const { id, of, meta } = props as StoryRefProps;

if (of) {
return context.storyIdByModuleExport(of);
return context.storyIdByModuleExport(of, meta);
}

const { name } = props as StoryDefProps;
Expand Down
6 changes: 3 additions & 3 deletions examples/external-docs/components/AccountForm.mdx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Meta, Story } from '@storybook/addon-docs';
import meta, { Standard } from './AccountForm.stories';
import * as AccountFormStories from './AccountForm.stories';

## Docs for Account form

<Meta of={meta} />
<Meta of={AccountFormStories} />

<Story of={Standard} />
<Story of={AccountFormStories.Standard} />
2 changes: 1 addition & 1 deletion examples/external-docs/pages/AccountForm.mdx
10 changes: 5 additions & 5 deletions examples/external-docs/pages/index.mdx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import Callout from 'nextra-theme-docs/callout';
import { Title, Meta, Story, Canvas } from '@storybook/addon-docs';
import meta, { Standard } from '../components/AccountForm.stories';
import buttonMeta, { Basic } from '../components/button.stories';
import * as AccountFormStories from '../components/AccountForm.stories';
import * as ButtonStories from '../components/button.stories';

<Title>Embedded docs demo</Title>

<Meta of={meta} />
<Meta of={AccountFormStories} />

This is an example of an MDX file that embeds Doc Blocks and CSF stories.

<Canvas withSource={{ language: 'html', code: '<h1>hahaha</h1>' }}>
<Story of={Standard} />
<Story of={AccountFormStories.Standard} />
</Canvas>

<Story of={Basic} meta={buttonMeta} />
<Story of={ButtonStories.Basic} meta={ButtonStories} />

<Callout emoji="">
**MDX** (the library), at its core, transforms MDX (the syntax) to JSX. It receives an MDX string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ DarkModeDocs.decorators = [
(storyFn) => (
<DocsContainer
context={{
type: 'legacy',
componentStories: () => [],
storyById: () => ({ parameters: { docs: { theme: themes.dark } } }),
}}
Expand Down
10 changes: 6 additions & 4 deletions examples/react-ts/src/docs2/MetaOf.mdx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Meta, Story } from '@storybook/addon-docs';
import meta, { Basic } from '../button.stories';
import { Meta, Story, Stories } from '@storybook/addon-docs';
import * as ButtonStories from '../button.stories';

<Meta of={meta} />
<Meta of={ButtonStories} />

# Docs with of

hello docs

<Story of={Basic} />
<Story of={ButtonStories.Basic} />

<Stories />
4 changes: 2 additions & 2 deletions lib/core-server/src/utils/StoryIndexGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class StoryIndexGenerator {
async ensureExtracted(): Promise<IndexEntry[]> {
// First process all the story files. Then, in a second pass,
// process the docs files. The reason for this is that the docs
// files may use the `<Meta of={meta} />` syntax, which requires
// files may use the `<Meta of={XStories} />` syntax, which requires
// that the story file that contains the meta be processed first.
await this.updateExtracted(async (specifier, absolutePath) =>
this.isDocsMdx(absolutePath) ? false : this.extractStories(specifier, absolutePath)
Expand Down Expand Up @@ -193,7 +193,7 @@ export class StoryIndexGenerator {
// are invalidated.
const dependencies = this.findDependencies(absoluteImports);

// Also, if `result.of` is set, it means that we're using the `<Meta of={meta} />` syntax,
// Also, if `result.of` is set, it means that we're using the `<Meta of={XStories} />` syntax,
// so find the `title` defined the file that `meta` points to.
let ofTitle: string;
if (result.of) {
Expand Down
Loading

0 comments on commit f33375b

Please sign in to comment.