Skip to content

Commit

Permalink
Merge pull request #265 from hipstersmoothie/static-props
Browse files Browse the repository at this point in the history
document static sub-components
  • Loading branch information
pvasek authored Jun 16, 2020
2 parents ef523b2 + 891c3e7 commit 1c8b621
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 35 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"react": "^16.4.2",
"source-map-support": "^0.5.6",
"tslint": "^5.11.0",
"typescript": "3.0.1"
"typescript": "3.1.6"
},
"files": [
"lib",
Expand Down
36 changes: 36 additions & 0 deletions src/__tests__/data/ColumnWithStaticComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';

interface LabelProps {
/** title description */
title: string;
}

/** Column.Label description */
const SubComponent = (props: LabelProps) => <div>My Property = {props.title}</div>;

/**
* Column properties.
*/
export interface IColumnProps {
/** prop1 description */
prop1: string;
}

/**
* Column description
*/
export class Column extends React.Component<IColumnProps, {}> {
public static Label = SubComponent;

/** Column.SubLabel description */
public static SubLabel() {
return <div>sub</div>
};

public render() {
const { prop1 } = this.props;
return <div>{prop1}</div>;
}
}

export default Column;
21 changes: 21 additions & 0 deletions src/__tests__/data/StatelessStaticComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from "react";

interface LabelProps {
/** title description */
title: string;
}

/** StatelessStaticComponents.Label description */
const SubComponent = (props: LabelProps) => <div>My Property = {props.title}</div>;

interface StatelessStaticComponentsProps {
/** myProp description */
myProp: string;
}

/** StatelessStaticComponents description */
export const StatelessStaticComponents = (props: StatelessStaticComponentsProps) => (
<div>My Property = {props.myProp}</div>
);

StatelessStaticComponents.Label = SubComponent;
32 changes: 27 additions & 5 deletions src/__tests__/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,10 @@ describe('parser', () => {
});

it('should parse simple react class component with picked properties', () => {
// we are not able to get correct descriptions for prop1,prop2
check('ColumnWithPick', {
Column: {
prop1: { type: 'string', required: false, description: '' },
prop2: { type: 'number', description: '' },
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
propx: { type: 'number' }
}
});
Expand Down Expand Up @@ -191,6 +190,29 @@ describe('parser', () => {
});
});

it('should parse static sub components', () => {
check('StatelessStaticComponents', {
StatelessStaticComponents: {
myProp: { type: 'string' }
},
'StatelessStaticComponents.Label': {
title: { type: 'string' }
}
});
});

it('should parse static sub components on class components', () => {
check('ColumnWithStaticComponents', {
Column: {
prop1: { type: 'string' }
},
'Column.Label': {
title: { type: 'string' }
},
'Column.SubLabel': {}
});
});

it('should parse react component with properties extended from an external .tsx file', () => {
check('ExtendsExternalPropsComponent', {
ExtendsExternalPropsComponent: {
Expand Down Expand Up @@ -457,13 +479,13 @@ describe('parser', () => {
as: { type: 'T', description: '' },
foo: {
description:
'The foo prop should not repeat the description \nThe foo prop should not repeat the description',
'The foo prop should not repeat the description\nThe foo prop should not repeat the description',
required: false,
type: '"red" | "blue"'
},
gap: {
description:
'The space between children \nYou cannot use gap when using a "space" justify property',
'The space between children\nYou cannot use gap when using a "space" justify property',
required: false,
type: 'number'
},
Expand Down
92 changes: 67 additions & 25 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import * as path from 'path';
import * as ts from 'typescript';

import { buildFilter } from './buildFilter';
import { symbol } from 'prop-types';
import { check } from './__tests__/testUtils';

// We'll use the currentDirectoryName to trim parent fileNames
const currentDirectoryPath = process.cwd();
Expand Down Expand Up @@ -238,10 +240,9 @@ export class Parser {
return null;
}

const type = this.checker.getTypeOfSymbolAtLocation(
exp,
exp.valueDeclaration || exp.declarations![0]
);
const declaration = exp.valueDeclaration || exp.declarations![0];
const type = this.checker.getTypeOfSymbolAtLocation(exp, declaration);

let commentSource = exp;
const typeSymbol = type.symbol || type.aliasSymbol;
const originalName = exp.getName();
Expand Down Expand Up @@ -273,6 +274,8 @@ export class Parser {
commentSource = exp;
}
}
} else if (type.symbol && (ts.isPropertyAccessExpression(declaration) || ts.isPropertyDeclaration(declaration))) {
commentSource = type.symbol;
}

// Skip over PropTypes that are exported
Expand Down Expand Up @@ -1087,10 +1090,10 @@ function parseWithProgramProvider(
const checker = program.getTypeChecker();

return filePaths
.map(filePath => program.getSourceFile(filePath))
.map((filePath) => program.getSourceFile(filePath))
.filter(
(sourceFile): sourceFile is ts.SourceFile =>
typeof sourceFile !== 'undefined'
typeof sourceFile !== "undefined"
)
.reduce<ComponentDoc[]>((docs, sourceFile) => {
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
Expand All @@ -1099,25 +1102,64 @@ function parseWithProgramProvider(
return docs;
}

Array.prototype.push.apply(
docs,
checker
.getExportsOfModule(moduleSymbol)
.map(exp =>
parser.getComponentInfo(
exp,
sourceFile,
parserOpts.componentNameResolver
)
)
.filter((comp): comp is ComponentDoc => comp !== null)
.filter((comp, index, comps) =>
comps
.slice(index + 1)
.every(innerComp => innerComp!.displayName !== comp!.displayName)
)
);
const components = checker.getExportsOfModule(moduleSymbol);
const componentDocs: ComponentDoc[] = [];

// First document all components
components.forEach((exp) => {
const doc = parser.getComponentInfo(
exp,
sourceFile,
parserOpts.componentNameResolver
);

if (doc) {
componentDocs.push(doc);
}

if (!exp.exports) {
return;
}

// Then document any static sub-components
exp.exports.forEach((symbol) => {
if (symbol.flags & ts.SymbolFlags.Prototype) {
return;
}

return docs;
if (symbol.flags & ts.SymbolFlags.Method) {
const signature = parser.getCallSignature(symbol);
const returnType = checker.typeToString(
signature.getReturnType()
);

if (returnType !== 'Element') {
return;
}
}

const doc = parser.getComponentInfo(
symbol,
sourceFile,
parserOpts.componentNameResolver
);

if (doc) {
componentDocs.push({
...doc,
displayName: `${exp.escapedName}.${symbol.escapedName}`,
});
}
});
});

return [
...docs,
...componentDocs.filter((comp, index, comps) =>
comps
.slice(index + 1)
.every((innerComp) => innerComp!.displayName !== comp!.displayName)
),
];
}, []);
}

0 comments on commit 1c8b621

Please sign in to comment.