diff --git a/packages/carbon-react/.storybook/main.js b/packages/carbon-react/.storybook/main.js index 1352640a42ea..ca348a08bfdc 100644 --- a/packages/carbon-react/.storybook/main.js +++ b/packages/carbon-react/.storybook/main.js @@ -7,9 +7,51 @@ 'use strict'; +const fs = require('fs'); +const glob = require('fast-glob'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const path = require('path'); +const stories = glob + .sync( + [ + './Welcome/Welcome.stories.js', + '../src/**/*.stories.js', + '../src/**/*.stories.mdx', + '../../react/src/**/next/*.stories.js', + '../../react/src/**/next/*.stories.mdx', + '../../react/src/**/*-story.js', + ], + { + cwd: __dirname, + } + ) + // Filters the stories by finding the paths that have a story file that ends + // in `-story.js` and checks to see if they also have a `.stories.js`, + // if so then defer to the `.stories.js` + .filter((match) => { + const filepath = path.resolve(__dirname, match); + const basename = path.basename(match, '.js'); + + if (basename.endsWith('-story')) { + const component = basename.replace(/-story$/, ''); + const storyName = path.resolve( + filepath, + '..', + 'next', + `${component}.stories.js` + ); + + if (fs.existsSync(storyName)) { + return false; + } + + return true; + } + + return true; + }); + module.exports = { addons: [ { @@ -32,13 +74,7 @@ module.exports = { features: { previewCsfV3: true, }, - stories: [ - './Welcome/Welcome.stories.js', - '../src/**/*.stories.js', - '../src/**/*.stories.mdx', - '../../react/src/**/next/*.stories.js', - '../../react/src/**/next/*.stories.mdx', - ], + stories, webpack(config) { const babelLoader = config.module.rules.find((rule) => { return rule.use.some(({ loader }) => { diff --git a/packages/carbon-react/.storybook/preview.js b/packages/carbon-react/.storybook/preview.js index 6867fb6a9fcb..2ddbd2fab609 100644 --- a/packages/carbon-react/.storybook/preview.js +++ b/packages/carbon-react/.storybook/preview.js @@ -12,6 +12,7 @@ import { white, g10, g90, g100 } from '@carbon/themes'; import React from 'react'; import { breakpoints } from '@carbon/layout'; import { unstable_ThemeContext as ThemeContext } from '../src'; +import { addParameters } from '@storybook/react'; export const globalTypes = { locale: { @@ -132,6 +133,74 @@ export const parameters = { }, }; +addParameters({ + options: { + storySort: (storyA, storyB) => { + // By default, sort by the story "kind". The "kind" refers to the + // top-level title of the story, either through Component Story Format + // with the default export, or the `storiesOf('kind', module)` format + if (storyA[1].kind !== storyB[1].kind) { + return storyA[1].kind.localeCompare(storyB[1].kind); + } + + const idA = storyA[0]; + const idB = storyB[0]; + + // To story the stories, we first build up a list of matches based on + // keywords. Each keyword has a specific weight that will be used to + // determine order later on. + const UNKNOWN_KEYWORD = 3; + const keywords = new Map([ + ['welcome', 0], + ['default', 1], + ['usage', 2], + ['playground', 4], + ['development', 5], + ['deprecated', 6], + ['unstable', 7], + ]); + const matches = new Map(); + + // We use this list of keywords to determine a collection of matches. By + // default, we will look for the greatest valued matched + for (const [keyword, weight] of keywords) { + // If we already have a match for a given id that is greater than the + // specific keyword we're looking for, break early + if (matches.get(idA) > weight || matches.get(idB) > weight) { + break; + } + + // If we don't have a match already for either id, we check to see if + // the id includes the keyword and assigns the relevant weight, if so + if (idA.includes(keyword)) { + matches.set(idA, weight); + } + + if (idB.includes(keyword)) { + matches.set(idB, weight); + } + } + + // If we have matches for either id, then we will compare the ids based on + // the weight assigned to the matching keyword + if (matches.size > 0) { + const weightA = matches.get(idA) ?? UNKNOWN_KEYWORD; + const weightB = matches.get(idB) ?? UNKNOWN_KEYWORD; + // If we have the same weight for the ids, then we should compare them + // using locale compare instead of by weight + if (weightA === weightB) { + return idA.localeCompare(idB); + } + return weightA - weightB; + } + + // By default, if we have no matches we'll do a locale compare between the + // two ids + return idA.localeCompare(idB); + }, + }, +}); + configureActions({ depth: 3, limit: 10, diff --git a/packages/carbon-react/package.json b/packages/carbon-react/package.json index 3a32a5bf1647..8a54ac9b84e4 100644 --- a/packages/carbon-react/package.json +++ b/packages/carbon-react/package.json @@ -77,6 +77,7 @@ "babel-preset-carbon": "^0.2.0", "browserslist-config-carbon": "^10.6.1", "css-loader": "^6.5.1", + "fast-glob": "^3.2.7", "fs-extra": "^10.0.0", "mini-css-extract-plugin": "^2.4.5", "postcss": "^8.4.5", diff --git a/yarn.lock b/yarn.lock index 1316b694e733..61c6e6bec770 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2095,6 +2095,7 @@ __metadata: carbon-components-react: ^7.52.0-rc.0 carbon-icons: ^7.0.7 css-loader: ^6.5.1 + fast-glob: ^3.2.7 fs-extra: ^10.0.0 mini-css-extract-plugin: ^2.4.5 postcss: ^8.4.5