-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[utils] Add component propType (#13816)
* [utils] Add component propType * use the componentPropType everywhere
- Loading branch information
1 parent
57ae964
commit 5be50c8
Showing
76 changed files
with
253 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import * as ReactIs from 'react-is'; | ||
|
||
/** | ||
* A factory that returns a propTypes validator that only accepts values that | ||
* are also accepted by React.createElement | ||
* e.g. "div", functional, class components, forwardRef etc. | ||
* | ||
* @param {boolean} isRequired If `true` returns a validator | ||
* that will throw if nullish values are passed | ||
*/ | ||
function createComponentProp(isRequired) { | ||
/* istanbul ignore if */ | ||
if (process.env.NODE_ENV === 'production') { | ||
return () => null; | ||
} | ||
|
||
return function componentPropType(props, key, componentName, location, propFullName) { | ||
const prop = props[key]; | ||
const propName = propFullName || key; | ||
let message; | ||
|
||
if (prop == null) { | ||
if (isRequired) { | ||
message = | ||
`The ${location} \`${propName}\` is marked as required in \`${componentName}\`, ` + | ||
`but its value is \`${typeof prop}\`.`; | ||
} | ||
} else if (!ReactIs.isValidElementType(prop)) { | ||
const preciseType = typeof prop; | ||
message = | ||
`Invalid ${location} \`${propName}\` of type \`${preciseType}\` ` + | ||
`supplied to \`${componentName}\`, expected a component.`; | ||
} | ||
|
||
if (message != null) { | ||
// change error message slightly on every check to prevent caching when testing | ||
// which would not trigger console errors on subsequent fails | ||
return new Error(`${message}${process.env.NODE_ENV === 'test' ? Date.now() : ''}`); | ||
} | ||
|
||
return null; | ||
}; | ||
} | ||
|
||
const componentPropType = createComponentProp(false); | ||
componentPropType.isRequired = createComponentProp(true); | ||
|
||
export default componentPropType; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { assert } from 'chai'; | ||
import PropTypes from 'prop-types'; | ||
import React from 'react'; | ||
import consoleErrorMock from 'test/utils/consoleErrorMock'; | ||
import componentPropType from './componentPropType'; | ||
|
||
describe('componentPropType', () => { | ||
function testPropType(value, validator, expectedError) { | ||
const propName = 'children'; | ||
const componentName = 'ComponentName'; | ||
const location = 'prop'; | ||
|
||
PropTypes.checkPropTypes( | ||
{ | ||
[propName]: validator, | ||
}, | ||
{ | ||
[propName]: value, | ||
}, | ||
location, | ||
componentName, | ||
); | ||
|
||
if (expectedError != null) { | ||
assert.strictEqual(consoleErrorMock.callCount(), 1); | ||
assert.include(consoleErrorMock.args()[0][0], expectedError); | ||
} else { | ||
assert.strictEqual(consoleErrorMock.callCount(), 0); | ||
} | ||
} | ||
|
||
beforeEach(() => { | ||
consoleErrorMock.spy(); | ||
}); | ||
|
||
afterEach(() => { | ||
consoleErrorMock.reset(); | ||
}); | ||
|
||
it('describe .isRequired', () => { | ||
it('rejectes null', () => { | ||
testPropType( | ||
undefined, | ||
componentPropType.isRequired, | ||
'The prop `children` is marked as required in `ComponentName`, ' + | ||
'but its value is `undefined`.', | ||
); | ||
}); | ||
|
||
it('rejects undefined', () => { | ||
testPropType( | ||
null, | ||
componentPropType.isRequired, | ||
'The prop `children` is marked as required in `ComponentName`, but its value is `object`.', | ||
); | ||
}); | ||
}); | ||
|
||
it('supports optional props', () => { | ||
testPropType(undefined, componentPropType, null); | ||
testPropType(null, componentPropType, null); | ||
}); | ||
|
||
it('accepts strings, class and functional components', () => { | ||
// eslint-disable-next-line react/prefer-stateless-function | ||
class ClassComponent extends React.Component { | ||
render() { | ||
return null; | ||
} | ||
} | ||
|
||
testPropType(ClassComponent, componentPropType, null); | ||
testPropType(() => null, componentPropType, null); | ||
testPropType('will accept any string though', componentPropType, null); | ||
}); | ||
|
||
it('rejects other types with their type hint', () => { | ||
testPropType( | ||
1, | ||
componentPropType, | ||
'Invalid prop `children` of type `number` supplied to `ComponentName`, expected a component', | ||
); | ||
}); | ||
|
||
it('rejects objects', () => { | ||
testPropType( | ||
{}, | ||
componentPropType, | ||
'Invalid prop `children` of type `object` supplied to `ComponentName`, expected a component', | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export { default as componentPropType } from './componentPropType'; | ||
export { default as exactProp } from './exactProp'; | ||
export { default as getDisplayName } from './getDisplayName'; | ||
export { default as ponyfillGlobal } from './ponyfillGlobal'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.