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

Issue:
react/jsx-sort-props not working
I seem to be having trouble getting this rule to work.  The documentation references the following interface:
```
"react/jsx-sort-props": [<enabled>, {
  "callbacksLast": <boolean>,
  "shorthandFirst": <boolean>,
  "shorthandLast": <boolean>,
  "ignoreCase": <boolean>,
  "noSortAlphabetically": <boolean>,
  "reservedFirst": <boolean>|<array<string>>,
}]
```

My current configuration is `"react/jsx-sort-props": [2]`.  I have omitted the options which seem to work fine for other rules in the library.  Don't get any errors for the following component:
```
<Component c="c" b="b" a="a" />
```

Pull request:
fix(jsx-sort-props): enforce prop sort when reservedFirst is enabled
If reservedFirst is enabled and noSortAlphabetically is off, then alphabetical order is only enforced in the reserved props but not in the other props. This PR fixes this.

If existing behavior is the desired, please ignore this PR.

Fixes #1320

Patch:
diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js
index 10ca976a..cb09747b 100644
--- a/lib/rules/jsx-sort-props.js
+++ b/lib/rules/jsx-sort-props.js
@@ -40,11 +40,21 @@ function isReservedPropName(name, list) {
   return list.indexOf(name) >= 0;
 }
 
-function alphabeticalCompare(a, b, ignoreCase) {
-  if (ignoreCase) {
+function propNameCompare(a, b, options) {
+  if (options.ignoreCase) {
     a = a.toLowerCase();
     b = b.toLowerCase();
   }
+  if (options.reservedFirst) {
+    const aIsReserved = isReservedPropName(a, options.reservedList);
+    const bIsReserved = isReservedPropName(b, options.reservedList);
+    if ((aIsReserved && bIsReserved) || (!aIsReserved && !bIsReserved)) {
+      return a.localeCompare(b);
+    } else if (aIsReserved && !bIsReserved) {
+      return -1;
+    }
+    return 1;
+  }
   return a.localeCompare(b);
 }
 
@@ -77,11 +87,12 @@ function getGroupsOfSortableAttributes(attributes) {
   return sortableAttributeGroups;
 }
 
-const generateFixerFunction = (node, context) => {
+const generateFixerFunction = (node, context, reservedList) => {
   const sourceCode = context.getSourceCode();
   const attributes = node.attributes.slice(0);
   const configuration = context.options[0] || {};
   const ignoreCase = configuration.ignoreCase || false;
+  const reservedFirst = configuration.reservedFirst || false;
 
   // Sort props according to the context. Only supports ignoreCase.
   // Since we cannot safely move JSXSpreadAttribute (due to potential variable overrides),
@@ -89,7 +100,7 @@ const generateFixerFunction = (node, context) => {
   const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes);
   const sortedAttributeGroups = sortableAttributeGroups.slice(0).map(group =>
     group.slice(0).sort((a, b) =>
-      alphabeticalCompare(propName(a), propName(b), ignoreCase)
+      propNameCompare(propName(a), propName(b), {ignoreCase, reservedFirst, reservedList})
     )
   );
 
@@ -235,23 +246,22 @@ module.exports = {
             const previousIsReserved = isReservedPropName(previousPropName, reservedList);
             const currentIsReserved = isReservedPropName(currentPropName, reservedList);
 
-            if (previousIsReserved && currentIsReserved) {
+            if ((previousIsReserved && currentIsReserved) || (!previousIsReserved && !currentIsReserved)) {
               if (!noSortAlphabetically && currentPropName < previousPropName) {
                 context.report({
                   node: decl,
                   message: 'Props should be sorted alphabetically',
-                  fix: generateFixerFunction(node, context)
+                  fix: generateFixerFunction(node, context, reservedList)
                 });
                 return memo;
               }
-              return decl;
             }
             if (!previousIsReserved && currentIsReserved) {
               context.report({
                 node: decl,
-                message: 'Reserved props must be listed before all other props'
+                message: 'Reserved props must be listed before all other props',
+                fix: generateFixerFunction(node, context, reservedList)
               });
-              return memo;
             }
             return decl;
           }


Test patch:
diff --git a/tests/lib/rules/jsx-sort-props.js b/tests/lib/rules/jsx-sort-props.js
index 4cd54ce7..9439d4ea 100644
--- a/tests/lib/rules/jsx-sort-props.js
+++ b/tests/lib/rules/jsx-sort-props.js
@@ -271,8 +271,21 @@ ruleTester.run('jsx-sort-props', rule, {
       errors: [expectedError]
     },
     {
-      code: '<App dangerouslySetInnerHTML={{__html: "EPR"}} key={2} b />',
+      code: '<App key={2} b a />',
       options: reservedFirstAsBooleanArgs,
+      output: '<App key={2} a b />',
+      errors: [expectedError]
+    },
+    {
+      code: '<App b a />',
+      options: reservedFirstAsBooleanArgs,
+      output: '<App a b />',
+      errors: [expectedError]
+    },
+    {
+      code: '<App dangerouslySetInnerHTML={{__html: "EPR"}} e key={2} b />',
+      options: reservedFirstAsBooleanArgs,
+      output: '<App key={2} b dangerouslySetInnerHTML={{__html: "EPR"}} e />',
       errors: [expectedReservedFirstError]
     },
     {