Giter Site home page Giter Site logo

eslint-plugin-react-native-a11y's Introduction

Eslint-plugin-react-native-a11y — Formidable, We build the modern web

Maintenance Status

Eslint-plugin-react-native-a11y is a collection of React Native specific ESLint rules for identifying accessibility issues. Building upon the foundation set down by eslint-plugin-jsx-a11y, eslint-plugin-react-native-a11y detects a few of the most commonly made accessibility issues found in react native apps. These rules make it easier for your apps to be navigable by users with screen readers.

Setup

Pre-Requisites

Before starting, check you already have ESLint as a devDependency of your project.

Projects created using react-native init will already have this, but for Expo depending on your template you may need to follow ESLint's installation instructions.

Installation

Next, install eslint-plugin-react-native-a11y:

npm install eslint-plugin-react-native-a11y --save-dev

# or

yarn add eslint-plugin-react-native-a11y --dev

Note: If you installed ESLint globally (using the -g flag in npm, or the global prefix in yarn) then you must also install eslint-plugin-react-native-a11y globally.

Configuration

This plugin exposes four recommended configs.

Name Description
basic Only use basic validation rules common to both iOS & Android
ios Use all rules from "basic", plus iOS-specific extras
android Use all rules from "basic", plus Android-specific extras
all Use all rules from "basic", plus iOS-specific extras, plus Android-specific extras

If your project only supports a single platform, you may get the best experience using a platform-specific config. This will both avoid reporting issues which do not affect your platform and also results in slightly faster linting for larger projects.

If you are unsure which one to use, in most cases all can be safely used.

Add the config you want to use to the extends section of your ESLint config using the pattern plugin:react-native-a11y/ followed by your config name, as shown below:

// .eslintrc.js

module.exports = {
  root: true,
  extends: [
    '@react-native-community',
    'plugin:react-native-a11y/ios'
  ]
};

Alternatively if you do not want to use one of the pre-defined configs — or want to override the behaviour of a specific rule — you can always include a list rules and configurations in the rules section of your ESLint config.

// .eslintrc.js

module.exports = {
  root: true,
  extends: [
    '@react-native-community',
  ],
  rules: {
    'react-native-a11y/rule-name': 2
  }
};

For more information on configuring behaviour of an individual rule, please refer to the ESLint docs

Supported Rules

Basic

iOS

Android

Rule Options

The following options are available to customize the recommended rule set.

Custom Touchables

react-native-a11y/has-accessibility-props and react-native-a11y/no-nested-touchables allow you to define an array of names for custom components that you may have that conform to the same accessibility interfaces as Touchables.

"react-native-a11y/has-accessibility-props": [
  "error",
  {
    "touchables": ["TouchableCustom"]
  }
]

Custom Invertable Components (iOS)

react-native-a11y/has-valid-accessibility-ignores-invert-colors allows you to optionally define an Array of component names to check in addition to <Image />.

For more information, see the rule docs.

"react-native-a11y/has-valid-accessibility-ignores-invert-colors": [
  "error",
  {
    "invertableComponents": [
      "FastImage",
    ]
  }
]

Creating a new rule

If you are developing new rules for this project, you can use the create-rule script to scaffold the new files.

$ ./scripts/create-rule.js my-new-rule

Attribution

This project started as a fork of eslint-plugin-jsx-a11y and a lot of the work was carried out by its contributors, to whom we owe a lot!

License

eslint-plugin-react-native-a11y is licensed under the MIT License.

Maintenance Status

Active: Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.

eslint-plugin-react-native-a11y's People

Contributors

alex-saunders avatar aryanj-nyc avatar boygirl avatar cpresler avatar draperunner avatar dylmye avatar fawcilize avatar gabts avatar gksander avatar grgr-dkrk avatar imranolas avatar jevakallio avatar jpdriver avatar knitcodemonkey avatar mattvot avatar philihp avatar virtualdominic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eslint-plugin-react-native-a11y's Issues

Support ESLint 8

Hi 👋🏻

Could you check if this plugin still works on ESLint 8 and, if so, update the peer dependencies to support it? Many thanks 🙏🏻

I tested it out on ESLint 8 and in my project, there were no issues.

not get any warning

I installed the library according to the instructions
But I do not get any warning about TouchableOpacity

What should I do?

eslintrc.js

module.exports = {
  root: true,
  extends: ['@react-native-community', 'plugin:react-native-a11y/ios'],
};

package.json

{
  "name": "reduxTs",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
  },
  "dependencies": {
    "@reduxjs/toolkit": "^1.5.0",
    "react": "16.13.1",
    "react-native": "0.63.4",
    "react-native-fingerprint-scanner": "^6.0.0",
    "react-native-touch-id": "^4.4.1",
    "react-redux": "^7.2.2",
    "redux-thunk": "^2.3.0"
  },
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/runtime": "^7.8.4",
    "@react-native-community/eslint-config": "^1.1.0",
    "@types/jest": "^25.2.3",
    "@types/react-native": "^0.63.2",
    "@types/react-redux": "^7.1.16",
    "@types/react-test-renderer": "^16.9.2",
    "babel-jest": "^25.1.0",
    "babel-plugin-module-resolver": "^4.1.0",
    "eslint": "^7.20.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-config-prettier": "^7.2.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-react": "^7.22.0",
    "eslint-plugin-react-native-a11y": "^2.0.4",
    "jest": "^25.1.0",
    "metro-react-native-babel-preset": "^0.59.0",
    "prettier": "^2.2.1",
    "react-test-renderer": "16.13.1",
    "typesafe-actions": "^5.1.0",
    "typescript": "^3.8.3"
  },
  "jest": {
    "preset": "react-native",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ]
  }
}

Error message for `-descriptors` rule is out of sync with accessibility-hint rule setting

In short - the has-valid-accessibility-descriptors's rule's error message is static text that doesn't take into account the config's settings on whether accessibilityHint is actually required with accessibilityLabel.


We have eslint-plugin-react-native-a11y set up with has-accessibility-hint rule disabled on a library of components intended to be shared between web and native projects, because accessibilityHint isn't supported in React Native Web, so mandating its use risks misleading devs into thinking an element has full contextual info when actually the hint part is unavailable on Web. Instead, we advise devs to use complete labels that contain full contextual info across platforms.

We recently updated to 3.x and were surprised to see an error seemingly complaining about missing hints with labels:

Missing a11y props. Expected one of: accessibilityRole OR BOTH accessibilityLabel + accessibilityHint OR BOTH accessibilityActions + onAccessibilityAction

...however, it turns out that the rule was correctly following our settings: if the element in question was given an accessibilityLabel without an accessibilityHint, there was no error as per our disabling of the accessibilityHint rule, but if the accessibilityLabel prop was removed, then the error misleadingly implied an accessibilityHint was also needed.

So the behaviour is fine, but the error message didn't reflect the behaviour of the rule.

Proposed fix

Replace the BOTH accessibilityLabel + accessibilityHint part of the error message here

'Missing a11y props. Expected one of: accessibilityRole OR BOTH accessibilityLabel + accessibilityHint OR BOTH accessibilityActions + onAccessibilityAction';
with just accessibilityLabel if the appropriate accessiblityHint rule is not active.

Prop value validators should allow expressions as possible valid values

Currently prop validators assume that the prop value is a string literal, and reports an error if it doesn't match one of the preset values.

Instead, the rule should allow expressions and variables and assume the value is valid. E.g.

<Text accessibilityLiveRegion={variable}>Click Me</Text>
<Text accessibilityLiveRegion={variable ? 'polite' : 'assertive'}>Click Me</Text>
<Text accessibilityLiveRegion={getLiveRegionValue()}>Click Me</Text>
<Text accessibilityLiveRegion={this.props.liveRegion}>Click Me</Text>

In these cases the linter cannot easily know what the value is, and attempting that is out of scope for ESLint. React PropTypes and the built-in React Native Flowtypes can provide more safety to the end user who wants to do dynamic accessibility props.

Affected rules:

Error with any custom components aliased as Image

When importing the component under an alias of Image or a named export { Image } I get the following error. Looks like the Eslint rule is looking for any component named Image and applying this rule. Is it possible to get the eslint rule to only apply to Images imported directly from react-native?

Found an element which will be inverted. Add the accessibilityIgnoresInvertColors propeslint(react-native-a11y/has-valid-accessibility-ignores-invert-colors)

Code:
Image.tsx

import React from 'react'
import { Image as RNImage, ImageProps } from 'react-native'

const Image = (props: ImageProps) => (
  <RNImage accessibilityIgnoresInvertColors source={props.source} />
)

export default Image

And example area you would have an error
App.tsx

import Image from 'components/Image'
const App = () => <Image source={FILE} /> // this will error saying you are not providing accessibilityIgnoresInvertColors for the image.

Add a script to automatically add rules to the README

There is currently an executable script (courtesy of eslint-plugin-jsx-a11y) to set up the boilerplate for a new linting rule (adding the rule src file, tests etc.) - using the same information given to this script we can add the new rule and it's description to the README automatically.

Obviously not a critical issue as adding new rules to the README is easy enough - automating it would just avoid any forgetfulness.

Deprecate accessibility-label

Currently the accessibility-label rule is defined as:

Enforce that views that have accessible={true}, also have an accessibilityLabel prop

While generally good practice, there may be times when other props supply descriptive information instead of accessibilityLabel (e.g. accessibilityValue)

The plugin should not enforce the presence of accessibilityLabel in every scenario

Feature: Rule to require accessibilityRole prop

In our project we often get bugs that the screenreader is not reading the type of pressed element correctly or not at all. This is typically fixed by adding accessibilityRole prop. I was wondering if it would be possible to require accessibilityRole prop for react-native components like text and button? Everything has accessible={true} prop anyway, so might as well.

Is this something that might be added in the future?

Getting a warning even when passing AccessibilityState as an object

const accessibilityState: AccessibilityState =
      disabled !== props.accessibilityState?.disabled
        ? {...props.accessibilityState, disabled, busy: loading}
        : {...props.accessibilityState, busy: loading}

This is how we are computing the accessibility state in a component. We see a warning triggered by react-native-a11y/has-valid-accessibility-state saying the passed value is not an object in the attached screenshot.

Screen Shot 2021-07-01 at 16 35 11

When we change the prop value to an empty object {} the warning seems to go away. Still, changing the accessibilityState variable value to an empty object, we see the warning again.

const accessibilityState = {}

The expected behavior is that we shouldn't see this warring as the value of accessibilityState is an object in both snippets.

Clear out unnecessary deps/files

Package.json still contains a number of unnecessary dependencies (relics from eslint-plugin-jsx-a11y) and files that are no longer used - we should locate everything that is not being used within the project and remove it.

Add an option to provide custom names to rules that rely on Touchable* component naming

Splitting off from #6, as a further enhancement we could add an array of additional names as an option to rules that rely on specific components names. That way if people have their own custom components that conform to the same accessibility interfaces as e.g. Touchables.

Not sure if this is something people would use, so classifying as least-priority enhancement.

Deprecate has-accessibility-props

Currently we enforce that all Touchable*s must have either accessibilityRole or both accessibilityTraits and accessibilityComponentType.

It is actually valid to have a Touchable without either in certain scenarios; so this rule should be changed to enforce that you are using the props correctly only in cases where you have included them.

has-valid-accessibility-role lacks several valid accessibility roles

Actual

has-valid-accessibility-role only allows accessibilityRole to be one of:

  • adjustable
  • button
  • header
  • image
  • imagebutton
  • keyboardkey
  • link
  • none
  • search
  • summary

when React Native supports 27 accessibilityRoles: https://facebook.github.io/react-native/docs/accessibility#accessibilityrole-ios-android

Expected

has-valid-accessibility-role allows all 27 accessibilityRoles found on https://facebook.github.io/react-native/docs/accessibility#accessibilityrole-ios-android

By the way, thanks for this package!

Oh! And I'll be more than happy to help out with this pull request once this is confirmed to be a bug and not a feature.

Add `yarn.lock` to source to make builds deterministic.

  • We presently do a yarn install --frozen-lockfile which is great to make sure no one makes package changes without updating the lock file.
  • The lockfile gives us build determinism to avoid awkward unrelated build failures like in: ub.com//pull/44

Task:

  • Create and add yarn.lock to git source.

Consider merging into eslint-plugin-jsx-a11y

Hi folks! Just ran into this and I think it's awesome; thanks for all the work you've done here. I filed https://github.com/evcohen/eslint-plugin-jsx-a11y/issues/287 a while ago to do exactly this. I'm here to ask if you'd consider merging this plugin into one tool.

Why?
At Twitter, we use react-native-web. For us eslint-plugin-jsx-a11y provides little to no support, but sometimes it's useful when we need to fallback to DOM elements for one reason or another. IMO having two packages is a nice separation of concerns, but adds a level of indirection for users who need to rely on both. For one, I didn't see this until at least 10 months later 🤷‍♂️ . I know that at Twitter, we could definitely benefit from using both whether it's one plugin or two.

Proposal
Add these rules to eslint-plugin-jsx-a11y under a native namespace OR add a global setting to eslint-plugin-jsx-a11y to specify platform (web, native, both). I'd prefer namespacing since that allows users to opt-in to any rule that suits their project.

How
Add these rules in the next upcoming minor release with a suggested native config.

I'm suggesting moving to jsx-a11y for two reasons:

  1. Pre-existing support and use.
  2. The name is not restrictive of platform, just syntax. (In hindsight, I wish I had just named it eslint-plugin-accessibility 😄 )

Thoughts?

cc/ @jessebeach @ljharb @necolas @comp615

has-valid-accessibility-state rule doesn't accept functions calls and object properties

Hi,

I started using this package and when running it I had some instances where the has-valid-accessibility-state rule gave an error. When looking into some of them, I realized that there's a small issue when you set an accessibilityState property with a function call. For example:

const isFirst : () => boolean = () => { 
    return something === "example";
}

<TouchableOpacity
    accessible
    accessibilityState={{ disabled: isFirst() }}
    disabled={isFirst()}
>
    ...
</TouchableOpacity>

In this example, the has-valid-accessibility-state rule will throw the following error, even though the isFirst function returns a boolean: accessibilityState object: "disabled" value is not a boolean.

Also, I found an instance we I set one of the accessibilityState properties with an object property and got an error. For example:

type MyType = {
    myBool: boolean;
};

const myObj: MyType = {
    myBool: true
};

<TouchableOpacity
    accessible
    accessibilityState={{ checked: myObj.myBool }}
>
    ...
</TouchableOpacity>

The object property is defined as a boolean, but the has-valid-accessibility-state throws the following error:
accessibilityState object: "checked" value is not either a boolean or 'mixed'.

Would it be possible to update the rule so it accepts those situations as well?

accessibilityState doesn't recognise boolean value in variable

Following code throws an error: "accessibilityState object: "selected" value is not a boolean"

interface Props {
  active: boolean;
}

const C: React.SFC<Props> = ({
  active,
}) => (
  <TouchableHighlight
    accessibilityState={{
      selected: active,
    }}
  >
  ...
);

If I replace active with true/false, it passes.

[Feature Request] Extended accessibilityRole values? (RNW)

Hello!

I am using this library on a cross-platform react native / react native web project. In the process of creating a "List" component -- wanting to apply the roles "list" and "listitem" -- I realized they aren't included in the currently supported RN roles.

I extended the missing TS types, but realized I was still getting flagged by this glorious lint plugin. So the thought occurred to me that maybe it'd be useful to augment the has-valid-accessibility-role rule to accept custom values -- similar to how the custom touchables list works in has-accessibility-props

I appreciate that this is a murky area with respect to the intended usage of this library, but I thought I'd check.

Thanks!

Add links to rule definitions in rule error messages

Consider error message like:

const errorMessage = 'accessibilityComponentType must be one of defined values'

If I get this error, I won't have any idea what the defined values are.

However, if the error message were something like:

const errorMessage = `accessibilityComponentType value is not valid. 

See valid values: https://github.com/FormidableLabs/eslint-plugin-react-native-a11y/blob/master/docs/rules/has-valid-accessibility-component-type.md`

Then it would be easier to check what the valid values are.

Unable to resolve dependency tree

I tried to install the package as described on npm
npm install eslint-plugin-react-native-a11y --save-dev
and got stucked with this error

oie_koHFcyykeLBX

I'm using Expo-CLI to setup this project
Other Eslint libraries are working so far

oie_uFw1bNLdgCkk

accessibilityIgnoresInvertColors behaves the opposite way this plugin expects it to

From the React Native documentation:

Inverting screen colors is an Accessibility feature that makes the iPhone and iPad easier on the eyes for some people with a sensitivity to brightness, easier to distinguish for some people with color blindness, and easier to make out for some people with low vision. However, sometimes you have views such as photos that you don't want to be inverted. In this case, you can set this property to be false so that these specific views won't have their colors inverted.

But from this plugin's documentation:

true: colors of everything in this view will not be inverted when color inversion is enabled
false: the default value (unless the view is nested inside a view with accessibilityIgnoresInvertColors={true}). Colors in everything contained in this view may be inverted

Which is actually the reverse of how the prop works. The prop is false by default, so adding the prop as enforced by this rule would actually cause <Image> to be inverted.

I was curious as to whether this was a result of a breaking change in how the prop works across react-native versions, but I went back as far as v0.40, and the behaviour is the same. So it should be safe to update the plugin.

has-valid-accessibility-actions rule doesn't accept variables

Hi,

I was fixing some lint errors and encountered this situation:

const onAccessibilityAction = (event: { nativeEvent: { actionName: any } }) => {
    switch (event.nativeEvent.actionName) {
        case "delete":
            deleteAction();
            break;
        default:
            Alert.alert("Some text);
    }
}

const accessibilityActionsList: Array<{ name: string, label: string }> = [{ name: "delete", label: "Delete" }];

<TouchableOpacity
    accessibilityActions={accessibilityActionsList}
    onAccessibilityAction={onAccessibilityAction}
>
    ...
</TouchableOpacity>

Here, the has-valid-accessibility-actions rule throws the following errors: accessibilityActions: value must be an Array and accessibilityActions: has accessibilityActions but onAccessibilityAction is not a function. However, both of my variables have the correct types. I tried putting the array and the function as is in the JSX (without putting them in a variable) and it worked fine, but I prefer having a simpler/cleaner JSX.

Do you think this is something that can be fixed?

Deprecate accessibilityStates in favour of accessibilityState?

From the latest docs (somewhat confusingly on the View component API page since version 0.60 and the Accessibility guide since version 0.59), accessibilityState replaces accessibilityStates. It is now an object with several options instead of an array of two possible strings.

It's not quite clear whether accessibilityStates will still work though.

Either way, has-valid-accessibility-state rule, docs and tests need to be updated to handle this.

why is the accessibilityHint enforced as mandatory?

From your documentation

"An accessibility hint helps users understand what will happen when they perform an action on the accessibility element when that result is not apparent from the accessibility label."

Therefore, if the result is apparent from a label, and the hint is not added, the linter shouldn't flag it as an error. But hint is flagged as an error each time the label is added to an element.

An example:

<Image accessibilityLabel='Facebook logo' accessibilityRole="image" />

will give me an error - error has accessibilityLabel prop but no accessibilityHint react-native-a11y/has-accessibility-hint
But there is no other information I would want to convey for a logo... so no hint is needed.

I guess this can be improved by not including images?

Update docs

README (+ other smaller files) need to be updated to be inline with this project

Inline functions/values are not recognized as valid accessibility actions

We are seeing errors we don't believe to be accurate in some of our components. When using inline functions and values the linter throws errors. Here is a contrived illustration of the issue.

import React, { useMemo, useCallback } from 'react';

export default function ActionsError() {
  return (
    <Container
      accessibilityActions={useMemo(() => {
        return [
          {
            name: 'name',
            label: 'label'
          }
        ];
      }, [false])}
      onAccessibilityAction={useCallback(
        event => {
          const actions = {
            activate: () => console.log('hi')
          };

          return actions[event.nativeEvent.actionName]() || NOOP;
        },
        [NOOP]
      )}
      accessible
      accessibilityRole="button"
      accessibilityLabel={text}
      style={style}
    />
  );
}
  5:5  error  accessibilityActions: has accessibilityActions but onAccessibilityAction is not a function  react-native-a11y/has-valid-accessibility-actions
  5:5  error  accessibilityActions: value must be an Array                                                react-native-a11y/has-valid-accessibility-actions

Support react-native-platform-touchable in accessible-touchable

The react-native-platform-touchable component is a commonly used interface for platform Touchable* primitives.

The suggested usage from their docs is:

import Touchable from 'react-native-platform-touchable';

The tricky bit is that since this is a default import, people can call the variable whatever they want, e.g. "PlatformTouchable".

Three alternative solutions to this:

  • Strict: Covers components called just "Touchable". Relies on convention.
  • Loose: Cover any component that includes the string Touchable in their name. Relies on semantics.
  • Options: Add an array of additional names as an option to the style. Future-proof, as people can then also add completely different names, e.g. "Smooshable" (or whatever). Relies on people reading the docs, and is slightly more work intensive.

I'm leaning on the combination first and last option: Include "Touchable" by default, and add an extensibility options.

In either case, this is a low-priority enhancement.

@knitcodemonkey thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.