diff --git a/README.md b/README.md index 370d908841..644d47b87a 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,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/static-property-placement](docs/rules/static-property-placement.md): Enforces 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 index 1cacd68b93..b3cd26c52c 100644 --- a/docs/rules/static-property-placement.md +++ b/docs/rules/static-property-placement.md @@ -8,7 +8,7 @@ and `propTypes` are declared in an ES6 class. 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`. +The three key options are `static public field`, `static getter`, and `property assignment`. ### When `static public field` is enabled (default): @@ -16,11 +16,11 @@ 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 childContextTypes() { /*...*/ } + static get contextTypes() { /*...*/ } + static get contextType() { /*...*/ } + static get displayName() { /*...*/ } + static get defaultProps() { /*...*/ } static get propTypes() { /*...*/ } } ``` @@ -39,11 +39,11 @@ 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 childContextTypes = { /*...*/ }; + static contextTypes = { /*...*/ }; + static contextType = { /*...*/ }; + static displayName = "Hello"; + static defaultProps = { /*...*/ }; static propTypes = { /*...*/ }; } ``` @@ -54,11 +54,11 @@ 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 childContextTypes = { /*...*/ }; + static contextTypes = { /*...*/ }; + static contextType = { /*...*/ }; + static displayName = "Hello"; + static defaultProps = { /*...*/ }; static propTypes = { /*...*/ }; } ``` @@ -77,11 +77,11 @@ 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 childContextTypes() { /*...*/ } + static get contextTypes() { /*...*/ } + static get contextType() { /*...*/ } + static get displayName() { /*...*/ } + static get defaultProps() { /*...*/ } static get propTypes() { /*...*/ } } ``` @@ -92,22 +92,22 @@ 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 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 childContextTypes() { /*...*/ } + static get contextTypes() { /*...*/ } + static get contextType() { /*...*/ } + static get displayName() { /*...*/ } + static get defaultProps() { /*...*/ } static get propTypes() { /*...*/ } } ``` @@ -159,7 +159,7 @@ The `` value must be one these options: * `static getter` * `property assignment` -The `options` schema defined above allows you to specify different rules for the different property fields available. +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._ diff --git a/lib/rules/static-property-placement.js b/lib/rules/static-property-placement.js index 995e1f6957..ddeebefc7a 100644 --- a/lib/rules/static-property-placement.js +++ b/lib/rules/static-property-placement.js @@ -83,10 +83,10 @@ module.exports = { // ---------------------------------------------------------------------- /** - * Checks if we are declaring function in class - * @returns {Boolean} True if we are declaring function in class, false if not. + * Checks if we are declaring context in class + * @returns {Boolean} True if we are declaring context in class, false if not. */ - function isFunctionInClass () { + function isContextInClass() { let blockNode; let scope = context.getScope(); while (scope) { @@ -129,9 +129,17 @@ module.exports = { ClassProperty: node => reportNodeIncorrectlyPositioned(node, STATIC_PUBLIC_FIELD), MemberExpression: node => { + // If definition type is undefined then it must not be a defining expression or if the definition is inside a + // class body then skip this node. + const right = node.parent.right; + if (!right || right.type === 'undefined' || isContextInClass()) { + return; + } + + // Get the related component const relatedComponent = utils.getRelatedComponent(node); - // Only check es6 components + // If the related component is not an ES6 component then skip this node if (!relatedComponent || !utils.isES6Component(relatedComponent.node)) { return; } @@ -142,7 +150,7 @@ module.exports = { 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') { + if (isContextInClass() && node.static && node.kind === 'get') { // Report error if needed reportNodeIncorrectlyPositioned(node, STATIC_GETTER); } diff --git a/tests/lib/rules/static-property-placement.js b/tests/lib/rules/static-property-placement.js index 6a56a3f0a4..3b890b512a 100644 --- a/tests/lib/rules/static-property-placement.js +++ b/tests/lib/rules/static-property-placement.js @@ -1072,6 +1072,35 @@ ruleTester.run('static-property-placement', rule, { } } `].join('\n') + }, + // ------------------------------------------------------------------------------ + // edge cases + // ------------------------------------------------------------------------------ + { + // Do not error if property assignment is inside a class function + code: [` + class MyComponent extends React.Component { + static displayName = "Hello"; + + myMethod() { + console.log(MyComponent.displayName); + } + } + `].join('\n'), + options: [STATIC_PUBLIC_FIELD] + }, + { + // Do not error if display name value changed + code: [` + class MyComponent extends React.Component { + static displayName = "Hello"; + + myMethod() { + MyComponent.displayName = "Bonjour"; + } + } + `].join('\n'), + options: [STATIC_PUBLIC_FIELD] } ],