Skip to content

Commit 0c84a87

Browse files
satya164ahmedlhanafy
authored andcommitted
Add docs generator (callstack#75)
* docs: start working on docs generator * docs: add a sidebar listing components * docs: use next/css instead of styled components * docs: properly setup routing * refactor: rework styling * docs: improve props styling * chore: fix flow errors * chore: styling tweaks
1 parent 57ca9aa commit 0c84a87

16 files changed

+355
-2
lines changed

.flowconfig

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
.*/Libraries/react-native/ReactNative.js
2525
.*/node_modules/jest-runtime/build/__tests__/.*
2626

27-
# Ignore duplicate modules under example/
27+
.*/node_modules/glamor/es6/CSSPropertyOperations/
28+
2829
.*/example/node_modules/fbjs
2930
.*/example/node_modules/react
3031
.*/example/node_modules/react-native

docs/.babelrc

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"presets": [
3+
"es2015",
4+
"stage-1",
5+
"react"
6+
]
7+
}

docs/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.next/
2+
pages/

docs/index.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* @flow */
2+
3+
import path from 'path';
4+
import fs from 'fs';
5+
import del from 'del';
6+
import { parse } from 'react-docgen';
7+
8+
const pages = path.join(__dirname, 'pages');
9+
10+
del.sync(pages);
11+
fs.mkdirSync(pages);
12+
13+
const components = fs.readFileSync(path.join(__dirname, '../src/index.js'))
14+
.toString()
15+
.split('\n')
16+
.map(line => line.split(' ').pop().replace(/('|;)/g, ''))
17+
.filter(line => line.startsWith('./components/'))
18+
.map(line => require.resolve(path.join(__dirname, '../src', line)));
19+
20+
const items = [];
21+
const index = [];
22+
23+
components.forEach(comp => {
24+
const componentName = comp.split('/').pop().split('.')[0];
25+
try {
26+
const componentInfo = parse(fs.readFileSync(comp).toString());
27+
items.push({ name: componentName, info: componentInfo });
28+
index.push(componentName);
29+
} catch (e) {
30+
console.log(`${componentName}: ${e.message}`);
31+
}
32+
});
33+
34+
items.forEach(it => {
35+
fs.writeFileSync(path.join(pages, `${it.name.toLowerCase()}.js`), `
36+
import React from 'react';
37+
import Page from '../templates/Page';
38+
import ComponentDocs from '../templates/ComponentDocs';
39+
40+
export default function ${it.name}Doc(props) {
41+
return (
42+
<Page {...props} pages={${JSON.stringify(index)}}>
43+
<ComponentDocs name='${it.name}' info={${JSON.stringify(it.info)}} />
44+
</Page>
45+
);
46+
}
47+
`);
48+
});
49+
50+
fs.writeFileSync(path.join(pages, 'index.js'), `
51+
import React from 'react';
52+
import Home from '../templates/Home';
53+
import Page from '../templates/Page';
54+
export default (props) => (
55+
<Page {...props} pages={${JSON.stringify(index)}}>
56+
<Home />
57+
</Page>
58+
);
59+
`);

docs/templates/ComponentDocs.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* @flow */
2+
3+
import React from 'react';
4+
import css from 'next/css';
5+
import mono from './styles/mono';
6+
import body from './styles/body';
7+
8+
const wrapper = css({
9+
padding: '24px 48px',
10+
});
11+
12+
const name = css({
13+
fontSize: '36px',
14+
margin: '16px 0 16px -24px',
15+
});
16+
17+
const propsHeader = css({
18+
fontSize: '24px',
19+
color: '#000',
20+
margin: '36px 0 16px',
21+
});
22+
23+
const propInfo = css({
24+
display: 'flex',
25+
flexDirection: 'row',
26+
alignItems: 'center',
27+
});
28+
29+
const propLabel = css({
30+
backgroundColor: '#f0f0f0',
31+
borderRadius: '3px',
32+
padding: '0 8px',
33+
margin: '8px 16px 8px 0',
34+
});
35+
36+
export default function ComponentDocs(props: any) {
37+
return (
38+
<div {...wrapper}>
39+
<div {...mono} {...name}>{`<${props.name} />`}</div>
40+
<div {...body}>{props.info.description}</div>
41+
<div {...mono} {...propsHeader}>Props</div>
42+
{Object.keys(props.info.props).map(prop => (
43+
<div {...propInfo} key={prop}>
44+
<div {...mono} {...propLabel}>{prop}: {props.info.props[prop].flowType.name}</div>
45+
<div {...body}>{props.info.props[prop].description}</div>
46+
</div>
47+
))}
48+
</div>
49+
);
50+
}

docs/templates/Home.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* @flow */
2+
3+
import React from 'react';
4+
import css from 'next/css';
5+
import mono from './styles/mono';
6+
import body from './styles/body';
7+
8+
const wrapper = css({
9+
display: 'flex',
10+
alignItems: 'center',
11+
justifyContent: 'center',
12+
height: '100vh',
13+
padding: '24px 48px',
14+
});
15+
16+
const inner = css({
17+
textAlign: 'center',
18+
});
19+
20+
const heading = css({
21+
fontSize: '36px',
22+
fontWeight: 'bold',
23+
margin: '16px 0 16px -24px',
24+
});
25+
26+
const description = css({
27+
fontSize: '24px',
28+
});
29+
30+
export default function Home() {
31+
return (
32+
<div {...wrapper}>
33+
<div {...inner}>
34+
<div {...mono} {...heading}>React Native Paper</div>
35+
<div {...body} {...description}>Documentation</div>
36+
</div>
37+
</div>
38+
);
39+
}

docs/templates/Page.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* @flow */
2+
3+
import React from 'react';
4+
import css from 'next/css';
5+
import Link from 'next/link';
6+
import mono from './styles/mono';
7+
8+
const wrapper = css({
9+
display: 'flex',
10+
flexDirection: 'row',
11+
height: '100vh',
12+
});
13+
14+
const sidebar = css({
15+
width: '240px',
16+
padding: '16px',
17+
backgroundColor: '#f0f0f0',
18+
overflow: 'auto',
19+
});
20+
21+
const content = css({
22+
flex: 1,
23+
height: '100%',
24+
overflow: 'auto',
25+
});
26+
27+
const separator = css({
28+
border: 0,
29+
backgroundColor: '#ddd',
30+
height: '1px',
31+
margin: '8px',
32+
});
33+
34+
const link = css({
35+
display: 'block',
36+
margin: '8px',
37+
textDecoration: 'none',
38+
opacity: 0.32,
39+
40+
':hover': {
41+
opacity: 1,
42+
},
43+
});
44+
45+
const active = css({
46+
opacity: 1,
47+
});
48+
49+
export default function Body({ url, pages, children }: any) {
50+
return (
51+
<div {...wrapper}>
52+
<div {...sidebar}>
53+
<Link href='/'>
54+
<a {...mono} {...link} {...(url.pathname === '/' ? active : null)}>
55+
Home
56+
</a>
57+
</Link>
58+
<hr {...separator} />
59+
{pages.map(page =>
60+
<Link key={page} href={`/${page.toLowerCase()}`}>
61+
<a {...mono} {...link} {...(url.pathname === '/' + page.toLowerCase() ? active : null)}>
62+
{page}
63+
</a>
64+
</Link>
65+
)}
66+
</div>
67+
<div {...content}>
68+
{children}
69+
</div>
70+
</div>
71+
);
72+
}

docs/templates/styles/body.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* @flow */
2+
3+
import css, { insertGlobal } from 'next/css';
4+
5+
insertGlobal(`
6+
html {
7+
box-sizing: border-box;
8+
}
9+
*, *:before, *:after {
10+
box-sizing: inherit;
11+
}
12+
html, body {
13+
margin: 0;
14+
padding: 0;
15+
color: '#555',
16+
}
17+
`);
18+
19+
const styles = css({
20+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
21+
lineHeight: 1.5,
22+
});
23+
24+
export default styles;

docs/templates/styles/mono.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* @flow */
2+
3+
import css from 'next/css';
4+
5+
const styles = css({
6+
fontFamily: '"Roboto Mono", "Operator Mono", "Fira Code", "Ubuntu Mono", "Droid Sans Mono", "Liberation Mono", "Source Code Pro", Menlo, Consolas, Courier, monospace',
7+
lineHeight: 2,
8+
color: '#000',
9+
});
10+
11+
export default styles;

package.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"scripts": {
77
"flow": "flow",
88
"lint": "eslint .",
9-
"test": "echo \"Error: no test specified\" && exit 1"
9+
"test": "echo \"Error: no test specified\" && exit 1",
10+
"docs:generate": "babel-node docs/index.js",
11+
"docs:dev": "cd docs && next"
1012
},
1113
"files": [
1214
"src/"
@@ -27,14 +29,22 @@
2729
"react-native-vector-icons": "*"
2830
},
2931
"devDependencies": {
32+
"babel-cli": "^6.18.0",
3033
"babel-eslint": "^7.1.0",
34+
"babel-preset-es2015": "^6.18.0",
35+
"babel-preset-react": "^6.16.0",
36+
"babel-preset-stage-1": "^6.16.0",
37+
"del": "^2.2.2",
3138
"eslint": "^3.9.1",
3239
"eslint-plugin-babel": "^3.3.0",
3340
"eslint-plugin-import": "^2.2.0",
3441
"eslint-plugin-react": "^6.6.0",
3542
"eslint-plugin-react-native": "^2.0.0",
3643
"flow-bin": "^0.33.0",
44+
"next": "^1.1.2",
3745
"react": "~15.3.2",
46+
"react-docgen": "^2.12.1",
47+
"react-dom": "^15.4.0",
3848
"react-native": "~0.37.0",
3949
"react-native-vector-icons": "~3.0.0"
4050
},

src/components/Button.js

+33
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,50 @@ type State = {
4040
elevation: Animated.Value;
4141
}
4242

43+
/**
44+
* Buttons communicate the action that will occur when the user touches them
45+
*/
4346
class Button extends PureComponent<void, Props, State> {
4447
static propTypes = {
48+
/**
49+
* Disable the button
50+
*/
4551
disabled: PropTypes.bool,
52+
/**
53+
* Shrink the button for a more compact look
54+
*/
4655
shrink: PropTypes.bool,
56+
/**
57+
* Add elevation to button, as opposed to default flat appearance
58+
*/
4759
raised: PropTypes.bool,
60+
/**
61+
* Use to primary color from theme
62+
*/
4863
primary: PropTypes.bool,
64+
/**
65+
* Text color of button, dark button will render ligh text and vice-versa
66+
*/
4967
dark: PropTypes.bool,
68+
/**
69+
* Whether to show a loading indicator
70+
*/
5071
loading: PropTypes.bool,
72+
/**
73+
* Icon name
74+
*/
5175
icon: PropTypes.string,
76+
/**
77+
* Custom text color for flat button or background color for raised button
78+
*/
5279
color: PropTypes.string,
80+
/**
81+
* Button text
82+
*/
5383
children: PropTypes.string.isRequired,
84+
/**
85+
* Function to execute on press
86+
*/
5487
onPress: PropTypes.func,
5588
style: View.propTypes.style,
5689
theme: PropTypes.object.isRequired,

src/components/Checkbox.js

+15
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ type Props = {
2020
theme: Theme;
2121
}
2222

23+
/**
24+
* Checkboxes allow the selection of multiple options from a set
25+
*/
2326
const Checkbox = (props: Props) => {
2427
const {
2528
checked,
@@ -60,9 +63,21 @@ const Checkbox = (props: Props) => {
6063
};
6164

6265
Checkbox.propTypes = {
66+
/**
67+
* Whether checkbox is checked
68+
*/
6369
checked: PropTypes.bool.isRequired,
70+
/**
71+
* Whether checkbox is disabled
72+
*/
6473
disabled: PropTypes.bool,
74+
/**
75+
* Function to execute on press
76+
*/
6577
onPress: PropTypes.func,
78+
/**
79+
* Custom color for checkbox
80+
*/
6681
color: PropTypes.string,
6782
theme: PropTypes.object.isRequired,
6883
};

0 commit comments

Comments
 (0)