-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
fix(selection-list): improve accessibility of selection list #10137
fix(selection-list): improve accessibility of selection list #10137
Conversation
expect(keyManager.activeItemIndex).toBe(2); | ||
}); | ||
|
||
it('should be able to not skip any item', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already covered by the custom predicate test.
}); | ||
|
||
it('should be able to skip items with a custom predicate', () => { | ||
keyManager.skipPredicate((item: any) => item.skipItem); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the item
here is a FakeFocusable
. Alternatively you could let TS infer the type itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
* Predicate function that determines items that should be skipped by the list key manager. | ||
* By default, disabled items are skipped by the key manager. | ||
*/ | ||
skipPredicate(predicateFn: (item: T) => boolean): this { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs an @param
for the predicate
. Also it might be better to call it predicate
, because the consumer can see that it's a function by looking at the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
* Predicate function that can be used to check whether an item should be skipped | ||
* by the key manager. By default, disabled items are skipped. | ||
*/ | ||
private _skipPredicateFn = (item: T) => item.disabled; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call this one skipPredicate
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would conflict with the function name that sets the predicate. We could name it _skipPredicate
, but having the Fn
suffix feels appropriate IMO.
@@ -74,7 +74,7 @@ export function createKeyboardEvent(type: string, keyCode: number, target?: Elem | |||
} | |||
|
|||
/** Creates a fake event object with any desired event type. */ | |||
export function createFakeEvent(type: string, canBubble = true, cancelable = true) { | |||
export function createFakeEvent(type: string, canBubble = false, cancelable = true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we should leave this one as true
, because most of the events bubble. We can opt-out of it for the ones that don't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also check for the type and just default to false
if the event is either focus
| blur
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could, but there might be more that don't bubble. We open ourselves up to having to maintain a list of events that don't bubble.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why I think that we should be explicit the other way. If some tests insist on event bubbling, which is very unlikely, then that test should explicitly turn on bubbling
.
Bubbling will most likely causing more side-effects, so disabling it by default in tests might be better. That's at least what I noticed while working with the focus
event right now.
src/lib/list/selection-list.ts
Outdated
'class': 'mat-selection-list', | ||
'(focus)': 'focus()', | ||
'(blur)': '_onTouched()', | ||
'(keydown)': '_keydown($event)', | ||
'[attr.tabindex]': 'disabled ? 0 : tabIndex', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be just tabIndex
? Either way the list will be focusable, unless the consumer passed in -1, in which case this will make it focusable only when disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With -1
the selection list won't be tabbable anymore, but it should be tabbable if disabled for a11y.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant was that with this setup, if the consumer set it to -1 explicitly, it would only be tabbable when it's disabled which seems wrong.
@@ -392,10 +406,6 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu | |||
|
|||
/** Passes relevant key presses to our key manager. */ | |||
_keydown(event: KeyboardEvent) { | |||
if (this.disabled) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check still has to apply to the ENTER
and SPACE
keypresses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Done
a625f70
to
9cb81dc
Compare
@crisbeto Done. Addressed most of your comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, left a couple more nits.
src/lib/list/selection-list.ts
Outdated
// Always prevent space from scrolling the page since the list has focus | ||
event.preventDefault(); | ||
|
||
if (!this.disabled) { | ||
this._toggleSelectOnFocusedOption(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move the preventDefault
into the check as well. That's what it used to do before.
@@ -72,6 +78,16 @@ export class ListKeyManager<T extends ListKeyManagerOption> { | |||
/** Stream that emits whenever the active item of the list manager changes. */ | |||
change = new Subject<number>(); | |||
|
|||
/** | |||
* Predicate function that determines items that should be skipped by the list key manager. | |||
* By default, disabled items are skipped by the key manager. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds like it's describing the predicate
argument, instead of what the method does. Consider changing it to something like "Sets the predicate function that the determines..."?
* Since the selection list is still a `list` that contains some content, there should be a way for screenreader users, to navigate through the options (even if disabled). With this change, if the list is disabled, it's still possible to walk through the options. * Adds a new functionality to the `ListKeyManager` that allows the developer to control the items that can't be focused. `skipPredicate`. (e.g. for the selection list we want to make sure that users can navigate using the arrow keys to disabled items as well) * Testing: Fixes that by default all fake events bubble up the DOM. This works in most of the cases, but it's wrong to always bubble events. e.g. `focus` doesn't bubble. Fixes angular#9995
9cb81dc
to
ca8273f
Compare
* Since the selection list is still a `list` that contains some content, there should be a way for screenreader users, to navigate through the options (even if disabled). With this change, if the list is disabled, it's still possible to walk through the options. * Adds a new functionality to the `ListKeyManager` that allows the developer to control the items that can't be focused. `skipPredicate`. (e.g. for the selection list we want to make sure that users can navigate using the arrow keys to disabled items as well) * Testing: Fixes that by default all fake events bubble up the DOM. This works in most of the cases, but it's wrong to always bubble events. e.g. `focus` doesn't bubble. Fixes #9995
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
list
that contains some content, there should be a way for screenreader users, to navigate through the options (even if disabled). With this change, if the list is disabled, it's still possible to walk through the options.ListKeyManager
that allows the developer to control the items that can't be focused.skipPredicate
. (e.g. for the selection list we want to make sure that users can navigate using the arrow keys to disabled items as well)focus
doesn't bubble.Fixes #9995