From f4045ba3d962105ae4a7c0ee785a83c678ca2f8a Mon Sep 17 00:00:00 2001 From: Zac Anger Date: Sat, 15 Apr 2017 05:27:27 -0600 Subject: [PATCH] Add eslint-plugin-jsx-a11y (#1651) * Add eslint-plugin-jsx-a11y. * Fix npm script. * Adjust npm scripts so test also runs lint. * Fix existing lint errors. * Don't break on a11y issues. * Add role and tabIndex. * Add vim and Mac files to .gitignore and .dockerignore. * Handle htmlFor (partially), a that's actually a button. * Fix missing tabIndex. * Add cursor:pointer to load-more * Revert change to load_more. * Fixes based on review. * Update yarn.lock. * Don't try to install fsevents on Linux (hides warning noise). --- .dockerignore | 3 ++ .eslintrc | 34 ++++++++++++++-- .gitignore | 8 +++- Dockerfile | 2 +- .../components/autosuggest_textarea.jsx | 7 +++- .../components/components/button.jsx | 1 + .../components/column_back_button.jsx | 4 +- .../components/column_back_button_slim.jsx | 2 +- .../components/column_collapsable.jsx | 4 +- .../components/components/load_more.jsx | 2 +- .../components/components/media_gallery.jsx | 2 +- .../components/components/permalink.jsx | 3 +- .../components/components/status_content.jsx | 2 +- .../components/components/video_player.jsx | 8 ++-- .../features/account/components/header.jsx | 11 +++++- .../compose/components/privacy_dropdown.jsx | 2 +- .../features/compose/components/search.jsx | 8 +++- .../components/clear_column_button.jsx | 2 +- .../components/column_settings.jsx | 4 +- .../notifications/components/notification.jsx | 2 +- .../components/setting_toggle.jsx | 7 ++-- .../features/ui/components/column_header.jsx | 2 +- .../features/ui/components/column_link.jsx | 3 +- .../features/ui/components/media_modal.jsx | 4 +- .../features/ui/components/modal_root.jsx | 2 +- .../javascripts/components/link_header.jsx | 2 +- .../javascripts/components/locales/es.jsx | 2 +- .../javascripts/components/locales/ru.jsx | 2 +- .../components/middleware/errors.jsx | 2 +- .../components/reducers/alerts.jsx | 24 ++++++------ package.json | 12 ++++-- yarn.lock | 39 ++++++++++++++++--- 32 files changed, 155 insertions(+), 57 deletions(-) diff --git a/.dockerignore b/.dockerignore index 21d1f59a1..920581ac9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,6 @@ node_modules storybook neo4j vendor/bundle +.DS_Store +*.swp +*~ diff --git a/.eslintrc b/.eslintrc index f91385cec..a3640b265 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,7 +8,8 @@ "parser": "babel-eslint", "plugins": [ - "react" + "react", + "jsx-a11y" ], "parserOptions": { @@ -43,9 +44,36 @@ "no-mixed-spaces-and-tabs": 1, "no-nested-ternary": 1, "no-trailing-spaces": 1, - "react/wrap-multilines": 2, + + "react/jsx-wrap-multilines": 2, "react/self-closing-comp": 2, "react/prop-types": 2, - "react/no-multi-comp": 0 + "react/no-multi-comp": 0, + + "jsx-a11y/accessible-emoji": 1, + "jsx-a11y/anchor-has-content": 1, + "jsx-a11y/aria-activedescendant-has-tabindex": 1, + "jsx-a11y/aria-props": 1, + "jsx-a11y/aria-proptypes": 1, + "jsx-a11y/aria-role": 1, + "jsx-a11y/aria-unsupported-elements": 1, + "jsx-a11y/heading-has-content": 1, + "jsx-a11y/href-no-hash": 1, + "jsx-a11y/html-has-lang": 1, + "jsx-a11y/iframe-has-title": 1, + "jsx-a11y/img-has-alt": 1, + "jsx-a11y/img-redundant-alt": 1, + "jsx-a11y/label-has-for": 1, + "jsx-a11y/mouse-events-have-key-events": 1, + "jsx-a11y/no-access-key": 1, + "jsx-a11y/no-distracting-elements": 1, + "jsx-a11y/no-onchange": 1, + "jsx-a11y/no-redundant-roles": 1, + "jsx-a11y/onclick-has-focus": 1, + "jsx-a11y/onclick-has-role": 1, + "jsx-a11y/role-has-required-aria-props": 1, + "jsx-a11y/role-supports-aria-props": 1, + "jsx-a11y/scope": 1, + "jsx-a11y/tabindex-no-positive": 1 } } diff --git a/.gitignore b/.gitignore index 5c95f7806..cda6b87b3 100644 --- a/.gitignore +++ b/.gitignore @@ -29,10 +29,16 @@ neo4j/ # Ignore Capistrano customizations config/deploy/* - # Ignore IDE files .vscode/ # Ignore postgres + redis volume optionally created by docker-compose postgres redis + +# Ignore Apple files +.DS_Store + +# Ignore vim files +*~ +*.swp diff --git a/Dockerfile b/Dockerfile index a05525b33..dc1ebb06d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ RUN BUILD_DEPS=" \ imagemagick \ && npm install -g npm@3 && npm install -g yarn \ && bundle install --deployment --without test development \ - && yarn \ + && yarn --ignore-optional \ && yarn cache clean \ && npm -g cache clean \ && apk del $BUILD_DEPS \ diff --git a/app/assets/javascripts/components/components/autosuggest_textarea.jsx b/app/assets/javascripts/components/components/autosuggest_textarea.jsx index 744424661..04328aae8 100644 --- a/app/assets/javascripts/components/components/autosuggest_textarea.jsx +++ b/app/assets/javascripts/components/components/autosuggest_textarea.jsx @@ -178,7 +178,12 @@ const AutosuggestTextarea = React.createClass({
0 && !suggestionsHidden) ? 'block' : 'none' }} className='autosuggest-textarea__suggestions'> {suggestions.map((suggestion, i) => ( -
+
))} diff --git a/app/assets/javascripts/components/components/button.jsx b/app/assets/javascripts/components/components/button.jsx index babc6b259..c3e184024 100644 --- a/app/assets/javascripts/components/components/button.jsx +++ b/app/assets/javascripts/components/components/button.jsx @@ -9,6 +9,7 @@ const Button = React.createClass({ block: React.PropTypes.bool, secondary: React.PropTypes.bool, size: React.PropTypes.number, + style: React.PropTypes.object, children: React.PropTypes.node }, diff --git a/app/assets/javascripts/components/components/column_back_button.jsx b/app/assets/javascripts/components/components/column_back_button.jsx index 6b5ffee53..91c3b92be 100644 --- a/app/assets/javascripts/components/components/column_back_button.jsx +++ b/app/assets/javascripts/components/components/column_back_button.jsx @@ -15,13 +15,13 @@ const ColumnBackButton = React.createClass({ mixins: [PureRenderMixin], handleClick () { - if (window.history && window.history.length == 1) this.context.router.push("/"); + if (window.history && window.history.length === 1) this.context.router.push("/"); else this.context.router.goBack(); }, render () { return ( -
+
diff --git a/app/assets/javascripts/components/components/column_back_button_slim.jsx b/app/assets/javascripts/components/components/column_back_button_slim.jsx index 780e3b182..536964b01 100644 --- a/app/assets/javascripts/components/components/column_back_button_slim.jsx +++ b/app/assets/javascripts/components/components/column_back_button_slim.jsx @@ -31,7 +31,7 @@ const ColumnBackButtonSlim = React.createClass({ render () { return (
-
+
diff --git a/app/assets/javascripts/components/components/column_collapsable.jsx b/app/assets/javascripts/components/components/column_collapsable.jsx index 85ce63835..75dfc8a4e 100644 --- a/app/assets/javascripts/components/components/column_collapsable.jsx +++ b/app/assets/javascripts/components/components/column_collapsable.jsx @@ -46,7 +46,9 @@ const ColumnCollapsable = React.createClass({ return (
-
+
+ +
{({ opacity, height }) => diff --git a/app/assets/javascripts/components/components/load_more.jsx b/app/assets/javascripts/components/components/load_more.jsx index 2cb9b09a8..b4bc8b711 100644 --- a/app/assets/javascripts/components/components/load_more.jsx +++ b/app/assets/javascripts/components/components/load_more.jsx @@ -1,7 +1,7 @@ import { FormattedMessage } from 'react-intl'; const LoadMore = ({ onClick }) => ( - + ); diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/assets/javascripts/components/components/media_gallery.jsx index 10b7d525b..325fd8157 100644 --- a/app/assets/javascripts/components/components/media_gallery.jsx +++ b/app/assets/javascripts/components/components/media_gallery.jsx @@ -220,7 +220,7 @@ const MediaGallery = React.createClass({ } children = ( -
+
{warning}
diff --git a/app/assets/javascripts/components/components/permalink.jsx b/app/assets/javascripts/components/components/permalink.jsx index ae2fb0d29..ebfa63cbc 100644 --- a/app/assets/javascripts/components/components/permalink.jsx +++ b/app/assets/javascripts/components/components/permalink.jsx @@ -6,7 +6,8 @@ const Permalink = React.createClass({ propTypes: { href: React.PropTypes.string.isRequired, - to: React.PropTypes.string.isRequired + to: React.PropTypes.string.isRequired, + children: React.PropTypes.node.isRequired }, handleClick (e) { diff --git a/app/assets/javascripts/components/components/status_content.jsx b/app/assets/javascripts/components/components/status_content.jsx index 33e407e43..ce8ead41e 100644 --- a/app/assets/javascripts/components/components/status_content.jsx +++ b/app/assets/javascripts/components/components/status_content.jsx @@ -119,7 +119,7 @@ const StatusContent = React.createClass({ return (
{mentionsPlaceholder} diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx index dce276c75..e9e860e39 100644 --- a/app/assets/javascripts/components/components/video_player.jsx +++ b/app/assets/javascripts/components/components/video_player.jsx @@ -194,7 +194,7 @@ const VideoPlayer = React.createClass({ if (!this.state.visible) { if (sensitive) { return ( -
+
{spoilerButton} @@ -202,7 +202,7 @@ const VideoPlayer = React.createClass({ ); } else { return ( -
+
{spoilerButton} @@ -213,7 +213,7 @@ const VideoPlayer = React.createClass({ if (this.state.preview && !autoplay) { return ( -
+
{spoilerButton}
@@ -225,7 +225,7 @@ const VideoPlayer = React.createClass({ {spoilerButton} {muteButton} {expandButton} -
); } diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx index a359963c4..c4619a3c7 100644 --- a/app/assets/javascripts/components/features/account/components/header.jsx +++ b/app/assets/javascripts/components/features/account/components/header.jsx @@ -43,7 +43,16 @@ const Avatar = React.createClass({ return ( {({ radius }) => - + {account.get('acct')} } diff --git a/app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx b/app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx index e54fa4d28..de8942d4d 100644 --- a/app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx +++ b/app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx @@ -83,7 +83,7 @@ const PrivacyDropdown = React.createClass({
{options.map(item => -
+
{item.shortText} diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/assets/javascripts/components/features/compose/components/search.jsx index 936e003f2..9ca1f5dc5 100644 --- a/app/assets/javascripts/components/features/compose/components/search.jsx +++ b/app/assets/javascripts/components/features/compose/components/search.jsx @@ -36,6 +36,10 @@ const Search = React.createClass({ } }, + noop () { + + }, + handleFocus () { this.props.onShow(); }, @@ -56,9 +60,9 @@ const Search = React.createClass({ onFocus={this.handleFocus} /> -
+
- +
); diff --git a/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx b/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx index debbfd01f..63fe86af6 100644 --- a/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx +++ b/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx @@ -15,7 +15,7 @@ const ClearColumnButton = React.createClass({ const { intl } = this.props; return ( -
+
); diff --git a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx b/app/assets/javascripts/components/features/notifications/components/column_settings.jsx index 2edf98292..03bfaa653 100644 --- a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx +++ b/app/assets/javascripts/components/features/notifications/components/column_settings.jsx @@ -27,9 +27,11 @@ const ColumnSettings = React.createClass({ propTypes: { settings: ImmutablePropTypes.map.isRequired, - intl: React.PropTypes.object.isRequired, onChange: React.PropTypes.func.isRequired, onSave: React.PropTypes.func.isRequired, + intl: React.PropTypes.shape({ + formatMessage: React.PropTypes.func.isRequired + }).isRequired }, mixins: [PureRenderMixin], diff --git a/app/assets/javascripts/components/features/notifications/components/notification.jsx b/app/assets/javascripts/components/features/notifications/components/notification.jsx index fdebe4bb5..2a9f2d076 100644 --- a/app/assets/javascripts/components/features/notifications/components/notification.jsx +++ b/app/assets/javascripts/components/features/notifications/components/notification.jsx @@ -71,7 +71,7 @@ const Notification = React.createClass({ ); }, - render () { + render () { // eslint-disable-line consistent-return const { notification } = this.props; const account = notification.get('account'); const displayName = account.get('display_name').length > 0 ? account.get('display_name') : account.get('username'); diff --git a/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx b/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx index eae3c2be2..c4bfad5cd 100644 --- a/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx +++ b/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx @@ -14,8 +14,8 @@ const labelSpanStyle = { marginLeft: '8px' }; -const SettingToggle = ({ settings, settingKey, label, onChange }) => ( -