From b08e351da0b928a1c5a4f2a662a0f9f4669a6afd Mon Sep 17 00:00:00 2001 From: Mihail Novikov Date: Fri, 6 Jul 2018 19:13:24 +0300 Subject: [PATCH] Add a11y linter (#130) --- web/.eslintrc.js | 12 +++- web/app/common/accessibility.js | 13 ++++ web/app/components/auth-panel/auth-panel.jsx | 17 +++-- .../blocked-users/blocked-users.jsx | 9 ++- web/app/components/comment/comment.jsx | 43 ++++++------- web/app/components/input/input.jsx | 2 +- web/app/components/user-info/user-info.jsx | 3 +- web/package-lock.json | 64 +++++++++++++++++++ web/package.json | 1 + 9 files changed, 127 insertions(+), 37 deletions(-) create mode 100644 web/app/common/accessibility.js diff --git a/web/.eslintrc.js b/web/.eslintrc.js index 112d9f98..5d7f5a4c 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -1,7 +1,15 @@ module.exports = { parser: 'babel-eslint', - extends: ['eslint:recommended', 'plugin:prettier/recommended'], - plugins: ['react', 'prettier'], + extends: [ + 'eslint:recommended', + 'plugin:jsx-a11y/recommended', + 'plugin:prettier/recommended', + ], + plugins: [ + 'react', + 'jsx-a11y', + 'prettier', + ], env: { browser: true, node: true, diff --git a/web/app/common/accessibility.js b/web/app/common/accessibility.js new file mode 100644 index 00000000..c4bf4ab2 --- /dev/null +++ b/web/app/common/accessibility.js @@ -0,0 +1,13 @@ +const handleBtnKeyPress = (event, handler) => { + if (event.key === ' ' || event.key === 'Enter') { + event.preventDefault(); + handler && handler(); + } +}; + +export const getHandleClickProps = handler => ({ + role: 'button', + tabIndex: 0, + onClick: handler, + onKeyPress: event => handleBtnKeyPress(event, handler), +}); diff --git a/web/app/components/auth-panel/auth-panel.jsx b/web/app/components/auth-panel/auth-panel.jsx index 3755040e..2a1e3d52 100644 --- a/web/app/components/auth-panel/auth-panel.jsx +++ b/web/app/components/auth-panel/auth-panel.jsx @@ -2,6 +2,7 @@ import { h, Component } from 'preact'; import { PROVIDER_NAMES } from 'common/constants'; +import { getHandleClickProps } from 'common/accessibility'; export default class AuthPanel extends Component { constructor(props) { @@ -41,11 +42,11 @@ export default class AuthPanel extends Component { {loggedIn && (
You signed in as{' '} - + {user.name} {isUserIdVisible && ({user.id})}.{' '} - + Sign out?
@@ -62,9 +63,8 @@ export default class AuthPanel extends Component { {comma} props.onSignIn(provider))} role="link" - tabIndex="0" - onClick={() => props.onSignIn(provider)} > {PROVIDER_NAMES[provider]} @@ -79,9 +79,8 @@ export default class AuthPanel extends Component { {user.admin && ( {isBlockedVisible ? 'Hide' : 'Show'} blocked @@ -91,16 +90,16 @@ export default class AuthPanel extends Component { Sort by{' '} - + diff --git a/web/app/components/blocked-users/blocked-users.jsx b/web/app/components/blocked-users/blocked-users.jsx index 82a87606..3bbeb02f 100644 --- a/web/app/components/blocked-users/blocked-users.jsx +++ b/web/app/components/blocked-users/blocked-users.jsx @@ -2,6 +2,7 @@ import { h, Component } from 'preact'; import api from 'common/api'; +import { getHandleClickProps } from 'common/accessibility'; export default class BlockedUsers extends Component { constructor(props) { @@ -51,12 +52,16 @@ export default class BlockedUsers extends Component { {user.name}{' '} ({user.id}) {isUserUnblocked && ( - this.block(user)}> + this.block(user))} + className="blocked-users__action"> block )} {!isUserUnblocked && ( - this.unblock(user)}> + this.unblock(user))} + className="blocked-users__action"> unblock )} diff --git a/web/app/components/comment/comment.jsx b/web/app/components/comment/comment.jsx index cc48f897..e56589ea 100644 --- a/web/app/components/comment/comment.jsx +++ b/web/app/components/comment/comment.jsx @@ -2,6 +2,7 @@ import { h, Component } from 'preact'; import api from 'common/api'; +import { getHandleClickProps } from 'common/accessibility'; import { API_BASE, BASE_URL, COMMENT_NODE_CLASSNAME_PREFIX } from 'common/constants'; import { url } from 'common/settings'; import store from 'common/store'; @@ -416,7 +417,11 @@ export default class Comment extends Component { )} {mods.view !== 'user' && ( - + {o.user.name} )} @@ -424,7 +429,7 @@ export default class Comment extends Component { {isAdmin && mods.view !== 'user' && ( + > + {' '} + )} {isAdmin && userBlocked && mods.view !== 'user' && Blocked} @@ -459,9 +466,8 @@ export default class Comment extends Component { {!mods.disabled && mods.view !== 'user' && ( {mods.collapsed ? '+' : '−'} @@ -474,10 +480,8 @@ export default class Comment extends Component { {}, { type: 'up', selected: scoreIncreased, disabled: isGuest || isCurrentUser } )} - role="button" aria-disabled={isGuest || isCurrentUser} - tabIndex="0" - onClick={isGuest || isCurrentUser ? null : this.increaseScore} + {...getHandleClickProps(isGuest || isCurrentUser ? null : this.increaseScore)} title={ isGuest ? 'Only authorized users are allowed to vote' @@ -495,15 +499,13 @@ export default class Comment extends Component { + {isReplying ? 'Cancel' : 'Reply'} )} @@ -536,10 +538,8 @@ export default class Comment extends Component { (!!editTimeLeft || isEditing) && mods.view !== 'user' && ( {isEditing ? 'Cancel' : 'Edit'} {editTimeLeft && ` (${editTimeLeft})`} @@ -550,31 +550,31 @@ export default class Comment extends Component { isAdmin && ( {!pinned && ( - + Pin )} {pinned && ( - + Unpin )} {userBlocked && ( - + Unblock )} {!userBlocked && ( - + Block )} {!deleted && ( - + Delete )} @@ -587,7 +587,7 @@ export default class Comment extends Component { {isReplying && mods.view !== 'user' && ( - + )} {isEditing && @@ -600,7 +600,6 @@ export default class Comment extends Component { id={o.id} value={o.orig} errorMessage={!editTimeLeft && 'Editing time has expired.'} - autoFocus /> )} diff --git a/web/app/components/input/input.jsx b/web/app/components/input/input.jsx index 69452004..b8e28069 100644 --- a/web/app/components/input/input.jsx +++ b/web/app/components/input/input.jsx @@ -134,7 +134,7 @@ export default class Input extends Component { const { mods = {}, value = null, errorMessage } = props; return ( -
+