Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gatsby-theme-docz): add optional iframe for preview and editor #1305

Merged
merged 6 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/gatsby-theme-docz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 23 additions & 0 deletions core/gatsby-theme-docz/src/components/Playground/IframeWrapper.js
Original file line number Diff line number Diff line change
@@ -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 = `<style> body { padding: 0; margin: 0; } </style>`
const INITIAL_IFRAME_CONTENT = `<!DOCTYPE html><html><head> ${CLEAR_PADDING} </head><body><div></div></body></html>`

export const IframeWrapper = ({ children, height, style = {} }) => {
return (
<Iframe
initialContent={INITIAL_IFRAME_CONTENT}
sx={{
...styles.previewInner(),
height,
...style,
}}
>
{children}
</Iframe>
)
}
115 changes: 74 additions & 41 deletions core/gatsby-theme-docz/src/components/Playground/index.js
Original file line number Diff line number Diff line change
@@ -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 `<React.Fragment>${code}</React.Fragment>`
}

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 => <IframeWrapper {...props}>{props.children}</IframeWrapper>
: props => (
<div sx={styles.previewInner(showingCode)}>{props.children}</div>
),
[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 (
<Resizable {...resizableProps} data-testid="playground">
<LiveProvider
Expand All @@ -65,11 +84,17 @@ export const Playground = ({ code, scope, language }) => {
theme={theme}
>
<div sx={styles.previewWrapper}>
<div sx={styles.previewInner(showingCode)}>
<Wrapper height={previewHeight}>
{showLivePreview && (
<LivePreview sx={styles.preview} data-testid="live-preview" />
<LivePreview style={styles.preview} data-testid="live-preview" />
)}
</div>
<ReactResizeDetector
handleHeight
onResize={(width, height) => {
setPreviewHeight(height)
}}
/>
</Wrapper>
<div sx={styles.buttons}>
<button sx={styles.button} onClick={copyCode}>
<Icons.Clipboard size={12} />
Expand All @@ -79,14 +104,22 @@ export const Playground = ({ code, scope, language }) => {
</button>
</div>
</div>
{showingCode && (
<Wrapper height={editorHeight}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am i correct in the assumption that this was done to solve this issue #1271 ?

Because now that you are wrapping the component in a iframe the encapsulation of the editor should not be needed any more. Am i missing something? :)

Copy link
Contributor Author

@rakannimer rakannimer Dec 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am i correct in the assumption that this was done to solve this issue #1271 ?

Yep that's exactly why ! Initially wanted to only wrap the preview.

I'm wrapping the preview and the editor each in an iframe, not wrapping the whole Playground with an iframe so it should be necessary if I'm not mistaken.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue was that the css from the component inside the preview was styling the code area of the preview. This should not be possible any more after the component preview is wrapped in a iframe.

<div style={styles.editor(theme)}>
<LiveEditor data-testid="live-editor" />
</div>
<ReactResizeDetector
handleHeight
onResize={(width, height) => {
setEditorHeight(height)
}}
/>
</Wrapper>
)}
{showLiveError && (
<LiveError sx={styles.error} data-testid="live-error" />
)}
{showingCode && (
<div sx={styles.editor(theme)}>
<LiveEditor data-testid="live-editor" />
</div>
)}
</LiveProvider>
</Resizable>
)
Expand Down
2 changes: 2 additions & 0 deletions core/gatsby-theme-docz/src/theme/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
2 changes: 2 additions & 0 deletions examples/with-styled-components-and-scoping/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.docz
node_modules
48 changes: 48 additions & 0 deletions examples/with-styled-components-and-scoping/README.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions examples/with-styled-components-and-scoping/doczrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
themeConfig: {
useScopingInPlayground: true,
},
}
22 changes: 22 additions & 0 deletions examples/with-styled-components-and-scoping/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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 => <AlertStyled {...props} />

Alert.propTypes = {
kind: t.oneOf(['info', 'positive', 'negative', 'warning']),
}

Alert.defaultProps = {
kind: 'info',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: Alert
menu: Components
---

import { Playground, Props } from 'docz'
import { Alert } from './Alert'

# Alert

## Properties

<Props of={Alert} />

## Basic usage

<Playground>
<Alert>Some message</Alert>
</Playground>

## Using different kinds

<Playground>
<Alert kind="info">Some message</Alert>
<Alert kind="positive">Some message</Alert>
<Alert kind="negative">Some message</Alert>
<Alert kind="warning">Some message</Alert>
</Playground>
Loading