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

Storybook: Add webpack loader for easier story descriptions #39165

Merged
merged 2 commits into from Mar 7, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 14 additions & 32 deletions packages/components/src/font-size-picker/stories/index.js
Expand Up @@ -91,20 +91,19 @@ WithSlider.args = {
withSlider: true,
};

/**
* With custom font sizes disabled via the `disableCustomFontSizes` prop, the user will
* only be able to pick one of the predefined sizes passed in `fontSizes`.
*/
export const WithCustomSizesDisabled = FontSizePickerWithState.bind( {} );
WithCustomSizesDisabled.args = {
...Default.args,
disableCustomFontSizes: true,
};
WithCustomSizesDisabled.parameters = {
docs: {
description: {
story:
'With custom font sizes disabled via the `disableCustomFontSizes` prop, the user will only be able to pick one of the predefined sizes passed in `fontSizes`.',
},
},
};

/**
* When there are more than 5 font size options, the UI is no longer a toggle group.
*/
export const WithMoreFontSizes = FontSizePickerWithState.bind( {} );
WithMoreFontSizes.args = {
...Default.args,
Expand Down Expand Up @@ -142,15 +141,10 @@ WithMoreFontSizes.args = {
],
initialValue: 8,
};
WithMoreFontSizes.parameters = {
docs: {
description: {
story:
'When there are more than 5 font size options, the UI is no longer a toggle group.',
},
},
};

/**
* When units like `px` are specified explicitly, it will be shown as a label hint.
*/
export const WithUnits = TwoFontSizePickersWithState.bind( {} );
WithUnits.args = {
...WithMoreFontSizes.args,
Expand All @@ -160,15 +154,11 @@ WithUnits.args = {
} ) ),
initialValue: '8px',
};
WithUnits.parameters = {
docs: {
description: {
story:
'When units like `px` are specified explicitly, it will be shown as a label hint.',
},
},
};

/**
* The label hint will not be shown if it is a complex CSS value. Some examples of complex CSS values
* in this context are CSS functions like `calc()`, `clamp()`, and `var()`.
*/
export const WithComplexCSSValues = TwoFontSizePickersWithState.bind( {} );
WithComplexCSSValues.args = {
...Default.args,
Expand Down Expand Up @@ -207,11 +197,3 @@ WithComplexCSSValues.args = {
],
initialValue: '1.125rem',
};
WithComplexCSSValues.parameters = {
docs: {
description: {
story:
'The label hint will not be shown if it is a complex CSS value. Some examples of complex CSS values in this context are CSS functions like `calc()`, `clamp()`, and `var()`.',
},
},
};
13 changes: 12 additions & 1 deletion storybook/webpack.config.js
Expand Up @@ -29,10 +29,21 @@ const scssLoaders = ( { isLazy } ) => [
module.exports = ( { config } ) => {
config.module.rules.push(
{
test: /\/stories\/.+\.js$/,
// Currently does not work with our tsx stories
// See https://github.com/storybookjs/storybook/issues/17275
test: /\/stories\/.+\.(j|t)sx?$/,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried adding a Story description to Divider (a tsx story) and it seemed to work — what should we expect to be broken?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule is for @storybook/source-loader, aka this tab:

The "Story" tab on Storybook for the Divider component is stuck on the "1 loading source..." state

It stays stuck in this loading state. Fortunately this Story tab isn't particularly necessary once we get the code snippets cleaned up in Docs view. We might even remove it at some point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if we show code snippets in the docs it's not really necessary

loader: require.resolve( '@storybook/source-loader' ),
enforce: 'pre',
},
{
// Allows a story description to be written as a doc comment above the exported story
test: /\/stories\/.+\.(j|t)sx?$/,
loader: path.resolve(
__dirname,
'./webpack/description-loader.js'
),
enforce: 'post',
},
{
test: /\.scss$/,
exclude: /\.lazy\.scss$/,
Expand Down
94 changes: 94 additions & 0 deletions storybook/webpack/description-loader.js
@@ -0,0 +1,94 @@
/**
* Allows a story description to be written as a doc comment above the exported story.
*
* Based on https://github.com/izhan/storybook-description-loader
*
* @example
* ```jsx
* // This comment will become the description for the story in the generated docs.
* export const MyStory = Template.bind({});
* ```
*/

/**
* External dependencies
*/
const babel = require( '@babel/core' );

function createDescriptionNode( name, description ) {
return babel.types.expressionStatement(
babel.types.assignmentExpression(
'=',
babel.types.memberExpression(
babel.types.identifier( name ),
babel.types.identifier( 'story' )
),
babel.types.objectExpression( [
babel.types.objectProperty(
babel.types.identifier( 'parameters' ),
babel.types.objectExpression( [
babel.types.objectProperty(
babel.types.identifier( 'docs' ),
babel.types.objectExpression( [
babel.types.objectProperty(
babel.types.identifier(
'storyDescription'
),
babel.types.stringLiteral( description )
),
] )
),
] )
),
] )
)
);
}

function annotateDescriptionPlugin() {
return {
visitor: {
ExportNamedDeclaration( path ) {
if ( path.node.leadingComments ) {
const commentValues = path.node.leadingComments.map(
( node ) => {
if ( node.type === 'CommentLine' ) {
return node.value.trimLeft();
}
// else, node.type === 'CommentBlock'
return node.value
.split( '\n' )
.map( ( line ) => {
// stripping out the whitespace and * from comment blocks
return line.replace(
/^(\s+)?(\*+)?(\s+)?/,
''
);
} )
.join( '\n' )
.trim();
}
);
const description = commentValues.join( '\n' );
const declaration = path.node.declaration.declarations[ 0 ];

path.insertAfter(
createDescriptionNode(
declaration.id.name,
description
)
);
}
},
},
};
}

module.exports = function ( source ) {
const output = babel.transform( source, {
plugins: [ annotateDescriptionPlugin ],
filename: __filename,
sourceType: 'module',
} );
return output.code;
};