Improved accessibility (#118):

* channel configuration: adding title to inputs.
* channel configuration: `aria-hidden="true"` on icons for add and
  remove row buttons.
This commit is contained in:
John Livingston 2024-08-29 12:20:36 +02:00
parent 944bdcebb7
commit b673a49af6
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
5 changed files with 38 additions and 8 deletions

View File

@ -135,6 +135,7 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
</livechat-configuration-section-header>
<div class="form-group">
<textarea
.title=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_TERMS_LABEL) as any}
name="terms"
id="peertube-livechat-terms"
.value=${el.channelConfiguration?.configuration.terms ?? ''}

View File

@ -6,6 +6,7 @@
// This content comes from the file assets/images/plus-square.svg, from the Feather icons set https://feathericons.com/
export const AddSVG: string =
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
aria-hidden="true"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="feather feather-plus-square">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
@ -15,6 +16,7 @@ export const AddSVG: string =
// This content comes from the file assets/images/x-square.svg, from the Feather icons set https://feathericons.com/
export const RemoveSVG: string =
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
aria-hidden="true"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="feather feather-x-square">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>

View File

@ -47,11 +47,11 @@ interface CellDataSchema {
minlength?: number
maxlength?: number
size?: number
label?: TemplateResult | string
options?: { [key: string]: string }
datalist?: DynamicTableAcceptedTypes[]
separator?: string
inputType?: DynamicTableAcceptedInputTypes
inputTitle?: string
default?: DynamicTableAcceptedTypes
colClassList?: string[] // CSS classes to add to the <td> element.
}
@ -295,6 +295,7 @@ export class DynamicTableFormElement extends LivechatElement {
const inputId =
`peertube-livechat-${this.formName.replace(/_/g, '-')}-${propertyName.toString().replace(/_/g, '-')}-${rowId}`
const inputTitle: DirectiveResult | undefined = propertySchema.inputTitle ?? this.header[propertyName]?.colName
const feedback = this._renderFeedback(inputId, propertyName, originalIndex)
switch (propertySchema.default?.constructor) {
@ -320,6 +321,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderInput(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue as string,
@ -332,6 +334,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderTextarea(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue as string,
@ -344,6 +347,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderSelect(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue as string,
@ -356,6 +360,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderImageFileInput(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue?.toString(),
@ -376,6 +381,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderInput(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
(propertyValue as Date).toISOString(),
@ -394,6 +400,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderInput(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue as string,
@ -411,6 +418,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderCheckbox(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue as boolean,
@ -446,6 +454,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderInput(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
(propertyValue)?.join(propertySchema.separator ?? ',') ??
@ -461,6 +470,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderTextarea(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
(propertyValue)?.join(propertySchema.separator ?? ',') ??
@ -476,6 +486,7 @@ export class DynamicTableFormElement extends LivechatElement {
formElement = html`${this._renderTagsInput(rowId,
inputId,
inputName,
inputTitle,
propertyName,
propertySchema,
propertyValue,
@ -501,6 +512,7 @@ export class DynamicTableFormElement extends LivechatElement {
_renderInput = (rowId: number,
inputId: string,
inputName: string,
inputTitle: string | DirectiveResult | undefined,
propertyName: string,
propertySchema: CellDataSchema,
propertyValue: string,
@ -515,6 +527,7 @@ export class DynamicTableFormElement extends LivechatElement {
)
)}
id=${inputId}
title=${ifDefined(inputTitle)}
aria-describedby="${inputId}-feedback"
list=${ifDefined(propertySchema.datalist ? inputId + '-datalist' : undefined)}
min=${ifDefined(propertySchema.min)}
@ -534,6 +547,7 @@ export class DynamicTableFormElement extends LivechatElement {
_renderTagsInput = (rowId: number,
inputId: string,
inputName: string,
inputTitle: string | DirectiveResult | undefined,
propertyName: string,
propertySchema: CellDataSchema,
propertyValue: Array<string | number>,
@ -547,7 +561,7 @@ export class DynamicTableFormElement extends LivechatElement {
)
)}
id=${inputId}
.inputPlaceholder=${propertySchema.label as any}
.inputTitle=${inputTitle as any}
aria-describedby="${inputId}-feedback"
.min=${propertySchema.min}
.max=${propertySchema.max}
@ -563,6 +577,7 @@ export class DynamicTableFormElement extends LivechatElement {
_renderTextarea = (rowId: number,
inputId: string,
inputName: string,
inputTitle: string | DirectiveResult | undefined,
propertyName: string,
propertySchema: CellDataSchema,
propertyValue: string,
@ -576,6 +591,7 @@ export class DynamicTableFormElement extends LivechatElement {
)
)}
id=${inputId}
title=${ifDefined(inputTitle)}
aria-describedby="${inputId}-feedback"
min=${ifDefined(propertySchema.min)}
max=${ifDefined(propertySchema.max)}
@ -588,6 +604,7 @@ export class DynamicTableFormElement extends LivechatElement {
_renderCheckbox = (rowId: number,
inputId: string,
inputName: string,
inputTitle: string | DirectiveResult | undefined,
propertyName: string,
propertySchema: CellDataSchema,
propertyValue: boolean,
@ -602,6 +619,7 @@ export class DynamicTableFormElement extends LivechatElement {
)
)}
id=${inputId}
title=${ifDefined(inputTitle)}
aria-describedby="${inputId}-feedback"
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
value="1"
@ -611,6 +629,7 @@ export class DynamicTableFormElement extends LivechatElement {
_renderSelect = (rowId: number,
inputId: string,
inputName: string,
inputTitle: string | DirectiveResult | undefined,
propertyName: string,
propertySchema: CellDataSchema,
propertyValue: string,
@ -623,11 +642,12 @@ export class DynamicTableFormElement extends LivechatElement {
)
)}
id=${inputId}
title=${ifDefined(inputTitle)}
aria-describedby="${inputId}-feedback"
aria-label=${inputName}
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
>
<option ?selected=${!propertyValue}>${propertySchema.label ?? 'Choose your option'}</option>
<option ?selected=${!propertyValue}>${inputTitle ?? ''}</option>
${Object.entries(propertySchema.options ?? {})
?.map(([value, name]) =>
html`<option ?selected=${propertyValue === value} value=${value}>${name}</option>`
@ -638,6 +658,7 @@ export class DynamicTableFormElement extends LivechatElement {
_renderImageFileInput = (rowId: number,
inputId: string,
inputName: string,
inputTitle: string | DirectiveResult | undefined,
propertyName: string,
propertySchema: CellDataSchema,
propertyValue: string,
@ -647,6 +668,7 @@ export class DynamicTableFormElement extends LivechatElement {
.name=${inputName}
class=${classMap(this._getInputValidationClass(propertyName, originalIndex))}
id=${inputId}
.inputTitle=${inputTitle as any}
aria-describedby="${inputId}-feedback"
@change=${(event: Event) => this._updatePropertyFromValue(event, propertyName, propertySchema, rowId)}
.value=${propertyValue}

View File

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
//
// SPDX-License-Identifier: AGPL-3.0-only
import { LivechatElement } from './livechat'
import { html } from 'lit'
import type { DirectiveResult } from 'lit/directive'
import { customElement, property } from 'lit/decorators.js'
import { ifDefined } from 'lit/directives/if-defined.js'
/**
* Special element to upload image files.
* If no current value, displays an input type="file" field.
@ -29,13 +29,16 @@ export class ImageFileInputElement extends LivechatElement {
@property({ attribute: false })
public maxSize?: number
@property({ attribute: false })
public inputTitle?: string | DirectiveResult
@property({ attribute: false })
public accept: string[] = ['image/jpg', 'image/png', 'image/gif']
protected override render = (): unknown => {
return html`
${this.value
? html`<img src=${this.value} @click=${(ev: Event) => {
? html`<img src=${this.value} alt=${ifDefined(this.inputTitle)} @click=${(ev: Event) => {
ev.preventDefault()
const upload: HTMLInputElement | null | undefined = this.parentElement?.querySelector('input[type="file"]')
upload?.click()
@ -44,6 +47,7 @@ export class ImageFileInputElement extends LivechatElement {
}
<input
type="file"
title=${ifDefined(this.inputTitle)}
accept="${this.accept.join(',')}"
class="form-control"
style=${this.value ? 'display: none;' : ''}

View File

@ -12,6 +12,7 @@ import { ifDefined } from 'lit/directives/if-defined.js'
import { classMap } from 'lit/directives/class-map.js'
import { animate, fadeOut, fadeIn } from '@lit-labs/motion'
import { repeat } from 'lit/directives/repeat.js'
import type { DirectiveResult } from 'lit/directive'
// FIXME: find a better way to store this image.
// This content comes from the file assets/images/copy.svg, after svgo cleaning.
@ -48,7 +49,7 @@ export class TagsInputElement extends LivechatElement {
private _inputValue?: string = ''
@property({ attribute: false })
public inputPlaceholder?: string = ''
public inputTitle?: string | DirectiveResult = ''
@property({ attribute: false })
public datalist?: string[]
@ -166,7 +167,7 @@ export class TagsInputElement extends LivechatElement {
@input=${(e: InputEvent) => this._handleInputEvent(e)}
@change=${(e: Event) => e.stopPropagation()}
.value=${this._inputValue ?? ''}
placeholder=${ifDefined(this.inputPlaceholder)} />
title=${ifDefined(this.inputTitle)} />
${(this.datalist)
? html`<datalist id="${this.id ?? 'tags-input'}-datalist">
${(this.datalist ?? []).map((value) => html`<option value=${value}>`)}