diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c8b50b22..067ebb802a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix `Popup` not closing on outside click @kuzhelov ([#598](https://github.com/stardust-ui/react/pull/598)) - Fix multiple React's warnings about keys in docs @layershifter ([#602](https://github.com/stardust-ui/react/pull/602)) - Fix incorrect handling of `isFromKeyboard` in `Menu` @layershifter ([#596](https://github.com/stardust-ui/react/pull/596)) +- Fix property names used in shorthand factories @kuzhelov ([#591](https://github.com/stardust-ui/react/pull/591)) ### Features - `Ref` components uses `forwardRef` API by default @layershifter ([#491](https://github.com/stardust-ui/react/pull/491)) diff --git a/src/components/Image/Image.tsx b/src/components/Image/Image.tsx index 289b0ebb69..738c6bb919 100644 --- a/src/components/Image/Image.tsx +++ b/src/components/Image/Image.tsx @@ -22,6 +22,9 @@ export interface ImageProps extends UIComponentProps { /** An image can take up the width of its container. */ fluid?: boolean + + /** Image source URL. */ + src?: string } /** diff --git a/src/components/ItemLayout/ItemLayout.tsx b/src/components/ItemLayout/ItemLayout.tsx index e13cf32df7..33feb9d309 100644 --- a/src/components/ItemLayout/ItemLayout.tsx +++ b/src/components/ItemLayout/ItemLayout.tsx @@ -208,7 +208,7 @@ class ItemLayout extends UIComponent, any> { } } -ItemLayout.create = createShorthandFactory(ItemLayout, 'main') +ItemLayout.create = createShorthandFactory(ItemLayout, 'content') export default ItemLayout diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 513e1bef91..febdea30d7 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -129,6 +129,6 @@ class ListItem extends UIComponent, ListItemState> { } } -ListItem.create = createShorthandFactory(ListItem, 'main') +ListItem.create = createShorthandFactory(ListItem, 'content') export default ListItem diff --git a/src/lib/factories.tsx b/src/lib/factories.tsx index ab431d07ba..a12ad81017 100644 --- a/src/lib/factories.tsx +++ b/src/lib/factories.tsx @@ -4,6 +4,7 @@ import * as React from 'react' import { ShorthandValue, Props, + PropsOf, ShorthandRenderCallback, ShorthandRenderFunction, ShorthandRenderer, @@ -74,18 +75,20 @@ export function createShorthand( // ============================================================ // Factory Creators // ============================================================ - /** * @param {React.ReactType} Component A ReactClass or string * @param {string} mappedProp A function that maps a primitive value to the Component props * @returns {function} A shorthand factory function waiting for `val` and `defaultProps`. */ -export function createShorthandFactory(Component: React.ReactType, mappedProp?: string) { +export function createShorthandFactory( + Component: T, + mappedProp?: keyof PropsOf, +) { if (typeof Component !== 'function' && typeof Component !== 'string') { throw new Error('createShorthandFactory() Component must be a string or function.') } - return (val, options) => createShorthand(Component, mappedProp, val, options) + return (val, options) => createShorthand(Component, mappedProp as string, val, options) } // ============================================================ diff --git a/test/specs/commonTests/implementsCollectionShorthandProp.tsx b/test/specs/commonTests/implementsCollectionShorthandProp.tsx index 4ce7ea9edc..de198dd628 100644 --- a/test/specs/commonTests/implementsCollectionShorthandProp.tsx +++ b/test/specs/commonTests/implementsCollectionShorthandProp.tsx @@ -1,31 +1,42 @@ import * as React from 'react' import { mountWithProvider as mount } from 'test/utils' import * as _ from 'lodash' +import { PropsOf } from 'types/utils' -export type CollectionShorthandTestOptions = { - mapsValueToProp?: string - skipArrayOfStrings?: boolean +export type CollectionShorthandTestOptions = { + mapsValueToProp: keyof (TProps & React.HTMLProps) | false } export const DefaultCollectionShorthandTestOptions: CollectionShorthandTestOptions = { mapsValueToProp: 'content', - skipArrayOfStrings: false, } -export default Component => { +export type CollectionShorthandPropTestsRunner = < + TShorthandComponent extends React.ComponentType +>( + shorthandProp: keyof PropsOf, + ShorthandComponent: TShorthandComponent, + options?: CollectionShorthandTestOptions>, +) => any + +export type CollectionShorthandPropTestsFactory = ( + Component: TComponent, +) => CollectionShorthandPropTestsRunner + +export default ((Component: React.ComponentType) => { return function implementsCollectionShorthandProp( shorthandPropertyName: string, ShorthandComponent: React.ComponentType, options: CollectionShorthandTestOptions = DefaultCollectionShorthandTestOptions, ) { - const { mapsValueToProp } = options + const mapsValueToProp = options.mapsValueToProp as string describe(`shorthand property for '${ShorthandComponent.displayName}'`, () => { test(`is defined`, () => { expect(Component.propTypes[shorthandPropertyName]).toBeTruthy() }) - if (!options.skipArrayOfStrings) { + if (options.mapsValueToProp) { test(`array of string values is spread as ${ ShorthandComponent.displayName }s' ${mapsValueToProp}`, () => { @@ -73,4 +84,4 @@ export default Component => { }) }) } -} +}) as CollectionShorthandPropTestsFactory diff --git a/test/specs/commonTests/implementsShorthandProp.tsx b/test/specs/commonTests/implementsShorthandProp.tsx index b498e532cc..2d2cf289b7 100644 --- a/test/specs/commonTests/implementsShorthandProp.tsx +++ b/test/specs/commonTests/implementsShorthandProp.tsx @@ -1,23 +1,35 @@ import * as React from 'react' import { ReactWrapper } from 'enzyme' import { mountWithProvider } from 'test/utils' -import { Props } from '../../../types/utils' +import { Props, PropsOf } from '../../../types/utils' -export type ShorthandTestOptions = { - mapsValueToProp?: string +export type ShorthandTestOptions = { + mapsValueToProp: keyof (TProps & React.HTMLProps) | false } export const DefaultShorthandTestOptions: ShorthandTestOptions = { mapsValueToProp: 'content', } -export default Component => { +export type ShorthandPropTestsRunner = < + TShorthandComponent extends React.ComponentType +>( + shorthandProp: keyof PropsOf, + ShorthandComponent: TShorthandComponent, + options?: ShorthandTestOptions>, +) => any + +export type ShorthandPropTestsFactory = ( + Component: TComponent, +) => ShorthandPropTestsRunner + +export default ((Component: React.ComponentType) => { return function implementsShorthandProp( shorthandProp: string, ShorthandComponent: React.ComponentType, options: ShorthandTestOptions = DefaultShorthandTestOptions, ) { - const { mapsValueToProp } = options + const mapsValueToProp = options.mapsValueToProp as string const { displayName } = ShorthandComponent const checkPropsMatch = (props: Props, matchedProps: Props) => @@ -46,13 +58,15 @@ export default Component => { expect(Component.propTypes[shorthandProp]).toBeTruthy() }) - test(`string value is handled as ${displayName}'s ${mapsValueToProp}`, () => { - expectShorthandPropsAreHandled('shorthand prop value') - }) + if (options.mapsValueToProp) { + test(`string value is handled as ${displayName}'s ${mapsValueToProp}`, () => { + expectShorthandPropsAreHandled('shorthand prop value') + }) + } test(`object value is spread as ${displayName}'s props`, () => { expectShorthandPropsAreHandled({ foo: 'foo value', bar: 'bar value' }) }) }) } -} +}) as ShorthandPropTestsFactory diff --git a/test/specs/components/List/List-test.ts b/test/specs/components/List/List-test.ts index 01de79309c..60f4471d91 100644 --- a/test/specs/components/List/List-test.ts +++ b/test/specs/components/List/List-test.ts @@ -9,5 +9,5 @@ const listImplementsCollectionShorthandProp = implementsCollectionShorthandProp( describe('List', () => { isConformant(List) handlesAccessibility(List, { defaultRootRole: 'list' }) - listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'main' }) + listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'content' }) }) diff --git a/test/specs/components/RadioGroup/RadioGroup-test.tsx b/test/specs/components/RadioGroup/RadioGroup-test.tsx index bfebdd896d..248b4155a6 100644 --- a/test/specs/components/RadioGroup/RadioGroup-test.tsx +++ b/test/specs/components/RadioGroup/RadioGroup-test.tsx @@ -55,8 +55,7 @@ describe('RadioGroup', () => { describe('implementsCollectionShorthandProp', () => { radioGroupImplementsCollectionShorthandProp('items', RadioGroupItem, { - mapsValueToProp: 'content', - skipArrayOfStrings: true, + mapsValueToProp: false, }) }) diff --git a/test/specs/lib/factories-test.tsx b/test/specs/lib/factories-test.tsx index 279bce263c..b5b4ef4763 100644 --- a/test/specs/lib/factories-test.tsx +++ b/test/specs/lib/factories-test.tsx @@ -153,7 +153,7 @@ describe('factories', () => { }) test('does not throw if passed a function Component', () => { - const goodUsage = () => createShorthandFactory(() =>
, '') + const goodUsage = () => createShorthandFactory(() =>
, 'children') expect(goodUsage).not.toThrowError() }) diff --git a/types/utils.d.ts b/types/utils.d.ts index 8d7078163c..42a517e27d 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -26,6 +26,17 @@ export type Props = ObjectOf export type ReactChildren = React.ReactNodeArray | React.ReactNode export type ComponentEventHandler = (event: React.SyntheticEvent, data: TProps) => void +type ChildrenProps = { children: any } +export type PropsOf = T extends React.ComponentClass> + ? (ChildrenProps & TProps) + : T extends React.ComponentClass + ? (ChildrenProps & TProps) + : T extends React.StatelessComponent> + ? (ChildrenProps & TProps) + : T extends React.StatelessComponent + ? (ChildrenProps & TProps) + : any + // ======================================================== // Shorthand Factories // ========================================================