From 9c5082e3f421743446330c8409f426d15316cdee Mon Sep 17 00:00:00 2001 From: Rakan Nimer Date: Fri, 6 Dec 2019 16:57:43 +0200 Subject: [PATCH] =?UTF-8?q?feat(gatsby-theme-docz):=20add=20optional=20ifr?= =?UTF-8?q?ame=20for=20preview=20and=20ed=E2=80=A6=20(#1305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(gatsby-theme-docz): add optional iframe for preview and editor * feat(gatsby-theme-docz): add useScopingInPlayground in theme config * docs: add example with styled components and scoping (#1306) - @lkuechler --- core/gatsby-theme-docz/package.json | 2 + .../components/Playground/IframeWrapper.js | 23 ++++ .../src/components/Playground/index.js | 115 +++++++++++------- core/gatsby-theme-docz/src/theme/index.js | 2 + .../.gitignore | 2 + .../README.md | 48 ++++++++ .../doczrc.js | 5 + .../package.json | 22 ++++ .../src/components/Alert.jsx | 28 +++++ .../src/components/Alert.mdx | 28 +++++ .../src/components/Button.jsx | 75 ++++++++++++ .../src/components/Button.mdx | 57 +++++++++ .../components/Playground/IframeWrapper.js | 32 +++++ .../src/index.mdx | 20 +++ yarn.lock | 31 +++++ 15 files changed, 449 insertions(+), 41 deletions(-) create mode 100644 core/gatsby-theme-docz/src/components/Playground/IframeWrapper.js create mode 100644 examples/with-styled-components-and-scoping/.gitignore create mode 100644 examples/with-styled-components-and-scoping/README.md create mode 100644 examples/with-styled-components-and-scoping/doczrc.js create mode 100644 examples/with-styled-components-and-scoping/package.json create mode 100644 examples/with-styled-components-and-scoping/src/components/Alert.jsx create mode 100644 examples/with-styled-components-and-scoping/src/components/Alert.mdx create mode 100644 examples/with-styled-components-and-scoping/src/components/Button.jsx create mode 100644 examples/with-styled-components-and-scoping/src/components/Button.mdx create mode 100644 examples/with-styled-components-and-scoping/src/gatsby-theme-docz/components/Playground/IframeWrapper.js create mode 100644 examples/with-styled-components-and-scoping/src/index.mdx diff --git a/core/gatsby-theme-docz/package.json b/core/gatsby-theme-docz/package.json index 2972328b6..2e6de2704 100644 --- a/core/gatsby-theme-docz/package.json +++ b/core/gatsby-theme-docz/package.json @@ -41,8 +41,10 @@ "prop-types": "^15.7.2", "re-resizable": "^6.1.0", "react-feather": "^2.0.3", + "react-frame-component": "^4.1.1", "react-helmet-async": "^1.0.4", "react-live": "^2.2.1", + "react-resize-detector": "^4.2.1", "rehype-docz": "^2.1.0", "rehype-slug": "^2.0.3", "remark-docz": "^2.1.0", diff --git a/core/gatsby-theme-docz/src/components/Playground/IframeWrapper.js b/core/gatsby-theme-docz/src/components/Playground/IframeWrapper.js new file mode 100644 index 000000000..71313a103 --- /dev/null +++ b/core/gatsby-theme-docz/src/components/Playground/IframeWrapper.js @@ -0,0 +1,23 @@ +/** @jsx jsx */ +import { jsx } from 'theme-ui' +import Iframe from 'react-frame-component' + +import * as styles from './styles' + +const CLEAR_PADDING = `` +const INITIAL_IFRAME_CONTENT = ` ${CLEAR_PADDING}
` + +export const IframeWrapper = ({ children, height, style = {} }) => { + return ( + + ) +} diff --git a/core/gatsby-theme-docz/src/components/Playground/index.js b/core/gatsby-theme-docz/src/components/Playground/index.js index 5372a54e8..2805ffe6c 100644 --- a/core/gatsby-theme-docz/src/components/Playground/index.js +++ b/core/gatsby-theme-docz/src/components/Playground/index.js @@ -1,60 +1,79 @@ /** @jsx jsx */ import { jsx } from 'theme-ui' -import { useState } from 'react' +import React from 'react' import { useConfig } from 'docz' import { LiveProvider, LiveError, LivePreview, LiveEditor } from 'react-live' import { Resizable } from 're-resizable' import copy from 'copy-text-to-clipboard' +import ReactResizeDetector from 'react-resize-detector' +import { IframeWrapper } from './IframeWrapper' import { usePrismTheme } from '~utils/theme' import * as styles from './styles' import * as Icons from '../Icons' +const getResizableProps = (width, setWidth) => ({ + minWidth: 260, + maxWidth: '100%', + size: { + width: width, + height: 'auto', + }, + style: { + margin: 0, + marginRight: 'auto', + }, + enable: { + top: false, + right: true, + bottom: false, + left: false, + topRight: false, + bottomRight: false, + bottomLeft: false, + topLeft: false, + }, + onResizeStop: (e, direction, ref) => { + setWidth(ref.style.width) + }, +}) + const transformCode = code => { if (code.startsWith('()') || code.startsWith('class')) return code return `${code}` } -export const Playground = ({ code, scope, language }) => { +export const Playground = ({ code, scope, language, useScoping = false }) => { const { - themeConfig: { showPlaygroundEditor, showLiveError, showLivePreview }, + themeConfig: { + showPlaygroundEditor, + showLiveError, + showLivePreview, + useScopingInPlayground, + }, } = useConfig() + + const [previewHeight, setPreviewHeight] = React.useState() + const [editorHeight, setEditorHeight] = React.useState() + const Wrapper = React.useCallback( + useScoping || useScopingInPlayground + ? props => {props.children} + : props => ( +
{props.children}
+ ), + [useScoping] + ) + // Makes sure scope is only given on mount to avoid infinite re-render on hot reloads - const [scopeOnMount] = useState(scope) + const [scopeOnMount] = React.useState(scope) const theme = usePrismTheme() - const [showingCode, setShowingCode] = useState(() => showPlaygroundEditor) - const [width, setWidth] = useState(() => '100%') + const [showingCode, setShowingCode] = React.useState(showPlaygroundEditor) + const [width, setWidth] = React.useState('100%') + const resizableProps = getResizableProps(width, setWidth) const copyCode = () => copy(code) - const toggleCode = () => setShowingCode(s => !s) - const resizableProps = { - minWidth: 260, - maxWidth: '100%', - size: { - width, - height: 'auto', - }, - style: { - margin: 0, - marginRight: 'auto', - }, - enable: { - top: false, - right: true, - bottom: false, - left: false, - topRight: false, - bottomRight: false, - bottomLeft: false, - topLeft: false, - }, - onResizeStop: (e, direction, ref) => { - setWidth(ref.style.width) - }, - } - return ( { theme={theme} >
-
+ {showLivePreview && ( - + )} -
+ { + setPreviewHeight(height) + }} + /> +
+ {showingCode && ( + +
+ +
+ { + setEditorHeight(height) + }} + /> +
+ )} {showLiveError && ( )} - {showingCode && ( -
- -
- )}
) diff --git a/core/gatsby-theme-docz/src/theme/index.js b/core/gatsby-theme-docz/src/theme/index.js index a32950264..18a6c7ad4 100644 --- a/core/gatsby-theme-docz/src/theme/index.js +++ b/core/gatsby-theme-docz/src/theme/index.js @@ -21,6 +21,8 @@ export default merge(typography, { showDarkModeSwitch: true, // Display edit this page button on every page showMarkdownEditButton: true, + // Wrap the playground editor and preview in iframes to avoid style/script collisions + useScopingInPlayground: false, colors: { ...modes.light, modes: { diff --git a/examples/with-styled-components-and-scoping/.gitignore b/examples/with-styled-components-and-scoping/.gitignore new file mode 100644 index 000000000..fdb33ee9a --- /dev/null +++ b/examples/with-styled-components-and-scoping/.gitignore @@ -0,0 +1,2 @@ +.docz +node_modules \ No newline at end of file diff --git a/examples/with-styled-components-and-scoping/README.md b/examples/with-styled-components-and-scoping/README.md new file mode 100644 index 000000000..95e7d94c4 --- /dev/null +++ b/examples/with-styled-components-and-scoping/README.md @@ -0,0 +1,48 @@ +# Docz Styled Components Example + +## Using `create-docz-app` + +```sh +npx create-docz-app docz-app-styled-docz --example with-styled-components-and-scoping +# or +yarn create docz-app docz-app-styled-docz --example with-styled-components-and-scoping +``` + +## Download manually + +```sh +curl https://codeload.github.com/doczjs/docz/tar.gz/master | tar -xz --strip=2 docz-master/examples/with-styled-components-and-scoping +mv with-styled-components-and-scoping docz-example-styled-docz +``` + +## Setup + +```sh +yarn # npm i +``` + +## Run + +```sh +yarn dev # npm run dev +``` + +## Build + +```sh +yarn build # npm run build +``` + +## Serve built app + +```sh +yarn serve # npm run serve +``` + +## Deploy + +```sh +yarn deploy +``` + +Note that by default `docz` generates the output site in `.docz/public` to change that add a `dest` field to your `doczrc.js` with the path you want to generate the code in. diff --git a/examples/with-styled-components-and-scoping/doczrc.js b/examples/with-styled-components-and-scoping/doczrc.js new file mode 100644 index 000000000..2454bcfdd --- /dev/null +++ b/examples/with-styled-components-and-scoping/doczrc.js @@ -0,0 +1,5 @@ +export default { + themeConfig: { + useScopingInPlayground: true, + }, +} diff --git a/examples/with-styled-components-and-scoping/package.json b/examples/with-styled-components-and-scoping/package.json new file mode 100644 index 000000000..223abee45 --- /dev/null +++ b/examples/with-styled-components-and-scoping/package.json @@ -0,0 +1,22 @@ +{ + "private": true, + "name": "docz-example-styled-components-with-scoping", + "version": "2.0.0-rc.41", + "license": "MIT", + "files": [ + "src/", + "package.json" + ], + "scripts": { + "dev": "docz dev", + "build": "docz build" + }, + "dependencies": { + "docz": "latest", + "prop-types": "^15.7.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "react-frame-component": "^4.1.1", + "styled-components": "^4.3.2" + } +} diff --git a/examples/with-styled-components-and-scoping/src/components/Alert.jsx b/examples/with-styled-components-and-scoping/src/components/Alert.jsx new file mode 100644 index 000000000..644e5832c --- /dev/null +++ b/examples/with-styled-components-and-scoping/src/components/Alert.jsx @@ -0,0 +1,28 @@ +import React from 'react' +import styled from 'styled-components' +import t from 'prop-types' + +const kinds = { + info: '#5352ED', + positive: '#2ED573', + negative: '#FF4757', + warning: '#FFA502', +} + +const AlertStyled = styled('div')` + padding: 15px 20px; + background: white; + border-radius: 3px; + color: white; + background: ${({ kind = 'info' }) => kinds[kind]}; +` + +export const Alert = props => + +Alert.propTypes = { + kind: t.oneOf(['info', 'positive', 'negative', 'warning']), +} + +Alert.defaultProps = { + kind: 'info', +} diff --git a/examples/with-styled-components-and-scoping/src/components/Alert.mdx b/examples/with-styled-components-and-scoping/src/components/Alert.mdx new file mode 100644 index 000000000..c0eab1417 --- /dev/null +++ b/examples/with-styled-components-and-scoping/src/components/Alert.mdx @@ -0,0 +1,28 @@ +--- +name: Alert +menu: Components +--- + +import { Playground, Props } from 'docz' +import { Alert } from './Alert' + +# Alert + +## Properties + + + +## Basic usage + + + Some message + + +## Using different kinds + + + Some message + Some message + Some message + Some message + diff --git a/examples/with-styled-components-and-scoping/src/components/Button.jsx b/examples/with-styled-components-and-scoping/src/components/Button.jsx new file mode 100644 index 000000000..b89a5342c --- /dev/null +++ b/examples/with-styled-components-and-scoping/src/components/Button.jsx @@ -0,0 +1,75 @@ +import React from 'react' +import styled from 'styled-components' +import t from 'prop-types' + +const scales = { + small: ` + padding: 5px 10px; + font-size: 14px; + `, + normal: ` + padding: 10px 20px; + font-size: 16px; + `, + big: ` + padding: 20px 30px; + font-size: 18px; + `, +} + +const kind = outline => (bg, color) => { + const boxShadowColor = outline ? bg : 'transparent' + const backgroundColor = outline ? 'transparent' : bg + + return ` + background: ${backgroundColor}; + box-shadow: inset 0 0 0 1px ${boxShadowColor}; + color: ${outline ? bg : color}; + transition: all .3s; + + &:hover { + box-shadow: inset 0 0 0 1000px ${boxShadowColor}; + color: ${color}; + } + ` +} + +const kinds = outline => { + const get = kind(outline) + + return { + primary: get('#1FB6FF', 'white'), + secondary: get('#5352ED', 'white'), + cancel: get('#FF4949', 'white'), + dark: get('#273444', 'white'), + gray: get('#8492A6', 'white'), + } +} + +const getScale = ({ scale = 'normal' }) => scales[scale] +const getKind = ({ kind = 'primary', outline = false }) => kinds(outline)[kind] + +const ButtonStyled = styled('button')` + ${getKind}; + ${getScale}; + cursor: pointer; + margin: 3px 5px; + border: none; + border-radius: 3px; +` + +export const Button = ({ children, ...props }) => ( + {children} +) + +Button.propTypes = { + scales: t.oneOf(['small', 'normal', 'big']), + kind: t.oneOf(['primary', 'secondary', 'cancel', 'dark', 'gray']), + outline: t.bool, +} + +Button.defaultProps = { + scales: 'normal', + kind: 'primary', + outline: false, +} diff --git a/examples/with-styled-components-and-scoping/src/components/Button.mdx b/examples/with-styled-components-and-scoping/src/components/Button.mdx new file mode 100644 index 000000000..8fc031a68 --- /dev/null +++ b/examples/with-styled-components-and-scoping/src/components/Button.mdx @@ -0,0 +1,57 @@ +--- +name: Button +menu: Components +--- + +import { Playground, Props } from 'docz' +import { Button } from './Button' + +# Button + +Buttons make common actions more obvious and help users more easily perform them. Buttons use labels and sometimes icons to communicate the action that will occur when the user touches them. + +### Best practices + +- Group buttons logically into sets based on usage and importance. +- Ensure that button actions are clear and consistent. +- The main action of a group set can be a primary button. +- Select a single button variation and do not mix them. + +## Properties + + + +## Basic usage + + + + + +## With different sizes + + + + + + + +## With different colors + + + + + + + + + +## Outlined + + + + + + + + + diff --git a/examples/with-styled-components-and-scoping/src/gatsby-theme-docz/components/Playground/IframeWrapper.js b/examples/with-styled-components-and-scoping/src/gatsby-theme-docz/components/Playground/IframeWrapper.js new file mode 100644 index 000000000..998f62fdc --- /dev/null +++ b/examples/with-styled-components-and-scoping/src/gatsby-theme-docz/components/Playground/IframeWrapper.js @@ -0,0 +1,32 @@ +/** @jsx jsx */ +import { jsx } from 'theme-ui' +import * as React from 'react' +import Iframe, { FrameContextConsumer } from 'react-frame-component' + +import { StyleSheetManager } from 'styled-components' + +import * as styles from 'gatsby-theme-docz/src/components/Playground/styles' + +const CLEAR_PADDING = `` +const INITIAL_IFRAME_CONTENT = ` ${CLEAR_PADDING}
` + +export const IframeWrapper = ({ children, height, style = {} }) => { + return ( + + ) +} diff --git a/examples/with-styled-components-and-scoping/src/index.mdx b/examples/with-styled-components-and-scoping/src/index.mdx new file mode 100644 index 000000000..cdffa6654 --- /dev/null +++ b/examples/with-styled-components-and-scoping/src/index.mdx @@ -0,0 +1,20 @@ +--- +name: Getting Started +route: / +--- + +# Getting Started + +Design systems enable teams to build better products faster by making design reusable—reusability makes scale possible. This is the heart and primary value of design systems. A design system is a collection of reusable components, guided by clear standards, that can be assembled together to build any number of applications. + +Regardless of the technologies and tools behind them, a successful design system follows these guiding principles: + +- **It’s consistent**. The way components are built and managed follows a predictable pattern. +- **It’s self-contained**. Your design system is treated as a standalone dependency. +- **It’s reusable**. You’ve built components so they can be reused in many contexts. +- **It’s accessible**. Applications built with your design system are usable by as many people as possible, no matter how they access the web. +- **It’s robust**. No matter the product or platform to which your design system is applied, it should perform with grace and minimal bugs. + +## Consistency + +Your first, most important task when starting out is to define the rules of your system, document them, and ensure that everyone follows them. When you have clearly documented code standards and best practices in place, designers and developers from across your organization can easily use and, more importantly, contribute to your design system. diff --git a/yarn.lock b/yarn.lock index 299d5e1b9..b4ed78497 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12535,6 +12535,11 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" +lodash-es@^4.17.15: + version "4.17.15" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -15653,6 +15658,11 @@ quick-lru@^1.0.0: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +raf-schd@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" + integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -15840,6 +15850,11 @@ react-feather@^2.0.3: dependencies: prop-types "^15.7.2" +react-frame-component@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/react-frame-component/-/react-frame-component-4.1.1.tgz#ea8f7c518ef6b5ad72146dd1f648752369826894" + integrity sha512-NfJp90AvYA1R6+uSYafQ+n+UM2HjHqi4WGHeprVXa6quU9d8o6ZFRzQ36uemY82dlkZFzf2jigFx6E4UzNFajA== + react-helmet-async@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97" @@ -15899,6 +15914,17 @@ react-reconciler@^0.20.0: prop-types "^15.6.2" scheduler "^0.13.6" +react-resize-detector@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-4.2.1.tgz#8982b74c3e1cf949afaa3c41050458c87b033982" + integrity sha512-ZfPMBPxXi0o3xox42MIEtz84tPSVMW9GgwLHYvjVXlFM+OkNzbeEtpVSV+mSTJmk4Znwomolzt35zHN9LNBQMQ== + dependencies: + lodash "^4.17.15" + lodash-es "^4.17.15" + prop-types "^15.7.2" + raf-schd "^4.0.2" + resize-observer-polyfill "^1.5.1" + react-simple-code-editor@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.10.0.tgz#73e7ac550a928069715482aeb33ccba36efe2373" @@ -16708,6 +16734,11 @@ reserved-words@^0.1.2: resolved "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE= +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-1.0.0.tgz#4eaeea41ed040d1702457df64a42b2b07d246f9f"