Skip to content

Commit b55af30

Browse files
feat(cdk/interactivity-checker) add optional parameters to isFocusable
Adds parameters to isFocusable to optionally return true even when an element is invisible or disabled. Setting both to true makes isFocusable behave like the internal method isPotentiallyFocusable. Example use case: call isFocusable with includeInvisible=true to find a focusable element within a collapsed element. If isFocusable returns true, expand the collapsed element and focus the focusable element. Fixes #6468.
1 parent df981ee commit b55af30

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

src/cdk/a11y/interactivity-checker/interactivity-checker.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,17 @@ describe('InteractivityChecker', () => {
144144
});
145145
});
146146

147+
it('should return true for disabled form controls with includeDisabled', () => {
148+
const elements = createElements('input', 'textarea', 'select', 'button');
149+
elements.forEach(el => el.setAttribute('disabled', ''));
150+
appendElements(elements);
151+
152+
elements.forEach(el => {
153+
expect(checker.isFocusable(el, true, false))
154+
.toBe(true, `Expected <${el.nodeName} disabled> to to be focusable`);
155+
});
156+
});
157+
147158
it('should return false for a `display: none` element', () => {
148159
testContainerElement.innerHTML =
149160
`<input style="display: none;">`;
@@ -153,6 +164,15 @@ describe('InteractivityChecker', () => {
153164
.toBe(false, 'Expected element with `display: none` to not be visible');
154165
});
155166

167+
it('should return true for a `display: none` element with includeInvisible', () => {
168+
testContainerElement.innerHTML =
169+
`<input style="display: none;">`;
170+
const input = testContainerElement.querySelector('input') as HTMLElement;
171+
172+
expect(checker.isFocusable(input, false, true))
173+
.toBe(true, 'Expected element with `display: none` to be focusable');
174+
});
175+
156176
it('should return false for the child of a `display: none` element', () => {
157177
testContainerElement.innerHTML =
158178
`<div style="display: none;">

src/cdk/a11y/interactivity-checker/interactivity-checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,15 @@ export class InteractivityChecker {
132132
* Gets whether an element can be focused by the user.
133133
*
134134
* @param element Element to be checked.
135+
* @param includeDisabled When true, considers element to be focusable even if it is disabled.
136+
* @param includeInvisible When true, considers element to be focusable even if it is not visible.
135137
* @returns Whether the element is focusable.
136138
*/
137-
isFocusable(element: HTMLElement): boolean {
139+
isFocusable(element: HTMLElement, includeDisabled = false, includeInvisible = false): boolean {
138140
// Perform checks in order of left to most expensive.
139141
// Again, naive approach that does not capture many edge cases and browser quirks.
140-
return isPotentiallyFocusable(element) && !this.isDisabled(element) && this.isVisible(element);
142+
return isPotentiallyFocusable(element) && (includeDisabled || !this.isDisabled(element)) &&
143+
(includeInvisible || this.isVisible(element));
141144
}
142145

143146
}

tools/public_api_guard/cdk/a11y.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export interface Highlightable extends ListKeyManagerOption {
181181
export declare class InteractivityChecker {
182182
constructor(_platform: Platform);
183183
isDisabled(element: HTMLElement): boolean;
184-
isFocusable(element: HTMLElement): boolean;
184+
isFocusable(element: HTMLElement, includeDisabled?: boolean, includeInvisible?: boolean): boolean;
185185
isTabbable(element: HTMLElement): boolean;
186186
isVisible(element: HTMLElement): boolean;
187187
static ɵfac: i0.ɵɵFactoryDef<InteractivityChecker, never>;

0 commit comments

Comments
 (0)