Repository: yannickcr/eslint-plugin-react (js)

Issue:
react/jsx-one-expression-per-line
There's a bug with the `--fix` part of this rule when you have plain text inside of the jsx element.

**Original code**:
```js
const something = () => (
  <Text>Plain text</Text>
)
```

**Result of `eslint --fix`**:
```js
const something = () => (
  <Text>
Plain text
  </Text>
)
```

**Expected result**:
```js
const something = () => (
  <Text>
    Plain text
  </Text>
)
```

Pull request description:
[New] Add allow option to jsx-one-expression-per-line
Resolves #1775

Patch:
diff --git a/docs/rules/jsx-one-expression-per-line.md b/docs/rules/jsx-one-expression-per-line.md
index 972b56ce..390f786e 100644
--- a/docs/rules/jsx-one-expression-per-line.md
+++ b/docs/rules/jsx-one-expression-per-line.md
@@ -102,3 +102,31 @@ The following patterns are **not** warnings:
   <Hello3 />
 </App>
 ```
+
+## Rule Options
+
+```js
+...
+"react/jsx-one-expression-per-line": [<enabled>, { "allow": "none"|"literal"|"single-child" }]
+...
+```
+
+### `allow`
+
+Defaults to `none`.
+
+The following pattern is **not** considered a warning when configured as `"literal"`:
+
+```jsx
+<App>Hello</App>
+```
+
+The following patterns are **not** considered warnings when configured as `"single-child"`:
+
+```jsx
+<App>Hello</App>
+
+<App>{"Hello"}</App>
+
+<App><Hello /></App>
+```
diff --git a/lib/rules/jsx-one-expression-per-line.js b/lib/rules/jsx-one-expression-per-line.js
index 7be01233..cc14ad83 100644
--- a/lib/rules/jsx-one-expression-per-line.js
+++ b/lib/rules/jsx-one-expression-per-line.js
@@ -11,6 +11,10 @@ const docsUrl = require('../util/docsUrl');
 // Rule Definition
 // ------------------------------------------------------------------------------
 
+const optionDefaults = {
+  allow: 'none'
+};
+
 module.exports = {
   meta: {
     docs: {
@@ -20,10 +24,22 @@ module.exports = {
       url: docsUrl('jsx-one-expression-per-line')
     },
     fixable: 'whitespace',
-    schema: []
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          allow: {
+            enum: ['none', 'literal', 'single-child']
+          }
+        },
+        default: optionDefaults,
+        additionalProperties: false
+      }
+    ]
   },
 
   create: function (context) {
+    const options = Object.assign({}, optionDefaults, context.options[0]);
     const sourceCode = context.getSourceCode();
 
     function nodeKey (node) {
@@ -44,8 +60,28 @@ module.exports = {
 
         const openingElement = node.openingElement;
         const closingElement = node.closingElement;
+        const openingElementStartLine = openingElement.loc.start.line;
         const openingElementEndLine = openingElement.loc.end.line;
         const closingElementStartLine = closingElement.loc.start.line;
+        const closingElementEndLine = closingElement.loc.end.line;
+
+        if (children.length === 1) {
+          const child = children[0];
+          if (
+            openingElementStartLine === openingElementEndLine &&
+            openingElementEndLine === closingElementStartLine &&
+            closingElementStartLine === closingElementEndLine &&
+            closingElementEndLine === child.loc.start.line &&
+            child.loc.start.line === child.loc.end.line
+          ) {
+            if (
+              options.allow === 'single-child' ||
+              options.allow === 'literal' && (child.type === 'Literal' || child.type === 'JSXText')
+            ) {
+              return;
+            }
+          }
+        }
 
         const childrenGroupedByLine = {};
         const fixDetailsByNode = {};


Test patch:
diff --git a/tests/lib/rules/jsx-one-expression-per-line.js b/tests/lib/rules/jsx-one-expression-per-line.js
index 173d6115..0ca57b88 100644
--- a/tests/lib/rules/jsx-one-expression-per-line.js
+++ b/tests/lib/rules/jsx-one-expression-per-line.js
@@ -84,6 +84,24 @@ ruleTester.run('jsx-one-expression-per-line', rule, {
       'App',
       '>'
     ].join('\n')
+  }, {
+    code: '<App>foo</App>',
+    options: [{allow: 'literal'}]
+  }, {
+    code: '<App>123</App>',
+    options: [{allow: 'literal'}]
+  }, {
+    code: '<App>foo</App>',
+    options: [{allow: 'single-child'}]
+  }, {
+    code: '<App>{"foo"}</App>',
+    options: [{allow: 'single-child'}]
+  }, {
+    code: '<App>{foo && <Bar />}</App>',
+    options: [{allow: 'single-child'}]
+  }, {
+    code: '<App><Foo /></App>',
+    options: [{allow: 'single-child'}]
   }],
 
   invalid: [{
@@ -852,5 +870,84 @@ ruleTester.run('jsx-one-expression-per-line', rule, {
       {message: '`{  foo}` must be placed on a new line'}
     ],
     parserOptions: parserOptions
+  }, {
+    code: '<App><Foo /></App>',
+    options: [{allow: 'none'}],
+    output: [
+      '<App>',
+      '<Foo />',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`Foo` must be placed on a new line'}]
+  }, {
+    code: '<App>foo</App>',
+    options: [{allow: 'none'}],
+    output: [
+      '<App>',
+      'foo',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`foo` must be placed on a new line'}]
+  }, {
+    code: '<App>{"foo"}</App>',
+    options: [{allow: 'none'}],
+    output: [
+      '<App>',
+      '{"foo"}',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`{"foo"}` must be placed on a new line'}]
+  }, {
+    code: [
+      '<App>foo',
+      '</App>'
+    ].join('\n'),
+    options: [{allow: 'literal'}],
+    output: [
+      '<App>',
+      'foo',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`foo` must be placed on a new line'}]
+  }, {
+    code: '<App><Foo /></App>',
+    options: [{allow: 'literal'}],
+    output: [
+      '<App>',
+      '<Foo />',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`Foo` must be placed on a new line'}]
+  }, {
+    code: [
+      '<App',
+      '  foo="1"',
+      '  bar="2"',
+      '>baz</App>'
+    ].join('\n'),
+    options: [{allow: 'literal'}],
+    output: [
+      '<App',
+      '  foo="1"',
+      '  bar="2"',
+      '>',
+      'baz',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`baz` must be placed on a new line'}]
+  }, {
+    code: [
+      '<App>foo',
+      'bar',
+      '</App>'
+    ].join('\n'),
+    options: [{allow: 'literal'}],
+    output: [
+      '<App>',
+      'foo',
+      'bar',
+      '</App>'
+    ].join('\n'),
+    errors: [{message: '`foobar` must be placed on a new line'}]
   }]
 });