mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(picker): manage focus to prevent a11y issues.
This commit is contained in:
@@ -135,12 +135,63 @@ export class Picker implements ComponentInterface {
|
||||
* function that has been set in onPointerDown
|
||||
* so that we enter/exit input mode correctly.
|
||||
*/
|
||||
private onClick = () => {
|
||||
private onClick = (ev: PointerEvent) => {
|
||||
const { actionOnClick } = this;
|
||||
if (actionOnClick) {
|
||||
actionOnClick();
|
||||
this.actionOnClick = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to avoid a11y issues we must manage focus
|
||||
* on the picker columns and picker itself.
|
||||
* This is because once picker is clicked we got an issue/warning because
|
||||
* picker input is being focused, and once it has tabindex -1 it can't be focused,
|
||||
* which ends on focusing the picker itself.
|
||||
* During the process above we fall into issues since there is an element
|
||||
* with tabindex -1 and aria-hidden='true' that is focused, which is not allowed.
|
||||
* That said and since onClick is being propagated to the picker itself, we need to
|
||||
* manage focus on the picker columns and picker itself to avoid the issue.
|
||||
*/
|
||||
const clickedTarget = ev.target as HTMLElement;
|
||||
let elementToFocus: HTMLElement | null = null;
|
||||
|
||||
switch (clickedTarget.tagName) {
|
||||
case 'ION-PICKER':
|
||||
/**
|
||||
* If the user clicked the picker itself
|
||||
* then we should focus the first picker options
|
||||
* so that users can scroll through them.
|
||||
*/
|
||||
const ionPickerColumn = this.el.querySelector('ion-picker-column');
|
||||
elementToFocus = ionPickerColumn?.shadowRoot?.querySelector('.picker-opts') as HTMLElement | null;
|
||||
break;
|
||||
|
||||
case 'ION-PICKER-COLUMN':
|
||||
/**
|
||||
* If the user clicked a picker column
|
||||
* then we should focus its own picker options
|
||||
* so that users can scroll through them.
|
||||
*/
|
||||
elementToFocus = clickedTarget.shadowRoot?.querySelector('.picker-opts') as HTMLElement | null;
|
||||
break;
|
||||
|
||||
case 'ION-PICKER-COLUMN-OPTION':
|
||||
/**
|
||||
* If the user clicked a picker column option
|
||||
* then we should focus its picker options parent so that
|
||||
* users can scroll through them.
|
||||
*/
|
||||
const ionPickerColumnOption = clickedTarget.closest('ion-picker-column');
|
||||
if (ionPickerColumnOption) {
|
||||
elementToFocus = ionPickerColumnOption.shadowRoot?.querySelector('.picker-opts') as HTMLElement | null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (elementToFocus) {
|
||||
elementToFocus.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -537,7 +588,10 @@ export class Picker implements ComponentInterface {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Host onPointerDown={(ev: PointerEvent) => this.onPointerDown(ev)} onClick={() => this.onClick()}>
|
||||
<Host
|
||||
onPointerDown={(ev: PointerEvent) => this.onPointerDown(ev)}
|
||||
onClick={(ev: PointerEvent) => this.onClick(ev)}
|
||||
>
|
||||
<input
|
||||
aria-hidden="true"
|
||||
tabindex={-1}
|
||||
|
||||
Reference in New Issue
Block a user