├── .gitignore
├── .storybook
├── main.js
└── preview.js
├── README.md
├── package.json
├── pages
├── api
│ └── hello.js
└── index.js
├── public
├── favicon.ico
└── vercel.svg
├── stories
├── Banner.jsx
├── Banner.stories.jsx
├── Button.jsx
├── Button.stories.jsx
├── Header.jsx
├── Header.stories.jsx
├── Introduction.stories.mdx
├── Page.jsx
├── Page.stories.jsx
├── assets
│ ├── code-brackets.svg
│ ├── colors.svg
│ ├── comments.svg
│ ├── direction.svg
│ ├── flow.svg
│ ├── plugin.svg
│ ├── repo.svg
│ └── stackalt.svg
├── button.css
├── header.css
└── page.css
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | #storybook
12 | /storybook-static
13 |
14 | # next.js
15 | /.next/
16 | /out/
17 |
18 | # production
19 | /build
20 |
21 | # misc
22 | .DS_Store
23 |
24 | # debug
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
29 | # local env files
30 | .env.local
31 | .env.development.local
32 | .env.test.local
33 | .env.production.local
34 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "stories": [
3 | "../stories/**/*.stories.mdx",
4 | "../stories/**/*.stories.@(js|jsx|ts|tsx)"
5 | ],
6 | "addons": [
7 | "@storybook/addon-links",
8 | "@storybook/addon-essentials"
9 | ]
10 | }
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | export const parameters = {
2 | actions: { argTypesRegex: "^on[A-Z].*" },
3 | controls: {
4 | matchers: {
5 | color: /(background|color)$/i,
6 | date: /Date$/,
7 | },
8 | },
9 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Storybook: React Component Library Example
2 |
3 | 
4 |
5 | This post/tutorial focuses on UI development with Storybook.
6 |
7 | What is covered:
8 |
9 | - What Storybook is?
10 | - How can it save you valuable time when working with components?
11 | - How to set up a Storybook instance that is co-located with a React application?
12 | - How to create the first instance of a component along with its story?
13 | - How do various prop values affect the component’s appearance and functionality?
14 |
15 |
16 |
17 | -> Follow the tutorial [here](https://snipcart.com/blog/storybook-react-tutorial-example).
18 |
19 | -> Try the demo [here](https://nextjs-storybook-example-4nh7fvdd0-m0nica.vercel.app/).
20 |
21 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
22 |
23 | ## Getting Started
24 |
25 | First, run the development server:
26 |
27 | ```bash
28 | npm run dev
29 | # or
30 | yarn dev
31 | ```
32 |
33 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
34 |
35 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
36 |
37 | ## Learn More
38 |
39 | To learn more about Next.js, take a look at the following resources:
40 |
41 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
42 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
43 |
44 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
45 |
46 | ## Deploy on Vercel
47 |
48 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
49 |
50 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-storybook-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "storybook": "start-storybook -p 6006",
10 | "build-storybook": "build-storybook"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.4.0",
14 | "next": "11.0.1",
15 | "react": "17.0.2",
16 | "react-dom": "17.0.2"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.14.6",
20 | "@storybook/addon-actions": "^6.3.4",
21 | "@storybook/addon-essentials": "^6.3.4",
22 | "@storybook/addon-links": "^6.3.4",
23 | "@storybook/react": "^6.3.4",
24 | "babel-loader": "^8.2.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default (req, res) => {
4 | res.statusCode = 200
5 | res.json({ name: 'John Doe' })
6 | }
7 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import Banner from "../stories/Banner.jsx";
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
Create Next App
8 |
9 |
10 |
11 |
12 |
13 |
14 | This is an example of how we can use our components from Storybook
15 | in a NextJS application. JSX components that are defined in
16 | Storybook can be imported/exported like regular JSX components. If
17 | you need to import components from Storybook in external
18 | applications then you should explore publishing a NPM package that
19 | contains the relevant components.
20 |
21 |
22 |
23 |
24 | Welcome to Next.js!
25 |
26 |
27 | Get started by editing pages/index.js
28 |
29 |
58 |
59 |
60 |
70 |
71 |
201 |
202 |
216 |
217 | );
218 | }
219 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snipcart/nextjs-storybook-example/5557c2a1ecf0e036d5909efe44b42e945a1b0f67/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/stories/Banner.jsx:
--------------------------------------------------------------------------------
1 | /** @jsxImportSource @emotion/react */
2 | import React from "react";
3 | export default function Banner({ variant = "info", children }) {
4 | const beforeStyles = {
5 | width: 35,
6 | height: 35,
7 | display: "inline-flex",
8 | justifyContent: "center",
9 | position: "absolute",
10 | left: "-1.2rem",
11 | borderRadius: "50%",
12 | alignItems: "center",
13 | top: "-0.8rem",
14 | };
15 |
16 | const variantStyles = {
17 | info: {
18 | borderLeft: "4px solid #b4aaff",
19 | backgroundColor: "rgba(224, 226, 255, 0.5)",
20 | color: "#2a2135",
21 | "&:before": {
22 | content: '"🔑"',
23 | backgroundColor: "#b4aaff",
24 | ...beforeStyles,
25 | },
26 | },
27 | danger: {
28 | borderLeft: "4px solid #ff7828",
29 | backgroundColor: "rgb(253, 236, 234)",
30 | "&:before": {
31 | content: '"⚠️"',
32 | backgroundColor: "#ff7828",
33 | ...beforeStyles,
34 | },
35 | },
36 | congrats: {
37 | borderLeft: "4px solid #72bc23",
38 | backgroundColor: "rgb(249, 253, 234)",
39 | "&:before": {
40 | content: '"🎉"',
41 | backgroundColor: "#72bc23",
42 | ...beforeStyles,
43 | },
44 | },
45 | documentation: {
46 | borderLeft: "4px solid #44a9ba",
47 | backgroundColor: "rgb(234, 248, 253);",
48 | "&:before": {
49 | content: '"📚"',
50 | backgroundColor: "#44a9ba",
51 | ...beforeStyles,
52 | },
53 | },
54 | };
55 | return (
56 |
72 | );
73 | }
74 |
75 | Banner.propTypes = {
76 | variant: "info" | "congrats" | "documentation" | "danger",
77 | };
78 |
--------------------------------------------------------------------------------
/stories/Banner.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Banner from "./Banner";
4 |
5 | export default {
6 | title: "Example/Banner",
7 | component: Banner,
8 | };
9 |
10 | export const Info = () => (
11 |
12 |
13 | This is an example of an info banner to display important information.
14 |
15 |
16 | );
17 |
18 | export const Danger = () => (
19 |
20 | This is an example of a danger banner to display warnings.
21 |
22 | );
23 |
24 | export const Congrats = () => (
25 |
26 | This is an example a congrats banner to celebrate a win!
27 |
28 | );
29 |
30 | export const Documentation = () => (
31 |
32 |
33 | This is an example a documentation banner to highlight relevant reading
34 | materials and documentation.
35 |
36 |
37 | );
38 |
--------------------------------------------------------------------------------
/stories/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import './button.css';
4 |
5 | /**
6 | * Primary UI component for user interaction
7 | */
8 | export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
9 | const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
10 | return (
11 |
17 | {label}
18 |
19 | );
20 | };
21 |
22 | Button.propTypes = {
23 | /**
24 | * Is this the principal call to action on the page?
25 | */
26 | primary: PropTypes.bool,
27 | /**
28 | * What background color to use
29 | */
30 | backgroundColor: PropTypes.string,
31 | /**
32 | * How large should the button be?
33 | */
34 | size: PropTypes.oneOf(['small', 'medium', 'large']),
35 | /**
36 | * Button contents
37 | */
38 | label: PropTypes.string.isRequired,
39 | /**
40 | * Optional click handler
41 | */
42 | onClick: PropTypes.func,
43 | };
44 |
45 | Button.defaultProps = {
46 | backgroundColor: null,
47 | primary: false,
48 | size: 'medium',
49 | onClick: undefined,
50 | };
51 |
--------------------------------------------------------------------------------
/stories/Button.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Button } from './Button';
4 |
5 | export default {
6 | title: 'Example/Button',
7 | component: Button,
8 | argTypes: {
9 | backgroundColor: { control: 'color' },
10 | },
11 | };
12 |
13 | const Template = (args) => ;
14 |
15 | export const Primary = Template.bind({});
16 | Primary.args = {
17 | primary: true,
18 | label: 'Button',
19 | };
20 |
21 | export const Secondary = Template.bind({});
22 | Secondary.args = {
23 | label: 'Button',
24 | };
25 |
26 | export const Large = Template.bind({});
27 | Large.args = {
28 | size: 'large',
29 | label: 'Button',
30 | };
31 |
32 | export const Small = Template.bind({});
33 | Small.args = {
34 | size: 'small',
35 | label: 'Button',
36 | };
37 |
--------------------------------------------------------------------------------
/stories/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { Button } from './Button';
5 | import './header.css';
6 |
7 | export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
8 |
41 | );
42 |
43 | Header.propTypes = {
44 | user: PropTypes.shape({}),
45 | onLogin: PropTypes.func.isRequired,
46 | onLogout: PropTypes.func.isRequired,
47 | onCreateAccount: PropTypes.func.isRequired,
48 | };
49 |
50 | Header.defaultProps = {
51 | user: null,
52 | };
53 |
--------------------------------------------------------------------------------
/stories/Header.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Header } from './Header';
4 |
5 | export default {
6 | title: 'Example/Header',
7 | component: Header,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const LoggedIn = Template.bind({});
13 | LoggedIn.args = {
14 | user: {},
15 | };
16 |
17 | export const LoggedOut = Template.bind({});
18 | LoggedOut.args = {};
19 |
--------------------------------------------------------------------------------
/stories/Introduction.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs';
2 | import Code from './assets/code-brackets.svg';
3 | import Colors from './assets/colors.svg';
4 | import Comments from './assets/comments.svg';
5 | import Direction from './assets/direction.svg';
6 | import Flow from './assets/flow.svg';
7 | import Plugin from './assets/plugin.svg';
8 | import Repo from './assets/repo.svg';
9 | import StackAlt from './assets/stackalt.svg';
10 |
11 |
12 |
13 |
116 |
117 | # Welcome to Storybook
118 |
119 | Storybook helps you build UI components in isolation from your app's business logic, data, and context.
120 | That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA.
121 |
122 | Browse example stories now by navigating to them in the sidebar.
123 | View their code in the `src/stories` directory to learn how they work.
124 | We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages.
125 |
126 | Configure
127 |
128 |
174 |
175 | Learn
176 |
177 |
207 |
208 |
209 | Tip Edit the Markdown in{' '}
210 | src/stories/Introduction.stories.mdx
211 |
212 |
--------------------------------------------------------------------------------
/stories/Page.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { Header } from './Header';
5 | import './page.css';
6 |
7 | export const Page = ({ user, onLogin, onLogout, onCreateAccount }) => (
8 |
9 |
10 |
11 |
12 | Pages in Storybook
13 |
14 | We recommend building UIs with a{' '}
15 |
16 | component-driven
17 | {' '}
18 | process starting with atomic components and ending with pages.
19 |
20 |
21 | Render pages with mock data. This makes it easy to build and review page states without
22 | needing to navigate to them in your app. Here are some handy patterns for managing page data
23 | in Storybook:
24 |
25 |
26 |
27 | Use a higher-level connected component. Storybook helps you compose such data from the
28 | "args" of child component stories
29 |
30 |
31 | Assemble data in the page component from your services. You can mock these services out
32 | using Storybook.
33 |
34 |
35 |
36 | Get a guided tutorial on component-driven development at{' '}
37 |
38 | Storybook tutorials
39 |
40 | . Read more in the{' '}
41 |
42 | docs
43 |
44 | .
45 |
46 |
47 |
Tip Adjust the width of the canvas with the{' '}
48 |
49 |
50 |
55 |
56 |
57 | Viewports addon in the toolbar
58 |
59 |
60 |
61 | );
62 | Page.propTypes = {
63 | user: PropTypes.shape({}),
64 | onLogin: PropTypes.func.isRequired,
65 | onLogout: PropTypes.func.isRequired,
66 | onCreateAccount: PropTypes.func.isRequired,
67 | };
68 |
69 | Page.defaultProps = {
70 | user: null,
71 | };
72 |
--------------------------------------------------------------------------------
/stories/Page.stories.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Page } from './Page';
4 | import * as HeaderStories from './Header.stories';
5 |
6 | export default {
7 | title: 'Example/Page',
8 | component: Page,
9 | };
10 |
11 | const Template = (args) => ;
12 |
13 | export const LoggedIn = Template.bind({});
14 | LoggedIn.args = {
15 | ...HeaderStories.LoggedIn.args,
16 | };
17 |
18 | export const LoggedOut = Template.bind({});
19 | LoggedOut.args = {
20 | ...HeaderStories.LoggedOut.args,
21 | };
22 |
--------------------------------------------------------------------------------
/stories/assets/code-brackets.svg:
--------------------------------------------------------------------------------
1 | illustration/code-brackets
--------------------------------------------------------------------------------
/stories/assets/colors.svg:
--------------------------------------------------------------------------------
1 | illustration/colors
--------------------------------------------------------------------------------
/stories/assets/comments.svg:
--------------------------------------------------------------------------------
1 | illustration/comments
--------------------------------------------------------------------------------
/stories/assets/direction.svg:
--------------------------------------------------------------------------------
1 | illustration/direction
--------------------------------------------------------------------------------
/stories/assets/flow.svg:
--------------------------------------------------------------------------------
1 | illustration/flow
--------------------------------------------------------------------------------
/stories/assets/plugin.svg:
--------------------------------------------------------------------------------
1 | illustration/plugin
--------------------------------------------------------------------------------
/stories/assets/repo.svg:
--------------------------------------------------------------------------------
1 | illustration/repo
--------------------------------------------------------------------------------
/stories/assets/stackalt.svg:
--------------------------------------------------------------------------------
1 | illustration/stackalt
--------------------------------------------------------------------------------
/stories/button.css:
--------------------------------------------------------------------------------
1 | .storybook-button {
2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | font-weight: 700;
4 | border: 0;
5 | border-radius: 3em;
6 | cursor: pointer;
7 | display: inline-block;
8 | line-height: 1;
9 | }
10 | .storybook-button--primary {
11 | color: white;
12 | background-color: #1ea7fd;
13 | }
14 | .storybook-button--secondary {
15 | color: #333;
16 | background-color: transparent;
17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
18 | }
19 | .storybook-button--small {
20 | font-size: 12px;
21 | padding: 10px 16px;
22 | }
23 | .storybook-button--medium {
24 | font-size: 14px;
25 | padding: 11px 20px;
26 | }
27 | .storybook-button--large {
28 | font-size: 16px;
29 | padding: 12px 24px;
30 | }
31 |
--------------------------------------------------------------------------------
/stories/header.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1);
4 | padding: 15px 20px;
5 | display: flex;
6 | align-items: center;
7 | justify-content: space-between;
8 | }
9 |
10 | svg {
11 | display: inline-block;
12 | vertical-align: top;
13 | }
14 |
15 | h1 {
16 | font-weight: 900;
17 | font-size: 20px;
18 | line-height: 1;
19 | margin: 6px 0 6px 10px;
20 | display: inline-block;
21 | vertical-align: top;
22 | }
23 |
24 | button + button {
25 | margin-left: 10px;
26 | }
27 |
--------------------------------------------------------------------------------
/stories/page.css:
--------------------------------------------------------------------------------
1 | section {
2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | font-size: 14px;
4 | line-height: 24px;
5 | padding: 48px 20px;
6 | margin: 0 auto;
7 | max-width: 600px;
8 | color: #333;
9 | }
10 |
11 | h2 {
12 | font-weight: 900;
13 | font-size: 32px;
14 | line-height: 1;
15 | margin: 0 0 4px;
16 | display: inline-block;
17 | vertical-align: top;
18 | }
19 |
20 | p {
21 | margin: 1em 0;
22 | }
23 |
24 | a {
25 | text-decoration: none;
26 | color: #1ea7fd;
27 | }
28 |
29 | ul {
30 | padding-left: 30px;
31 | margin: 1em 0;
32 | }
33 |
34 | li {
35 | margin-bottom: 8px;
36 | }
37 |
38 | .tip {
39 | display: inline-block;
40 | border-radius: 1em;
41 | font-size: 11px;
42 | line-height: 12px;
43 | font-weight: 700;
44 | background: #e7fdd8;
45 | color: #66bf3c;
46 | padding: 4px 12px;
47 | margin-right: 10px;
48 | vertical-align: top;
49 | }
50 |
51 | .tip-wrapper {
52 | font-size: 13px;
53 | line-height: 20px;
54 | margin-top: 40px;
55 | margin-bottom: 40px;
56 | }
57 |
58 | .tip-wrapper svg {
59 | display: inline-block;
60 | height: 12px;
61 | width: 12px;
62 | margin-right: 4px;
63 | vertical-align: top;
64 | margin-top: 3px;
65 | }
66 |
67 | .tip-wrapper svg path {
68 | fill: #1ea7fd;
69 | }
70 |
--------------------------------------------------------------------------------