Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit bf9d3e2

Browse files
authored
fix: names of mapped props for shorthand factories (#591)
* introduce fix and type checks * remove invalid test for RadioGroup * adjust typings * address problems of RadioGroup tests * update changelog
1 parent 3d1f4b8 commit bf9d3e2

File tree

11 files changed

+68
-26
lines changed

11 files changed

+68
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
4040
- Fix `Popup` not closing on outside click @kuzhelov ([#598](https://github.com/stardust-ui/react/pull/598))
4141
- Fix multiple React's warnings about keys in docs @layershifter ([#602](https://github.com/stardust-ui/react/pull/602))
4242
- Fix incorrect handling of `isFromKeyboard` in `Menu` @layershifter ([#596](https://github.com/stardust-ui/react/pull/596))
43+
- Fix property names used in shorthand factories @kuzhelov ([#591](https://github.com/stardust-ui/react/pull/591))
4344

4445
### Features
4546
- `Ref` components uses `forwardRef` API by default @layershifter ([#491](https://github.com/stardust-ui/react/pull/491))

src/components/Image/Image.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export interface ImageProps extends UIComponentProps {
2222

2323
/** An image can take up the width of its container. */
2424
fluid?: boolean
25+
26+
/** Image source URL. */
27+
src?: string
2528
}
2629

2730
/**

src/components/ItemLayout/ItemLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class ItemLayout extends UIComponent<Extendable<ItemLayoutProps>, any> {
208208
}
209209
}
210210

211-
ItemLayout.create = createShorthandFactory(ItemLayout, 'main')
211+
ItemLayout.create = createShorthandFactory(ItemLayout, 'content')
212212

213213
export default ItemLayout
214214

src/components/List/ListItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,6 @@ class ListItem extends UIComponent<Extendable<ListItemProps>, ListItemState> {
129129
}
130130
}
131131

132-
ListItem.create = createShorthandFactory(ListItem, 'main')
132+
ListItem.create = createShorthandFactory(ListItem, 'content')
133133

134134
export default ListItem

src/lib/factories.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as React from 'react'
44
import {
55
ShorthandValue,
66
Props,
7+
PropsOf,
78
ShorthandRenderCallback,
89
ShorthandRenderFunction,
910
ShorthandRenderer,
@@ -74,18 +75,20 @@ export function createShorthand(
7475
// ============================================================
7576
// Factory Creators
7677
// ============================================================
77-
7878
/**
7979
* @param {React.ReactType} Component A ReactClass or string
8080
* @param {string} mappedProp A function that maps a primitive value to the Component props
8181
* @returns {function} A shorthand factory function waiting for `val` and `defaultProps`.
8282
*/
83-
export function createShorthandFactory(Component: React.ReactType, mappedProp?: string) {
83+
export function createShorthandFactory<T extends React.ReactType>(
84+
Component: T,
85+
mappedProp?: keyof PropsOf<T>,
86+
) {
8487
if (typeof Component !== 'function' && typeof Component !== 'string') {
8588
throw new Error('createShorthandFactory() Component must be a string or function.')
8689
}
8790

88-
return (val, options) => createShorthand(Component, mappedProp, val, options)
91+
return (val, options) => createShorthand(Component, mappedProp as string, val, options)
8992
}
9093

9194
// ============================================================

test/specs/commonTests/implementsCollectionShorthandProp.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,42 @@
11
import * as React from 'react'
22
import { mountWithProvider as mount } from 'test/utils'
33
import * as _ from 'lodash'
4+
import { PropsOf } from 'types/utils'
45

5-
export type CollectionShorthandTestOptions = {
6-
mapsValueToProp?: string
7-
skipArrayOfStrings?: boolean
6+
export type CollectionShorthandTestOptions<TProps = any> = {
7+
mapsValueToProp: keyof (TProps & React.HTMLProps<HTMLElement>) | false
88
}
99

1010
export const DefaultCollectionShorthandTestOptions: CollectionShorthandTestOptions = {
1111
mapsValueToProp: 'content',
12-
skipArrayOfStrings: false,
1312
}
1413

15-
export default Component => {
14+
export type CollectionShorthandPropTestsRunner<TComponent> = <
15+
TShorthandComponent extends React.ComponentType
16+
>(
17+
shorthandProp: keyof PropsOf<TComponent>,
18+
ShorthandComponent: TShorthandComponent,
19+
options?: CollectionShorthandTestOptions<PropsOf<TShorthandComponent>>,
20+
) => any
21+
22+
export type CollectionShorthandPropTestsFactory = <TComponent extends React.ComponentType>(
23+
Component: TComponent,
24+
) => CollectionShorthandPropTestsRunner<TComponent>
25+
26+
export default ((Component: React.ComponentType) => {
1627
return function implementsCollectionShorthandProp(
1728
shorthandPropertyName: string,
1829
ShorthandComponent: React.ComponentType,
1930
options: CollectionShorthandTestOptions = DefaultCollectionShorthandTestOptions,
2031
) {
21-
const { mapsValueToProp } = options
32+
const mapsValueToProp = options.mapsValueToProp as string
2233

2334
describe(`shorthand property for '${ShorthandComponent.displayName}'`, () => {
2435
test(`is defined`, () => {
2536
expect(Component.propTypes[shorthandPropertyName]).toBeTruthy()
2637
})
2738

28-
if (!options.skipArrayOfStrings) {
39+
if (options.mapsValueToProp) {
2940
test(`array of string values is spread as ${
3041
ShorthandComponent.displayName
3142
}s' ${mapsValueToProp}`, () => {
@@ -73,4 +84,4 @@ export default Component => {
7384
})
7485
})
7586
}
76-
}
87+
}) as CollectionShorthandPropTestsFactory

test/specs/commonTests/implementsShorthandProp.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
11
import * as React from 'react'
22
import { ReactWrapper } from 'enzyme'
33
import { mountWithProvider } from 'test/utils'
4-
import { Props } from '../../../types/utils'
4+
import { Props, PropsOf } from '../../../types/utils'
55

6-
export type ShorthandTestOptions = {
7-
mapsValueToProp?: string
6+
export type ShorthandTestOptions<TProps = any> = {
7+
mapsValueToProp: keyof (TProps & React.HTMLProps<HTMLElement>) | false
88
}
99

1010
export const DefaultShorthandTestOptions: ShorthandTestOptions = {
1111
mapsValueToProp: 'content',
1212
}
1313

14-
export default Component => {
14+
export type ShorthandPropTestsRunner<TComponent> = <
15+
TShorthandComponent extends React.ComponentType
16+
>(
17+
shorthandProp: keyof PropsOf<TComponent>,
18+
ShorthandComponent: TShorthandComponent,
19+
options?: ShorthandTestOptions<PropsOf<TShorthandComponent>>,
20+
) => any
21+
22+
export type ShorthandPropTestsFactory = <TComponent extends React.ComponentType>(
23+
Component: TComponent,
24+
) => ShorthandPropTestsRunner<TComponent>
25+
26+
export default ((Component: React.ComponentType) => {
1527
return function implementsShorthandProp(
1628
shorthandProp: string,
1729
ShorthandComponent: React.ComponentType,
1830
options: ShorthandTestOptions = DefaultShorthandTestOptions,
1931
) {
20-
const { mapsValueToProp } = options
32+
const mapsValueToProp = options.mapsValueToProp as string
2133
const { displayName } = ShorthandComponent
2234

2335
const checkPropsMatch = (props: Props, matchedProps: Props) =>
@@ -46,13 +58,15 @@ export default Component => {
4658
expect(Component.propTypes[shorthandProp]).toBeTruthy()
4759
})
4860

49-
test(`string value is handled as ${displayName}'s ${mapsValueToProp}`, () => {
50-
expectShorthandPropsAreHandled('shorthand prop value')
51-
})
61+
if (options.mapsValueToProp) {
62+
test(`string value is handled as ${displayName}'s ${mapsValueToProp}`, () => {
63+
expectShorthandPropsAreHandled('shorthand prop value')
64+
})
65+
}
5266

5367
test(`object value is spread as ${displayName}'s props`, () => {
5468
expectShorthandPropsAreHandled({ foo: 'foo value', bar: 'bar value' })
5569
})
5670
})
5771
}
58-
}
72+
}) as ShorthandPropTestsFactory

test/specs/components/List/List-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ const listImplementsCollectionShorthandProp = implementsCollectionShorthandProp(
99
describe('List', () => {
1010
isConformant(List)
1111
handlesAccessibility(List, { defaultRootRole: 'list' })
12-
listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'main' })
12+
listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'content' })
1313
})

test/specs/components/RadioGroup/RadioGroup-test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ describe('RadioGroup', () => {
5555

5656
describe('implementsCollectionShorthandProp', () => {
5757
radioGroupImplementsCollectionShorthandProp('items', RadioGroupItem, {
58-
mapsValueToProp: 'content',
59-
skipArrayOfStrings: true,
58+
mapsValueToProp: false,
6059
})
6160
})
6261

test/specs/lib/factories-test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ describe('factories', () => {
153153
})
154154

155155
test('does not throw if passed a function Component', () => {
156-
const goodUsage = () => createShorthandFactory(() => <div />, '')
156+
const goodUsage = () => createShorthandFactory(() => <div />, 'children')
157157

158158
expect(goodUsage).not.toThrowError()
159159
})

types/utils.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ export type Props = ObjectOf<any>
2626
export type ReactChildren = React.ReactNodeArray | React.ReactNode
2727
export type ComponentEventHandler<TProps> = (event: React.SyntheticEvent, data: TProps) => void
2828

29+
type ChildrenProps = { children: any }
30+
export type PropsOf<T> = T extends React.ComponentClass<Extendable<infer TProps>>
31+
? (ChildrenProps & TProps)
32+
: T extends React.ComponentClass<infer TProps>
33+
? (ChildrenProps & TProps)
34+
: T extends React.StatelessComponent<Extendable<infer TProps>>
35+
? (ChildrenProps & TProps)
36+
: T extends React.StatelessComponent<infer TProps>
37+
? (ChildrenProps & TProps)
38+
: any
39+
2940
// ========================================================
3041
// Shorthand Factories
3142
// ========================================================

0 commit comments

Comments
 (0)