Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "feat: add opt-in `storyGranularity: 'story'` mode that emits a per-story sliced `fullSource` for files with multiple story exports",
"packageName": "@fluentui/babel-preset-storybook-full-source",
"email": "martinhochel@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ To use this Babel preset, add it to your Babel configuration:
- **Removes Storybook specific assignments**: Avoids issues with undefined stories and unnecessary clutter.
- **Collects and modifies import declarations**: Ensures valid single-file code examples.
- **Adds the `context.parameters.fullSource` property**: post-processed, single-file source for the "Open in Sandbox" flow.
- **Per-story source** (opt-in via `storyGranularity: 'story'`): when a story file contains multiple story exports (the standard Component Story Format convention), each story gets its **own** sliced `fullSource` containing only the imports and helper declarations it references, plus that single story converted to a renderable function. CSF3 object stories (`{ render }`, or `args`-only stories rendered via the meta `component`) and spread stories (`{ ...Base }`) are supported. Capitalized non-story exports (e.g. shared data objects) are ignored — only genuine story-shaped exports are emitted. `play`/`parameters` are omitted from the generated sample (source files are never modified). Defaults to `'file'`, which attaches the whole file to the last story export (suited to the one-story-per-file convention).
- **CSS module support** (opt-in via `cssModules` option): when enabled, reads `*.module.css` files from disk and injects `context.parameters.cssModuleSources` with `{ cssModules, tokensSource }` entries for the sandbox addon and docs panel. Set `cssModules: true` to enable, or `cssModules: { tokensFilePath: '...' }` to also inject a tokens CSS file as `tokensSource`.

## Note
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as Babel from '@babel/core';
export interface BabelPluginOptions {
cssModules?: boolean | CssModulesConfig;
importMappings: Record<string, DependencyEntry>;
storyGranularity?: 'file' | 'story';
}

// @public
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';

const meta = {
title: 'Button',
component: Button,
};

export default meta;

// Genuine story — should get a sliced fullSource.
export const Default = {
render: () => <Button>Default</Button>,
};

// Capitalized data export (not a story) — must NOT be turned into a fake story.
export const BrandColors = {
primary: '#0f6cbd',
danger: '#c50f1f',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';
const meta = {
title: 'Button',
component: Button,
};
export default meta;

// Genuine story — should get a sliced fullSource.
export const Default = {
render: () => /*#__PURE__*/ React.createElement(Button, null, 'Default'),
};

// Capitalized data export (not a story) — must NOT be turned into a fake story.
export const BrandColors = {
primary: '#0f6cbd',
danger: '#c50f1f',
};
Default.parameters = {};
Default.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\n\n// Genuine story \u2014 should get a sliced fullSource.\nexport const Default = () => <Button>Default</Button>;\n';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';

const meta = {
title: 'Button',
component: Button,
args: {
appearance: 'primary',
},
};

export default meta;

export const Default = {
render: () => <Button>Default</Button>,
};

export const WithArgs = {
args: {
appearance: 'outline',
},
render: args => <Button {...args}>With args</Button>,
};

export const ArgsOnly = {
args: {
children: 'Args only',
},
play: async () => {
/* interaction test - must not leak into fullSource */
},
parameters: {
docs: {
description: {
story: 'Rendered purely from args.',
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';
const meta = {
title: 'Button',
component: Button,
args: {
appearance: 'primary',
},
};
export default meta;
export const Default = {
render: () => /*#__PURE__*/ React.createElement(Button, null, 'Default'),
};
export const WithArgs = {
args: {
appearance: 'outline',
},
render: args => /*#__PURE__*/ React.createElement(Button, args, 'With args'),
};
export const ArgsOnly = {
args: {
children: 'Args only',
},
play: async () => {
/* interaction test - must not leak into fullSource */
},
parameters: {
docs: {
description: {
story: 'Rendered purely from args.',
},
},
},
};
Default.parameters = {};
Default.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\nexport const Default = () => <Button>Default</Button>;\n';
WithArgs.parameters = {};
WithArgs.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\nexport const WithArgs = () => {\n const args = { appearance: "outline" };\n return <Button {...args}>With args</Button>;\n};\n';
ArgsOnly.parameters = {};
ArgsOnly.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\nexport const ArgsOnly = () => {\n const args = { appearance: "primary", children: "Args only" };\n return <Button {...args} />;\n};\n';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';

const meta = {
title: 'Button',
component: Button,
};

export default meta;

export const Base = {
render: () => <Button appearance="primary">Base</Button>,
};

export const Derived = {
...Base,
parameters: {
docs: {
description: {
story: 'Spreads Base.',
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';
const meta = {
title: 'Button',
component: Button,
};
export default meta;
export const Base = {
render: () =>
/*#__PURE__*/ React.createElement(
Button,
{
appearance: 'primary',
},
'Base',
),
};
export const Derived = {
...Base,
parameters: {
docs: {
description: {
story: 'Spreads Base.',
},
},
},
};
Base.parameters = {};
Base.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\nexport const Base = () => <Button appearance="primary">Base</Button>;\n';
Derived.parameters = {};
Derived.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\nexport const Derived = () => <Button appearance="primary">Base</Button>;\n';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';

const Wrapper = ({ children }) => <div className="wrapper">{children}</div>;

export const Primary = () => (
<Wrapper>
<Button appearance="primary">Primary</Button>
</Wrapper>
);

export const Secondary = () => <Button appearance="secondary">Secondary</Button>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';
const Wrapper = ({ children }) =>
/*#__PURE__*/ React.createElement(
'div',
{
className: 'wrapper',
},
children,
);
export const Primary = () =>
/*#__PURE__*/ React.createElement(
Wrapper,
null,
/*#__PURE__*/ React.createElement(
Button,
{
appearance: 'primary',
},
'Primary',
),
);
export const Secondary = () =>
/*#__PURE__*/ React.createElement(
Button,
{
appearance: 'secondary',
},
'Secondary',
);
Primary.parameters = {};
Primary.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\n\nconst Wrapper = ({ children }) => <div className="wrapper">{children}</div>;\n\nexport const Primary = () => (\n <Wrapper>\n <Button appearance="primary">Primary</Button>\n </Wrapper>\n);\n';
Secondary.parameters = {};
Secondary.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\n\nexport const Secondary = () => (\n <Button appearance="secondary">Secondary</Button>\n);\n';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';

const meta = {
title: 'Card',
component: Button,
};

export default meta;

// --- Custom React components defined in the module ---
const Card = ({ children }) => <div className="card">{children}</div>;

const CardHeader = ({ title }) => <h3 className="card-header">{title}</h3>;

const CardMedia = ({ src }) => <img className="card-media" src={src} alt="" />;

const CardFooter = () => <footer className="card-footer">© Contoso</footer>;

// Uses Card + CardHeader + Button.
export const Basic = {
render: () => (
<Card>
<CardHeader title="Basic" />
<Button>Action</Button>
</Card>
),
};

// Uses Card + CardMedia only.
export const WithMedia = {
render: () => (
<Card>
<CardMedia src="cat.png" />
</Card>
),
};

// Uses Card + CardHeader + CardMedia + CardFooter.
export const Full = {
render: () => (
<Card>
<CardHeader title="Full" />
<CardMedia src="dog.png" />
<CardFooter />
</Card>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as React from 'react';
import { Button } from '@fluentui/react-button';
const meta = {
title: 'Card',
component: Button,
};
export default meta;

// --- Custom React components defined in the module ---
const Card = ({ children }) =>
/*#__PURE__*/ React.createElement(
'div',
{
className: 'card',
},
children,
);
const CardHeader = ({ title }) =>
/*#__PURE__*/ React.createElement(
'h3',
{
className: 'card-header',
},
title,
);
const CardMedia = ({ src }) =>
/*#__PURE__*/ React.createElement('img', {
className: 'card-media',
src: src,
alt: '',
});
const CardFooter = () =>
/*#__PURE__*/ React.createElement(
'footer',
{
className: 'card-footer',
},
'\xA9 Contoso',
);

// Uses Card + CardHeader + Button.
export const Basic = {
render: () =>
/*#__PURE__*/ React.createElement(
Card,
null,
/*#__PURE__*/ React.createElement(CardHeader, {
title: 'Basic',
}),
/*#__PURE__*/ React.createElement(Button, null, 'Action'),
),
};

// Uses Card + CardMedia only.
export const WithMedia = {
render: () =>
/*#__PURE__*/ React.createElement(
Card,
null,
/*#__PURE__*/ React.createElement(CardMedia, {
src: 'cat.png',
}),
),
};

// Uses Card + CardHeader + CardMedia + CardFooter.
export const Full = {
render: () =>
/*#__PURE__*/ React.createElement(
Card,
null,
/*#__PURE__*/ React.createElement(CardHeader, {
title: 'Full',
}),
/*#__PURE__*/ React.createElement(CardMedia, {
src: 'dog.png',
}),
/*#__PURE__*/ React.createElement(CardFooter, null),
),
};
Basic.parameters = {};
Basic.parameters.fullSource =
'import { Button } from "@fluentui/react-components";\nimport * as React from "react";\n\n// --- Custom React components defined in the module ---\nconst Card = ({ children }) => <div className="card">{children}</div>;\n\nconst CardHeader = ({ title }) => <h3 className="card-header">{title}</h3>;\n\n// Uses Card + CardHeader + Button.\nexport const Basic = () => (\n <Card>\n <CardHeader title="Basic" />\n <Button>Action</Button>\n </Card>\n);\n';
WithMedia.parameters = {};
WithMedia.parameters.fullSource =
'import * as React from "react";\n\n// --- Custom React components defined in the module ---\nconst Card = ({ children }) => <div className="card">{children}</div>;\n\nconst CardMedia = ({ src }) => <img className="card-media" src={src} alt="" />;\n\n// Uses Card + CardMedia only.\nexport const WithMedia = () => (\n <Card>\n <CardMedia src="cat.png" />\n </Card>\n);\n';
Full.parameters = {};
Full.parameters.fullSource =
'import * as React from "react";\n\n// --- Custom React components defined in the module ---\nconst Card = ({ children }) => <div className="card">{children}</div>;\n\nconst CardHeader = ({ title }) => <h3 className="card-header">{title}</h3>;\n\nconst CardMedia = ({ src }) => <img className="card-media" src={src} alt="" />;\n\nconst CardFooter = () => <footer className="card-footer">\xA9 Contoso</footer>;\n\n// Uses Card + CardHeader + CardMedia + CardFooter.\nexport const Full = () => (\n <Card>\n <CardHeader title="Full" />\n <CardMedia src="dog.png" />\n <CardFooter />\n </Card>\n);\n';
Loading
Loading