diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b7803af4318..c0d16e74c37e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -477,7 +477,7 @@ jobs: executor: class: medium+ name: sb_node_14_browsers - parallelism: 7 + parallelism: 8 steps: - git-shallow-clone/checkout_advanced: clone_options: '--depth 1 --verbose' @@ -497,7 +497,7 @@ jobs: executor: class: medium+ name: sb_node_14_browsers - parallelism: 7 + parallelism: 8 steps: - git-shallow-clone/checkout_advanced: clone_options: '--depth 1 --verbose' @@ -513,7 +513,7 @@ jobs: executor: class: medium+ name: sb_node_14_browsers - parallelism: 7 + parallelism: 8 steps: - git-shallow-clone/checkout_advanced: clone_options: '--depth 1 --verbose' @@ -533,7 +533,7 @@ jobs: executor: class: medium+ name: sb_node_14_browsers - parallelism: 7 + parallelism: 8 steps: - git-shallow-clone/checkout_advanced: clone_options: '--depth 1 --verbose' @@ -549,7 +549,7 @@ jobs: executor: class: medium+ name: sb_node_14_browsers - parallelism: 7 + parallelism: 8 steps: - git-shallow-clone/checkout_advanced: clone_options: '--depth 1 --verbose' @@ -565,7 +565,7 @@ jobs: executor: class: medium+ name: sb_playwright - parallelism: 7 + parallelism: 8 steps: - git-shallow-clone/checkout_advanced: clone_options: '--depth 1 --verbose' diff --git a/code/addons/a11y/template/stories/parameters.stories.ts b/code/addons/a11y/template/stories/parameters.stories.ts index 159a6f07fd25..700c3e4500a8 100644 --- a/code/addons/a11y/template/stories/parameters.stories.ts +++ b/code/addons/a11y/template/stories/parameters.stories.ts @@ -3,7 +3,7 @@ import globalThis from 'global'; export default { component: globalThis.Components.Html, args: { - contents: '', + content: '', }, parameters: { chromatic: { disable: true }, @@ -12,7 +12,7 @@ export default { export const Options = { args: { - contents: + content: '', }, parameters: { diff --git a/code/addons/a11y/template/stories/tests.stories.ts b/code/addons/a11y/template/stories/tests.stories.ts index 6c729ad66e05..03346296518a 100644 --- a/code/addons/a11y/template/stories/tests.stories.ts +++ b/code/addons/a11y/template/stories/tests.stories.ts @@ -3,7 +3,7 @@ import globalThis from 'global'; export default { component: globalThis.Components.Html, args: { - contents: '', + content: '', }, parameters: { chromatic: { disable: true }, @@ -12,7 +12,7 @@ export default { export const Violations = { args: { - contents: ` + content: `

empty heading

@@ -39,7 +39,7 @@ export const Violations = { export const Passes = { args: { - contents: ` + content: `

heading

heading 1

diff --git a/code/addons/actions/src/preview/action.ts b/code/addons/actions/src/preview/action.ts index b0c874e41cbd..89505ceead39 100644 --- a/code/addons/actions/src/preview/action.ts +++ b/code/addons/actions/src/preview/action.ts @@ -64,6 +64,7 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti }; channel.emit(EVENT_ID, actionDisplayToEmit); }; + handler.isAction = true; return handler; } diff --git a/code/addons/interactions/src/preset/preview.ts b/code/addons/interactions/src/preset/preview.ts index 6995d3037159..6525e672a4d7 100644 --- a/code/addons/interactions/src/preset/preview.ts +++ b/code/addons/interactions/src/preset/preview.ts @@ -37,7 +37,7 @@ const addSpies = (id: string, val: any, key?: string): any => { if (Array.isArray(val)) { return val.map((item, index) => addSpies(id, item, `${key}[${index}]`)); } - if (typeof val === 'function' && val.name === 'actionHandler') { + if (typeof val === 'function' && val.isAction) { Object.defineProperty(val, 'name', { value: key, writable: false }); Object.defineProperty(val, '__storyId__', { value: id, writable: false }); const spy = action(val); diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts index cf6c444b54a0..19277c7c69fd 100644 --- a/code/e2e-tests/addon-docs.spec.ts +++ b/code/e2e-tests/addon-docs.spec.ts @@ -14,8 +14,9 @@ test.describe('addon-docs', () => { }); test('should provide source snippet', async ({ page }) => { + // templateName is e.g. 'Vue-CLI (Default JS)' test.skip( - /^vue3/.test(templateName), + /^(vue3|vue-cli)/i.test(templateName), `Skipping ${templateName}, which does not support dynamic source snippets` ); diff --git a/code/frameworks/vue-webpack5/src/preset.ts b/code/frameworks/vue-webpack5/src/preset.ts index 07ee35af0511..c813c89cff4b 100644 --- a/code/frameworks/vue-webpack5/src/preset.ts +++ b/code/frameworks/vue-webpack5/src/preset.ts @@ -21,11 +21,7 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti }; }; -export const typescript = async ( - config: StorybookConfig['typescript'] -): Promise => { - return { - ...config, - skipBabel: true, - }; -}; +export const typescript: PresetProperty<'typescript', StorybookConfig> = async (config) => ({ + ...config, + skipBabel: true, +}); diff --git a/code/frameworks/vue3-webpack5/src/preset.ts b/code/frameworks/vue3-webpack5/src/preset.ts index 7babf251a76d..01edab6bf545 100644 --- a/code/frameworks/vue3-webpack5/src/preset.ts +++ b/code/frameworks/vue3-webpack5/src/preset.ts @@ -20,3 +20,8 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti }, }; }; + +export const typescript: PresetProperty<'typescript', StorybookConfig> = async (config) => ({ + ...config, + skipBabel: true, +}); diff --git a/code/lib/cli/src/generators/VUE3/index.ts b/code/lib/cli/src/generators/VUE3/index.ts index 8a6c44f716ec..3bc35c4fe375 100644 --- a/code/lib/cli/src/generators/VUE3/index.ts +++ b/code/lib/cli/src/generators/VUE3/index.ts @@ -3,7 +3,7 @@ import { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { await baseGenerator(packageManager, npmOptions, options, 'vue3', { - extraPackages: ['vue-loader@^16.0.0'], + extraPackages: ['vue-loader@^17.0.0', '@vue/compiler-sfc@^3.2.0'], }); }; diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 910bbf64a720..9c486db6abd4 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -15,7 +15,7 @@ const defaultOptions: FrameworkOptions = { staticDir: undefined, addScripts: true, addComponents: true, - addBabel: true, + addBabel: false, addESLint: false, extraMain: undefined, framework: undefined, diff --git a/code/lib/cli/src/repro-templates.ts b/code/lib/cli/src/repro-templates.ts index 66b9a5835a6e..8910264bc56c 100644 --- a/code/lib/cli/src/repro-templates.ts +++ b/code/lib/cli/src/repro-templates.ts @@ -94,11 +94,39 @@ const svelteViteTemplates = { // } }; +const vueCliTemplates = { + 'vue-cli/default-js': { + name: 'Vue-CLI (Default JS)', + script: 'npx -p @vue/cli vue create . --default --packageManager=yarn --force --merge', + cadence: ['ci', 'daily', 'weekly'], + expected: { + framework: '@storybook/vue3-webpack5', + renderer: '@storybook/vue3', + builder: '@storybook/builder-webpack5', + }, + }, + // + // FIXME: https://github.com/storybookjs/storybook/issues/19204 + // + // 'vue-cli/vue2-default-js': { + // name: 'Vue-CLI (Vue2 JS)', + // script: + // 'npx -p @vue/cli vue create . --default --packageManager=yarn --force --merge --preset=Default\\ (Vue\\ 2)', + // cadence: ['ci', 'daily', 'weekly'], + // expected: { + // framework: '@storybook/vue-webpack5', + // renderer: '@storybook/vue', + // builder: '@storybook/builder-webpack5', + // }, + // }, +}; + export default { ...craTemplates, ...reactViteTemplates, ...vue3ViteTemplates, ...svelteViteTemplates, + ...vueCliTemplates, // FIXME: missing documentation.json // 'angular/latest': { // name: 'Angular (latest)', diff --git a/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix b/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix index 6dcf0e0f0cba..0b6a8cb32416 100644 --- a/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix +++ b/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix @@ -40,7 +40,7 @@ Object { "include": Array [ "ROOT", ], - "test": "/\\\\.(mjs|tsx?|jsx?)$/", + "test": "/\\\\.(mjs|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", diff --git a/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix b/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix index 4ed666b00a7a..48c4096f1e3e 100644 --- a/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix +++ b/code/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix @@ -39,7 +39,7 @@ Object { "include": Array [ "ROOT", ], - "test": "/\\\\.(mjs|tsx?|jsx?)$/", + "test": "/\\\\.(mjs|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", diff --git a/code/lib/store/template/stories/rendering.stories.ts b/code/lib/store/template/stories/rendering.stories.ts index f7388fe59d61..455629a231b6 100644 --- a/code/lib/store/template/stories/rendering.stories.ts +++ b/code/lib/store/template/stories/rendering.stories.ts @@ -25,11 +25,15 @@ export const ForceReRender = { export const ChangeArgs = { play: async ({ canvasElement, id }: PlayFunctionContext) => { - // const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__; + const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__; const button = await within(canvasElement).findByRole('button'); await button.focus(); await expect(button).toHaveFocus(); + // Vue3: https://github.com/storybookjs/storybook/issues/13913 + // Svelte: https://github.com/storybookjs/storybook/issues/19205 + if (['vue3', 'svelte'].includes(globalThis.storybookRenderer)) return; + // When we change the args to the button, it should not rerender await channel.emit('updateStoryArgs', { storyId: id, updatedArgs: { children: 'New Text' } }); await within(canvasElement).findByText(/New Text/); diff --git a/code/lib/store/template/stories/title.stories.ts b/code/lib/store/template/stories/title.stories.ts index f3f98d05420f..2b185006dbd1 100644 --- a/code/lib/store/template/stories/title.stories.ts +++ b/code/lib/store/template/stories/title.stories.ts @@ -4,7 +4,7 @@ import { PlayFunctionContext } from '@storybook/csf'; export default { component: globalThis.Components.Pre, - title: 'manual title', + title: 'lib/store/manual title', args: { text: 'No content' }, }; diff --git a/code/renderers/react/template/components/index.js b/code/renderers/react/template/components/index.js index 2f9e0f79a14d..dd5fc33a43bc 100644 --- a/code/renderers/react/template/components/index.js +++ b/code/renderers/react/template/components/index.js @@ -6,3 +6,4 @@ import { Form } from './Form.jsx'; import { Html } from './Html.jsx'; globalThis.Components = { Button, Pre, Form, Html }; +globalThis.storybookRenderer = 'react'; diff --git a/code/renderers/svelte/template/components/index.js b/code/renderers/svelte/template/components/index.js index 1dd45137a0d7..e01d07856369 100644 --- a/code/renderers/svelte/template/components/index.js +++ b/code/renderers/svelte/template/components/index.js @@ -6,3 +6,4 @@ import Form from './Form.svelte'; import Html from './Html.svelte'; globalThis.Components = { Button, Pre, Form, Html }; +globalThis.storybookRenderer = 'svelte'; diff --git a/code/renderers/vue/template/components/Button.vue b/code/renderers/vue/template/components/Button.vue new file mode 100644 index 000000000000..aab630b064d3 --- /dev/null +++ b/code/renderers/vue/template/components/Button.vue @@ -0,0 +1,54 @@ + + + diff --git a/code/renderers/vue/template/components/Form.vue b/code/renderers/vue/template/components/Form.vue new file mode 100644 index 000000000000..fc46f159e337 --- /dev/null +++ b/code/renderers/vue/template/components/Form.vue @@ -0,0 +1,44 @@ + + + diff --git a/code/renderers/vue/template/components/Html.vue b/code/renderers/vue/template/components/Html.vue new file mode 100644 index 000000000000..4547ffb477d8 --- /dev/null +++ b/code/renderers/vue/template/components/Html.vue @@ -0,0 +1,17 @@ + + + diff --git a/code/renderers/vue/template/components/Pre.vue b/code/renderers/vue/template/components/Pre.vue new file mode 100644 index 000000000000..eda0553dce3d --- /dev/null +++ b/code/renderers/vue/template/components/Pre.vue @@ -0,0 +1,29 @@ + + + diff --git a/code/renderers/vue/template/components/button.css b/code/renderers/vue/template/components/button.css new file mode 100644 index 000000000000..dc91dc76370b --- /dev/null +++ b/code/renderers/vue/template/components/button.css @@ -0,0 +1,30 @@ +.storybook-button { + font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-weight: 700; + border: 0; + border-radius: 3em; + cursor: pointer; + display: inline-block; + line-height: 1; +} +.storybook-button--primary { + color: white; + background-color: #1ea7fd; +} +.storybook-button--secondary { + color: #333; + background-color: transparent; + box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; +} +.storybook-button--small { + font-size: 12px; + padding: 10px 16px; +} +.storybook-button--medium { + font-size: 14px; + padding: 11px 20px; +} +.storybook-button--large { + font-size: 16px; + padding: 12px 24px; +} diff --git a/code/renderers/vue/template/components/index.js b/code/renderers/vue/template/components/index.js new file mode 100644 index 000000000000..5bb98ddcd197 --- /dev/null +++ b/code/renderers/vue/template/components/index.js @@ -0,0 +1,9 @@ +import globalThis from 'global'; + +import Button from './Button.vue'; +import Pre from './Pre.vue'; +import Form from './Form.vue'; +import Html from './Html.vue'; + +globalThis.Components = { Button, Pre, Form, Html }; +globalThis.storybookRenderer = 'vue'; diff --git a/code/renderers/vue3/template/components/index.js b/code/renderers/vue3/template/components/index.js index 1aef69a6655a..af1301e7e2ae 100644 --- a/code/renderers/vue3/template/components/index.js +++ b/code/renderers/vue3/template/components/index.js @@ -6,3 +6,4 @@ import Form from './Form.vue'; import Html from './Html.vue'; globalThis.Components = { Button, Pre, Form, Html }; +globalThis.storybookRenderer = 'vue3'; diff --git a/scripts/sandbox.ts b/scripts/sandbox.ts index cf260fd784ce..07e85b15cd5c 100644 --- a/scripts/sandbox.ts +++ b/scripts/sandbox.ts @@ -13,7 +13,6 @@ import { import prompts from 'prompts'; import type { AbortController } from 'node-abort-controller'; import command from 'execa'; -import dedent from 'ts-dedent'; import { createOptions, getOptionsOrPrompt, OptionValues } from './utils/options'; import { executeCLIStep } from './utils/cli-step'; @@ -222,6 +221,7 @@ function addEsbuildLoaderToStories(mainConfig: ConfigFile) { module: { ...config.modules, rules: [ + // Ensure esbuild-loader applies to all files in ./template-stories { test: [/\\/template-stories\\//], loader: '${loaderPath}', @@ -230,7 +230,11 @@ function addEsbuildLoaderToStories(mainConfig: ConfigFile) { target: 'es2015', }, }, - ...config.module.rules, + // Ensure no other loaders from the framework apply + ...config.module.rules.map(rule => ({ + ...rule, + exclude: [/\\/template-stories\\//].concat(rule.exclude || []), + })), ], }, })`; diff --git a/scripts/task.ts b/scripts/task.ts index 7805f9008027..857e9a810166 100644 --- a/scripts/task.ts +++ b/scripts/task.ts @@ -1,6 +1,6 @@ /* eslint-disable no-await-in-loop, no-restricted-syntax */ import { getJunitXml } from 'junit-xml'; -import { outputFile } from 'fs-extra'; +import { outputFile, existsSync, readFile } from 'fs-extra'; import { join, resolve } from 'path'; import { createOptions, getOptionsOrPrompt } from './utils/options'; @@ -158,6 +158,13 @@ async function runTask( if (junit && !task.junit) await writeJunitXml(taskKey, templateKey, start, err); throw err; + } finally { + const { junitFilename } = details; + if (existsSync(junitFilename)) { + const junitXml = await (await readFile(junitFilename)).toString(); + const prefixedXml = junitXml.replace(/classname="(.*)"/g, `classname="${templateKey} $1"`); + await outputFile(junitFilename, prefixedXml); + } } } diff --git a/scripts/tasks/chromatic.ts b/scripts/tasks/chromatic.ts index c8818826ca08..91dfa8156da1 100644 --- a/scripts/tasks/chromatic.ts +++ b/scripts/tasks/chromatic.ts @@ -10,12 +10,13 @@ export const chromatic: Task = { async run(templateKey, { sandboxDir, builtSandboxDir, junitFilename }) { const tokenEnvVarName = `CHROMATIC_TOKEN_${templateKey.toUpperCase().replace(/\/|-/g, '_')}`; const token = process.env[tokenEnvVarName]; - return exec( + + await exec( `npx chromatic \ - --exit-zero-on-changes \ - --storybook-build-dir=${builtSandboxDir} \ - --junit-report=${junitFilename} \ - --projectToken=${token}`, + --exit-zero-on-changes \ + --storybook-build-dir=${builtSandboxDir} \ + --junit-report=${junitFilename} \ + --projectToken=${token}`, { cwd: sandboxDir }, { debug: true } );