diff --git a/ui/block-components/package.json b/ui/block-components/package.json index 7abffade3..c20398dc1 100644 --- a/ui/block-components/package.json +++ b/ui/block-components/package.json @@ -37,7 +37,6 @@ "global": "^4.3.2", "js-string-escape": "^1.0.1", "polished": "^3.4.4", - "prism-react-renderer": "^1.0.2", "qs": "^6.9.1", "react": "^16.8.3", "react-dom": "^16.8.3", diff --git a/ui/block-components/src/StorySource/TaggedSource.tsx b/ui/block-components/src/StorySource/TaggedSource.tsx index 91c0a70a0..42ccc63f2 100644 --- a/ui/block-components/src/StorySource/TaggedSource.tsx +++ b/ui/block-components/src/StorySource/TaggedSource.tsx @@ -8,7 +8,7 @@ import { } from '@component-controls/specification'; import { Styled } from 'theme-ui'; import { transparentize } from 'polished'; -import { Source, SourceProps } from '../Source'; +import { Source, SourceProps } from '@component-controls/components'; interface ArgumentLocations { name: string; @@ -73,7 +73,7 @@ export const TaggedSource: React.FC = ({ return ( {tokens.map((line, i: number) => (
diff --git a/ui/block-components/src/index.ts b/ui/block-components/src/index.ts index 294987038..43ffbc037 100644 --- a/ui/block-components/src/index.ts +++ b/ui/block-components/src/index.ts @@ -1,3 +1,2 @@ export * from './ControlsTable'; export * from './StorySource'; -export * from './Source'; diff --git a/ui/block-components/tsconfig.json b/ui/block-components/tsconfig.json index 25ce695d2..2ac1b8af2 100644 --- a/ui/block-components/tsconfig.json +++ b/ui/block-components/tsconfig.json @@ -8,6 +8,6 @@ "types": ["node", "jest"], "typeRoots": ["../../node_modules/@types", "node_modules/@types"] }, - "include": ["src/**/*", "src/typings.d.ts"], + "include": ["src/**/*", "src/typings.d.ts", "../components/Source"], "exclude": ["node_modules/**"] } \ No newline at end of file diff --git a/ui/blocks/src/ComponentSource/ComponentSource.tsx b/ui/blocks/src/ComponentSource/ComponentSource.tsx index 789d7de4c..64bde97c9 100644 --- a/ui/blocks/src/ComponentSource/ComponentSource.tsx +++ b/ui/blocks/src/ComponentSource/ComponentSource.tsx @@ -1,11 +1,11 @@ import React, { FC } from 'react'; import { ActionItem } from '@component-controls/components'; +import { useControlsContext, StoryInputProps } from '../BlocksContext'; import { + ThemeContext, Source as SourceBlock, SourceProps, -} from '@component-controls/block-components'; -import { useControlsContext, StoryInputProps } from '../BlocksContext'; -import { ThemeContext } from '@component-controls/components'; +} from '@component-controls/components'; import { repositoryActions } from '../utils/repositoryActions'; export type ComponentSourceProps = StoryInputProps & SourceProps; diff --git a/ui/blocks/src/StorySource/StorySource.tsx b/ui/blocks/src/StorySource/StorySource.tsx index de9f3884d..cc1e238e2 100644 --- a/ui/blocks/src/StorySource/StorySource.tsx +++ b/ui/blocks/src/StorySource/StorySource.tsx @@ -3,8 +3,8 @@ import { StorySource as BaseStorySource, StorySourceProps as BaseStorySourceProps, } from '@component-controls/block-components'; -import { useControlsContext, StoryInputProps } from '../BlocksContext'; import { ThemeContext } from '@component-controls/components'; +import { useControlsContext, StoryInputProps } from '../BlocksContext'; import { repositoryActions } from '../utils/repositoryActions'; export type StorySourceProps = StoryInputProps & BaseStorySourceProps; diff --git a/ui/components/package.json b/ui/components/package.json index f38ee8f4a..6cde328a8 100644 --- a/ui/components/package.json +++ b/ui/components/package.json @@ -29,9 +29,10 @@ }, "license": "MIT", "dependencies": { - "@mdx-js/react": "^1.5.7", "@primer/octicons-react": "^9.5.0", + "copy-to-clipboard": "^3.2.1", "markdown-to-jsx": "^6.11.0", + "prism-react-renderer": "^1.0.2", "react": "^16.8.3", "react-animate-height": "^2.0.20", "react-dom": "^16.8.3", diff --git a/ui/components/src/Markdown/Markdown.stories.tsx b/ui/components/src/Markdown/Markdown.stories.tsx index 3c722d0dc..af35d71bf 100644 --- a/ui/components/src/Markdown/Markdown.stories.tsx +++ b/ui/components/src/Markdown/Markdown.stories.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Markdown } from './Markdown'; +import { ThemeProvider } from '../ThemeContext'; export default { title: 'Components/Markdown', @@ -7,7 +8,8 @@ export default { }; export const simple = () => ( - {` + + {` # Header H1 ## Header H2 ### Header H3 @@ -15,5 +17,8 @@ export const simple = () => ( ##### Header H5 some text + +\`@theme-ui\` `} + ); diff --git a/ui/components/src/Markdown/Markdown.tsx b/ui/components/src/Markdown/Markdown.tsx index b622bfaf0..10d435df8 100644 --- a/ui/components/src/Markdown/Markdown.tsx +++ b/ui/components/src/Markdown/Markdown.tsx @@ -1,14 +1,28 @@ /* eslint-disable react/display-name */ import React, { FC } from 'react'; -import MarkdownToJSX from 'markdown-to-jsx'; -import { MDXProvider, Components } from '@mdx-js/react'; +import MarkdownToJSX, { MarkdownOptions } from 'markdown-to-jsx'; +import { SyntaxHighlighter } from '../SyntaxHighlighter'; export interface MarkdownProps { children: string; - components?: Components; + components?: MarkdownOptions['overrides']; } + +const defaultComponents: MarkdownOptions['overrides'] = { + code: SyntaxHighlighter, +}; + +/** + * MDX display component that works at run time + * uses `markdown-to-jsx` to compile MDX + */ export const Markdown: FC = ({ children, components }) => ( - - {children} - + + {children} + ); diff --git a/ui/block-components/src/Source/Source.stories.tsx b/ui/components/src/Source/Source.stories.tsx similarity index 97% rename from ui/block-components/src/Source/Source.stories.tsx rename to ui/components/src/Source/Source.stories.tsx index c6fb92206..450af08e8 100644 --- a/ui/block-components/src/Source/Source.stories.tsx +++ b/ui/components/src/Source/Source.stories.tsx @@ -3,7 +3,7 @@ import shadesOfPurple from 'prism-react-renderer/themes/shadesOfPurple'; import { Source, SourceProps } from './Source'; export default { - title: 'Blocks/Components/Source', + title: 'Components/Source', component: Source, }; diff --git a/ui/components/src/Source/Source.tsx b/ui/components/src/Source/Source.tsx new file mode 100644 index 000000000..9715e957e --- /dev/null +++ b/ui/components/src/Source/Source.tsx @@ -0,0 +1,49 @@ +/** @jsx jsx */ +/* eslint react/jsx-key: 0 */ +import { jsx } from 'theme-ui'; +import React, { FC, MouseEvent } from 'react'; +import copy from 'copy-to-clipboard'; +import { + SyntaxHighlighter, + SyntaxHighlighterProps, +} from '../SyntaxHighlighter'; +import { BlockContainer, BlockContainerProps } from '../BlockContainer'; + +export type SourceProps = SyntaxHighlighterProps & BlockContainerProps; +/** + * Source component used to display source code + * + */ +export const Source: FC = ({ + children = '', + actions, + title, + as = 'div', + ...props +}) => { + const [copied, setCopied] = React.useState(false); + const onCopy = (e: MouseEvent) => { + e.preventDefault(); + setCopied(true); + copy(children as string); + window.setTimeout(() => setCopied(false), 1500); + }; + + const actionsItems = Array.isArray(actions) ? [...actions] : []; + + actionsItems.push({ title: copied ? 'copied' : 'copy', onClick: onCopy }); + return ( + + + {children} + + + ); +}; diff --git a/ui/block-components/src/Source/index.ts b/ui/components/src/Source/index.ts similarity index 100% rename from ui/block-components/src/Source/index.ts rename to ui/components/src/Source/index.ts diff --git a/ui/components/src/SyntaxHighlighter/SyntaxHighlighter.stories.tsx b/ui/components/src/SyntaxHighlighter/SyntaxHighlighter.stories.tsx new file mode 100644 index 000000000..0d2fb1d77 --- /dev/null +++ b/ui/components/src/SyntaxHighlighter/SyntaxHighlighter.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import shadesOfPurple from 'prism-react-renderer/themes/shadesOfPurple'; +import { SyntaxHighlighter, SyntaxHighlighterProps } from './SyntaxHighlighter'; + +export default { + title: 'Components/SyntaxHighlighter', + component: SyntaxHighlighter, +}; + +const source = `import { Button } from 'theme-ui';`; +export const simpleSource = ({ children, dark }: SyntaxHighlighterProps) => { + return {children}; +}; + +simpleSource.story = { + parameters: { + controls: { + children: { + type: 'text', + rows: 10, + value: source, + data: null, + }, + dark: { type: 'boolean' }, + }, + }, +}; + +export const theme = () => ( + {source} +); diff --git a/ui/block-components/src/Source/Source.tsx b/ui/components/src/SyntaxHighlighter/SyntaxHighlighter.tsx similarity index 58% rename from ui/block-components/src/Source/Source.tsx rename to ui/components/src/SyntaxHighlighter/SyntaxHighlighter.tsx index b186fc298..c246dd4c2 100644 --- a/ui/block-components/src/Source/Source.tsx +++ b/ui/components/src/SyntaxHighlighter/SyntaxHighlighter.tsx @@ -1,9 +1,8 @@ /** @jsx jsx */ /* eslint react/jsx-key: 0 */ import { jsx } from 'theme-ui'; -import React, { FC, MouseEvent } from 'react'; -import { Styled } from 'theme-ui'; -import copy from 'copy-to-clipboard'; +import React, { FC } from 'react'; +import { Styled, Box } from 'theme-ui'; import Highlight, { defaultProps, PrismTheme, @@ -12,18 +11,13 @@ import Highlight, { import duotoneDark from 'prism-react-renderer/themes/duotoneDark'; import duotoneLight from 'prism-react-renderer/themes/duotoneLight'; -import { - BlockContainer, - BlockContainerProps, -} from '@component-controls/components'; - type RenderProps = Parameters[0]; -export interface SourceOwnProps { +export interface SyntaxHighlighterProps { /** - * source code to display + * source code to be displayed */ - children?: string; + children: React.ReactNode; /** * optional theme provided to the component */ @@ -43,33 +37,36 @@ export interface SourceOwnProps { * used to specify a "dark" color theme - applcable only if no custom theme prop is provided */ dark?: boolean; + + /** + * css styles for the container + */ + style?: React.CSSProperties; + + /** + * syntax container as element + */ + as?: React.ElementType; } -export type SourceProps = SourceOwnProps & BlockContainerProps; /** - * Source component used to display source code - * + * Syntax highlighter component */ -export const Source: FC = ({ +export const SyntaxHighlighter: FC = ({ children = '', language = 'jsx', theme: customTheme, renderFn, - actions, dark = false, - title, + style: propStyle, + as = 'span', }) => { - const [copied, setCopied] = React.useState(false); - const onCopy = (e: MouseEvent) => { - e.preventDefault(); - setCopied(true); - copy(children); - window.setTimeout(() => setCopied(false), 1500); - }; - - const actionsItems = Array.isArray(actions) ? [...actions] : []; + if (typeof children !== 'string') { + console.error( + 'Invalid children roperty passed to Source: must be a string', + ); + } - actionsItems.push({ title: copied ? 'copied' : 'copy', onClick: onCopy }); const theme = customTheme ? customTheme : dark ? duotoneDark : duotoneLight; const renderProps = typeof renderFn === 'function' @@ -77,27 +74,31 @@ export const Source: FC = ({ : ({ className, style, tokens, getLineProps, getTokenProps }: any) => ( {tokens.map((line: string[], i: number) => ( -
+ {line.map((token, key) => ( ))} -
+ ))}
); const props = { ...defaultProps, theme }; return ( - - - {renderProps} - - + + {renderProps} + ); }; diff --git a/ui/components/src/SyntaxHighlighter/index.ts b/ui/components/src/SyntaxHighlighter/index.ts new file mode 100644 index 000000000..0b9a0b886 --- /dev/null +++ b/ui/components/src/SyntaxHighlighter/index.ts @@ -0,0 +1 @@ +export * from './SyntaxHighlighter'; diff --git a/ui/components/src/index.ts b/ui/components/src/index.ts index 103e95b79..2a202a7b3 100644 --- a/ui/components/src/index.ts +++ b/ui/components/src/index.ts @@ -5,6 +5,7 @@ export * from './ExternalLink'; export * from './FlexContainer'; export * from './Markdown'; export * from './Popover'; +export * from './Source'; export * from './Subtitle'; export * from './Tabs'; export * from './ThemeContext';