Make poll accessible

This commit is contained in:
tusooa 2023-11-14 17:40:48 -05:00
parent 18c0cf1845
commit 129e4a2034
No known key found for this signature in database
GPG Key ID: 42AEC43D48433C51
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"] {
&:not(.visible-for-screenreader-only) {
display: none;
}
&:checked + label::before {
box-shadow: 0 0 2px black inset, 0 0 0 4px $fallback--fg inset;
@ -597,6 +599,7 @@ textarea,
flex-shrink: 0;
display: inline-block;
content: "";
content: "" / "";
transition: color 200ms;
width: 1.1em;
height: 1.1em;

View File

@ -1,7 +1,6 @@
import Timeago from 'components/timeago/timeago.vue'
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
import RichContent from 'components/rich_content/rich_content.jsx'
import { forEach, map } from 'lodash'
export default {
name: 'Poll',
@ -81,25 +80,17 @@ export default {
this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id })
},
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) {
// Checkboxes, toggle only the clicked one
clickedElement.checked = !clickedElement.checked
// Multiple choice: toggle the current index
const nextChoices = [...this.choices]
nextChoices[index] = !nextChoices[index]
this.choices = nextChoices
} else {
// Radio button, uncheck everything and check the clicked one
forEach(allElements, element => { element.checked = false })
clickedElement.checked = true
const nextChoices = new Array(this.choices.length).fill(false)
nextChoices[index] = true
this.choices = nextChoices
}
this.choices = map(allElements, e => e.checked)
},
optionId (index) {
return `poll${this.poll.id}-${index}`

View File

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