Skip to content

Commit

Permalink
Storybook: Add webpack loader for easier story descriptions (#39165)
Browse files Browse the repository at this point in the history
* Storybook: Add webpack loader for story descriptions

* Convert story descriptions for FontSizePicker
  • Loading branch information
mirka committed Mar 7, 2022
1 parent 2834416 commit 555fa88
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 33 deletions.
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?$/,
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;
};

0 comments on commit 555fa88

Please sign in to comment.