about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2019-12-03 19:53:16 +0100
committerEugen Rochko <eugen@zeonfederated.com>2019-12-03 19:53:16 +0100
commitc05ed8a6254bc82fda3ae0fd3934dc2cdcf7c82d (patch)
tree3907d5b225a240aa43611a3d0cd16471d907bc5b
parentf1ef777d40235dc922a0b56da9fc00b98827c189 (diff)
Fix poll options not being selectable via keyboard (#12538)
* Fix poll options not being selectable via keyboard

Fixes #12384

* Improve styling of poll option checkboxes/radio buttons

* Use more appropriate ARIA roles for poll options

* Allow switching between single and multiple choice from keyboard

* Coding style

* Avoid using .bind()
-rw-r--r--app/javascript/mastodon/components/poll.js28
-rw-r--r--app/javascript/mastodon/features/compose/components/poll_form.js11
-rw-r--r--app/javascript/styles/mastodon/polls.scss17
3 files changed, 52 insertions, 4 deletions
diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js
index 0edd064e0..3a17e80e7 100644
--- a/app/javascript/mastodon/components/poll.js
+++ b/app/javascript/mastodon/components/poll.js
@@ -67,9 +67,7 @@ class Poll extends ImmutablePureComponent {
     }
   }
 
-  handleOptionChange = e => {
-    const { target: { value } } = e;
-
+  _toggleOption = value => {
     if (this.props.poll.get('multiple')) {
       const tmp = { ...this.state.selected };
       if (tmp[value]) {
@@ -83,8 +81,20 @@ class Poll extends ImmutablePureComponent {
       tmp[value] = true;
       this.setState({ selected: tmp });
     }
+  }
+
+  handleOptionChange = ({ target: { value } }) => {
+    this._toggleOption(value);
   };
 
+  handleOptionKeyPress = (e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      this._toggleOption(e.target.getAttribute('data-index'));
+      e.stopPropagation();
+      e.preventDefault();
+    }
+  }
+
   handleVote = () => {
     if (this.props.disabled) {
       return;
@@ -135,7 +145,17 @@ class Poll extends ImmutablePureComponent {
             disabled={disabled}
           />
 
-          {!showResults && <span className={classNames('poll__input', { checkbox: poll.get('multiple'), active })} />}
+          {!showResults && (
+            <span
+              className={classNames('poll__input', { checkbox: poll.get('multiple'), active })}
+              tabIndex='0'
+              role={poll.get('multiple') ? 'checkbox' : 'radio'}
+              onKeyPress={this.handleOptionKeyPress}
+              aria-checked={active}
+              aria-label={option.get('title')}
+              data-index={optionIndex}
+            />
+          )}
           {showResults && <span className='poll__number'>
             {!!voted && <Icon id='check' className='poll__vote__mark' title={intl.formatMessage(messages.voted)} />}
             {Math.round(percent)}%
diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js
index f4cd71a76..81ab0ec1a 100644
--- a/app/javascript/mastodon/features/compose/components/poll_form.js
+++ b/app/javascript/mastodon/features/compose/components/poll_form.js
@@ -13,6 +13,8 @@ const messages = defineMessages({
   add_option: { id: 'compose_form.poll.add_option', defaultMessage: 'Add a choice' },
   remove_option: { id: 'compose_form.poll.remove_option', defaultMessage: 'Remove this choice' },
   poll_duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll duration' },
+  switchToMultiple: { id: 'compose_form.poll.switch_to_multiple', defaultMessage: 'Change poll to allow multiple choices' },
+  switchToSingle: { id: 'compose_form.poll.switch_to_single', defaultMessage: 'Change poll to allow for a single choice' },
   minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
   hours: { id: 'intervals.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}}' },
   days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
@@ -50,6 +52,12 @@ class Option extends React.PureComponent {
     e.stopPropagation();
   };
 
+  handleCheckboxKeypress = e => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      this.handleToggleMultiple(e);
+    }
+  }
+
   onSuggestionsClearRequested = () => {
     this.props.onClearSuggestions();
   }
@@ -71,8 +79,11 @@ class Option extends React.PureComponent {
           <span
             className={classNames('poll__input', { checkbox: isPollMultiple })}
             onClick={this.handleToggleMultiple}
+            onKeyPress={this.handleCheckboxKeypress}
             role='button'
             tabIndex='0'
+            title={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
+            aria-label={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
           />
 
           <AutosuggestInput
diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss
index f59a9d693..833f77009 100644
--- a/app/javascript/styles/mastodon/polls.scss
+++ b/app/javascript/styles/mastodon/polls.scss
@@ -91,6 +91,23 @@
       border-color: $valid-value-color;
       background: $valid-value-color;
     }
+
+    &:active,
+    &:focus,
+    &:hover {
+      border-width: 4px;
+      background: none;
+    }
+
+    &::-moz-focus-inner {
+      outline: 0 !important;
+      border: 0;
+    }
+
+    &:focus,
+    &:active {
+      outline: 0 !important;
+    }
   }
 
   &__number {