Skip to content

Commit

Permalink
Support React 18 (#320)
Browse files Browse the repository at this point in the history
An [exciting PR](storybookjs/storybook#17215) has been merged and released in `6.5.0-alpha.58 ` that adds storybook support for React 18.  However, it currently breaks in the vite-builder test because of an `import()` of the new `react-dom/client`, which only exists in react 18.  Due to vitejs/vite#6007, we have to do a little bit of trickery to stop vite from erroring out.  The approach I'm taking here is:

1) Rewriting the import of `react-dom/client` to a dummy file if it can't be `require.resolve()`ed.  This fixes the problem in production builds.
2) Additionally, adding `react-dom/client` to the list of `optimizeDeps.exclude` if the framework is react and the package can't be required.  This is necessary to prevent vite from trying to pre-bundle, and throwing errors in dev.

This PR also updates the examples to the new `6.5.0-alpha.58` version of storybook, which includes the PR mentioned above, so that it can be tested in our react examples, and it adds a new `react-18` example as well.
  • Loading branch information
IanVS authored Apr 7, 2022
1 parent b6ef9de commit 52b6fdf
Show file tree
Hide file tree
Showing 37 changed files with 1,295 additions and 473 deletions.
3 changes: 3 additions & 0 deletions examples/react-18/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
STORYBOOK_ENV_VAR=included
VITE_ENV_VAR=included
ENV_VAR=should_not_be_included
15 changes: 15 additions & 0 deletions examples/react-18/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
framework: '@storybook/react',
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-a11y', '@storybook/addon-links', '@storybook/addon-essentials'],
core: {
builder: '@storybook/builder-vite',
},
features: {
storyStoreV7: true,
},
async viteFinal(config, { configType }) {
// customize the Vite config here
return config;
},
};
9 changes: 9 additions & 0 deletions examples/react-18/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
34 changes: 34 additions & 0 deletions examples/react-18/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "example-react-18",
"private": true,
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"storybook": "start-storybook --port 6018",
"build-storybook": "build-storybook",
"preview-storybook": "http-server storybook-static --port 6018 --silent",
"test": "wait-on tcp:6018 && test-storybook --url 'http://localhost:6018'",
"test-ci": "run-p --race test preview-storybook"
},
"author": "",
"license": "MIT",
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@storybook/addon-a11y": "^6.5.0-alpha.58",
"@storybook/addon-docs": "^6.5.0-alpha.58",
"@storybook/addon-essentials": "^6.5.0-alpha.58",
"@storybook/builder-vite": "workspace:*",
"@storybook/react": "^6.5.0-alpha.58",
"@storybook/test-runner": "^0.0.4",
"@vitejs/plugin-react": "^1.3.0",
"http-server": "^14.1.0",
"jest": "^27.5.1",
"npm-run-all": "^4.1.5",
"vite": "2.9.0",
"wait-on": "^6.0.1"
}
}
49 changes: 49 additions & 0 deletions examples/react-18/stories/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import PropTypes from 'prop-types';
import './button.css';

/**
* Primary UI component for user interaction
*/
export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
return (
<button
type="button"
className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
style={backgroundColor && { backgroundColor }}
{...props}
>
{label}
</button>
);
};

Button.propTypes = {
/**
* Is this the principal call to action on the page?
*/
primary: PropTypes.bool,
/**
* What background color to use
*/
backgroundColor: PropTypes.string,
/**
* How large should the button be?
*/
size: PropTypes.oneOf(['small', 'medium', 'large']),
/**
* Button contents
*/
label: PropTypes.string.isRequired,
/**
* Optional click handler
*/
onClick: PropTypes.func,
};

Button.defaultProps = {
backgroundColor: null,
primary: false,
size: 'medium',
onClick: undefined,
};
36 changes: 36 additions & 0 deletions examples/react-18/stories/Button.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Button } from './Button';

export default {
// no title, to demonstrate autotitle
component: Button,
argTypes: {
backgroundColor: { control: 'color' },
},
};

export const Primary = {
args: {
primary: true,
label: 'Button',
},
};

export const Secondary = {
args: {
label: 'Button',
},
};

export const Large = {
args: {
size: 'large',
label: 'Button',
},
};

export const Small = {
args: {
size: 'small',
label: 'Button',
},
};
16 changes: 16 additions & 0 deletions examples/react-18/stories/EnvironmentVariables.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function EnvironmentVariables() {
return (
<div>
<h1>import . meta . env:</h1>
<div>{JSON.stringify(import.meta.env, null, 2)}</div>
<h1>import . meta . env . STORYBOOK:</h1>
<div>{import.meta.env.STORYBOOK}</div>
<h1>import . meta . env . STORYBOOK_ENV_VAR:</h1>
<div>{import.meta.env.STORYBOOK_ENV_VAR}</div>
<h1>import . meta . env . VITE_ENV_VAR:</h1>
<div>{import.meta.env.VITE_ENV_VAR}</div>
<h1>import . meta . env . ENV_VAR:</h1>
<div>{import.meta.env.ENV_VAR}</div>
</div>
);
}
8 changes: 8 additions & 0 deletions examples/react-18/stories/EnvironmentVariables.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { EnvironmentVariables } from './EnvironmentVariables';

export default {
title: 'Environment Variables',
component: EnvironmentVariables,
};

export const Info = () => <EnvironmentVariables />;
42 changes: 42 additions & 0 deletions examples/react-18/stories/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import PropTypes from 'prop-types';

import { Button } from './Button';
import './header.css';

export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
<header>
<div className="wrapper">
<div>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z" fill="#FFF" />
<path d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z" fill="#555AB9" />
<path d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z" fill="#91BAF8" />
</g>
</svg>
<h1>Acme</h1>
</div>
<div>
{user ? (
<Button size="small" onClick={onLogout} label="Log out" />
) : (
<>
<Button size="small" onClick={onLogin} label="Log in" />
<Button primary size="small" onClick={onCreateAccount} label="Sign up" />
</>
)}
</div>
</div>
</header>
);

Header.propTypes = {
user: PropTypes.shape({}),
onLogin: PropTypes.func.isRequired,
onLogout: PropTypes.func.isRequired,
onCreateAccount: PropTypes.func.isRequired,
};

Header.defaultProps = {
user: null,
};
16 changes: 16 additions & 0 deletions examples/react-18/stories/Header.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Header } from './Header';

export default {
title: 'Example/Header',
component: Header,
};

const Template = (args) => <Header {...args} />;

export const LoggedIn = Template.bind({});
LoggedIn.args = {
user: {},
};

export const LoggedOut = Template.bind({});
LoggedOut.args = {};
Loading

0 comments on commit 52b6fdf

Please sign in to comment.