From 016f27bae1c417b8542698a2f136e033c549889b Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 20 Jan 2025 15:04:14 +0100 Subject: [PATCH 01/12] add docs codemod command for scripts directory --- scripts/package.json | 2 + scripts/snippets/codemod.ts | 372 ++++++++++++++++++++++++++++++++++++ scripts/utils/constants.ts | 1 + scripts/yarn.lock | 212 ++++++++++++++++++-- 4 files changed, 575 insertions(+), 12 deletions(-) create mode 100644 scripts/snippets/codemod.ts diff --git a/scripts/package.json b/scripts/package.json index 0dae28f4b070..bcb940272fd4 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -10,6 +10,7 @@ "check-package": "jiti ./check-package.ts", "docs:prettier:check": "cd ../docs && prettier --check ./_snippets || echo 'Please run \"docs:prettier:write\" in the \"scripts\" directory to fix the issues'", "docs:prettier:write": "cd ../docs && prettier --write ./_snippets", + "docs:codemod": "jiti ./snippets/codemod.ts", "generate-sandboxes": "jiti ./sandbox/generate.ts", "get-report-message": "jiti ./get-report-message.ts", "get-sandbox-dir": "jiti ./get-sandbox-dir.ts", @@ -129,6 +130,7 @@ "fast-glob": "^3.3.2", "github-release-from-changelog": "^2.1.1", "glob": "^10.4.5", + "globby": "^14.0.1", "http-server": "^14.1.1", "husky": "^4.3.7", "jiti": "^2.4.2", diff --git a/scripts/snippets/codemod.ts b/scripts/snippets/codemod.ts new file mode 100644 index 000000000000..fbb35c05276b --- /dev/null +++ b/scripts/snippets/codemod.ts @@ -0,0 +1,372 @@ +/** + * DISCLAIMER: + * + * This file exists with the sole purpose of assisting the documentation snippets during + * introduction of new features. This will probably used only once or twice, but it's REALLY useful + * to test codemods as it helps detect many bugs very quickly. It also will be used once we decide + * to add extra snippets to more renderers. + */ + +/* eslint-disable @typescript-eslint/no-loop-func */ + +/* eslint-disable @typescript-eslint/default-param-last */ +import os from 'node:os'; +import { join } from 'node:path'; + +import { program } from 'commander'; +import { promises as fs } from 'fs'; +import pLimit from 'p-limit'; +import picocolors from 'picocolors'; +import prompts from 'prompts'; +import slash from 'slash'; + +import { configToCsfFactory } from '../../code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory'; +import { storyToCsfFactory } from '../../code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory'; +import { SNIPPETS_DIRECTORY } from '../utils/constants'; + +const logger = console; + +export const maxConcurrentTasks = Math.max(1, os.cpus().length - 1); + +type SnippetInfo = { + path: string; + source: string; + attributes: { + filename: string; + language: string; + renderer: string; + tabTitle: string; + highlightSyntax: string; + [key: string]: string; + }; +}; + +type Codemod = { + getTargetSnippet: (snippetInfo: SnippetInfo) => boolean; + check: (snippetInfo: SnippetInfo, filePath: string) => boolean; + transform: (snippetInfo: SnippetInfo) => string | Promise; +}; + +const previousTabTitle = 'CSF 3'; +const newTabTitle = 'CSF Factory ๐Ÿงช'; + +export async function runSnippetCodemod({ + glob, + check, + getTargetSnippet, + transform, + dryRun = false, + promptUser = false, +}: { + glob: string; + check: Codemod['check']; + getTargetSnippet: Codemod['getTargetSnippet']; + transform: Codemod['transform']; + dryRun?: boolean; + promptUser?: boolean; +}) { + let modifiedCount = 0; + let unmodifiedCount = 0; + let errorCount = 0; + let skippedCount = 0; + + try { + // Dynamically import these packages because they are pure ESM modules + // eslint-disable-next-line depend/ban-dependencies + const { globby } = await import('globby'); + + const files = await globby(slash(glob), { + followSymbolicLinks: true, + ignore: ['node_modules/**', 'dist/**', 'storybook-static/**', 'build/**'], + }); + + if (!files.length) { + logger.error(`No files found for pattern ${glob}`); + return; + } + + const limit = pLimit(10); + + for (const file of files) { + await limit(async () => { + try { + let source = await fs.readFile(file, 'utf-8'); + const originalSource = source; + const snippets = extractSnippets(source).filter((snip) => check(snip, file)); + + if (snippets.length === 0) { + unmodifiedCount++; + return; + } + + const targetSnippet = snippets.find(getTargetSnippet); + if (!targetSnippet) { + skippedCount++; + return; + } + + if (promptUser) { + logger.log(`\nFile: ${picocolors.yellow(file)}`); + const response = await prompts( + { + type: 'confirm', + name: 'apply', + message: `Apply codemod?`, + initial: true, + }, + { + onCancel: () => process.exit(0), + } + ); + + if (!response.apply) { + skippedCount++; + return; + } + } + + const counterpartSnippets = snippets.filter((snippet) => { + return ( + snippet !== targetSnippet && + snippet.attributes.tabTitle !== previousTabTitle && + snippet.attributes.tabTitle !== newTabTitle && + snippet.attributes.renderer === targetSnippet.attributes.renderer && + snippet.attributes.language !== targetSnippet.attributes.language + ); + }); + + const getSource = (snippet: SnippetInfo) => + `\n\`\`\`${formatAttributes(snippet.attributes)}\n${snippet.source}\n\`\`\`\n`; + + const updateTabTitle = (snippet: SnippetInfo, newTitle: string) => { + return snippet.attributes.tabTitle === newTitle + ? newTitle + : snippet.attributes.tabTitle + ? `${snippet.attributes.tabTitle} (${newTitle})` + : newTitle; + }; + + const allSnippets = [targetSnippet, ...counterpartSnippets]; + + let lastModifiedSnippet = ''; + // clone the snippets and apply codemod, then append them to the bottom + try { + let appendedContent = ''; + + if (!dryRun) { + // replace attributes of the original snippets with CSF 3 + await allSnippets.forEach(async (snippet) => { + // warn us if there is already a tab title + source = source.replace( + formatAttributes(snippet.attributes), + formatAttributes({ + ...snippet.attributes, + tabTitle: updateTabTitle(snippet, previousTabTitle), + }) + ); + await fs.writeFile(file, source, 'utf-8'); + }); + } + + for (const snippet of allSnippets) { + // warn us if there is already a tab title + if (snippet !== targetSnippet) { + appendedContent += + '\n\n'; + } + + const newSnippet = { ...snippet }; + lastModifiedSnippet = formatAttributes(newSnippet.attributes); + let transformedSource = getSource({ + ...newSnippet, + attributes: { + ...newSnippet.attributes, + renderer: 'react', + tabTitle: updateTabTitle(newSnippet, newTabTitle), + }, + source: await transform(newSnippet), + }); + + if (newSnippet.path.includes('.stories')) { + transformedSource = transformedSource + .replace(/\/\/ Replace your-renderer with .*\n/, '') + .replace(/\/\/ Replace your-framework with .*\n/, '') + .replace( + /(import preview from \"#\.storybook\/preview\";)/g, + '// Learn about the # subpath import: https://storybook.js.org/docs/api/csf/csf-factories#subpath-imports\n$1' + ); + } else { + transformedSource = transformedSource.replace( + /Replace your-framework with .*\n/, + 'Replace your-framework with the framework you are using (e.g., react-vite, nextjs, experimental-nextjs-vite)\n' + ); + } + appendedContent += transformedSource; + } + + const updatedSource = source + appendedContent; + + if (!dryRun) { + await fs.writeFile(file, updatedSource, 'utf-8'); + } else { + logger.log( + `Dry run: would have modified ${picocolors.yellow(file)} with new snippets \n` + + picocolors.green(appendedContent) + ); + } + + modifiedCount++; + } catch (transformError) { + logger.error( + `\nError transforming snippet in file ${picocolors.yellow(file)}:`, + '\n', + picocolors.green(lastModifiedSnippet), + '\n', + picocolors.red((transformError as any).message) + ); + errorCount++; + await fs.writeFile(file, originalSource, 'utf-8'); + } + } catch (fileError) { + logger.error(`Error processing file ${file}:`, fileError); + errorCount++; + } + }); + } + } catch (error) { + logger.error('Error applying snippet transform:', error); + errorCount++; + } + + logger.log( + `Summary: ${picocolors.green(`${modifiedCount} files modified`)}, ${picocolors.yellow(`${unmodifiedCount} files unmodified`)}, ${picocolors.gray(`${skippedCount} skipped`)}, ${picocolors.red(`${errorCount} errors`)}` + ); +} + +export function extractSnippets(source: string): SnippetInfo[] { + const snippetRegex = + /```(?[a-zA-Z0-9]+)?(?[^\n]*)\n(?[\s\S]*?)```/g; + const snippets: SnippetInfo[] = []; + let match; + + while ((match = snippetRegex.exec(source)) !== null) { + const { highlightSyntax, attributes, content } = match.groups || {}; + const snippetAttributes = parseAttributes(attributes || ''); + if (highlightSyntax) { + snippetAttributes.highlightSyntax = highlightSyntax.trim(); + } + + snippets.push({ + path: snippetAttributes.filename || '', + source: content.trim(), + attributes: snippetAttributes, + }); + } + + return snippets; +} + +export function parseAttributes(attributes: string) { + const attributeRegex = /([a-zA-Z0-9.-]+)="([^"]+)"/g; + const result: Record = {}; + let match; + + while ((match = attributeRegex.exec(attributes)) !== null) { + result[match[1]] = match[2]; + } + + return result as SnippetInfo['attributes']; +} + +function formatAttributes(attributes: Record): string { + const formatted = Object.entries(attributes) + .filter(([key]) => key !== 'highlightSyntax') + .map(([key, value]) => `${key}="${value}"`) + .join(' '); + return `${attributes.highlightSyntax || 'js'} ${formatted}`; +} + +const codemods: Record = { + 'csf-factory-story': { + check: (snippetInfo: SnippetInfo, filePath: string) => { + return ( + snippetInfo.path.includes('.stories') && + !snippetInfo.attributes?.filename?.includes('CSF 2') && + !filePath.split('/')?.pop()?.startsWith('csf-3') && + snippetInfo.attributes.highlightSyntax !== 'mdx' && + snippetInfo.attributes.tabTitle !== previousTabTitle && + snippetInfo.attributes.tabTitle !== newTabTitle + ); + }, + getTargetSnippet: (snippetInfo: SnippetInfo) => { + return ( + snippetInfo.attributes.language === 'ts' && + snippetInfo.attributes.tabTitle !== previousTabTitle && + snippetInfo.attributes.tabTitle !== newTabTitle && + (snippetInfo.attributes.renderer === 'react' || + snippetInfo.attributes.renderer === 'common') + ); + }, + transform: (snippetInfo: SnippetInfo) => { + return storyToCsfFactory(snippetInfo, { + previewConfigPath: undefined, + useSubPathImports: true, + }); + }, + }, + 'csf-factory-config': { + check: (snippetInfo: SnippetInfo) => { + return ( + snippetInfo.attributes.tabTitle !== previousTabTitle && + snippetInfo.attributes.tabTitle !== newTabTitle && + (snippetInfo.path.includes('/preview.') || snippetInfo.path.includes('/main.')) + ); + }, + getTargetSnippet: (snippetInfo: SnippetInfo) => { + return ( + snippetInfo.attributes.language === 'ts' && + snippetInfo.attributes.tabTitle !== previousTabTitle && + snippetInfo.attributes.tabTitle !== newTabTitle && + (snippetInfo.attributes.renderer === 'react' || + snippetInfo.attributes.renderer === 'common') + ); + }, + transform: (snippetInfo: SnippetInfo) => { + const configType = snippetInfo.path.includes('preview') ? 'preview' : 'main'; + return configToCsfFactory(snippetInfo, { + configType, + frameworkPackage: '@storybook/your-framework', + }); + }, + }, +}; + +program + .name('command') + .description('A minimal CLI for demonstration') + .argument('', 'ID to process') + .requiredOption('--glob ', 'Glob pattern to match') + .option('--dry-run', 'Run without making actual changes', false) + .option('--prompt', 'Prompt before applying changes', false) + .action(async (id, { glob, dryRun, prompt }) => { + const codemod = codemods[id as keyof typeof codemods]; + if (!codemod) { + logger.error(`Unknown codemod "${id}"`); + logger.log( + `\n\nAvailable codemods: ${Object.keys(codemods) + .map((c) => `\n- ${c}`) + .join('')}` + ); + process.exit(1); + } + + await runSnippetCodemod({ + glob: join(SNIPPETS_DIRECTORY, glob), + dryRun, + promptUser: prompt, + ...codemod, + }); + }); + +// Parse and validate arguments +program.parse(process.argv); diff --git a/scripts/utils/constants.ts b/scripts/utils/constants.ts index d783410d3825..53069ee0ffee 100644 --- a/scripts/utils/constants.ts +++ b/scripts/utils/constants.ts @@ -5,6 +5,7 @@ export const BEFORE_DIR_NAME = 'before-storybook'; export const ROOT_DIRECTORY = join(__dirname, '..', '..'); export const CODE_DIRECTORY = join(ROOT_DIRECTORY, 'code'); +export const SNIPPETS_DIRECTORY = join(ROOT_DIRECTORY, 'docs', '_snippets'); export const PACKS_DIRECTORY = join(ROOT_DIRECTORY, 'packs'); export const REPROS_DIRECTORY = join(ROOT_DIRECTORY, 'repros'); export const SANDBOX_DIRECTORY = join(ROOT_DIRECTORY, 'sandbox'); diff --git a/scripts/yarn.lock b/scripts/yarn.lock index abb82c0d7b61..995780c614df 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -135,6 +135,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-string-parser@npm:7.24.8" + checksum: 10c0/6361f72076c17fabf305e252bf6d580106429014b3ab3c1f5c4eb3e6d465536ea6b670cc0e9a637a77a9ad40454d3e41361a2909e70e305116a23d68ce094c08 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-string-parser@npm:7.25.9" @@ -149,7 +156,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.20.5, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.25.8, @babel/parser@npm:^7.26.8": +"@babel/helper-validator-identifier@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-identifier@npm:7.24.7" + checksum: 10c0/87ad608694c9477814093ed5b5c080c2e06d44cb1924ae8320474a74415241223cc2a725eea2640dd783ff1e3390e5f95eede978bc540e870053152e58f1d651 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.20.5, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.25.8, @babel/parser@npm:^7.26.8": version: 7.26.8 resolution: "@babel/parser@npm:7.26.8" dependencies: @@ -160,6 +174,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.25.4": + version: 7.26.5 + resolution: "@babel/parser@npm:7.26.5" + dependencies: + "@babel/types": "npm:^7.26.5" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/2e77dd99ee028ee3c10fa03517ae1169f2432751adf71315e4dc0d90b61639d51760d622f418f6ac665ae4ea65f8485232a112ea0e76f18e5900225d3d19a61e + languageName: node + linkType: hard + "@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.23.2": version: 7.23.2 resolution: "@babel/runtime@npm:7.23.2" @@ -223,7 +248,38 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.17.0, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.4, @babel/types@npm:^7.26.8": +"@babel/types@npm:^7.17.0, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.7": + version: 7.26.3 + resolution: "@babel/types@npm:7.26.3" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10c0/966c5242c5e55c8704bf7a7418e7be2703a0afa4d19a8480999d5a4ef13d095dd60686615fe5983cb7593b4b06ba3a7de8d6ca501c1d78bdd233a10d90be787b + languageName: node + linkType: hard + +"@babel/types@npm:^7.22.5": + version: 7.25.2 + resolution: "@babel/types@npm:7.25.2" + dependencies: + "@babel/helper-string-parser": "npm:^7.24.8" + "@babel/helper-validator-identifier": "npm:^7.24.7" + to-fast-properties: "npm:^2.0.0" + checksum: 10c0/e489435856be239f8cc1120c90a197e4c2865385121908e5edb7223cfdff3768cba18f489adfe0c26955d9e7bbb1fb10625bc2517505908ceb0af848989bd864 + languageName: node + linkType: hard + +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.26.5": + version: 7.26.5 + resolution: "@babel/types@npm:7.26.5" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10c0/0278053b69d7c2b8573aa36dc5242cad95f0d965e1c0ed21ccacac6330092e59ba5949753448f6d6eccf6ad59baaef270295cc05218352e060ea8c68388638c4 + languageName: node + linkType: hard + +"@babel/types@npm:^7.26.8": version: 7.26.8 resolution: "@babel/types@npm:7.26.8" dependencies: @@ -1509,6 +1565,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + "@storybook/scripts@workspace:.": version: 0.0.0-use.local resolution: "@storybook/scripts@workspace:." @@ -1582,6 +1645,7 @@ __metadata: fast-glob: "npm:^3.3.2" github-release-from-changelog: "npm:^2.1.1" glob: "npm:^10.4.5" + globby: "npm:^14.0.1" http-server: "npm:^14.1.1" husky: "npm:^4.3.7" jiti: "npm:^2.4.2" @@ -3661,7 +3725,14 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^5.0.0, chalk@npm:^5.4.1": +"chalk@npm:^5.0.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 10c0/8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09 + languageName: node + linkType: hard + +"chalk@npm:^5.4.1": version: 5.4.1 resolution: "chalk@npm:5.4.1" checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef @@ -4233,15 +4304,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0, debug@npm:^4.4.1": - version: 4.4.1 - resolution: "debug@npm:4.4.1" +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5": + version: 4.3.7 + resolution: "debug@npm:4.3.7" dependencies: ms: "npm:^2.1.3" peerDependenciesMeta: supports-color: optional: true - checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55 + checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b languageName: node linkType: hard @@ -4266,6 +4337,30 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.0": + version: 4.4.0 + resolution: "debug@npm:4.4.0" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/db94f1a182bf886f57b4755f85b3a74c39b5114b9377b7ab375dc2cfa3454f09490cc6c30f829df3fc8042bc8b8995f6567ce5cd96f3bc3688bd24027197d9de + languageName: node + linkType: hard + +"debug@npm:^4.4.1": + version: 4.4.1 + resolution: "debug@npm:4.4.1" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55 + languageName: node + linkType: hard + "decode-named-character-reference@npm:^1.0.0": version: 1.0.2 resolution: "decode-named-character-reference@npm:1.0.2" @@ -5864,6 +5959,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.2": + version: 6.4.3 + resolution: "fdir@npm:6.4.3" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/d13c10120e9625adf21d8d80481586200759928c19405a816b77dd28eaeb80e7c59c5def3e2941508045eb06d34eb47fad865ccc8bf98e6ab988bb0ed160fb6f + languageName: node + linkType: hard + "fdir@npm:^6.4.4, fdir@npm:^6.4.6": version: 6.4.6 resolution: "fdir@npm:6.4.6" @@ -6478,6 +6585,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:^14.0.1": + version: 14.0.2 + resolution: "globby@npm:14.0.2" + dependencies: + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.2" + ignore: "npm:^5.2.4" + path-type: "npm:^5.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/3f771cd683b8794db1e7ebc8b6b888d43496d93a82aad4e9d974620f578581210b6c5a6e75ea29573ed16a1345222fab6e9b877a8d1ed56eeb147e09f69c6f78 + languageName: node + linkType: hard + "globby@npm:^7.1.1": version: 7.1.1 resolution: "globby@npm:7.1.1" @@ -7378,7 +7499,16 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15": +"is-typed-array@npm:^1.1.13": + version: 1.1.13 + resolution: "is-typed-array@npm:1.1.13" + dependencies: + which-typed-array: "npm:^1.1.14" + checksum: 10c0/fa5cb97d4a80e52c2cc8ed3778e39f175a1a2ae4ddf3adae3187d69586a1fd57cfa0b095db31f66aa90331e9e3da79184cea9c6abdcd1abc722dc3c3edd51cca + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15": version: 1.1.15 resolution: "is-typed-array@npm:1.1.15" dependencies: @@ -8686,7 +8816,17 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.4": + version: 4.0.7 + resolution: "micromatch@npm:4.0.7" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/58fa99bc5265edec206e9163a1d2cec5fabc46a5b473c45f4a700adce88c2520456ae35f2b301e4410fb3afb27e9521fb2813f6fc96be0a48a89430e0916a772 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -9296,7 +9436,7 @@ __metadata: languageName: node linkType: hard -"object.values@npm:^1.1.6, object.values@npm:^1.1.7, object.values@npm:^1.2.0": +"object.values@npm:^1.1.6, object.values@npm:^1.2.0": version: 1.2.1 resolution: "object.values@npm:1.2.1" dependencies: @@ -9308,6 +9448,17 @@ __metadata: languageName: node linkType: hard +"object.values@npm:^1.1.7": + version: 1.2.0 + resolution: "object.values@npm:1.2.0" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/15809dc40fd6c5529501324fec5ff08570b7d70fb5ebbe8e2b3901afec35cf2b3dc484d1210c6c642cd3e7e0a5e18dd1d6850115337fef46bdae14ab0cb18ac3 + languageName: node + linkType: hard + "on-exit-leak-free@npm:^0.2.0": version: 0.2.0 resolution: "on-exit-leak-free@npm:0.2.0" @@ -9681,6 +9832,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^5.0.0": + version: 5.0.0 + resolution: "path-type@npm:5.0.0" + checksum: 10c0/e8f4b15111bf483900c75609e5e74e3fcb79f2ddb73e41470028fcd3e4b5162ec65da9907be077ee5012c18801ff7fffb35f9f37a077f3f81d85a0b7d6578efd + languageName: node + linkType: hard + "pathe@npm:^2.0.3": version: 2.0.3 resolution: "pathe@npm:2.0.3" @@ -11219,7 +11377,7 @@ __metadata: languageName: node linkType: hard -"slash@npm:^5.0.0": +"slash@npm:^5.0.0, slash@npm:^5.1.0": version: 5.1.0 resolution: "slash@npm:5.1.0" checksum: 10c0/eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 @@ -11931,7 +12089,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.9": +"tinyglobby@npm:^0.2.14": version: 0.2.14 resolution: "tinyglobby@npm:0.2.14" dependencies: @@ -11941,6 +12099,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.9": + version: 0.2.10 + resolution: "tinyglobby@npm:0.2.10" + dependencies: + fdir: "npm:^6.4.2" + picomatch: "npm:^4.0.2" + checksum: 10c0/ce946135d39b8c0e394e488ad59f4092e8c4ecd675ef1bcd4585c47de1b325e61ec6adfbfbe20c3c2bfa6fd674c5b06de2a2e65c433f752ae170aff11793e5ef + languageName: node + linkType: hard + "tinypool@npm:^1.1.1": version: 1.1.1 resolution: "tinypool@npm:1.1.1" @@ -12279,6 +12447,13 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + "union@npm:~0.5.0": version: 0.5.0 resolution: "union@npm:0.5.0" @@ -12860,6 +13035,19 @@ __metadata: languageName: node linkType: hard +"which-typed-array@npm:^1.1.14": + version: 1.1.15 + resolution: "which-typed-array@npm:1.1.15" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/4465d5348c044032032251be54d8988270e69c6b7154f8fcb2a47ff706fe36f7624b3a24246b8d9089435a8f4ec48c1c1025c5d6b499456b9e5eff4f48212983 + languageName: node + linkType: hard + "which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18": version: 1.1.18 resolution: "which-typed-array@npm:1.1.18" From 5459bc7c778fa782c3a7ac8e263e3af9e80d8942 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Fri, 29 Aug 2025 15:56:43 -0600 Subject: [PATCH 02/12] Update CSF Factories snippets codemod --- scripts/snippets/codemod.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scripts/snippets/codemod.ts b/scripts/snippets/codemod.ts index fbb35c05276b..7467a8fc6853 100644 --- a/scripts/snippets/codemod.ts +++ b/scripts/snippets/codemod.ts @@ -48,7 +48,7 @@ type Codemod = { }; const previousTabTitle = 'CSF 3'; -const newTabTitle = 'CSF Factory ๐Ÿงช'; +const newTabTitle = 'CSF Next ๐Ÿงช'; export async function runSnippetCodemod({ glob, @@ -172,7 +172,7 @@ export async function runSnippetCodemod({ // warn us if there is already a tab title if (snippet !== targetSnippet) { appendedContent += - '\n\n'; + '\n\n'; } const newSnippet = { ...snippet }; @@ -189,17 +189,22 @@ export async function runSnippetCodemod({ if (newSnippet.path.includes('.stories')) { transformedSource = transformedSource - .replace(/\/\/ Replace your-renderer with .*\n/, '') .replace(/\/\/ Replace your-framework with .*\n/, '') + .replace(/\* Replace your-framework with .*\n/, '') .replace( /(import preview from \"#\.storybook\/preview\";)/g, - '// Learn about the # subpath import: https://storybook.js.org/docs/api/csf/csf-factories#subpath-imports\n$1' + `import preview from '../.storybook/preview';` ); } else { - transformedSource = transformedSource.replace( - /Replace your-framework with .*\n/, - 'Replace your-framework with the framework you are using (e.g., react-vite, nextjs, experimental-nextjs-vite)\n' - ); + transformedSource = transformedSource + .replace( + /Replace your-framework with .*\n/, + 'Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)\n' + ) + .replace( + /(import { define(Main|Preview) .*)\n/, + '// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)\n$1\n' + ); } appendedContent += transformedSource; } From 75fbe79ff009b1a6e2883558963f757566f311b0 Mon Sep 17 00:00:00 2001 From: Kyle Gach Date: Fri, 29 Aug 2025 16:50:39 -0600 Subject: [PATCH 03/12] Run docs script for CSF Factories stories codemod - `docs:codemod csf-factory-story --glob="*.md"` --- .../addon-a11y-config-in-meta-and-story.md | 119 ++++++++++- docs/_snippets/addon-a11y-disable.md | 43 +++- .../_snippets/addon-a11y-parameter-example.md | 60 +++++- docs/_snippets/addon-a11y-parameter-remove.md | 48 ++++- .../addon-a11y-parameter-todo-in-meta.md | 36 +++- .../addon-actions-action-function.md | 38 +++- .../addon-backgrounds-define-globals.md | 48 ++++- docs/_snippets/addon-backgrounds-disabled.md | 103 ++++++--- docs/_snippets/addon-backgrounds-grid.md | 49 ++++- .../addon-backgrounds-options-in-meta.md | 46 +++- .../addon-viewport-configuration-in-meta.md | 42 +++- .../addon-viewport-define-globals.md | 48 ++++- docs/_snippets/after-each-in-meta.md | 46 +++- .../api-doc-block-argtypes-parameter.md | 36 +++- .../api-doc-block-canvas-parameter.md | 41 +++- .../api-doc-block-controls-parameter.md | 36 +++- .../api-doc-block-description-example.md | 72 ++++++- .../api-doc-block-source-parameter.md | 41 +++- .../api-doc-block-story-parameter.md | 41 +++- docs/_snippets/arg-types-control.md | 46 +++- docs/_snippets/arg-types-default-value.md | 47 ++++- docs/_snippets/arg-types-description.md | 36 +++- docs/_snippets/arg-types-if.md | 80 ++++++- docs/_snippets/arg-types-in-meta.md | 40 +++- docs/_snippets/arg-types-in-story.md | 45 +++- docs/_snippets/arg-types-mapping.md | 46 +++- docs/_snippets/arg-types-name.md | 36 +++- docs/_snippets/arg-types-options.md | 36 +++- docs/_snippets/arg-types-table.md | 42 +++- docs/_snippets/arg-types-type.md | 32 ++- docs/_snippets/automocked-modules-in-story.md | 67 +++++- .../before-each-in-meta-mock-date.md | 60 +++++- .../button-story-argtypes-with-categories.md | 90 +++++++- ...utton-story-argtypes-with-subcategories.md | 98 ++++++++- ...ory-baseline-with-satisfies-story-level.md | 19 +- .../button-story-baseline-with-satisfies.md | 12 +- docs/_snippets/button-story-baseline.md | 19 +- .../button-story-click-handler-args.md | 47 ++++- ...utton-story-click-handler-simplificated.md | 33 ++- docs/_snippets/button-story-click-handler.md | 39 +++- .../button-story-component-args-primary.md | 43 +++- .../button-story-component-decorator.md | 44 +++- .../button-story-controls-primary-variant.md | 37 +++- .../button-story-controls-radio-group.md | 38 +++- docs/_snippets/button-story-decorator.md | 51 ++++- ...ton-story-default-export-with-component.md | 25 ++- docs/_snippets/button-story-default-export.md | 36 +++- docs/_snippets/button-story-disable-addon.md | 43 +++- docs/_snippets/button-story-grouped.md | 36 +++- docs/_snippets/button-story-hoisted.md | 42 +++- .../button-story-hypothetical-example.md | 43 +++- .../button-story-matching-argtypes.md | 27 ++- .../button-story-onclick-action-spy.md | 34 ++- .../button-story-primary-composition.md | 53 ++++- .../button-story-primary-long-name.md | 53 ++++- docs/_snippets/button-story-rename-story.md | 43 +++- docs/_snippets/button-story-using-args.md | 67 +++++- .../button-story-with-addon-example.md | 64 +++++- docs/_snippets/button-story-with-args.md | 39 +++- docs/_snippets/button-story-with-emojis.md | 59 +++++- docs/_snippets/button-story.md | 87 +++++++- docs/_snippets/checkbox-story-csf.md | 37 +++- docs/_snippets/checkbox-story-grouped.md | 36 +++- .../_snippets/code-panel-in-meta-and-story.md | 78 ++++++- ...y-conditional-controls-mutual-exclusion.md | 48 ++++- ...onent-story-conditional-controls-toggle.md | 42 +++- .../component-story-custom-args-complex.md | 84 +++++++- .../component-story-custom-args-mapping.md | 66 +++++- .../component-story-disable-controls-alt.md | 38 +++- .../component-story-disable-controls-regex.md | 73 ++++++- .../component-story-disable-controls.md | 42 +++- .../component-story-figma-integration.md | 44 +++- docs/_snippets/component-story-highlight.md | 55 ++++- .../component-story-sort-controls.md | 27 ++- .../component-story-static-asset-cdn.md | 39 +++- ...omponent-story-static-asset-with-import.md | 47 ++++- ...onent-story-static-asset-without-import.md | 37 +++- ...onent-story-with-custom-render-function.md | 8 +- ...gure-mock-provider-with-story-parameter.md | 45 +++- docs/_snippets/foo-bar-baz-story.md | 40 +++- .../gizmo-story-controls-customization.md | 120 ++++++++++- docs/_snippets/highlight-custom-style.md | 89 +++++++- docs/_snippets/highlight-menu.md | 85 +++++++- docs/_snippets/highlight-remove.md | 59 +++++- docs/_snippets/highlight-reset.md | 57 ++++- docs/_snippets/highlight-scroll-into-view.md | 51 ++++- docs/_snippets/histogram-story.md | 43 +++- docs/_snippets/interaction-test-complex.md | 90 +++++++- .../_snippets/interaction-test-fn-mock-spy.md | 59 +++++- docs/_snippets/interaction-test-simple.md | 46 +++- docs/_snippets/list-story-expanded.md | 77 ++++++- docs/_snippets/list-story-reuse-data.md | 57 ++++- docs/_snippets/list-story-starter.md | 31 ++- docs/_snippets/list-story-template.md | 104 ++++++++- docs/_snippets/list-story-unchecked.md | 63 +++++- .../list-story-with-subcomponents.md | 56 ++++- .../list-story-with-unchecked-children.md | 56 ++++- docs/_snippets/loader-story.md | 50 ++++- .../login-form-with-play-function.md | 71 ++++++- docs/_snippets/mount-advanced.md | 67 +++++- docs/_snippets/mount-basic.md | 36 +++- .../msw-addon-configure-handlers-graphql.md | 199 +++++++++++++++++- .../msw-addon-configure-handlers-http.md | 133 +++++++++++- docs/_snippets/my-component-disable-toc.md | 42 +++- docs/_snippets/my-component-env-var-config.md | 37 +++- docs/_snippets/my-component-exclude-tags.md | 40 +++- docs/_snippets/my-component-include-tags.md | 40 +++- docs/_snippets/my-component-skip-tags.md | 40 +++- .../my-component-story-basic-and-props.md | 39 +++- .../my-component-story-mandatory-export.md | 23 +- .../my-component-story-use-globaltype.md | 69 +++++- .../my-component-story-with-nonstory.md | 67 +++++- .../my-component-story-with-storyname.md | 35 ++- .../my-component-vite-env-variables.md | 39 +++- .../my-component-with-env-variables.md | 37 +++- .../_snippets/nextjs-app-directory-in-meta.md | 36 +++- docs/_snippets/nextjs-cache-mock.md | 53 ++++- docs/_snippets/nextjs-headers-mock.md | 65 +++++- docs/_snippets/nextjs-navigation-mock.md | 82 +++++++- .../nextjs-navigation-override-in-story.md | 64 +++++- ...egments-for-use-params-override-in-meta.md | 48 ++++- ...js-navigation-segments-override-in-meta.md | 42 +++- docs/_snippets/nextjs-router-mock.md | 55 ++++- .../nextjs-router-override-in-story.md | 55 ++++- docs/_snippets/other-foo-bar-story.md | 48 ++++- .../_snippets/page-story-args-within-story.md | 70 +++++- docs/_snippets/page-story-slots.md | 50 ++++- docs/_snippets/parameters-in-meta.md | 38 +++- docs/_snippets/parameters-in-story.md | 51 ++++- docs/_snippets/play-function-composition.md | 75 ++++++- docs/_snippets/play-function-with-canvas.md | 41 +++- docs/_snippets/play-function-with-screen.md | 51 ++++- docs/_snippets/play-function.md | 79 ++++++- docs/_snippets/render-custom-in-meta.md | 66 +++++- docs/_snippets/render-custom-in-story.md | 53 ++++- docs/_snippets/rsc-parameter-in-meta.md | 32 ++- .../storybook-canvas-doc-block-story.md | 111 +++++++++- .../storybook-component-layout-param.md | 34 ++- .../storybook-csf-3-auto-title-redundant.md | 43 +++- ...storybook-fix-imports-autodocs-monorepo.md | 44 +++- .../storybook-interactions-play-function.md | 79 ++++++- .../_snippets/storybook-story-layout-param.md | 37 +++- docs/_snippets/storybook-test-fn-mock-spy.md | 123 ++++++++--- .../storybook-test-mock-return-value.md | 46 +++- docs/_snippets/tags-autodocs-in-meta.md | 30 ++- .../tags-autodocs-remove-component.md | 30 ++- docs/_snippets/tags-autodocs-remove-story.md | 40 +++- docs/_snippets/tags-combo-example.md | 71 ++++++- docs/_snippets/tags-docs-only-in-meta.md | 38 +++- docs/_snippets/tags-in-meta-and-story.md | 62 +++++- docs/_snippets/tags-remove-in-story.md | 40 +++- docs/_snippets/typed-csf-file.md | 20 +- .../your-component-with-decorator.md | 44 +++- docs/_snippets/your-component.md | 38 +++- 154 files changed, 7716 insertions(+), 388 deletions(-) diff --git a/docs/_snippets/addon-a11y-config-in-meta-and-story.md b/docs/_snippets/addon-a11y-config-in-meta-and-story.md index b38c96394c30..167a010153ab 100644 --- a/docs/_snippets/addon-a11y-config-in-meta-and-story.md +++ b/docs/_snippets/addon-a11y-config-in-meta-and-story.md @@ -57,7 +57,7 @@ export const ExampleStory: Story = { }; ``` -```ts filename="Button.stories.ts" renderer="common" language="ts" +```ts filename="Button.stories.ts" renderer="common" language="ts" tabTitle="CSF 3" // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { Meta, StoryObj } from '@storybook/your-framework'; @@ -117,7 +117,7 @@ export const ExampleStory: Story = { }; ``` -```js filename="Button.stories.js" renderer="common" language="js" +```js filename="Button.stories.js" renderer="common" language="js" tabTitle="CSF 3" import { Button } from './Button'; export default { @@ -511,3 +511,118 @@ export const ExampleStory = { }, }; ``` + +```ts filename="Button.stories.ts" renderer="react" language="ts" tabTitle="CSF Next ๐Ÿงช" +import preview from '../.storybook/preview'; + +import { Button } from './Button'; + +const meta = preview.meta({ + component: Button, + parameters: { + a11y: { + /* + * Axe's context parameter + * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#context-parameter + * to learn more. + */ + context: {}, + /* + * Axe's configuration + * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure + * to learn more about the available properties. + */ + config: {}, + /* + * Axe's options parameter + * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter + * to learn more about the available options. + */ + options: {}, + /* + * Configure test behavior + * See: https://storybook.js.org/docs/next/writing-tests/accessibility-testing#test-behavior + */ + test: 'error', + }, + }, + globals: { + a11y: { + // Optional flag to prevent the automatic check + manual: true, + }, + }, +}); + +export const ExampleStory = meta.story({ + parameters: { + a11y: { + // ...same config available as above + }, + }, + globals: { + a11y: { + // ...same config available as above + }, + }, +}); +``` + + + +```js filename="Button.stories.js" renderer="react" language="js" tabTitle="CSF Next ๐Ÿงช" +import preview from '../.storybook/preview'; +import { Button } from './Button'; + +const meta = preview.meta({ + component: Button, + + parameters: { + a11y: { + /* + * Axe's context parameter + * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#context-parameter + * to learn more. + */ + context: {}, + /* + * Axe's configuration + * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure + * to learn more about the available properties. + */ + config: {}, + /* + * Axe's options parameter + * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter + * to learn more about the available options. + */ + options: {}, + /* + * Configure test behavior + * See: https://storybook.js.org/docs/next/writing-tests/accessibility-testing#test-behavior + */ + test: 'error', + }, + }, + + globals: { + a11y: { + // Optional flag to prevent the automatic check + manual: true, + }, + } +}); + +export const ExampleStory = meta.story({ + parameters: { + a11y: { + // ...same config available as above + }, + }, + globals: { + a11y: { + // ...same config available as above + }, + }, +}); +``` diff --git a/docs/_snippets/addon-a11y-disable.md b/docs/_snippets/addon-a11y-disable.md index 588c16b2637e..51db64ca48e1 100644 --- a/docs/_snippets/addon-a11y-disable.md +++ b/docs/_snippets/addon-a11y-disable.md @@ -20,7 +20,7 @@ export const NonA11yStory: Story = { }; ``` -```js filename="MyComponent.stories.js|jsx" renderer="react" language="js" +```js filename="MyComponent.stories.js|jsx" renderer="react" language="js" tabTitle="CSF 3" import { MyComponent } from './MyComponent'; export default { @@ -37,7 +37,7 @@ export const NonA11yStory = { }; ``` -```ts filename="MyComponent.stories.ts|tsx" renderer="react" language="ts" +```ts filename="MyComponent.stories.ts|tsx" renderer="react" language="ts" tabTitle="CSF 3" // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc. import type { Meta, StoryObj } from '@storybook/your-framework'; @@ -217,3 +217,42 @@ export const ExampleStory: Story = { }, }; ``` + +```ts filename="MyComponent.stories.ts|tsx" renderer="react" language="ts" tabTitle="CSF Next ๐Ÿงช" +import preview from '../.storybook/preview'; + +import { MyComponent } from './MyComponent'; + +const meta = preview.meta({ + component: MyComponent, +}); + +export const NonA11yStory = meta.story({ + globals: { + a11y: { + // This option disables all automatic a11y checks on this story + manual: true, + }, + }, +}); +``` + + + +```js filename="MyComponent.stories.js|jsx" renderer="react" language="js" tabTitle="CSF Next ๐Ÿงช" +import preview from '../.storybook/preview'; +import { MyComponent } from './MyComponent'; + +const meta = preview.meta({ + component: MyComponent +}); + +export const NonA11yStory = meta.story({ + globals: { + a11y: { + // This option disables all automatic a11y checks on this story + manual: true, + }, + }, +}); +``` diff --git a/docs/_snippets/addon-a11y-parameter-example.md b/docs/_snippets/addon-a11y-parameter-example.md index 42df11421336..b422e60dd778 100644 --- a/docs/_snippets/addon-a11y-parameter-example.md +++ b/docs/_snippets/addon-a11y-parameter-example.md @@ -28,7 +28,7 @@ export const NoA11yFail: Story = { }; ``` -```ts filename="Button.stories.ts" renderer="common" language="ts" +```ts filename="Button.stories.ts" renderer="common" language="ts" tabTitle="CSF 3" // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { Meta, StoryObj } from '@storybook/your-framework'; @@ -59,7 +59,7 @@ export const NoA11yFail: Story = { }; ``` -```js filename="Button.stories.js" renderer="common" language="js" +```js filename="Button.stories.js" renderer="common" language="js" tabTitle="CSF 3" import { Button } from './Button'; export default { @@ -252,3 +252,59 @@ export const NoA11yFail = { }, }; ``` + +```ts filename="Button.stories.ts" renderer="react" language="ts" tabTitle="CSF Next ๐Ÿงช" +import preview from '../.storybook/preview'; + +import { Button } from './Button'; + +const meta = preview.meta({ + component: Button, + parameters: { + // ๐Ÿ‘‡ Applies to all stories in this file + a11y: { test: 'error' }, + }, +}); + +// ๐Ÿ‘‡ This story will use the 'error' value and fail on accessibility violations +export const Primary = meta.story({ + args: { primary: true }, +}); + +// ๐Ÿ‘‡ This story will not fail on accessibility violations +// (but will still run the tests and show warnings) +export const NoA11yFail = meta.story({ + parameters: { + a11y: { test: 'todo' }, + }, +}); +``` + + + +```js filename="Button.stories.js" renderer="react" language="js" tabTitle="CSF Next ๐Ÿงช" +import preview from '../.storybook/preview'; +import { Button } from './Button'; + +const meta = preview.meta({ + component: Button, + + parameters: { + // ๐Ÿ‘‡ Applies to all stories in this file + a11y: { test: 'error' }, + } +}); + +// ๐Ÿ‘‡ This story will use the 'error' value and fail on accessibility violations +export const Primary = meta.story({ + args: { primary: true }, +}); + +// ๐Ÿ‘‡ This story will not fail on accessibility violations +// (but will still run the tests and show warnings) +export const NoA11yFail = meta.story({ + parameters: { + a11y: { test: 'todo' }, + }, +}); +``` diff --git a/docs/_snippets/addon-a11y-parameter-remove.md b/docs/_snippets/addon-a11y-parameter-remove.md index 2b376f75a269..b685f360669c 100644 --- a/docs/_snippets/addon-a11y-parameter-remove.md +++ b/docs/_snippets/addon-a11y-parameter-remove.md @@ -13,7 +13,7 @@ const meta: Meta