diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc5c6bc55a..0d8a066c34 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Added
* [`hook-use-state`]: add `allowDestructuredState` option ([#3449][] @ljharb)
+* add [`sort-default-props`] and deprecate [`jsx-sort-default-props`] ([#1861][] @alexzherdev)
[#3449]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3449
+[#1861]: https://github.com/jsx-eslint/eslint-plugin-react/pull/1861
## [7.31.9] - 2022.10.09
@@ -4023,6 +4025,7 @@ If you're still not using React 15 you can keep the old behavior by setting the
[`require-render-return`]: docs/rules/require-render-return.md
[`self-closing-comp`]: docs/rules/self-closing-comp.md
[`sort-comp`]: docs/rules/sort-comp.md
+[`sort-default-props`]: docs/rules/sort-default-props.md
[`sort-prop-types`]: docs/rules/sort-prop-types.md
[`state-in-constructor`]: docs/rules/state-in-constructor.md
[`static-property-placement`]: docs/rules/static-property-placement.md
diff --git a/README.md b/README.md
index a7c98e5739..2862f522b4 100644
--- a/README.md
+++ b/README.md
@@ -172,6 +172,7 @@ Enable the rules that you would like to use.
| ✔ | | | [react/require-render-return](docs/rules/require-render-return.md) | Enforce ES5 or ES6 class for returning value in render function |
| | 🔧 | | [react/self-closing-comp](docs/rules/self-closing-comp.md) | Disallow extra closing tags for components without children |
| | | | [react/sort-comp](docs/rules/sort-comp.md) | Enforce component methods order |
+| | | | [react/sort-default-props](docs/rules/sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting |
| | | | [react/sort-prop-types](docs/rules/sort-prop-types.md) | Enforce propTypes declarations alphabetical sorting |
| | | | [react/state-in-constructor](docs/rules/state-in-constructor.md) | Enforce class component state initialization style |
| | | | [react/static-property-placement](docs/rules/static-property-placement.md) | Enforces where React component static properties should be positioned. |
@@ -216,7 +217,7 @@ Enable the rules that you would like to use.
| | | | [react/jsx-pascal-case](docs/rules/jsx-pascal-case.md) | Enforce PascalCase for user-defined JSX components |
| | 🔧 | | [react/jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md) | Disallow multiple spaces between inline JSX props |
| | | | [react/jsx-props-no-spreading](docs/rules/jsx-props-no-spreading.md) | Disallow JSX prop spreading |
-| | | | [react/jsx-sort-default-props](docs/rules/jsx-sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting |
+| | | | [react/jsx-sort-default-props](docs/rules/jsx-sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting. ❌ This rule is deprecated. |
| | 🔧 | | [react/jsx-sort-props](docs/rules/jsx-sort-props.md) | Enforce props alphabetical sorting |
| | 🔧 | | [react/jsx-space-before-closing](docs/rules/jsx-space-before-closing.md) | Enforce spacing before closing bracket in JSX. ❌ This rule is deprecated. |
| | 🔧 | | [react/jsx-tag-spacing](docs/rules/jsx-tag-spacing.md) | Enforce whitespace in and around the JSX opening and closing brackets |
diff --git a/docs/rules/jsx-sort-default-props.md b/docs/rules/jsx-sort-default-props.md
index 6188e8d10c..db6331abde 100644
--- a/docs/rules/jsx-sort-default-props.md
+++ b/docs/rules/jsx-sort-default-props.md
@@ -1,6 +1,6 @@
# Enforce defaultProps declarations alphabetical sorting (react/jsx-sort-default-props)
-💼 This rule is enabled in the following [configs](https://github.com/jsx-eslint/eslint-plugin-react#shareable-configurations): `all`.
+❌ This rule is deprecated. Please use the [`sort-default-props`](./sort-default-props.md) rule instead.
Some developers prefer to sort `defaultProps` declarations alphabetically to be able to find necessary declarations easier at a later time. Others feel that it adds complexity and becomes a burden to maintain.
diff --git a/docs/rules/sort-default-props.md b/docs/rules/sort-default-props.md
new file mode 100644
index 0000000000..fd4bb07fe5
--- /dev/null
+++ b/docs/rules/sort-default-props.md
@@ -0,0 +1,187 @@
+# Enforce defaultProps declarations alphabetical sorting (react/sort-default-props)
+
+💼 This rule is enabled in the following [configs](https://github.com/jsx-eslint/eslint-plugin-react#shareable-configurations): `all`.
+
+Some developers prefer to sort `defaultProps` declarations alphabetically to be able to find necessary declarations easier at a later time. Others feel that it adds complexity and becomes a burden to maintain.
+
+## Rule Details
+
+This rule checks all components and verifies that all `defaultProps` declarations are sorted alphabetically. A spread attribute resets the verification. The default configuration of the rule is case-sensitive.
+
+The following patterns are considered warnings:
+
+```jsx
+var Component = createReactClass({
+...
+ getDefaultProps: function() {
+ return {
+ z: "z",
+ a: "a",
+ b: "b"
+ };
+ },
+...
+});
+
+class Component extends React.Component {
+ ...
+}
+Component.defaultProps = {
+ z: "z",
+ a: "a",
+ b: "b"
+};
+
+class Component extends React.Component {
+ static defaultProps = {
+ z: "z",
+ y: "y",
+ a: "a"
+ }
+ render() {
+ return
;
+ }
+}
+
+const Component = (props) => (...);
+Component.defaultProps = {
+ z: "z",
+ y: "y",
+ a: "a"
+};
+
+const defaults = {
+ b: "b"
+};
+const types = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string'
+};
+function StatelessComponentWithSpreadInPropTypes({ a, b, c }) {
+ return {a}{b}{c}
;
+}
+StatelessComponentWithSpreadInPropTypes.propTypes = types;
+StatelessComponentWithSpreadInPropTypes.defaultProps = {
+ c: "c",
+ a: "a",
+ ...defaults,
+};
+
+export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string,
+ d: PropTypes.string,
+ e: PropTypes.string,
+ f: PropTypes.string
+ }
+ static defaultProps = {
+ b: "b",
+ a: "a",
+ ...c.defaultProps,
+ f: "f",
+ e: "e",
+ ...d.defaultProps
+ }
+}
+```
+
+The following patterns are considered okay and do **not** cause warnings:
+
+```jsx
+var Component = createReactClass({
+...
+ getDefaultProps: function() {
+ return {
+ a: "a",
+ b: "b",
+ c: "c"
+ };
+ },
+...
+});
+
+class Component extends React.Component {
+ ...
+}
+Component.defaultProps = {
+ a: "a",
+ b: "b",
+ c: "c"
+};
+
+class Component extends React.Component {
+ static defaultProps = {
+ a: PropTypes.any,
+ b: PropTypes.any,
+ c: PropTypes.any
+ }
+ render() {
+ return ;
+ }
+}
+
+const Component = (props) => (...);
+Component.defaultProps = {
+ a: "a",
+ y: "y",
+ z: "z"
+};
+
+const defaults = {
+ b: "b"
+};
+const types = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string'
+};
+function StatelessComponentWithSpreadInPropTypes({ a, b, c }) {
+ return {a}{b}{c}
;
+}
+StatelessComponentWithSpreadInPropTypes.propTypes = types;
+StatelessComponentWithSpreadInPropTypes.defaultProps = {
+ a: "a",
+ c: "c",
+ ...defaults,
+};
+
+export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string,
+ d: PropTypes.string,
+ e: PropTypes.string,
+ f: PropTypes.string
+ }
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ ...c.defaultProps,
+ e: "e",
+ f: "f",
+ ...d.defaultProps
+ }
+}
+```
+
+## Rule Options
+
+```js
+...
+"react/sort-default-props": [, {
+ "ignoreCase": ,
+}]
+...
+```
+
+### `ignoreCase`
+
+When `true` the rule ignores the case-sensitivity of the declarations order.
+
+## When not to use
+
+This rule is a formatting preference and not following it won't negatively affect the quality of your code. If alphabetizing `defaultProps` declarations isn't a part of your coding standards, then you can leave this rule off.
diff --git a/index.js b/index.js
index be3c94a992..12c60874b3 100644
--- a/index.js
+++ b/index.js
@@ -98,6 +98,7 @@ const allRules = {
'require-render-return': require('./lib/rules/require-render-return'),
'self-closing-comp': require('./lib/rules/self-closing-comp'),
'sort-comp': require('./lib/rules/sort-comp'),
+ 'sort-default-props': require('./lib/rules/sort-default-props'),
'sort-prop-types': require('./lib/rules/sort-prop-types'),
'state-in-constructor': require('./lib/rules/state-in-constructor'),
'static-property-placement': require('./lib/rules/static-property-placement'),
diff --git a/lib/rules/jsx-sort-default-props.js b/lib/rules/jsx-sort-default-props.js
index f7062b81c5..a951976336 100644
--- a/lib/rules/jsx-sort-default-props.js
+++ b/lib/rules/jsx-sort-default-props.js
@@ -1,6 +1,7 @@
/**
* @fileoverview Enforce default props alphabetical sorting
* @author Vladimir Kattsov
+ * @deprecated
*/
'use strict';
@@ -8,6 +9,9 @@
const variableUtil = require('../util/variable');
const docsUrl = require('../util/docsUrl');
const report = require('../util/report');
+const log = require('../util/log');
+
+let isWarnedForDeprecation = false;
// ------------------------------------------------------------------------------
// Rule Definition
@@ -19,6 +23,7 @@ const messages = {
module.exports = {
meta: {
+ deprecated: true,
docs: {
description: 'Enforce defaultProps declarations alphabetical sorting',
category: 'Stylistic Issues',
@@ -168,6 +173,15 @@ module.exports = {
checkNode(node.parent.right);
},
+
+ Program() {
+ if (isWarnedForDeprecation) {
+ return;
+ }
+
+ log('The react/jsx-sort-default-props rule is deprecated. It has been renamed to `react/sort-default-props`.');
+ isWarnedForDeprecation = true;
+ },
};
},
};
diff --git a/lib/rules/sort-default-props.js b/lib/rules/sort-default-props.js
new file mode 100644
index 0000000000..aae419ba18
--- /dev/null
+++ b/lib/rules/sort-default-props.js
@@ -0,0 +1,174 @@
+/**
+ * @fileoverview Enforce default props alphabetical sorting
+ * @author Vladimir Kattsov
+ * @deprecated
+ */
+
+'use strict';
+
+const variableUtil = require('../util/variable');
+const docsUrl = require('../util/docsUrl');
+const report = require('../util/report');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const messages = {
+ propsNotSorted: 'Default prop types declarations should be sorted alphabetically',
+};
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Enforce defaultProps declarations alphabetical sorting',
+ category: 'Stylistic Issues',
+ recommended: false,
+ url: docsUrl('sort-default-props'),
+ },
+ // fixable: 'code',
+
+ messages,
+
+ schema: [{
+ type: 'object',
+ properties: {
+ ignoreCase: {
+ type: 'boolean',
+ },
+ },
+ additionalProperties: false,
+ }],
+ },
+
+ create(context) {
+ const configuration = context.options[0] || {};
+ const ignoreCase = configuration.ignoreCase || false;
+
+ /**
+ * Get properties name
+ * @param {Object} node - Property.
+ * @returns {String} Property name.
+ */
+ function getPropertyName(node) {
+ if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
+ return node.key.name;
+ }
+ if (node.type === 'MemberExpression') {
+ return node.property.name;
+ // Special case for class properties
+ // (babel-eslint@5 does not expose property name so we have to rely on tokens)
+ }
+ if (node.type === 'ClassProperty') {
+ const tokens = context.getSourceCode().getFirstTokens(node, 2);
+ return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
+ }
+ return '';
+ }
+
+ /**
+ * Checks if the Identifier node passed in looks like a defaultProps declaration.
+ * @param {ASTNode} node The node to check. Must be an Identifier node.
+ * @returns {Boolean} `true` if the node is a defaultProps declaration, `false` if not
+ */
+ function isDefaultPropsDeclaration(node) {
+ const propName = getPropertyName(node);
+ return (propName === 'defaultProps' || propName === 'getDefaultProps');
+ }
+
+ function getKey(node) {
+ return context.getSourceCode().getText(node.key || node.argument);
+ }
+
+ /**
+ * Find a variable by name in the current scope.
+ * @param {string} name Name of the variable to look for.
+ * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
+ */
+ function findVariableByName(name) {
+ const variable = variableUtil.variablesInScope(context).find((item) => item.name === name);
+
+ if (!variable || !variable.defs[0] || !variable.defs[0].node) {
+ return null;
+ }
+
+ if (variable.defs[0].node.type === 'TypeAlias') {
+ return variable.defs[0].node.right;
+ }
+
+ return variable.defs[0].node.init;
+ }
+
+ /**
+ * Checks if defaultProps declarations are sorted
+ * @param {Array} declarations The array of AST nodes being checked.
+ * @returns {void}
+ */
+ function checkSorted(declarations) {
+ // function fix(fixer) {
+ // return propTypesSortUtil.fixPropTypesSort(fixer, context, declarations, ignoreCase);
+ // }
+
+ declarations.reduce((prev, curr, idx, decls) => {
+ if (/Spread(?:Property|Element)$/.test(curr.type)) {
+ return decls[idx + 1];
+ }
+
+ let prevPropName = getKey(prev);
+ let currentPropName = getKey(curr);
+
+ if (ignoreCase) {
+ prevPropName = prevPropName.toLowerCase();
+ currentPropName = currentPropName.toLowerCase();
+ }
+
+ if (currentPropName < prevPropName) {
+ report(context, messages.propsNotSorted, 'propsNotSorted', {
+ node: curr,
+ // fix
+ });
+
+ return prev;
+ }
+
+ return curr;
+ }, declarations[0]);
+ }
+
+ function checkNode(node) {
+ if (!node) {
+ return;
+ }
+ if (node.type === 'ObjectExpression') {
+ checkSorted(node.properties);
+ } else if (node.type === 'Identifier') {
+ const propTypesObject = findVariableByName(node.name);
+ if (propTypesObject && propTypesObject.properties) {
+ checkSorted(propTypesObject.properties);
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Public API
+ // --------------------------------------------------------------------------
+
+ return {
+ 'ClassProperty, PropertyDefinition'(node) {
+ if (!isDefaultPropsDeclaration(node)) {
+ return;
+ }
+
+ checkNode(node.value);
+ },
+
+ MemberExpression(node) {
+ if (!isDefaultPropsDeclaration(node)) {
+ return;
+ }
+
+ checkNode(node.parent.right);
+ },
+ };
+ },
+};
diff --git a/tests/lib/rules/sort-default-props.js b/tests/lib/rules/sort-default-props.js
new file mode 100644
index 0000000000..8e3a35974d
--- /dev/null
+++ b/tests/lib/rules/sort-default-props.js
@@ -0,0 +1,938 @@
+/**
+ * @fileoverview Tests for sort-default-props
+ * @author Vladimir Kattsov
+ */
+
+'use strict';
+
+// -----------------------------------------------------------------------------
+// Requirements
+// -----------------------------------------------------------------------------
+
+const babelEslintVersion = require('babel-eslint/package.json').version;
+const semver = require('semver');
+const RuleTester = require('eslint').RuleTester;
+
+const rule = require('../../../lib/rules/sort-default-props');
+
+const parsers = require('../../helpers/parsers');
+
+const parserOptions = {
+ ecmaVersion: 2018,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ },
+};
+
+// -----------------------------------------------------------------------------
+// Tests
+// -----------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({ parserOptions });
+ruleTester.run('sort-default-props', rule, {
+ valid: parsers.all([].concat(
+ {
+ code: `
+ var First = createReactClass({
+ render: function() {
+ return ;
+ }
+ });
+ `,
+ },
+ {
+ code: `
+ var First = createReactClass({
+ propTypes: {
+ A: PropTypes.any,
+ Z: PropTypes.string,
+ a: PropTypes.any,
+ z: PropTypes.string
+ },
+ getDefaultProps: function() {
+ return {
+ A: "A",
+ Z: "Z",
+ a: "a",
+ z: "z"
+ };
+ },
+ render: function() {
+ return ;
+ }
+ });
+ `,
+ },
+ {
+ code: `
+ var First = createReactClass({
+ propTypes: {
+ a: PropTypes.any,
+ A: PropTypes.any,
+ z: PropTypes.string,
+ Z: PropTypes.string
+ },
+ getDefaultProps: function() {
+ return {
+ a: "a",
+ A: "A",
+ z: "z",
+ Z: "Z"
+ };
+ },
+ render: function() {
+ return ;
+ }
+ });
+ `,
+ options: [{ ignoreCase: true }],
+ },
+ {
+ code: `
+ var First = createReactClass({
+ propTypes: {
+ a: PropTypes.any,
+ z: PropTypes.string
+ },
+ getDefaultProps: function() {
+ return {
+ a: "a",
+ z: "z"
+ };
+ },
+ render: function() {
+ return ;
+ }
+ });
+ var Second = createReactClass({
+ propTypes: {
+ AA: PropTypes.any,
+ ZZ: PropTypes.string
+ },
+ getDefaultProps: function() {
+ return {
+ AA: "AA",
+ ZZ: "ZZ"
+ };
+ },
+ render: function() {
+ return ;
+ }
+ });
+ `,
+ },
+ {
+ code: `
+ class First extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ First.propTypes = {
+ a: PropTypes.string,
+ z: PropTypes.string
+ };
+ First.propTypes.justforcheck = PropTypes.string;
+ First.defaultProps = {
+ a: a,
+ z: z
+ };
+ First.defaultProps.justforcheck = "justforcheck";
+ `,
+ },
+ {
+ code: `
+ class First extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ First.propTypes = {
+ a: PropTypes.any,
+ A: PropTypes.any,
+ z: PropTypes.string,
+ Z: PropTypes.string
+ };
+ First.defaultProps = {
+ a: "a",
+ A: "A",
+ z: "z",
+ Z: "Z"
+ };
+ `,
+ options: [{ ignoreCase: true }],
+ },
+ {
+ code: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any,
+ c: PropTypes.any
+ };
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ c: "c"
+ };
+ render() {
+ return ;
+ }
+ }
+ `,
+ features: ['class fields'],
+ },
+ {
+ code: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "aria-controls": PropTypes.string
+ };
+ Hello.defaultProps = {
+ "aria-controls": "aria-controls"
+ };
+ `,
+ options: [{ ignoreCase: true }],
+ },
+ semver.satisfies(babelEslintVersion, '< 9') ? {
+ // Invalid code, should not be validated
+ code: `
+ class Component extends React.Component {
+ propTypes: {
+ a: PropTypes.any,
+ c: PropTypes.any,
+ b: PropTypes.any
+ };
+ defaultProps: {
+ a: "a",
+ c: "c",
+ b: "b"
+ };
+ render() {
+ return ;
+ }
+ }
+ `,
+ parser: parsers.BABEL_ESLINT,
+ } : [],
+ {
+ code: `
+ var Hello = createReactClass({
+ render: function() {
+ let { a, ...b } = obj;
+ let c = { ...d };
+ return ;
+ }
+ });
+ `,
+ },
+ {
+ code: `
+ var First = createReactClass({
+ propTypes: {
+ barRequired: PropTypes.func.isRequired,
+ onBar: PropTypes.func,
+ z: PropTypes.any
+ },
+ getDefaultProps: function() {
+ return {
+ barRequired: "barRequired",
+ onBar: "onBar",
+ z: "z"
+ };
+ },
+ render: function() {
+ return ;
+ }
+ });
+ `,
+ },
+ {
+ code: `
+ export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ b: PropTypes.string,
+ ...c.propTypes,
+ a: PropTypes.string
+ }
+ static defaultProps = {
+ b: "b",
+ ...c.defaultProps,
+ a: "a"
+ }
+ }
+ `,
+ features: ['class fields'],
+ },
+ {
+ code: `
+ export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string,
+ d: PropTypes.string,
+ e: PropTypes.string,
+ f: PropTypes.string
+ }
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ ...c.defaultProps,
+ e: "e",
+ f: "f",
+ ...d.defaultProps
+ }
+ }
+ `,
+ features: ['class fields'],
+ },
+ {
+ code: `
+ const defaults = {
+ b: "b"
+ };
+ const types = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string
+ };
+ function StatelessComponentWithSpreadInPropTypes({ a, b, c }) {
+ return {a}{b}{c}
;
+ }
+ StatelessComponentWithSpreadInPropTypes.propTypes = types;
+ StatelessComponentWithSpreadInPropTypes.defaultProps = {
+ c: "c",
+ ...defaults,
+ a: "a"
+ };
+ `,
+ },
+ {
+ code: `
+ const propTypes = require('./externalPropTypes')
+ const defaultProps = require('./externalDefaultProps')
+ const TextFieldLabel = (props) => {
+ return ;
+ };
+ TextFieldLabel.propTypes = propTypes;
+ TextFieldLabel.defaultProps = defaultProps;
+ `,
+ },
+ {
+ code: `
+ const First = (props) => ;
+ export const propTypes = {
+ a: PropTypes.any,
+ z: PropTypes.string,
+ };
+ export const defaultProps = {
+ a: "a",
+ z: "z",
+ };
+ First.propTypes = propTypes;
+ First.defaultProps = defaultProps;
+ `,
+ },
+ {
+ code: `
+ const defaults = {
+ b: "b"
+ };
+ const First = (props) => ;
+ export const propTypes = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ z: PropTypes.string,
+ };
+ export const defaultProps = {
+ ...defaults,
+ a: "a",
+ z: "z",
+ };
+ First.propTypes = propTypes;
+ First.defaultProps = defaultProps;
+ `,
+ },
+ {
+ code: `
+ class First extends React.Component {
+ render() {
+ return ;
+ }
+ }
+
+ First.defaultProps = {
+ a: PropTypes.any,
+ onBar: PropTypes.func,
+ onFoo: PropTypes.func,
+ z: PropTypes.string,
+ };
+ `,
+ }
+ )),
+
+ invalid: parsers.all([
+ {
+ code: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any,
+ c: PropTypes.any
+ };
+ static defaultProps = {
+ a: "a",
+ c: "c",
+ b: "b"
+ };
+ render() {
+ return ;
+ }
+ }
+ `,
+ features: ['class fields'],
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 11,
+ column: 13,
+ type: 'Property',
+ },
+ ], /* ,
+ output: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any,
+ c: PropTypes.any
+ };
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ c: "c"
+ };
+ render() {
+ return ;
+ }
+ }
+ ` */
+ },
+ {
+ code: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any,
+ c: PropTypes.any
+ };
+ static defaultProps = {
+ c: "c",
+ b: "b",
+ a: "a"
+ };
+ render() {
+ return ;
+ }
+ }
+ `,
+ /* output: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any,
+ c: PropTypes.any
+ };
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ c: "c"
+ };
+ render() {
+ return ;
+ }
+ }
+ `, */
+ features: ['class fields'],
+ errors: 2,
+ },
+ {
+ code: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any
+ };
+ static defaultProps = {
+ Z: "Z",
+ a: "a",
+ };
+ render() {
+ return ;
+ }
+ }
+ `,
+ /* output: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ b: PropTypes.any
+ };
+ static defaultProps = {
+ a: "a",
+ Z: "Z",
+ };
+ render() {
+ return ;
+ }
+ }
+ `, */
+ features: ['class fields'],
+ options: [{ ignoreCase: true }],
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 9,
+ column: 13,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ z: PropTypes.any
+ };
+ static defaultProps = {
+ a: "a",
+ Z: "Z",
+ };
+ render() {
+ return ;
+ }
+ }
+ `,
+ /* output: `
+ class Component extends React.Component {
+ static propTypes = {
+ a: PropTypes.any,
+ z: PropTypes.any
+ };
+ static defaultProps = {
+ Z: "Z",
+ a: "a",
+ };
+ render() {
+ return ;
+ }
+ }
+ `, */
+ features: ['class fields'],
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 9,
+ column: 13,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "b": PropTypes.string
+ };
+ Hello.defaultProps = {
+ "b": "b",
+ "a": "a"
+ };
+ `,
+ /* output: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "b": PropTypes.string
+ };
+ Hello.defaultProps = {
+ "a": "a",
+ "b": "b"
+ };
+ `, */
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 13,
+ column: 11,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "b": PropTypes.string,
+ "c": PropTypes.string
+ };
+ Hello.defaultProps = {
+ "c": "c",
+ "b": "b",
+ "a": "a"
+ };
+ `,
+ /* output: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "b": PropTypes.string,
+ "c": PropTypes.string
+ };
+ Hello.defaultProps = {
+ "a": "a",
+ "b": "b",
+ "c": "c"
+ };
+ `, */
+ errors: 2,
+ },
+ {
+ code: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "B": PropTypes.string,
+ };
+ Hello.defaultProps = {
+ "a": "a",
+ "B": "B",
+ };
+ `,
+ /* output: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "B": PropTypes.string,
+ };
+ Hello.defaultProps = {
+ "B": "B",
+ "a": "a",
+ };
+ `, */
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 13,
+ column: 11,
+ type: 'Property',
+ },
+ ],
+ },
+ // {
+ // Disabled test for comments -- fails
+ // code: `
+ // class Hello extends React.Component {
+ // render() {
+ // return Hello
;
+ // }
+ // }
+ // Hello.propTypes = {
+ // "a": PropTypes.string,
+ // "B": PropTypes.string,
+ // };
+ // Hello.defaultProps = {
+ // /* a */
+ // "a": "a",
+ // /* B */
+ // "B": "B",
+ // };
+ // `,
+ // errors: [
+ // {
+ // messageId: 'propsNotSorted',
+ // line: 14,
+ // column: 3,
+ // type: 'Property'
+ // }
+ // ],
+ // output: `
+ // class Hello extends React.Component {
+ // render() {
+ // return Hello
;
+ // }
+ // }
+ // Hello.propTypes = {
+ // "a": PropTypes.string,
+ // "B": PropTypes.string,
+ // };
+ // Hello.defaultProps = {
+ // /* B */
+ // "B": "B",
+ // /* a */
+ // "a": "a",
+ // };
+ // `
+ // },
+ {
+ code: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "B": PropTypes.string,
+ };
+ Hello.defaultProps = {
+ "B": "B",
+ "a": "a",
+ };
+ `,
+ /* output: `
+ class Hello extends React.Component {
+ render() {
+ return Hello
;
+ }
+ }
+ Hello.propTypes = {
+ "a": PropTypes.string,
+ "B": PropTypes.string,
+ };
+ Hello.defaultProps = {
+ "a": "a",
+ "B": "B",
+ };
+ `, */
+ options: [{ ignoreCase: true }],
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 13,
+ column: 11,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ const First = (props) => ;
+ const propTypes = {
+ z: PropTypes.string,
+ a: PropTypes.any,
+ };
+ const defaultProps = {
+ z: "z",
+ a: "a",
+ };
+ First.propTypes = propTypes;
+ First.defaultProps = defaultProps;
+ `,
+ /* output: `
+ const First = (props) => ;
+ const propTypes = {
+ z: PropTypes.string,
+ a: PropTypes.any,
+ };
+ const defaultProps = {
+ a: "a",
+ z: "z",
+ };
+ First.propTypes = propTypes;
+ First.defaultProps = defaultProps;
+ `, */
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 9,
+ column: 11,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ b: PropTypes.string,
+ ...c.propTypes,
+ a: PropTypes.string
+ }
+ static defaultProps = {
+ b: "b",
+ a: "a",
+ ...c.defaultProps
+ }
+ }
+ `,
+ /* output: `
+ export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ b: PropTypes.string,
+ ...c.propTypes,
+ a: PropTypes.string
+ }
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ ...c.defaultProps
+ }
+ }
+ `, */
+ features: ['class fields'],
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 10,
+ column: 13,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string,
+ d: PropTypes.string,
+ e: PropTypes.string,
+ f: PropTypes.string
+ }
+ static defaultProps = {
+ b: "b",
+ a: "a",
+ ...c.defaultProps,
+ f: "f",
+ e: "e",
+ ...d.defaultProps
+ }
+ }
+ `,
+ /* output: `
+ export default class ClassWithSpreadInPropTypes extends BaseClass {
+ static propTypes = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string,
+ d: PropTypes.string,
+ e: PropTypes.string,
+ f: PropTypes.string
+ }
+ static defaultProps = {
+ a: "a",
+ b: "b",
+ ...c.defaultProps,
+ e: "e",
+ f: "f",
+ ...d.defaultProps
+ }
+ }
+ `, */
+ features: ['class fields'],
+ errors: 2,
+ },
+ {
+ code: `
+ const defaults = {
+ b: "b"
+ };
+ const types = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string
+ };
+ function StatelessComponentWithSpreadInPropTypes({ a, b, c }) {
+ return {a}{b}{c}
;
+ }
+ StatelessComponentWithSpreadInPropTypes.propTypes = types;
+ StatelessComponentWithSpreadInPropTypes.defaultProps = {
+ c: "c",
+ a: "a",
+ ...defaults,
+ };
+ `,
+ /* output: `
+ const defaults = {
+ b: "b"
+ };
+ const types = {
+ a: PropTypes.string,
+ b: PropTypes.string,
+ c: PropTypes.string
+ };
+ function StatelessComponentWithSpreadInPropTypes({ a, b, c }) {
+ return {a}{b}{c}
;
+ }
+ StatelessComponentWithSpreadInPropTypes.propTypes = types;
+ StatelessComponentWithSpreadInPropTypes.defaultProps = {
+ a: "a",
+ c: "c",
+ ...defaults,
+ };
+ `, */
+ errors: [
+ {
+ messageId: 'propsNotSorted',
+ line: 16,
+ column: 11,
+ type: 'Property',
+ },
+ ],
+ },
+ {
+ code: `
+ class First extends React.Component {
+ render() {
+ return ;
+ }
+ }
+
+ First.defaultProps = {
+ a: PropTypes.any,
+ z: PropTypes.string,
+ onFoo: PropTypes.func,
+ onBar: PropTypes.func,
+ };
+ `,
+ errors: [
+ { messageId: 'propsNotSorted', line: 11 },
+ { messageId: 'propsNotSorted', line: 12 },
+ ],
+ },
+ ]),
+});