diff --git a/examples/react-18/.env b/examples/react-18/.env new file mode 100644 index 00000000..e613627a --- /dev/null +++ b/examples/react-18/.env @@ -0,0 +1,3 @@ +STORYBOOK_ENV_VAR=included +VITE_ENV_VAR=included +ENV_VAR=should_not_be_included diff --git a/examples/react-18/.storybook/main.js b/examples/react-18/.storybook/main.js new file mode 100644 index 00000000..36b7d5fe --- /dev/null +++ b/examples/react-18/.storybook/main.js @@ -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; + }, +}; diff --git a/examples/react-18/.storybook/preview.js b/examples/react-18/.storybook/preview.js new file mode 100644 index 00000000..d3914580 --- /dev/null +++ b/examples/react-18/.storybook/preview.js @@ -0,0 +1,9 @@ +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; diff --git a/examples/react-18/package.json b/examples/react-18/package.json new file mode 100644 index 00000000..ba4f3163 --- /dev/null +++ b/examples/react-18/package.json @@ -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" + } +} diff --git a/examples/react-18/stories/Button.jsx b/examples/react-18/stories/Button.jsx new file mode 100644 index 00000000..eefda2bf --- /dev/null +++ b/examples/react-18/stories/Button.jsx @@ -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.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, +}; diff --git a/examples/react-18/stories/Button.stories.jsx b/examples/react-18/stories/Button.stories.jsx new file mode 100644 index 00000000..11428b26 --- /dev/null +++ b/examples/react-18/stories/Button.stories.jsx @@ -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', + }, +}; diff --git a/examples/react-18/stories/EnvironmentVariables.jsx b/examples/react-18/stories/EnvironmentVariables.jsx new file mode 100644 index 00000000..eb6729d3 --- /dev/null +++ b/examples/react-18/stories/EnvironmentVariables.jsx @@ -0,0 +1,16 @@ +export function EnvironmentVariables() { + return ( +
+

import . meta . env:

+
{JSON.stringify(import.meta.env, null, 2)}
+

import . meta . env . STORYBOOK:

+
{import.meta.env.STORYBOOK}
+

import . meta . env . STORYBOOK_ENV_VAR:

+
{import.meta.env.STORYBOOK_ENV_VAR}
+

import . meta . env . VITE_ENV_VAR:

+
{import.meta.env.VITE_ENV_VAR}
+

import . meta . env . ENV_VAR:

+
{import.meta.env.ENV_VAR}
+
+ ); +} diff --git a/examples/react-18/stories/EnvironmentVariables.stories.jsx b/examples/react-18/stories/EnvironmentVariables.stories.jsx new file mode 100644 index 00000000..d9544b7b --- /dev/null +++ b/examples/react-18/stories/EnvironmentVariables.stories.jsx @@ -0,0 +1,8 @@ +import { EnvironmentVariables } from './EnvironmentVariables'; + +export default { + title: 'Environment Variables', + component: EnvironmentVariables, +}; + +export const Info = () => ; diff --git a/examples/react-18/stories/Header.jsx b/examples/react-18/stories/Header.jsx new file mode 100644 index 00000000..14393acf --- /dev/null +++ b/examples/react-18/stories/Header.jsx @@ -0,0 +1,42 @@ +import PropTypes from 'prop-types'; + +import { Button } from './Button'; +import './header.css'; + +export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => ( +
+
+
+ + + + + + + +

Acme

+
+
+ {user ? ( +
+
+
+); + +Header.propTypes = { + user: PropTypes.shape({}), + onLogin: PropTypes.func.isRequired, + onLogout: PropTypes.func.isRequired, + onCreateAccount: PropTypes.func.isRequired, +}; + +Header.defaultProps = { + user: null, +}; diff --git a/examples/react-18/stories/Header.stories.jsx b/examples/react-18/stories/Header.stories.jsx new file mode 100644 index 00000000..5d776c95 --- /dev/null +++ b/examples/react-18/stories/Header.stories.jsx @@ -0,0 +1,16 @@ +import { Header } from './Header'; + +export default { + title: 'Example/Header', + component: Header, +}; + +const Template = (args) =>
; + +export const LoggedIn = Template.bind({}); +LoggedIn.args = { + user: {}, +}; + +export const LoggedOut = Template.bind({}); +LoggedOut.args = {}; diff --git a/examples/react-18/stories/Introduction.stories.mdx b/examples/react-18/stories/Introduction.stories.mdx new file mode 100644 index 00000000..e0cdad29 --- /dev/null +++ b/examples/react-18/stories/Introduction.stories.mdx @@ -0,0 +1,198 @@ +import { Meta } from '@storybook/addon-docs'; +import Code from './assets/code-brackets.svg'; +import Colors from './assets/colors.svg'; +import Comments from './assets/comments.svg'; +import Direction from './assets/direction.svg'; +import Flow from './assets/flow.svg'; +import Plugin from './assets/plugin.svg'; +import Repo from './assets/repo.svg'; +import StackAlt from './assets/stackalt.svg'; + + + + + +# Welcome to Storybook + +Storybook helps you build UI components in isolation from your app's business logic, data, and context. +That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA. + +Browse example stories now by navigating to them in the sidebar. +View their code in the `src/stories` directory to learn how they work. +We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages. + +
Configure
+ +
+ + plugin + + Presets for popular tools + Easy setup for TypeScript, SCSS and more. + + + + Build + + Build configuration + How to customize webpack and Babel + + + + colors + + Styling + How to load and configure CSS libraries + + + + flow + + Data + Providers and mocking for data libraries + + +
+ +
Learn
+ +
+ + repo + + Storybook documentation + Configure, customize, and extend + + + + direction + + In-depth guides + Best practices from leading teams + + + + code + + GitHub project + View the source and add issues + + + + comments + + Discord chat + Chat with maintainers and the community + + +
+ +
+ TipEdit the Markdown in src/stories/Introduction.stories.mdx +
diff --git a/examples/react-18/stories/Page.jsx b/examples/react-18/stories/Page.jsx new file mode 100644 index 00000000..d4e0e2ed --- /dev/null +++ b/examples/react-18/stories/Page.jsx @@ -0,0 +1,69 @@ +/* eslint-disable max-len */ +import PropTypes from 'prop-types'; + +import { Header } from './Header'; +import './page.css'; + +export const Page = ({ user, onLogin, onLogout, onCreateAccount }) => ( +
+
+ +
+

Pages in Storybook

+

+ We recommend building UIs with a{' '} + + component-driven + {' '} + process starting with atomic components and ending with pages. +

+

+ Render pages with mock data. This makes it easy to build and review page states without needing to navigate to + them in your app. Here are some handy patterns for managing page data in Storybook: +

+
    +
  • + Use a higher-level connected component. Storybook helps you compose such data from the "args" of child + component stories +
  • +
  • + Assemble data in the page component from your services. You can mock these services out using Storybook. +
  • +
+

+ Get a guided tutorial on component-driven development at{' '} + + Storybook tutorials + + . Read more in the{' '} + + docs + + . +

+
+ Tip Adjust the width of the canvas with the{' '} + + + + + + Viewports addon in the toolbar +
+
+
+); +Page.propTypes = { + user: PropTypes.shape({}), + onLogin: PropTypes.func.isRequired, + onLogout: PropTypes.func.isRequired, + onCreateAccount: PropTypes.func.isRequired, +}; + +Page.defaultProps = { + user: null, +}; diff --git a/examples/react-18/stories/Page.stories.jsx b/examples/react-18/stories/Page.stories.jsx new file mode 100644 index 00000000..15e09a5f --- /dev/null +++ b/examples/react-18/stories/Page.stories.jsx @@ -0,0 +1,19 @@ +import { Page } from './Page'; +import * as HeaderStories from './Header.stories'; + +export default { + title: 'Example/Page', + component: Page, +}; + +const Template = (args) => ; + +export const LoggedIn = Template.bind({}); +LoggedIn.args = { + ...HeaderStories.LoggedIn.args, +}; + +export const LoggedOut = Template.bind({}); +LoggedOut.args = { + ...HeaderStories.LoggedOut.args, +}; diff --git a/examples/react-18/stories/assets/code-brackets.svg b/examples/react-18/stories/assets/code-brackets.svg new file mode 100644 index 00000000..73de9477 --- /dev/null +++ b/examples/react-18/stories/assets/code-brackets.svg @@ -0,0 +1 @@ +illustration/code-brackets \ No newline at end of file diff --git a/examples/react-18/stories/assets/colors.svg b/examples/react-18/stories/assets/colors.svg new file mode 100644 index 00000000..17d58d51 --- /dev/null +++ b/examples/react-18/stories/assets/colors.svg @@ -0,0 +1 @@ +illustration/colors \ No newline at end of file diff --git a/examples/react-18/stories/assets/comments.svg b/examples/react-18/stories/assets/comments.svg new file mode 100644 index 00000000..6493a139 --- /dev/null +++ b/examples/react-18/stories/assets/comments.svg @@ -0,0 +1 @@ +illustration/comments \ No newline at end of file diff --git a/examples/react-18/stories/assets/direction.svg b/examples/react-18/stories/assets/direction.svg new file mode 100644 index 00000000..65676ac2 --- /dev/null +++ b/examples/react-18/stories/assets/direction.svg @@ -0,0 +1 @@ +illustration/direction \ No newline at end of file diff --git a/examples/react-18/stories/assets/flow.svg b/examples/react-18/stories/assets/flow.svg new file mode 100644 index 00000000..8ac27db4 --- /dev/null +++ b/examples/react-18/stories/assets/flow.svg @@ -0,0 +1 @@ +illustration/flow \ No newline at end of file diff --git a/examples/react-18/stories/assets/plugin.svg b/examples/react-18/stories/assets/plugin.svg new file mode 100644 index 00000000..29e5c690 --- /dev/null +++ b/examples/react-18/stories/assets/plugin.svg @@ -0,0 +1 @@ +illustration/plugin \ No newline at end of file diff --git a/examples/react-18/stories/assets/repo.svg b/examples/react-18/stories/assets/repo.svg new file mode 100644 index 00000000..f386ee90 --- /dev/null +++ b/examples/react-18/stories/assets/repo.svg @@ -0,0 +1 @@ +illustration/repo \ No newline at end of file diff --git a/examples/react-18/stories/assets/stackalt.svg b/examples/react-18/stories/assets/stackalt.svg new file mode 100644 index 00000000..9b7ad274 --- /dev/null +++ b/examples/react-18/stories/assets/stackalt.svg @@ -0,0 +1 @@ +illustration/stackalt \ No newline at end of file diff --git a/examples/react-18/stories/button.css b/examples/react-18/stories/button.css new file mode 100644 index 00000000..dc91dc76 --- /dev/null +++ b/examples/react-18/stories/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/examples/react-18/stories/header.css b/examples/react-18/stories/header.css new file mode 100644 index 00000000..acadc9ec --- /dev/null +++ b/examples/react-18/stories/header.css @@ -0,0 +1,26 @@ +.wrapper { + font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding: 15px 20px; + display: flex; + align-items: center; + justify-content: space-between; +} + +svg { + display: inline-block; + vertical-align: top; +} + +h1 { + font-weight: 900; + font-size: 20px; + line-height: 1; + margin: 6px 0 6px 10px; + display: inline-block; + vertical-align: top; +} + +button + button { + margin-left: 10px; +} diff --git a/examples/react-18/stories/mdx-in-stories/Example.docs.mdx b/examples/react-18/stories/mdx-in-stories/Example.docs.mdx new file mode 100644 index 00000000..357cc05f --- /dev/null +++ b/examples/react-18/stories/mdx-in-stories/Example.docs.mdx @@ -0,0 +1,16 @@ +import { Canvas, Story } from '@storybook/addon-docs'; + +# Embedding stories by reference in MDX files + +In this example `Example.stories.jsx` import an MDX file, which contains +references to stories by their unique ID. + +See also [CSF Stories with arbitrary MDX](https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/recipes.md#csf-stories-with-arbitrary-mdx). + +## Button + + + + + +_You should be able to see the source of the above story._ diff --git a/examples/react-18/stories/mdx-in-stories/Example.stories.jsx b/examples/react-18/stories/mdx-in-stories/Example.stories.jsx new file mode 100644 index 00000000..d9a4b857 --- /dev/null +++ b/examples/react-18/stories/mdx-in-stories/Example.stories.jsx @@ -0,0 +1,16 @@ +import page from './Example.docs.mdx'; +import { Button } from '../Button'; + +export default { + title: 'Example/MDX in stories', + + parameters: { + docs: { + page, + }, + }, +}; + +export function PrimaryButton() { + return