Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(QRating): add a11y for icons #13941 #14477

Merged
merged 1 commit into from
Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions ui/dev/src/pages/form/rating.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
</div>

<div class="column q-gutter-md" style="font-size: 2rem; margin-top: 20px;">
<q-rating v-model="ratingModel" max="3" @change="onChange" @input="onInput" />
<q-rating v-model="ratingModel" max="3" :icon-aria-label="['Bad', 'Normal', 'Good']" @change="onChange" @input="onInput" />
<q-rating v-model="ratingModel" color="primary" max="5" icon="pets" @input="onInput" />
<q-rating color="teal" v-model="ratingModel" max="9" icon="thumb_up" />
<q-rating color="teal" v-model="ratingModel" max="9" icon="thumb_up" icon-aria-label="Thumbs" />
<q-rating size="3rem" color="red" v-model="ratingModel" :max="6" icon="favorite_border" />
<q-rating size="3rem" color="red" v-model="ratingModel" :max="6" icon="img:https://cdn.quasar.dev/logo/svg/quasar-logo.svg" />
<q-rating size="3rem" color="red" v-model="ratingModel" :max="6" icon="star_border" icon-selected="star" />
Expand Down
57 changes: 44 additions & 13 deletions ui/src/components/rating/QRating.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export default Vue.extend({
iconHalf: [String, Array],
iconSelected: [String, Array],

iconAriaLabel: [String, Array],

color: [String, Array],
colorHalf: [String, Array],
colorSelected: [String, Array],
Expand Down Expand Up @@ -85,11 +87,30 @@ export default Vue.extend({
}
},

iconLabel () {
if (typeof this.iconAriaLabel === 'string') {
const label = this.iconAriaLabel.length > 0 ? `${this.iconAriaLabel} ` : ''

return i => `${label}${i}`
}

if (Array.isArray(this.iconAriaLabel) === true) {
const iMax = this.iconAriaLabel.length

if (iMax > 0) {
return i => this.iconAriaLabel[Math.min(i, iMax) - 1]
}
}

return (i, label) => `${label} ${i}`
},

stars () {
const
acc = [],
icons = this.iconData,
ceil = Math.ceil(this.value)
ceil = Math.ceil(this.value),
tabindex = this.editable === true ? 0 : null

const halfIndex = this.iconHalf === void 0 || ceil === this.value
? -1
Expand All @@ -106,18 +127,26 @@ export default Vue.extend({
icons.selColor !== void 0 && active === true
? (i <= icons.selColorLen ? this.colorSelected[i - 1] : icons.selColor)
: (i <= icons.colorLen ? this.color[i - 1] : icons.color)
)

acc.push({
name: (
),
name = (
half === true
? (i <= icons.halfIconLen ? this.iconHalf[i - 1] : icons.halfIcon)
: (
icons.selIcon !== void 0 && (active === true || exSelected === true)
? (i <= icons.selIconLen ? this.iconSelected[i - 1] : icons.selIcon)
: (i <= icons.iconLen ? this.icon[i - 1] : icons.icon)
)
) || this.$q.iconSet.rating.icon,
) || this.$q.iconSet.rating.icon

acc.push({
name,

attrs: {
tabindex,
role: 'radio',
'aria-checked': this.value === i ? 'true' : 'false',
'aria-label': this.iconLabel(i, name)
},

classes: 'q-rating__icon' +
(active === true || half === true ? ' q-rating__icon--active' : '') +
Expand All @@ -131,12 +160,16 @@ export default Vue.extend({
},

attrs () {
const attrs = {
role: 'radiogroup'
}
if (this.disable === true) {
return { 'aria-disabled': 'true' }
attrs[ 'aria-disabled' ] = 'true'
}
if (this.readonly === true) {
return { 'aria-readonly': 'true' }
attrs[ 'aria-readonly' ] = 'true'
}
return attrs
}
},

Expand Down Expand Up @@ -181,19 +214,17 @@ export default Vue.extend({
},

render (h) {
const
child = [],
tabindex = this.editable === true ? 0 : null
const child = []

this.stars.forEach(({ classes, name }, index) => {
this.stars.forEach(({ classes, name, attrs }, index) => {
const i = index + 1

child.push(
h('div', {
key: i,
ref: `rt${i}`,
class: 'q-rating__icon-container flex flex-center',
attrs: { tabindex },
attrs,
on: cache(this, 'i#' + i, {
click: () => { this.__set(i) },
mouseover: () => { this.__setHoverValue(i) },
Expand Down
11 changes: 11 additions & 0 deletions ui/src/components/rating/QRating.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@
"addedIn": "v1.7.4"
},

"icon-aria-label": {
"type": [ "String", "Array" ],
"desc": "Label to be set on aria-label for Icon; If an array is provided each rating value will use the corresponding aria-label in the array (0 based); If string value is provided the rating value will be appended; If not provided the name of the icon will be used",
"examples": [
"Rating",
"[\"Bad\", \"Normal\", \"Good\"]"
],
"category": "content",
"addedIn": "v1.20.3"
},

"color": {
"extends": "color",
"type": [ "String", "Array" ],
Expand Down