Skip to content

Commit

Permalink
feat: Update title to support 'of' prop
Browse files Browse the repository at this point in the history
Partially implements #22490
  • Loading branch information
Sidnioulz committed Aug 7, 2023
1 parent dc9ac08 commit 975c32d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 2 deletions.
4 changes: 4 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,10 @@ Additionally to changing the docs information architecture, we've updated the AP
The primary change of the `Meta` block is the ability to attach to CSF files with `<Meta of={}>` as described above.
##### Title block
The `Title` block now also accepts an `of` prop as described above. It still accepts being passed `children`.
##### Description block, `parameters.notes` and `parameters.info`
In 6.5 the Description doc block accepted a range of different props, `markdown`, `type` and `children` as a way to customize the content.
Expand Down
56 changes: 56 additions & 0 deletions code/ui/blocks/src/blocks/Title.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Title } from './Title';
import * as DefaultButtonStories from '../examples/Button.stories';

const meta: Meta<typeof Title> = {
component: Title,
title: 'Blocks/Title',
parameters: {
controls: {
include: [],
hideNoControlsWarning: true,
},
// workaround for https://github.com/storybookjs/storybook/issues/20505
docs: { source: { type: 'code' } },
attached: false,
docsStyles: true,
},
};
export default meta;

type Story = StoryObj<typeof meta>;

export const OfCSFFileInComponentTitle: Story = {
name: 'Of CSF File with title',
args: {
of: DefaultButtonStories,
},
parameters: { relativeCsfPaths: ['../examples/Button.stories'] },
};

export const OfMetaInComponentTitle: Story = {
name: 'Of meta with title',
args: {
of: DefaultButtonStories,
},
parameters: { relativeCsfPaths: ['../examples/Button.stories'] },
};

export const OfStringMetaAttached: Story = {
name: 'Of attached "meta"',
args: {
of: 'meta',
},
parameters: { relativeCsfPaths: ['../examples/Button.stories'], attached: true },
};

export const Children: Story = {
args: {
children: 'Custom title',
},
};

export const DefaultAttached: Story = {
args: {},
parameters: { relativeCsfPaths: ['../examples/Button.stories'], attached: true },
};
55 changes: 53 additions & 2 deletions code/ui/blocks/src/blocks/Title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@ import type { FunctionComponent, ReactNode } from 'react';
import React, { useContext } from 'react';
import { Title as PureTitle } from '../components';
import { DocsContext } from './DocsContext';
import type { Of } from './useOf';
import { useOf } from './useOf';

interface TitleProps {
/**
* Specify where to get the title from. Must be a CSF file's default export.
* If not specified, the title will be read from children, or extracted from the meta of the attached CSF file.
*/
of?: Of;

/**
* Specify content to display as the title.
*/
children?: ReactNode;
}

Expand All @@ -15,9 +26,49 @@ export const extractTitle = (title: ComponentTitle) => {
return (groups && groups[groups.length - 1]) || title;
};

export const Title: FunctionComponent<TitleProps> = ({ children }) => {
const getTitleFromResolvedOf = (resolvedOf: ReturnType<typeof useOf>): string | null => {
switch (resolvedOf.type) {
case 'meta': {
return resolvedOf.preparedMeta.title || null;
}
case 'story':
case 'component': {
throw new Error(
`Unsupported module type. Title's \`of\` prop only supports \`meta\`, got: ${
(resolvedOf as any).type
}`
);
}
default: {
throw new Error(
`Unrecognized module type resolved from 'useOf', got: ${(resolvedOf as any).type}`
);
}
}
};

export const Title: FunctionComponent<TitleProps> = (props) => {
const { children, of } = props;

if ('of' in props && of === undefined) {
throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?');
}

const context = useContext(DocsContext);
const content = children || extractTitle(context.storyById().title);

let content;
if (of) {
const resolvedOf = useOf(of || 'meta');
content = getTitleFromResolvedOf(resolvedOf);
}

if (!content) {
content = children;
}

if (!content) {
content = extractTitle(context.storyById().title);
}

return content ? <PureTitle className="sbdocs-title sb-unstyled">{content}</PureTitle> : null;
};
6 changes: 6 additions & 0 deletions docs/api/doc-block-title.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ import { Title } from '@storybook/blocks';
Type: `JSX.Element | string`

Provides the content. Falls back to value of `title` in an [attached](./doc-block-meta.md#attached-vs-unattached) CSF file (or value derived from [autotitle](../configure/sidebar-and-urls.md#csf-30-auto-titles)), trimmed to the last segment. For example, if the title value is `'path/to/components/Button'`, the default content is `'Button'`.

### `of`

Type: CSF file exports

Specifies which meta's title is displayed.

0 comments on commit 975c32d

Please sign in to comment.