416 eslint a11y (#420)

* Add eslint-plugin-react-native-a11y dependency

* Do not fix warnings on eslint run

* Change all a11y error rules to warnings for now

* Add a11y hint to Tabs

* Add a11y prop to UserIcon

* Update strings.ftl

* Update strings

* Update README.md

* Update a11y props for InlineUser

* Update a11y label

* Add explanation for strings
This commit is contained in:
Johannes Klein
2023-02-01 14:12:15 +01:00
committed by GitHub
parent 1b4039eb36
commit 7bceb1214b
10 changed files with 116 additions and 39 deletions

View File

@@ -10,7 +10,8 @@ module.exports = {
extends: [
"airbnb",
"plugin:i18next/recommended",
"plugin:@tanstack/eslint-plugin-query/recommended"
"plugin:@tanstack/eslint-plugin-query/recommended",
"plugin:react-native-a11y/ios"
],
plugins: [
"module-resolver",
@@ -28,12 +29,15 @@ module.exports = {
"consistent-return": [2, { treatUndefinedAsUnspecified: true }],
"func-names": 0,
"global-require": 0,
"i18next/no-literal-string": [2, {
words: {
// Minor change to the default to disallow all-caps string literals as well
exclude: ["[0-9!-/:-@[-`{-~]+"]
"i18next/no-literal-string": [
2,
{
words: {
// Minor change to the default to disallow all-caps string literals as well
exclude: ["[0-9!-/:-@[-`{-~]+"]
}
}
}],
],
// The AirBNB approach at
// https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/rules/imports.js#L71
// is quite particular and forbids imports of devDependencies anywhere
@@ -45,22 +49,30 @@ module.exports = {
// raise alarms when you try to import things not declared in
// package.json.
"import/no-extraneous-dependencies": ["error", {}],
"max-len": ["error", 100, 2, {
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: false,
ignoreTemplateLiterals: false
}],
"max-len": [
"error",
100,
2,
{
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: false,
ignoreTemplateLiterals: false
}
],
"no-alert": 0,
"no-underscore-dangle": 0,
"no-unused-vars": ["error", {
vars: "all",
args: "after-used",
// Overriding airbnb to allow leading underscore to indicate unused var
argsIgnorePattern: "^_",
ignoreRestSiblings: true
}],
"no-unused-vars": [
"error",
{
vars: "all",
args: "after-used",
// Overriding airbnb to allow leading underscore to indicate unused var
argsIgnorePattern: "^_",
ignoreRestSiblings: true
}
],
"no-void": 0,
"prefer-destructuring": [2, { object: true, array: false }],
quotes: [2, "double"],
@@ -76,7 +88,10 @@ module.exports = {
"react/prop-types": 0,
"react/destructuring-assignment": 0,
"react/jsx-filename-extension": 0,
"react/function-component-definition": [2, { namedComponents: "arrow-function" }],
"react/function-component-definition": [
2,
{ namedComponents: "arrow-function" }
],
"react/require-default-props": 0,
// React-Hooks Plugin
@@ -87,7 +102,21 @@ module.exports = {
"react-native/no-inline-styles": "error",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
"simple-import-sort/exports": "error",
"react-native-a11y/has-accessibility-hint": 1,
"react-native-a11y/has-accessibility-props": 1,
"react-native-a11y/has-valid-accessibility-actions": 1,
"react-native-a11y/has-valid-accessibility-role": 1,
"react-native-a11y/has-valid-accessibility-state": 1,
"react-native-a11y/has-valid-accessibility-states": 1,
"react-native-a11y/has-valid-accessibility-component-type": 1,
"react-native-a11y/has-valid-accessibility-traits": 1,
"react-native-a11y/has-valid-accessibility-value": 1,
"react-native-a11y/no-nested-touchables": 1,
"react-native-a11y/has-valid-accessibility-descriptors": 1,
"react-native-a11y/has-valid-accessibility-ignores-invert-colors": 1,
"react-native-a11y/has-valid-accessibility-live-region": 1,
"react-native-a11y/has-valid-important-for-accessibility": 1
},
// need this so jest doesn't show as undefined in jest.setup.js
env: {

View File

@@ -127,7 +127,7 @@ We're using Nativewind, a styling system for React Native based on Tailwind CSS.
1. Download custom icon from Figma as an SVG file.
2. Add new icon to the iNaturalist icon set in Fontastic. Select all relevant iNaturalist icons, tap the Publish tab, and download the zip of icons.
3. Create a glpyh file from the CSS file you just downloaded, using the following command (be sure to replace /path/to/styles with your path):
3. Create a glyph file from the CSS file you just downloaded, using the following command (be sure to replace /path/to/styles with your path):
```
./node_modules/.bin/generate-icon '/path/to/styles.css' --componentName=INatIcon --fontFamily=inaturalisticons > 'src/components/INatIcon.js'

29
package-lock.json generated
View File

@@ -110,6 +110,7 @@
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-native": "^4.0.0",
"eslint-plugin-react-native-a11y": "^3.3.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-testing-library": "^5.10.0",
"factoria": "^3.2.2",
@@ -9235,6 +9236,23 @@
"eslint": "^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
"node_modules/eslint-plugin-react-native-a11y": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-native-a11y/-/eslint-plugin-react-native-a11y-3.3.0.tgz",
"integrity": "sha512-21bIs/0yROcMq7KtAG+OVNDWAh8M+6scII0iXcO3i9NYHe2xZ443yPs5KSUMSvQJeRLLjuKB7V5saqNjoMWDHA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.15.4",
"ast-types-flow": "^0.0.7",
"jsx-ast-utils": "^3.2.1"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
"node_modules/eslint-plugin-react-native-globals": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz",
@@ -29175,6 +29193,17 @@
"eslint-plugin-react-native-globals": "^0.1.1"
}
},
"eslint-plugin-react-native-a11y": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-native-a11y/-/eslint-plugin-react-native-a11y-3.3.0.tgz",
"integrity": "sha512-21bIs/0yROcMq7KtAG+OVNDWAh8M+6scII0iXcO3i9NYHe2xZ443yPs5KSUMSvQJeRLLjuKB7V5saqNjoMWDHA==",
"dev": true,
"requires": {
"@babel/runtime": "^7.15.4",
"ast-types-flow": "^0.0.7",
"jsx-ast-utils": "^3.2.1"
}
},
"eslint-plugin-react-native-globals": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz",

View File

@@ -9,7 +9,7 @@
"clean-start": "npx react-native clean-project-auto && npx pod-install && npm start",
"test": "jest",
"lint": "npm run lint:eslint && npm run lint:flow",
"lint:eslint": "eslint . --fix",
"lint:eslint": "eslint . --fix --quiet",
"lint:flow": "flow check",
"postinstall": "husky install",
"translate": "node src/i18n/i18ncli.js build",
@@ -121,6 +121,7 @@
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-native": "^4.0.0",
"eslint-plugin-react-native-a11y": "^3.3.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-testing-library": "^5.10.0",
"factoria": "^3.2.2",

View File

@@ -53,15 +53,13 @@ const InlineUser = ( { user }: Props ): Node => {
testID="InlineUser"
className="flex flex-row items-center"
accessibilityRole="link"
accessibilityLabel={t( "Navigate-to-user-profile" )}
accessibilityValue={{ text: userHandle }}
accessibilityLabel={t( "User", { userHandle } )}
accessibilityHint={t( "Navigates-to-user-profile" )}
onPress={() => {
navigation.navigate( "UserProfile", { userId: user.id } );
}}
>
<View className="mr-[7px]">
{renderUserIcon()}
</View>
<View className="mr-[7px]">{renderUserIcon()}</View>
<Text>{userHandle}</Text>
</Pressable>
);

View File

@@ -1,6 +1,7 @@
// @flow
import { Text, View } from "components/styledComponents";
import { t } from "i18next";
import type { Node } from "react";
import React from "react";
import { TouchableOpacity } from "react-native";
@@ -39,8 +40,9 @@ const Tabs = ( { tabs = DEFAULT_TABS, activeId }: Props ): Node => (
}
}}
testID={testID || `${id}-tab`}
accessibilityLabel={text}
accessibilityRole="tab"
accessibilityLabel={text}
accessibilityHint={t( "Switch-to-tab", { tab: text } )}
accessibilityState={{
selected: active,
expanded: active

View File

@@ -17,6 +17,7 @@ const UserIcon = ( { uri, small }: Props ): React.Node => {
testID="UserIcon.photo"
className={className}
source={uri}
accessibilityIgnoresInvertColors
/>
);
};

View File

@@ -687,7 +687,15 @@ No = No
Discard-Comment = Discard Comment
Are-you-sure-discard-comment = Are you sure you want to discard this comment?
## Accessibility labels: these are used by screen readers to describe actionable elements
## Accessibility labels: these are used by screen readers to label actionable elements iOS: https://developer.apple.com/documentation/uikit/uiaccessibilityelement/1619577-accessibilitylabel
## iOS Guidelines "A string that succinctly identifies the accessibility element." Starts with capital letter, no ending punctuation.
User = User { $userHandle }
## Accessibility hints: these are used by screen readers to describe what happens when the user interacts with an element iOS: https://developer.apple.com/documentation/uikit/uiaccessibilityelement/1619585-accessibilityhint
## iOS Guidelines "A string that briefly describes the result of performing an action on the accessibility element." Third person singular ending with a period.
Navigates-to-user-profile = Navigates to user profile.
## The following are actually more like "accessibility hints" than labels we should probably refactor
Add-this-ID = Add this identification
# Accessible label for the camera button
Camera-button-label-switch-camera = Use the device's other camera.
@@ -712,12 +720,12 @@ Navigate-to-login-screen = Navigate to login screen
Navigate-to-observation-details = Navigate to observation details screen
Navigate-to-project-details = Navigate to project details
Navigate-to-taxon-details = Navigate to taxon details
Navigate-to-user-profile = Navigate to user profile
Number-of-comments = Number of comments
Number-of-identifications = Number of identifications
Observation-has-no-photos-and-no-sounds = This observation has no photos and no sounds.
Take-photo = Take photo
Photo-taken-at = Photo taken at { $date }
Switch-to-tab = Switch to { $tab } tab
Take-photo = Take photo
# Accessibility labels for no internet state in ObsDetails
Location-map-unavailable-without-internet = Location map unavailable without internet
Observation-photos-unavailable-without-internet = Observation photos unavailable without internet

View File

@@ -390,7 +390,7 @@
"comment": "Shows the number of observations a user is currently uploading on my observations page",
"val": "Uploading { $count ->\n [one] 1 Observation\n *[other] { $count } Observations\n}"
},
"User": "User",
"User": "User { $userHandle }",
"Username": "Username",
"Username-or-Email": {
"comment": "Appears above the text fields",
@@ -490,6 +490,7 @@
},
"Discard-Comment": "Discard Comment",
"Are-you-sure-discard-comment": "Are you sure you want to discard this comment?",
"Navigates-to-user-profile": "Navigates to user profile.",
"Add-this-ID": "Add this identification",
"Camera-button-label-switch-camera": {
"comment": "Accessible label for the camera button",
@@ -524,12 +525,12 @@
"Navigate-to-observation-details": "Navigate to observation details screen",
"Navigate-to-project-details": "Navigate to project details",
"Navigate-to-taxon-details": "Navigate to taxon details",
"Navigate-to-user-profile": "Navigate to user profile",
"Number-of-comments": "Number of comments",
"Number-of-identifications": "Number of identifications",
"Observation-has-no-photos-and-no-sounds": "This observation has no photos and no sounds.",
"Take-photo": "Take photo",
"Photo-taken-at": "Photo taken at { $date }",
"Switch-to-tab": "Switch to { $tab } tab",
"Take-photo": "Take photo",
"Location-map-unavailable-without-internet": {
"comment": "Accessibility labels for no internet state in ObsDetails",
"val": "Location map unavailable without internet"

View File

@@ -687,7 +687,15 @@ No = No
Discard-Comment = Discard Comment
Are-you-sure-discard-comment = Are you sure you want to discard this comment?
## Accessibility labels: these are used by screen readers to describe actionable elements
## Accessibility labels: these are used by screen readers to label actionable elements iOS: https://developer.apple.com/documentation/uikit/uiaccessibilityelement/1619577-accessibilitylabel
## iOS Guidelines "A string that succinctly identifies the accessibility element." Starts with capital letter, no ending punctuation.
User = User { $userHandle }
## Accessibility hints: these are used by screen readers to describe what happens when the user interacts with an element iOS: https://developer.apple.com/documentation/uikit/uiaccessibilityelement/1619585-accessibilityhint
## iOS Guidelines "A string that briefly describes the result of performing an action on the accessibility element." Third person singular ending with a period.
Navigates-to-user-profile = Navigates to user profile.
## The following are actually more like "accessibility hints" than labels we should probably refactor
Add-this-ID = Add this identification
# Accessible label for the camera button
Camera-button-label-switch-camera = Use the device's other camera.
@@ -712,12 +720,12 @@ Navigate-to-login-screen = Navigate to login screen
Navigate-to-observation-details = Navigate to observation details screen
Navigate-to-project-details = Navigate to project details
Navigate-to-taxon-details = Navigate to taxon details
Navigate-to-user-profile = Navigate to user profile
Number-of-comments = Number of comments
Number-of-identifications = Number of identifications
Observation-has-no-photos-and-no-sounds = This observation has no photos and no sounds.
Take-photo = Take photo
Photo-taken-at = Photo taken at { $date }
Switch-to-tab = Switch to { $tab } tab
Take-photo = Take photo
# Accessibility labels for no internet state in ObsDetails
Location-map-unavailable-without-internet = Location map unavailable without internet
Observation-photos-unavailable-without-internet = Observation photos unavailable without internet