Merge pull request #8896 from storybookjs/docgen-lib-maintenance

Docgen lib maintenance
This commit is contained in:
Patrick Lafrance 2019-11-20 10:25:40 -05:00 committed by GitHub
commit 4da8cf433d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 175 additions and 130 deletions

View File

@ -2,7 +2,7 @@ import React, { FunctionComponent, useContext } from 'react';
import { Description, DescriptionProps as PureDescriptionProps } from '@storybook/components';
import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, CURRENT_SELECTION, DescriptionSlot } from './shared';
import { str } from '../lib/docgen/utils';
import { str } from '../lib/docgen';
export enum DescriptionType {
INFO = 'info',

View File

@ -1 +0,0 @@
export * from '../../lib/docgen';

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { isForwardRef, isMemo } from 'react-is';
import { PropDef } from '@storybook/components';
import { hasDocgen, extractPropsFromDocgen, PropsExtractor, TypeSystem } from '../../lib/docgen';
import { hasDocgen, extractComponentProps, PropsExtractor, TypeSystem } from '../../lib/docgen';
import { Component } from '../../blocks/shared';
import { enhancePropTypesProps } from './propTypes/handleProp';
@ -32,7 +32,7 @@ function getPropDefs(component: Component, section: string): PropDef[] {
}
}
const extractedProps = extractPropsFromDocgen(processedComponent, section);
const extractedProps = extractComponentProps(processedComponent, section);
if (extractedProps.length === 0) {
return [];
}

View File

@ -3,7 +3,7 @@
import { PropDef } from '@storybook/components';
import PropTypes from 'prop-types';
import { Component } from '../../../blocks/shared';
import { extractPropsFromDocgen, DocgenInfo } from '../../../lib/docgen';
import { extractComponentProps, DocgenInfo } from '../../../lib/docgen';
import { enhancePropTypesProp, enhancePropTypesProps } from './handleProp';
const DOCGEN_SECTION = 'props';
@ -43,7 +43,7 @@ function createComponent({ propTypes = {}, defaultProps = {}, docgenInfo = {} })
}
function extractPropDef(component: Component): PropDef {
return enhancePropTypesProp(extractPropsFromDocgen(component, DOCGEN_SECTION)[0]);
return enhancePropTypesProp(extractComponentProps(component, DOCGEN_SECTION)[0]);
}
describe('enhancePropTypesProp', () => {
@ -914,7 +914,7 @@ describe('enhancePropTypesProps', () => {
});
const props = enhancePropTypesProps(
extractPropsFromDocgen(component, DOCGEN_SECTION),
extractComponentProps(component, DOCGEN_SECTION),
component
);
@ -945,7 +945,7 @@ describe('enhancePropTypesProps', () => {
});
const props = enhancePropTypesProps(
extractPropsFromDocgen(component, DOCGEN_SECTION),
extractComponentProps(component, DOCGEN_SECTION),
component
);

View File

@ -4,7 +4,7 @@ import toReact from '@egoist/vue-to-react';
import { StoryFn } from '@storybook/addons';
import { addParameters } from '@storybook/client-api';
import { extractProps } from './extractProps';
import { extractComponentDescription } from '../../lib/docgen/utils';
import { extractComponentDescription } from '../../lib/docgen';
addParameters({
docs: {

View File

@ -1,5 +1,5 @@
import { PropDef } from '@storybook/components';
import { PropsExtractor, hasDocgen, extractPropsFromDocgen } from '../../lib/docgen';
import { PropsExtractor, hasDocgen, extractComponentProps } from '../../lib/docgen';
const SECTIONS = ['props', 'events', 'slots'];
@ -9,7 +9,7 @@ export const extractProps: PropsExtractor = component => {
}
const sections: Record<string, PropDef[]> = {};
SECTIONS.forEach(section => {
sections[section] = extractPropsFromDocgen(component, section).map(x => x.propDef);
sections[section] = extractComponentProps(component, section).map(x => x.propDef);
});
return { sections };
};

View File

@ -1,22 +0,0 @@
import { isNil } from 'lodash';
import { PropDefaultValue } from '@storybook/components';
import { DocgenPropDefaultValue } from './types';
import { createSummaryValue } from '../utils';
const BLACKLIST = ['null', 'undefined'];
function isDefaultValueBlacklisted(value: string) {
return BLACKLIST.some(x => x === value);
}
export function createDefaultValue(defaultValue: DocgenPropDefaultValue): PropDefaultValue {
if (!isNil(defaultValue)) {
const { value } = defaultValue;
if (!isDefaultValueBlacklisted(value)) {
return createSummaryValue(value);
}
}
return null;
}

View File

@ -1,10 +1,10 @@
import { isNil } from 'lodash';
import { PropDef } from '@storybook/components';
import { TypeSystem, DocgenInfo, DocgenType } from './types';
import { PropDef, PropDefaultValue } from '@storybook/components';
import { TypeSystem, DocgenInfo, DocgenType, DocgenPropDefaultValue } from './types';
import { JsDocParsingResult } from '../jsdocParser';
import { createDefaultValue } from './createDefaultValue';
import { createSummaryValue } from '../utils';
import { createFlowPropDef } from './flow/createPropDef';
import { isDefaultValueBlacklisted } from './utils/defaultValue';
export type PropDefFactory = (
propName: string,
@ -12,6 +12,18 @@ export type PropDefFactory = (
jsDocParsingResult?: JsDocParsingResult
) => PropDef;
function createDefaultValue(defaultValue: DocgenPropDefaultValue): PropDefaultValue {
if (!isNil(defaultValue)) {
const { value } = defaultValue;
if (!isDefaultValueBlacklisted(value)) {
return createSummaryValue(value);
}
}
return null;
}
function createBasicPropDef(name: string, type: DocgenType, docgenInfo: DocgenInfo): PropDef {
const { description, required, defaultValue } = docgenInfo;

View File

@ -1,7 +1,7 @@
/* eslint-disable no-underscore-dangle */
import { Component } from '../../blocks/shared';
import { extractPropsFromDocgen } from './extractDocgenProps';
import { extractComponentProps } from './extractDocgenProps';
const DOCGEN_SECTION = 'props';
const PROP_NAME = 'propName';
@ -54,103 +54,127 @@ function createComponent(docgenInfo: Record<string, any>): Component {
}
TypeSystems.forEach(x => {
it('should map defaults docgen info properly', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey! Hey!',
defaultValue: {
value: 'Default',
},
describe(`${x.name}`, () => {
it('should map defaults docgen info properly', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey! Hey!',
defaultValue: {
value: 'Default',
},
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.name).toBe(PROP_NAME);
expect(propDef.type.summary).toBe('string');
expect(propDef.description).toBe('Hey! Hey!');
expect(propDef.required).toBe(false);
expect(propDef.defaultValue.summary).toBe('Default');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should remove JSDoc tags from the description', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey!\n@param event\nreturns {string}',
});
expect(propDef.name).toBe(PROP_NAME);
expect(propDef.type.summary).toBe('string');
expect(propDef.description).toBe('Hey! Hey!');
expect(propDef.required).toBe(false);
expect(propDef.defaultValue.summary).toBe('Default');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should remove JSDoc tags from the description', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey!\n@param event\nreturns {string}',
expect(propDef.description).toBe('Hey!');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription',
});
expect(propDef.description).toBe('Hey!');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription',
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription\n@param event',
});
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription\n@param event',
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.',
});
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.',
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.\n@param event',
});
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.\n@param event',
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should return null when the property is marked with @ignore', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\n@ignore',
});
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
it('should return null when the property is marked with @ignore', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\n@ignore',
expect(extractComponentProps(component, DOCGEN_SECTION).length).toBe(0);
});
expect(extractPropsFromDocgen(component, DOCGEN_SECTION).length).toBe(0);
});
it('should provide raw @param tags', () => {
const component = createComponent({
...createFuncType(x),
description:
'onClick description\n@param {SyntheticEvent} event - Original event.\n@param {string} value',
});
it('should provide raw @param tags', () => {
const component = createComponent({
...createFuncType(x),
description:
'onClick description\n@param {SyntheticEvent} event - Original event.\n@param {string} value',
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.description).toBe('onClick description');
expect(propDef.jsDocTags).toBeDefined();
expect(propDef.jsDocTags.params).toBeDefined();
expect(propDef.jsDocTags.params[0].name).toBe('event');
expect(propDef.jsDocTags.params[0].description).toBe('Original event.');
expect(propDef.jsDocTags.params[1].name).toBe('value');
expect(propDef.jsDocTags.params[1].description).toBeNull();
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it("should not return 'null' default value", () => {
const component = createComponent({
...createStringType(x),
defaultValue: { value: 'null' },
});
expect(propDef.description).toBe('onClick description');
expect(propDef.jsDocTags).toBeDefined();
expect(propDef.jsDocTags.params).toBeDefined();
expect(propDef.jsDocTags.params[0].name).toBe('event');
expect(propDef.jsDocTags.params[0].description).toBe('Original event.');
expect(propDef.jsDocTags.params[1].name).toBe('value');
expect(propDef.jsDocTags.params[1].description).toBeNull();
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.defaultValue).toBeNull();
});
it("should not return 'undefined' default value", () => {
const component = createComponent({
...createStringType(x),
defaultValue: { value: 'undefined' },
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.defaultValue).toBeNull();
});
});
});

View File

@ -3,7 +3,7 @@ import { PropDef } from '@storybook/components';
import { Component } from '../../blocks/shared';
import { ExtractedJsDoc, parseJsDoc } from '../jsdocParser';
import { DocgenInfo, TypeSystem } from './types';
import { getDocgenSection, isValidDocgenSection } from './utils';
import { getDocgenSection, isValidDocgenSection, getDocgenDescription } from './utils';
import { getPropDefFactory, PropDefFactory } from './createPropDef';
export interface ExtractedProp {
@ -31,7 +31,7 @@ const getTypeSystem = (docgenInfo: DocgenInfo): TypeSystem => {
return TypeSystem.UNKNOWN;
};
export const extractPropsFromDocgen: ExtractProps = (component, section) => {
export const extractComponentProps: ExtractProps = (component, section) => {
const docgenSection = getDocgenSection(component, section);
if (!isValidDocgenSection(docgenSection)) {
@ -75,3 +75,7 @@ function extractProp(
return null;
}
export function extractComponentDescription(component: Component): string {
return getDocgenDescription(component);
}

View File

@ -2,6 +2,7 @@ import { PropDefaultValue } from '@storybook/components';
import { isNil } from 'lodash';
import { DocgenPropDefaultValue, DocgenPropType } from '../types';
import { createSummaryValue, isTooLongForDefaultValueSummary } from '../../utils';
import { isDefaultValueBlacklisted } from '../utils/defaultValue';
export function createDefaultValue(
defaultValue: DocgenPropDefaultValue,
@ -10,9 +11,11 @@ export function createDefaultValue(
if (!isNil(defaultValue)) {
const { value } = defaultValue;
return !isTooLongForDefaultValueSummary(value)
? createSummaryValue(value)
: createSummaryValue(type.name, value);
if (!isDefaultValueBlacklisted(value)) {
return !isTooLongForDefaultValueSummary(value)
? createSummaryValue(value)
: createSummaryValue(type.name, value);
}
}
return null;

View File

@ -1,4 +1,4 @@
import { PropType, PropSummaryValue } from '@storybook/components';
import { PropType } from '@storybook/components';
import { isNil } from 'lodash';
import { DocgenFlowType } from '../types';
import { createSummaryValue, isTooLongForTypeSummary } from '../../utils';
@ -12,7 +12,7 @@ interface DocgenFlowUnionType extends DocgenFlowType {
elements: { name: string; value: string }[];
}
function generateUnion({ name, raw, elements }: DocgenFlowUnionType): PropSummaryValue {
function generateUnion({ name, raw, elements }: DocgenFlowUnionType): PropType {
if (!isNil(raw)) {
return createSummaryValue(raw);
}
@ -24,7 +24,7 @@ function generateUnion({ name, raw, elements }: DocgenFlowUnionType): PropSummar
return createSummaryValue(name);
}
function generateFuncSignature({ type, raw }: DocgenFlowType) {
function generateFuncSignature({ type, raw }: DocgenFlowType): PropType {
if (!isNil(raw)) {
return createSummaryValue(raw);
}
@ -32,7 +32,7 @@ function generateFuncSignature({ type, raw }: DocgenFlowType) {
return createSummaryValue(type);
}
function generateObjectSignature({ type, raw }: DocgenFlowType) {
function generateObjectSignature({ type, raw }: DocgenFlowType): PropType {
if (!isNil(raw)) {
return !isTooLongForTypeSummary(raw) ? createSummaryValue(raw) : createSummaryValue(type, raw);
}
@ -40,13 +40,13 @@ function generateObjectSignature({ type, raw }: DocgenFlowType) {
return createSummaryValue(type);
}
function generateSignature(flowType: DocgenFlowType): PropSummaryValue {
function generateSignature(flowType: DocgenFlowType): PropType {
const { type } = flowType;
return type === 'object' ? generateObjectSignature(flowType) : generateFuncSignature(flowType);
}
function generateDefault({ name, raw }: DocgenFlowType): PropSummaryValue {
function generateDefault({ name, raw }: DocgenFlowType): PropType {
if (!isNil(raw)) {
return !isTooLongForTypeSummary(raw) ? createSummaryValue(raw) : createSummaryValue(name, raw);
}

View File

@ -0,0 +1,5 @@
const BLACKLIST = ['null', 'undefined'];
export function isDefaultValueBlacklisted(value: string): boolean {
return BLACKLIST.some(x => x === value);
}

View File

@ -1,17 +1,8 @@
/* eslint-disable no-underscore-dangle */
import { isNil } from 'lodash';
import { Component } from '../../blocks/shared';
export const str = (obj: any) => {
if (!obj) {
return '';
}
if (typeof obj === 'string') {
return obj as string;
}
throw new Error(`Description: expected string, got: ${JSON.stringify(obj)}`);
};
import { Component } from '../../../blocks/shared';
import { str } from './string';
export function hasDocgen(component: Component): boolean {
return !!component.__docgenInfo;
@ -25,5 +16,6 @@ export function getDocgenSection(component: Component, section: string): any {
return hasDocgen(component) ? component.__docgenInfo[section] : null;
}
export const extractComponentDescription = (component?: Component) =>
component && hasDocgen(component) && str(component.__docgenInfo.description);
export function getDocgenDescription(component: Component): string {
return hasDocgen(component) && str(component.__docgenInfo.description);
}

View File

@ -0,0 +1,3 @@
export * from './defaultValue';
export * from './string';
export * from './docgen';

View File

@ -0,0 +1,9 @@
export const str = (obj: any) => {
if (!obj) {
return '';
}
if (typeof obj === 'string') {
return obj as string;
}
throw new Error(`Description: expected string, got: ${JSON.stringify(obj)}`);
};

View File

@ -0,0 +1,12 @@
import PropTypes from 'prop-types';
export const PRESET_SHAPE = {
text: PropTypes.string.isRequired,
startDate: PropTypes.object.isRequired,
endDate: PropTypes.object.isRequired,
};
export const SOME_PROP_TYPES = {
ext1: PropTypes.string,
ext2: PropTypes.number,
};

View File

@ -1,6 +1,8 @@
/* eslint-disable react/require-default-props */
/* eslint-disable react/no-unused-prop-types */
import React from 'react';
import PropTypes, { string, shape } from 'prop-types';
import { PRESET_SHAPE, SOME_PROP_TYPES } from './ext';
const NAMED_OBJECT = {
text: PropTypes.string.isRequired,
@ -159,6 +161,7 @@ PropTypesProps.propTypes = {
),
})
),
arrayExternalShape: PropTypes.arrayOf(PropTypes.shape(PRESET_SHAPE)),
/**
* A simple `objectOf` propType.
*/
@ -257,6 +260,7 @@ PropTypesProps.propTypes = {
requiredString: PropTypes.string.isRequired,
nullDefaultValue: PropTypes.string,
undefinedDefaultValue: PropTypes.string,
...SOME_PROP_TYPES,
};
PropTypesProps.defaultProps = {