From 3ac60e35ce1359f460ca600e35e4ba1bc97afc61 Mon Sep 17 00:00:00 2001 From: Daniel Almaguer Date: Mon, 11 Nov 2019 14:12:21 -0600 Subject: [PATCH] feat(docs): add js/ts toggle in code previewer --- package.json | 3 ++ .../components/CodePreview/CodePreview.tsx | 53 +++++++++++++++---- .../components/CodeSnippet/CodeSnippet.tsx | 4 +- .../docs/components/Icons/JavascriptIcon.tsx | 31 +++++++++++ .../docs/components/Icons/TypescriptIcon.tsx | 31 +++++++++++ .../SnippetControls/SnippetControls.tsx | 15 +++++- .../components/StoryWrapper/StoryWrapper.tsx | 22 +++++--- packages/docs/package.json | 8 ++- packages/docs/pages/Dropdown/DropdownPage.tsx | 5 +- packages/docs/pages/Table/TablePage.tsx | 9 ++-- yarn.lock | 25 +++++++-- 11 files changed, 170 insertions(+), 36 deletions(-) create mode 100644 packages/docs/components/Icons/JavascriptIcon.tsx create mode 100644 packages/docs/components/Icons/TypescriptIcon.tsx diff --git a/package.json b/package.json index ab6c5144f..919427190 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,8 @@ "@commitlint/config-conventional": "^8.1.0", "husky": "^3.0.2", "lerna": "^3.16.4" + }, + "resolutions": { + "**/react-live/prism-react-renderer": "^1.0.2" } } diff --git a/packages/docs/components/CodePreview/CodePreview.tsx b/packages/docs/components/CodePreview/CodePreview.tsx index 5f9348db0..3bb5e74df 100644 --- a/packages/docs/components/CodePreview/CodePreview.tsx +++ b/packages/docs/components/CodePreview/CodePreview.tsx @@ -1,13 +1,15 @@ +import { transform } from '@babel/standalone'; import * as BigDesign from '@bigcommerce/big-design'; import * as BigDesignIcons from '@bigcommerce/big-design-icons'; import clipboardCopy from 'clipboard-copy'; -import { Language } from 'prism-react-renderer'; -import React, { useContext, useState } from 'react'; +import parser from 'prettier/parser-babylon'; +import prettier from 'prettier/standalone'; +import React, { useContext, useEffect, useState } from 'react'; import { LiveEditor, LivePreview, LiveProvider } from 'react-live'; import styled from 'styled-components'; import { SnippetControls } from '../SnippetControls'; -import { CodeEditorThemeContext } from '../StoryWrapper/StoryWrapper'; +import { CodeEditorContext, Language } from '../StoryWrapper/StoryWrapper'; import { StyledLiveError } from './styled'; @@ -18,29 +20,59 @@ const defaultScope = { styled, }; -function getInitialCode(children: React.ReactNode): string { +function getInitialCode(children: React.ReactNode, language: Language): string { if (typeof children !== 'string') { throw new Error(' children must be of type string'); } - return children; + if (language === 'tsx') { + return children; + } + + const code = transform(children, { + compact: false, + retainLines: true, + presets: [['typescript', { allExtensions: true, isTSX: true, jsxPragma: 'preserve' }]], + }).code; + + return prettier.format(code, { + parser: 'babel', + plugins: [parser], + printWidth: 100, + singleQuote: true, + trailingComma: 'all', + }); +} + +function transformCode(input: string): string { + try { + return transform(input, { + presets: [['typescript', { allExtensions: true, isTSX: true }], 'react'], + }).code; + } catch (e) { + return input; + } } export interface CodePreviewProps { scope?: { [key: string]: any }; - language?: Language; } export const CodePreview: React.FC = props => { - const { children, language } = props; - const initialCode = getInitialCode(children); + const { children } = props; + const { theme: editorTheme, language } = useContext(CodeEditorContext); + + const initialCode = getInitialCode(children, language); const [code, setCode] = useState(initialCode); - const { editorTheme } = useContext(CodeEditorThemeContext); const scope = { ...defaultScope, ...props.scope }; + useEffect(() => { + setCode(getInitialCode(children, language)); + }, [children, language, setCode, getInitialCode]); + return ( - + @@ -53,6 +85,5 @@ export const CodePreview: React.FC = props => { }; CodePreview.defaultProps = { - language: 'jsx', scope: defaultScope, }; diff --git a/packages/docs/components/CodeSnippet/CodeSnippet.tsx b/packages/docs/components/CodeSnippet/CodeSnippet.tsx index 14cd65715..ca1948e33 100644 --- a/packages/docs/components/CodeSnippet/CodeSnippet.tsx +++ b/packages/docs/components/CodeSnippet/CodeSnippet.tsx @@ -5,7 +5,7 @@ import React, { useContext } from 'react'; import { Editor } from 'react-live'; import { SnippetControls } from '../SnippetControls'; -import { CodeEditorThemeContext } from '../StoryWrapper/StoryWrapper'; +import { CodeEditorContext } from '../StoryWrapper/StoryWrapper'; interface EditorProps { language?: Language; @@ -41,7 +41,7 @@ function getCode(children: React.ReactNode) { export const CodeSnippet: React.FC = props => { const { children, language, showControls } = props; - const { editorTheme } = useContext(CodeEditorThemeContext); + const { theme: editorTheme } = useContext(CodeEditorContext); const code = getCode(children); return ( diff --git a/packages/docs/components/Icons/JavascriptIcon.tsx b/packages/docs/components/Icons/JavascriptIcon.tsx new file mode 100644 index 000000000..274b580a9 --- /dev/null +++ b/packages/docs/components/Icons/JavascriptIcon.tsx @@ -0,0 +1,31 @@ +// ********************************** +// Auto-generated file, do NOT modify +// ********************************** +import { createStyledIcon, IconProps } from '@bigcommerce/big-design-icons'; +import React from 'react'; + +const Icon = + /*#__PURE__*/ + React.memo>(({ title, theme, ...props }) => ( + + {title} + + + + )); + +export const JavascriptIcon = + /*#__PURE__*/ + createStyledIcon(Icon); diff --git a/packages/docs/components/Icons/TypescriptIcon.tsx b/packages/docs/components/Icons/TypescriptIcon.tsx new file mode 100644 index 000000000..6d3d20788 --- /dev/null +++ b/packages/docs/components/Icons/TypescriptIcon.tsx @@ -0,0 +1,31 @@ +// ********************************** +// Auto-generated file, do NOT modify +// ********************************** +import { createStyledIcon, IconProps } from '@bigcommerce/big-design-icons'; +import React from 'react'; + +const Icon = + /*#__PURE__*/ + React.memo>(({ title, theme, ...props }) => ( + + {title} + + + + )); + +export const TypescriptIcon = + /*#__PURE__*/ + createStyledIcon(Icon); diff --git a/packages/docs/components/SnippetControls/SnippetControls.tsx b/packages/docs/components/SnippetControls/SnippetControls.tsx index 48a392cdd..a3606063d 100644 --- a/packages/docs/components/SnippetControls/SnippetControls.tsx +++ b/packages/docs/components/SnippetControls/SnippetControls.tsx @@ -2,7 +2,9 @@ import { Button, Flex, Small } from '@bigcommerce/big-design'; import { AssignmentIcon, CheckIcon, InvertColorsIcon, RestoreIcon } from '@bigcommerce/big-design-icons'; import React, { useContext, useState } from 'react'; -import { CodeEditorThemeContext } from '../StoryWrapper/StoryWrapper'; +import { JavascriptIcon } from '../Icons/JavascriptIcon'; +import { TypescriptIcon } from '../Icons/TypescriptIcon'; +import { CodeEditorContext } from '../StoryWrapper/StoryWrapper'; import { StyledFlex } from './styled'; @@ -32,7 +34,7 @@ function onCopy(setIsCopying: (copying: boolean) => void, copyToClipboard: () => export const SnippetControls: React.FC = props => { const { copyToClipboard, helperText, resetCode } = props; const [isCopying, setIsCopying] = useState(false); - const { toggleEditorTheme } = useContext(CodeEditorThemeContext); + const { toggleTheme: toggleEditorTheme, setLanguage } = useContext(CodeEditorContext); return ( = props => { {helperText} + + + } /> {/* jsx-to-string:end */} diff --git a/packages/docs/pages/Table/TablePage.tsx b/packages/docs/pages/Table/TablePage.tsx index b268fe675..eb37eaf77 100644 --- a/packages/docs/pages/Table/TablePage.tsx +++ b/packages/docs/pages/Table/TablePage.tsx @@ -75,7 +75,7 @@ export default () => { {/* jsx-to-string:start */} {function Example() { - const [selectedItems, setSelectedItems] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); return ( { columns={columns} items={data} itemName="Products" - // @ts-ignore selectable={{ selectedItems, onSelectionChange: setSelectedItems, @@ -102,7 +101,7 @@ export default () => { const [currentPage, setCurrentPage] = React.useState(1); const [itemsPerPageOptions] = React.useState([5, 10, 20, 30]); const [itemsPerPage, setItemsPerPage] = React.useState(5); - const [currentItems, setCurrentItems] = React.useState([]); + const [currentItems, setCurrentItems] = React.useState([]); const onItemsPerPageChange = newRange => { setCurrentPage(1); @@ -114,7 +113,6 @@ export default () => { const lastItem = Math.min(maxItems, data.length); const firstItem = Math.max(0, maxItems - itemsPerPage); - // @ts-ignore setCurrentItems(data.slice(firstItem, lastItem)); }, [currentPage, data, itemsPerPage]); @@ -145,7 +143,7 @@ export default () => { {function Example() { const [items, setItems] = React.useState(data); const [columnHash, setColumnHash] = React.useState(''); - const [direction, setDirection] = React.useState('ASC'); + const [direction, setDirection] = React.useState<'ASC' | 'DESC'>('ASC'); const onSort = (newColumnHash, newDirection) => { setColumnHash(newColumnHash); @@ -163,7 +161,6 @@ export default () => { ]} items={items} itemName="Products" - // @ts-ignore sortable={{ columnHash, direction, diff --git a/yarn.lock b/yarn.lock index 9b665a08e..5546e62b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1025,6 +1025,11 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/standalone@^7.7.3": + version "7.7.3" + resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.7.3.tgz#9a4f7fc64876b11398e7371665969ccfdb0c9806" + integrity sha512-tVwxALbq7HGudHD0oZ6SQPkWM2L/Snw1vcbrtirDTqZsgswZ9xyuOOsSplqGtyazCCt9fidbymM47ZfYjASscg== + "@babel/template@^7.4.0": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" @@ -2520,6 +2525,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/prettier@^1.18.3": + version "1.18.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.18.3.tgz#64ff53329ce16139f17c3db9d3e0487199972cd8" + integrity sha512-48rnerQdcZ26odp+HOvDGX8IcUkYOCuMc2BodWYTe956MqkHlOGAG4oFQ83cjZ0a4GAgj7mb4GUClxYd2Hlodg== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -8693,7 +8703,12 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@^1.16.4, prettier@^1.17.1: +prettier@^1.16.4: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^1.17.1: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== @@ -8708,10 +8723,10 @@ pretty-format@^24.0.0, pretty-format@^24.8.0: ansi-styles "^3.2.0" react-is "^16.8.4" -prism-react-renderer@^0.1.0, prism-react-renderer@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-0.1.7.tgz#dc273d0cb6e4a498ba0775094e9a8b01a3ad2eaa" - integrity sha512-EhnM0sYfLK103ASK0ViSv0rta//ZGB0dBA9TiFyOvA+zOj5peLmGEG01sLEDwl9sMe+gSqncInafBe1VFTCMvA== +prism-react-renderer@^0.1.0, prism-react-renderer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.0.2.tgz#3bb9a6a42f76fc049b03266298c7068fdd4b7ea9" + integrity sha512-0++pJyRfu4v2OxI/Us/5RLui9ESDkTiLkVCtKuPZYdpB8UQWJpnJQhPrWab053XtsKW3oM0sD69uJ6N9exm1Ag== private@^0.1.6: version "0.1.8"