Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fix] static-property-placement: fix bug with static property refs inside methods #2284

Merged
merged 1 commit into from May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -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. `<img />`, `<br />`) from receiving children

Expand Down
64 changes: 32 additions & 32 deletions docs/rules/static-property-placement.md
Expand Up @@ -8,19 +8,19 @@ 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):

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() { /*...*/ }
}
```
Expand All @@ -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 = { /*...*/ };
}
```
Expand All @@ -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 = { /*...*/ };
}
```
Expand All @@ -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() { /*...*/ }
}
```
Expand All @@ -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() { /*...*/ }
}
```
Expand Down Expand Up @@ -159,7 +159,7 @@ The `<string>` 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._
Expand Down
18 changes: 13 additions & 5 deletions lib/rules/static-property-placement.js
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
Expand Down
29 changes: 29 additions & 0 deletions tests/lib/rules/static-property-placement.js
Expand Up @@ -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]
}
],

Expand Down