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 @@
+
+ {{ finalText }}
+
+
+
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 }
);