Skip to content

Commit

Permalink
Merge pull request #23728 from Sidnioulz/feat/22490-Title
Browse files Browse the repository at this point in the history
Addon-Docs: Title blocks to support `of` prop
  • Loading branch information
JReinhold committed Apr 23, 2024
2 parents 7b2ddea + 51ede75 commit 37b8aef
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 6 deletions.
7 changes: 7 additions & 0 deletions MIGRATION.md
Expand Up @@ -2,6 +2,7 @@

- [From version 8.0 to 8.1.0](#from-version-80-to-810)
- [Subtitle block and `parameters.componentSubtitle`](#subtitle-block-and-parameterscomponentsubtitle)
- [Title block](#title-block)
- [From version 7.x to 8.0.0](#from-version-7x-to-800)
- [Portable stories](#portable-stories)
- [Project annotations are now merged instead of overwritten in composeStory](#project-annotations-are-now-merged-instead-of-overwritten-in-composestory)
Expand Down Expand Up @@ -413,6 +414,12 @@ The `Subtitle` block now accepts an `of` prop, which can be a reference to a CSF

`parameters.componentSubtitle` has been deprecated to be consistent with other parameters related to autodocs, instead use `parameters.docs.subtitle`.

##### Title block

The `Title` block now accepts an `of` prop, which can be a reference to a CSF file or a default export (meta).

It still accepts being passed `children`.

## From version 7.x to 8.0.0

### Portable stories
Expand Down
55 changes: 55 additions & 0 deletions code/ui/blocks/src/blocks/Title.stories.tsx
@@ -0,0 +1,55 @@
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 OfCSFFile: Story = {
args: {
of: DefaultButtonStories,
},
parameters: { relativeCsfPaths: ['../examples/Button.stories'] },
};

export const OfMeta: Story = {
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: 'Title as children',
},
parameters: { relativeCsfPaths: ['../examples/Button.stories'], attached: false },
};

export const DefaultAttached: Story = {
args: {},
parameters: { relativeCsfPaths: ['../examples/Button.stories'], attached: true },
};
37 changes: 31 additions & 6 deletions code/ui/blocks/src/blocks/Title.tsx
@@ -1,23 +1,48 @@
import type { ComponentTitle } from '@storybook/types';
import type { FunctionComponent, ReactNode } from 'react';
import React, { useContext } from 'react';
import React 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;
}

const STORY_KIND_PATH_SEPARATOR = /\s*\/\s*/;

export const extractTitle = (title: ComponentTitle) => {
const groups = title.trim().split(STORY_KIND_PATH_SEPARATOR);
return (groups && groups[groups.length - 1]) || title;
return groups?.[groups?.length - 1] || title;
};

export const Title: FunctionComponent<TitleProps> = ({ children }) => {
const context = useContext(DocsContext);
const content = children || extractTitle(context.storyById().title);
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?');
}

let preparedMeta;
try {
preparedMeta = useOf(of || 'meta', ['meta']).preparedMeta;
} catch (error) {
if (children && !error.message.includes('did you forget to use <Meta of={} />?')) {
// ignore error about unattached CSF since we can still render children
throw error;
}
}

const content = children || extractTitle(preparedMeta.title);

return content ? <PureTitle className="sbdocs-title sb-unstyled">{content}</PureTitle> : null;
};
6 changes: 6 additions & 0 deletions docs/api/doc-block-title.md
Expand Up @@ -31,3 +31,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 37b8aef

Please sign in to comment.