UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

179 lines (155 loc) 4.88 kB
import _ from 'lodash'; export const stripIndent = text => { if (!text) { return ''; } const sigLines = text.split(/\n/).filter(line => !/^\s*$/.test(line)); const leadingWhitespaces = sigLines.map((line) => { const reg = /^(\s*)/g.exec(line); reg ? reg[1] : null; }); const minimumLeadingWhitespace = leadingWhitespaces.reduce( (min, line) => { return (line && line.length < min && min.length ? line : min) }, leadingWhitespaces[0] ); return text .split(/\n/) .map(line => line.replace(minimumLeadingWhitespace, '')) .join('\n'); }; export const formatSource = source => source .replace(/(\.\.\/)*(src\/)?index(\.js)?/, 'lucid-ui') .replace(/\t/g, ' ') .replace(/\/\/\s?begin-hide-from-docs[\s\S]*\/\/\s?end-hide-from-docs/, '') .replace(/\n*$/, '\n'); // custom props parsing for storybook DocsPage // our usage of prop types and peek break some assumptions that storybook makes about how props info is passed // this function tries to get prop type info from typescript (via react-docgen-typescript-loader) for TS components // then falls back on propTypes for non-TS components // in addition, it handles setting default values based on defaultProps export const getPropDefs = component => _.sortBy( _.sortBy( _.has(component, '__docgenInfo') ? getPropsFromDocgen(component) : getPropsFromPropTypes(component), prop => prop.name ), prop => !prop.required ); const getPropsFromDocgen = component => _.map( component.__docgenInfo.props, ({ type, required, description, defaultValue }, property) => mergePropwithDefaults(component, { type, required, description, defaultValue, name: property, }) ); const getPropsFromPropTypes = component => _.map( getPropsData(component), ({ name, type, isRequired, dynamicData, text, defaultValue }) => mergePropwithDefaults(component, { name, type: { name: propTypeName({ type, dynamicData }), }, required: isRequired, description: text, defaultValue, }) ); const mergePropwithDefaults = ( component, { name, type, required, description, defaultValue } ) => { // compare with defaultProps to set props optional and set default value as needed const defaultValueValue = _.get(defaultValue, 'defaultValue.value') || getDefaultPropValue(component, name); return { name, type, required: required && _.isUndefined(defaultValueValue), description, defaultValue: defaultValueValue, }; }; // formatter function for complex PropTypes similar to <PropType> from lucid-docs-addon const propTypeName = ({ type, ...propData }) => { if (type === 'oneOf') { return `${type}: ${JSON.stringify(propData.dynamicData, null, 2)}`; } if (type === 'arrayOf' || type === 'objectOf') { return `${type}: ${propTypeName({ ...propData.dynamicData })}`; } if (type === 'oneOfType') { return `${type}: ${_.map(propData.dynamicData, propTypeName).join(', ')}`; } if (type === 'instanceOf') { return `${type}: ${propData.dynamicData}`; } if (type === 'shape') { return `${type}: { ${_.map( propData.dynamicData, (propTypeData, key) => `${key}: ${propTypeName({ ...propTypeData })}` ).join(', ')} }`; } return type; }; // helper function for parsing PropTypes and peek data to generate props information export const getPropsData = componentRef => { return _.map(componentRef.propTypes, (resolverFn, property) => { return { ...getPropTypeData(resolverFn), name: property, defaultValue: getDefaultPropValue(componentRef, property), }; }); }; const getDefaultPropValue = (componentRef, property) => { const defaultValue = _.get(componentRef, ['defaultProps', property]); return _.isUndefined(defaultValue) ? _.get(componentRef, ['peekDefaultProps', property]) : defaultValue; }; const getPropTypeData = resolverFn => { const type = _.get(resolverFn, ['peek', 'type']); let dynamicData; if (type === 'oneOf') { dynamicData = _.get(resolverFn, ['peek', 'args', 0]); } if (type === 'arrayOf') { const arrayOfResolverFn = _.get(resolverFn, ['peek', 'args', 0]); dynamicData = getPropTypeData(arrayOfResolverFn); } if (type === 'oneOfType') { const oneOfTypeResolverFns = _.get(resolverFn, ['peek', 'args', 0]); dynamicData = _.map(oneOfTypeResolverFns, getPropTypeData); } if (type === 'instanceOf') { const instanceOfClass = _.get(resolverFn, ['peek', 'args', 0]); dynamicData = instanceOfClass.name; } if (type === 'objectOf') { const objectOfResolverFn = _.get(resolverFn, ['peek', 'args', 0]); dynamicData = getPropTypeData(objectOfResolverFn); } if (type === 'shape') { const shapeObject = _.get(resolverFn, ['peek', 'args', 0]); dynamicData = _.mapValues(shapeObject, getPropTypeData); } return { ...resolverFn.peek, type, text: stripIndent(_.get(resolverFn, ['peek', 'text'])).trim(), dynamicData, }; };