diff --git a/CHANGELOG.md b/CHANGELOG.md index db0dc89776..a9daa2566e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `defaultBehavior` was removed @layershifter ([#1600](https://github.com/stardust-ui/react/pull/1600)) - `ReactElement`s are now handled in the same way as string in all shorthands @layershifter ([#1600](https://github.com/stardust-ui/react/pull/1600)) - `Props` param is required in `ShorthandValue` and `ShorthandCollection` @layershifter ([#1605](https://github.com/stardust-ui/react/pull/1605)) +- `LoaderPosition` type is no longer exported @layershifter ([#1634](https://github.com/stardust-ui/react/pull/1634)) ### Features - Split action handlers with "OR" condition in accessibility behaviors @sophieH29 ([#1622](https://github.com/stardust-ui/react/pull/1622)) @@ -40,6 +41,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add best practices and form usage example for `Slider` component @Bugaa92 ([#1641](https://github.com/stardust-ui/react/pull/1641)) - Add examples with `Tooltip` for the actionable components @mnajdova ([#1636](https://github.com/stardust-ui/react/pull/1636)) - Fix broken fragments on CodeSandbox @lucivpav ([#1655](https://github.com/stardust-ui/react/pull/1655)) +- Improve a table with component props in docs @layershifter ([#1634](https://github.com/stardust-ui/react/pull/1634)) ## [v0.34.1](https://github.com/stardust-ui/react/tree/v0.34.1) (2019-07-11) diff --git a/build/gulp/plugins/util/getComponentInfo.ts b/build/gulp/plugins/util/getComponentInfo.ts index e4e29f5375..ed86f39cfe 100644 --- a/build/gulp/plugins/util/getComponentInfo.ts +++ b/build/gulp/plugins/util/getComponentInfo.ts @@ -1,18 +1,36 @@ +import * as Babel from '@babel/core' +import * as t from '@babel/types' import _ from 'lodash' import path from 'path' import fs from 'fs' + +import { BehaviorInfo, ComponentInfo, ComponentProp } from 'docs/src/types' +import * as docgen from './docgen' import parseDefaultValue from './parseDefaultValue' import parseDocblock from './parseDocblock' import parseType from './parseType' -import * as docgen from './docgen' +import getShorthandInfo from './getShorthandInfo' + +const getAvailableBehaviors = (accessibilityProp: ComponentProp): BehaviorInfo[] => { + const docTags = accessibilityProp && accessibilityProp.tags + const availableTag = _.find(docTags, { title: 'available' }) + const availableBehaviorNames = _.get(availableTag, 'description', '') + + if (!availableBehaviorNames) { + return undefined + } -interface BehaviorInfo { - name: string - displayName: string - category: string + return availableBehaviorNames + .replace(/\s/g, '') + .split(',') + .map(name => ({ + name, + displayName: _.upperFirst(name.replace('Behavior', '')), + category: _.upperFirst(name.split(/(?=[A-Z])/)[0]), + })) } -const getComponentInfo = (filepath: string, ignoredParentInterfaces: string[]) => { +const getComponentInfo = (filepath: string, ignoredParentInterfaces: string[]): ComponentInfo => { const absPath = path.resolve(process.cwd(), filepath) const dir = path.dirname(absPath) @@ -22,7 +40,7 @@ const getComponentInfo = (filepath: string, ignoredParentInterfaces: string[]) = // singular form of the component's ../../ directory // "element" for "src/elements/Button/Button.js" - const componentType = path.basename(path.dirname(dir)).replace(/s$/, '') + const componentType = path.basename(path.dirname(dir)).replace(/s$/, '') as ComponentInfo['type'] const components = docgen.withDefaultConfig().parse(absPath) @@ -37,31 +55,37 @@ const getComponentInfo = (filepath: string, ignoredParentInterfaces: string[]) = ].join(' '), ) } - const info: any = components[0] + const info: docgen.ComponentDoc = components[0] // add exported Component info // // this 'require' instruction might break by producing partially initialized types - because of ts-node module's cache used during processing // - in that case we might consider to disable ts-node cache when running this command: https://github.com/ReactiveX/rxjs/commit/2f86b9ddccbf020b2e695dd8fe0b79194efa3f56 const Component = require(absPath).default - info.constructorName = _.get(Component, 'prototype.constructor.name', null) + + if (!Component) { + throw new Error(`Your file with component under "${absPath}" doesn't have a default export`) + } + + const componentFile = Babel.parse(fs.readFileSync(absPath).toString(), { + configFile: false, + presets: [['@babel/preset-typescript', { allExtensions: true, isTSX: true }]], + }) as t.File + const constructorName = _.get(Component, 'prototype.constructor.name', null) // add component type - info.type = componentType + const type = componentType // add parent/child info - info.isParent = filenameWithoutExt === dirname - info.isChild = !info.isParent - info.parentDisplayName = info.isParent ? null : dirname + const isParent = filenameWithoutExt === dirname + const isChild = !isParent + const parentDisplayName = isParent ? null : dirname // "Field" for "FormField" since it is accessed as "Form.Field" in the API - info.subcomponentName = info.isParent - ? null - : info.displayName.replace(info.parentDisplayName, '') + const subcomponentName = isParent ? null : info.displayName.replace(parentDisplayName, '') // "ListItem.js" is a subcomponent is the "List" directory const subcomponentRegExp = new RegExp(`^${dirname}\\w+\\.tsx$`) - - info.subcomponents = info.isParent + const subcomponents = isParent ? fs .readdirSync(dir) .filter(file => subcomponentRegExp.test(file)) @@ -69,79 +93,89 @@ const getComponentInfo = (filepath: string, ignoredParentInterfaces: string[]) = : null // where this component should be exported in the api - info.apiPath = info.isChild - ? `${info.parentDisplayName}.${info.subcomponentName}` - : info.displayName + const apiPath = isChild ? `${parentDisplayName}.${subcomponentName}` : info.displayName // class name for the component // example, the "button" in class="ui-button" // name of the component, sub component, or plural parent for sub component groups - info.componentClassName = (info.isChild - ? _.includes(info.subcomponentName, 'Group') - ? `ui-${info.parentDisplayName}s` - : `ui-${info.parentDisplayName}__${info.subcomponentName}` + const componentClassName = (isChild + ? _.includes(subcomponentName, 'Group') + ? `ui-${parentDisplayName}s` + : `ui-${parentDisplayName}__${subcomponentName}` : `ui-${info.displayName}` ).toLowerCase() // replace the component.description string with a parsed docblock object - info.docblock = parseDocblock(info.description) - delete info.description + const docblock = parseDocblock(info.description) // file and path info - info.repoPath = absPath + const repoPath = absPath .replace(`${process.cwd()}${path.sep}`, '') .replace(new RegExp(_.escapeRegExp(path.sep), 'g'), '/') - info.filename = filename - info.filenameWithoutExt = filenameWithoutExt - // replace prop `description` strings with a parsed docblock object and updated `type` - _.each(info.props, (propDef, propName) => { - const { description, tags } = parseDocblock(propDef.description) - const { name, value } = parseType(propName, propDef) + let props: ComponentProp[] = [] + _.forEach(info.props, (propDef: docgen.PropItem, propName: string) => { + const { description, tags } = parseDocblock(propDef.description) const parentInterface = _.get(propDef, 'parent.name') - const visibleInDefinition = !_.includes(ignoredParentInterfaces, parentInterface) - if (visibleInDefinition) { - info.props[propName] = { - ...propDef, + // `propDef.parent` should be defined to avoid insertion of computed props + const visibleInDefinition = + propDef.parent && !_.includes(ignoredParentInterfaces, parentInterface) + const visibleInTags = !_.find(tags, { title: 'docSiteIgnore' }) + + if (visibleInDefinition && visibleInTags) { + const types = parseType(componentFile, info.displayName, propName, propDef) + const defaultValue = parseDefaultValue(Component, propDef, types) + + props.push({ description, + defaultValue, tags, - value, - defaultValue: parseDefaultValue(propDef, name), + types, name: propName, - type: name, - } - } else { - delete info.props[propName] + required: propDef.required, + }) } }) + // manually insert `as` prop + if (info.props.as) { + props.push({ + description: 'An element type to render as (string or component).', + defaultValue: parseDefaultValue(Component, info.props.as, []) || 'div', + tags: [], + types: [{ name: 'React.ElementType' }], + name: 'as', + required: false, + }) + } + // sort props - info.props = _.sortBy(info.props, 'name') + props = _.sortBy(props, 'name') // available behaviors - info.behaviors = getAvailableBehaviors(_.find(info.props, { name: 'accessibility' })) - return info -} - -const getAvailableBehaviors: (accessibilityProp: any) => BehaviorInfo = accessibilityProp => { - const docTags = accessibilityProp && accessibilityProp.tags - const availableTag = _.find(docTags, { title: 'available' }) - const availableBehaviorNames = _.get(availableTag, 'description', '') - - if (!availableBehaviorNames) { - return undefined + const behaviors = getAvailableBehaviors(_.find(props, { name: 'accessibility' })) + + return { + ...getShorthandInfo(componentFile, info.displayName), + apiPath, + behaviors, + componentClassName, + constructorName, + displayName: info.displayName, + docblock, + filename, + filenameWithoutExt, + isChild, + isParent, + parentDisplayName, + props, + repoPath, + subcomponentName, + subcomponents, + type, } - - return availableBehaviorNames - .replace(/\s/g, '') - .split(',') - .map(name => ({ - name, - displayName: _.upperFirst(name.replace('Behavior', '')), - category: _.upperFirst(name.split(/(?=[A-Z])/)[0]), - })) } export default getComponentInfo diff --git a/build/gulp/plugins/util/getShorthandInfo.ts b/build/gulp/plugins/util/getShorthandInfo.ts new file mode 100644 index 0000000000..6da230c0dc --- /dev/null +++ b/build/gulp/plugins/util/getShorthandInfo.ts @@ -0,0 +1,71 @@ +import * as Babel from '@babel/core' +import { NodePath } from '@babel/traverse' +import * as t from '@babel/types' + +import { ComponentInfo } from 'docs/src/types' + +type ShorthandInfo = Required< + Pick +> + +/** + * Checks that an expression matches signature: + * [componentName].create = createShorthandFactory([config]) + */ +const isShorthandExpression = ( + componentName: string, + path: NodePath, +): boolean => { + const left = path.get('left') + const right = path.get('right') + + if (!left.isMemberExpression() || !right.isCallExpression()) { + return false + } + + const object = left.get('object') + const property = left.get('property') as NodePath + const callee = right.get('callee') + + return ( + object.isIdentifier({ name: componentName }) && + property.isIdentifier({ name: 'create' }) && + callee.isIdentifier({ name: 'createShorthandFactory' }) + ) +} + +const getShorthandInfo = (componentFile: t.File, componentName: string): ShorthandInfo => { + let implementsCreateShorthand = false + let mappedShorthandProp = undefined + + Babel.traverse(componentFile, { + AssignmentExpression: path => { + if (isShorthandExpression(componentName, path)) { + implementsCreateShorthand = true + + const config = path.get('right.arguments.0') as NodePath + config.assertObjectExpression() + + const mappedProperty = config.node.properties.find((property: t.ObjectProperty) => { + return t.isIdentifier(property.key, { name: 'mappedProp' }) + }) as t.ObjectProperty | null + + if (mappedProperty) { + // @ts-ignore + t.assertStringLiteral(mappedProperty.value) + mappedShorthandProp = (mappedProperty.value as t.StringLiteral).value + } else { + // `mappedProp` is optional in `createShorthandFactory()` + mappedShorthandProp = 'children' + } + } + }, + }) + + return { + implementsCreateShorthand, + mappedShorthandProp, + } +} + +export default getShorthandInfo diff --git a/build/gulp/plugins/util/parseDefaultValue.ts b/build/gulp/plugins/util/parseDefaultValue.ts index 4898504731..9414d3cf71 100644 --- a/build/gulp/plugins/util/parseDefaultValue.ts +++ b/build/gulp/plugins/util/parseDefaultValue.ts @@ -1,5 +1,45 @@ import _ from 'lodash' +import * as React from 'react' -// export default propDef => _.get(propDef, 'defaultValue.value', undefined) -export default (propDef, typeName) => - _.get(propDef, 'defaultValue.value', typeName === 'boolean' ? 'false' : 'undefined') +import { ComponentPropType } from 'docs/src/types' +import { PropItem } from './docgen' + +const parseDefaultValue = ( + Component: React.ComponentType, + propDef: PropItem, + types: ComponentPropType[], +) => { + if (Component.defaultProps && _.has(Component.defaultProps, propDef.name)) { + const defaultValue = Component.defaultProps[propDef.name] + + if (_.isFunction(defaultValue)) { + return defaultValue.name + } + + if (_.isNumber(defaultValue) || _.isString(defaultValue) || _.isBoolean(defaultValue)) { + return defaultValue + } + + if (_.isPlainObject(defaultValue)) { + return defaultValue + } + + if (_.isNull(defaultValue)) { + return null + } + + throw new Error(`Can't parse a value in "${Component.name}.defaultProps.${propDef.name}"`) + } + + if (propDef.name === 'as') { + return 'div' + } + + if (types.length === 1 && types[0].name === 'boolean') { + return false + } + + return undefined +} + +export default parseDefaultValue diff --git a/build/gulp/plugins/util/parseDocblock.ts b/build/gulp/plugins/util/parseDocblock.ts index a6a9357273..0343a90ace 100644 --- a/build/gulp/plugins/util/parseDocblock.ts +++ b/build/gulp/plugins/util/parseDocblock.ts @@ -1,11 +1,13 @@ import * as doctrine from 'doctrine' -export default docblock => { +const parseDocblock = (docblock: string) => { const { description = '', tags = [], ...rest } = doctrine.parse(docblock || '', { unwrap: true }) return { ...rest, + description, tags, - description: description.split('\n'), } } + +export default parseDocblock diff --git a/build/gulp/plugins/util/parseType.ts b/build/gulp/plugins/util/parseType.ts index cfa0740612..3bd85369dc 100644 --- a/build/gulp/plugins/util/parseType.ts +++ b/build/gulp/plugins/util/parseType.ts @@ -1,52 +1,89 @@ -const _ = require('lodash') +import * as Babel from '@babel/core' +import { NodePath } from '@babel/traverse' +import * as t from '@babel/types' +import _ from 'lodash' -// eslint-disable-next-line no-eval -const evalValue = value => eval(value) // tslint:disable-line no-eval +import { ComponentPropType } from 'docs/src/types' +import { PropItem } from './docgen' +import parseTypeAnnotation from './parseTypeAnnotation' -const isTransformable = value => typeof value === 'string' && value.includes('names') +/** Performs transform: `ShorthandValue` to `ShorthandCollection[]`. */ +const normalizeShorthandCollection = (propType: string): string => { + const regex = /ShorthandValue<(.+) & { kind\?: (.+); }>\[]$/ + const result = regex.exec(propType) -const uniqValues = values => _.uniqWith(values, (val, other) => `${val}` === `${other}`) + if (result) { + return `ShorthandCollection<${result[1]}, ${result[2]}>` + } -const transformEnumValues = values => - _.flatMap(values, ({ value }) => { - if (value === 'names') return evalValue(value) - return value.replace(/'/g, '') - }) + return propType +} -const parseEnum = type => { - const { value } = type +const normalizeType = (propType: string): string => { + _.reduce( + [normalizeShorthandCollection], + (propType, normalizer): string => { + return normalizer(propType) + }, + propType, + ) - if (isTransformable(value)) return { ...type, value: uniqValues(evalValue(value)) } - return { ...type, value: uniqValues(transformEnumValues(value)) } + return normalizeShorthandCollection(propType) } -const parseUnion = union => { - const { value } = union - const values = _.flatten(_.map(_.filter(value, { name: 'enum' }), type => parseEnum(type).value)) +const getTypeFromBabelTree = (componentFile: t.File, componentName: string, propName: string) => { + let typeAnnotation: t.TSType + + const propertyVisitor: Babel.Visitor = { + TSPropertySignature: path => { + if (path.get('key').isIdentifier({ name: propName })) { + const annotationPath: NodePath = path.get('typeAnnotation') - return { - ...union, - name: _.map(value, 'name').join('|'), - value: values, + typeAnnotation = annotationPath.get('typeAnnotation').node + } + }, } -} -const parsers = { - enum: parseEnum, - union: parseUnion, + Babel.traverse(componentFile, { + TSInterfaceDeclaration: path => { + if (path.get('id').isIdentifier({ name: `${componentName}Props` })) { + path.traverse(propertyVisitor) + } + }, + }) + + return typeAnnotation } -export default (propName, { type }) => { - if (type === undefined) { - throw new Error( - [ - `The prop "${propName}" does not contain propType definition. This happens if the property is in the `, - 'defaultProps, but it is not in the propTypes', - ].join(' '), - ) - } +const parseType = ( + componentFile: t.File, + componentName: string, + propName: string, + propInfo: PropItem, +): ComponentPropType[] => { + const propType = normalizeType(propInfo.type.name) + + let typeAnnotation: t.TSType + + try { + const result = Babel.parse(`type __ = ${propType}`, { + configFile: false, + presets: [['@babel/preset-typescript', { allExtensions: true }]], + }) as t.File + + const body = result.program.body + const declaration = body[0] - const parser = parsers[type.name] + if (body.length !== 1 || !t.isTSTypeAliasDeclaration(declaration)) { + throw new Error(`A prop "${propName}" has unsupported type definition: ${propType}`) + } - return parser ? parser(type) : type + typeAnnotation = declaration.typeAnnotation + } catch (e) { + typeAnnotation = getTypeFromBabelTree(componentFile, componentName, propName) + } + + return parseTypeAnnotation(propName, propType, typeAnnotation) } + +export default parseType diff --git a/build/gulp/plugins/util/parseTypeAnnotation.ts b/build/gulp/plugins/util/parseTypeAnnotation.ts new file mode 100644 index 0000000000..b6b34a26de --- /dev/null +++ b/build/gulp/plugins/util/parseTypeAnnotation.ts @@ -0,0 +1,84 @@ +import * as t from '@babel/types' +import _ from 'lodash' + +import { ComponentPropType } from 'docs/src/types' + +const keywords: Record = { + any: t.isTSAnyKeyword, + boolean: t.isTSBooleanKeyword, + never: t.isTSNeverKeyword, + number: t.isTSNumberKeyword, + null: t.isTSNullKeyword, + object: t.isTSObjectKeyword, + string: t.isTSStringKeyword, +} + +const parseTypeAnnotation = ( + propName: string, + propType: string, + tsType: t.TSType, +): ComponentPropType[] => { + if (t.isTSParenthesizedType(tsType)) { + return parseTypeAnnotation(propName, propType, tsType.typeAnnotation) + } + + if (t.isTSUnionType(tsType)) { + return _.flatMap(tsType.types, type => parseTypeAnnotation(propName, propType, type)) + } + + const keyword = _.findKey(keywords, matcher => matcher(tsType)) + + if (keyword) { + return [{ name: keyword, keyword: true }] + } + + if (t.isTSArrayType(tsType)) { + return [ + { + name: 'array', + parameters: parseTypeAnnotation(propName, propType, tsType.elementType), + }, + ] + } + + // TODO: improve parser to support function params and return values + if (t.isTSFunctionType(tsType)) { + return [{ name: 'function', value: propType }] + } + + // TODO: improve parser for + if (t.isTSIntersectionType(tsType)) { + return parseTypeAnnotation(propName, propType, tsType.types[0]) + } + + if (t.isTSLiteralType(tsType)) { + return [{ name: 'literal', value: tsType.literal.value.toString() }] + } + + // Naming is weird, but it's a different type than LiteralType + // TSLiteralType: type __ = false + // TSTypeLiteral: type __ = { a: string } + if (t.isTSTypeLiteral(tsType)) { + return [{ name: 'object', value: propType }] + } + + if (t.isTSTypeReference(tsType)) { + if (t.isIdentifier(tsType.typeName)) { + const definition: ComponentPropType = { + name: tsType.typeName.name, + } + + if (t.isTSTypeParameterInstantiation(tsType.typeParameters)) { + definition.parameters = _.flatMap(tsType.typeParameters.params, param => + parseTypeAnnotation(propName, propType, param), + ) + } + + return [definition] + } + } + + throw new Error(`A prop "${propName}" has unsupported type definition: ${propType}`) +} + +export default parseTypeAnnotation diff --git a/build/gulp/tasks/docs.ts b/build/gulp/tasks/docs.ts index 72e023648b..922341ab88 100644 --- a/build/gulp/tasks/docs.ts +++ b/build/gulp/tasks/docs.ts @@ -93,11 +93,7 @@ const markdownSrc = [ task('build:docs:component-info', () => src(componentsSrc, { since: lastRun('build:docs:component-info') }) - .pipe( - cache(gulpReactDocgen(['DOMAttributes', 'HTMLAttributes']), { - name: 'componentInfo', - }), - ) + .pipe(cache(gulpReactDocgen(['DOMAttributes', 'HTMLAttributes']), { name: 'componentInfo-1' })) .pipe(dest(paths.docsSrc('componentInfo'))), ) diff --git a/docs/src/components/ComponentDoc/AccessibilityDescription.tsx b/docs/src/components/ComponentDoc/AccessibilityDescription.tsx deleted file mode 100644 index 3b54ba3505..0000000000 --- a/docs/src/components/ComponentDoc/AccessibilityDescription.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react' -import * as ReactMarkdown from 'react-markdown' - -const AccessibilityDescription: React.FunctionComponent<{ value: string }> = ({ value }) => ( - -) - -export default AccessibilityDescription diff --git a/docs/src/components/ComponentDoc/BehaviorDescription.tsx b/docs/src/components/ComponentDoc/BehaviorDescription.tsx index dfe47db9c3..4fdeea2f32 100644 --- a/docs/src/components/ComponentDoc/BehaviorDescription.tsx +++ b/docs/src/components/ComponentDoc/BehaviorDescription.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { Loader } from '@stardust-ui/react/src' -const AccessibilityDescription = React.lazy(() => import('./AccessibilityDescription')) +const AccessibilityDescription = React.lazy(() => import('./InlineMarkdown')) const item = '- ' diff --git a/docs/src/components/ComponentDoc/ComponentDoc.tsx b/docs/src/components/ComponentDoc/ComponentDoc.tsx index 6f22235fd6..ff6cdf5cb0 100644 --- a/docs/src/components/ComponentDoc/ComponentDoc.tsx +++ b/docs/src/components/ComponentDoc/ComponentDoc.tsx @@ -1,10 +1,7 @@ -import * as _ from 'lodash' -import * as PropTypes from 'prop-types' import * as React from 'react' -import { withRouter } from 'react-router-dom' +import { RouteComponentProps, withRouter } from 'react-router-dom' import { Flex, Header, Icon, Dropdown, Text, Grid } from '@stardust-ui/react' -import componentInfoShape from 'docs/src/utils/componentInfoShape' import { scrollToAnchor, examplePathToHash, getFormattedHash } from 'docs/src/utils' import ComponentDocLinks from './ComponentDocLinks' import ComponentDocSee from './ComponentDocSee' @@ -14,6 +11,7 @@ import ComponentAccessibility from './ComponentDocAccessibility' import { ThemeContext } from 'docs/src/context/ThemeContext' import ExampleContext from 'docs/src/context/ExampleContext' import ComponentPlayground from 'docs/src/components/ComponentPlayground' +import { ComponentInfo } from 'docs/src/types' const exampleEndStyle: React.CSSProperties = { textAlign: 'center', @@ -21,12 +19,11 @@ const exampleEndStyle: React.CSSProperties = { paddingTop: '75vh', } -class ComponentDoc extends React.Component { - static propTypes = { - history: PropTypes.object.isRequired, - info: componentInfoShape.isRequired, - } +type ComponentDocProps = { + info: ComponentInfo +} & RouteComponentProps<{}> +class ComponentDoc extends React.Component { state: any = {} componentWillMount() { @@ -127,10 +124,7 @@ class ComponentDoc extends React.Component { /> - + diff --git a/docs/src/components/ComponentDoc/ComponentDocAccessibility.tsx b/docs/src/components/ComponentDoc/ComponentDocAccessibility.tsx index 9eee0ea4a8..7618d48965 100644 --- a/docs/src/components/ComponentDoc/ComponentDocAccessibility.tsx +++ b/docs/src/components/ComponentDoc/ComponentDocAccessibility.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import * as _ from 'lodash' import { Flex, Loader, Text, Accordion } from '@stardust-ui/react' -const AccessibilityDescription = React.lazy(() => import('./AccessibilityDescription')) +const InlineMarkdown = React.lazy(() => import('./InlineMarkdown')) const behaviorMenu = require('docs/src/behaviorMenu') @@ -24,7 +24,7 @@ const ComponentDocAccessibility = ({ info }) => { {description && ( }> - + )} diff --git a/docs/src/components/ComponentDoc/ComponentDocSee.tsx b/docs/src/components/ComponentDoc/ComponentDocSee.tsx index f837f63dae..b14a4cc3fc 100644 --- a/docs/src/components/ComponentDoc/ComponentDocSee.tsx +++ b/docs/src/components/ComponentDoc/ComponentDocSee.tsx @@ -10,8 +10,8 @@ const listStyle = { display: 'block' } const ComponentDocSee: any = ({ displayName }) => { const items = getInfoForSeeTags(displayName) - if (items.length === 0) return null + if (_.isEmpty(items)) return null return ( {/* Heads up! Still render empty lists to reserve the whitespace */} diff --git a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropDefaultValue.tsx b/docs/src/components/ComponentDoc/ComponentProp/ComponentPropDefaultValue.tsx deleted file mode 100644 index 5ef55696e9..0000000000 --- a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropDefaultValue.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as _ from 'lodash' -import * as PropTypes from 'prop-types' -import * as React from 'react' - -export default class ComponentPropDefaultValue extends React.PureComponent { - static propTypes = { - value: PropTypes.node, - } - - render() { - const { value } = this.props - return _.isNil(value) ? null : ( - {value.indexOf('_1.') === -1 ? value : value.split('_1.')[1]} - ) - } -} diff --git a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropDescription.tsx b/docs/src/components/ComponentDoc/ComponentProp/ComponentPropDescription.tsx deleted file mode 100644 index 895b6ddfae..0000000000 --- a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropDescription.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as _ from 'lodash' -import * as PropTypes from 'prop-types' -import * as React from 'react' - -export default class ComponentPropDescription extends React.PureComponent { - static propTypes = { - description: PropTypes.arrayOf(PropTypes.string), - } - - render() { - const { description } = this.props - return

{_.map(description, line => [line,
])}

- } -} diff --git a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnum.tsx b/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnum.tsx deleted file mode 100644 index 1a1d501009..0000000000 --- a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnum.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as _ from 'lodash' -import * as PropTypes from 'prop-types' -import * as React from 'react' - -import ComponentPropExtra from './ComponentPropExtra' -import ComponentPropToggle from './ComponentPropEnumToggle' -import ComponentPropValue from './ComponentPropEnumValue' - -const ComponentPropEnum: any = ({ limit, showAll, toggle, type, values }) => { - if (!_.includes(type, 'enum') || !values) return null - - const exceeds = values.length > limit - const sliced = showAll ? values : _.slice(values, 0, limit) - - return ( - - {exceeds && } - -
- {_.map(sliced, value => ( - {value} - ))} - {exceeds && !showAll && '...'} -
-
- ) -} - -ComponentPropEnum.defaultProps = { - limit: 50, -} - -ComponentPropEnum.propTypes = { - limit: PropTypes.number, - showAll: PropTypes.bool, - toggle: PropTypes.func, - type: PropTypes.string, - values: PropTypes.array, -} - -const arePropsEqual = (prevProps, nextProps) => - prevProps.showAll === nextProps.showAll && - prevProps.type === nextProps.type && - prevProps.values === nextProps.values - -export default React.memo(ComponentPropEnum, arePropsEqual) diff --git a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnumToggle.tsx b/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnumToggle.tsx deleted file mode 100644 index a5541f03ce..0000000000 --- a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnumToggle.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as PropTypes from 'prop-types' -import * as React from 'react' - -const toggleStyle = { - cursor: 'pointer', -} - -const ComponentPropEnumToggle: any = ({ showAll, toggle, total }) => ( -
- {showAll ? 'Show less' : `Show all ${total}`} - -) - -ComponentPropEnumToggle.propTypes = { - showAll: PropTypes.bool, - toggle: PropTypes.func, - total: PropTypes.number, -} - -const areEqualProps = (prevProps, nextProps) => prevProps.showAll === nextProps.showAll - -export default React.memo(ComponentPropEnumToggle, areEqualProps) diff --git a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnumValue.tsx b/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnumValue.tsx deleted file mode 100644 index ffd1b8ba50..0000000000 --- a/docs/src/components/ComponentDoc/ComponentProp/ComponentPropEnumValue.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as PropTypes from 'prop-types' -import * as React from 'react' - -const spanStyle = { - display: 'inline-block', - paddingRight: '0.2em', -} - -const ComponentPropEnumValue: any = ({ children }) => ( - - {children} - -) - -ComponentPropEnumValue.propTypes = { - children: PropTypes.node, -} - -const arePropsEqual = () => true - -export default React.memo(ComponentPropEnumValue, arePropsEqual) diff --git a/docs/src/components/ComponentDoc/ComponentProps/ComponentProps.tsx b/docs/src/components/ComponentDoc/ComponentProps/ComponentProps.tsx index c12316bbac..da59ef9873 100644 --- a/docs/src/components/ComponentDoc/ComponentProps/ComponentProps.tsx +++ b/docs/src/components/ComponentDoc/ComponentProps/ComponentProps.tsx @@ -67,7 +67,7 @@ export default class ComponentProps extends React.Component { {activeDisplayName && ( <> - + diff --git a/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsRow.tsx b/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsRow.tsx index 66d2deab4c..098f1315f6 100644 --- a/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsRow.tsx +++ b/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsRow.tsx @@ -1,68 +1,81 @@ -import * as PropTypes from 'prop-types' import * as React from 'react' import * as _ from 'lodash' +import { Link } from 'react-router-dom' -import ComponentPropDefaultValue from '../ComponentProp/ComponentPropDefaultValue' -import ComponentPropDescription from '../ComponentProp/ComponentPropDescription' +import { ComponentProp, ComponentPropType } from 'docs/src/types' +import componentInfoContext from 'docs/src/utils/componentInfoContext' import ComponentPropName from '../ComponentProp/ComponentPropName' -export default class ComponentPropsRow extends React.Component { - static propTypes = { - defaultValue: PropTypes.string, - description: PropTypes.arrayOf(PropTypes.string), - name: PropTypes.string, - required: PropTypes.bool, - tags: PropTypes.array, - type: PropTypes.string, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), - } +const InlineMarkdown = React.lazy(() => import('../InlineMarkdown')) + +type ComponentPropsRowProps = ComponentProp + +const ComponentPropValue: React.FunctionComponent = props => { + const { name, parameters } = props - state: any = {} + if (name === 'literal') return enum + if (name === 'ShorthandValue' || name === 'ShorthandCollection') { + const componentName = parameters[0].name.replace('Props', '') - toggleEnums = () => this.setState({ showEnums: !this.state.showEnums }) + const parentInfo = componentInfoContext.byDisplayName[componentName] + const linkName = _.kebabCase(parentInfo.parentDisplayName || componentName) - render() { - const { defaultValue, description, name, required, tags, type } = this.props - const hideRow = this.docSiteHidden(tags) - const slot = this.isSlotProp(type, tags) - const rowStyle: React.CSSProperties = { - borderTopWidth: '1px', - borderTopStyle: 'solid', - borderTopColor: 'grey', - } + const kindParam = parameters[1] && parameters[1].name !== 'never' + const kindIsVisible = name === 'ShorthandCollection' && kindParam - // TODO: use Flex or a Table component, when it will be available return ( - - - - - - - - {`{${type}}`} - - - {/* TODO change these according to the react-docgen-typescript generated json */} - {/* */} - {/* */} - - + + {name} + {`<`} + {parameters[0].name} + {kindIsVisible && , {parameters[1].name}} + {`>`} + ) } - docSiteHidden(tags) { - return _.some(tags, ['title', 'docSiteIgnore']) - } - isSlotProp(type: string, tags): boolean { - if (type.startsWith('ShorthandValue') || type.startsWith('ShorthandCollection')) { - return true - } - return _.some(tags, ['title', 'slot']) - } + return {name} } + +const ComponentPropsRow: React.FunctionComponent = props => { + const { defaultValue, description, name, required, types } = props + + const shorthand = types.some( + type => type.name === 'ShorthandValue' || type.name === 'ShorthandCollection', + ) + + const typeValues = _.uniqBy(types, type => type.name) + const enumValues = _.filter(types, type => type.name === 'literal') + + return ( + + + + + {_.isNil(defaultValue) ? null : {JSON.stringify(defaultValue)}} + + + {typeValues.map((type, index) => ( + + + {index < typeValues.length - 1 && | } + + ))} + + + + + {enumValues.length > 0 && Values:} + {enumValues.map(type => ( + + {type.value} + + ))} + {/* TODO change these according to the react-docgen-typescript generated json */} + {/* */} + + + ) +} + +export default React.memo(ComponentPropsRow) diff --git a/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsTable.tsx b/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsTable.tsx index 4831c3ffcf..317ba8fb2e 100644 --- a/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsTable.tsx +++ b/docs/src/components/ComponentDoc/ComponentPropsTable/ComponentPropsTable.tsx @@ -1,8 +1,8 @@ -import * as _ from 'lodash' +import { Loader } from '@stardust-ui/react' import * as React from 'react' -import ComponentPropsRow from './ComponentPropsRow' import useComponentProps from 'docs/src/components/ComponentDoc/useComponentProps' +import ComponentPropsRow from './ComponentPropsRow' const tableStyles: React.CSSProperties = { textAlign: 'left', @@ -21,22 +21,24 @@ const ComponentPropsTable: React.FunctionComponent = props const componentProps = useComponentProps(props.componentName) return ( - - - - - - - - - + }> +
NameDefaultTypeDescription
+ + + + + + + + - - {_.map(componentProps, propDef => ( - - ))} - -
NameDefaultTypeDescription
+ + {componentProps.map(propDef => ( + + ))} + + + ) } diff --git a/docs/src/components/ComponentDoc/InlineMarkdown.tsx b/docs/src/components/ComponentDoc/InlineMarkdown.tsx new file mode 100644 index 0000000000..6c47d219d7 --- /dev/null +++ b/docs/src/components/ComponentDoc/InlineMarkdown.tsx @@ -0,0 +1,8 @@ +import * as React from 'react' +import * as ReactMarkdown from 'react-markdown' + +const InlineMarkdown: React.FunctionComponent<{ value: string }> = ({ value }) => ( + +) + +export default InlineMarkdown diff --git a/docs/src/components/ComponentDoc/useComponentProps.ts b/docs/src/components/ComponentDoc/useComponentProps.ts index fcfa2b809b..8ebc9cd8e1 100644 --- a/docs/src/components/ComponentDoc/useComponentProps.ts +++ b/docs/src/components/ComponentDoc/useComponentProps.ts @@ -1,6 +1,7 @@ import componentInfoContext from 'docs/src/utils/componentInfoContext' +import { ComponentInfo } from 'docs/src/types' -const useComponentProps = (componentName: string): any[] => { +const useComponentProps = (componentName: string): ComponentInfo['props'] => { const info = componentInfoContext.byDisplayName[componentName] if (!info) { diff --git a/docs/src/types.ts b/docs/src/types.ts index cb58586c10..1b9bc90a16 100644 --- a/docs/src/types.ts +++ b/docs/src/types.ts @@ -2,3 +2,54 @@ export type ExampleSource = { js: string ts: string } + +export type BehaviorInfo = { + name: string + displayName: string + category: string +} + +export type ComponentInfo = { + behaviors?: BehaviorInfo[] + constructorName: string + componentClassName: string + implementsCreateShorthand: boolean + mappedShorthandProp?: string + displayName: string + filename: string + filenameWithoutExt: string + docblock: { + description: string + tags: { description: string; title: string }[] + } + apiPath: string + isChild: boolean + isParent: boolean + parentDisplayName: null | string + props: ComponentProp[] + repoPath: string + subcomponentName: null | string + subcomponents: string[] + type: 'component' +} + +export type ComponentProp = { + defaultValue: any + description: string + name: string + tags: { + title: string + description: string + type: null + name: string + }[] + types: ComponentPropType[] + required: boolean +} + +export type ComponentPropType = { + name?: 'any' | 'boolean' | 'never' | 'string' | 'array' | 'literal' | string + keyword?: boolean + parameters?: ComponentPropType[] + value?: string +} diff --git a/docs/src/utils/componentInfoShape.ts b/docs/src/utils/componentInfoShape.ts deleted file mode 100644 index 1b60ed774a..0000000000 --- a/docs/src/utils/componentInfoShape.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as PropTypes from 'prop-types' - -const componentInfoShape = PropTypes.shape({ - displayName: PropTypes.string.isRequired, - props: PropTypes.arrayOf( - PropTypes.shape({ - type: PropTypes.string, - required: PropTypes.bool, - description: PropTypes.arrayOf(PropTypes.string), - defaultValue: PropTypes.string, - tags: PropTypes.arrayOf( - PropTypes.shape({ - title: PropTypes.string, - description: PropTypes.string, - }), - ), - name: PropTypes.string, - }), - ), - constructorName: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - isParent: PropTypes.bool.isRequired, - isChild: PropTypes.bool.isRequired, - parentDisplayName: PropTypes.string, - subcomponentName: PropTypes.string, - subcomponents: PropTypes.arrayOf(PropTypes.string), - apiPath: PropTypes.string.isRequired, - componentClassName: PropTypes.string.isRequired, - docblock: PropTypes.shape({ - tags: PropTypes.arrayOf( - PropTypes.shape({ - title: PropTypes.string, - description: PropTypes.string, - }), - ), - description: PropTypes.arrayOf(PropTypes.string), - }).isRequired, - repoPath: PropTypes.string.isRequired, - filename: PropTypes.string.isRequired, - filenameWithoutExt: PropTypes.string.isRequired, -}) - -export default componentInfoShape diff --git a/docs/src/utils/index.tsx b/docs/src/utils/index.tsx index 309e826eab..8ddd177e53 100644 --- a/docs/src/utils/index.tsx +++ b/docs/src/utils/index.tsx @@ -1,5 +1,4 @@ export { default as componentInfoContext } from './componentInfoContext' -export { default as componentInfoShape } from './componentInfoShape' export { exampleIndexContext, examplePlaygroundContext, diff --git a/packages/react/src/components/Alert/Alert.tsx b/packages/react/src/components/Alert/Alert.tsx index 8c4f46c7f9..c6956e38d4 100644 --- a/packages/react/src/components/Alert/Alert.tsx +++ b/packages/react/src/components/Alert/Alert.tsx @@ -29,7 +29,6 @@ export interface AlertProps ContentComponentProps> { /** * Accessibility behavior if overridden by the user. - * @default alertBehavior * @available alertWarningBehavior */ accessibility?: Accessibility diff --git a/packages/react/src/components/Attachment/Attachment.tsx b/packages/react/src/components/Attachment/Attachment.tsx index 80c98ed414..ce09182ee2 100644 --- a/packages/react/src/components/Attachment/Attachment.tsx +++ b/packages/react/src/components/Attachment/Attachment.tsx @@ -19,10 +19,7 @@ import { Accessibility } from '../../lib/accessibility/types' import { attachmentBehavior } from '../../lib/accessibility' export interface AttachmentProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default attachmentBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Button shorthand for the action slot. */ diff --git a/packages/react/src/components/Button/Button.tsx b/packages/react/src/components/Button/Button.tsx index 5d9097c8f0..e53c847f5d 100644 --- a/packages/react/src/components/Button/Button.tsx +++ b/packages/react/src/components/Button/Button.tsx @@ -26,10 +26,7 @@ export interface ButtonProps extends UIComponentProps, ContentComponentProps>, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default buttonBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** A button can appear circular. */ diff --git a/packages/react/src/components/Chat/Chat.tsx b/packages/react/src/components/Chat/Chat.tsx index 0d3d9ea108..b1550cd9d4 100644 --- a/packages/react/src/components/Chat/Chat.tsx +++ b/packages/react/src/components/Chat/Chat.tsx @@ -22,10 +22,7 @@ export interface ChatSlotClassNames { } export interface ChatProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default chatBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Shorthand array of the items inside the chat. */ diff --git a/packages/react/src/components/Chat/ChatMessage.tsx b/packages/react/src/components/Chat/ChatMessage.tsx index 345c48cf02..3e66d117bf 100644 --- a/packages/react/src/components/Chat/ChatMessage.tsx +++ b/packages/react/src/components/Chat/ChatMessage.tsx @@ -49,10 +49,7 @@ export interface ChatMessageProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps> { - /** - * Accessibility behavior if overridden by the user. - * @default chatMessageBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Menu with actions of the message. */ diff --git a/packages/react/src/components/Checkbox/Checkbox.tsx b/packages/react/src/components/Checkbox/Checkbox.tsx index 383488edb1..08d69974e4 100644 --- a/packages/react/src/components/Checkbox/Checkbox.tsx +++ b/packages/react/src/components/Checkbox/Checkbox.tsx @@ -20,10 +20,7 @@ import { checkboxBehavior } from '../../lib/accessibility' import { SupportedIntrinsicInputProps } from '../../lib/htmlPropsUtils' export interface CheckboxProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default checkboxBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Initial checked value. */ diff --git a/packages/react/src/components/Dialog/Dialog.tsx b/packages/react/src/components/Dialog/Dialog.tsx index d5a07bc7e4..f044415256 100644 --- a/packages/react/src/components/Dialog/Dialog.tsx +++ b/packages/react/src/components/Dialog/Dialog.tsx @@ -34,10 +34,7 @@ export interface DialogProps extends UIComponentProps, ContentComponentProps>, ColorComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default dialogBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** A dialog can contain actions. */ diff --git a/packages/react/src/components/Embed/Embed.tsx b/packages/react/src/components/Embed/Embed.tsx index 8f18ea45d4..3c7fa603f9 100644 --- a/packages/react/src/components/Embed/Embed.tsx +++ b/packages/react/src/components/Embed/Embed.tsx @@ -23,10 +23,7 @@ export interface EmbedSlotClassNames { } export interface EmbedProps extends UIComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default embedBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Whether the embedded object should be active. */ diff --git a/packages/react/src/components/Grid/Grid.tsx b/packages/react/src/components/Grid/Grid.tsx index 639c394220..f397b2cf5b 100644 --- a/packages/react/src/components/Grid/Grid.tsx +++ b/packages/react/src/components/Grid/Grid.tsx @@ -14,14 +14,11 @@ import { import { WithAsProp, withSafeTypeForAs } from '../../types' import { Accessibility } from '../../lib/accessibility/types' -export interface GridProps - extends UIComponentProps, - ChildrenComponentProps, - ContentComponentProps { +export interface GridProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps { /** * Accessibility behavior if overridden by the user. * @available gridBehavior - * */ + */ accessibility?: Accessibility /** The columns of the grid with a space-separated list of values. The values represent the track size, and the space between them represents the grid line. */ diff --git a/packages/react/src/components/Icon/Icon.tsx b/packages/react/src/components/Icon/Icon.tsx index 3c2e1e2f79..60a9c5d7f3 100644 --- a/packages/react/src/components/Icon/Icon.tsx +++ b/packages/react/src/components/Icon/Icon.tsx @@ -17,10 +17,7 @@ import { WithAsProp, withSafeTypeForAs } from '../../types' export type IconXSpacing = 'none' | 'before' | 'after' | 'both' export interface IconProps extends UIComponentProps, ColorComponentProps { - /** - * Accessibility behavior if overriden by the user. - * @default iconBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Icon can appear with rectangular border. */ diff --git a/packages/react/src/components/Image/Image.tsx b/packages/react/src/components/Image/Image.tsx index b2fd567d22..bc49fb6d17 100644 --- a/packages/react/src/components/Image/Image.tsx +++ b/packages/react/src/components/Image/Image.tsx @@ -8,10 +8,7 @@ import { Accessibility } from '../../lib/accessibility/types' import { WithAsProp, withSafeTypeForAs } from '../../types' export interface ImageProps extends UIComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default imageBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** An image may be formatted to appear inline with text as an avatar. */ diff --git a/packages/react/src/components/List/List.tsx b/packages/react/src/components/List/List.tsx index 3f8059d394..702f6b4651 100644 --- a/packages/react/src/components/List/List.tsx +++ b/packages/react/src/components/List/List.tsx @@ -29,10 +29,7 @@ export interface ListSlotClassNames { } export interface ListProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default listBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Toggle debug mode */ diff --git a/packages/react/src/components/List/ListItem.tsx b/packages/react/src/components/List/ListItem.tsx index 9e5cb05616..fefd5b87dc 100644 --- a/packages/react/src/components/List/ListItem.tsx +++ b/packages/react/src/components/List/ListItem.tsx @@ -29,10 +29,7 @@ export interface ListItemSlotClassNames { export interface ListItemProps extends UIComponentProps, ContentComponentProps> { - /** - * Accessibility behavior if overridden by the user. - * @default listItemBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility contentMedia?: ShorthandValue /** Toggle debug mode. */ diff --git a/packages/react/src/components/Loader/Loader.tsx b/packages/react/src/components/Loader/Loader.tsx index d9ca7b960d..762f250ab8 100644 --- a/packages/react/src/components/Loader/Loader.tsx +++ b/packages/react/src/components/Loader/Loader.tsx @@ -15,8 +15,6 @@ import { Accessibility } from '../../lib/accessibility/types' import { WithAsProp, ShorthandValue, withSafeTypeForAs } from '../../types' import Box, { BoxProps } from '../Box/Box' -export type LoaderPosition = 'above' | 'below' | 'start' | 'end' - export interface LoaderSlotClassNames { indicator: string label: string @@ -24,10 +22,7 @@ export interface LoaderSlotClassNames { } export interface LoaderProps extends UIComponentProps, ColorComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default loaderBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Time in milliseconds after component mount before spinner is visible. */ @@ -43,7 +38,7 @@ export interface LoaderProps extends UIComponentProps, ColorComponentProps { label?: ShorthandValue /** A label in the loader can have different positions. */ - labelPosition?: LoaderPosition + labelPosition?: 'above' | 'below' | 'start' | 'end' /** A size of the loader. */ size?: SizeValue diff --git a/packages/react/src/components/Menu/Menu.tsx b/packages/react/src/components/Menu/Menu.tsx index 34694f49e5..be1a090741 100644 --- a/packages/react/src/components/Menu/Menu.tsx +++ b/packages/react/src/components/Menu/Menu.tsx @@ -34,9 +34,8 @@ export interface MenuSlotClassNames { export interface MenuProps extends UIComponentProps, ChildrenComponentProps { /** * Accessibility behavior if overridden by the user. - * @default menuBehavior * @available menuAsToolbarBehavior, tabListBehavior - * */ + */ accessibility?: Accessibility /** Index of the currently active item. */ diff --git a/packages/react/src/components/Menu/MenuDivider.tsx b/packages/react/src/components/Menu/MenuDivider.tsx index f96e9aa7ee..2dce0dd96c 100644 --- a/packages/react/src/components/Menu/MenuDivider.tsx +++ b/packages/react/src/components/Menu/MenuDivider.tsx @@ -19,10 +19,7 @@ export interface MenuDividerProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default menuDividerBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility vertical?: boolean diff --git a/packages/react/src/components/Menu/MenuItem.tsx b/packages/react/src/components/Menu/MenuItem.tsx index 8fa15a5baa..c2f48f1ebb 100644 --- a/packages/react/src/components/Menu/MenuItem.tsx +++ b/packages/react/src/components/Menu/MenuItem.tsx @@ -45,9 +45,8 @@ export interface MenuItemProps ContentComponentProps { /** * Accessibility behavior if overridden by the user. - * @default menuItemBehavior * @available menuItemAsToolbarButtonBehavior, tabBehavior - * */ + */ accessibility?: Accessibility /** A menu item can be active. */ diff --git a/packages/react/src/components/Popup/Popup.tsx b/packages/react/src/components/Popup/Popup.tsx index e58f622658..3eeabbe55e 100644 --- a/packages/react/src/components/Popup/Popup.tsx +++ b/packages/react/src/components/Popup/Popup.tsx @@ -54,9 +54,8 @@ export interface PopupProps PositioningProps { /** * Accessibility behavior if overridden by the user. - * @default popupBehavior * @available dialogBehavior - * */ + */ accessibility?: Accessibility /** Additional CSS class name(s) to apply. */ diff --git a/packages/react/src/components/RadioGroup/RadioGroup.tsx b/packages/react/src/components/RadioGroup/RadioGroup.tsx index b8eecb7aa9..33414262c7 100644 --- a/packages/react/src/components/RadioGroup/RadioGroup.tsx +++ b/packages/react/src/components/RadioGroup/RadioGroup.tsx @@ -29,10 +29,7 @@ export interface RadioGroupSlotClassNames { } export interface RadioGroupProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default radioGroupBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Value of the currently checked radio item. */ diff --git a/packages/react/src/components/RadioGroup/RadioGroupItem.tsx b/packages/react/src/components/RadioGroup/RadioGroupItem.tsx index a4f1e6754d..c169f48c72 100644 --- a/packages/react/src/components/RadioGroup/RadioGroupItem.tsx +++ b/packages/react/src/components/RadioGroup/RadioGroupItem.tsx @@ -20,10 +20,7 @@ import { Accessibility } from '../../lib/accessibility/types' import { radioGroupItemBehavior } from '../../lib/accessibility' export interface RadioGroupItemProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default radioGroupItemBehavior - * */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Whether or not radio item is checked. */ diff --git a/packages/react/src/components/Slider/Slider.tsx b/packages/react/src/components/Slider/Slider.tsx index 86f2177d9a..88a112477a 100644 --- a/packages/react/src/components/Slider/Slider.tsx +++ b/packages/react/src/components/Slider/Slider.tsx @@ -55,25 +55,16 @@ export interface SliderProps extends UIComponentProps, ChildrenComponentProps, Omit { - /** - * Accessibility behavior if overridden by the user. - * @default sliderBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** The default value of the slider. */ defaultValue?: string | number - /** - * A slider can be read-only and unable to change states. - * @default false - */ + /** A slider can be read-only and unable to change states. */ disabled?: SupportedIntrinsicInputProps['disabled'] - /** - * A slider can take the width of its container. - * @default false - */ + /** A slider can take the width of its container. */ fluid?: boolean /** @@ -88,16 +79,10 @@ export interface SliderProps /** Ref for input DOM node. */ inputRef?: React.Ref - /** - * The maximum value of the slider. - * @default 100 - */ + /** The maximum value of the slider. */ max?: SupportedIntrinsicInputProps['max'] - /** - * The minimum value of the slider - * @default 0 - */ + /** The minimum value of the slider. */ min?: SupportedIntrinsicInputProps['min'] /** @@ -111,17 +96,13 @@ export interface SliderProps * A number that specifies the granularity that the value must adhere to, or the special value 'any'. * A string value of any means that no stepping is implied, and any value is allowed * (barring other constraints, such as min and max). - * @default 1 */ step?: SupportedIntrinsicInputProps['step'] /** The value of the slider. */ value?: string | number - /** - * A slider can be displayed vertically. - * @default false - */ + /** A slider can be displayed vertically. */ vertical?: boolean } diff --git a/packages/react/src/components/Status/Status.tsx b/packages/react/src/components/Status/Status.tsx index 8033bc5768..ad84121d1d 100644 --- a/packages/react/src/components/Status/Status.tsx +++ b/packages/react/src/components/Status/Status.tsx @@ -15,10 +15,7 @@ import { import { WithAsProp, ShorthandValue, withSafeTypeForAs } from '../../types' export interface StatusProps extends UIComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default statusBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** A custom color. */ diff --git a/packages/react/src/components/Toolbar/Toolbar.tsx b/packages/react/src/components/Toolbar/Toolbar.tsx index bc55144f35..65eeb6c144 100644 --- a/packages/react/src/components/Toolbar/Toolbar.tsx +++ b/packages/react/src/components/Toolbar/Toolbar.tsx @@ -33,10 +33,7 @@ export interface ToolbarProps ContentComponentProps, ChildrenComponentProps, ColorComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default toolbarBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Shorthand array of props for Toolbar. */ diff --git a/packages/react/src/components/Toolbar/ToolbarItem.tsx b/packages/react/src/components/Toolbar/ToolbarItem.tsx index e6e18c0c64..c2ebde03e4 100644 --- a/packages/react/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react/src/components/Toolbar/ToolbarItem.tsx @@ -36,15 +36,13 @@ import Box, { BoxProps } from '../Box/Box' import Popup, { PopupProps } from '../Popup/Popup' import { mergeComponentVariables } from '../../lib/mergeThemes' import { ToolbarMenuItemProps } from '../Toolbar/ToolbarMenuItem' +import { ToolbarItemShorthandKinds } from '@stardust-ui/react' export interface ToolbarItemProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default toolbarItemBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** A toolbar item can be active. */ @@ -60,7 +58,9 @@ export interface ToolbarItemProps * Shorthand for the submenu. * If submenu is specified, the item is wrapped to group the item and the menu elements together. */ - menu?: ShorthandValue | ShorthandCollection + menu?: + | ShorthandValue + | ShorthandCollection /** Indicates if the menu inside the item is open. */ menuOpen?: boolean diff --git a/packages/react/src/components/Toolbar/ToolbarRadioGroup.tsx b/packages/react/src/components/Toolbar/ToolbarRadioGroup.tsx index 8576345259..39f7c8c886 100644 --- a/packages/react/src/components/Toolbar/ToolbarRadioGroup.tsx +++ b/packages/react/src/components/Toolbar/ToolbarRadioGroup.tsx @@ -34,7 +34,7 @@ export interface ToolbarRadioGroupProps accessibility?: Accessibility /** Shorthand array of props for ToolbarRadioGroup. */ - items?: ShorthandCollection + items?: ShorthandCollection } class ToolbarRadioGroup extends UIComponent> { diff --git a/packages/react/src/components/Tree/Tree.tsx b/packages/react/src/components/Tree/Tree.tsx index 2bba1c4414..2a865d8cbd 100644 --- a/packages/react/src/components/Tree/Tree.tsx +++ b/packages/react/src/components/Tree/Tree.tsx @@ -32,10 +32,7 @@ export interface TreeProps extends UIComponentProps, ChildrenComponentProps { /** Index of the currently active subtree. */ activeIndex?: number[] | number - /** - * Accessibility behavior if overridden by the user. - * @default treeBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Initial activeIndex value. */ diff --git a/packages/react/src/components/Tree/TreeItem.tsx b/packages/react/src/components/Tree/TreeItem.tsx index 9b8e248c31..9c5d2b7c78 100644 --- a/packages/react/src/components/Tree/TreeItem.tsx +++ b/packages/react/src/components/Tree/TreeItem.tsx @@ -34,10 +34,7 @@ export interface TreeItemSlotClassNames { } export interface TreeItemProps extends UIComponentProps, ChildrenComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default treeItemBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** Only allow one subtree to be open at a time. */ diff --git a/packages/react/src/components/Tree/TreeTitle.tsx b/packages/react/src/components/Tree/TreeTitle.tsx index 63872d56fe..b97a121ab6 100644 --- a/packages/react/src/components/Tree/TreeTitle.tsx +++ b/packages/react/src/components/Tree/TreeTitle.tsx @@ -21,10 +21,7 @@ export interface TreeTitleProps extends UIComponentProps, ChildrenComponentProps, ContentComponentProps { - /** - * Accessibility behavior if overridden by the user. - * @default treeTitleBehavior - */ + /** Accessibility behavior if overridden by the user. */ accessibility?: Accessibility /** diff --git a/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.tsx b/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.tsx index 1f0535b74f..e77026f7bc 100644 --- a/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.tsx +++ b/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.tsx @@ -12,7 +12,7 @@ import getElementType from '../../getElementType' import callable from '../../callable' /** AutoFocusZone is used to focus inner element on mount. */ -export class AutoFocusZone extends React.Component { +export default class AutoFocusZone extends React.Component { root = React.createRef() static propTypes = { diff --git a/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.types.tsx b/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.types.tsx index 56dcf64e68..ee4bd681a0 100644 --- a/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.types.tsx +++ b/packages/react/src/lib/accessibility/FocusZone/AutoFocusZone.types.tsx @@ -1,10 +1,7 @@ import * as React from 'react' export interface AutoFocusZoneProps extends React.HTMLAttributes { - /** - * Element type the root element will use - * @default div - */ + /** Element type the root element will use. */ as?: React.ReactType /** diff --git a/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.tsx b/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.tsx index 91305de2a0..1b9e02a3d1 100644 --- a/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.tsx +++ b/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.tsx @@ -22,7 +22,7 @@ import getElementType from '../../getElementType' /** FocusTrapZone is used to trap the focus in any html element placed in body * and hide other elements outside of Focus Trap Zone from accessibility tree. * Pressing tab will circle focus within the inner focusable elements of the FocusTrapZone. */ -export class FocusTrapZone extends React.Component { +export default class FocusTrapZone extends React.Component { static _focusStack: FocusTrapZone[] = [] _root: { current: HTMLElement | null } = { current: null } _previouslyFocusedElementOutsideTrapZone: HTMLElement diff --git a/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.types.tsx b/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.types.tsx index 3385b33b00..475e6c8b42 100644 --- a/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.types.tsx +++ b/packages/react/src/lib/accessibility/FocusZone/FocusTrapZone.types.tsx @@ -1,10 +1,7 @@ import * as React from 'react' export interface FocusTrapZoneProps extends React.HTMLAttributes { - /** - * Element type the root element will use. - * @default div - */ + /** Element type the root element will use. */ as?: React.ReactType /** @@ -22,10 +19,7 @@ export interface FocusTrapZoneProps extends React.HTMLAttributes */ ariaLabelledBy?: string - /** - * Indicates if this Trap Zone will allow clicks outside the FocusTrapZone - * @default true - */ + /** Indicates if this Trap Zone will allow clicks outside the FocusTrapZone. */ isClickableOutsideFocusTrap?: boolean /** diff --git a/packages/react/src/lib/accessibility/FocusZone/FocusZone.tsx b/packages/react/src/lib/accessibility/FocusZone/FocusZone.tsx index 19e09db772..1053dd4904 100644 --- a/packages/react/src/lib/accessibility/FocusZone/FocusZone.tsx +++ b/packages/react/src/lib/accessibility/FocusZone/FocusZone.tsx @@ -41,7 +41,7 @@ function getParent(child: HTMLElement): HTMLElement | null { return child && child.parentElement } -export class FocusZone extends React.Component implements IFocusZone { +export default class FocusZone extends React.Component implements IFocusZone { static propTypes = { className: PropTypes.string, direction: PropTypes.number, diff --git a/packages/react/src/lib/accessibility/FocusZone/FocusZone.types.ts b/packages/react/src/lib/accessibility/FocusZone/FocusZone.types.ts index 15896b4b83..04f8321469 100644 --- a/packages/react/src/lib/accessibility/FocusZone/FocusZone.types.ts +++ b/packages/react/src/lib/accessibility/FocusZone/FocusZone.types.ts @@ -1,5 +1,5 @@ import * as React from 'react' -import { FocusZone } from './FocusZone' +import FocusZone from './FocusZone' /** * FocusZone component class interface. @@ -32,10 +32,7 @@ export interface IFocusZone { * FocusZone component props. */ export interface FocusZoneProps extends React.HTMLAttributes { - /** - * Element type the root element will use. - * @default div - */ + /** Element type the root element will use. */ as?: React.ReactType /** * Additional class name to provide on the root element, in addition to the ms-FocusZone class. @@ -45,7 +42,6 @@ export interface FocusZoneProps extends React.HTMLAttributes = ReactNode | Props

-export type ShorthandCollection = ShorthandValue

[] +export type ShorthandCollection = ShorthandValue

[] // ======================================================== // Types for As prop support diff --git a/packages/react/test/specs/commonTests/isConformant.tsx b/packages/react/test/specs/commonTests/isConformant.tsx index 59d3fb1365..89a83ea2cf 100644 --- a/packages/react/test/specs/commonTests/isConformant.tsx +++ b/packages/react/test/specs/commonTests/isConformant.tsx @@ -152,7 +152,7 @@ export default (Component, options: Conformant = {}) => { // ---------------------------------------- // Docblock description // ---------------------------------------- - const hasDocblockDescription = info.docblock.description.join('').trim().length > 0 + const hasDocblockDescription = info.docblock.description.trim().length > 0 test('has a docblock description', () => { expect(hasDocblockDescription).toEqual(true)