diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts
index 9516671597cb..b824eadabc2b 100644
--- a/code/addons/docs/src/preset.ts
+++ b/code/addons/docs/src/preset.ts
@@ -17,11 +17,20 @@ type BabelParams = {
};
function createBabelOptions({ babelOptions, mdxBabelOptions, configureJSX }: BabelParams) {
const babelPlugins = mdxBabelOptions?.plugins || babelOptions?.plugins || [];
+
+ const filteredBabelPlugins = babelPlugins.filter((p: any) => {
+ const name = Array.isArray(p) ? p[0] : p;
+ if (typeof name === 'string') {
+ return !name.includes('plugin-transform-react-jsx');
+ }
+ return true;
+ });
+
const jsxPlugin = [
require.resolve('@babel/plugin-transform-react-jsx'),
{ pragma: 'React.createElement', pragmaFrag: 'React.Fragment' },
];
- const plugins = configureJSX ? [...babelPlugins, jsxPlugin] : babelPlugins;
+ const plugins = configureJSX ? [...filteredBabelPlugins, jsxPlugin] : babelPlugins;
return {
// don't use the root babelrc by default (users can override this in mdxBabelOptions)
babelrc: false,
diff --git a/code/lib/cli/rendererAssets/preact/Button.jsx b/code/lib/cli/rendererAssets/preact/Button.jsx
index 83ee57533ddc..eefda2bffe3f 100644
--- a/code/lib/cli/rendererAssets/preact/Button.jsx
+++ b/code/lib/cli/rendererAssets/preact/Button.jsx
@@ -1,5 +1,3 @@
-/** @jsx h */
-import { h } from 'preact';
import PropTypes from 'prop-types';
import './button.css';
diff --git a/code/lib/cli/rendererAssets/preact/Button.stories.jsx b/code/lib/cli/rendererAssets/preact/Button.stories.jsx
index f047a64a4508..080a4bfc3d61 100644
--- a/code/lib/cli/rendererAssets/preact/Button.stories.jsx
+++ b/code/lib/cli/rendererAssets/preact/Button.stories.jsx
@@ -1,5 +1,3 @@
-/** @jsx h */
-import { h } from 'preact';
import { Button } from './Button';
// More on default export: https://storybook.js.org/docs/preact/writing-stories/introduction#default-export
diff --git a/code/lib/cli/rendererAssets/preact/Header.jsx b/code/lib/cli/rendererAssets/preact/Header.jsx
index 06580039741c..9a81e7788225 100644
--- a/code/lib/cli/rendererAssets/preact/Header.jsx
+++ b/code/lib/cli/rendererAssets/preact/Header.jsx
@@ -1,5 +1,3 @@
-/** @jsx h */
-import { h, Fragment } from 'preact';
import PropTypes from 'prop-types';
import { Button } from './Button';
@@ -29,17 +27,17 @@ export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
{user ? (
-
+ <>
Welcome, {user.name}!
-
+ >
) : (
-
+ <>
-
+ >
)}
diff --git a/code/lib/cli/rendererAssets/preact/Header.stories.jsx b/code/lib/cli/rendererAssets/preact/Header.stories.jsx
index c4bb91225001..19f58b372ed6 100644
--- a/code/lib/cli/rendererAssets/preact/Header.stories.jsx
+++ b/code/lib/cli/rendererAssets/preact/Header.stories.jsx
@@ -1,5 +1,3 @@
-/** @jsx h */
-import { h } from 'preact';
import { Header } from './Header';
export default {
diff --git a/code/lib/cli/rendererAssets/preact/Page.jsx b/code/lib/cli/rendererAssets/preact/Page.jsx
index 4b04da0af2b3..a88603626679 100644
--- a/code/lib/cli/rendererAssets/preact/Page.jsx
+++ b/code/lib/cli/rendererAssets/preact/Page.jsx
@@ -1,5 +1,3 @@
-/** @jsx h */
-import { h } from 'preact';
import { useState } from 'preact/hooks';
import { Header } from './Header';
diff --git a/code/lib/cli/rendererAssets/preact/Page.stories.jsx b/code/lib/cli/rendererAssets/preact/Page.stories.jsx
index f41bab61b7a9..c79af4904740 100644
--- a/code/lib/cli/rendererAssets/preact/Page.stories.jsx
+++ b/code/lib/cli/rendererAssets/preact/Page.stories.jsx
@@ -1,5 +1,3 @@
-/** @jsx h */
-import { h } from 'preact';
import { within, userEvent } from '@storybook/testing-library';
import { Page } from './Page';
diff --git a/code/lib/cli/src/repro-templates.ts b/code/lib/cli/src/repro-templates.ts
index f0d7439e6e2a..6b949fc19844 100644
--- a/code/lib/cli/src/repro-templates.ts
+++ b/code/lib/cli/src/repro-templates.ts
@@ -1,7 +1,7 @@
const craTemplates = {
'cra/default-js': {
name: 'Create React App (Javascript)',
- script: 'npx create-react-app .',
+ script: 'npx create-react-app {{beforeDir}}',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/cra',
@@ -11,7 +11,7 @@ const craTemplates = {
},
'cra/default-ts': {
name: 'Create React App (Typescript)',
- script: 'npx create-react-app . --template typescript',
+ script: 'npx create-react-app {{beforeDir}} --template typescript',
cadence: ['ci', 'daily', 'weekly'],
// Re-enable once https://github.com/storybookjs/storybook/issues/19351 is fixed.
skipTasks: ['smoke-test'],
@@ -26,7 +26,7 @@ const craTemplates = {
const reactViteTemplates = {
'react-vite/default-js': {
name: 'React Vite (JS)',
- script: 'yarn create vite . --template react',
+ script: 'yarn create vite {{beforeDir}} --template react',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/react-vite',
@@ -36,7 +36,7 @@ const reactViteTemplates = {
},
'react-vite/default-ts': {
name: 'React Vite (TS)',
- script: 'yarn create vite . --template react-ts',
+ script: 'yarn create vite {{beforeDir}} --template react-ts',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/react-vite',
@@ -49,7 +49,7 @@ const reactViteTemplates = {
const reactWebpackTemplates = {
'react-webpack/18-ts': {
name: 'React Webpack5 (TS)',
- script: 'yarn create webpack5-react .',
+ script: 'yarn create webpack5-react {{beforeDir}}',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/react-webpack5',
@@ -59,7 +59,8 @@ const reactWebpackTemplates = {
},
'react-webpack/17-ts': {
name: 'React Webpack5 (TS)',
- script: 'yarn create webpack5-react . --version-react="17" --version-react-dom="17"',
+ script:
+ 'yarn create webpack5-react {{beforeDir}} --version-react="17" --version-react-dom="17"',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/react-webpack5',
@@ -72,7 +73,7 @@ const reactWebpackTemplates = {
const vue3ViteTemplates = {
'vue3-vite/default-js': {
name: 'Vue3 Vite (JS)',
- script: 'yarn create vite . --template vue',
+ script: 'yarn create vite {{beforeDir}} --template vue',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/vue3-vite',
@@ -82,7 +83,7 @@ const vue3ViteTemplates = {
},
'vue3-vite/default-ts': {
name: 'Vue3 Vite (TS)',
- script: 'yarn create vite . --template vue-ts',
+ script: 'yarn create vite {{beforeDir}} --template vue-ts',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/vue3-vite',
@@ -99,7 +100,7 @@ const vue2ViteTemplates = {
// We don't really want to maintain weird custom scripts like this,
// preferring community bootstrap scripts / generators instead.
script:
- 'yarn create vite . --template vanilla && yarn add --dev @vitejs/plugin-vue2 vue-template-compiler vue@2 && echo "import vue2 from \'@vitejs/plugin-vue2\';\n\nexport default {\n\tplugins: [vue2()]\n};" > vite.config.js',
+ 'yarn create vite {{beforeDir}} --template vanilla && yarn add --dev @vitejs/plugin-vue2 vue-template-compiler vue@2 && echo "import vue2 from \'@vitejs/plugin-vue2\';\n\nexport default {\n\tplugins: [vue2()]\n};" > vite.config.js',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/vue2-vite',
@@ -112,7 +113,7 @@ const vue2ViteTemplates = {
const htmlWebpackTemplates = {
'html-webpack/default': {
name: 'HTML Webpack5',
- script: 'yarn create webpack5-html .',
+ script: 'yarn create webpack5-html {{beforeDir}}',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/html-webpack5',
@@ -125,7 +126,7 @@ const htmlWebpackTemplates = {
const svelteViteTemplates = {
'svelte-vite/default-js': {
name: 'Svelte Vite (JS)',
- script: 'yarn create vite . --template svelte',
+ script: 'yarn create vite {{beforeDir}} --template svelte',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/svelte-vite',
@@ -135,7 +136,7 @@ const svelteViteTemplates = {
},
'svelte-vite/default-ts': {
name: 'Svelte Vite (TS)',
- script: 'yarn create vite . --template svelte-ts',
+ script: 'yarn create vite {{beforeDir}} --template svelte-ts',
cadence: ['ci', 'daily', 'weekly'],
expected: {
framework: '@storybook/svelte-vite',
@@ -148,7 +149,7 @@ const svelteViteTemplates = {
const litViteTemplates = {
'lit-vite/default-js': {
name: 'Lit Vite (JS)',
- script: 'yarn create vite . --template lit',
+ script: 'yarn create vite {{beforeDir}} --template lit',
cadence: ['ci', 'daily', 'weekly'] as any,
// Re-enable once https://github.com/storybookjs/storybook/issues/19351 is fixed.
skipTasks: ['smoke-test'],
@@ -160,7 +161,7 @@ const litViteTemplates = {
},
'lit-vite/default-ts': {
name: 'Lit Vite (TS)',
- script: 'yarn create vite . --template lit-ts',
+ script: 'yarn create vite {{beforeDir}} --template lit-ts',
cadence: ['ci', 'daily', 'weekly'] as any,
// Re-enable once https://github.com/storybookjs/storybook/issues/19351 is fixed.
skipTasks: ['smoke-test'],
@@ -175,7 +176,8 @@ const litViteTemplates = {
const vueCliTemplates = {
'vue-cli/default-js': {
name: 'Vue-CLI (Default JS)',
- script: 'npx -p @vue/cli vue create . --default --packageManager=yarn --force --merge',
+ script:
+ 'npx -p @vue/cli vue create {{beforeDir}} --default --packageManager=yarn --force --merge',
cadence: ['ci', 'daily', 'weekly'],
// Re-enable once https://github.com/storybookjs/storybook/issues/19351 is fixed.
skipTasks: ['smoke-test'],
@@ -188,7 +190,7 @@ const vueCliTemplates = {
'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)',
+ 'npx -p @vue/cli vue create {{beforeDir}} --default --packageManager=yarn --force --merge --preset=Default\\ (Vue\\ 2)',
cadence: ['ci', 'daily', 'weekly'],
// Re-enable once https://github.com/storybookjs/storybook/issues/19351 is fixed.
skipTasks: ['smoke-test'],
@@ -200,6 +202,31 @@ const vueCliTemplates = {
},
};
+const preactWebpackTemplates = {
+ 'preact-webpack5/default-js': {
+ name: 'Preact CLI (Default JS)',
+ script: 'npx preact-cli create default {{beforeDir}} --name preact-app --yarn --no-install',
+ // cadence: ['ci', 'daily', 'weekly'],
+ cadence: [] as string[],
+ expected: {
+ framework: '@storybook/preact-webpack5',
+ renderer: '@storybook/preact',
+ builder: '@storybook/builder-webpack5',
+ },
+ },
+ 'preact-webpack5/default-ts': {
+ name: 'Preact CLI (Default TS)',
+ script: 'npx preact-cli create typescript {{beforeDir}} --name preact-app --yarn --no-install',
+ // cadence: ['ci', 'daily', 'weekly'],
+ cadence: [] as string[],
+ expected: {
+ framework: '@storybook/preact-webpack5',
+ renderer: '@storybook/preact',
+ builder: '@storybook/builder-webpack5',
+ },
+ },
+};
+
export default {
...craTemplates,
...reactWebpackTemplates,
@@ -210,6 +237,7 @@ export default {
...litViteTemplates,
...vueCliTemplates,
...htmlWebpackTemplates,
+ ...preactWebpackTemplates,
// FIXME: missing documentation.json
// 'angular/latest': {
// name: 'Angular (latest)',
diff --git a/code/presets/preact-webpack/src/index.ts b/code/presets/preact-webpack/src/index.ts
index a64e9d1771a8..72d6f12c46a7 100644
--- a/code/presets/preact-webpack/src/index.ts
+++ b/code/presets/preact-webpack/src/index.ts
@@ -14,7 +14,7 @@ export const babel: StorybookConfig['babelDefault'] = (config) => {
...(config.plugins || []).filter((p) => {
const name = Array.isArray(p) ? p[0] : p;
if (typeof name === 'string') {
- return !name.includes('babel-plugin-transform-react-jsx');
+ return !name.includes('plugin-transform-react-jsx');
}
return true;
}),
@@ -23,20 +23,6 @@ export const babel: StorybookConfig['babelDefault'] = (config) => {
};
export const webpackFinal: StorybookConfig['webpackFinal'] = (config) => {
- const rules = config.module?.rules || [];
- const tsxRule = rules.find((rule) => (rule.test as RegExp).test?.('main.tsx'));
- tsxRule.use = (tsxRule.use as any).map((entry: any) => {
- let newPlugins = entry.options.plugins;
- if (entry.loader?.includes('babel-loader')) {
- newPlugins = (entry.options as any).plugins.map((plugin: any) => {
- if (plugin[0]?.includes?.('@babel/plugin-transform-react-jsx')) {
- return [plugin[0], { importSource: 'preact', runtime: 'automatic' }];
- }
- return plugin;
- });
- }
- return { ...entry, options: { ...entry.options, plugins: newPlugins } };
- });
return {
...config,
resolve: {
diff --git a/code/renderers/preact/src/config.ts b/code/renderers/preact/src/config.ts
index 7e2023a437d5..73bae9076ba3 100644
--- a/code/renderers/preact/src/config.ts
+++ b/code/renderers/preact/src/config.ts
@@ -1,3 +1,3 @@
-export { renderToDOM } from './render';
+export { renderToDOM, render } from './render';
export const parameters = { framework: 'preact' as const };
diff --git a/code/renderers/preact/src/render.tsx b/code/renderers/preact/src/render.tsx
index 0ed43f5ebf06..a3328dac2577 100644
--- a/code/renderers/preact/src/render.tsx
+++ b/code/renderers/preact/src/render.tsx
@@ -1,8 +1,26 @@
+/** @jsx h */
import * as preact from 'preact';
import { dedent } from 'ts-dedent';
import type { RenderContext } from '@storybook/store';
+import { ArgsStoryFn } from '@storybook/csf';
+
import type { StoryFnPreactReturnType, PreactFramework } from './types';
+const { h } = preact;
+
+export const render: ArgsStoryFn = (args, context) => {
+ const { id, component: Component } = context;
+ if (!Component) {
+ throw new Error(
+ `Unable to render story ${id} as the component annotation is missing from the default export`
+ );
+ }
+
+ // @ts-expect-error I think the type of Component should be Preact.ComponentType, but even that
+ // doens't make TS happy, I suspect because TS wants "react" components.
+ return ;
+};
+
let renderedStory: Element;
function preactRender(story: StoryFnPreactReturnType | null, domElement: Element): void {
diff --git a/code/renderers/preact/template/components/Button.jsx b/code/renderers/preact/template/components/Button.jsx
new file mode 100644
index 000000000000..6115046e83ec
--- /dev/null
+++ b/code/renderers/preact/template/components/Button.jsx
@@ -0,0 +1,14 @@
+/* eslint-disable react/react-in-jsx-scope */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import PropTypes from 'prop-types';
+
+export const Button = ({ onClick, children }) => (
+
+);
+
+Button.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ children: PropTypes.node.isRequired,
+};
diff --git a/code/renderers/preact/template/components/Form.jsx b/code/renderers/preact/template/components/Form.jsx
new file mode 100644
index 000000000000..f12e905f0dba
--- /dev/null
+++ b/code/renderers/preact/template/components/Form.jsx
@@ -0,0 +1,38 @@
+/* eslint-disable react/react-in-jsx-scope */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import PropTypes from 'prop-types';
+import { useState } from 'preact/hooks';
+
+export const Form = ({ onSuccess }) => {
+ const [value, setValue] = useState('');
+ const [complete, setComplete] = useState(false);
+
+ function onSubmit(event) {
+ event.preventDefault();
+ onSuccess(value);
+
+ setTimeout(() => setComplete(true), 500);
+ setTimeout(() => setComplete(false), 1500);
+ }
+
+ return (
+
+ );
+};
+
+Form.propTypes = {
+ onSuccess: PropTypes.func.isRequired,
+};
diff --git a/code/renderers/preact/template/components/Html.jsx b/code/renderers/preact/template/components/Html.jsx
new file mode 100644
index 000000000000..36cff8218890
--- /dev/null
+++ b/code/renderers/preact/template/components/Html.jsx
@@ -0,0 +1,10 @@
+/* eslint-disable react/react-in-jsx-scope */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import PropTypes from 'prop-types';
+
+// eslint-disable-next-line react/no-danger
+export const Html = ({ content }) => ;
+
+Html.propTypes = {
+ content: PropTypes.string.isRequired,
+};
diff --git a/code/renderers/preact/template/components/Pre.jsx b/code/renderers/preact/template/components/Pre.jsx
new file mode 100644
index 000000000000..7efec93be9d6
--- /dev/null
+++ b/code/renderers/preact/template/components/Pre.jsx
@@ -0,0 +1,21 @@
+/* eslint-disable react/react-in-jsx-scope */
+// eslint-disable-next-line import/no-extraneous-dependencies
+import PropTypes from 'prop-types';
+
+export const Pre = ({ style, object, text }) => (
+
+ {object ? JSON.stringify(object, null, 2) : text}
+
+);
+
+Pre.propTypes = {
+ style: PropTypes.shape({}),
+ object: PropTypes.shape({}),
+ text: PropTypes.string,
+};
+
+Pre.defaultProps = {
+ style: {},
+ object: null,
+ text: '',
+};
diff --git a/code/renderers/preact/template/components/index.js b/code/renderers/preact/template/components/index.js
new file mode 100644
index 000000000000..d952d26f9b1f
--- /dev/null
+++ b/code/renderers/preact/template/components/index.js
@@ -0,0 +1,9 @@
+import globalThis from 'global';
+
+import { Button } from './Button.jsx';
+import { Pre } from './Pre.jsx';
+import { Form } from './Form.jsx';
+import { Html } from './Html.jsx';
+
+globalThis.Components = { Button, Pre, Form, Html };
+globalThis.storybookRenderer = 'preact';
diff --git a/code/renderers/preact/template/stories/.gitkeep b/code/renderers/preact/template/stories/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/scripts/next-repro-generators/generate-repros.ts b/scripts/next-repro-generators/generate-repros.ts
index 735c54b51349..6b9fbadc113d 100755
--- a/scripts/next-repro-generators/generate-repros.ts
+++ b/scripts/next-repro-generators/generate-repros.ts
@@ -84,7 +84,7 @@ export const runCommand = async (script: string, options: ExecaOptions) => {
console.log(`Running command: ${script}`);
}
- return command(script, { stdout: shouldDebug ? 'inherit' : 'ignore', ...options });
+ return command(script, { stdout: shouldDebug ? 'inherit' : 'ignore', shell: true, ...options });
};
const addDocumentation = async (
@@ -133,13 +133,11 @@ const runGenerators = async (
// We do the creation inside a temp dir to avoid yarn container problems
const createBaseDir = directory();
- const createBeforeDir = join(createBaseDir, BEFORE_DIR_NAME);
-
- await ensureDir(createBeforeDir);
-
await setupYarn({ cwd: createBaseDir });
- await runCommand(script, { cwd: createBeforeDir });
+ const createBeforeDir = join(createBaseDir, BEFORE_DIR_NAME);
+ const scriptWithBeforeDir = script.replace('{{beforeDir}}', createBeforeDir);
+ await runCommand(scriptWithBeforeDir, { cwd: createBaseDir });
await localizeYarnConfigFiles(createBaseDir, createBeforeDir);