Add logic to show style props in docs

This commit is contained in:
Felix Kling 2015-03-19 14:05:07 -07:00
parent e811181034
commit d5f670d19c
5 changed files with 178 additions and 16 deletions

View File

@ -17,6 +17,7 @@ var React = require('React');
var Site = require('Site'); var Site = require('Site');
var slugify = require('slugify'); var slugify = require('slugify');
var styleReferencePattern = /^[^.]+\.propTypes\.style$/;
var ComponentDoc = React.createClass({ var ComponentDoc = React.createClass({
renderType: function(type) { renderType: function(type) {
@ -40,12 +41,20 @@ var ComponentDoc = React.createClass({
} }
if (type.name === 'custom') { if (type.name === 'custom') {
if (styleReferencePattern.test(type.raw)) {
var name = type.raw.substring(0, type.raw.indexOf('.'));
return <a href={slugify(name) + '.html#style'}>{name}#style</a>
}
if (type.raw === 'EdgeInsetsPropType') { if (type.raw === 'EdgeInsetsPropType') {
return '{top: number, left: number, bottom: number, right: number}'; return '{top: number, left: number, bottom: number, right: number}';
} }
return type.raw; return type.raw;
} }
if (type.name === 'stylesheet') {
return 'style';
}
if (type.name === 'func') { if (type.name === 'func') {
return 'function'; return 'function';
} }
@ -63,6 +72,8 @@ var ComponentDoc = React.createClass({
{this.renderType(prop.type)} {this.renderType(prop.type)}
</span>} </span>}
</Header> </Header>
{prop.type && prop.type.name === 'stylesheet' &&
this.renderStylesheetProps(prop.type.value)}
{prop.description && <Marked>{prop.description}</Marked>} {prop.description && <Marked>{prop.description}</Marked>}
</div> </div>
); );
@ -78,6 +89,41 @@ var ComponentDoc = React.createClass({
); );
}, },
renderStylesheetProps: function(stylesheetName) {
var style = this.props.content.styles[stylesheetName];
return (
<div className="compactProps">
{(style.composes || []).map((name) => {
var link;
if (name !== 'LayoutPropTypes') {
name = name.replace('StylePropTypes', '');
link =
<a href={slugify(name) + '.html#style'}>{name}#style...</a>;
} else {
link =
<a href={slugify(name) + '.html#proptypes'}>{name}...</a>;
}
return (
<div className="prop" key={name}>
<h6 className="propTitle">{link}</h6>
</div>
);
})}
{Object.keys(style.props).sort().map((name) =>
<div className="prop" key={name}>
<h6 className="propTitle">
{name}
{' '}
{style.props[name].type && <span className="propType">
{this.renderType(style.props[name].type)}
</span>}
</h6>
</div>
)}
</div>
);
},
renderProps: function(props, composes) { renderProps: function(props, composes) {
return ( return (
<div className="props"> <div className="props">
@ -197,7 +243,11 @@ var APIDoc = React.createClass({
var Autodocs = React.createClass({ var Autodocs = React.createClass({
render: function() { render: function() {
var metadata = this.props.metadata; var metadata = this.props.metadata;
var content = JSON.parse(this.props.children); var docs = JSON.parse(this.props.children);
var content = docs.type === 'component' || docs.type === 'style' ?
<ComponentDoc content={docs} /> :
<APIDoc content={docs} />;
return ( return (
<Site section="docs"> <Site section="docs">
<section className="content wrap documentationContent"> <section className="content wrap documentationContent">
@ -205,11 +255,9 @@ var Autodocs = React.createClass({
<div className="inner-content"> <div className="inner-content">
<a id="content" /> <a id="content" />
<h1>{metadata.title}</h1> <h1>{metadata.title}</h1>
{content.type === 'component' ? {content}
<ComponentDoc content={content} /> :
<APIDoc content={content} />}
<Marked> <Marked>
{content.fullDescription} {docs.fullDescription}
</Marked> </Marked>
<div className="docs-prevnext"> <div className="docs-prevnext">
{metadata.previous && <a className="docs-prev" href={metadata.previous + '.html#content'}>&larr; Prev</a>} {metadata.previous && <a className="docs-prev" href={metadata.previous + '.html#content'}>&larr; Prev</a>}

View File

@ -11,7 +11,7 @@
"mkdirp": "*", "mkdirp": "*",
"optimist": "0.6.0", "optimist": "0.6.0",
"react": "~0.12.0", "react": "~0.12.0",
"react-docgen": "^1.0.0", "react-docgen": "^1.1.0",
"react-page-middleware": "git://github.com/facebook/react-page-middleware.git", "react-page-middleware": "git://github.com/facebook/react-page-middleware.git",
"request": "*" "request": "*"
} }

View File

@ -0,0 +1,67 @@
"use strict";
var b = require('react-docgen/node_modules/recast').types.builders;
var docgen = require('react-docgen');
function stylePropTypeHandler(documentation, path) {
var propTypesPath = docgen.utils.getPropertyValuePath(path, 'propTypes');
if (!propTypesPath) {
return;
}
propTypesPath = docgen.utils.resolveToValue(propTypesPath);
if (!propTypesPath || propTypesPath.node.type !== 'ObjectExpression') {
return;
}
// Check if the there is a style prop
propTypesPath.get('properties').each(function(propertyPath) {
if (propertyPath.node.type !== 'Property' ||
docgen.utils.getPropertyName(propertyPath) !== 'style') {
return;
}
var valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
// If it's a call to StyleSheetPropType, do stuff
if (valuePath.node.type !== 'CallExpression' ||
valuePath.node.callee.name !== 'StyleSheetPropType') {
return;
}
// Get type of style sheet
var styleSheetModule = docgen.utils.resolveToModule(
valuePath.get('arguments', 0)
);
if (styleSheetModule) {
var propDescriptor = documentation.getPropDescriptor('style');
propDescriptor.type = {name: 'stylesheet', value: styleSheetModule};
}
});
}
function findExportedOrFirst(node, recast) {
return docgen.resolver.findExportedReactCreateClassCall(node, recast) ||
docgen.resolver.findAllReactCreateClassCalls(node, recast)[0];
}
function findExportedObject(ast, recast) {
var objPath;
recast.visit(ast, {
visitAssignmentExpression: function(path) {
if (!objPath && docgen.utils.isExportsOrModuleAssignment(path)) {
objPath = docgen.utils.resolveToValue(path.get('right'));
}
return false;
}
});
if (objPath) {
var b = recast.types.builders;
// This is a bit hacky, but easier than replicating the default propType
// handler. All this does is convert `{...}` to `{propTypes: {...}}`.
objPath.replace(b.objectExpression([
b.property('init', b.literal('propTypes'), objPath.node)
]));
}
return objPath;
}
exports.stylePropTypeHandler = stylePropTypeHandler;
exports.findExportedOrFirst = findExportedOrFirst;
exports.findExportedObject = findExportedObject;

View File

@ -7,7 +7,8 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
var docs = require('react-docgen'); var docgen = require('react-docgen');
var docgenHelpers = require('./docgenHelpers');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var slugify = require('../core/slugify'); var slugify = require('../core/slugify');
@ -21,7 +22,7 @@ function getNameFromPath(filepath) {
return filepath; return filepath;
} }
function componentsToMarkdown(type, json, filepath, i) { function componentsToMarkdown(type, json, filepath, i, styles) {
var componentName = getNameFromPath(filepath); var componentName = getNameFromPath(filepath);
var docFilePath = '../docs/' + componentName + '.md'; var docFilePath = '../docs/' + componentName + '.md';
@ -29,6 +30,9 @@ function componentsToMarkdown(type, json, filepath, i) {
json.fullDescription = fs.readFileSync(docFilePath).toString(); json.fullDescription = fs.readFileSync(docFilePath).toString();
} }
json.type = type; json.type = type;
if (styles) {
json.styles = styles;
}
var res = [ var res = [
'---', '---',
@ -84,20 +88,34 @@ var apis = [
'../Libraries/Vibration/VibrationIOS.ios.js', '../Libraries/Vibration/VibrationIOS.ios.js',
]; ];
var all = components.concat(apis); var styles = [
'../Libraries/StyleSheet/LayoutPropTypes.js',
'../Libraries/Components/View/ViewStylePropTypes.js',
'../Libraries/Text/TextStylePropTypes.js',
'../Libraries/Image/ImageStylePropTypes.js',
];
var all = components.concat(apis).concat(styles.slice(0, 1));
var styleDocs = styles.slice(1).reduce(function(docs, filepath) {
docs[path.basename(filepath).replace(path.extname(filepath), '')] =
docgen.parse(
fs.readFileSync(filepath),
docgenHelpers.findExportedObject,
[docgen.handlers.propTypeHandler]
);
return docs;
}, {});
module.exports = function() { module.exports = function() {
var i = 0; var i = 0;
return [].concat( return [].concat(
components.map(function(filepath) { components.map(function(filepath) {
var json = docs.parse( var json = docgen.parse(
fs.readFileSync(filepath), fs.readFileSync(filepath),
function(node, recast) { docgenHelpers.findExportedOrFirst,
return docs.resolver.findExportedReactCreateClassCall(node, recast) || docgen.defaultHandlers.concat(docgenHelpers.stylePropTypeHandler)
docs.resolver.findAllReactCreateClassCalls(node, recast)[0];
}
); );
return componentsToMarkdown('component', json, filepath, i++); return componentsToMarkdown('component', json, filepath, i++, styleDocs);
}), }),
apis.map(function(filepath) { apis.map(function(filepath) {
try { try {
@ -107,6 +125,14 @@ module.exports = function() {
var json = {}; var json = {};
} }
return componentsToMarkdown('api', json, filepath, i++); return componentsToMarkdown('api', json, filepath, i++);
}),
styles.slice(0, 1).map(function(filepath) {
var json = docgen.parse(
fs.readFileSync(filepath),
docgenHelpers.findExportedObject,
[docgen.handlers.propTypeHandler]
);
return componentsToMarkdown('style', json, filepath, i++);
}) })
); );
}; };

View File

@ -901,7 +901,13 @@ div[data-twttr-id] iframe {
background-color: hsl(198, 100%, 96%); background-color: hsl(198, 100%, 96%);
} }
.prop:nth-child(2n) { .compactProps {
border-left: 2px solid hsl(198, 100%, 94%);
margin-left: 20px;
padding-left: 5px;
}
.props > .prop:nth-child(2n) {
background-color: hsl(198, 100%, 94%); background-color: hsl(198, 100%, 94%);
} }
@ -910,15 +916,30 @@ div[data-twttr-id] iframe {
font-size: 16px; font-size: 16px;
} }
.compactProps .propTitle {
font-size: 14px;
margin-bottom: 0;
margin-top: 0;
}
.prop { .prop {
padding: 5px 10px; padding: 5px 10px;
} }
.compactProps .prop {
padding: 3px 10px;
}
.propType { .propType {
font-weight: normal; font-weight: normal;
font-size: 15px; font-size: 15px;
} }
.compactProps .propType {
font-weight: normal;
font-size: 13px;
}
#content { #content {
display: none; display: none;