Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit 77c4ee0

Browse files
committed
docs(Examples): refactor SourceManager, add toggle for languages
1 parent e5fddd8 commit 77c4ee0

File tree

6 files changed

+203
-199
lines changed

6 files changed

+203
-199
lines changed

docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx

Lines changed: 86 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import Editor, { EDITOR_BACKGROUND_COLOR, EDITOR_GUTTER_COLOR } from 'docs/src/c
1313
import { babelConfig, importResolver } from 'docs/src/components/Playground/renderConfig'
1414
import ComponentControls from '../ComponentControls'
1515
import ComponentExampleTitle from './ComponentExampleTitle'
16-
import ContributionPrompt from '../ContributionPrompt'
17-
import SourceCodeManager, { SourceCodeType } from './SourceCodeManager'
16+
import SourceManager, { SourceManagerRenderProps } from './SourceCodeManager'
1817
import { ThemeInput, ThemePrepared } from 'src/themes/types'
1918
import { mergeThemeVariables } from '../../../../../src/lib/mergeThemes'
2019
import { ThemeContext } from '../../../context/theme-context'
2120
import CodeSnippet from '../../CodeSnippet'
22-
import formatCode from '../../../utils/formatCode'
2321

24-
export interface ComponentExampleProps extends RouteComponentProps<any, any> {
22+
export interface ComponentExampleProps
23+
extends RouteComponentProps<any, any>,
24+
SourceManagerRenderProps {
2525
title: React.ReactNode
2626
description?: React.ReactNode
2727
examplePath: string
@@ -34,7 +34,6 @@ interface ComponentExampleState {
3434
componentVariables: Object
3535
handleMouseLeave: () => void
3636
handleMouseMove: () => void
37-
sourceCode: string
3837
showCode: boolean
3938
showRtl: boolean
4039
showTransparent: boolean
@@ -48,19 +47,13 @@ const childrenStyle: React.CSSProperties = {
4847
maxWidth: pxToRem(500),
4948
}
5049

51-
const codeTypeApiButtonLabels: { [key in SourceCodeType]: string } = {
52-
normal: 'Children API',
53-
shorthand: 'Shorthand API',
54-
}
55-
5650
const disabledStyle = { opacity: 0.5, pointerEvents: 'none' }
5751

5852
/**
5953
* Renders a `component` and the raw `code` that produced it.
6054
* Allows toggling the the raw `code` code block.
6155
*/
6256
class ComponentExample extends React.Component<ComponentExampleProps, ComponentExampleState> {
63-
sourceCodeMgr: SourceCodeManager
6457
anchorName: string
6558
kebabExamplePath: string
6659
KnobsComponent: any
@@ -71,7 +64,6 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
7164
componentVariables: {},
7265
handleMouseLeave: _.noop,
7366
handleMouseMove: _.noop,
74-
sourceCode: '',
7567
showCode: false,
7668
showRtl: false,
7769
showTransparent: false,
@@ -97,14 +89,12 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
9789

9890
componentWillMount() {
9991
const { examplePath } = this.props
100-
this.sourceCodeMgr = new SourceCodeManager(examplePath)
10192
this.anchorName = examplePathToHash(examplePath)
10293

10394
this.setState({
10495
handleMouseLeave: this.handleMouseLeave,
10596
handleMouseMove: this.handleMouseMove,
10697
showCode: this.isActiveHash(),
107-
sourceCode: this.sourceCodeMgr.currentCode,
10898
})
10999
}
110100

@@ -216,16 +206,16 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
216206
if (title) _.invoke(this.context, 'onPassed', null, this.props)
217207
}
218208

219-
copyJSX = () => {
220-
copyToClipboard(this.state.sourceCode)
209+
copySourceCode = () => {
210+
copyToClipboard(this.props.currentCode)
211+
221212
this.setState({ copiedCode: true })
222213
setTimeout(() => this.setState({ copiedCode: false }), 1000)
223214
}
224215

225-
resetJSX = () => {
226-
if (this.sourceCodeMgr.originalCodeHasChanged && confirm('Lose your changes?')) {
227-
this.sourceCodeMgr.resetToOriginalCode()
228-
this.updateAndRenderSourceCode()
216+
resetSourceCode = () => {
217+
if (confirm('Lose your changes?')) {
218+
this.props.handleCodeReset()
229219
}
230220
}
231221

@@ -239,16 +229,6 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
239229

240230
hasKnobs = () => _.includes(knobsContext.keys(), this.getKnobsFilename())
241231

242-
renderExampleFromCode = (): JSX.Element => {
243-
const { sourceCode } = this.state
244-
245-
if (sourceCode == null) {
246-
return this.renderMissingExample()
247-
}
248-
249-
return <SourceRender.Consumer>{({ element }) => element}</SourceRender.Consumer>
250-
}
251-
252232
renderElement = (element: React.ReactElement<any>) => {
253233
const { examplePath } = this.props
254234
const { showRtl, componentVariables, themeName } = this.state
@@ -269,16 +249,6 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
269249
)
270250
}
271251

272-
renderMissingExample = (): JSX.Element => {
273-
const missingExamplePath = `./docs/src/examples/${this.sourceCodeMgr.currentPath}.tsx`
274-
return (
275-
<ContributionPrompt>
276-
Looks like we're need an example file at:
277-
<p>{missingExamplePath}</p>
278-
</ContributionPrompt>
279-
)
280-
}
281-
282252
handleKnobChange = knobs => {
283253
this.setState(prevState => ({
284254
knobs: {
@@ -312,85 +282,58 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
312282

313283
getDisplayName = () => this.props.examplePath.split('/')[1]
314284

315-
handleChangeCode = (sourceCode: string) => {
316-
this.sourceCodeMgr.currentCode = sourceCode
317-
this.updateAndRenderSourceCode()
285+
handleCodeApiChange = apiType => () => {
286+
this.props.handleCodeAPIChange(apiType)
318287
}
319288

320-
updateAndRenderSourceCode = () => {
321-
this.setState({ sourceCode: this.sourceCodeMgr.currentCode })
289+
handleCodeLanguageChange = language => () => {
290+
this.props.handleCodeLanguageChange(language)
322291
}
323292

324-
setApiCodeType = (codeType: SourceCodeType) => {
325-
this.sourceCodeMgr.codeType = codeType
326-
this.updateAndRenderSourceCode()
327-
}
328-
329-
renderApiCodeMenu = (): JSX.Element => {
330-
const { sourceCode } = this.state
331-
const lineCount = sourceCode && sourceCode.match(/^/gm)!.length
332-
333-
const menuItems = [SourceCodeType.shorthand, SourceCodeType.normal].map(codeType => {
334-
// we disable the menu button for Children API in case we don't have the example for it
335-
const disabled =
336-
codeType === SourceCodeType.normal && !this.sourceCodeMgr.isCodeValidForType(codeType)
337-
338-
return {
339-
active: this.sourceCodeMgr.codeType === codeType,
340-
disabled,
341-
key: codeType,
342-
onClick: this.setApiCodeType.bind(this, codeType),
343-
content: (
293+
renderAPIsMenu = (): JSX.Element => {
294+
const menuItems = _.map(this.props.codeAPIs, ({ active, enabled, name }, type) => (
295+
<Menu.Item
296+
active={active}
297+
content={
344298
<span>
345-
{codeTypeApiButtonLabels[codeType]}
346-
{disabled && <em> (not supported)</em>}
299+
{name}
300+
{enabled && <em> (not supported)</em>}
347301
</span>
348-
),
349-
}
350-
})
351-
352-
return (
353-
// match code editor background and gutter size and colors
354-
<div style={{ background: EDITOR_BACKGROUND_COLOR } as React.CSSProperties}>
355-
<div
356-
style={
357-
{
358-
borderLeft: `${lineCount > 9 ? 41 : 34}px solid ${EDITOR_GUTTER_COLOR}`,
359-
paddingBottom: '1rem',
360-
} as React.CSSProperties
361-
}
362-
>
363-
<Menu size="small" inverted secondary pointing items={menuItems} />
364-
</div>
365-
</div>
366-
)
367-
}
368-
369-
canBePrettified = () => {
370-
const { sourceCode } = this.state
302+
}
303+
disabled={!enabled}
304+
key={type}
305+
onClick={this.handleCodeApiChange(type)}
306+
/>
307+
))
371308

372-
try {
373-
return sourceCode !== formatCode(sourceCode)
374-
} catch (err) {
375-
return false
376-
}
309+
return <Menu.Menu>{menuItems}</Menu.Menu>
377310
}
378311

379-
handleFormat = () => {
380-
const { sourceCode } = this.state
312+
renderLanguagesMenu = (): JSX.Element => {
313+
const { currentLanguage } = this.props
381314

382-
this.handleChangeCode(formatCode(sourceCode))
315+
return (
316+
<Menu.Menu position="right">
317+
<Menu.Item
318+
active={currentLanguage === 'js'}
319+
content="JavaScript"
320+
onClick={this.handleCodeLanguageChange('js')}
321+
/>
322+
<Menu.Item
323+
active={currentLanguage === 'ts'}
324+
content="TypeScript"
325+
onClick={this.handleCodeLanguageChange('ts')}
326+
/>
327+
</Menu.Menu>
328+
)
383329
}
384330

385331
renderCodeEditorMenu = (): JSX.Element => {
332+
const { canBeFormatted, handleCodeFormat, wasChanged } = this.props
386333
const { copiedCode } = this.state
387-
const { originalCodeHasChanged, currentPath } = this.sourceCodeMgr
388-
const codeEditorStyle: React.CSSProperties = {
389-
position: 'absolute',
390-
margin: 0,
391-
top: '2px',
392-
right: '0.5rem',
393-
}
334+
335+
// TODO: !!!!
336+
const currentPath = ''
394337

395338
// get component name from file path:
396339
// elements/Button/Types/ButtonButtonExample
@@ -403,30 +346,30 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
403346
].join('')
404347

405348
return (
406-
<Menu size="small" secondary inverted text style={codeEditorStyle}>
349+
<Menu size="small" secondary inverted floated="right" text>
407350
<SourceRender.Consumer>
408351
{({ error }) => (
409352
<Menu.Item
410-
icon={(error && 'bug') || (this.canBePrettified() ? 'magic' : 'check')}
353+
icon={(error && 'bug') || (canBeFormatted ? 'magic' : 'check')}
411354
color={error ? 'red' : undefined}
412355
active={error}
413356
content="Prettier"
414-
onClick={this.handleFormat}
415-
style={!this.canBePrettified() ? disabledStyle : undefined}
357+
onClick={handleCodeFormat}
358+
style={!canBeFormatted ? disabledStyle : undefined}
416359
/>
417360
)}
418361
</SourceRender.Consumer>
419362
<Menu.Item
420-
style={!originalCodeHasChanged ? disabledStyle : undefined}
363+
style={!wasChanged ? disabledStyle : undefined}
421364
icon="refresh"
422365
content="Reset"
423-
onClick={this.resetJSX}
366+
onClick={this.resetSourceCode}
424367
/>
425368
<Menu.Item
426369
active={copiedCode} // to show the color
427370
icon={copiedCode ? { color: 'green', name: 'check' } : 'copy'}
428371
content="Copy"
429-
onClick={this.copyJSX}
372+
onClick={this.copySourceCode}
430373
/>
431374
<Menu.Item
432375
style={{ border: 'none' }}
@@ -440,20 +383,33 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
440383
}
441384

442385
renderJSX = () => {
443-
const { showCode, sourceCode } = this.state
386+
const { currentCode = '', handleCodeChange } = this.props
387+
const { showCode } = this.state
444388

445-
if (!showCode) return null
389+
const lineCount = currentCode.match(/^/gm)!.length
446390

447-
return (
448-
<div>
449-
{this.renderApiCodeMenu()}
391+
return showCode ? (
392+
// match code editor background and gutter size and colors
393+
<div style={{ background: EDITOR_BACKGROUND_COLOR } as React.CSSProperties}>
394+
<div
395+
style={
396+
{
397+
borderLeft: `${lineCount > 9 ? 41 : 34}px solid ${EDITOR_GUTTER_COLOR}`,
398+
paddingBottom: '2.6rem',
399+
} as React.CSSProperties
400+
}
401+
>
402+
<Menu attached="top" size="small" inverted secondary pointing>
403+
{this.renderAPIsMenu()}
404+
{this.renderLanguagesMenu()}
405+
</Menu>
450406

451-
<div>
452407
{this.renderCodeEditorMenu()}
453-
<Editor value={sourceCode} onChange={this.handleChangeCode} />
454408
</div>
409+
410+
<Editor value={currentCode} onChange={handleCodeChange} />
455411
</div>
456-
)
412+
) : null
457413
}
458414

459415
renderError = () => {
@@ -549,7 +505,7 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
549505
}
550506

551507
render() {
552-
const { children, description, title } = this.props
508+
const { children, currentCode, description, title } = this.props
553509
const {
554510
handleMouseLeave,
555511
handleMouseMove,
@@ -559,11 +515,11 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
559515
showRtl,
560516
showTransparent,
561517
showVariables,
562-
sourceCode,
563518
} = this.state
564519

565520
const isActive = this.isActiveHash() || this.isActiveState()
566-
const currentExamplePath = this.sourceCodeMgr.currentPath
521+
// TODO: !!!!!!!!!!
522+
const currentExamplePath = ''
567523

568524
const exampleStyle: React.CSSProperties = {
569525
position: 'relative',
@@ -624,7 +580,7 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
624580
<SourceRender
625581
babelConfig={babelConfig}
626582
knobs={knobs}
627-
source={sourceCode}
583+
source={currentCode}
628584
render={this.renderElement}
629585
renderHtml={showCode}
630586
resolver={importResolver}
@@ -647,7 +603,7 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
647603
}),
648604
}}
649605
>
650-
{this.renderExampleFromCode()}
606+
<SourceRender.Consumer>{({ element }) => element}</SourceRender.Consumer>
651607
</Grid.Column>
652608
)
653609
}}
@@ -668,7 +624,11 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
668624

669625
const ComponentExampleWithTheme = props => (
670626
<ThemeContext.Consumer>
671-
{({ themeName }) => <ComponentExample {...props} themeName={themeName} />}
627+
{({ themeName }) => (
628+
<SourceManager examplePath={props.examplePath}>
629+
{codeProps => <ComponentExample {...props} {...codeProps} themeName={themeName} />}
630+
</SourceManager>
631+
)}
672632
</ThemeContext.Consumer>
673633
)
674634

0 commit comments

Comments
 (0)