Skip to content

Commit cd864d0

Browse files
♿ a11y(accordion): accordion not accessible (#1542)
* Create PR for #1419 * fix(accordion): improve a11y * chore: adjust export types --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gery Hirschfeld <[email protected]>
1 parent f939d0e commit cd864d0

File tree

27 files changed

+319
-34
lines changed

27 files changed

+319
-34
lines changed

.changeset/quiet-maps-divide.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@baloise/ds-core': patch
3+
---
4+
5+
**list**: connect accordion head and body for a11y

.changeset/weak-pots-tickle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@baloise/ds-core': patch
3+
---
4+
5+
**accordion**: add a11y labels to the accordion trigger

packages/core/src/components.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { BalCheckboxOption } from "./components/bal-checkbox/bal-checkbox.type";
1111
import { BalAriaForm } from "./utils/form";
1212
import { BalOption } from "./utils/dropdown";
1313
import { FooterLink } from "@baloise/web-app-utils";
14+
import { BalListItemAccordionBodyAria } from "./components/bal-list/bal-list-item-accordion-body/bal-list-item-accordion-body";
15+
import { BalListItemAccordionHeadAria } from "./components/bal-list/bal-list-item-accordion-head/bal-list-item-accordion-head";
1416
import { OverlayEventDetail } from "./components/bal-modal/bal-modal.type";
1517
import { PopoverPresentOptions } from "./components/bal-popover/bal-popover";
1618
import { BalRadioOption } from "./components/bal-radio/bal-radio.type";
@@ -23,6 +25,8 @@ export { BalCheckboxOption } from "./components/bal-checkbox/bal-checkbox.type";
2325
export { BalAriaForm } from "./utils/form";
2426
export { BalOption } from "./utils/dropdown";
2527
export { FooterLink } from "@baloise/web-app-utils";
28+
export { BalListItemAccordionBodyAria } from "./components/bal-list/bal-list-item-accordion-body/bal-list-item-accordion-body";
29+
export { BalListItemAccordionHeadAria } from "./components/bal-list/bal-list-item-accordion-head/bal-list-item-accordion-head";
2630
export { OverlayEventDetail } from "./components/bal-modal/bal-modal.type";
2731
export { PopoverPresentOptions } from "./components/bal-popover/bal-popover";
2832
export { BalRadioOption } from "./components/bal-radio/bal-radio.type";
@@ -106,6 +110,7 @@ export namespace Components {
106110
* The color to use from your application's color palette.
107111
*/
108112
"color": BalProps.BalButtonColor;
113+
"configChanged": (state: BalConfigState) => Promise<void>;
109114
/**
110115
* If `true` the button is aligned over the whole width
111116
*/
@@ -1807,16 +1812,19 @@ export namespace Components {
18071812
* Sets space to content of the accordion body
18081813
*/
18091814
"contentSpace": BalProps.BalListContentSpacing;
1815+
"setAria": (aria: BalListItemAccordionBodyAria) => Promise<void>;
18101816
}
18111817
interface BalListItemAccordionHead {
18121818
/**
18131819
* If `true` the list accordion is open
18141820
*/
18151821
"accordionOpen": boolean;
1822+
"configChanged": (state: BalConfigState) => Promise<void>;
18161823
/**
18171824
* Icon name string with value 'plus' on default
18181825
*/
18191826
"icon": BalProps.BalListItemAccordionHeadIcon;
1827+
"setAria": (aria: BalListItemAccordionHeadAria) => Promise<void>;
18201828
}
18211829
interface BalListItemContent {
18221830
"contentAlignment"?: string;

packages/core/src/components/bal-accordion/bal-accordion-trigger/bal-accordion-trigger.tsx

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import { Component, Host, h, Element, ComponentInterface, State, Prop } from '@stencil/core'
1+
import { Component, Host, h, Element, ComponentInterface, State, Prop, Method } from '@stencil/core'
22
import { BEM } from '../../../utils/bem'
33
import { Loggable, Logger, LogInstance } from '../../../utils/log'
44
import { stopEventBubbling } from '../../../utils/form-input'
55
import { AccordionState } from '../../../interfaces'
6+
import { BalConfigState, BalLanguage, defaultConfig, ListenToConfig } from '../../../utils/config'
7+
import { i18nBalAccordion } from '../bal-accordion.i18n'
8+
import { ariaBooleanToString } from 'packages/core/src/utils/aria'
69

710
@Component({
811
tag: 'bal-accordion-trigger',
@@ -14,6 +17,7 @@ export class AccordionTrigger implements ComponentInterface, Loggable {
1417
@Element() el?: HTMLElement
1518

1619
@State() parentAccordionId?: string
20+
@State() language: BalLanguage = defaultConfig.language
1721

1822
log!: LogInstance
1923

@@ -90,6 +94,20 @@ export class AccordionTrigger implements ComponentInterface, Loggable {
9094
this.updateAccordionId()
9195
}
9296

97+
/**
98+
* LISTENERS
99+
* ------------------------------------------------------
100+
*/
101+
102+
/**
103+
* @internal define config for the component
104+
*/
105+
@Method()
106+
@ListenToConfig()
107+
async configChanged(state: BalConfigState): Promise<void> {
108+
this.language = state.language
109+
}
110+
93111
/**
94112
* GETTERS
95113
* ------------------------------------------------------
@@ -170,6 +188,9 @@ export class AccordionTrigger implements ComponentInterface, Loggable {
170188
iconTurn={turn}
171189
color={this.color}
172190
size={this.size}
191+
title={this.active ? i18nBalAccordion[this.language].close : i18nBalAccordion[this.language].open}
192+
aria-label={this.active ? i18nBalAccordion[this.language].close : i18nBalAccordion[this.language].open}
193+
aria-expanded={ariaBooleanToString(this.active)}
173194
onClick={this.onClick}
174195
>
175196
{label}
@@ -182,9 +203,11 @@ export class AccordionTrigger implements ComponentInterface, Loggable {
182203
}}
183204
id={`${id}-button`}
184205
aria-controls={`${this.parentAccordionId}-details-content`}
185-
aria-label="accordion trigger"
206+
aria-expanded={ariaBooleanToString(this.active)}
186207
part={buttonPart}
187208
data-testid="bal-accordion-trigger"
209+
title={this.active ? i18nBalAccordion[this.language].close : i18nBalAccordion[this.language].open}
210+
aria-label={this.active ? i18nBalAccordion[this.language].close : i18nBalAccordion[this.language].open}
188211
onClick={this.onClick}
189212
{...triggerAttributes}
190213
>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { I18n } from '../../interfaces'
2+
3+
interface I18nBalAccordion {
4+
open: string
5+
close: string
6+
}
7+
8+
export const i18nBalAccordion: I18n<I18nBalAccordion> = {
9+
de: {
10+
open: 'Öffnen',
11+
close: 'Schließen',
12+
},
13+
en: {
14+
open: 'Open',
15+
close: 'Close',
16+
},
17+
fr: {
18+
open: 'Ouvrir',
19+
close: 'Fermer',
20+
},
21+
it: {
22+
open: 'Apri',
23+
close: 'Chiudi',
24+
},
25+
nl: {
26+
open: 'Open',
27+
close: 'Sluiten',
28+
},
29+
es: {
30+
open: 'Abrir',
31+
close: 'Cerrar',
32+
},
33+
pl: {
34+
open: 'Otwórz',
35+
close: 'Zamknij',
36+
},
37+
pt: {
38+
open: 'Abrir',
39+
close: 'Fechar',
40+
},
41+
sv: {
42+
open: 'Öppna',
43+
close: 'Stäng',
44+
},
45+
fi: {
46+
open: 'Avaa',
47+
close: 'Sulje',
48+
},
49+
}

packages/core/src/components/bal-button/bal-button.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Listen,
1212
} from '@stencil/core'
1313
import { Attributes, inheritAttributes } from '../../utils/attributes'
14+
import { ariaBooleanToString } from '../../utils/aria'
1415
import { rOnLoad } from '../../utils/helpers'
1516

1617
@Component({
@@ -335,7 +336,7 @@ export class Button implements ComponentInterface {
335336
onFocus={this.onFocus}
336337
onBlur={this.onBlur}
337338
onClick={this.onClick}
338-
aria-disabled={this.disabled ? 'true' : null}
339+
aria-disabled={ariaBooleanToString(this.disabled)}
339340
data-testid="bal-button"
340341
{...ariaAttributes}
341342
>

packages/core/src/components/bal-checkbox/bal-checkbox-group/bal-checkbox-group.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Loggable, Logger, LogInstance } from '../../../utils/log'
2222
import { BalMutationObserver, ListenToMutation } from '../../../utils/mutation'
2323
import { BalAriaForm, BalAriaFormLinking, defaultBalAriaForm } from '../../../utils/form'
2424
import { BalFocusObserver, ListenToFocus } from '../../../utils/focus'
25+
import { ariaBooleanToString } from 'packages/core/src/utils/aria'
2526

2627
@Component({
2728
tag: 'bal-checkbox-group',
@@ -443,7 +444,7 @@ export class CheckboxGroup
443444
...block.class(),
444445
}}
445446
role="group"
446-
aria-disabled={this.disabled ? 'true' : null}
447+
aria-disabled={ariaBooleanToString(this.disabled)}
447448
aria-labelledby={this.ariaForm.labelId}
448449
aria-describedby={this.ariaForm.messageId}
449450
onClick={this.onClick}

packages/core/src/components/bal-date/bal-date-calendar-cell/bal-date-calendar-cell.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, h, ComponentInterface, Host, Prop, Event, EventEmitter } from '@stencil/core'
22
import { stopEventBubbling } from '../../../utils/form-input'
33
import { BEM } from '../../../utils/bem'
4+
import { ariaBooleanToString } from 'packages/core/src/utils/aria'
45

56
@Component({
67
tag: 'bal-date-calendar-cell',
@@ -59,8 +60,8 @@ export class DateCalendarCell implements ComponentInterface {
5960
type="button"
6061
role="button"
6162
aria-label={this.fullDate}
62-
aria-selected={this.selected ? 'true' : 'false'}
63-
aria-disabled={this.disabled ? 'true' : 'false'}
63+
aria-selected={ariaBooleanToString(this.selected)}
64+
aria-disabled={ariaBooleanToString(this.disabled)}
6465
title={this.fullDate}
6566
onClick={(ev: MouseEvent) => this.onClick(ev, this.isoDate)}
6667
>

packages/core/src/components/bal-date/bal-date.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { BalConfigState, BalLanguage, ListenToConfig, defaultConfig } from '../.
2323
import { debounceEvent } from '../../utils/helpers'
2424
import { BalAriaForm, defaultBalAriaForm, BalAriaFormLinking } from '../../utils/form'
2525
import { balFloatingUi } from '../../utils/floating-ui'
26+
import { ariaBooleanToString } from '../../utils/aria'
2627

2728
@Component({
2829
tag: 'bal-date',
@@ -530,7 +531,7 @@ export class Date implements ComponentInterface, Loggable, BalAriaFormLinking {
530531
onClick={this.onIconClick}
531532
aria-label={i18nBalDate[this.language].toggleDatepicker}
532533
aria-haspopup="true"
533-
aria-expanded={this.isExpanded ? 'true' : 'false'}
534+
aria-expanded={ariaBooleanToString(this.isExpanded)}
534535
/>
535536
) : (
536537
''

packages/core/src/components/bal-file-upload/bal-file-upload.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { FileListComponent } from './components/file-list'
1414
import { toFileArray, toFileList } from './utils/file-list.util'
1515
import { validateFileArray } from './utils/file-validation.util'
1616
import { BalAriaForm, BalAriaFormLinking, defaultBalAriaForm } from '../../utils/form'
17+
import { ariaBooleanToString } from '../../utils/aria'
1718

1819
@Component({
1920
tag: 'bal-file-upload',
@@ -376,7 +377,7 @@ export class FileUpload implements FormInput<File[]>, BalAriaFormLinking {
376377
return (
377378
<Host
378379
onClick={this.onHostClick}
379-
aria-disabled={this.disabled ? 'true' : null}
380+
aria-disabled={ariaBooleanToString(this.disabled)}
380381
class={{
381382
'bal-file-upload': true,
382383
}}
@@ -403,7 +404,7 @@ export class FileUpload implements FormInput<File[]>, BalAriaFormLinking {
403404
aria-labelledby={this.ariaForm.labelId}
404405
aria-describedby={this.ariaForm.messageId}
405406
aria-invalid={this.invalid === true ? 'true' : 'false'}
406-
aria-disabled={this.disabled ? 'true' : null}
407+
aria-disabled={ariaBooleanToString(this.disabled)}
407408
name={this.name}
408409
multiple={this.multiple}
409410
disabled={this.disabled || this.loading || this.readonly}

packages/core/src/components/bal-input-slider/bal-input-slider.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { debounceEvent } from '../../utils/helpers'
44
import { stopEventBubbling } from '../../utils/form-input'
55
import { BEM } from '../../utils/bem'
66
import { BalAriaForm, BalAriaFormLinking, defaultBalAriaForm } from '../../utils/form'
7+
import { ariaBooleanToString } from '../../utils/aria'
78

89
@Component({
910
tag: 'bal-input-slider',
@@ -271,7 +272,7 @@ export class InputSlider implements BalAriaFormLinking {
271272
...block.modifier('disabled').class(this.disabled || this.readonly),
272273
}}
273274
onClick={this.handleClick}
274-
aria-disabled={this.disabled || this.readonly ? 'true' : null}
275+
aria-disabled={ariaBooleanToString(this.disabled || this.readonly)}
275276
>
276277
<div
277278
class={{
@@ -308,7 +309,7 @@ export class InputSlider implements BalAriaFormLinking {
308309
id={this.ariaForm.controlId || this.inputId}
309310
aria-labelledby={this.ariaForm.labelId}
310311
aria-describedby={this.ariaForm.messageId}
311-
aria-disabled={this.disabled ? 'true' : null}
312+
aria-disabled={ariaBooleanToString(this.disabled)}
312313
aria-invalid={this.invalid === true ? 'true' : 'false'}
313314
disabled={this.disabled}
314315
readonly={this.readonly}

packages/core/src/components/bal-input-stepper/bal-input-stepper.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { BEM } from '../../utils/bem'
2929
import { BalAriaForm, BalAriaFormLinking, defaultBalAriaForm } from '../../utils/form'
3030
import { i18nBalInputStepper } from './bal-input-stepper.i18n'
3131
import { LogInstance, Loggable, Logger } from '../../utils/log'
32+
import { ariaBooleanToString } from '../../utils/aria'
3233

3334
@Component({
3435
tag: 'bal-input-stepper',
@@ -263,7 +264,7 @@ export class InputStepper
263264

264265
return (
265266
<Host
266-
aria-disabled={this.disabled ? 'true' : null}
267+
aria-disabled={ariaBooleanToString(this.disabled)}
267268
aria-focused={this.focused ? 'true' : null}
268269
class={{
269270
...block.class(),
@@ -328,7 +329,7 @@ export class InputStepper
328329
aria-labelledby={this.ariaForm.labelId}
329330
aria-describedby={this.ariaForm.messageId}
330331
aria-invalid={this.invalid === true ? 'true' : 'false'}
331-
aria-disabled={this.disabled ? 'true' : null}
332+
aria-disabled={ariaBooleanToString(this.disabled)}
332333
data-testid="bal-input-stepper"
333334
type="text"
334335
value={this.value}

packages/core/src/components/bal-input/bal-input-date/bal-input-date.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { hasParent } from '../../../utils/helpers'
2020
import { DateMask, MaskComponentAdapter } from '../../../utils/mask'
2121
import { inputSetBlur, inputSetFocus } from '../../../utils/form-input'
2222
import { BalAriaForm, BalAriaFormLinking, defaultBalAriaForm } from '../../../utils/form'
23+
import { ariaBooleanToString } from 'packages/core/src/utils/aria'
2324

2425
@Component({
2526
tag: 'bal-input-date',
@@ -277,7 +278,7 @@ export class InputDate implements ComponentInterface, Loggable, BalConfigObserve
277278
aria-labelledby={this.ariaForm.labelId}
278279
aria-describedby={this.ariaForm.messageId}
279280
aria-invalid={this.invalid === true ? 'true' : 'false'}
280-
aria-disabled={this.disabled ? 'true' : null}
281+
aria-disabled={ariaBooleanToString(this.disabled)}
281282
required={this.required}
282283
disabled={this.disabled}
283284
readonly={this.readonly}

packages/core/src/components/bal-input/bal-input.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { ACTION_KEYS, isCtrlOrCommandKey, NUMBER_KEYS } from '../../utils/consta
4545
import { BEM } from '../../utils/bem'
4646
import { Loggable, Logger, LogInstance } from '../../utils/log'
4747
import { BalAriaForm, BalAriaFormLinking, defaultBalAriaForm } from '../../utils/form'
48+
import { ariaBooleanToString } from '../../utils/aria'
4849

4950
@Component({
5051
tag: 'bal-input',
@@ -548,7 +549,7 @@ export class Input implements ComponentInterface, FormInput<string | undefined>,
548549
return (
549550
<Host
550551
onClick={this.handleClick}
551-
aria-disabled={this.disabled ? 'true' : null}
552+
aria-disabled={ariaBooleanToString(this.disabled)}
552553
class={{
553554
...block.class(),
554555
}}
@@ -570,7 +571,7 @@ export class Input implements ComponentInterface, FormInput<string | undefined>,
570571
aria-labelledby={this.ariaForm.labelId}
571572
aria-describedby={this.ariaForm.messageId}
572573
aria-invalid={this.invalid === true ? 'true' : 'false'}
573-
aria-disabled={this.disabled ? 'true' : null}
574+
aria-disabled={ariaBooleanToString(this.disabled)}
574575
disabled={this.disabled}
575576
accept={this.accept}
576577
inputMode={this.inputmode}

0 commit comments

Comments
 (0)