diff --git a/README.md b/README.md
index 926a965abd..329e51b3a5 100644
--- a/README.md
+++ b/README.md
@@ -142,6 +142,7 @@ Enable the rules that you would like to use.
* [react/sort-comp](docs/rules/sort-comp.md): Enforce component methods order (fixable)
* [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 the state initialization style to be either in a constructor or with a class property
+* [react/static-property-placement](docs/rules/static-property-placement.md): Defines where React component static properties should be positioned.
* [react/style-prop-object](docs/rules/style-prop-object.md): Enforce style prop value being an object
* [react/void-dom-elements-no-children](docs/rules/void-dom-elements-no-children.md): Prevent void DOM elements (e.g. ``, `
`) from receiving children
diff --git a/docs/rules/static-property-placement.md b/docs/rules/static-property-placement.md
new file mode 100644
index 0000000000..14aa925f72
--- /dev/null
+++ b/docs/rules/static-property-placement.md
@@ -0,0 +1,185 @@
+# Enforces where React component static properties should be positioned. (static-property-placement)
+
+This rule allows you to enforce where `childContextTypes`, `contextTypes`, `contextType`, `defaultProps`, `displayName`,
+and `propTypes` are declared in an ES6 class.
+
+
+## Rule Details
+
+By default, this rule will check for and warn about declaring any of the above properties outside of the class body.
+
+There are three key options are `static public field`, `static getter`, and `property assignment`.
+
+### When `static public field` is enabled (default):
+
+Examples of **incorrect** code for this rule:
+
+```js
+class MyComponent extends React.Component {
+ static get childContextTypes() { /*...*/ }
+ static get contextTypes() { /*...*/ }
+ static get contextType() { /*...*/ }
+ static get displayName() { /*...*/ }
+ static get defaultProps() { /*...*/ }
+ static get propTypes() { /*...*/ }
+}
+```
+
+```js
+class MyComponent extends React.Component { /*...*/ }
+MyComponent.childContextTypes = { /*...*/ };
+MyComponent.contextTypes = { /*...*/ };
+MyComponent.contextType = { /*...*/ };
+MyComponent.displayName = "Hello";
+MyComponent.defaultProps = { /*...*/ };
+MyComponent.propTypes = { /*...*/ };
+```
+
+Examples of **correct** code for this rule:
+
+```js
+class MyComponent extends React.Component {
+ static childContextTypes = { /*...*/ };
+ static contextTypes = { /*...*/ };
+ static contextType = { /*...*/ };
+ static displayName = "Hello";
+ static defaultProps = { /*...*/ };
+ static propTypes = { /*...*/ };
+}
+```
+
+### When `static getter` is enabled:
+
+Examples of **incorrect** code for this rule:
+
+```js
+class MyComponent extends React.Component {
+ static childContextTypes = { /*...*/ };
+ static contextTypes = { /*...*/ };
+ static contextType = { /*...*/ };
+ static displayName = "Hello";
+ static defaultProps = { /*...*/ };
+ static propTypes = { /*...*/ };
+}
+```
+
+```js
+class MyComponent extends React.Component { /*...*/ }
+MyComponent.childContextTypes = { /*...*/ };
+MyComponent.contextTypes = { /*...*/ };
+MyComponent.contextType = { /*...*/ };
+MyComponent.displayName = "Hello";
+MyComponent.defaultProps = { /*...*/ };
+MyComponent.propTypes = { /*...*/ };
+```
+
+Examples of **correct** code for this rule:
+
+```js
+class MyComponent extends React.Component {
+ static get childContextTypes() { /*...*/ }
+ static get contextTypes() { /*...*/ }
+ static get contextType() { /*...*/ }
+ static get displayName() { /*...*/ }
+ static get defaultProps() { /*...*/ }
+ static get propTypes() { /*...*/ }
+}
+```
+
+### When `property assignment` is enabled:
+
+Examples of **incorrect** code for this rule:
+
+```js
+class MyComponent extends React.Component {
+ static childContextTypes = { /*...*/ };
+ static contextTypes = { /*...*/ };
+ static contextType = { /*...*/ };
+ static displayName = "Hello";
+ static defaultProps = { /*...*/ };
+ static propTypes = { /*...*/ };
+}
+```
+
+```js
+class MyComponent extends React.Component {
+ static get childContextTypes() { /*...*/ }
+ static get contextTypes() { /*...*/ }
+ static get contextType() { /*...*/ }
+ static get displayName() { /*...*/ }
+ static get defaultProps() { /*...*/ }
+ static get propTypes() { /*...*/ }
+}
+```
+
+Examples of **correct** code for this rule:
+
+```js
+class MyComponent extends React.Component { /*...*/ }
+MyComponent.childContextTypes = { /*...*/ };
+MyComponent.contextTypes = { /*...*/ };
+MyComponent.contextType = { /*...*/ };
+MyComponent.displayName = "Hello";
+MyComponent.defaultProps = { /*...*/ };
+MyComponent.propTypes = { /*...*/ };
+```
+
+### Options
+
+```
+...
+"react/static-property-placement": [] // `static public field` enabled
+...
+```
+
+or alternatively:
+
+```
+...
+"react/static-property-placement": [, ]
+...
+```
+
+or alternatively:
+
+```
+...
+"react/static-property-placement": [, , {
+ childContextTypes: ,
+ contextTypes: ,
+ contextType: ,
+ defaultProps: ,
+ displayName: ,
+ propTypes: ,
+}]
+...
+```
+The `` value must be one these options:
+* `static public field`
+* `static getter`
+* `property assignment`
+
+The `options` schema defined above allows you to specify different rules for the different property fields available.
+
+##### Example configuration:
+_This is only an example, we do not recommend this as a configuration._
+```
+...
+"react/static-property-placement": ["warn", "property assignment", {
+ childContextTypes: "static getter",
+ contextTypes: "static public field",
+ contextType: "static public field",
+ displayName: "static public field",
+}]
+...
+```
+
+Based on the above configuration:
+* `defaultProps` and `propTypes` will both enforce the `property assignment` rule.
+* `childContextTypes` will enforce the `static getter` rule.
+* `contextTypes`, `contextType`, and `displayName` will enforce the `static public field` rule.
+
+## When Not To Use It
+
+If you have no placement preference for React's static class properties.
+
diff --git a/index.js b/index.js
index 79cf2d997a..b2d4ccdb90 100644
--- a/index.js
+++ b/index.js
@@ -82,6 +82,7 @@ const allRules = {
'sort-comp': require('./lib/rules/sort-comp'),
'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'),
'style-prop-object': require('./lib/rules/style-prop-object'),
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children')
};
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index fcd77895c6..97da4890a6 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -7,6 +7,7 @@
const Components = require('../util/Components');
const astUtil = require('../util/ast');
const docsUrl = require('../util/docsUrl');
+const propsUtil = require('../util/props');
// ------------------------------------------------------------------------------
// Rule Definition
@@ -38,24 +39,6 @@ module.exports = {
const MISSING_MESSAGE = 'Component definition is missing display name';
- /**
- * Checks if we are declaring a display name
- * @param {ASTNode} node The AST node being checked.
- * @returns {Boolean} True if we are declaring a display name, false if not.
- */
- function isDisplayNameDeclaration(node) {
- switch (node.type) {
- case 'ClassProperty':
- return node.key && node.key.name === 'displayName';
- case 'Identifier':
- return node.name === 'displayName';
- case 'Literal':
- return node.value === 'displayName';
- default:
- return false;
- }
- }
-
/**
* Mark a prop type as declared
* @param {ASTNode} node The AST node being checked.
@@ -139,14 +122,14 @@ module.exports = {
return {
ClassProperty: function(node) {
- if (!isDisplayNameDeclaration(node)) {
+ if (!propsUtil.isDisplayNameDeclaration(node)) {
return;
}
markDisplayNameAsDeclared(node);
},
MemberExpression: function(node) {
- if (!isDisplayNameDeclaration(node.property)) {
+ if (!propsUtil.isDisplayNameDeclaration(node.property)) {
return;
}
const component = utils.getRelatedComponent(node);
@@ -184,7 +167,7 @@ module.exports = {
},
MethodDefinition: function(node) {
- if (!isDisplayNameDeclaration(node.key)) {
+ if (!propsUtil.isDisplayNameDeclaration(node.key)) {
return;
}
markDisplayNameAsDeclared(node);
@@ -208,7 +191,7 @@ module.exports = {
if (ignoreTranspilerName || !hasTranspilerName(node)) {
// Search for the displayName declaration
node.properties.forEach(property => {
- if (!property.key || !isDisplayNameDeclaration(property.key)) {
+ if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) {
return;
}
markDisplayNameAsDeclared(node);
diff --git a/lib/rules/static-property-placement.js b/lib/rules/static-property-placement.js
new file mode 100644
index 0000000000..19b3313f9b
--- /dev/null
+++ b/lib/rules/static-property-placement.js
@@ -0,0 +1,152 @@
+/**
+ * @fileoverview Defines where React component static properties should be positioned.
+ * @author Daniel Mason
+ */
+'use strict';
+
+const Components = require('../util/Components');
+const docsUrl = require('../util/docsUrl');
+const astUtil = require('../util/ast');
+const propsUtil = require('../util/props');
+const fromEntries = require('object.fromentries');
+
+// ------------------------------------------------------------------------------
+// Positioning Options
+// ------------------------------------------------------------------------------
+const STATIC_PUBLIC_FIELD = 'static public field';
+const STATIC_GETTER = 'static getter';
+const PROPERTY_ASSIGNMENT = 'property assignment';
+const POSITION_SETTINGS = [STATIC_PUBLIC_FIELD, STATIC_GETTER, PROPERTY_ASSIGNMENT];
+
+// ------------------------------------------------------------------------------
+// Rule messages
+// ------------------------------------------------------------------------------
+const ERROR_MESSAGES = {
+ [STATIC_PUBLIC_FIELD]: '\'{{name}}\' should be declared as a static class property.',
+ [STATIC_GETTER]: '\'{{name}}\' should be declared as a static getter class function.',
+ [PROPERTY_ASSIGNMENT]: '\'{{name}}\' should be declared outside the class body.'
+};
+
+// ------------------------------------------------------------------------------
+// Properties to check
+// ------------------------------------------------------------------------------
+const propertiesToCheck = {
+ propTypes: propsUtil.isPropTypesDeclaration,
+ defaultProps: propsUtil.isDefaultPropsDeclaration,
+ childContextTypes: propsUtil.isChildContextTypesDeclaration,
+ contextTypes: propsUtil.isContextTypesDeclaration,
+ contextType: propsUtil.isContextTypeDeclaration,
+ displayName: node => propsUtil.isDisplayNameDeclaration(astUtil.getPropertyNameNode(node))
+};
+
+const classProperties = Object.keys(propertiesToCheck);
+const schemaProperties = fromEntries(classProperties.map(property => [property, {enum: POSITION_SETTINGS}]));
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Defines where React component static properties should be positioned.',
+ category: 'Stylistic Issues',
+ recommended: false,
+ url: docsUrl('static-property-placement')
+ },
+ fixable: null, // or 'code' or 'whitespace'
+ schema: [
+ {enum: POSITION_SETTINGS},
+ {
+ type: 'object',
+ properties: schemaProperties,
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create: Components.detect((context, components, utils) => {
+ // variables should be defined here
+ const options = context.options;
+ const defaultCheckType = options[0] || STATIC_PUBLIC_FIELD;
+ const hasAdditionalConfig = options.length > 1;
+ const additionalConfig = hasAdditionalConfig ? options[1] : {};
+
+ // Set config
+ const config = fromEntries(classProperties.map(property => [
+ property,
+ additionalConfig[property] || defaultCheckType
+ ]));
+
+ // ----------------------------------------------------------------------
+ // Helpers
+ // ----------------------------------------------------------------------
+
+ /**
+ * Checks if we are declaring function in class
+ * @returns {Boolean} True if we are declaring function in class, false if not.
+ */
+ function isFunctionInClass () {
+ let blockNode;
+ let scope = context.getScope();
+ while (scope) {
+ blockNode = scope.block;
+ if (blockNode && blockNode.type === 'ClassDeclaration') {
+ return true;
+ }
+ scope = scope.upper;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if we should report this property node
+ * @param node
+ * @param expectedRule
+ */
+ function reportNodeIncorrectlyPositioned(node, expectedRule) {
+ // Detect if this node is an expected property declaration adn return the property name
+ const name = classProperties.find(propertyName => {
+ if (propertiesToCheck[propertyName](node)) {
+ return propertyName;
+ }
+
+ return null;
+ });
+
+ // If name is set but the configured rule does not match expected then report error
+ if (name && config[name] !== expectedRule) {
+ // Report the error
+ context.report(node, ERROR_MESSAGES[config[name]], {name});
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Public
+ // ----------------------------------------------------------------------
+ return {
+ ClassProperty: node => reportNodeIncorrectlyPositioned(node, STATIC_PUBLIC_FIELD),
+
+ MemberExpression: node => {
+ const relatedComponent = utils.getRelatedComponent(node);
+
+ // Only check es6 components
+ if (!relatedComponent || !utils.isES6Component(relatedComponent.node)) {
+ return;
+ }
+
+ // Report if needed
+ reportNodeIncorrectlyPositioned(node, PROPERTY_ASSIGNMENT);
+ },
+
+ MethodDefinition: node => {
+ // If the function is inside a class and is static getter then check if correctly positioned
+ if (isFunctionInClass() && node.static && node.kind === 'get') {
+ // Report error if needed
+ reportNodeIncorrectlyPositioned(node, STATIC_GETTER);
+ }
+ }
+ };
+ })
+};
diff --git a/lib/util/props.js b/lib/util/props.js
index f90648ccf2..66f6042fff 100644
--- a/lib/util/props.js
+++ b/lib/util/props.js
@@ -35,6 +35,15 @@ function isContextTypesDeclaration(node) {
return astUtil.getPropertyName(node) === 'contextTypes';
}
+/**
+ * Checks if the node passed in looks like a contextType declaration.
+ * @param {ASTNode} node The node to check.
+ * @returns {Boolean} `true` if the node is a contextType declaration, `false` if not
+ */
+function isContextTypeDeclaration(node) {
+ return astUtil.getPropertyName(node) === 'contextType';
+}
+
/**
* Checks if the node passed in looks like a childContextTypes declaration.
* @param {ASTNode} node The node to check.
@@ -54,6 +63,24 @@ function isDefaultPropsDeclaration(node) {
return (propName === 'defaultProps' || propName === 'getDefaultProps');
}
+/**
+ * Checks if we are declaring a display name
+ * @param {node} node The AST node being checked.
+ * @returns {Boolean} True if we are declaring a display name, false if not.
+ */
+function isDisplayNameDeclaration(node) {
+ switch (node.type) {
+ case 'ClassProperty':
+ return node.key && node.key.name === 'displayName';
+ case 'Identifier':
+ return node.name === 'displayName';
+ case 'Literal':
+ return node.value === 'displayName';
+ default:
+ return false;
+ }
+}
+
/**
* Checks if the PropTypes MemberExpression node passed in declares a required propType.
* @param {ASTNode} propTypeExpression node to check. Must be a `PropTypes` MemberExpression.
@@ -66,7 +93,9 @@ function isRequiredPropType(propTypeExpression) {
module.exports = {
isPropTypesDeclaration: isPropTypesDeclaration,
isContextTypesDeclaration: isContextTypesDeclaration,
+ isContextTypeDeclaration: isContextTypeDeclaration,
isChildContextTypesDeclaration: isChildContextTypesDeclaration,
isDefaultPropsDeclaration: isDefaultPropsDeclaration,
+ isDisplayNameDeclaration: isDisplayNameDeclaration,
isRequiredPropType: isRequiredPropType
};
diff --git a/tests/lib/rules/static-property-placement.js b/tests/lib/rules/static-property-placement.js
new file mode 100644
index 0000000000..e3123fef01
--- /dev/null
+++ b/tests/lib/rules/static-property-placement.js
@@ -0,0 +1,1797 @@
+/**
+ * @fileoverview Defines where React component static properties should be positioned.
+ * @author Daniel Mason
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Positioning Options
+// ------------------------------------------------------------------------------
+const STATIC_PUBLIC_FIELD = 'static public field';
+const STATIC_GETTER = 'static getter';
+const PROPERTY_ASSIGNMENT = 'property assignment';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/static-property-placement');
+const RuleTester = require('eslint').RuleTester;
+
+const ruleTesterConfig = {
+ parser: 'babel-eslint',
+ parserOptions: {
+ ecmaVersion: 2018,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true
+ }
+ },
+ settings: {
+ react: {
+ version: '15'
+ }
+ }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester(ruleTesterConfig);
+ruleTester.run('static-property-placement', rule, {
+ valid: [
+ // ------------------------------------------------------------------------------
+ // Ignore creatClass/createReactClass and Static Functional Components
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error on createReactClass pragma
+ code: [`
+ var MyComponent = createReactClass({
+ childContextTypes: {
+ something: PropTypes.bool
+ },
+
+ contextTypes: {
+ something: PropTypes.bool
+ },
+
+ getDefaultProps: function() {
+ name: 'Bob'
+ },
+
+ displayName: 'Hello',
+
+ propTypes: {
+ something: PropTypes.bool
+ },
+
+ render: function() {
+ return null;
+ },
+ });
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error on createClass pragma
+ code: [`
+ var MyComponent = React.createClass({
+ childContextTypes: {
+ something: PropTypes.bool
+ },
+
+ contextTypes: {
+ something: PropTypes.bool
+ },
+
+ getDefaultProps: function() {
+ name: 'Bob'
+ },
+
+ displayName: 'Hello',
+
+ propTypes: {
+ something: PropTypes.bool
+ },
+
+ render: function() {
+ return null;
+ },
+ });
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error on SFC arrow function with return
+ code: [`
+ const MyComponent = () => {
+ return Hello
;
+ };
+
+ MyComponent.childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ MyComponent.contextTypes = {
+ something: PropTypes.bool
+ };
+
+ MyComponent.defaultProps = {
+ something: 'Bob'
+ };
+
+ MyComponent.displayName = 'Hello';
+
+ MyComponent.propTypes = {
+ something: PropTypes.bool
+ };
+ `].join('\n')
+ },
+ {
+ // Do not error on SFC arrow function with direct return
+ code: [`
+ const MyComponent = () => (Hello
);
+
+ MyComponent.childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ MyComponent.contextTypes = {
+ something: PropTypes.bool
+ };
+
+ MyComponent.defaultProps = {
+ something: 'Bob'
+ };
+
+ MyComponent.displayName = 'Hello';
+
+ MyComponent.propTypes = {
+ something: PropTypes.bool
+ };
+ `].join('\n')
+ },
+ {
+ // Do not error on SFC as unnamed function
+ code: [`
+ export function MyComponent () {
+ return Hello
;
+ };
+
+ MyComponent.childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ MyComponent.contextTypes = {
+ something: PropTypes.bool
+ };
+
+ MyComponent.defaultProps = {
+ something: 'Bob'
+ };
+
+ MyComponent.displayName = 'Hello';
+
+ MyComponent.propTypes = {
+ something: PropTypes.bool
+ };
+ `].join('\n')
+ },
+ // ------------------------------------------------------------------------------
+ // no properties
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if no properties defined
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if unchecked properties defined
+ code: [`
+ class MyComponent extends React.Component {
+ static randomlyNamed = {
+ name: 'random'
+ }
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if unchecked static properties defined and assignment rule enabled
+ code: [`
+ class MyComponent extends React.Component {
+ static randomlyNamed = {
+ name: 'random'
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if unchecked assignment properties defined and assignment rule enabled
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.randomlyNamed = {
+ name: 'random'
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if unchecked assignment properties defined and static rule enabled
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.randomlyNamed = {
+ name: 'random'
+ }
+ `].join('\n')
+ },
+ // ------------------------------------------------------------------------------
+ // childContextTypes - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if childContextTypes correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if childContextTypes correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {childContextTypes: STATIC_PUBLIC_FIELD}]
+ },
+ // ------------------------------------------------------------------------------
+ // childContextTypes - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if childContextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {childContextTypes: STATIC_GETTER}]
+ },
+ // ------------------------------------------------------------------------------
+ // childContextTypes - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if childContextTypes correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if childContextTypes correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {childContextTypes: PROPERTY_ASSIGNMENT}]
+ },
+ // ------------------------------------------------------------------------------
+ // contextTypes - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if contextTypes correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if contextTypes correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {contextTypes: STATIC_PUBLIC_FIELD}]
+ },
+ // ------------------------------------------------------------------------------
+ // contextTypes - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {contextTypes: STATIC_GETTER}]
+ },
+ // ------------------------------------------------------------------------------
+ // contextTypes - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if contextTypes correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if contextTypes correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {contextTypes: PROPERTY_ASSIGNMENT}]
+ },
+ // ------------------------------------------------------------------------------
+ // contextType - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if contextType correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static contextType = MyContext;
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if contextType correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static contextType = MyContext;
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {contextType: STATIC_PUBLIC_FIELD}]
+ },
+ // ------------------------------------------------------------------------------
+ // contextType - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if contextType correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static get contextType() {
+ return MyContext;
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextType correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static get contextType() {
+ return MyContext;
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {contextType: STATIC_GETTER}]
+ },
+ // ------------------------------------------------------------------------------
+ // contextType - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if contextType correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.contextType = MyContext;
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if contextType correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.contextType = MyContext;
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {contextType: PROPERTY_ASSIGNMENT}]
+ },
+ // ------------------------------------------------------------------------------
+ // displayName - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if displayName correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static displayName = "Hello";
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if displayName correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static displayName = "Hello";
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {displayName: STATIC_PUBLIC_FIELD}]
+ },
+ // ------------------------------------------------------------------------------
+ // displayName - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if displayName correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get displayName() {
+ return "Hello";
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get displayName() {
+ return "Hello";
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {displayName: STATIC_GETTER}]
+ },
+ // ------------------------------------------------------------------------------
+ // displayName - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if displayName correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.displayName = "Hello";
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if displayName correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.displayName = "Hello";
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {displayName: PROPERTY_ASSIGNMENT}]
+ },
+ // ------------------------------------------------------------------------------
+ // defaultProps - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if defaultProps correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static defaultProps = {
+ something: 'Bob'
+ };
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if defaultProps correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static defaultProps = {
+ something: 'Bob'
+ };
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {defaultProps: STATIC_PUBLIC_FIELD}]
+ },
+ // ------------------------------------------------------------------------------
+ // defaultProps - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if defaultProps correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get defaultProps() {
+ return {
+ something: 'Bob'
+ };
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get defaultProps() {
+ return {
+ something: 'Bob'
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {defaultProps: STATIC_GETTER}]
+ },
+ // ------------------------------------------------------------------------------
+ // defaultProps - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if defaultProps correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if defaultProps correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {defaultProps: PROPERTY_ASSIGNMENT}]
+ },
+ // ------------------------------------------------------------------------------
+ // propTypes - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if propTypes correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if propTypes correctly defined - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {propTypes: STATIC_PUBLIC_FIELD}]
+ },
+ // ------------------------------------------------------------------------------
+ // propTypes - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if propTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {propTypes: STATIC_GETTER}]
+ },
+ // ------------------------------------------------------------------------------
+ // propTypes - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if propTypes correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if propTypes correctly defined - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {propTypes: PROPERTY_ASSIGNMENT}]
+ },
+ // ------------------------------------------------------------------------------
+ // multiple - static field
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if multiple properties and match config - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+
+ static defaultProps = {
+ something: 'Bob'
+ };
+
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n')
+ },
+ {
+ // Do not error if multiple properties and match config - static field
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+
+ static defaultProps = {
+ something: 'Bob'
+ };
+
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ childContextTypes: STATIC_PUBLIC_FIELD,
+ contextTypes: STATIC_PUBLIC_FIELD,
+ contextType: STATIC_PUBLIC_FIELD,
+ displayName: STATIC_PUBLIC_FIELD,
+ defaultProps: STATIC_PUBLIC_FIELD,
+ propTypes: STATIC_PUBLIC_FIELD
+ }]
+ },
+ // ------------------------------------------------------------------------------
+ // multiple - static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if childContextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextType() {
+ return MyContext;
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+
+ static get defaultProps() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER]
+ },
+ {
+ // Do not error if contextTypes correctly defined - static getter
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextType() {
+ return MyContext;
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+
+ static get defaultProps() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ childContextTypes: STATIC_GETTER,
+ contextTypes: STATIC_GETTER,
+ contextType: STATIC_GETTER,
+ displayName: STATIC_GETTER,
+ defaultProps: STATIC_GETTER,
+ propTypes: STATIC_GETTER
+ }]
+ },
+ // ------------------------------------------------------------------------------
+ // multiple - assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if multiple properties and match config - assignment
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.displayName = "Hello";
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT]
+ },
+ {
+ // Do not error if multiple properties and match config - static field
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.displayName = "Hello";
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {
+ childContextTypes: PROPERTY_ASSIGNMENT,
+ contextTypes: PROPERTY_ASSIGNMENT,
+ displayName: PROPERTY_ASSIGNMENT,
+ defaultProps: PROPERTY_ASSIGNMENT,
+ propTypes: PROPERTY_ASSIGNMENT
+ }]
+ },
+ // ------------------------------------------------------------------------------
+ // combined - mixed
+ // ------------------------------------------------------------------------------
+ {
+ // Do not error if mixed property positions and match config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static get displayName() {
+ return "Hello"
+ }
+ }
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {
+ displayName: STATIC_GETTER,
+ defaultProps: PROPERTY_ASSIGNMENT,
+ propTypes: PROPERTY_ASSIGNMENT
+ }]
+ },
+ {
+ // Do not error if mixed property positions and match config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static get displayName() {
+ return "Hello"
+ }
+ }
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ childContextTypes: STATIC_PUBLIC_FIELD,
+ contextTypes: STATIC_PUBLIC_FIELD,
+ displayName: STATIC_GETTER
+ }]
+ },
+ // ------------------------------------------------------------------------------
+ // mixed component types
+ // ------------------------------------------------------------------------------
+ {
+ // SFC ignored and component is valid
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static displayName = "Hello";
+ }
+
+ const OtherComponent = () => (Hello
);
+
+ OtherComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ OtherComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n')
+ },
+ {
+ // Multiple components validated
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static displayName = "Hello";
+ }
+
+ class OtherComponent extends React.Component {
+ static defaultProps = {
+ name: 'Bob'
+ }
+
+ static propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ }
+ `].join('\n')
+ }
+ ],
+
+ invalid: [
+ // ------------------------------------------------------------------------------
+ // expected static field when got property assigment
+ // ------------------------------------------------------------------------------
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextType = MyContext;
+
+ MyComponent.displayName = "Hello";
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static class property.'},
+ {message: '\'contextTypes\' should be declared as a static class property.'},
+ {message: '\'contextType\' should be declared as a static class property.'},
+ {message: '\'displayName\' should be declared as a static class property.'},
+ {message: '\'defaultProps\' should be declared as a static class property.'},
+ {message: '\'propTypes\' should be declared as a static class property.'}
+ ]
+ },
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextType = MyContext;
+
+ MyComponent.displayName = "Hello";
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ childContextTypes: STATIC_PUBLIC_FIELD,
+ contextTypes: STATIC_PUBLIC_FIELD,
+ contextType: STATIC_PUBLIC_FIELD,
+ displayName: STATIC_PUBLIC_FIELD,
+ defaultProps: STATIC_PUBLIC_FIELD,
+ propTypes: STATIC_PUBLIC_FIELD
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static class property.'},
+ {message: '\'contextTypes\' should be declared as a static class property.'},
+ {message: '\'contextType\' should be declared as a static class property.'},
+ {message: '\'displayName\' should be declared as a static class property.'},
+ {message: '\'defaultProps\' should be declared as a static class property.'},
+ {message: '\'propTypes\' should be declared as a static class property.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // expected static field when got static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextType() {
+ return MyContext;
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+
+ static get defaultProps() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static class property.'},
+ {message: '\'contextTypes\' should be declared as a static class property.'},
+ {message: '\'contextType\' should be declared as a static class property.'},
+ {message: '\'displayName\' should be declared as a static class property.'},
+ {message: '\'defaultProps\' should be declared as a static class property.'},
+ {message: '\'propTypes\' should be declared as a static class property.'}
+ ]
+ },
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextType() {
+ return MyContext;
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+
+ static get defaultProps() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ childContextTypes: STATIC_PUBLIC_FIELD,
+ contextTypes: STATIC_PUBLIC_FIELD,
+ contextType: STATIC_PUBLIC_FIELD,
+ displayName: STATIC_PUBLIC_FIELD,
+ defaultProps: STATIC_PUBLIC_FIELD,
+ propTypes: STATIC_PUBLIC_FIELD
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static class property.'},
+ {message: '\'contextTypes\' should be declared as a static class property.'},
+ {message: '\'contextType\' should be declared as a static class property.'},
+ {message: '\'displayName\' should be declared as a static class property.'},
+ {message: '\'defaultProps\' should be declared as a static class property.'},
+ {message: '\'propTypes\' should be declared as a static class property.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // expected property assignment when got static field
+ // ------------------------------------------------------------------------------
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+
+ static defaultProps = {
+ something: 'Bob'
+ };
+
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'},
+ {message: '\'defaultProps\' should be declared outside the class body.'},
+ {message: '\'propTypes\' should be declared outside the class body.'}
+ ]
+ },
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+
+ static defaultProps = {
+ something: 'Bob'
+ };
+
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {
+ childContextTypes: PROPERTY_ASSIGNMENT,
+ contextTypes: PROPERTY_ASSIGNMENT,
+ contextType: PROPERTY_ASSIGNMENT,
+ displayName: PROPERTY_ASSIGNMENT,
+ defaultProps: PROPERTY_ASSIGNMENT,
+ propTypes: PROPERTY_ASSIGNMENT
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'},
+ {message: '\'defaultProps\' should be declared outside the class body.'},
+ {message: '\'propTypes\' should be declared outside the class body.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // expected property assignment when got static getter
+ // ------------------------------------------------------------------------------
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextType() {
+ return MyContext;
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+
+ static get defaultProps() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'},
+ {message: '\'defaultProps\' should be declared outside the class body.'},
+ {message: '\'propTypes\' should be declared outside the class body.'}
+ ]
+ },
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static get childContextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get contextType() {
+ return MyContext;
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+
+ static get defaultProps() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+
+ static get propTypes() {
+ return {
+ something: PropTypes.bool
+ };
+ }
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER, {
+ childContextTypes: PROPERTY_ASSIGNMENT,
+ contextTypes: PROPERTY_ASSIGNMENT,
+ contextType: PROPERTY_ASSIGNMENT,
+ displayName: PROPERTY_ASSIGNMENT,
+ defaultProps: PROPERTY_ASSIGNMENT,
+ propTypes: PROPERTY_ASSIGNMENT
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'},
+ {message: '\'defaultProps\' should be declared outside the class body.'},
+ {message: '\'propTypes\' should be declared outside the class body.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // expected static getter when got static field
+ // ------------------------------------------------------------------------------
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+
+ static defaultProps = {
+ something: 'Bob'
+ };
+
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER],
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextType\' should be declared as a static getter class function.'},
+ {message: '\'displayName\' should be declared as a static getter class function.'},
+ {message: '\'defaultProps\' should be declared as a static getter class function.'},
+ {message: '\'propTypes\' should be declared as a static getter class function.'}
+ ]
+ },
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextTypes = {
+ something: PropTypes.bool
+ };
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+
+ static defaultProps = {
+ something: 'Bob'
+ };
+
+ static propTypes = {
+ something: PropTypes.bool
+ };
+ }
+ `].join('\n'),
+ options: [STATIC_PUBLIC_FIELD, {
+ childContextTypes: STATIC_GETTER,
+ contextTypes: STATIC_GETTER,
+ contextType: STATIC_GETTER,
+ displayName: STATIC_GETTER,
+ defaultProps: STATIC_GETTER,
+ propTypes: STATIC_GETTER
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextType\' should be declared as a static getter class function.'},
+ {message: '\'displayName\' should be declared as a static getter class function.'},
+ {message: '\'defaultProps\' should be declared as a static getter class function.'},
+ {message: '\'propTypes\' should be declared as a static getter class function.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // expected static getter when got property assignment
+ // ------------------------------------------------------------------------------
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextType = MyContext;
+
+ MyComponent.displayName = "Hello";
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER],
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextType\' should be declared as a static getter class function.'},
+ {message: '\'displayName\' should be declared as a static getter class function.'},
+ {message: '\'defaultProps\' should be declared as a static getter class function.'},
+ {message: '\'propTypes\' should be declared as a static getter class function.'}
+ ]
+ },
+ {
+ // Error if multiple properties are incorrectly positioned according to config
+ code: [`
+ class MyComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ MyComponent.childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ MyComponent.contextType = MyContext;
+
+ MyComponent.displayName = "Hello";
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ childContextTypes: STATIC_GETTER,
+ contextTypes: STATIC_GETTER,
+ contextType: STATIC_GETTER,
+ displayName: STATIC_GETTER,
+ defaultProps: STATIC_GETTER,
+ propTypes: STATIC_GETTER
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextTypes\' should be declared as a static getter class function.'},
+ {message: '\'contextType\' should be declared as a static getter class function.'},
+ {message: '\'displayName\' should be declared as a static getter class function.'},
+ {message: '\'defaultProps\' should be declared as a static getter class function.'},
+ {message: '\'propTypes\' should be declared as a static getter class function.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // combined - mixed
+ // ------------------------------------------------------------------------------
+ {
+ // Error if mixed property positions but dont match config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextType = MyContext;
+
+ static get displayName() {
+ return "Hello";
+ }
+ }
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ defaultProps: STATIC_GETTER,
+ propTypes: STATIC_PUBLIC_FIELD,
+ displayName: STATIC_PUBLIC_FIELD
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared as a static class property.'},
+ {message: '\'defaultProps\' should be declared as a static getter class function.'},
+ {message: '\'propTypes\' should be declared as a static class property.'}
+ ]
+ },
+ {
+ // Error if mixed property positions but dont match config
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextType = MyContext;
+
+ static get displayName() {
+ return "Hello";
+ }
+ }
+
+ MyComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ MyComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [STATIC_GETTER, {
+ childContextTypes: PROPERTY_ASSIGNMENT,
+ contextTypes: PROPERTY_ASSIGNMENT,
+ contextType: PROPERTY_ASSIGNMENT,
+ displayName: PROPERTY_ASSIGNMENT
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'},
+ {message: '\'defaultProps\' should be declared as a static getter class function.'},
+ {message: '\'propTypes\' should be declared as a static getter class function.'}
+ ]
+ },
+ // ------------------------------------------------------------------------------
+ // mixed component types
+ // ------------------------------------------------------------------------------
+ {
+ // SFC ignored and component is invalid
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextType = MyContext;
+
+ static get displayName() {
+ return "Hello";
+ }
+ }
+
+ const OtherComponent = () => (Hello
);
+
+ OtherComponent.defaultProps = {
+ name: 'Bob'
+ }
+
+ OtherComponent.propTypes = {
+ name: PropTypes.string.isRequired
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT, {
+ defaultProps: STATIC_PUBLIC_FIELD,
+ propTypes: STATIC_GETTER
+ }],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'}
+ ]
+ },
+ {
+ // Multiple components validated
+ code: [`
+ class MyComponent extends React.Component {
+ static childContextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static contextType = MyContext;
+
+ static displayName = "Hello";
+ }
+
+ class OtherComponent extends React.Component {
+ static contextTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static defaultProps = {
+ name: 'Bob'
+ }
+
+ static propTypes = {
+ name: PropTypes.string.isRequired
+ }
+
+ static get displayName() {
+ return "Hello";
+ }
+ }
+ `].join('\n'),
+ options: [PROPERTY_ASSIGNMENT],
+ errors: [
+ {message: '\'childContextTypes\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'contextType\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'},
+ {message: '\'contextTypes\' should be declared outside the class body.'},
+ {message: '\'defaultProps\' should be declared outside the class body.'},
+ {message: '\'propTypes\' should be declared outside the class body.'},
+ {message: '\'displayName\' should be declared outside the class body.'}
+ ]
+
+ }
+ ]
+});