Skip to content

Commit 80fac72

Browse files
committed
update accessibilityState rule for RN v0.61+
1 parent 2539584 commit 80fac72

File tree

5 files changed

+164
-27
lines changed

5 files changed

+164
-27
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Alternatively, you can enable all the recommended rules at once by adding `plugi
6363
- [accessibility-label](docs/rules/accessibility-label.md): Enforce that views that have `accessible={true}`, also have an `accessibilityLabel` prop
6464
- [has-accessibility-props](docs/rules/has-accessibility-props.md): Enforce all `<Touchable\*>` components have `accessibilityRole` prop or both `accessibilityTraits` and `accessibilityComponentType` props set
6565
- [has-valid-accessibility-role](docs/rules/has-valid-accessibility-role.md): Enforce `accessibilityRole` property value is valid
66+
- [has-valid-accessibility-state](docs/rules/has-valid-accessibility-state.md): Enforce `accessibilityState` property value is valid
6667
- [has-valid-accessibility-states](docs/rules/has-valid-accessibility-states.md): Enforce `accessibilityStates` property value is valid
6768
- [has-valid-accessibility-component-type](docs/rules/has-valid-accessibility-component-type.md): Enforce `accessibilityComponentType` property value is valid
6869
- [has-valid-accessibility-traits](docs/rules/has-valid-accessibility-traits.md): Enforce `accessibilityTraits` and `accessibilityComponentType` prop values must be valid
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* eslint-env jest */
2+
/**
3+
* @fileoverview Describes the current state of a component to the user of an assistive technology.
4+
* @author JP Driver
5+
*/
6+
7+
// -----------------------------------------------------------------------------
8+
// Requirements
9+
// -----------------------------------------------------------------------------
10+
11+
import { RuleTester } from 'eslint';
12+
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
13+
import rule from '../../../src/rules/has-valid-accessibility-state';
14+
15+
// -----------------------------------------------------------------------------
16+
// Tests
17+
// -----------------------------------------------------------------------------
18+
19+
const ruleTester = new RuleTester();
20+
21+
const propMustBeAnObject = {
22+
message: 'accessibilityState must be an object',
23+
type: 'JSXAttribute'
24+
};
25+
26+
const invalidObjectKey = key => ({
27+
message: `accessibilityState object: "${key}" is not a valid key`,
28+
type: 'JSXAttribute'
29+
});
30+
31+
const valueMustBeBoolean = key => ({
32+
message: `accessibilityState object: "${key}" value is not a boolean`,
33+
type: 'JSXAttribute'
34+
});
35+
36+
const checkedMustBeBooleanOrMixed = {
37+
message: `accessibilityState object: "checked" value is not either a boolean or 'mixed'`,
38+
type: 'JSXAttribute'
39+
};
40+
41+
ruleTester.run('has-valid-accessibility-state', rule, {
42+
valid: [
43+
{ code: '<TouchableOpacity accessibilityState={{ disabled: true }} />;' },
44+
{ code: '<TouchableOpacity accessibilityState={{ checked: true }} />;' },
45+
{ code: '<TouchableOpacity accessibilityState={{ checked: "mixed" }} />;' }
46+
].map(parserOptionsMapper),
47+
invalid: [
48+
{
49+
code: '<TouchableOpacity accessibilityState="disabled" />',
50+
errors: [propMustBeAnObject]
51+
},
52+
{
53+
code: '<TouchableOpacity accessibilityState={["disabled"]} />',
54+
errors: [propMustBeAnObject]
55+
},
56+
{
57+
code: '<TouchableOpacity accessibilityState={{ disabled: "yes" }} />',
58+
errors: [valueMustBeBoolean('disabled')]
59+
},
60+
{
61+
code: '<TouchableOpacity accessibilityState={{ checked: "yes" }} />',
62+
errors: [checkedMustBeBooleanOrMixed]
63+
},
64+
{
65+
code: '<TouchableOpacity accessibilityState={{ foo: true }} />',
66+
errors: [invalidObjectKey('foo')]
67+
},
68+
{
69+
code: '<TouchableOpacity accessibilityState={{ foo: "yes" }} />',
70+
errors: [invalidObjectKey('foo')]
71+
}
72+
].map(parserOptionsMapper)
73+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# has-valid-accessibility-state
2+
3+
Describes the current state of a component to the user of an assistive technology.
4+
5+
## `accessibilityState` is an object. It contains the following fields:
6+
7+
NAME|TYPE|REQUIRED
8+
-|-|-
9+
disabled|boolean|No
10+
selecte|boolean|No
11+
checked|boolean or 'mixed'|No
12+
busy|boolean|No
13+
expanded|boolean|No
14+
15+
### References
16+
17+
1. [React Native Docs - accessibilityState (iOS, Android)](https://facebook.github.io/react-native/docs/accessibility#accessibilitystate-ios-android)
18+
19+
## Rule details
20+
21+
This rule takes no arguments.
22+
23+
### Succeed
24+
25+
```jsx
26+
<TouchableOpacity accessibilityState={{ disabled: true }} />
27+
<TouchableOpacity accessibilityState={{ checked: true }} />
28+
<TouchableOpacity accessibilityState={{ checked: "mixed" }} />
29+
```
30+
31+
### Fail
32+
33+
```jsx
34+
<TouchableOpacity accessibilityState="disabled" />
35+
<TouchableOpacity accessibilityState={["disabled"]} />
36+
<TouchableOpacity accessibilityState={{ disabled: "yes" }} />
37+
<TouchableOpacity accessibilityState={{ checked: "yes" }} />
38+
<TouchableOpacity accessibilityState={{ foo: true }} />
39+
<TouchableOpacity accessibilityState={{ foo: "yes" }} />
40+
```
41+
42+
*Note*: This plugin previously defined a rule with this name which is now `has-valid-accessibility-states` (see [#42](https://github.com/FormidableLabs/eslint-plugin-react-native-a11y/pull/42)). React Native v0.61 introduced a new `accessibilityState` prop (see [commit](https://github.com/facebook/react-native/commit/099be9b35634851b178e990c47358c2129c0dd7d)) -- which is now covered by this rule.

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = {
2626
'react-native-a11y/has-valid-accessibility-component-type': 'error',
2727
'react-native-a11y/has-valid-accessibility-live-region': 'error',
2828
'react-native-a11y/has-valid-accessibility-role': 'error',
29+
'react-native-a11y/has-valid-accessibility-state': 'error',
2930
'react-native-a11y/has-valid-accessibility-states': 'error',
3031
'react-native-a11y/has-valid-accessibility-traits': 'error',
3132
'react-native-a11y/has-valid-important-for-accessibility': 'error',
Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,60 @@
11
/**
2-
* @fileoverview Used to tell Talkback or Voiceover the state a UI Element is in
3-
* @author Jen Luker
2+
* @fileoverview Describes the current state of a component to the user of an assistive technology.
3+
* @author JP Driver
44
* @flow
55
*/
66

7-
import createValidPropRule from '../factory/valid-prop';
7+
import type { JSXAttribute } from 'ast-types-flow';
8+
import { elementType, getPropValue, getLiteralPropValue } from 'jsx-ast-utils';
9+
import { generateObjSchema } from '../util/schemas';
10+
import type { ESLintContext } from '../../flow/eslint';
811

912
// ----------------------------------------------------------------------------
1013
// Rule Definition
1114
// ----------------------------------------------------------------------------
1215

13-
const errorMessage =
14-
'accessibilityStates must be one or both of the defined values';
16+
const validKeys = ['disabled', 'selected', 'checked', 'busy', 'expanded'];
1517

16-
const validValues = ['selected', 'disabled'];
18+
module.exports = {
19+
meta: {
20+
docs: {},
21+
schema: [generateObjSchema()]
22+
},
1723

18-
let deprecationHasBeenWarned = false;
24+
create: (context: ESLintContext) => ({
25+
JSXAttribute: (node: JSXAttribute) => {
26+
const attrName = elementType(node);
27+
if (attrName === 'accessibilityState') {
28+
const attrValue = getPropValue(node);
29+
const test = getLiteralPropValue(node);
1930

20-
const rule = createValidPropRule(
21-
'accessibilityStates',
22-
validValues,
23-
errorMessage,
24-
{
25-
deprecated: true
26-
},
27-
{
28-
Program: () => {
29-
if (deprecationHasBeenWarned) return;
30-
// eslint-disable-next-line no-console
31-
console.log(
32-
'The react-native-a11y/has-valid-accessibility-state rule is deprecated. ' +
33-
'Please use the react-native-a11y/has-valid-accessibility-states rule instead.'
34-
);
35-
deprecationHasBeenWarned = true;
36-
}
37-
}
38-
);
31+
const error = message =>
32+
context.report({
33+
node,
34+
message
35+
});
3936

40-
module.exports = rule;
37+
if (typeof attrValue !== 'object' || Array.isArray(attrValue)) {
38+
error('accessibilityState must be an object');
39+
} else {
40+
Object.entries(attrValue).map(([key, value]) => {
41+
if (validKeys.indexOf(key) < 0) {
42+
error(`accessibilityState object: "${key}" is not a valid key`);
43+
} else if (key !== 'checked' && typeof value !== 'boolean') {
44+
error(
45+
`accessibilityState object: "${key}" value is not a boolean`
46+
);
47+
} else if (
48+
key === 'checked' &&
49+
!(typeof value === 'boolean' || value === 'mixed')
50+
) {
51+
error(
52+
`accessibilityState object: "checked" value is not either a boolean or 'mixed'`
53+
);
54+
}
55+
});
56+
}
57+
}
58+
}
59+
})
60+
};

0 commit comments

Comments
 (0)