diff --git a/CHANGELOG.md b/CHANGELOG.md index 8efc7fb0d2..c7dd4b2815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel * [`jsx-newline`]: add new rule ([#2693][] @jzabala) * [`jsx-no-constructed-context-values`]: add new rule which checks when the value passed to a Context Provider will cause needless rerenders ([#2763][] @dylanOshima) * [`jsx-wrap-multilines`]: fix crash with `declaration`s that are on a new line after `=` ([#2875][] @ljharb) +* [`jsx-indent-props`]: add `ignoreTernaryOperator` option ([#2846][] @SebastianZimmer) ### Fixed * [`display-name`]/component detection: avoid a crash on anonymous components ([#2840][] @ljharb) @@ -36,6 +37,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel [#2869]: https://github.com/yannickcr/eslint-plugin-react/issues/2869 [#2852]: https://github.com/yannickcr/eslint-plugin-react/pull/2852 [#2851]: https://github.com/yannickcr/eslint-plugin-react/issues/2851 +[#2846]: https://github.com/yannickcr/eslint-plugin-react/pull/2846 [#2843]: https://github.com/yannickcr/eslint-plugin-react/pull/2843 [#2840]: https://github.com/yannickcr/eslint-plugin-react/issues/2840 [#2835]: https://github.com/yannickcr/eslint-plugin-react/pull/2835 diff --git a/docs/rules/jsx-indent-props.md b/docs/rules/jsx-indent-props.md index 94123de0b8..510f85ed64 100644 --- a/docs/rules/jsx-indent-props.md +++ b/docs/rules/jsx-indent-props.md @@ -29,12 +29,15 @@ firstName="John" ## Rule Options -It takes an option as the second parameter which can be `"tab"` for tab-based indentation, a positive number for space indentations or `"first"` for aligning the first prop for each line with the tag's first prop. +It takes an option as the second parameter which can either be the indent mode or an object to define further settings. +The indent mode can be `"tab"` for tab-based indentation, a positive number for space indentations or `"first"` for aligning the first prop for each line with the tag's first prop. Note that using the `"first"` option allows very inconsistent indentation unless you also enable a rule that enforces the position of the first prop. +If the second parameter is an object, it can be used to specify the indent mode as well as the option `ignoreTernaryOperator`, which causes the indent level not to be increased by a `?` or `:` operator (default is `false`). + ```js ... -"react/jsx-indent-props": [, 'tab'||'first'] +"react/jsx-indent-props": [, 'tab'||'first'|] ... ``` @@ -100,6 +103,20 @@ firstName="John" + +// indent level increase on ternary operator (default setting) +// [2, 2] +? + +// no indent level increase on ternary operator +// [2, { indentMode: 2, ignoreTernaryOperator: true} ] +? ``` ## When not to use diff --git a/lib/rules/jsx-indent-props.js b/lib/rules/jsx-indent-props.js index b2c08cec83..63f8b2455d 100644 --- a/lib/rules/jsx-indent-props.js +++ b/lib/rules/jsx-indent-props.js @@ -51,6 +51,20 @@ module.exports = { enum: ['tab', 'first'] }, { type: 'integer' + }, { + type: 'object', + properties: { + indentMode: { + oneOf: [{ + enum: ['tab', 'first'] + }, { + type: 'integer' + }] + }, + ignoreTernaryOperator: { + type: 'boolean' + } + } }] }] }, @@ -66,18 +80,28 @@ module.exports = { isUsingOperator: false, currentOperator: false }; + let ignoreTernaryOperator = false; if (context.options.length) { - if (context.options[0] === 'first') { + const isConfigObject = typeof context.options[0] === 'object'; + const indentMode = isConfigObject + ? context.options[0].indentMode + : context.options[0]; + + if (indentMode === 'first') { indentSize = 'first'; indentType = 'space'; - } else if (context.options[0] === 'tab') { + } else if (indentMode === 'tab') { indentSize = 1; indentType = 'tab'; - } else if (typeof context.options[0] === 'number') { - indentSize = context.options[0]; + } else if (typeof indentMode === 'number') { + indentSize = indentMode; indentType = 'space'; } + + if (isConfigObject && context.options[0].ignoreTernaryOperator) { + ignoreTernaryOperator = true; + } } /** @@ -145,7 +169,7 @@ module.exports = { function checkNodesIndent(nodes, indent) { nodes.forEach((node) => { const nodeIndent = getNodeIndent(node); - if (line.isUsingOperator && !line.currentOperator && indentSize !== 'first') { + if (line.isUsingOperator && !line.currentOperator && indentSize !== 'first' && !ignoreTernaryOperator) { indent += indentSize; line.isUsingOperator = false; } diff --git a/tests/lib/rules/jsx-indent-props.js b/tests/lib/rules/jsx-indent-props.js index 288b38ddad..540394f7ec 100644 --- a/tests/lib/rules/jsx-indent-props.js +++ b/tests/lib/rules/jsx-indent-props.js @@ -153,6 +153,48 @@ ruleTester.run('jsx-indent-props', rule, { '/>' ].join('\n'), options: ['first'] + }, { + code: [ + '{this.props.ignoreTernaryOperatorFalse', + ' ? ', + ' : null}' + ].join('\n'), + output: [ + '{this.props.ignoreTernaryOperatorFalse', + ' ? ', + ' : null}' + ].join('\n'), + options: [{ + indentMode: 2, + ignoreTernaryOperator: false + }] + }, { + code: [ + '{this.props.ignoreTernaryOperatorTrue', + ' ? ', + ' : null}' + ].join('\n'), + output: [ + '{this.props.ignoreTernaryOperatorTrue', + ' ? ', + ' : null}' + ].join('\n'), + options: [{ + indentMode: 2, + ignoreTernaryOperator: true + }] }], invalid: [{