Compare commits

...

1 Commits

Author SHA1 Message Date
tusooa 129e4a2034
Make poll accessible 2023-11-14 18:06:32 -05:00
4 changed files with 27 additions and 31 deletions

View File

@ -0,0 +1 @@
Make poll accessible

View File

@ -540,7 +540,9 @@ textarea,
} }
&[type="radio"] { &[type="radio"] {
&:not(.visible-for-screenreader-only) {
display: none; display: none;
}
&:checked + label::before { &:checked + label::before {
box-shadow: 0 0 2px black inset, 0 0 0 4px $fallback--fg inset; box-shadow: 0 0 2px black inset, 0 0 0 4px $fallback--fg inset;
@ -597,6 +599,7 @@ textarea,
flex-shrink: 0; flex-shrink: 0;
display: inline-block; display: inline-block;
content: ""; content: "";
content: "" / "";
transition: color 200ms; transition: color 200ms;
width: 1.1em; width: 1.1em;
height: 1.1em; height: 1.1em;

View File

@ -1,7 +1,6 @@
import Timeago from 'components/timeago/timeago.vue' import Timeago from 'components/timeago/timeago.vue'
import genRandomSeed from '../../services/random_seed/random_seed.service.js' import genRandomSeed from '../../services/random_seed/random_seed.service.js'
import RichContent from 'components/rich_content/rich_content.jsx' import RichContent from 'components/rich_content/rich_content.jsx'
import { forEach, map } from 'lodash'
export default { export default {
name: 'Poll', name: 'Poll',
@ -81,25 +80,17 @@ export default {
this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id }) this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id })
}, },
activateOption (index) { activateOption (index) {
// forgive me father: doing checking the radio/checkboxes
// in code because of customized input elements need either
// a) an extra element for the actual graphic, or b) use a
// pseudo element for the label. We use b) which mandates
// using "for" and "id" matching which isn't nice when the
// same poll appears multiple times on the site (notifs and
// timeline for example). With code we can make sure it just
// works without altering the pseudo element implementation.
const allElements = this.$el.querySelectorAll('input')
const clickedElement = this.$el.querySelector(`input[value="${index}"]`)
if (this.poll.multiple) { if (this.poll.multiple) {
// Checkboxes, toggle only the clicked one // Multiple choice: toggle the current index
clickedElement.checked = !clickedElement.checked const nextChoices = [...this.choices]
nextChoices[index] = !nextChoices[index]
this.choices = nextChoices
} else { } else {
// Radio button, uncheck everything and check the clicked one // Radio button, uncheck everything and check the clicked one
forEach(allElements, element => { element.checked = false }) const nextChoices = new Array(this.choices.length).fill(false)
clickedElement.checked = true nextChoices[index] = true
this.choices = nextChoices
} }
this.choices = map(allElements, e => e.checked)
}, },
optionId (index) { optionId (index) {
return `poll${this.poll.id}-${index}` return `poll${this.poll.id}-${index}`

View File

@ -31,36 +31,41 @@
:style="{ 'width': `${percentageForOption(option.votes_count)}%` }" :style="{ 'width': `${percentageForOption(option.votes_count)}%` }"
/> />
</div> </div>
<div <template
v-else v-else
tabindex="0"
:role="poll.multiple ? 'checkbox' : 'radio'"
:aria-labelledby="`option-vote-${randomSeed}-${index}`"
:aria-checked="choices[index]"
@click="activateOption(index)"
> >
<input <input
v-if="poll.multiple" v-if="poll.multiple"
:id="`option-vote-input-${randomSeed}-${index}`"
type="checkbox" type="checkbox"
class="poll-checkbox" tabindex="0"
class="visible-for-screenreader-only"
:disabled="loading" :disabled="loading"
:value="index" :value="index"
:checked="choices[index]"
@change="activateOption(index)"
> >
<input <input
v-else v-else
:id="`option-vote-input-${randomSeed}-${index}`"
type="radio" type="radio"
class="visible-for-screenreader-only"
:disabled="loading" :disabled="loading"
:value="index" :value="index"
:checked="choices[index]"
@change="activateOption(index)"
>
<label
class="option-vote"
:for="`option-vote-input-${randomSeed}-${index}`"
> >
<label class="option-vote">
<RichContent <RichContent
:id="`option-vote-${randomSeed}-${index}`"
:html="option.title_html" :html="option.title_html"
:handle-links="false" :handle-links="false"
:emoji="emoji" :emoji="emoji"
/> />
</label> </label>
</div> </template>
</div> </div>
</div> </div>
<div class="footer faint"> <div class="footer faint">
@ -171,9 +176,5 @@
padding: 0 0.5em; padding: 0 0.5em;
margin-right: 0.5em; margin-right: 0.5em;
} }
.poll-checkbox {
display: none;
}
} }
</style> </style>