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

Commit 763f9f4

Browse files
author
Alexandru Buliga
authored
fix(factories): shorthand fix for applying props to react element (#496)
* addressed PR comments * addressed all comments * addressed comments and reimplemented to return element as is * fixed tests * fixed examples and removed manual check for `Input` `wrapper` styles * fixed changelog entry * addressed comments from layershifter * addressed Roman's comments * remove unsupported example
1 parent fab64ab commit 763f9f4

12 files changed

+104
-146
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2929
- Add `https` protocol to all urls used in the scripts and stylesheets in index.ejs @mnajdova ([#571](https://github.com/stardust-ui/react/pull/571))
3030
- Fix support for fallback values in styles (`color: ['#ccc', 'rgba(0, 0, 0, 0.5)']`) @miroslavstastny ([#573](https://github.com/stardust-ui/react/pull/573))
3131
- Fix styles for RTL mode of doc site component examples @kuzhelov ([#579](https://github.com/stardust-ui/react/pull/579))
32+
- Prevent blind props forwarding for `createShorthand` calls if the value is a React element and remove manual check for `Input` `wrapper` @Bugaa92 ([#496](https://github.com/stardust-ui/react/pull/496))
3233

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

docs/src/examples/components/Input/Variations/InputExampleInputSlot.shorthand.tsx

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,6 @@ const InputExampleInputSlot = () => (
2222
styles: inputStyles,
2323
}}
2424
/>
25-
26-
<Text content="Wrapped Input with existing component:" />
27-
<Input
28-
placeholder="Search..."
29-
role="presentation"
30-
input={
31-
<Text
32-
as="input"
33-
placeholder="Placeholder Override..."
34-
role="checkbox"
35-
styles={inputStyles}
36-
/>
37-
}
38-
/>
3925
</Grid>
4026
)
4127

docs/src/examples/components/Input/Variations/InputExampleWrapperSlot.shorthand.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,6 @@ const InputExampleWrapperSlot = () => (
2323
styles: { padding: '5px', backgroundColor: 'red' },
2424
}}
2525
/>
26-
27-
<Text content="Wrapped Input with existing component:" />
28-
<Input
29-
placeholder="Search..."
30-
tabIndex={-1}
31-
styles={{ color: 'blue', backgroundColor: 'yellow' }}
32-
wrapper={<Text tabIndex={0} styles={{ padding: '5px', backgroundColor: 'red' }} />}
33-
/>
34-
35-
<Text content="Wrapped Input with custom element:" />
36-
<Input
37-
placeholder="Search..."
38-
tabIndex={-1}
39-
styles={{ color: 'blue', backgroundColor: 'yellow' }}
40-
wrapper={<span tabIndex={0} style={{ padding: '5px', backgroundColor: 'red' }} />}
41-
/>
4226
</Grid>
4327
)
4428

docs/src/examples/components/RadioGroup/Item/RadioGroupItemExample.shorthand.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const handleChange = () => {
88
const RadioGroupItemExample = () => (
99
<RadioGroup
1010
items={[
11-
<RadioGroup.Item key="1" label="Make your choice" value="1" checkedChanged={handleChange} />,
12-
<RadioGroup.Item key="2" label="Another option" value="2" checkedChanged={handleChange} />,
11+
{ key: '1', label: 'Make your choice', value: '1', checkedChanged: handleChange },
12+
{ key: '2', label: 'Another option', value: '2', checkedChanged: handleChange },
1313
]}
1414
/>
1515
)

docs/src/examples/components/RadioGroup/Item/RadioGroupItemExampleChecked.shorthand.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { RadioGroup } from '@stardust-ui/react'
44
const RadioGroupItemExampleCheckedShorthand = () => (
55
<RadioGroup
66
defaultCheckedValue="1"
7-
items={[<RadioGroup.Item key="1" label="This radio comes pre-checked" value="1" />]}
7+
items={[
8+
{ key: '1', label: 'This radio comes pre-checked', value: '1' },
9+
{ key: '2', label: 'This radio is not pre-checked', value: '2' },
10+
]}
811
/>
912
)
1013

docs/src/examples/components/RadioGroup/Item/RadioGroupItemExampleDisabled.shorthand.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { RadioGroup } from '@stardust-ui/react'
44
const RadioGroupItemExampleDisabledShorthand = () => (
55
<RadioGroup
66
items={[
7-
<RadioGroup.Item key="1" label="Disabled" value="1" disabled />,
8-
<RadioGroup.Item key="2" label="Enabled" value="2" />,
7+
{ key: '1', label: 'Disabled', value: '1', disabled: true },
8+
{ key: '2', label: 'Enabled', value: '2' },
99
]}
1010
/>
1111
)

docs/src/examples/components/RadioGroup/Types/RadioGroupColorPickerExample.shorthand.tsx

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,33 @@ import React from 'react'
22
import { Divider, RadioGroup } from '@stardust-ui/react'
33

44
class RadioGroupColorPickerExample extends React.Component {
5-
createIcon = value => {
5+
state = { selectedValue: '' }
6+
7+
render() {
8+
const { selectedValue } = this.state
9+
return (
10+
<div>
11+
The selected value is: {selectedValue}
12+
<Divider />
13+
<RadioGroup
14+
defaultCheckedValue="pink"
15+
items={['pink', 'blue', 'green', 'red', 'orange'].map(color => ({
16+
key: color,
17+
value: color,
18+
name: color,
19+
'aria-label': color,
20+
icon: this.createIcon(color),
21+
}))}
22+
checkedValueChanged={(e, props) => this.setState({ selectedValue: props.value })}
23+
/>
24+
</div>
25+
)
26+
}
27+
28+
createIcon(value) {
629
const { selectedValue } = this.state
730
const isSelected = selectedValue === value
31+
832
return {
933
variables: {
1034
backgroundColor: value,
@@ -22,37 +46,6 @@ class RadioGroupColorPickerExample extends React.Component {
2246
},
2347
}
2448
}
25-
26-
items = () => {
27-
const colors = ['pink', 'blue', 'green', 'red', 'orange']
28-
return colors.map(color => (
29-
<RadioGroup.Item
30-
value={color}
31-
icon={this.createIcon(color)}
32-
name={color}
33-
key={color}
34-
aria-label={color}
35-
/>
36-
))
37-
}
38-
39-
state = { selectedValue: '' }
40-
handleChange = (e, props) => {
41-
this.setState({ selectedValue: props.value })
42-
}
43-
render() {
44-
const { selectedValue } = this.state
45-
return (
46-
<div>
47-
The selected value is: {selectedValue}
48-
<Divider />
49-
<RadioGroup
50-
defaultCheckedValue="pink"
51-
items={this.items()}
52-
checkedValueChanged={this.handleChange}
53-
/>
54-
</div>
55-
)
56-
}
5749
}
50+
5851
export default RadioGroupColorPickerExample

src/components/Input/Input.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,8 @@ class Input extends AutoControlledComponent<Extendable<InputProps>, InputState>
146146
})}
147147
</>
148148
),
149+
styles: styles.root,
149150
...rest,
150-
151-
// do not pass Stardust 'styles' prop
152-
// in case if React Element was used to define 'wrapper'
153-
...(!React.isValidElement(wrapper) && {
154-
styles: styles.root,
155-
}),
156151
},
157152
overrideProps: {
158153
as: (wrapper && (wrapper as any).as) || ElementType,

src/lib/factories.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,18 @@ function createShorthandFromValue(
126126
)
127127
}
128128

129+
// return value 'as is' if it a ReactElement
130+
if (valIsReactElement) {
131+
return value as React.ReactElement<Props>
132+
}
133+
129134
// ----------------------------------------
130135
// Build up props
131136
// ----------------------------------------
132137
const { defaultProps = {} } = options
133138

134139
// User's props
135-
const usersProps =
136-
(valIsReactElement && (value as React.ReactElement<Props>).props) ||
137-
(valIsPropsObject && (value as Props)) ||
138-
{}
140+
const usersProps = valIsPropsObject ? (value as Props) : {}
139141

140142
// Override props
141143
let { overrideProps } = options
@@ -191,9 +193,6 @@ function createShorthandFromValue(
191193
return render(Component, props)
192194
}
193195

194-
// Clone ReactElements
195-
if (valIsReactElement) return React.cloneElement(value as React.ReactElement<Props>, props)
196-
197196
// Create ReactElements from built up props
198197
if (valIsPrimitive || valIsPropsObject) return <Component {...props} />
199198

test/specs/commonTests/implementsWrapperProp.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import { ShorthandValue } from 'utils'
77

88
export interface ImplementsWrapperPropOptions {
99
wrapppedComponentSelector: any
10-
wrappperComponentSelector?: any
10+
WrapperComponent?: any
1111
}
1212

1313
const implementsWrapperProp = <P extends { wrapper: ShorthandValue }>(
1414
Component: React.ReactType<P>,
1515
options: ImplementsWrapperPropOptions,
1616
) => {
17-
const { wrapppedComponentSelector, wrappperComponentSelector = Slot.defaultProps.as } = options
17+
const { wrapppedComponentSelector, WrapperComponent = Slot } = options
1818

1919
const wrapperTests = (wrapper: ReactWrapper) => {
2020
expect(wrapper.length).toBeGreaterThan(0)
@@ -23,11 +23,7 @@ const implementsWrapperProp = <P extends { wrapper: ShorthandValue }>(
2323

2424
describe('"wrapper" prop', () => {
2525
it('wraps the component by default', () => {
26-
wrapperTests(mount(<Component />).find(wrappperComponentSelector))
27-
})
28-
29-
it('wraps the component with a custom element', () => {
30-
wrapperTests(mount(<Component wrapper={<span />} />).find('span'))
26+
wrapperTests(mount(<Component />).find(WrapperComponent))
3127
})
3228

3329
it('wraps the component with a custom element using "as" prop', () => {

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

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ describe('RadioGroup', () => {
6060
})
6161
})
6262

63-
const itemsTest = getItems => {
63+
const itemsTest = (getItems: Function, isShorthandApiTest: boolean = true) => {
6464
it('renders children', () => {
6565
const items = mountWithProvider(<RadioGroup items={getItems()} />).find('RadioGroupItem')
6666

@@ -101,31 +101,33 @@ describe('RadioGroup', () => {
101101
})
102102
})
103103

104-
describe('click event handler', () => {
105-
it('should set the value when item is clicked', () => {
106-
const checkedValueChanged = jest.fn()
107-
const wrapper = mountWithProvider(
108-
<RadioGroup items={getItems()} checkedValueChanged={checkedValueChanged} />,
109-
)
110-
const radioGroupItems = wrapper.find('RadioGroupItem')
104+
if (isShorthandApiTest) {
105+
describe('click event handler', () => {
106+
it('should set "checked" when item is clicked', () => {
107+
const checkedValueChanged = jest.fn()
108+
const wrapper = mountWithProvider(
109+
<RadioGroup items={getItems()} checkedValueChanged={checkedValueChanged} />,
110+
)
111+
const radioGroupItems = wrapper.find('RadioGroupItem')
111112

112-
radioGroupItems
113-
.at(1)
114-
.find('div')
115-
.first()
116-
.simulate('click')
113+
radioGroupItems
114+
.at(1)
115+
.find('div')
116+
.first()
117+
.simulate('click')
117118

118-
const updatedItems = wrapper.find('RadioGroupItem')
119+
const updatedItems = wrapper.find('RadioGroupItem')
119120

120-
expect(updatedItems.at(0).props().checked).toBe(false)
121-
expect(updatedItems.at(1).props().checked).toBe(true)
121+
expect(updatedItems.at(0).props().checked).toBe(false)
122+
expect(updatedItems.at(1).props().checked).toBe(true)
122123

123-
expect(checkedValueChanged).toHaveBeenCalledWith(
124-
expect.anything(),
125-
expect.objectContaining({ value: 'test-value2' }),
126-
)
124+
expect(checkedValueChanged).toHaveBeenCalledWith(
125+
expect.anything(),
126+
expect.objectContaining({ value: 'test-value2' }),
127+
)
128+
})
127129
})
128-
})
130+
}
129131

130132
it('should not call checkedValueChanged when index did not change', () => {
131133
const checkedValueChanged = jest.fn()
@@ -147,21 +149,23 @@ describe('RadioGroup', () => {
147149
expect(checkedValueChanged).not.toHaveBeenCalled()
148150
})
149151

150-
it('should not set the value when disabled item is clicked', () => {
151-
const wrapper = mountWithProvider(<RadioGroup items={getItems({ disabledItem: 1 })} />)
152-
const radioGroupItems = wrapper.find('RadioGroupItem')
152+
if (isShorthandApiTest) {
153+
it('should not set "checked" when disabled item is clicked', () => {
154+
const wrapper = mountWithProvider(<RadioGroup items={getItems({ disabledItem: 1 })} />)
155+
const radioGroupItems = wrapper.find('RadioGroupItem')
153156

154-
radioGroupItems
155-
.at(1)
156-
.find('div')
157-
.first()
158-
.simulate('click')
157+
radioGroupItems
158+
.at(1)
159+
.find('div')
160+
.first()
161+
.simulate('click')
159162

160-
const updatedItems = wrapper.find('RadioGroupItem')
163+
const updatedItems = wrapper.find('RadioGroupItem')
161164

162-
expect(updatedItems.at(0).props().checked).toBe(false)
163-
expect(updatedItems.at(1).props().checked).toBe(false)
164-
})
165+
expect(updatedItems.at(0).props().checked).toBe(false)
166+
expect(updatedItems.at(1).props().checked).toBe(false)
167+
})
168+
}
165169

166170
describe('keyDown event handler', () => {
167171
const testKeyDown = (testName, items, initialValue, keyCode, expectedValue) => {
@@ -244,6 +248,6 @@ describe('RadioGroup', () => {
244248
})
245249
}
246250

247-
itemsTest(getChildrenItems)
251+
itemsTest(getChildrenItems, false)
248252
})
249253
})

0 commit comments

Comments
 (0)