From 97b7a1b093948b25cb3c2eadddfb5d08e5d108aa Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 8 Jun 2020 15:45:53 -0700 Subject: [PATCH] fix: Handle enums that are nested in a shape prop type --- src/utils/__tests__/propTypeInfo.js | 26 +++++++++++++++++ src/utils/propTypeInfo.js | 44 ++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/utils/__tests__/propTypeInfo.js b/src/utils/__tests__/propTypeInfo.js index d7bcb6c94..2418a37ec 100644 --- a/src/utils/__tests__/propTypeInfo.js +++ b/src/utils/__tests__/propTypeInfo.js @@ -516,6 +516,32 @@ describe('getTypeMeta', () => { }); }); + test('handles enums nested in shape prop types', () => { + const OUTER_SPACE = { + SMALL: 'sm', + MEDIUM: 'md', + LARGE: 'lg', + }; + const enumPropType = createPropType('oneOf', [Object.values(OUTER_SPACE)]); + const propType = createPropType('shape', [{ space: enumPropType }]); + + const component = { + name: 'Button', + propTypes: { + outer: propType, + }, + OUTER_SPACE, + }; + + expect(getTypeMeta('space', enumPropType, { component })).toEqual({ + constants: [ + 'Button.OUTER_SPACE.SMALL', + 'Button.OUTER_SPACE.MEDIUM', + 'Button.OUTER_SPACE.LARGE', + ], + }); + }); + test('handles advanced case', () => { const CRAZY = { ONE: 1, diff --git a/src/utils/propTypeInfo.js b/src/utils/propTypeInfo.js index 3f82d0626..c46685ee1 100644 --- a/src/utils/propTypeInfo.js +++ b/src/utils/propTypeInfo.js @@ -10,12 +10,30 @@ const SPECIAL_NUMBERS = [ 'EPSILON', ]; +const IGNORED_PROPERTIES = [ + 'prototype', + 'length', + 'name', + 'propTypes', + 'getDerivedStateFromProps', + 'defaultProps', +]; + const getArgs = (propType) => propType.__reflect__.find(({ args }) => args)?.args; const isEnum = (propType) => getRawTypeName(propType) === 'oneOf'; const isUnion = (propType) => getRawTypeName(propType) === 'oneOfType'; +const matchesEnum = (property, enums) => { + const values = Object.values(property); + + return ( + values.length === enums.length && + enums.every((value) => values.includes(value)) + ); +}; + const findSpecialNumber = (number) => SPECIAL_NUMBERS.find((property) => Number[property] === number); @@ -92,7 +110,7 @@ export const getDefaultValue = (component, propTypeName) => { return defaultValue; }; -export const getTypeMeta = (name, propType, { component, prefix }) => { +export const getTypeMeta = (name, propType, { component }) => { const propTypeDocs = propType.__docs__; switch (getRawTypeName(propType)) { @@ -106,16 +124,19 @@ export const getTypeMeta = (name, propType, { component, prefix }) => { return { types: Object.entries(shape).map(([shapePropName, propType]) => - getPropTypeDefinition(component, shapePropName, propType, { - prefix: name, - }) + getPropTypeDefinition(component, shapePropName, propType) ), }; } case 'oneOf': { - const staticProperty = toStaticPropertyName( - prefix ? `${prefix}.${name}` : name - ); + const [enums] = getArgs(propType); + const staticProperty = Object.getOwnPropertyNames(component) + .filter( + (member) => + !IGNORED_PROPERTIES.includes(member) && + typeof component[member] === 'object' + ) + .find((member) => matchesEnum(component[member], enums)); return { constants: Object.keys(component[staticProperty] ?? {}).map( @@ -150,12 +171,7 @@ export const getTypeMeta = (name, propType, { component, prefix }) => { } }; -export const getPropTypeDefinition = ( - component, - name, - propType, - { prefix } = {} -) => { +export const getPropTypeDefinition = (component, name, propType) => { const propDocs = propType.__docs__; const propMeta = propType.__reflect__; @@ -167,7 +183,7 @@ export const getPropTypeDefinition = ( isRequired: propMeta?.some((item) => item.name === 'isRequired') ?? false, examples: propDocs?.tags?.examples ?? [], type: { - meta: getTypeMeta(name, propType, { component, prefix }), + meta: getTypeMeta(name, propType, { component }), raw: getRawTypeName(propType), name: getNormalizedTypeName(propType), },