diff --git a/MIGRATION.md b/MIGRATION.md
index 1708f26a3dbe..c692f787fa6d 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -1,5 +1,7 @@
Migration
+- [From version 7.5.0 to 7.6.0](#from-version-750-to-760)
+ - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop)
- [From version 7.4.0 to 7.5.0](#from-version-740-to-750)
- [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated)
- [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers)
@@ -305,6 +307,12 @@
- [Packages renaming](#packages-renaming)
- [Deprecated embedded addons](#deprecated-embedded-addons)
+## From version 7.5.0 to 7.6.0
+
+##### Primary doc block accepts of prop
+
+The `Primary` doc block now also accepts an `of` prop as described in the [Doc Blocks](#doc-blocks) section. It still accepts being passed `name` or no props at all.
+
## From version 7.4.0 to 7.5.0
#### `storyStoreV6` and `storiesOf` is deprecated
diff --git a/code/ui/blocks/src/blocks/Primary.stories.tsx b/code/ui/blocks/src/blocks/Primary.stories.tsx
index 42977ff6c9e3..7c4747d53a3b 100644
--- a/code/ui/blocks/src/blocks/Primary.stories.tsx
+++ b/code/ui/blocks/src/blocks/Primary.stories.tsx
@@ -1,13 +1,16 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Primary } from './Primary';
+import * as DefaultButtonStories from '../examples/Button.stories';
+import * as StoriesParametersStories from '../examples/StoriesParameters.stories';
const meta = {
component: Primary,
parameters: {
+ // workaround for https://github.com/storybookjs/storybook/issues/20505
+ docs: { source: { type: 'code' } },
docsStyles: true,
},
} satisfies Meta;
-
export default meta;
type Story = StoryObj;
@@ -22,3 +25,55 @@ export const WithoutToolbar: Story = {
relativeCsfPaths: ['../examples/StoriesParameters.stories'],
},
};
+
+export const DefaultWithName: Story = {
+ name: 'Name',
+ args: {
+ name: 'Primary',
+ },
+ parameters: {
+ relativeCsfPaths: ['../examples/Button.stories'],
+ },
+};
+
+export const WithoutToolbarWithName: Story = {
+ name: 'Name Without Toolbar',
+ args: {
+ name: 'Without Toolbar',
+ },
+ parameters: {
+ relativeCsfPaths: ['../examples/StoriesParameters.stories'],
+ },
+};
+
+export const DefaultWithOf: Story = {
+ name: 'Of',
+ args: {
+ of: DefaultButtonStories,
+ },
+ parameters: { relativeCsfPaths: ['../examples/Button.stories'] },
+};
+
+export const WithoutToolbarWithOf: Story = {
+ name: 'Of Without Toolbar',
+ args: {
+ of: StoriesParametersStories,
+ },
+ parameters: { relativeCsfPaths: ['../examples/StoriesParameters.stories'] },
+};
+
+export const DefaultOfStringMetaAttached: Story = {
+ name: 'Of Attached "meta"',
+ args: {
+ of: 'meta',
+ },
+ parameters: { relativeCsfPaths: ['../examples/Button.stories'] },
+};
+
+export const WithoutToolbarOfStringMetaAttached: Story = {
+ name: 'Of Attached "meta" Without Toolbar',
+ args: {
+ of: 'meta',
+ },
+ parameters: { relativeCsfPaths: ['../examples/StoriesParameters.stories'] },
+};
diff --git a/code/ui/blocks/src/blocks/Primary.tsx b/code/ui/blocks/src/blocks/Primary.tsx
index 7583200f72c8..7d1364f2dc51 100644
--- a/code/ui/blocks/src/blocks/Primary.tsx
+++ b/code/ui/blocks/src/blocks/Primary.tsx
@@ -2,7 +2,8 @@ import type { FC } from 'react';
import React, { useContext } from 'react';
import dedent from 'ts-dedent';
import { deprecate } from '@storybook/client-logger';
-
+import type { Of } from './useOf';
+import { useOf } from './useOf';
import { DocsContext } from './DocsContext';
import { DocsStory } from './DocsStory';
@@ -11,17 +12,38 @@ interface PrimaryProps {
* @deprecated Primary block should only be used to render the primary story, which is automatically found.
*/
name?: string;
+ /**
+ * Specify where to get the primary story from.
+ */
+ of?: Of;
}
-export const Primary: FC = ({ name }) => {
+export const Primary: FC = (props) => {
+ const { name, of } = props;
+
+ if ('of' in props && of === undefined) {
+ throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?');
+ }
+
const docsContext = useContext(DocsContext);
+
+ let story;
+ if (of) {
+ const resolvedOf = useOf(of || 'meta', ['meta']);
+ story = resolvedOf.csfFile.stories[0] || null;
+ }
+
+ if (!story) {
+ const storyId = name && docsContext.storyIdByName(name);
+ story = docsContext.storyById(storyId);
+ }
+
if (name) {
deprecate(dedent`\`name\` prop is deprecated on the Primary block.
The Primary block should only be used to render the primary story, which is automatically found.
`);
}
- const storyId = name && docsContext.storyIdByName(name);
- const story = docsContext.storyById(storyId);
+
return story ? (
) : null;
diff --git a/docs/api/doc-block-primary.md b/docs/api/doc-block-primary.md
index 5d124df65b9f..c4993c2db2a0 100644
--- a/docs/api/doc-block-primary.md
+++ b/docs/api/doc-block-primary.md
@@ -29,6 +29,12 @@ import { Primary } from '@storybook/blocks';
`Primary` is configured with the following props:
+### `of`
+
+Type: CSF file exports
+
+Specifies which CSF file is used to find the first story, which is then rendered by this block. Pass the full set of exports from the CSF file (not the default export!).
+
### `name`
(⛔️ **Deprecated**)