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

Blocks: Add of prop to Title #23728

Merged
merged 8 commits into from
Apr 23, 2024
Merged
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
7 changes: 7 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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.
Loading