diff --git a/docs/rules/style-prop-object.md b/docs/rules/style-prop-object.md index 464f889cb6..386c09cfb3 100644 --- a/docs/rules/style-prop-object.md +++ b/docs/rules/style-prop-object.md @@ -49,3 +49,33 @@ React.createElement("Hello", { style: { color: 'red' }}); const styles = { height: '100px' }; React.createElement("div", { style: styles }); ``` +## Rule Options + +```js +... +"react/style-prop-object": [, { + "allow": [] +}] +... +``` + +### `allow` +A list of elements that are allowed to have a non-object value in their style attribute. The default value is `[]`. + +#### Example +```js +{ + "allow": ["MyComponent"] +} +``` +The following patterns are considered warnings: +```js + +React.createElement(Hello, { style: "some styling" }); +``` + +The following patterns are **not** considered warnings: +```js + +React.createElement(MyComponent, { style: "some styling" }); +``` diff --git a/lib/rules/style-prop-object.js b/lib/rules/style-prop-object.js index 4a29d33d25..7aa12eea39 100644 --- a/lib/rules/style-prop-object.js +++ b/lib/rules/style-prop-object.js @@ -19,10 +19,24 @@ module.exports = { recommended: false, url: docsUrl('style-prop-object') }, - schema: [] + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + } + } + } + } + ] }, create: function(context) { + const allowed = new Set(context.options.length > 0 && context.options[0].allow || []); + /** * @param {object} node An Identifier node */ @@ -53,6 +67,16 @@ module.exports = { && node.callee.property.name === 'createElement' && node.arguments.length > 1 ) { + if (node.arguments[0].name) { + // store name of component + const componentName = node.arguments[0].name; + + // allowed list contains the name + if (allowed.has(componentName)) { + // abort operation + return; + } + } if (node.arguments[1].type === 'ObjectExpression') { const style = node.arguments[1].properties.find(property => property.key && property.key.name === 'style' && !property.computed); if (style) { @@ -70,6 +94,20 @@ module.exports = { if (!node.value || node.name.name !== 'style') { return; } + // store parent element + const parentElement = node.parent; + + // parent element is a JSXOpeningElement + if (parentElement && parentElement.type === 'JSXOpeningElement') { + // get the name of the JSX element + const name = parentElement.name && parentElement.name.name; + + // allowed list contains the name + if (allowed.has(name)) { + // abort operation + return; + } + } if (node.value.type !== 'JSXExpressionContainer' || isNonNullaryLiteral(node.value.expression)) { context.report(node, 'Style prop value must be an object'); diff --git a/tests/lib/rules/style-prop-object.js b/tests/lib/rules/style-prop-object.js index c2d8b3d765..13fa702d8c 100644 --- a/tests/lib/rules/style-prop-object.js +++ b/tests/lib/rules/style-prop-object.js @@ -191,6 +191,22 @@ ruleTester.run('style-prop-object', rule, { ' });', '};' ].join('\n') + }, + { + code: '', + options: [ + { + allow: ['MyComponent'] + } + ] + }, + { + code: 'React.createElement(MyComponent, { style: "mySpecialStyle" })', + options: [ + { + allow: ['MyComponent'] + } + ] } ], invalid: [ @@ -262,6 +278,34 @@ ruleTester.run('style-prop-object', rule, { column: 22, type: 'Identifier' }] + }, + { + code: '', + options: [ + { + allow: ['MyOtherComponent'] + } + ], + errors: [{ + message: 'Style prop value must be an object', + line: 1, + column: 14, + type: 'JSXAttribute' + }] + }, + { + code: 'React.createElement(MyComponent, { style: "mySpecialStyle" })', + options: [ + { + allow: ['MyOtherComponent'] + } + ], + errors: [{ + message: 'Style prop value must be an object', + line: 1, + column: 43, + type: 'Literal' + }] } ] });