Skip to content

chore: expand and polish cdk docs #7675

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
Oct 27, 2017
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
92 changes: 92 additions & 0 deletions src/cdk/a11y/a11y.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
The `a11y` package provides a number of tools to improve accessibility, described below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FocusMonitor as well

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add that in a follow-up PR


### ListKeyManager
`ListKeyManager` manages the active option in a list of items based on keyboard interaction.
Intended to be used with components that correspond to a `role="menu"` or `role="listbox"` pattern.

#### Basic usage
Any component that uses a `ListKeyManager` will generally do three things:
* Create a `@ViewChildren` query for the options being managed.
* Initialize the `ListKeyManager`, passing in the options.
* Forward keyboard events from the managed component to the `ListKeyManager`.

Each option should implement the `ListKeyManagerOption` interface:
```ts
interface ListKeyManagerOption {
disabled?: boolean;
getLabel?(): string;
}
```

#### Wrapping
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a short paragraph like this for typeahead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can add in a follow-up PR.

Navigation through options can be made to wrap via the `withWrap` method
```ts
this.keyManager = new FocusKeyManager(...).withWrap();
```

#### Types of key managers
There are two varieties of `ListKeyManager`, `FocusKeyManager` and `ActiveDescendantKeyManager`.

##### FocusKeyManager
Used when options will directly receive browser focus. Each item managed must implement the
`FocusableOption` interface:
```ts
interface FocusableOption extends ListKeyManagerOption {
focus(): void;
}
```

##### ActiveDescendantKeyManager
Used when options will be marked as active via `aria-activedescendant`.
Each item managed must implement the
`Highlightable` interface:
```ts
interface Highlightable extends ListKeyManagerOption {
setActiveStyles(): void;
setInactiveStyles(): void;
}
```

Each item must also have an ID bound to the listbox's or menu's `aria-activedescendant`.


### FocusTrap
The `cdkTrapFocus` directive traps <kbd>Tab</kbd> key focus within an element. This is intended to
be used to create accessible experience for components like
[modal dialogs](https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal), where focus must be
constrained.

This directive is declared in `A11yModule`.

#### Example
```html
<div class="my-inner-dialog-content" cdkTrapFocus>
<!-- Tab and Shift + Tab will not leave this element. -->
</div>
```

This directive will not prevent focus from moving out of the trapped region due to mouse
interaction.


### InteractivityChecker
`InteractivityChecker` is used to check the interactivity of an element, capturing disabled,
visible, tabbable, and focusable states for accessibility purposes. See the API docs for more
details.


### LiveAnnouncer
`LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region.
See [the W3C's WAI-ARIA](https://www.w3.org/TR/wai-aria/states_and_properties#aria-live)
for more information on aria-live regions.

#### Example
```ts
@Component({...})
export class MyComponent {

constructor(liveAnnouncer: LiveAnnouncer) {
liveAnnouncer.announce("Hey Google");
}
}
```
23 changes: 16 additions & 7 deletions src/cdk/a11y/focus-trap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import {InteractivityChecker} from './interactivity-checker';
/**
* Class that allows for trapping focus within a DOM element.
*
* NOTE: This class currently uses a very simple (naive) approach to focus trapping.
* This class currently uses a relatively simple approach to focus trapping.
* It assumes that the tab order is the same as DOM order, which is not necessarily true.
* Things like tabIndex > 0, flex `order`, and shadow roots can cause to two to misalign.
* This will be replaced with a more intelligent solution before the library is considered stable.
*/
export class FocusTrap {
private _startAnchor: HTMLElement | null;
Expand Down Expand Up @@ -169,7 +168,7 @@ export class FocusTrap {

/**
* Focuses the element that should be focused when the focus trap is initialized.
* @returns Returns whether focus was moved successfuly.
* @returns Whether focus was moved successfuly.
*/
focusInitialElement(): boolean {
if (!this._platform.isBrowser) {
Expand All @@ -188,7 +187,7 @@ export class FocusTrap {

/**
* Focuses the first tabbable element within the focus trap region.
* @returns Returns whether focus was moved successfuly.
* @returns Whether focus was moved successfuly.
*/
focusFirstTabbableElement(): boolean {
const redirectToElement = this._getRegionBoundary('start');
Expand All @@ -202,7 +201,7 @@ export class FocusTrap {

/**
* Focuses the last tabbable element within the focus trap region.
* @returns Returns whether focus was moved successfuly.
* @returns Whether focus was moved successfuly.
*/
focusLastTabbableElement(): boolean {
const redirectToElement = this._getRegionBoundary('end');
Expand Down Expand Up @@ -287,14 +286,23 @@ export class FocusTrapFactory {
private _platform: Platform,
private _ngZone: NgZone) { }

create(element: HTMLElement, deferAnchors: boolean = false): FocusTrap {
return new FocusTrap(element, this._platform, this._checker, this._ngZone, deferAnchors);
/**
* Creates a focus-trapped region around the given element.
* @param element The element around which focus will be trapped.
* @param deferCaptureElements Defers the creation of focus-capturing elements to be done
* manually by the user.
* @returns The created focus trap instance.
*/
create(element: HTMLElement, deferCaptureElements: boolean = false): FocusTrap {
return new FocusTrap(
element, this._platform, this._checker, this._ngZone, deferCaptureElements);
}
}


/**
* Directive for trapping focus within a region.
* @docs-private
* @deprecated
*/
@Directive({
Expand Down Expand Up @@ -330,6 +338,7 @@ export class FocusTrapDeprecatedDirective implements OnDestroy, AfterContentInit
exportAs: 'cdkTrapFocus',
})
export class FocusTrapDirective implements OnDestroy, AfterContentInit {
/** Underlying FocusTrap instance. */
focusTrap: FocusTrap;

/** Whether the focus trap is active. */
Expand Down
9 changes: 4 additions & 5 deletions src/cdk/a11y/interactivity-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
import {Injectable} from '@angular/core';
import {Platform} from '@angular/cdk/platform';

/**
* The InteractivityChecker leans heavily on the ally.js accessibility utilities.
* Methods like `isTabbable` are only covering specific edge-cases for the browsers which are
* supported.
*/

// The InteractivityChecker leans heavily on the ally.js accessibility utilities.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a11y.ts instead of .js?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With ally.js is a library meant, not a source file inside of Material. https://allyjs.io/

// Methods like `isTabbable` are only covering specific edge-cases for the browsers which are
// supported.

/**
* Utility for checking the interactivity of an element, such as whether is is focusable or
Expand Down
4 changes: 1 addition & 3 deletions src/cdk/a11y/list-key-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import {Subscription} from 'rxjs/Subscription';
import {UP_ARROW, DOWN_ARROW, TAB, A, Z, ZERO, NINE} from '@angular/cdk/keycodes';
import {RxChain, debounceTime, filter, map, doOperator} from '@angular/cdk/rxjs';

/**
* This interface is for items that can be passed to a ListKeyManager.
*/
/** This interface is for items that can be passed to a ListKeyManager. */
export interface ListKeyManagerOption {
disabled?: boolean;
getLabel?(): string;
Expand Down
10 changes: 9 additions & 1 deletion src/cdk/bidi/bidi.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
### BidiModule
The `bidi` package provides a common system for components to get and respond to change in the
application's LTR/RTL layout direction.

### Directionality

When including the CDK's `BidiModule`, components can inject `Directionality` to get the current
text direction (RTL or LTR);
Expand Down Expand Up @@ -28,3 +31,8 @@ export class MyWidget implements OnDestroy {
}
```

### The `Dir` directive
The `BidiModule` also includes a directive that matches any elements with a `dir` attribute. This
directive has the same API as Directionality and provides itself _as_ `Directionality`. By doing
this, any component that injects `Directionality` will get the closest ancestor layout direction
context.
9 changes: 3 additions & 6 deletions src/cdk/bidi/dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {Direction, Directionality} from './directionality';
/**
* Directive to listen for changes of direction of part of the DOM.
*
* Would provide itself in case a component looks for the Directionality service
* Provides itself as Directionality such that descendant directives only need to ever inject
* Directionality to get the closest direction.
*/
@Directive({
selector: '[dir]',
Expand All @@ -27,7 +28,6 @@ import {Direction, Directionality} from './directionality';
exportAs: 'dir',
})
export class Dir implements Directionality {
/** Layout direction of the element. */
_dir: Direction = 'ltr';

/** Whether the `value` has been set to its initial value. */
Expand All @@ -38,10 +38,7 @@ export class Dir implements Directionality {

/** @docs-private */
@Input('dir')
get dir(): Direction {
return this._dir;
}

get dir(): Direction { return this._dir; }
set dir(v: Direction) {
let old = this._dir;
this._dir = v;
Expand Down
3 changes: 3 additions & 0 deletions src/cdk/bidi/directionality.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export const DIR_DOCUMENT = new InjectionToken<Document>('mat-dir-doc');
*/
@Injectable()
export class Directionality {
/** The current 'ltr' or 'rtl' value. */
readonly value: Direction = 'ltr';

/** Stream that emits whenever the 'ltr' / 'rtl' state changes. */
readonly change = new EventEmitter<void>();

constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) {
Expand Down
11 changes: 7 additions & 4 deletions src/cdk/layout/breakpoints-observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ interface Query {
mql: MediaQueryList;
}

/**
* Utility for checking the matching state of @media queries.
*/
/** Utility for checking the matching state of @media queries. */
@Injectable()
export class BreakpointObserver implements OnDestroy {
/** A map of all media queries currently being listened for. */
Expand All @@ -42,7 +40,11 @@ export class BreakpointObserver implements OnDestroy {
this._destroySubject.complete();
}

/** Whether the query currently is matched. */
/**
* Whether one or more media queries match the current viewport size.
* @param value One or more media queries to check.
* @returns Whether any of the media queries match.
*/
isMatched(value: string | string[]): boolean {
let queries = coerceArray(value);
return queries.some(mediaQuery => this._registerQuery(mediaQuery).mql.matches);
Expand All @@ -51,6 +53,7 @@ export class BreakpointObserver implements OnDestroy {
/**
* Gets an observable of results for the given queries that will emit new results for any changes
* in matching of the given queries.
* @returns A stream of matches for the given queries.
*/
observe(value: string | string[]): Observable<BreakpointState> {
let queries = coerceArray(value);
Expand Down
77 changes: 77 additions & 0 deletions src/cdk/layout/layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
The `layout` package provides utilities to build responsive UIs that react to screen-size changes.

### BreakpointObserver

`BreakpointObserver` is a utility for evaluating media queries and reacting to their changing.

#### Evaluate against the current viewport
The `isMatched` method is used to evaluate one or more media queries against the current viewport
size.
```ts
const isSmallScreen = breakpointObserver.isMatched('(max-width: 599px)');
```

#### React to changes to the viewport
The `observe` method is used to get an observable stream that will emit whenever one of the given
media queries would have a different result.
```ts
const layoutChanges = breakpointObserver.observe([
'(orientation: portrait)',
'(orientation: landscape)',
]);

layoutChanges.subscribe(result => {
updateMyLayoutForOrientationChange();
});
```

#### Default breakpoints
A set of default media queries are available corresponding to breakpoints for different device
types.

```ts
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';

@Component({...})
class MyComponent {
constructor(breakpointObserver: BreakpointObserver) {
breakpointObserver.observe([
Breakpoints.HandsetLandscape,
Breakpoints.HandsetPortrait
]).subscribe(result => {
if (result.matches) {
this.activateHandsetLayout();
}
});
}
}
```

The built-in breakpoints based on [Google's Material Design
specification](https://material.io/guidelines/layout/responsive-ui.html#responsive-ui-breakpoints).
The available values are:
* Handset
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be styled as code? i.e. Handset

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine without the code style

* Tablet
* Web
* HandsetPortrait
* TabletPortrait
* WebPortrait
* HandsetLandscape
* TabletLandscape
* WebLandscape


### MediaMatcher
`MediaMatcher` is a lower-level utility that wraps the native `matchMedia`. This service normalizes
browser differences and serves as a convenient API that can be replaces with a fake in unit tests.
The `matchMedia` method can be used to get a native
[`MediaQueryList`](https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList).

```ts
@Component({...})
class MyComponent {
constructor(mediaMatcher: MediaMatcher) {
const mediaQueryList = mediaMatcher.matchMedia('(min-width: 1px)');
}
}
```
6 changes: 3 additions & 3 deletions src/cdk/layout/media-matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import {Platform} from '@angular/cdk/platform';
*/
const styleElementForWebkitCompatibility: Map<string, HTMLStyleElement> = new Map();

/**
* A utility for calling matchMedia queries.
*/
/** A utility for calling matchMedia queries. */
@Injectable()
export class MediaMatcher {
/** The internal matchMedia method to return back a MediaQueryList like object. */
Expand All @@ -30,6 +28,8 @@ export class MediaMatcher {
}

/**
* Evaluates the given media query and returns the native MediaQueryList from which results
* can be retrieved.
* Confirms the layout engine will trigger for the selector query provided and returns the
* MediaQueryList for the query provided.
*/
Expand Down
8 changes: 5 additions & 3 deletions src/cdk/observers/observers.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
### Observers
The `observers` package provides convenience directives built on top of native web platform
observers, such as MutationObserver.


### cdkObserveContent

A directive for observing when the content of the host element changes. An event is emitted when a
mutation to the content is observed.

#### Example

```html
<div class="projected-content-wrapper" (cdkObserveContent)="projectContentChanged()">
<ng-content></ng-content>
Expand Down
Loading