Skip to content

Commit

Permalink
chore(react-examples): replace storybook deprecated api with static s…
Browse files Browse the repository at this point in the history
…tories (#32074)
  • Loading branch information
dmytrokirpa authored Jul 25, 2024
1 parent da667ca commit 1e212a4
Show file tree
Hide file tree
Showing 142 changed files with 1,882 additions and 150 deletions.
26 changes: 17 additions & 9 deletions packages/react-examples/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { createStorybookWebpackConfig } from '@fluentui/scripts-webpack';
import * as fs from 'fs';
import * as path from 'path';
import { merge } from 'webpack-merge';

/** @type {Partial<import('@storybook/core-common').StorybookConfig>} */
const config = {
stories: getStories(),
addons: [
'@storybook/addon-a11y',
'@storybook/addon-essentials',
Expand All @@ -22,15 +24,6 @@ const config = {
const customConfig = createStorybookWebpackConfig(config);

return merge(customConfig, {
module: {
rules: [
{
// Special loader that only includes stories from the current package
test: /\.storybook[/\\]preview.js/,
loader: path.resolve(__dirname, 'preview-loader.js'),
},
],
},
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
Expand All @@ -48,4 +41,19 @@ const config = {
},
};

function getStories() {
const packageName = path.basename(process.cwd());

if (!fs.existsSync(path.resolve(__dirname, '../src', packageName))) {
throw new Error(`Package ${packageName} does not have examples!`);
}

// For @fluentui/react's storybook, also include examples from @fluentui/react-focus
if (packageName === 'react') {
return ['../src/react/**/*.stories.tsx', '../src/react-focus/**/*.stories.tsx'];
}

return [`../src/${packageName}/**/*.stories.tsx`];
}

export default config;
23 changes: 0 additions & 23 deletions packages/react-examples/.storybook/preview-loader.js

This file was deleted.

117 changes: 3 additions & 114 deletions packages/react-examples/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,125 +1,14 @@
import * as React from 'react';
import { initializeIcons } from '@fluentui/font-icons-mdl2';
import { configure, addParameters, addDecorator } from '@storybook/react';
import 'cypress-storybook/react';
import { withPerformance } from 'storybook-addon-performance';
import { withKeytipLayer, withStrictMode } from '@fluentui/storybook';

initializeIcons();

// This API is deprecated (in favor of `export const decorators = []`) but the new way appears not
// to work when using the legacy configure() API
addDecorator(withPerformance);
addDecorator(withStrictMode);
addDecorator(withKeytipLayer);
export const decorators = [withPerformance, withStrictMode, withKeytipLayer];

// This API is deprecated (in favor of `export const parameters = {}`) but same issue as above
addParameters({
export const parameters = {
a11y: /** @type {import('@storybook/addon-a11y').A11yParameters} */ ({
manual: true,
}),
});

// This API is deprecated and will no longer work in storybook 7 or with storyStoreV7.
// https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-configure
// The selective loading functionality currently implemented by preview-loader can be more easily
// handled by just settings `stories: [/*glob*/]` in main.js, but it's unclear if a replacement is
// available for transforming files before loading as stories.
configure(loadStories, module);

// ==========================================================
// Helpers for loading examples in component story format
// ==========================================================

/**
* @typedef {{
* default: { title: string };
* [storyName: string]: React.FunctionComponent | { title: string };
* }} StoryGroup - Group of stories for a particular component (follows component story format)
*
* @typedef {{
* [exportName: string]: React.ComponentType;
* }} ExampleModule - A loaded `*.Example.tsx` or `*.stories.tsx` file, exporting an example component
*/

/**
* Read the `*.Example.tsx` and/or `*.stories.tsx` files for the current package `start-storybook`
* is running in and generate a list of stories in component story format.
*/
function loadStories() {
/** @type {Map<string, StoryGroup>} */
const stories = new Map();

/** @type {__WebpackModuleApi.RequireContext[]} */
const contexts = [
// This will be updated by preview-loader with the actual current package name
require.context('../src/PACKAGE_NAME', true, /\.(Example|stories)\.tsx$/),
];

// This will be replaced with the actual package name by the custom webpack loader ./preview-loader.js
// (note: for some reason the replacement doesn't work if the const is outside the function)
const packageNamePlaceholder = /** @type {string} */ ('PACKAGE_NAME');

if (packageNamePlaceholder === 'react') {
// For @fluentui/react's storybook, also include examples from @fluentui/react-focus
contexts.push(require.context('../src/react-focus', true, /\.(Example|stories)\.tsx$/));
}

for (const req of contexts) {
req.keys().forEach(key => {
generateStoriesForExampleFile(key, stories, req);
});
}

// convert stories Set to array
const sorted = [...stories.values()].sort((s1, s2) => (s1.default.title > s2.default.title ? 1 : -1));

return sorted;
}

/**
* Generate stories (usually one story) for a `*.Example.tsx` or `*.stories.tsx` file
* @param {string} key - key for the module in require.context (the relative path to the module
* from the root path passed to require.context)
* @param {Map<string, StoryGroup>} stories - mapping from component path (`Components/<ComponentName>`) to stories
* @param {__WebpackModuleApi.RequireContext} req
*/
function generateStoriesForExampleFile(key, stories, req) {
// The key will be like this:
// ./ComponentName/ComponentName.Something.Example.tsx
// group 1: component name
const pathParts = key.match(/\/(\w+)\/[\w.]+$/);
if (!pathParts) {
console.error(`Invalid path found in storybook require.context: "${key}"`);
return;
}

const componentName = pathParts[1];
const componentPath = `Components/${componentName}`;

if (!stories.has(componentPath)) {
stories.set(componentPath, {
// A default "export" with a title is required by component story format
default: {
title: componentPath,
},
});
}

const storyGroup = /** @type {StoryGroup} */ (stories.get(componentPath));
const exampleModule = /** @type {(key: string) => ExampleModule} */ (req)(key);

for (const [exportName, ExampleComponent] of Object.entries(exampleModule)) {
const storyName = exportName.replace(componentName, '').replace(/Example$/, '');

if (typeof ExampleComponent === 'function') {
if (ExampleComponent.prototype.render) {
// class component -- make a wrapper function component
storyGroup[storyName] = () => React.createElement(ExampleComponent);
} else {
// function component
storyGroup[storyName] = /** @type {React.FunctionComponent} */ (ExampleComponent);
}
}
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ import { SpinnerBasicExample } from '../components/spinner.stories';
import { DetailsListCustomColumnsExample } from '../components/DetailsListCustomColumnsExample.stories';
import { ChoiceGroupImageExample } from '../components/choiceGroupWithImagesandIcons.stories';

export default {
title: 'Components/Themes',
};

const Example = () => (
<Stack gap={8} horizontalAlign="center" style={{ maxWidth: 1000 }}>
<Stack gap={8} horizontalAlign="center">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react';
import { Calendar, Dropdown, IDropdownOption, mergeStyleSets, defaultCalendarStrings } from '@fluentui/react';

export default {
title: 'Components/CalendarInlineMultidayDayView',
};

const styles = mergeStyleSets({
wrapper: { height: 360 },
dropdown: { width: 230 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { ContextualMenuItemType, IContextualMenuProps, IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu';
import { DefaultButton } from '@fluentui/react/lib/Button';

export default {
title: 'Components/ContextualMenu',
};

export const ContextualMenuDefaultExample: React.FunctionComponent = () => {
return <DefaultButton text="Click for ContextualMenu" menuProps={menuProps} />;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { createListItems, IExampleItem } from '@fluentui/example-data';
import { Link } from '@fluentui/react/lib/Link';
import { DetailsList, buildColumns, IColumn } from '@fluentui/react/lib/DetailsList';

export default {
title: 'Components/DetailsList',
};

export interface IDetailsListCustomColumnsExampleState {
sortedItems: IExampleItem[];
columns: IColumn[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { Label, ILabelStyles } from '@fluentui/react/lib/Label';
import { Pivot, PivotItem } from '@fluentui/react/lib/Pivot';
import { IStyleSet } from '@fluentui/react/lib/Styling';

export default {
title: 'Components/Pivot',
};

const labelStyles: Partial<IStyleSet<ILabelStyles>> = {
root: { marginTop: 10 },
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react';
import { ProgressIndicator } from '@fluentui/react/lib/ProgressIndicator';

export default {
title: 'Components/ProgressIndicator',
};

const intervalDelay = 100;
const intervalIncrement = 0.01;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { SpinButton, ISpinButtonStyles, Position, IIconProps } from '@fluentui/react';
import { Stack, IStackTokens } from '@fluentui/react/lib/Stack';

export default {
title: 'Components/SpinButton',
};

const stackTokens: IStackTokens = { childrenGap: 20 };
// By default the field grows to fit available width. Constrain the width instead.
const styles: Partial<ISpinButtonStyles> = { spinButtonWrapper: { width: 75 } };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { DefaultButton, IButtonProps } from '@fluentui/react/lib/Button';
import { TeachingBubble } from '@fluentui/react/lib/TeachingBubble';
import { useBoolean } from '@fluentui/react-hooks';

export default {
title: 'Components/TeachingBubble',
};

const examplePrimaryButtonProps: IButtonProps = {
children: 'Try it out',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { IIconProps } from '@fluentui/react';
import { ActionButton } from '@fluentui/react/lib/Button';

export default {
title: 'Components/ActionButton',
};

export interface IButtonExampleProps {
// These are set based on the toggles shown above the examples (not needed in real code)
disabled?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react';
import { ActivityItem, Icon, Link, mergeStyleSets } from '@fluentui/react';

export default {
title: 'Components/ActivityItem',
};

const classNames = mergeStyleSets({
exampleRoot: {
marginTop: '20px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { ActivityItem, IActivityItemProps, Link, mergeStyleSets } from '@fluentui/react';
import { TestImages } from '@fluentui/example-data';

export default {
title: 'Components/ActivityItemPersona',
};

const classNames = mergeStyleSets({
exampleRoot: {
marginTop: '20px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { IIconProps } from '@fluentui/react';
import { DefaultButton } from '@fluentui/react/lib/Button';

export default {
title: 'Components/ButtonToggle',
};

export interface IButtonExampleProps {
// These are set based on the toggles shown above the examples (not needed in real code)
disabled?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
getId,
} from '@fluentui/react';

export default {
title: 'Components/Callout',
};

export interface ICalloutBasicExampleState {
isCalloutVisible?: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { ChoiceGroup, IChoiceGroupOption } from '@fluentui/react/lib/ChoiceGroup';
import { TestImages } from '@fluentui/example-data';

export default {
title: 'Components/ChoiceGroup',
};

const options: IChoiceGroupOption[] = [
{
key: 'bar',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react';
import { ChoiceGroup, IChoiceGroupOption } from '@fluentui/react/lib/ChoiceGroup';

export default {
title: 'Components/ChoiceGroup',
};

const options: IChoiceGroupOption[] = [
{ key: 'A', text: 'Option A' },
{ key: 'B', text: 'Option B' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { ColorPicker, Toggle, getColorFromString, IColor, IColorPickerStyles, updateA } from '@fluentui/react';
import { mergeStyleSets } from '@fluentui/react/lib/Styling';

export default {
title: 'Components/ColorPicker',
};

const classNames = mergeStyleSets({
wrapper: { display: 'flex' },
column2: { marginLeft: 10 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
} from '@fluentui/react';
import { PrimaryButton } from '@fluentui/react/lib/Button';

export default {
title: 'Components/ComboBox',
};

const INITIAL_OPTIONS: IComboBoxOption[] = [
{ key: 'Header1', text: 'First heading', itemType: SelectableOptionMenuItemType.Header },
{ key: 'A', text: 'Option A' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as React from 'react';
import { CommandBar, ICommandBarItemProps } from '@fluentui/react/lib/CommandBar';
import { IButtonProps } from '@fluentui/react/lib/Button';

export default {
title: 'Components/CommandBar',
};

const overflowProps: IButtonProps = { ariaLabel: 'More commands' };

export const CommandBarBasicExample: React.FunctionComponent = () => {
Expand Down
Loading

0 comments on commit 1e212a4

Please sign in to comment.