Skip to content

feat(cdk/interactivity-checker) add config parameter to isFocusable #19594

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

Merged
merged 1 commit into from
Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/cdk/a11y/interactivity-checker/interactivity-checker.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Platform} from '@angular/cdk/platform';
import {inject} from '@angular/core/testing';
import {InteractivityChecker} from './interactivity-checker';
import {InteractivityChecker, IsFocusableConfig} from './interactivity-checker';

describe('InteractivityChecker', () => {
let platform: Platform;
Expand Down Expand Up @@ -153,6 +153,17 @@ describe('InteractivityChecker', () => {
.toBe(false, 'Expected element with `display: none` to not be visible');
});

it('should return true for a `display: none` element with ignoreVisibility', () => {
testContainerElement.innerHTML =
`<input style="display: none;">`;
const input = testContainerElement.querySelector('input') as HTMLElement;
let config = new IsFocusableConfig();
config.ignoreVisibility = true;

expect(checker.isFocusable(input, config))
.toBe(true, 'Expected element with `display: none` to be focusable');
});

it('should return false for the child of a `display: none` element', () => {
testContainerElement.innerHTML =
`<div style="display: none;">
Expand Down
15 changes: 13 additions & 2 deletions src/cdk/a11y/interactivity-checker/interactivity-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
import {Platform} from '@angular/cdk/platform';
import {Injectable} from '@angular/core';

/**
* Configuration for the isFocusable method.
*/
export class IsFocusableConfig {
/**
* Whether to count an element as focusable even if it is not currently visible.
*/
ignoreVisibility: boolean = false;
}

// The InteractivityChecker leans heavily on the ally.js accessibility utilities.
// Methods like `isTabbable` are only covering specific edge-cases for the browsers which are
Expand Down Expand Up @@ -132,12 +141,14 @@ export class InteractivityChecker {
* Gets whether an element can be focused by the user.
*
* @param element Element to be checked.
* @param config The config object with options to customize this method's behavior
* @returns Whether the element is focusable.
*/
isFocusable(element: HTMLElement): boolean {
isFocusable(element: HTMLElement, config?: IsFocusableConfig): boolean {
// Perform checks in order of left to most expensive.
// Again, naive approach that does not capture many edge cases and browser quirks.
return isPotentiallyFocusable(element) && !this.isDisabled(element) && this.isVisible(element);
return isPotentiallyFocusable(element) && !this.isDisabled(element) &&
(config?.ignoreVisibility || this.isVisible(element));
}

}
Expand Down
6 changes: 5 additions & 1 deletion tools/public_api_guard/cdk/a11y.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export interface Highlightable extends ListKeyManagerOption {
export declare class InteractivityChecker {
constructor(_platform: Platform);
isDisabled(element: HTMLElement): boolean;
isFocusable(element: HTMLElement): boolean;
isFocusable(element: HTMLElement, config?: IsFocusableConfig): boolean;
isTabbable(element: HTMLElement): boolean;
isVisible(element: HTMLElement): boolean;
static ɵfac: i0.ɵɵFactoryDef<InteractivityChecker, never>;
Expand All @@ -190,6 +190,10 @@ export declare class InteractivityChecker {

export declare function isFakeMousedownFromScreenReader(event: MouseEvent): boolean;

export declare class IsFocusableConfig {
ignoreVisibility: boolean;
}

export declare class ListKeyManager<T extends ListKeyManagerOption> {
get activeItem(): T | null;
get activeItemIndex(): number | null;
Expand Down