├── .circleci └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── chromatic.yml │ ├── release.yml │ └── type-checking.yml ├── .gitignore ├── .node-version ├── .nvmrc ├── .storybook ├── main.ts ├── preview.tsx └── static │ └── chromatic-logo-square.png ├── .travis.yml ├── .vscode └── settings.json ├── .yarnrc ├── CHANGELOG.md ├── README.md ├── babel.config.js ├── build-storybook.log ├── package.json ├── prettier.config.js ├── src ├── .eslintrc ├── Intro.mdx ├── components │ ├── Avatar.stories.tsx │ ├── Avatar.tsx │ ├── AvatarList.stories.js │ ├── AvatarList.tsx │ ├── Badge.stories.tsx │ ├── Badge.tsx │ ├── Button.stories.tsx │ ├── Button.tsx │ ├── ButtonAction.stories.tsx │ ├── ButtonAction.tsx │ ├── ButtonToggle.stories.tsx │ ├── ButtonToggle.tsx │ ├── Cardinal.stories.tsx │ ├── Cardinal.tsx │ ├── Checkbox.stories.tsx │ ├── Checkbox.tsx │ ├── CodeSnippets.stories.tsx │ ├── CodeSnippets.tsx │ ├── Color.mdx │ ├── FormErrorState.stories.tsx │ ├── FormErrorState.tsx │ ├── Highlight.stories.tsx │ ├── Highlight.tsx │ ├── Icon.stories.tsx │ ├── Icon.tsx │ ├── Input.stories.tsx │ ├── Input.tsx │ ├── Link.stories.tsx │ ├── Link.tsx │ ├── LinkTabs.stories.tsx │ ├── LinkTabs.tsx │ ├── OutlineCTA.stories.tsx │ ├── OutlineCTA.tsx │ ├── ProgressDots.stories.tsx │ ├── ProgressDots.tsx │ ├── Radio.stories.tsx │ ├── Radio.tsx │ ├── Select.stories.tsx │ ├── Select.tsx │ ├── ShadowBoxCTA.stories.tsx │ ├── ShadowBoxCTA.tsx │ ├── SharedStyles.stories.tsx │ ├── Spinner.stories.tsx │ ├── Spinner.tsx │ ├── StoryLinkWrapper.tsx │ ├── Subheading.stories.tsx │ ├── Subheading.tsx │ ├── Textarea.stories.tsx │ ├── Textarea.tsx │ ├── Typography.mdx │ ├── TypographyStyles.stories.tsx │ ├── clipboard │ │ ├── Clipboard.stories.tsx │ │ ├── Clipboard.tsx │ │ ├── ClipboardCode.stories.tsx │ │ ├── ClipboardCode.tsx │ │ ├── ClipboardIcon.stories.tsx │ │ ├── ClipboardIcon.tsx │ │ ├── ClipboardInput.stories.tsx │ │ └── ClipboardInput.tsx │ ├── global.d.ts │ ├── header │ │ ├── Header.stories.tsx │ │ ├── Header.tsx │ │ ├── HeaderContext.ts │ │ ├── Nav.tsx │ │ ├── NavItem.tsx │ │ └── NavLink.tsx │ ├── index.tsx │ ├── modal │ │ ├── Modal.stories.tsx │ │ ├── Modal.tsx │ │ ├── WithModal.stories.tsx │ │ └── WithModal.tsx │ ├── shared │ │ ├── animation.ts │ │ ├── global.tsx │ │ ├── icons.tsx │ │ └── styles.ts │ ├── table-of-contents │ │ ├── BulletLink.stories.tsx │ │ ├── BulletLink.tsx │ │ ├── ItemLink.stories.tsx │ │ ├── ItemLink.tsx │ │ ├── MenuLink.stories.tsx │ │ ├── MenuLink.tsx │ │ ├── TableOfContents.stories.tsx │ │ ├── TableOfContents.tsx │ │ └── TableOfContentsItems.tsx │ ├── tag │ │ ├── TagItem.stories.tsx │ │ ├── TagItem.tsx │ │ ├── TagLink.stories.tsx │ │ ├── TagLink.tsx │ │ ├── TagList.stories.tsx │ │ └── TagList.tsx │ └── tooltip │ │ ├── ListItem.stories.tsx │ │ ├── ListItem.tsx │ │ ├── Tooltip.stories.tsx │ │ ├── Tooltip.tsx │ │ ├── TooltipLinkList.stories.tsx │ │ ├── TooltipLinkList.tsx │ │ ├── TooltipMessage.stories.tsx │ │ ├── TooltipMessage.tsx │ │ ├── TooltipNote.stories.tsx │ │ ├── TooltipNote.tsx │ │ ├── WithTooltip.stories.tsx │ │ └── WithTooltip.tsx ├── design-system.png ├── images │ ├── Chrome.tsx │ ├── Firefox.tsx │ ├── Ie.tsx │ ├── Illustrations.stories.tsx │ ├── Safari.tsx │ ├── chrome.svg │ ├── colored-icons │ │ ├── Accessibility.tsx │ │ ├── Assign.tsx │ │ ├── Bell.tsx │ │ ├── Boxmodel.tsx │ │ ├── Branch.tsx │ │ ├── Browsers.tsx │ │ ├── Bug.tsx │ │ ├── Catalog.tsx │ │ ├── Check.tsx │ │ ├── Code.tsx │ │ ├── CodeBrackets.tsx │ │ ├── Colors.tsx │ │ ├── Comments.tsx │ │ ├── Components.tsx │ │ ├── CustomAddon.tsx │ │ ├── Delete.tsx │ │ ├── Detect.tsx │ │ ├── Direction.tsx │ │ ├── Document.tsx │ │ ├── DocumentAlt.tsx │ │ ├── Email.tsx │ │ ├── Error.tsx │ │ ├── Eye.tsx │ │ ├── FastForward.svg │ │ ├── FastForward.tsx │ │ ├── Flow.tsx │ │ ├── Group.tsx │ │ ├── Ignore.tsx │ │ ├── Interact.tsx │ │ ├── Interface.tsx │ │ ├── Key.tsx │ │ ├── Knob.tsx │ │ ├── Lightning.tsx │ │ ├── Link.tsx │ │ ├── Overlap.tsx │ │ ├── Pixel.tsx │ │ ├── Plugin.tsx │ │ ├── Plus.tsx │ │ ├── Projects.tsx │ │ ├── Redo.svg │ │ ├── Redo.tsx │ │ ├── Repo.tsx │ │ ├── Review.tsx │ │ ├── Rewind.tsx │ │ ├── Runtest.tsx │ │ ├── Scale.tsx │ │ ├── Search.tsx │ │ ├── Shield.tsx │ │ ├── Soc2.tsx │ │ ├── Stack.tsx │ │ ├── Stoplight.tsx │ │ ├── Testflake.tsx │ │ ├── Testpyramid.tsx │ │ ├── Text.tsx │ │ ├── Turbo.tsx │ │ ├── Undo.tsx │ │ ├── Unignore.tsx │ │ ├── Update.tsx │ │ ├── accessibility.svg │ │ ├── assign.svg │ │ ├── bell.svg │ │ ├── boxmodel.svg │ │ ├── branch.svg │ │ ├── browsers.svg │ │ ├── bug.svg │ │ ├── catalog.svg │ │ ├── check.svg │ │ ├── code-brackets.svg │ │ ├── code.svg │ │ ├── colors.svg │ │ ├── comments.svg │ │ ├── components.svg │ │ ├── custom-addon.svg │ │ ├── delete.svg │ │ ├── detect.svg │ │ ├── direction.svg │ │ ├── document-alt.svg │ │ ├── document.svg │ │ ├── email.svg │ │ ├── error.svg │ │ ├── eye.svg │ │ ├── flow.svg │ │ ├── group.svg │ │ ├── ignore.svg │ │ ├── index.tsx │ │ ├── interact.svg │ │ ├── interface.svg │ │ ├── key.svg │ │ ├── knob.svg │ │ ├── lightning.svg │ │ ├── link.svg │ │ ├── overlap.svg │ │ ├── pixel.svg │ │ ├── plugin.svg │ │ ├── plus.svg │ │ ├── projects.svg │ │ ├── repo.svg │ │ ├── review.svg │ │ ├── rewind.svg │ │ ├── runtest.svg │ │ ├── scale.svg │ │ ├── search.svg │ │ ├── shield.svg │ │ ├── soc2.svg │ │ ├── stack.svg │ │ ├── stoplight.svg │ │ ├── testflake.svg │ │ ├── testpyramid.svg │ │ ├── text.svg │ │ ├── turbo.svg │ │ ├── undo.svg │ │ ├── unignore.svg │ │ └── update.svg │ ├── firefox.svg │ ├── ie.svg │ ├── index.tsx │ ├── logos │ │ ├── Chromatic.tsx │ │ ├── ChromaticInverted.tsx │ │ ├── Circleci.tsx │ │ ├── Netlify.tsx │ │ ├── Storybook.tsx │ │ ├── StorybookIcon.tsx │ │ ├── StorybookInverted.tsx │ │ ├── StorybookMonochrome.tsx │ │ ├── StorybookMonochromeInverse.tsx │ │ ├── chromatic-inverted.svg │ │ ├── chromatic.svg │ │ ├── circleci.svg │ │ ├── index.tsx │ │ ├── netlify.svg │ │ ├── storybook-icon.svg │ │ ├── storybook-inverted.svg │ │ ├── storybook-monochrome-inverse.svg │ │ ├── storybook-monochrome.svg │ │ └── storybook.svg │ └── safari.svg ├── index.tsx ├── styles.css ├── test.js └── utils │ ├── index.ts │ └── loadFontsForStorybook.ts ├── svgr.config.js ├── tsconfig.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | lint: 5 | docker: 6 | - image: cimg/node:16.17.1 7 | working_directory: ~/repo 8 | steps: 9 | - checkout 10 | - restore_cache: 11 | keys: 12 | - v1-dependencies-{{ checksum "package.json" }} 13 | - v1-dependencies- 14 | - run: yarn install 15 | - run: yarn lint 16 | build: 17 | docker: 18 | - image: cimg/node:16.17.1 19 | working_directory: ~/repo 20 | steps: 21 | - checkout 22 | - restore_cache: 23 | keys: 24 | - v1-dependencies-{{ checksum "package.json" }} 25 | - v1-dependencies- 26 | - run: yarn install 27 | - save_cache: 28 | paths: 29 | - node_modules 30 | key: v1-dependencies-{{ checksum "package.json" }} 31 | 32 | - run: yarn build 33 | - run: yarn build-storybook 34 | 35 | workflows: 36 | version: 2 37 | lint-build: 38 | jobs: 39 | - build 40 | - lint 41 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/**/*.js 2 | storybook-static/**/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['@storybook/eslint-config-storybook', 'plugin:storybook/recommended'], 4 | parserOptions: { 5 | project: ['./tsconfig.json'], 6 | createDefaultProgram: true, 7 | }, 8 | overrides: [ 9 | { 10 | files: ['**/*.tsx'], 11 | rules: { 12 | 'react/prop-types': 'off', 13 | 'react/require-default-props': 'off', 14 | 'react/default-props-match-prop-types': 'off', 15 | }, 16 | }, 17 | ], 18 | settings: { 19 | jest: { 20 | version: 'latest', 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /.github/workflows/chromatic.yml: -------------------------------------------------------------------------------- 1 | name: "Chromatic" 2 | on: 3 | push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Use Node.js 16.17.1 11 | uses: actions/setup-node@v3 12 | with: 13 | node-version: 16.17.1 14 | - run: | 15 | yarn 16 | - run: | 17 | yarn build 18 | - run: | 19 | yarn build-storybook 20 | - uses: chromaui/action@v1 21 | with: 22 | projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | storybookBuildDir: storybook-static 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: [push] 4 | 5 | jobs: 6 | release: 7 | runs-on: ubuntu-latest 8 | if: "!contains(github.event.head_commit.message, 'ci skip') && !contains(github.event.head_commit.message, 'skip ci')" 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Prepare repository 13 | run: git fetch --unshallow --tags 14 | 15 | - name: Use Node.js 16.17.1 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: 16.17.1 19 | 20 | - name: Install dependencies 21 | uses: bahmutov/npm-install@v1 22 | 23 | - name: Create Release 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | run: | 28 | yarn release 29 | -------------------------------------------------------------------------------- /.github/workflows/type-checking.yml: -------------------------------------------------------------------------------- 1 | name: "Type Checking" 2 | on: 3 | push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - run: | 11 | yarn 12 | - run: | 13 | yarn typescript:check 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | storybook-static 12 | .cache 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # we use yarn 27 | package-lock.json -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | v12.14.1 -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.14.1 -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-webpack5'; 2 | 3 | const config: StorybookConfig = { 4 | stories: ['../src/**/*.mdx', '../src/**/*.stories.js', '../src/**/*.stories.tsx'], 5 | addons: ['@storybook/addon-essentials', '@storybook/addon-storysource', '@storybook/addon-a11y'], 6 | framework: { 7 | name: '@storybook/react-webpack5', 8 | options: {}, 9 | }, 10 | docs: { 11 | autodocs: true, 12 | }, 13 | staticDirs: ['./static'], 14 | }; 15 | 16 | export default config; 17 | -------------------------------------------------------------------------------- /.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { loadFontsForStorybook } from '../src/utils/index'; 3 | import { GlobalStyle } from '../src/components/shared/global'; 4 | 5 | import type { Preview } from '@storybook/react'; 6 | 7 | loadFontsForStorybook(); 8 | 9 | const withGlobalStyle = (storyFn) => ( 10 | <> 11 | 12 | {storyFn()} 13 | 14 | ); 15 | 16 | const preview: Preview = { 17 | parameters: { 18 | // automatically create action args for all props that start with "on" 19 | actions: { argTypesRegex: '^on.*' }, 20 | dependencies: { 21 | // display only dependencies/dependents that have a story in storybook 22 | // by default this is false 23 | withStoriesOnly: true, 24 | 25 | // completely hide a dependency/dependents block if it has no elements 26 | // by default this is false 27 | hideEmpty: true, 28 | }, 29 | }, 30 | decorators: [withGlobalStyle], 31 | } 32 | 33 | export default preview; 34 | -------------------------------------------------------------------------------- /.storybook/static/chromatic-logo-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/design-system/0c4c043e8bf956ee9aad4664383829f807b4bfaf/.storybook/static/chromatic-logo-square.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 9 4 | - 8 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deepscan.enable": true 3 | } -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | registry "https://registry.npmjs.org/" 2 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true); 3 | 4 | return { 5 | sourceMaps: false, 6 | presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'], 7 | plugins: [ 8 | '@babel/plugin-proposal-object-rest-spread', 9 | '@babel/transform-runtime', 10 | [ 11 | '@emotion', 12 | { 13 | sourceMap: false, 14 | importMap: { 15 | '@storybook/theming': { 16 | styled: { canonicalImport: ['@emotion/styled', 'default'] }, 17 | css: { canonicalImport: ['@emotion/react', 'css'] }, 18 | Global: { canonicalImport: ['@emotion/react', 'Global'] }, 19 | }, 20 | }, 21 | }, 22 | ], 23 | ], 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | const base = require('@storybook/linter-config/prettier.config'); 2 | 3 | module.exports = { 4 | ...base, 5 | arrowParens: 'always', 6 | overrides: [ 7 | { 8 | files: '*.html', 9 | options: { parser: 'babel' }, 10 | }, 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/Intro.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/blocks'; 2 | import designSystem from './design-system.png'; 3 | 4 | 5 | 6 | 7 | 8 | # Get started 9 | 10 | Storybook Design System is a reusable component library that helps Storybook contributors build UIs faster. The goal is to make building durable UIs more productive and satisfying. 11 | 12 | ## Install 13 | 14 | SDS components are written in React, and its stories are written in [Component Story Format](https://medium.com/storybookjs/component-story-format-66f4c32366df). It requires Storybook version 5.2-beta and up. 15 | 16 | Add SDS to your project. 17 | 18 | `npm install --save @storybook/design-system` 19 | 20 | ### **Use** 21 | 22 | Import components you want into your UI 23 | 24 | `import { Button, Badge } from '@storybook/design-system';` 25 | 26 | and use them like so 27 | 28 | ``` 29 | const example = () => ( 30 |
31 | 32 | Cool 33 |
34 | ) 35 | ``` 36 | 37 | ### **Browse SDS components in your own Storybook** 38 | 39 | Once you add the package, update your .storybook/config.js to import all files ending in .stories.js. 40 | 41 | ``` 42 | import { configure } from '@storybook/react'; 43 | configure(require.context('../src', true, /.stories.js$/), module); 44 | ``` 45 | 46 | ### **Run and develop SDS locally** 47 | 48 | Clone the [SDS GitHub project](https://github.com/storybookjs/design-system) then start Storybook. 49 | 50 | `yarn && yarn run storybook` 51 | 52 | ## Used by 53 | 54 | - [Storybook homepage](https://storybook.js.org/) 55 | - [LearnStorybook.com](https://www.learnstorybook.com/) 56 | - [ChromaticQA.com](https://www.chromaticqa.com/) 57 | 58 | Note: this package is not used in Storybook's UI, but the visual design is identical. 59 | 60 | ### **Resources** 61 | 62 | - [Introducing Storybook Design System](https://medium.com/storybookjs/introducing-storybook-design-system-23fd9b1ac3c0) 63 | - [GitHub repository](https://github.com/storybookjs/design-system) 64 | -------------------------------------------------------------------------------- /src/components/Avatar.stories.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps, FunctionComponent } from 'react'; 2 | 3 | import { Avatar, AvatarType } from './Avatar'; 4 | 5 | export default { 6 | title: 'Avatar', 7 | component: Avatar, 8 | args: { 9 | type: 'user', 10 | }, 11 | }; 12 | 13 | const Base: FunctionComponent> = ({ src, ...props }) => ( 14 |
15 | 16 | 17 | 22 |
23 | ); 24 | 25 | export const Large = () => ; 26 | 27 | export const Medium = () => ; 28 | 29 | export const Small = () => ; 30 | 31 | export const Tiny = () => ; 32 | 33 | export const Organization = () => ( 34 | 35 | ); 36 | -------------------------------------------------------------------------------- /src/components/AvatarList.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { AvatarList } from './AvatarList'; 3 | import { Avatar } from './Avatar'; 4 | 5 | export const users = [ 6 | { 7 | id: '1', 8 | name: 'Dominic Nguyen', 9 | avatarUrl: 'https://avatars2.githubusercontent.com/u/263385', 10 | }, 11 | { 12 | id: '2', 13 | name: 'Tom Coleman', 14 | avatarUrl: 'https://avatars2.githubusercontent.com/u/132554', 15 | }, 16 | { 17 | id: '3', 18 | name: 'Zoltan Olah', 19 | avatarUrl: 'https://avatars0.githubusercontent.com/u/81672', 20 | }, 21 | { 22 | id: '4', 23 | name: 'Tim Hingston', 24 | avatarUrl: 'https://avatars3.githubusercontent.com/u/1831709', 25 | }, 26 | ]; 27 | 28 | export default { 29 | title: 'AvatarList', 30 | component: AvatarList, 31 | parameters: { 32 | subcomponents: { Avatar }, 33 | }, 34 | excludeStories: ['users'], 35 | }; 36 | 37 | export const Basic = (args) => ; 38 | Basic.args = { users }; 39 | 40 | export const Short = Basic.bind(); 41 | Short.args = { users: users.slice(0, 2) }; 42 | 43 | export const Ellipsized = Basic.bind(); 44 | Ellipsized.args = { users }; 45 | 46 | export const BigUserCount = Basic.bind(); 47 | BigUserCount.args = { users, userCount: 100 }; 48 | 49 | export const SmallSize = Basic.bind(); 50 | SmallSize.args = { users, userCount: 100, size: 'small' }; 51 | 52 | export const Loading = Basic.bind(); 53 | Loading.args = { users: undefined, isLoading: true }; 54 | 55 | export const Empty = Basic.bind(); 56 | Empty.args = { users: [] }; 57 | -------------------------------------------------------------------------------- /src/components/Badge.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Badge } from './Badge'; 3 | import { Icon } from './Icon'; 4 | 5 | export default { 6 | title: 'Badge', 7 | component: Badge, 8 | }; 9 | 10 | export const Basic = (args) => Badge; 11 | 12 | export const All = () => ( 13 |
14 | Positive 15 | Negative 16 | Neutral 17 | Error 18 | Warning 19 | Selected 20 |
21 | ); 22 | 23 | export const WithIcon = () => ( 24 | 25 | 26 | with icon 27 | 28 | ); 29 | -------------------------------------------------------------------------------- /src/components/Badge.tsx: -------------------------------------------------------------------------------- 1 | import { styled, css } from '@storybook/theming'; 2 | import { rgba } from 'polished'; 3 | import { background, color, typography } from './shared/styles'; 4 | 5 | export const Badge = styled.div<{ 6 | status: 'positive' | 'negative' | 'neutral' | 'error' | 'warning' | 'selected'; 7 | }>` 8 | display: inline-block; 9 | vertical-align: top; 10 | font-size: ${typography.size.s1}px; 11 | line-height: 12px; 12 | padding: 4px 12px; 13 | border-radius: 3em; 14 | font-weight: ${typography.weight.bold}; 15 | 16 | svg { 17 | height: 12px; 18 | width: 12px; 19 | margin-right: 4px; 20 | margin-top: -2px; 21 | } 22 | 23 | ${(props) => 24 | props.status === 'positive' && 25 | css` 26 | color: ${color.positive}; 27 | background: ${background.positive}; 28 | box-shadow: ${rgba(color.positive, 0.1)} 0 0 0 1px inset; 29 | `}; 30 | 31 | ${(props) => 32 | props.status === 'negative' && 33 | css` 34 | color: ${color.negative}; 35 | background: ${background.negative}; 36 | box-shadow: ${rgba(color.negative, 0.1)} 0 0 0 1px inset; 37 | `}; 38 | 39 | ${(props) => 40 | props.status === 'warning' && 41 | css` 42 | color: ${color.warning}; 43 | background: ${background.warning}; 44 | box-shadow: ${rgba(color.warning, 0.1)} 0 0 0 1px inset; 45 | `}; 46 | 47 | ${(props) => 48 | props.status === 'error' && 49 | css` 50 | color: ${color.lightest}; 51 | background: ${color.red}; 52 | `}; 53 | 54 | ${(props) => 55 | props.status === 'selected' && 56 | css` 57 | color: ${color.selected}; 58 | background: ${background.calmBlue}; 59 | box-shadow: ${rgba(color.selected, 0.1)} 0 0 0 1px inset; 60 | `}; 61 | 62 | ${(props) => 63 | (props.status === 'neutral' || props.status === undefined) && 64 | css` 65 | color: ${color.darker}; 66 | background: ${color.mediumlight}; 67 | box-shadow: ${rgba(color.darker, 0.1)} 0 0 0 1px inset; 68 | `}; 69 | `; 70 | -------------------------------------------------------------------------------- /src/components/ButtonAction.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react'; 2 | 3 | import { ButtonAction } from './ButtonAction'; 4 | 5 | const meta: Meta = { 6 | title: 'ButtonAction', 7 | component: ButtonAction, 8 | tags: ['autodocs'], 9 | }; 10 | 11 | export default meta; 12 | type Story = StoryObj; 13 | 14 | export const IconOnly: Story = { 15 | args: { 16 | children: '', 17 | icon: 'starhollow', 18 | isActive: false, 19 | }, 20 | }; 21 | 22 | export const IconOnlyActive: Story = { 23 | args: { 24 | children: '', 25 | icon: 'starhollow', 26 | isActive: true, 27 | }, 28 | }; 29 | 30 | export const WithLabel: Story = { 31 | args: { 32 | children: 'Hello World', 33 | icon: 'starhollow', 34 | isActive: false, 35 | }, 36 | }; 37 | 38 | export const WithLabelActive: Story = { 39 | args: { 40 | children: 'Hello World', 41 | icon: 'starhollow', 42 | isActive: true, 43 | }, 44 | }; 45 | 46 | export const IconOnlyIsSelect: Story = { 47 | args: { 48 | children: '', 49 | icon: 'starhollow', 50 | isActive: false, 51 | isSelect: true, 52 | }, 53 | }; 54 | 55 | export const WithLabelIsSelect: Story = { 56 | args: { 57 | children: 'Hello World', 58 | icon: 'starhollow', 59 | isActive: false, 60 | isSelect: true, 61 | }, 62 | }; 63 | 64 | export const IconOnlyWithTooltip: Story = { 65 | args: { 66 | icon: 'starhollow', 67 | isActive: false, 68 | tooltip: "I'm a tooltip", 69 | }, 70 | }; 71 | 72 | export const WithLabelWithTooltip: Story = { 73 | args: { 74 | children: 'Hello World', 75 | icon: 'starhollow', 76 | isActive: false, 77 | tooltip: "I'm a tooltip", 78 | }, 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/Checkbox.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { action } from '@storybook/addon-actions'; 3 | 4 | import { Checkbox } from './Checkbox'; 5 | 6 | const onChange = action('change'); 7 | 8 | export default { 9 | title: 'forms/Checkbox', 10 | component: Checkbox, 11 | }; 12 | 13 | export const Template = (args) => ; 14 | Template.args = { label: 'Basic', hideLabel: false }; 15 | Template.storyName = 'Playground'; 16 | 17 | export const All = () => ( 18 |
19 | 20 | 21 | 27 | 28 | 35 | 36 | 37 | ); 38 | 39 | export const Unchecked = Template.bind(); 40 | Unchecked.args = { id: 'Unchecked', label: 'Cats', hideLabel: true }; 41 | 42 | export const Checked = Template.bind(); 43 | Checked.args = { id: 'Checked', label: 'Cats', hideLabel: true, checked: true }; 44 | -------------------------------------------------------------------------------- /src/components/CodeSnippets.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { CodeSnippets } from './CodeSnippets'; 4 | import { javascriptCodeWithWrappers, typescriptCodeWithWrappers } from './Highlight.stories'; 5 | import { color } from './shared/styles'; 6 | 7 | // The wrapper allows you to see the shadow in Chromatic 8 | const Wrapper = styled.div` 9 | padding: 20px; 10 | `; 11 | 12 | /* eslint-disable react/no-danger */ 13 | function JavaScriptSnippetContent() { 14 | return
; 15 | } 16 | 17 | function TypescriptSnippetContent() { 18 | return
; 19 | } 20 | /* eslint-enable react/no-danger */ 21 | 22 | const snippets = [ 23 | { 24 | Snippet: JavaScriptSnippetContent, 25 | id: '1', 26 | renderTabLabel: () => 'Label 1', 27 | }, 28 | { 29 | Snippet: TypescriptSnippetContent, 30 | id: '2', 31 | renderTabLabel: () => 'Label 2', 32 | }, 33 | ]; 34 | 35 | export default { 36 | component: CodeSnippets, 37 | decorators: [(story) => {story()}], 38 | title: 'CodeSnippets', 39 | }; 40 | 41 | const Story = (args) => ; 42 | 43 | export const Single = Story.bind({}); 44 | Single.args = { 45 | snippets: [snippets[0]], 46 | }; 47 | 48 | export const Multiple = Story.bind({}); 49 | Multiple.args = { 50 | snippets, 51 | }; 52 | 53 | const PreSnippetWrapper = styled.div` 54 | padding: 10px; 55 | border-bottom: 1px solid ${color.border}; 56 | background: #fdf5d3; 57 | `; 58 | 59 | export const PreSnippet = Story.bind({}); 60 | PreSnippet.args = { 61 | snippets: snippets.map((snippet) => ({ 62 | ...snippet, 63 | PreSnippet: () => PreSnippet content, 64 | })), 65 | }; 66 | -------------------------------------------------------------------------------- /src/components/Color.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, ColorPalette, ColorItem } from '@storybook/blocks'; 2 | 3 | import { color } from './shared/styles'; 4 | 5 | 6 | 7 | # Colors 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 39 | 40 | -------------------------------------------------------------------------------- /src/components/Icon.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled, css } from '@storybook/theming'; 3 | 4 | import { Icon } from './Icon'; 5 | import { icons } from './shared/icons'; 6 | 7 | const Meta = styled.div` 8 | color: #666; 9 | font-size: 12px; 10 | `; 11 | 12 | const Item = styled.li<{ minimal?: boolean }>` 13 | display: inline-flex; 14 | flex-direction: row; 15 | align-items: center; 16 | flex: 0 1 16%; 17 | min-width: 120px; 18 | margin: 16px; 19 | 20 | svg { 21 | margin-right: 6px; 22 | width: 14px; 23 | height: 14px; 24 | } 25 | 26 | ${(props) => 27 | props.minimal && 28 | css` 29 | flex: none; 30 | min-width: auto; 31 | padding: 0; 32 | background: #fff; 33 | margin: 16px; 34 | 35 | svg { 36 | display: block; 37 | margin-right: 0; 38 | width: 14px; 39 | height: 14px; 40 | } 41 | `}; 42 | `; 43 | 44 | const List = styled.ul` 45 | display: flex; 46 | flex-flow: row wrap; 47 | list-style: none; 48 | padding: 0; 49 | margin: 0; 50 | `; 51 | 52 | const Header = styled.h2` 53 | font-size: 16px; 54 | margin: 16px; 55 | `; 56 | 57 | export default { 58 | title: 'Icon', 59 | component: Icon, 60 | }; 61 | 62 | export const Basic = (args) => ; 63 | Basic.args = { icon: 'watch' }; 64 | 65 | export const Labels = () => ( 66 | <> 67 |
{Object.keys(icons).length} icons
68 | 69 | {Object.keys(icons).map((key) => ( 70 | 71 | 72 | {key} 73 | 74 | ))} 75 | 76 | 77 | ); 78 | 79 | export const NoLabels = () => ( 80 | <> 81 |
{Object.keys(icons).length} icons
82 | 83 | {Object.keys(icons).map((key) => ( 84 | 85 | 86 | 87 | ))} 88 | 89 | 90 | ); 91 | -------------------------------------------------------------------------------- /src/components/Icon.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { icons } from './shared/icons'; 4 | 5 | const Svg = styled.svg` 6 | display: inline-block; 7 | shape-rendering: inherit; 8 | transform: translate3d(0, 0, 0); 9 | vertical-align: middle; 10 | 11 | path { 12 | fill: currentColor; 13 | } 14 | `; 15 | 16 | /** 17 | * An Icon is a piece of visual element, but we must ensure its accessibility while using it. 18 | * It can have 2 purposes: 19 | * 20 | * - *decorative only*: for example, it illustrates a label next to it. We must ensure that it is ignored by screen readers, by setting `aria-hidden` attribute (ex: ``) 21 | * - *non-decorative*: it means that it delivers information. For example, an icon as only child in a button. The meaning can be obvious visually, but it must have a proper text alternative via `aria-label` for screen readers. (ex: ``) 22 | */ 23 | export const Icon: FunctionComponent = ({ icon, ...props }: IconProps) => { 24 | return ( 25 | 26 | <>{icons[icon]} 27 | 28 | ); 29 | }; 30 | 31 | export type IconType = keyof typeof icons; 32 | 33 | export interface IconProps { 34 | icon: IconType; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Link.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { action } from '@storybook/addon-actions'; 4 | 5 | import { Icon } from './Icon'; 6 | import { Link } from './Link'; 7 | // @ts-ignore 8 | import { StoryLinkWrapper } from './StoryLinkWrapper'; 9 | 10 | const CustomLink = styled(Link)` 11 | && { 12 | color: red; 13 | } 14 | `; 15 | 16 | const onLinkClick = action('onLinkClick'); 17 | 18 | export default { 19 | title: 'Link', 20 | component: Link, 21 | }; 22 | 23 | export const Basic: React.FunctionComponent> = () => ( 24 | 25 | link text 26 | 27 | ); 28 | 29 | export const All = () => ( 30 | <> 31 | default{' '} 32 | 33 | secondary 34 | {' '} 35 | 36 | tertiary 37 | {' '} 38 | 39 | nochrome 40 | {' '} 41 | 42 | 43 | inverse 44 | 45 | 46 | 47 | ); 48 | 49 | export const WithArrow = () => ( 50 | 51 | withArrow shows an arrow behind the link 52 | 53 | ); 54 | 55 | export const ContainsIcon = () => ( 56 | 57 | 58 | 59 | ); 60 | 61 | export const WithIcon = () => ( 62 | 63 | 64 | Link with an icon in front 65 | 66 | ); 67 | 68 | export const IsButton = () => ( 69 | /* eslint-disable-next-line */ 70 | 71 | is actually a button 72 | 73 | ); 74 | 75 | export const HasLinkWrapper = () => ( 76 | <> 77 | 78 | has a LinkWrapper like GatsbyLink or NextLink 79 | 80 |
81 | 82 | has a LinkWrapper like GatsbyLink or NextLink with custom styling 83 | 84 | 85 | ); 86 | -------------------------------------------------------------------------------- /src/components/LinkTabs.stories.tsx: -------------------------------------------------------------------------------- 1 | import { action } from '@storybook/addon-actions'; 2 | import React from 'react'; 3 | 4 | import { LinkTabs } from './LinkTabs'; 5 | 6 | const items = [ 7 | { key: '1', label: 'Activity', title: 'View activity', href: '/activity' }, 8 | { key: '2', label: 'Components', title: 'View components', href: '/components', isActive: true }, 9 | { key: '3', label: 'Changeset', title: 'View UI changes', href: '/changes' }, 10 | ]; 11 | 12 | export default { 13 | title: 'LinkTabs', 14 | }; 15 | 16 | export const Default = () => ; 17 | Default.storyName = 'default'; 18 | -------------------------------------------------------------------------------- /src/components/LinkTabs.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps } from 'react'; 2 | import { styled, css } from '@storybook/theming'; 3 | import type { StyledComponent } from '@storybook/theming'; 4 | 5 | import { Link } from './Link'; 6 | import { color, typography } from './shared/styles'; 7 | import { inlineGlow } from './shared/animation'; 8 | 9 | const Wrapper = styled.ul` 10 | display: flex; 11 | list-style: none; 12 | margin: 0; 13 | padding: 0; 14 | overflow-x: auto; 15 | overflow-y: hidden; 16 | 17 | li { 18 | list-style: none; 19 | } 20 | `; 21 | 22 | interface TabProps { 23 | isLoading?: boolean; 24 | isActive?: boolean; 25 | } 26 | 27 | const Tab: StyledComponent = styled(Link)` 28 | padding: 10px 15px; 29 | line-height: 20px; 30 | font-size: ${typography.size.s2}px; 31 | font-weight: ${typography.weight.bold}; 32 | color: ${color.mediumdark}; 33 | white-space: nowrap; 34 | 35 | &:hover { 36 | color: ${color.secondary}; 37 | } 38 | 39 | ${(props) => 40 | props.isLoading && 41 | css` 42 | cursor: progress !important; 43 | > * { 44 | ${inlineGlow}; 45 | } 46 | `} 47 | 48 | ${(props) => 49 | props.isActive && 50 | !props.isLoading && 51 | css` 52 | color: ${color.secondary}; 53 | box-shadow: ${color.secondary} 0 -3px 0 0 inset; 54 | `}; 55 | `; 56 | 57 | type ItemProps = { 58 | key: string; 59 | label: string; 60 | } & ComponentProps; 61 | 62 | interface LinkTabsProps { 63 | isLoading?: boolean; 64 | items: ItemProps[]; 65 | } 66 | 67 | export const LinkTabs = ({ 68 | isLoading = false, 69 | items = [], 70 | ...props 71 | }: LinkTabsProps & ComponentProps) => ( 72 | 73 | {items.map(({ key, label, ...item }) => ( 74 |
  • 75 | 76 | {label} 77 | 78 |
  • 79 | ))} 80 |
    81 | ); 82 | -------------------------------------------------------------------------------- /src/components/OutlineCTA.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { Badge } from './Badge'; 4 | import { Button } from './Button'; 5 | import { Link } from './Link'; 6 | import { OutlineCTA } from './OutlineCTA'; 7 | 8 | // The wrapper allows you to see the shadow in Chromatic 9 | const Wrapper = styled.div` 10 | padding: 20px; 11 | `; 12 | 13 | const InlineWrapper = styled.div` 14 | display: flex; 15 | justify-content: center; 16 | `; 17 | 18 | export default { 19 | component: OutlineCTA, 20 | title: 'OutlineCTA', 21 | argTypes: { 22 | action: { control: false }, 23 | badge: { control: false }, 24 | }, 25 | decorators: [(story) => {story()}], 26 | }; 27 | 28 | const Story = (args) => ; 29 | 30 | export const Default = Story.bind({}); 31 | Default.args = { 32 | action: ( 33 | // eslint-disable-next-line jsx-a11y/anchor-is-valid 34 | 35 | Go to latest docs 36 | 37 | ), 38 | badge: New, 39 | children: 'These docs are for version 6.0. Newer docs are available for version 6.4.', 40 | }; 41 | Default.parameters = { 42 | chromatic: { viewports: [320, 1200] }, 43 | }; 44 | 45 | export const NoBadge = Story.bind({}); 46 | NoBadge.args = { 47 | action: Default.args.action, 48 | children: Default.args.children, 49 | }; 50 | 51 | export const Inline = Story.bind({}); 52 | Inline.args = { 53 | ...Default.args, 54 | action: ( 55 | // eslint-disable-next-line jsx-a11y/anchor-is-valid 56 | 57 | Read more 58 | 59 | ), 60 | badge: Default.args.badge, 61 | children: 'Learn how to automate UI tests with Github Actions.', 62 | }; 63 | Inline.decorators = [(story) => {story()}]; 64 | -------------------------------------------------------------------------------- /src/components/OutlineCTA.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | 4 | import { breakpoint, color, spacing, typography } from './shared/styles'; 5 | 6 | const OutlineCTAWrapper = styled.div` 7 | border-radius: ${spacing.borderRadius.small}px; 8 | box-shadow: ${color.border} 0 0 0 1px inset; 9 | padding: ${spacing.padding.small}px ${spacing.padding.small}px; 10 | 11 | font-size: ${typography.size.s2}px; 12 | line-height: 20px; 13 | 14 | @media (min-width: ${breakpoint}px) { 15 | flex-wrap: nowrap; 16 | justify-content: flex-start; 17 | padding: ${spacing.padding.small}px ${spacing.padding.medium}px; 18 | } 19 | `; 20 | 21 | const MessageText = styled.span` 22 | margin-left: 2px; 23 | margin-right: 2px; 24 | `; 25 | 26 | const ActionWrapper = styled.span` 27 | font-weight: ${typography.weight.bold}; 28 | white-space: nowrap; 29 | `; 30 | 31 | interface Props { 32 | /** Displays after `children`; typically a [Link](/?path=/docs/link--basic) */ 33 | action: ReactNode; 34 | /** Displays before `children`; should be a [Badge](/?path=/docs/badge--basic) */ 35 | badge?: ReactNode; 36 | children: string; 37 | } 38 | 39 | export const OutlineCTA = ({ action, badge, children, ...rest }: Props) => ( 40 | 41 | {badge} {children} {action} 42 | 43 | ); 44 | 45 | OutlineCTA.defaultProps = { 46 | badge: null, 47 | }; 48 | -------------------------------------------------------------------------------- /src/components/ProgressDots.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ProgressDots } from './ProgressDots'; 3 | 4 | export default { 5 | title: 'ProgressDots', 6 | component: ProgressDots, 7 | argTypes: { steps: { control: { type: 'range', max: 10 } } }, 8 | }; 9 | 10 | export const Basic = (args) => ; 11 | 12 | export const Loading = Basic.bind(); 13 | Loading.args = { isLoading: true }; 14 | 15 | export const Starting = Basic.bind(); 16 | Starting.args = { steps: 4, progress: 1 }; 17 | 18 | export const Halfway = Basic.bind(); 19 | Halfway.args = { steps: 4, progress: 2 }; 20 | 21 | export const Complete = Basic.bind(); 22 | Complete.args = { steps: 4, progress: 4 }; 23 | 24 | export const LargeComplete = Basic.bind(); 25 | LargeComplete.args = { steps: 4, progress: 4, size: 'large' }; 26 | -------------------------------------------------------------------------------- /src/components/ProgressDots.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps, FunctionComponent } from 'react'; 2 | import { styled, css } from '@storybook/theming'; 3 | import { color } from './shared/styles'; 4 | import { glow } from './shared/animation'; 5 | 6 | const ProgressWrapper = styled.div<{ size?: 'small' | 'large' }>` 7 | display: inline-block; 8 | height: ${(props) => (props.size === 'small' ? 4 : 5)}px; 9 | `; 10 | 11 | interface DotProps { 12 | size: 'small' | 'large'; 13 | active: boolean; 14 | isLoading: boolean; 15 | } 16 | 17 | const Dot = styled.div` 18 | background: ${color.medium}; 19 | display: inline-block; 20 | vertical-align: top; 21 | width: ${(props) => (props.size === 'small' ? 4 : 5)}px; 22 | height: ${(props) => (props.size === 'small' ? 4 : 5)}px; 23 | border-radius: 3em; 24 | margin: 0 ${(props) => (props.size === 'small' ? 2 : 3)}px; 25 | 26 | ${(props) => 27 | props.active && 28 | css` 29 | background: ${color.mediumdark}; 30 | `}; 31 | 32 | ${(props) => 33 | props.isLoading && 34 | css` 35 | animation: ${glow} 1.5s ease-in-out infinite; 36 | &:nth-child(1) { 37 | animation-delay: 0s; 38 | } 39 | &:nth-child(2) { 40 | animation-delay: 0.3s; 41 | } 42 | &:nth-child(3) { 43 | animation-delay: 0.6s; 44 | } 45 | &:nth-child(4) { 46 | animation-delay: 0.9s; 47 | } 48 | `}; 49 | `; 50 | 51 | interface ProgressDotsProps { 52 | isLoading?: boolean; 53 | steps?: number; 54 | progress?: number; 55 | size?: 'small' | 'large'; 56 | } 57 | 58 | export const ProgressDots: FunctionComponent< 59 | ProgressDotsProps & ComponentProps 60 | > = ({ isLoading = false, steps = 4, progress = 0, size = 'small', ...rest }) => { 61 | const dots = []; 62 | for (let i = 0; i < steps; i += 1) { 63 | dots.push(); 64 | } 65 | return ( 66 | 73 | {dots} 74 | 75 | ); 76 | }; 77 | -------------------------------------------------------------------------------- /src/components/Radio.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { action } from '@storybook/addon-actions'; 3 | 4 | import { Radio } from './Radio'; 5 | 6 | const onChange = action('change'); 7 | 8 | export default { 9 | title: 'forms/Radio', 10 | component: Radio, 11 | }; 12 | 13 | export const Template = (args) => ; 14 | Template.args = { label: 'label', hideLabel: false }; 15 | Template.storyName = 'Playground'; 16 | 17 | export const All = () => ( 18 |
    19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | 28 | export const Unchecked = Template.bind(); 29 | Unchecked.args = { id: 'Mice', label: 'Mice', hideLabel: true, value: 'mice' }; 30 | 31 | export const Checked = Template.bind(); 32 | Checked.args = { 33 | id: 'Dogs', 34 | label: 'Dogs', 35 | hideLabel: true, 36 | value: 'dogs', 37 | checked: true, 38 | }; 39 | -------------------------------------------------------------------------------- /src/components/ShadowBoxCTA.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { Button } from './Button'; 4 | import { ShadowBoxCTA } from './ShadowBoxCTA'; 5 | 6 | // The wrapper allows you to see the shadow in Chromatic 7 | const Wrapper = styled.div` 8 | padding: 20px; 9 | `; 10 | 11 | const ctaAction = ; 12 | 13 | export default { 14 | component: ShadowBoxCTA, 15 | decorators: [(story) => {story()}], 16 | title: 'ShadowBoxCTA', 17 | }; 18 | 19 | const Story = (args) => ; 20 | export const Default = Story.bind({}); 21 | Default.args = { 22 | action: ctaAction, 23 | headingText: 'Composite component', 24 | messageText: 'Assemble a composite component out of simpler components', 25 | }; 26 | 27 | export const WithoutMessageText = Story.bind({}); 28 | WithoutMessageText.args = { 29 | action: ctaAction, 30 | headingText: 'Composite component', 31 | }; 32 | -------------------------------------------------------------------------------- /src/components/ShadowBoxCTA.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps, ReactNode } from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | 4 | import { breakpoint, spacing, typography } from './shared/styles'; 5 | 6 | const ShadowBox = styled.div` 7 | background: #ffffff; 8 | box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 5px 15px 0 rgba(0, 0, 0, 0.05); 9 | border-radius: ${spacing.borderRadius.small}px; 10 | `; 11 | 12 | const ShadowBoxCTAWrapper = styled(ShadowBox)` 13 | padding: ${spacing.padding.large}px; 14 | display: flex; 15 | flex-wrap: wrap; 16 | align-items: center; 17 | text-align: center; 18 | 19 | @media (min-width: ${breakpoint}px) { 20 | text-align: left; 21 | } 22 | `; 23 | 24 | const TextWrapper = styled.div` 25 | line-height: 20px; 26 | flex: 0 1 100%; 27 | 28 | @media (min-width: ${breakpoint}px) { 29 | flex: 1; 30 | } 31 | `; 32 | 33 | const HeadingText = styled.div` 34 | font-size: ${typography.size.s3}px; 35 | font-weight: ${typography.weight.bold}; 36 | `; 37 | 38 | const MessageText = styled.div` 39 | font-size: ${typography.size.s2}px; 40 | margin-top: 4px; 41 | `; 42 | 43 | const Action = styled.div` 44 | flex: 0 0 100%; 45 | margin-top: 1.5rem; 46 | 47 | button { 48 | padding: 13px 28px; 49 | } 50 | 51 | @media (min-width: ${breakpoint}px) { 52 | flex: 0 0 auto; 53 | margin-top: 0; 54 | padding-left: 60px; 55 | } 56 | `; 57 | 58 | interface ShadowBoxCTAProps { 59 | headingText: ReactNode; 60 | messageText?: ReactNode; 61 | action: ReactNode; 62 | } 63 | 64 | export const ShadowBoxCTA = ({ 65 | action, 66 | headingText, 67 | messageText, 68 | ...rest 69 | }: ShadowBoxCTAProps & ComponentProps) => ( 70 | 71 | 72 | {headingText} 73 | {messageText && {messageText}} 74 | 75 | 76 | {action} 77 | 78 | ); 79 | -------------------------------------------------------------------------------- /src/components/SharedStyles.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { pageMargins, hoverEffect } from './shared/styles'; 4 | 5 | export default { 6 | title: 'SharedStyles', 7 | }; 8 | 9 | const BlockWithMargin = styled.div` 10 | ${pageMargins}; 11 | height: 300px; 12 | background-color: #333; 13 | `; 14 | 15 | export const PageMargins = () => ( 16 |
    17 |

    18 | The box below has pageMargins styles applied to it which controls the horizontal 19 | padding and margin 20 |

    21 | 22 |
    23 | ); 24 | 25 | const BlockWithHoverEffect = styled.div` 26 | ${hoverEffect}; 27 | display: block; 28 | height: 300px; 29 | `; 30 | 31 | export const HoverEffectRest = () => ; 32 | 33 | export const HoverEffectHover = () => ; 34 | 35 | export const HoverEffectActive = () => ; 36 | -------------------------------------------------------------------------------- /src/components/Spinner.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Spinner } from './Spinner'; 3 | 4 | export default { 5 | title: 'Spinner', 6 | component: Spinner, 7 | }; 8 | 9 | export const Default = () => ( 10 |
    11 | 12 |
    13 | ); 14 | 15 | export const Inverse = () => ( 16 |
    17 | 18 |
    19 | ); 20 | 21 | export const InForm = () => ( 22 |
    23 | 24 |
    25 | ); 26 | 27 | export const Inline = () => ( 28 |
    29 | 30 |
    31 | ); 32 | 33 | export const InlinePositive = () => ( 34 |
    35 | 36 |
    37 | ); 38 | 39 | export const InlineNegative = () => ( 40 |
    41 | 42 |
    43 | ); 44 | 45 | export const InlineNeutral = () => ( 46 |
    47 | 48 |
    49 | ); 50 | 51 | export const InlineInverse = () => ( 52 |
    53 | 54 |
    55 | ); 56 | -------------------------------------------------------------------------------- /src/components/StoryLinkWrapper.tsx: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-extraneous-dependencies 2 | import { action } from '@storybook/addon-actions'; 3 | import React, { ComponentProps } from 'react'; 4 | 5 | // This is allows us to test whether the link works via the actions addon 6 | const fireClickAction = action('onLinkClick'); 7 | 8 | interface StoryLinkWrapperProps { 9 | to: string; 10 | } 11 | 12 | export const StoryLinkWrapper = ({ 13 | children, 14 | href, 15 | onClick, 16 | to, 17 | ...rest 18 | }: StoryLinkWrapperProps & ComponentProps<'a'>) => { 19 | const modifiedOnClick: React.DOMAttributes['onClick'] = (event) => { 20 | event.preventDefault(); 21 | onClick(event); 22 | fireClickAction(href || to); 23 | }; 24 | 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /src/components/Subheading.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Subheading } from './Subheading'; 4 | 5 | export default { 6 | title: 'Subheading', 7 | component: Subheading, 8 | }; 9 | 10 | export const Basic = () => Subheading; 11 | 12 | export const Muted = () => Subheading; 13 | -------------------------------------------------------------------------------- /src/components/Subheading.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react'; 2 | import { styled } from '@storybook/theming'; 3 | import { typography, color } from './shared/styles'; 4 | 5 | const StyledSubheading = styled('span', { shouldForwardProp: (prop) => !['muted'].includes(prop) })< 6 | Partial 7 | >` 8 | font-size: ${typography.size.s2 - 1}px; 9 | font-weight: ${typography.weight.extrabold}; 10 | letter-spacing: 0.35em; 11 | text-transform: uppercase; 12 | 13 | ${(props) => 14 | props.muted && 15 | ` 16 | color: ${color.dark}; 17 | display: block; 18 | line-height: ${typography.size.m1}px; 19 | margin-bottom: 12px; 20 | `} 21 | `; 22 | 23 | export const Subheading: FunctionComponent = ({ 24 | muted = false, 25 | ...props 26 | }: SubheadingProps) => ; 27 | 28 | export interface SubheadingProps { 29 | /** This prop lightens the Subheading color and increases the letter spacing */ 30 | muted?: boolean; 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Textarea.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { action } from '@storybook/addon-actions'; 3 | 4 | import { Textarea } from './Textarea'; 5 | 6 | const onChange = action('change'); 7 | 8 | export default { 9 | title: 'forms/Textarea', 10 | component: Textarea, 11 | }; 12 | 13 | export const playground = (args) =>