Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Document the non supported children properties #11879

Merged
merged 2 commits into from
Jun 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import ButtonBase from '../ButtonBase';
import unsupportedProp from '../utils/unsupportedProp';

export const styles = theme => ({
root: {
Expand Down Expand Up @@ -105,6 +106,11 @@ class BottomNavigationAction extends React.Component {
}

BottomNavigationAction.propTypes = {
/**
* This property isn't supported.
* Use the `component` property if you need to change the children structure.
*/
children: unsupportedProp,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css-api) below for more details.
Expand Down
6 changes: 6 additions & 0 deletions packages/material-ui/src/Chip/Chip.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import keycode from 'keycode';
import CancelIcon from '../internal/svg-icons/Cancel';
import withStyles from '../styles/withStyles';
import { emphasize, fade } from '../styles/colorManipulator';
import unsupportedProp from '../utils/unsupportedProp';
import '../Avatar/Avatar'; // So we don't have any override priority issue.

export const styles = theme => {
Expand Down Expand Up @@ -202,6 +203,11 @@ Chip.propTypes = {
* Avatar element.
*/
avatar: PropTypes.element,
/**
* This property isn't supported.
* Use the `component` property if you need to change the children structure.
*/
children: unsupportedProp,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css-api) below for more details.
Expand Down
36 changes: 7 additions & 29 deletions packages/material-ui/src/Chip/Chip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ describe('<Chip />', () => {
let wrapper;

before(() => {
wrapper = shallow(
<Chip className="my-Chip" data-my-prop="woofChip">
Text Chip
</Chip>,
);
wrapper = shallow(<Chip className="my-Chip" data-my-prop="woofChip" />);
});

it('should render a div containing a span', () => {
Expand All @@ -56,11 +52,7 @@ describe('<Chip />', () => {

before(() => {
handleClick = () => {};
wrapper = shallow(
<Chip className="my-Chip" data-my-prop="woofChip" onClick={handleClick}>
Text Chip
</Chip>,
);
wrapper = shallow(<Chip className="my-Chip" data-my-prop="woofChip" onClick={handleClick} />);
});

it('should render a div containing a span', () => {
Expand All @@ -82,9 +74,7 @@ describe('<Chip />', () => {
it('should apply user value of tabIndex', () => {
wrapper = shallow(
// eslint-disable-next-line jsx-a11y/tabindex-no-positive
<Chip onClick={() => {}} tabIndex={5}>
{'Text Chip'}
</Chip>,
<Chip onClick={() => {}} tabIndex={5} />,
);
assert.strictEqual(wrapper.props().tabIndex, 5);
});
Expand Down Expand Up @@ -180,11 +170,7 @@ describe('<Chip />', () => {
it('should call onKeyDown when a key is pressed', () => {
const anyKeydownEvent = { keycode: keycode('p') };
const onKeyDownSpy = spy();
wrapper = mount(
<Chip classes={{}} onKeyDown={onKeyDownSpy}>
Text Chip
</Chip>,
);
wrapper = mount(<Chip classes={{}} onKeyDown={onKeyDownSpy} />);
wrapper.find('div').simulate('keydown', anyKeydownEvent);
assert.strictEqual(onKeyDownSpy.callCount, 1, 'should have called onKeyDown');
assert.strictEqual(
Expand All @@ -197,7 +183,7 @@ describe('<Chip />', () => {

describe('escape', () => {
it('should unfocus when a esc key is pressed', () => {
const wrapper2 = mount(<ChipNaked classes={{}}>Text Chip</ChipNaked>);
const wrapper2 = mount(<ChipNaked classes={{}} />);
const handleBlur = spy();
wrapper2.instance().chipRef.blur = handleBlur;
wrapper2.find('div').simulate('keydown', {
Expand All @@ -212,11 +198,7 @@ describe('<Chip />', () => {
let onClickSpy;
before(() => {
onClickSpy = spy();
wrapper = mount(
<ChipNaked classes={{}} onClick={onClickSpy}>
Text Chip
</ChipNaked>,
);
wrapper = mount(<ChipNaked classes={{}} onClick={onClickSpy} />);
});

afterEach(() => {
Expand Down Expand Up @@ -251,11 +233,7 @@ describe('<Chip />', () => {
describe('onDelete is defined and `backspace` is pressed', () => {
it('should call onDelete', () => {
const onDeleteSpy = spy();
const wrapper2 = mount(
<ChipNaked classes={{}} onDelete={onDeleteSpy}>
Text Chip
</ChipNaked>,
);
const wrapper2 = mount(<ChipNaked classes={{}} onDelete={onDeleteSpy} />);

const preventDefaultSpy = spy();
const backspaceKeydownEvent = {
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/CssBaseline/CssBaseline.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ CssBaseline.propTypes = {
classes: PropTypes.object.isRequired,
};

CssBaseline.propTypes = exactProp(CssBaseline.propTypes, 'CssBaseline');
CssBaseline.propTypes = exactProp(CssBaseline.propTypes);

CssBaseline.defaultProps = {
children: null,
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/Hidden/HiddenJs.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,6 @@ HiddenJs.propTypes = {
xsUp: PropTypes.bool,
};

HiddenJs.propTypes = exactProp(HiddenJs.propTypes, 'HiddenJs');
HiddenJs.propTypes = exactProp(HiddenJs.propTypes);

export default withWidth()(HiddenJs);
2 changes: 1 addition & 1 deletion packages/material-ui/src/Portal/Portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@ Portal.propTypes = {
onRendered: PropTypes.func,
};

Portal.propTypes = exactProp(Portal.propTypes, 'Portal');
Portal.propTypes = exactProp(Portal.propTypes);

export default Portal;
2 changes: 1 addition & 1 deletion packages/material-ui/src/RootRef/RootRef.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ RootRef.propTypes = {
rootRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
};

RootRef.propTypes = exactProp(RootRef.propTypes, 'RootRef');
RootRef.propTypes = exactProp(RootRef.propTypes);

export default RootRef;
6 changes: 6 additions & 0 deletions packages/material-ui/src/Tab/Tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import ButtonBase from '../ButtonBase';
import { capitalize } from '../utils/helpers';
import unsupportedProp from '../utils/unsupportedProp';

export const styles = theme => ({
root: {
Expand Down Expand Up @@ -200,6 +201,11 @@ class Tab extends React.Component {
}

Tab.propTypes = {
/**
* This property isn't supported.
* Use the `component` property if you need to change the children structure.
*/
children: unsupportedProp,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css-api) below for more details.
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/styles/MuiThemeProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ MuiThemeProvider.propTypes = {
theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
};

MuiThemeProvider.propTypes = exactProp(MuiThemeProvider.propTypes, 'MuiThemeProvider');
MuiThemeProvider.propTypes = exactProp(MuiThemeProvider.propTypes);

MuiThemeProvider.childContextTypes = {
...themeListener.contextTypes,
Expand Down
22 changes: 15 additions & 7 deletions packages/material-ui/src/utils/exactProp.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@
// However, in order to reduce the number of dependencies and to remove some extra safe checks
// the module was forked.

// Only exported for test purposes.
export const specialProperty = 'exact-prop: \u200b';

export default function exactProp(propTypes: Object, componentNameInError: string) {
function exactProp(propTypes: Object) {
/* istanbul ignore if */
if (process.env.NODE_ENV === 'production') {
return propTypes;
}

return {
...propTypes,
// eslint-disable-next-line prefer-arrow-callback
[specialProperty]: props => {
const unknownProps = Object.keys(props).filter(prop => !propTypes.hasOwnProperty(prop));
if (unknownProps.length > 0) {
return new TypeError(
`${componentNameInError}: unknown props found: ${unknownProps.join(
', ',
)}. Please remove the unknown properties.`,
const unsupportedProps = Object.keys(props).filter(prop => !propTypes.hasOwnProperty(prop));
if (unsupportedProps.length > 0) {
return new Error(
`The following properties are not supported: ${unsupportedProps
.map(prop => `\`${prop}\``)
.join(', ')}. Please remove them.`,
);
}
return null;
},
};
}

export default exactProp;
23 changes: 10 additions & 13 deletions packages/material-ui/src/utils/exactProp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ import { assert } from 'chai';
import exactProp, { specialProperty } from './exactProp';

describe('exactProp()', () => {
const componentNameInError = 'componentNameInError';
let exactPropTypes;

before(() => {
exactPropTypes = exactProp(
{
bar: {},
},
componentNameInError,
);
exactPropTypes = exactProp({
bar: {},
});
});

it('should have the right shape', () => {
Expand All @@ -22,22 +18,23 @@ describe('exactProp()', () => {
});

describe('exactPropTypes', () => {
let props;

it('should return null for supported properties', () => {
props = {
const props = {
bar: false,
};
const result = exactPropTypes[specialProperty](props);
assert.strictEqual(result, null);
});

it('should return an error for unknown properties', () => {
props = {
it('should return an error for unsupported properties', () => {
const props = {
foo: true,
};
const result = exactPropTypes[specialProperty](props);
assert.match(result.message, /componentNameInError: unknown props found: foo/);
assert.match(
result.message,
/The following properties are not supported: `foo`. Please remove them/,
);
});
});
});
9 changes: 7 additions & 2 deletions packages/material-ui/src/utils/requirePropFactory.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// @flow weak

const requirePropFactory = (componentNameInError: string) => {
function requirePropFactory(componentNameInError: string) {
/* istanbul ignore if */
if (process.env.NODE_ENV === 'production') {
return () => null;
}

const requireProp = (requiredProp: string) => (
props: Object,
propName: string,
Expand All @@ -20,6 +25,6 @@ const requirePropFactory = (componentNameInError: string) => {
return null;
};
return requireProp;
};
}

export default requirePropFactory;
24 changes: 24 additions & 0 deletions packages/material-ui/src/utils/unsupportedProp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @flow

function unsupportedProp(
props: Object,
propName: string,
componentName?: string,
location?: string,
propFullName?: string,
) {
/* istanbul ignore if */
if (process.env.NODE_ENV === 'production') {
return null;
}

const propFullNameSafe = propFullName || propName;

if (typeof props[propName] !== 'undefined') {
return new Error(`The property \`${propFullNameSafe}\` is not supported. Please remove it.`);
}

return null;
}

export default unsupportedProp;
23 changes: 23 additions & 0 deletions packages/material-ui/src/utils/unsupportedProp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { assert } from 'chai';
import unsupportedProp from './unsupportedProp';

describe('unsupportedProp', () => {
const propName = 'children';
const componentName = 'ComponentName';
const location = 'prop';
const propFullName = null;

it('should return null for supported properties', () => {
const props = {};
const result = unsupportedProp(props, propName, componentName, location, propFullName);
assert.strictEqual(result, null);
});

it('should return an error for unsupported properties', () => {
const props = {
children: null,
};
const result = unsupportedProp(props, propName, componentName, location, propFullName);
assert.match(result.message, /The property `children` is not supported. Please remove it/);
});
});
1 change: 1 addition & 0 deletions pages/api/bottom-navigation-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ filename: /packages/material-ui/src/BottomNavigationAction/BottomNavigationActio

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name">children</span> | <span class="prop-type">unsupportedProp |   | This property isn't supported. Use the `component` property if you need to change the children structure. |
| <span class="prop-name">classes</span> | <span class="prop-type">object |   | Override or extend the styles applied to the component. See [CSS API](#css-api) below for more details. |
| <span class="prop-name">icon</span> | <span class="prop-type">node |   | The icon element. |
| <span class="prop-name">label</span> | <span class="prop-type">node |   | The label element. |
Expand Down
1 change: 1 addition & 0 deletions pages/api/chip.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Chips represent complex entities in small blocks, such as a contact.
| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name">avatar</span> | <span class="prop-type">element |   | Avatar element. |
| <span class="prop-name">children</span> | <span class="prop-type">unsupportedProp |   | This property isn't supported. Use the `component` property if you need to change the children structure. |
| <span class="prop-name">classes</span> | <span class="prop-type">object |   | Override or extend the styles applied to the component. See [CSS API](#css-api) below for more details. |
| <span class="prop-name">clickable</span> | <span class="prop-type">bool | <span class="prop-default">false</span> | If true, the chip will appear clickable, and will raise when pressed, even if the onClick property is not defined. This can be used, for example, along with the component property to indicate an anchor Chip is clickable. |
| <span class="prop-name">component</span> | <span class="prop-type">union:&nbsp;string&nbsp;&#124;<br>&nbsp;func&nbsp;&#124;<br>&nbsp;object<br> | <span class="prop-default">'div'</span> | The component used for the root node. Either a string to use a DOM element or a component. |
Expand Down
1 change: 1 addition & 0 deletions pages/api/tab.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ filename: /packages/material-ui/src/Tab/Tab.js

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name">children</span> | <span class="prop-type">unsupportedProp |   | This property isn't supported. Use the `component` property if you need to change the children structure. |
| <span class="prop-name">classes</span> | <span class="prop-type">object |   | Override or extend the styles applied to the component. See [CSS API](#css-api) below for more details. |
| <span class="prop-name">disabled</span> | <span class="prop-type">bool | <span class="prop-default">false</span> | If `true`, the tab will be disabled. |
| <span class="prop-name">icon</span> | <span class="prop-type">node |   | The icon element. |
Expand Down