diff --git a/CHANGELOG.md b/CHANGELOG.md index 778e908d03..fff57f229c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Added * [`sort-prop-types`]: give errors on TS types ([#3615][] @akulsr0) +### Fixed +* [`jsx-no-leaked-render`]: preserve RHS parens for multiline jsx elements while fixing ([#3623][] @akulsr0) + +[#3623]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3623 [#3615]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3615 ## [7.33.2] - 2023.08.15 diff --git a/lib/rules/jsx-no-leaked-render.js b/lib/rules/jsx-no-leaked-render.js index 0fcf407fbd..05f319edbf 100644 --- a/lib/rules/jsx-no-leaked-render.js +++ b/lib/rules/jsx-no-leaked-render.js @@ -82,6 +82,15 @@ function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNod if (rightNode.type === 'ConditionalExpression') { return fixer.replaceText(reportedNode, `${newText} && (${rightSideText})`); } + if (rightNode.type === 'JSXElement') { + const rightSideTextLines = rightSideText.split('\n'); + if (rightSideTextLines.length > 1) { + const rightSideTextLastLine = rightSideTextLines[rightSideTextLines.length - 1]; + const indentSpacesStart = ' '.repeat(rightSideTextLastLine.search(/\S/)); + const indentSpacesClose = ' '.repeat(rightSideTextLastLine.search(/\S/) - 2); + return fixer.replaceText(reportedNode, `${newText} && (\n${indentSpacesStart}${rightSideText}\n${indentSpacesClose})`); + } + } if (rightNode.type === 'Literal') { return null; } diff --git a/tests/lib/rules/jsx-no-leaked-render.js b/tests/lib/rules/jsx-no-leaked-render.js index 5bfd5a21ea..969c93635c 100644 --- a/tests/lib/rules/jsx-no-leaked-render.js +++ b/tests/lib/rules/jsx-no-leaked-render.js @@ -884,6 +884,76 @@ ruleTester.run('jsx-no-leaked-render', rule, { line: 3, column: 38, }], - } + }, + semver.satisfies(eslintPkg.version, '> 4') ? { + code: ` + const MyComponent = () => { + return ( + <> + {someCondition && ( +
+

hello

+
+ )} + + ) + } + `, + output: ` + const MyComponent = () => { + return ( + <> + {!!someCondition && ( +
+

hello

+
+ )} + + ) + } + `, + options: [{ validStrategies: ['coerce', 'ternary'] }], + errors: [{ + message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes', + line: 5, + column: 16, + }], + } : [], + semver.satisfies(eslintPkg.version, '> 4') ? { + code: ` + const MyComponent = () => { + return ( + <> + {someCondition && ( + + )} + + ) + } + `, + output: ` + const MyComponent = () => { + return ( + <> + {!!someCondition && ( + + )} + + ) + } + `, + options: [{ validStrategies: ['coerce', 'ternary'] }], + errors: [{ + message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes', + line: 5, + column: 16, + }], + } : [] )), });