Skip to content

Commit 26daf99

Browse files
authored
chore: remove usages of lodash/fp in customPropTypes (#4073)
* update unit tests * remove customPropTypes.some, rewrite propTypes to avoid lodash/fp * remove only * fix use proper prop type
1 parent 42f6e64 commit 26daf99

File tree

4 files changed

+162
-91
lines changed

4 files changed

+162
-91
lines changed

src/collections/Form/FormField.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ FormField.propTypes = {
163163
* Extra FormField props are passed to the control component.
164164
* Mutually exclusive with children.
165165
*/
166-
control: customPropTypes.some([
167-
PropTypes.func,
166+
control: PropTypes.oneOfType([
167+
PropTypes.elementType,
168168
PropTypes.oneOf(['button', 'input', 'select', 'textarea']),
169169
]),
170170

src/elements/Button/Button.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ Button.propTypes = {
247247
fluid: PropTypes.bool,
248248

249249
/** Add an Icon by name, props object, or pass an <Icon />. */
250-
icon: customPropTypes.some([
250+
icon: PropTypes.oneOfType([
251251
PropTypes.bool,
252252
PropTypes.string,
253253
PropTypes.object,
@@ -258,7 +258,7 @@ Button.propTypes = {
258258
inverted: PropTypes.bool,
259259

260260
/** Add a Label by text, props object, or pass a <Label />. */
261-
label: customPropTypes.some([PropTypes.string, PropTypes.object, PropTypes.element]),
261+
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element]),
262262

263263
/** A labeled button can format a Label or Icon to appear on the left or right. */
264264
labelPosition: PropTypes.oneOf(['right', 'left']),

src/lib/customPropTypes.js

Lines changed: 45 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import _ from 'lodash/fp'
1+
import _ from 'lodash'
22
import PropTypes from 'prop-types'
3+
34
import leven from './leven'
45

56
const typeOf = (...args) => Object.prototype.toString.call(...args)
@@ -33,27 +34,31 @@ export const suggest = (suggestions) => {
3334
const findBestSuggestions = _.memoize((str) => {
3435
const propValueWords = str.split(' ')
3536

36-
return _.flow(
37-
_.map((suggestion) => {
38-
const suggestionWords = suggestion.split(' ')
39-
40-
const propValueScore = _.flow(
41-
_.map((x) => _.map((y) => leven(x, y), suggestionWords)),
42-
_.map(_.min),
43-
_.sum,
44-
)(propValueWords)
45-
46-
const suggestionScore = _.flow(
47-
_.map((x) => _.map((y) => leven(x, y), propValueWords)),
48-
_.map(_.min),
49-
_.sum,
50-
)(suggestionWords)
51-
52-
return { suggestion, score: propValueScore + suggestionScore }
53-
}),
54-
_.sortBy(['score', 'suggestion']),
55-
_.take(3),
56-
)(suggestions)
37+
return _.take(
38+
_.sortBy(
39+
_.map(suggestions, (suggestion) => {
40+
const suggestionWords = suggestion.split(' ')
41+
42+
const propValueScore = _.sum(
43+
_.map(
44+
_.map(propValueWords, (x) => _.map(suggestionWords, (y) => leven(x, y))),
45+
_.min,
46+
),
47+
)
48+
49+
const suggestionScore = _.sum(
50+
_.map(
51+
_.map(suggestionWords, (x) => _.map(propValueWords, (y) => leven(x, y))),
52+
_.min,
53+
),
54+
)
55+
56+
return { suggestion, score: propValueScore + suggestionScore }
57+
}),
58+
['score', 'suggestion'],
59+
),
60+
3,
61+
)
5762
})
5863
/* eslint-enable max-nested-callbacks */
5964

@@ -110,7 +115,9 @@ export const disallow = (disallowedProps) => (props, propName, componentName) =>
110115
}
111116

112117
// skip if prop is undefined
113-
if (_.isNil(props[propName]) || props[propName] === false) return
118+
if (_.isNil(props[propName]) || props[propName] === false) {
119+
return
120+
}
114121

115122
// find disallowed props with values
116123
const disallowed = disallowedProps.reduce((acc, disallowedProp) => {
@@ -146,53 +153,24 @@ export const every = (validators) => (props, propName, componentName, ...rest) =
146153
)
147154
}
148155

149-
const errors = _.flow(
150-
_.map((validator) => {
151-
if (typeof validator !== 'function') {
152-
throw new Error(
153-
`every() argument "validators" should contain functions, found: ${typeOf(validator)}.`,
154-
)
155-
}
156-
return validator(props, propName, componentName, ...rest)
157-
}),
158-
_.compact,
159-
)(validators)
156+
const errors = []
160157

161-
// we can only return one error at a time
162-
return errors[0]
163-
}
158+
validators.forEach((validator) => {
159+
if (typeof validator !== 'function') {
160+
throw new Error(
161+
`every() argument "validators" should contain functions, found: ${typeOf(validator)}.`,
162+
)
163+
}
164164

165-
/**
166-
* Ensure a prop adherers to at least one of the given prop type validators.
167-
* @param {function[]} validators An array of propType functions.
168-
*/
169-
export const some = (validators) => (props, propName, componentName, ...rest) => {
170-
if (!Array.isArray(validators)) {
171-
throw new Error(
172-
[
173-
'Invalid argument supplied to some, expected an instance of array.',
174-
`See \`${propName}\` prop in \`${componentName}\`.`,
175-
].join(' '),
176-
)
177-
}
165+
const error = validator(props, propName, componentName, ...rest)
178166

179-
const errors = _.compact(
180-
_.map(validators, (validator) => {
181-
if (!_.isFunction(validator)) {
182-
throw new Error(
183-
`some() argument "validators" should contain functions, found: ${typeOf(validator)}.`,
184-
)
185-
}
186-
return validator(props, propName, componentName, ...rest)
187-
}),
188-
)
189-
190-
// fail only if all validators failed
191-
if (errors.length === validators.length) {
192-
const error = new Error('One of these validators must pass:')
193-
error.message += `\n${_.map(errors, (err, i) => `[${i + 1}]: ${err.message}`).join('\n')}`
194-
return error
195-
}
167+
if (error) {
168+
errors.push(error)
169+
}
170+
})
171+
172+
// we can only return one error at a time
173+
return errors[0]
196174
}
197175

198176
/**
Lines changed: 113 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,135 @@
1+
import PropTypes from 'prop-types'
12
import { customPropTypes } from 'src/lib'
23

3-
describe('suggest prop type', () => {
4-
it('should throw error when non-array argument given', () => {
5-
expect(() => customPropTypes.suggest('foo')).to.throw(
6-
Error,
7-
/Invalid argument supplied to suggest, expected an instance of array./,
8-
)
4+
import { consoleUtil, sandbox } from 'test/utils'
5+
6+
/* eslint-disable no-console */
7+
8+
describe('customPropTypes', () => {
9+
beforeEach(() => {
10+
consoleUtil.disable()
11+
sandbox.spy(console, 'error')
912
})
1013

11-
it('should return undefined when prop is valid', () => {
12-
const propType = customPropTypes.suggest(['foo', 'bar', 'baz'])
13-
expect(propType({ name: 'bar' }, 'name', 'FooComponent')).to.equal(undefined)
14+
describe('every', () => {
15+
it('should throw error when a non-array argument given', () => {
16+
PropTypes.checkPropTypes(
17+
{
18+
name: customPropTypes.every('foo'),
19+
},
20+
{ name: 'foo' },
21+
'name',
22+
'FooComponent',
23+
)
24+
25+
console.error.should.have.been.calledWithMatch(
26+
/Invalid argument supplied to every, expected an instance of array/,
27+
)
28+
})
29+
30+
it('should throw error when a non-function argument given', () => {
31+
PropTypes.checkPropTypes(
32+
{
33+
name: customPropTypes.every([{}]),
34+
},
35+
{ name: 'foo' },
36+
'name',
37+
'FooComponent',
38+
)
39+
40+
console.error.should.have.been.calledWithMatch(
41+
/argument "validators" should contain functions/,
42+
)
43+
})
44+
45+
it('should execute all validators', () => {
46+
PropTypes.checkPropTypes(
47+
{
48+
name: customPropTypes.every([PropTypes.string]),
49+
},
50+
{ name: 1 },
51+
'name',
52+
'FooComponent',
53+
)
54+
55+
console.error.should.have.been.calledWithMatch(
56+
/Invalid name `name` of type `number` supplied/,
57+
)
58+
})
59+
60+
it('should execute all validators including custom', () => {
61+
PropTypes.checkPropTypes(
62+
{
63+
name: customPropTypes.every([customPropTypes.suggest(['foo']), PropTypes.number]),
64+
},
65+
{ name: 'bar' },
66+
'name',
67+
'FooComponent',
68+
)
69+
70+
console.error.should.have.been.calledWithMatch(/Instead of `bar`, did you mean:/)
71+
})
1472
})
1573

16-
it('should return Error with suggestions when prop is invalid', () => {
17-
const propType = customPropTypes.suggest(['foo', 'bar', 'baz'])
18-
const props = { name: 'bad', title: 'bat words' }
74+
describe('suggest', () => {
75+
it('should throw error when non-array argument given', () => {
76+
expect(() => customPropTypes.suggest('foo')).to.throw(
77+
Error,
78+
/Invalid argument supplied to suggest, expected an instance of array./,
79+
)
80+
})
81+
82+
it('should return undefined when prop is valid', () => {
83+
PropTypes.checkPropTypes(
84+
{
85+
name: customPropTypes.suggest(['foo', 'bar', 'baz']),
86+
},
87+
{ name: 'foo' },
88+
'name',
89+
'FooComponent',
90+
)
1991

20-
const resultFooComponent = propType(props, 'name', 'FooComponent')
21-
expect(resultFooComponent).to.be.an.instanceof(Error)
22-
expect(resultFooComponent.message).to
23-
.equal(`Invalid prop \`name\` of value \`bad\` supplied to \`FooComponent\`.
92+
console.error.should.have.not.been.called()
93+
})
94+
95+
it('should return Error with suggestions when prop is invalid', () => {
96+
PropTypes.checkPropTypes(
97+
{
98+
name: customPropTypes.suggest(['foo', 'bar', 'baz']),
99+
},
100+
{ name: 'bad' },
101+
'name',
102+
'FooComponent',
103+
)
104+
105+
console.error.should.have.been
106+
.calledWithMatch(`Invalid prop \`name\` of value \`bad\` supplied to \`FooComponent\`.
24107
25108
Instead of \`bad\`, did you mean:
26109
- bar
27110
- baz
28111
- foo
29112
`)
113+
})
114+
115+
it('should return Error with suggestions when prop contains multiple words and is invalid', () => {
116+
PropTypes.checkPropTypes(
117+
{
118+
name: customPropTypes.suggest(['foo', 'bar', 'baz']),
119+
},
120+
{ name: 'bat words' },
121+
'name',
122+
'FooComponent',
123+
)
30124

31-
const resultBarComponent = propType(props, 'title', 'BarComponent')
32-
expect(resultBarComponent).to.be.an.instanceof(Error)
33-
expect(resultBarComponent.message).to
34-
.equal(`Invalid prop \`title\` of value \`bat words\` supplied to \`BarComponent\`.
125+
console.error.should.have.been
126+
.calledWithMatch(`Invalid prop \`name\` of value \`bat words\` supplied to \`FooComponent\`.
35127
36128
Instead of \`bat words\`, did you mean:
37129
- bar
38130
- baz
39131
- foo
40132
`)
133+
})
41134
})
42135
})

0 commit comments

Comments
 (0)