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 ( +
+ + + {complete &&

Completed!!

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