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

Allow a custom message with forbid props #2615

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions docs/rules/forbid-component-props.md
Expand Up @@ -42,16 +42,17 @@ The following patterns are **not** considered warnings:
### `forbid`

An array specifying the names of props that are forbidden. The default value of this option is `['className', 'style']`.
Each array element can either be a string with the property name or object specifying the property name and a component whitelist:
Each array element can either be a string with the property name or object specifying the property name, an optional
custom message, and a component whitelist:

```js
{
"propName": "someProp",
"allowedFor": [SomeComponent, AnotherComponent]
"allowedFor": [SomeComponent, AnotherComponent],
"message": "Avoid using someProp"
}
```


### Related rules

- [forbid-dom-props](./forbid-dom-props.md)
10 changes: 9 additions & 1 deletion docs/rules/forbid-dom-props.md
Expand Up @@ -36,14 +36,22 @@ The following patterns are **not** considered warnings:

```js
...
"react/forbid-dom-props": [<enabled>, { "forbid": [<string>] }]
"react/forbid-dom-props": [<enabled>, { "forbid": [<string>|<object>] }]
...
```

### `forbid`

An array of strings, with the names of props that are forbidden. The default value of this option `[]`.
Each array element can either be a string with the property name or object specifying the property name and an optional
custom message:

```js
{
"propName": "someProp",
"message": "Avoid using someProp"
}
```

### Related rules

Expand Down
20 changes: 15 additions & 5 deletions lib/rules/forbid-component-props.js
Expand Up @@ -46,6 +46,9 @@ module.exports = {
items: {
type: 'string'
}
},
message: {
type: 'string'
}
}
}]
Expand All @@ -59,14 +62,18 @@ module.exports = {
const configuration = context.options[0] || {};
const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => {
const propName = typeof value === 'string' ? value : value.propName;
const whitelist = typeof value === 'string' ? [] : (value.allowedFor || []);
return [propName, whitelist];
const options = {
allowList: typeof value === 'string' ? [] : (value.allowedFor || []),
message: typeof value === 'string' ? null : value.message
};
return [propName, options];
}));

function isForbidden(prop, tagName) {
const whitelist = forbid.get(prop);
const options = forbid.get(prop);
const allowList = options ? options.allowList : undefined;
// if the tagName is undefined (`<this.something>`), we assume it's a forbidden element
return typeof whitelist !== 'undefined' && (typeof tagName === 'undefined' || whitelist.indexOf(tagName) === -1);
return typeof allowList !== 'undefined' && (typeof tagName === 'undefined' || allowList.indexOf(tagName) === -1);
}

return {
Expand All @@ -83,9 +90,12 @@ module.exports = {
return;
}

const customMessage = forbid.get(prop).message;
const errorMessage = customMessage || `Prop \`${prop}\` is forbidden on Components`;

context.report({
node,
message: `Prop \`${prop}\` is forbidden on Components`
message: errorMessage
});
}
};
Expand Down
33 changes: 27 additions & 6 deletions lib/rules/forbid-dom-props.js
Expand Up @@ -32,7 +32,19 @@ module.exports = {
forbid: {
type: 'array',
items: {
type: 'string',
onfOf: [{
type: 'string'
}, {
type: 'object',
properties: {
propName: {
type: 'string'
},
message: {
type: 'string'
}
}
}],
minLength: 1
},
uniqueItems: true
Expand All @@ -43,11 +55,17 @@ module.exports = {
},

create(context) {
function isForbidden(prop) {
const configuration = context.options[0] || {};
const configuration = context.options[0] || {};
const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => {
const propName = typeof value === 'string' ? value : value.propName;
const options = {
message: typeof value === 'string' ? null : value.message
};
return [propName, options];
}));

const forbid = configuration.forbid || DEFAULTS;
return forbid.indexOf(prop) >= 0;
function isForbidden(prop) {
return forbid.has(prop);
}

return {
Expand All @@ -64,9 +82,12 @@ module.exports = {
return;
}

const customMessage = forbid.get(prop).message;
const errorMessage = customMessage || `Prop \`${prop}\` is forbidden on DOM Nodes`;

context.report({
node,
message: `Prop \`${prop}\` is forbidden on DOM Nodes`
message: errorMessage
});
}
};
Expand Down
61 changes: 61 additions & 0 deletions tests/lib/rules/forbid-component-props.js
Expand Up @@ -190,5 +190,66 @@ ruleTester.run('forbid-component-props', rule, {
column: 32,
type: 'JSXAttribute'
}]
}, {
code: 'const item = (<Foo className="foo" />);',
options: [{
forbid: [{propName: 'className', message: 'Please use ourCoolClassName instead of ClassName'}]
}],
errors: [{
message: 'Please use ourCoolClassName instead of ClassName',
line: 1,
column: 20,
type: 'JSXAttribute'
}]
}, {
code: [
'const item = () => (',
'<Foo className="foo">',
' <Bar option="high" />',
'</Foo>',
');'
].join('\n'),
options: [{
forbid: [
{propName: 'className', message: 'Please use ourCoolClassName instead of ClassName'},
{propName: 'option', message: 'Avoid using option'}
]
}],
errors: [{
message: 'Please use ourCoolClassName instead of ClassName',
line: 2,
column: 6,
type: 'JSXAttribute'
}, {
message: 'Avoid using option',
line: 3,
column: 8,
type: 'JSXAttribute'
}]
}, {
code: [
'const item = () => (',
'<Foo className="foo">',
' <Bar option="high" />',
'</Foo>',
');'
].join('\n'),
options: [{
forbid: [
{propName: 'className'},
{propName: 'option', message: 'Avoid using option'}
]
}],
errors: [{
message: 'Prop `className` is forbidden on Components',
line: 2,
column: 6,
type: 'JSXAttribute'
}, {
message: 'Avoid using option',
line: 3,
column: 8,
type: 'JSXAttribute'
}]
}]
});
65 changes: 65 additions & 0 deletions tests/lib/rules/forbid-dom-props.js
Expand Up @@ -126,5 +126,70 @@ ruleTester.run('forbid-element-props', rule, {
column: 8,
type: 'JSXAttribute'
}]
}, {
code: [
'const First = (props) => (',
' <div className="foo" />',
');'
].join('\n'),
options: [{
forbid: [{propName: 'className', message: 'Please use class instead of ClassName'}]
}],
errors: [{
message: 'Please use class instead of ClassName',
line: 2,
column: 8,
type: 'JSXAttribute'
}]
}, {
code: [
'const First = (props) => (',
' <div className="foo">',
' <div otherProp="bar" />',
' </div>',
');'
].join('\n'),
options: [{
forbid: [
{propName: 'className', message: 'Please use class instead of ClassName'},
{propName: 'otherProp', message: 'Avoid using otherProp'}
]
}],
errors: [{
message: 'Please use class instead of ClassName',
line: 2,
column: 8,
type: 'JSXAttribute'
}, {
message: 'Avoid using otherProp',
line: 3,
column: 10,
type: 'JSXAttribute'
}]
}, {
code: [
'const First = (props) => (',
' <div className="foo">',
' <div otherProp="bar" />',
' </div>',
');'
].join('\n'),
options: [{
forbid: [
{propName: 'className'},
{propName: 'otherProp', message: 'Avoid using otherProp'}
]
}],
errors: [{
message: 'Prop `className` is forbidden on DOM Nodes',
line: 2,
column: 8,
type: 'JSXAttribute'
}, {
message: 'Avoid using otherProp',
line: 3,
column: 10,
type: 'JSXAttribute'
}]
}]
});