diff --git a/src/cdk/a11y/a11y.md b/src/cdk/a11y/a11y.md new file mode 100644 index 000000000000..c42198d5cba3 --- /dev/null +++ b/src/cdk/a11y/a11y.md @@ -0,0 +1,92 @@ +The `a11y` package provides a number of tools to improve accessibility, described below. + +### 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 +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 Tab 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 +
+ +
+``` + +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"); + } +} +``` diff --git a/src/cdk/a11y/focus-trap.ts b/src/cdk/a11y/focus-trap.ts index c01eff86098a..e32cf568d106 100644 --- a/src/cdk/a11y/focus-trap.ts +++ b/src/cdk/a11y/focus-trap.ts @@ -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; @@ -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) { @@ -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'); @@ -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'); @@ -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({ @@ -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. */ diff --git a/src/cdk/a11y/interactivity-checker.ts b/src/cdk/a11y/interactivity-checker.ts index 7d092c9f1fad..cc133d95c0cd 100644 --- a/src/cdk/a11y/interactivity-checker.ts +++ b/src/cdk/a11y/interactivity-checker.ts @@ -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. +// 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 diff --git a/src/cdk/a11y/list-key-manager.ts b/src/cdk/a11y/list-key-manager.ts index af8e82711a56..79299ed9731e 100644 --- a/src/cdk/a11y/list-key-manager.ts +++ b/src/cdk/a11y/list-key-manager.ts @@ -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; diff --git a/src/cdk/bidi/bidi.md b/src/cdk/bidi/bidi.md index d9b92efb5e8c..dddc26874980 100644 --- a/src/cdk/bidi/bidi.md +++ b/src/cdk/bidi/bidi.md @@ -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); @@ -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. diff --git a/src/cdk/bidi/dir.ts b/src/cdk/bidi/dir.ts index 2568fe541270..cd4afa11570e 100644 --- a/src/cdk/bidi/dir.ts +++ b/src/cdk/bidi/dir.ts @@ -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]', @@ -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. */ @@ -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; diff --git a/src/cdk/bidi/directionality.ts b/src/cdk/bidi/directionality.ts index d3c9f4bcf2ff..17eb64bf07ab 100644 --- a/src/cdk/bidi/directionality.ts +++ b/src/cdk/bidi/directionality.ts @@ -37,7 +37,10 @@ export const DIR_DOCUMENT = new InjectionToken('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(); constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) { diff --git a/src/cdk/layout/breakpoints-observer.ts b/src/cdk/layout/breakpoints-observer.ts index 7e919318556a..4af24b1f8a44 100644 --- a/src/cdk/layout/breakpoints-observer.ts +++ b/src/cdk/layout/breakpoints-observer.ts @@ -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. */ @@ -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); @@ -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 { let queries = coerceArray(value); diff --git a/src/cdk/layout/layout.md b/src/cdk/layout/layout.md new file mode 100644 index 000000000000..7168d3d1ef37 --- /dev/null +++ b/src/cdk/layout/layout.md @@ -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 +* 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)'); + } +} +``` diff --git a/src/cdk/layout/media-matcher.ts b/src/cdk/layout/media-matcher.ts index 2b0aed2aac8c..2079aa1004f8 100644 --- a/src/cdk/layout/media-matcher.ts +++ b/src/cdk/layout/media-matcher.ts @@ -13,9 +13,7 @@ import {Platform} from '@angular/cdk/platform'; */ const styleElementForWebkitCompatibility: Map = 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. */ @@ -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. */ diff --git a/src/cdk/observers/observers.md b/src/cdk/observers/observers.md index b5d2495a11b2..4b622846169a 100644 --- a/src/cdk/observers/observers.md +++ b/src/cdk/observers/observers.md @@ -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
diff --git a/src/cdk/overlay/fullscreen-overlay-container.ts b/src/cdk/overlay/fullscreen-overlay-container.ts index 5165f8ce97de..aa7c16d33f18 100644 --- a/src/cdk/overlay/fullscreen-overlay-container.ts +++ b/src/cdk/overlay/fullscreen-overlay-container.ts @@ -10,13 +10,11 @@ import {Injectable} from '@angular/core'; import {OverlayContainer} from './overlay-container'; /** - * The FullscreenOverlayContainer is the alternative to OverlayContainer - * that supports correct displaying of overlay elements in Fullscreen mode + * Alternative to OverlayContainer that supports correct displaying of overlay elements in + * Fullscreen mode * https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen - * It should be provided in the root component that way: - * providers: [ - * {provide: OverlayContainer, useClass: FullscreenOverlayContainer} - * ], + * + * Should be provided in the root component. */ @Injectable() export class FullscreenOverlayContainer extends OverlayContainer { diff --git a/src/cdk/overlay/overlay-config.ts b/src/cdk/overlay/overlay-config.ts index cd362ced3fe0..ed12730a7fff 100644 --- a/src/cdk/overlay/overlay-config.ts +++ b/src/cdk/overlay/overlay-config.ts @@ -12,7 +12,7 @@ import {ScrollStrategy} from './scroll/scroll-strategy'; import {NoopScrollStrategy} from './scroll/noop-scroll-strategy'; -/** OverlayConfig captures the initial configuration used when opening an overlay. */ +/** Initial configuration used when creating an overlay. */ export class OverlayConfig { /** Strategy with which to position the overlay. */ positionStrategy?: PositionStrategy; diff --git a/src/cdk/overlay/overlay-container.ts b/src/cdk/overlay/overlay-container.ts index e932e75f4210..25aea7618478 100644 --- a/src/cdk/overlay/overlay-container.ts +++ b/src/cdk/overlay/overlay-container.ts @@ -9,10 +9,7 @@ import {Injectable, Optional, SkipSelf, OnDestroy} from '@angular/core'; -/** - * The OverlayContainer is the container in which all overlays will load. - * It should be provided in the root component to ensure it is properly shared. - */ +/** Container inside which all overlays will render. */ @Injectable() export class OverlayContainer implements OnDestroy { protected _containerElement: HTMLElement; diff --git a/src/cdk/overlay/overlay-directives.ts b/src/cdk/overlay/overlay-directives.ts index 431addab3d90..7bc537629f5f 100644 --- a/src/cdk/overlay/overlay-directives.ts +++ b/src/cdk/overlay/overlay-directives.ts @@ -79,7 +79,9 @@ export const MAT_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { exportAs: 'cdkOverlayOrigin', }) export class OverlayOrigin { - constructor(public elementRef: ElementRef) { } + constructor( + /** Reference to the element on which the directive is applied. */ + public elementRef: ElementRef) { } } diff --git a/src/cdk/overlay/overlay.md b/src/cdk/overlay/overlay.md index f37cce43fe7a..ca71948a8494 100644 --- a/src/cdk/overlay/overlay.md +++ b/src/cdk/overlay/overlay.md @@ -1,6 +1,4 @@ -### Overlay - -The `Overlay` service provides a way to open floating panels on the screen. +The `overlay` package provides a way to open floating panels on the screen. ### Creating overlays Calling `overlay.create()` will return an `OverlayRef` instance. This instance is a handle for diff --git a/src/cdk/overlay/overlay.ts b/src/cdk/overlay/overlay.ts index 2bfa74a58126..d1a3630410b3 100644 --- a/src/cdk/overlay/overlay.ts +++ b/src/cdk/overlay/overlay.ts @@ -38,7 +38,9 @@ let defaultConfig = new OverlayConfig(); */ @Injectable() export class Overlay { - constructor(public scrollStrategies: ScrollStrategyOptions, + constructor( + /** Scrolling strategies that can be used when creating an overlay. */ + public scrollStrategies: ScrollStrategyOptions, private _overlayContainer: OverlayContainer, private _componentFactoryResolver: ComponentFactoryResolver, private _positionBuilder: OverlayPositionBuilder, @@ -48,7 +50,7 @@ export class Overlay { /** * Creates an overlay. - * @param config Config to apply to the overlay. + * @param config Configuration applied to the overlay. * @returns Reference to the created overlay. */ create(config: OverlayConfig = defaultConfig): OverlayRef { @@ -58,8 +60,9 @@ export class Overlay { } /** - * Returns a position builder that can be used, via fluent API, + * Gets a position builder that can be used, via fluent API, * to construct and configure a position strategy. + * @returns An overlay position builder. */ position(): OverlayPositionBuilder { return this._positionBuilder; diff --git a/src/cdk/overlay/position/connected-position-strategy.ts b/src/cdk/overlay/position/connected-position-strategy.ts index 2d3269d0642a..eb08712ba797 100644 --- a/src/cdk/overlay/position/connected-position-strategy.ts +++ b/src/cdk/overlay/position/connected-position-strategy.ts @@ -90,6 +90,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { return this._preferredPositions; } + /** Attach this position strategy to an overlay. */ attach(overlayRef: OverlayRef): void { this._overlayRef = overlayRef; this._pane = overlayRef.overlayElement; @@ -97,7 +98,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { this._resizeSubscription = this._viewportRuler.change().subscribe(() => this.apply()); } - /** Performs any cleanup after the element is destroyed. */ + /** Disposes all resources used by the position strategy. */ dispose() { this._resizeSubscription.unsubscribe(); } @@ -156,7 +157,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { } /** - * This re-aligns the overlay element with the trigger in its last calculated position, + * Re-positions the overlay element with the trigger in its last calculated position, * even if a position higher in the "preferred positions" list would now fit. This * allows one to re-align the panel without changing the orientation of the panel. */ diff --git a/src/cdk/overlay/position/connected-position.ts b/src/cdk/overlay/position/connected-position.ts index ff9114fd8538..0b8d8a9c6173 100644 --- a/src/cdk/overlay/position/connected-position.ts +++ b/src/cdk/overlay/position/connected-position.ts @@ -28,9 +28,13 @@ export interface OverlayConnectionPosition { /** The points of the origin element and the overlay element to connect. */ export class ConnectionPositionPair { + /** X-axis attachment point for connected overlay origin. Can be 'start', 'end', or 'center'. */ originX: HorizontalConnectionPos; + /** Y-axis attachment point for connected overlay origin. Can be 'top', 'bottom', or 'center'. */ originY: VerticalConnectionPos; + /** X-axis attachment point for connected overlay. Can be 'start', 'end', or 'center'. */ overlayX: HorizontalConnectionPos; + /** Y-axis attachment point for connected overlay. Can be 'top', 'bottom', or 'center'. */ overlayY: VerticalConnectionPos; constructor( @@ -68,6 +72,8 @@ export class ConnectionPositionPair { * | Scrollable | * | | * -------------------------- + * + * @docs-private */ export class ScrollingVisibility { isOriginClipped: boolean; @@ -78,6 +84,9 @@ export class ScrollingVisibility { /** The change event emitted by the strategy when a fallback position is used. */ export class ConnectedOverlayPositionChange { - constructor(public connectionPair: ConnectionPositionPair, - @Optional() public scrollableViewProperties: ScrollingVisibility) {} + constructor( + /** The position used as a result of this change. */ + public connectionPair: ConnectionPositionPair, + /** @docs-private */ + @Optional() public scrollableViewProperties: ScrollingVisibility) {} } diff --git a/src/cdk/overlay/scroll/block-scroll-strategy.ts b/src/cdk/overlay/scroll/block-scroll-strategy.ts index a1701c5c0c9b..df517a41436b 100644 --- a/src/cdk/overlay/scroll/block-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/block-scroll-strategy.ts @@ -19,8 +19,10 @@ export class BlockScrollStrategy implements ScrollStrategy { constructor(private _viewportRuler: ViewportRuler) { } + /** Attaches this scroll strategy to an overlay. */ attach() { } + /** Blocks page-level scroll while the attached overlay is open. */ enable() { if (this._canBeEnabled()) { const root = document.documentElement; @@ -40,6 +42,7 @@ export class BlockScrollStrategy implements ScrollStrategy { } } + /** Unblocks page-level scroll while the attached overlay is open. */ disable() { if (this._isEnabled) { this._isEnabled = false; diff --git a/src/cdk/overlay/scroll/close-scroll-strategy.ts b/src/cdk/overlay/scroll/close-scroll-strategy.ts index 1f8a328b163e..e7defe7942da 100644 --- a/src/cdk/overlay/scroll/close-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/close-scroll-strategy.ts @@ -21,6 +21,7 @@ export class CloseScrollStrategy implements ScrollStrategy { constructor(private _scrollDispatcher: ScrollDispatcher) { } + /** Attaches this scroll strategy to an overlay. */ attach(overlayRef: OverlayRef) { if (this._overlayRef) { throw getMatScrollStrategyAlreadyAttachedError(); @@ -29,6 +30,7 @@ export class CloseScrollStrategy implements ScrollStrategy { this._overlayRef = overlayRef; } + /** Enables the closing of the attached on scroll. */ enable() { if (!this._scrollSubscription) { this._scrollSubscription = this._scrollDispatcher.scrolled(0).subscribe(() => { @@ -41,6 +43,7 @@ export class CloseScrollStrategy implements ScrollStrategy { } } + /** Disables the closing the attached overlay on scroll. */ disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); diff --git a/src/cdk/overlay/scroll/noop-scroll-strategy.ts b/src/cdk/overlay/scroll/noop-scroll-strategy.ts index 1aa1eac9e0fa..ab9baf6bd633 100644 --- a/src/cdk/overlay/scroll/noop-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/noop-scroll-strategy.ts @@ -8,11 +8,12 @@ import {ScrollStrategy} from './scroll-strategy'; -/** - * Scroll strategy that doesn't do anything. - */ +/** Scroll strategy that doesn't do anything. */ export class NoopScrollStrategy implements ScrollStrategy { + /** Does nothing, as this scroll strategy is a no-op. */ enable() { } + /** Does nothing, as this scroll strategy is a no-op. */ disable() { } + /** Does nothing, as this scroll strategy is a no-op. */ attach() { } } diff --git a/src/cdk/overlay/scroll/reposition-scroll-strategy.ts b/src/cdk/overlay/scroll/reposition-scroll-strategy.ts index f4d0468dcb66..716adec4c2c3 100644 --- a/src/cdk/overlay/scroll/reposition-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/reposition-scroll-strategy.ts @@ -29,6 +29,7 @@ export class RepositionScrollStrategy implements ScrollStrategy { private _scrollDispatcher: ScrollDispatcher, private _config?: RepositionScrollStrategyConfig) { } + /** Attaches this scroll strategy to an overlay. */ attach(overlayRef: OverlayRef) { if (this._overlayRef) { throw getMatScrollStrategyAlreadyAttachedError(); @@ -37,6 +38,7 @@ export class RepositionScrollStrategy implements ScrollStrategy { this._overlayRef = overlayRef; } + /** Enables repositioning of the attached overlay on scroll. */ enable() { if (!this._scrollSubscription) { let throttle = this._config ? this._config.scrollThrottle : 0; @@ -47,6 +49,7 @@ export class RepositionScrollStrategy implements ScrollStrategy { } } + /** Disables repositioning of the attached overlay on scroll. */ disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); diff --git a/src/cdk/portal/dom-portal-host.ts b/src/cdk/portal/dom-portal-host.ts index f39b09d7b824..19b475a17302 100644 --- a/src/cdk/portal/dom-portal-host.ts +++ b/src/cdk/portal/dom-portal-host.ts @@ -19,8 +19,6 @@ import {BasePortalHost, ComponentPortal, TemplatePortal} from './portal'; /** * A PortalHost for attaching portals to an arbitrary DOM element outside of the Angular * application context. - * - * This is the only part of the portal core that directly touches the DOM. */ export class DomPortalHost extends BasePortalHost { constructor( @@ -34,6 +32,7 @@ export class DomPortalHost extends BasePortalHost { /** * Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. * @param portal Portal to be attached + * @returns Reference to the created component. */ attachComponentPortal(portal: ComponentPortal): ComponentRef { let componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component); @@ -68,6 +67,7 @@ export class DomPortalHost extends BasePortalHost { /** * Attaches a template portal to the DOM as an embedded view. * @param portal Portal to be attached. + * @returns Reference to the created embedded view. */ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { let viewContainer = portal.viewContainerRef; diff --git a/src/cdk/portal/portal-directives.ts b/src/cdk/portal/portal-directives.ts index 9f4b9e217067..f3da22329cb0 100644 --- a/src/cdk/portal/portal-directives.ts +++ b/src/cdk/portal/portal-directives.ts @@ -23,11 +23,6 @@ import {Portal, TemplatePortal, ComponentPortal, BasePortalHost} from './portal' /** * Directive version of a `TemplatePortal`. Because the directive *is* a TemplatePortal, * the directive instance itself can be attached to a host, enabling declarative use of portals. - * - * Usage: - * - *

Hello {{name}}

- *
*/ @Directive({ selector: '[cdk-portal], [cdkPortal], [portal]', @@ -93,6 +88,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { * Attach the given ComponentPortal to this PortalHost using the ComponentFactoryResolver. * * @param portal Portal to be attached to the portal host. + * @returns Reference to the created component. */ attachComponentPortal(portal: ComponentPortal): ComponentRef { portal.setAttachedHost(this); @@ -118,6 +114,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { /** * Attach the given TemplatePortal to this PortlHost as an embedded View. * @param portal Portal to be attached. + * @returns Reference to the created embedded view. */ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { portal.setAttachedHost(this); diff --git a/src/cdk/portal/portal.md b/src/cdk/portal/portal.md index 79910d6c3371..cb7503b378fc 100644 --- a/src/cdk/portal/portal.md +++ b/src/cdk/portal/portal.md @@ -1,3 +1,5 @@ +The `portals` package provides a flexible system for rendering dynamic content into an application. + ### Portals A `Portal `is a piece of UI that can be dynamically rendered to an open slot on the page. @@ -30,7 +32,7 @@ Used to get a portal from an ``. `TemplatePortalDirectives` *is* a Usage: ```html - +

The content of this template is captured by the portal.

diff --git a/src/cdk/portal/portal.ts b/src/cdk/portal/portal.ts index 8352e5b31586..e0b781a64e00 100644 --- a/src/cdk/portal/portal.ts +++ b/src/cdk/portal/portal.ts @@ -161,8 +161,8 @@ export interface PortalHost { /** - * Partial implementation of PortalHost that only deals with attaching either a - * ComponentPortal or a TemplatePortal. + * Partial implementation of PortalHost that handles attaching + * ComponentPortal and TemplatePortal. */ export abstract class BasePortalHost implements PortalHost { /** The portal currently attached to the host. */ @@ -179,6 +179,7 @@ export abstract class BasePortalHost implements PortalHost { return !!this._attachedPortal; } + /** Attaches a portal. */ attach(portal: Portal): any { if (!portal) { throwNullPortalError(); @@ -207,6 +208,7 @@ export abstract class BasePortalHost implements PortalHost { abstract attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef; + /** Detaches a previously attached portal. */ detach(): void { if (this._attachedPortal) { this._attachedPortal.setAttachedHost(null); @@ -216,7 +218,8 @@ export abstract class BasePortalHost implements PortalHost { this._invokeDisposeFn(); } - dispose() { + /** Permanently dispose of this portal host. */ + dispose(): void { if (this.hasAttached()) { this.detach(); } @@ -225,6 +228,7 @@ export abstract class BasePortalHost implements PortalHost { this._isDisposed = true; } + /** @docs-private */ setDisposeFn(fn: () => void) { this._disposeFn = fn; } diff --git a/src/cdk/scrolling/scrolling.md b/src/cdk/scrolling/scrolling.md index 7db6c4e046aa..67adae01ec95 100644 --- a/src/cdk/scrolling/scrolling.md +++ b/src/cdk/scrolling/scrolling.md @@ -1,3 +1,10 @@ -### Scrolling +The `scrolling` package provides helpers for directives that react to scroll events. -Some things to help with scrollling. \ No newline at end of file +### cdkScrollable and ScrollDispatcher +The `cdkScrollable` directive and the `ScrollDispatcher` service to together to allow components to +react to scrolling in any of its ancestor scrolling containers. + +The `cdkScrollable` directive should be applied to any element that acts as a scrolling container. +This marks the element as a `Scrollable` and registers it with the `ScrollDispatcher`. The +dispatcher, then, allows components to share both event listeners and knowledge of all of the +scrollable containers in the application. diff --git a/src/cdk/stepper/step-label.ts b/src/cdk/stepper/step-label.ts index 974c3800165b..a7f048239890 100644 --- a/src/cdk/stepper/step-label.ts +++ b/src/cdk/stepper/step-label.ts @@ -12,5 +12,5 @@ import {Directive, TemplateRef} from '@angular/core'; selector: '[cdkStepLabel]', }) export class CdkStepLabel { - constructor(public template: TemplateRef) { } + constructor(/** @docs-private */ public template: TemplateRef) { } } diff --git a/src/cdk/stepper/stepper.md b/src/cdk/stepper/stepper.md new file mode 100644 index 000000000000..6e5fe2f7f511 --- /dev/null +++ b/src/cdk/stepper/stepper.md @@ -0,0 +1,59 @@ +CDK stepper provides a foundation upon which more concrete stepper varities can be built. A +stepper is a wizard-like workflow that divides content into logical steps + +### Behavior captured by CdkStepper +The base CDK version of the stepper primarily manages which step is active. This includes handling +keyboard interactions and exposing an API for advancing or rewinding through the workflow. + +#### Linear stepper +A stepper marked as `linear` requires the user to complete previous steps before proceeding. +For each step, the `stepControl` attribute can be set to the top level +`AbstractControl` that is used to check the validity of the step. + +There are two possible approaches. One is using a single form for stepper, and the other is +using a different form for each step. + +#### Using a single form for the entire stepper +When using a single form for the stepper, any intermediate next/previous buttons within the steps +must be set to `type="button"` in order to prevent submission of the form before all steps are +complete. + +#### Using a form for each individual step +When using a form for each step, the workflow is advanced whenever one of the forms is submitted. + +### Types of steps + +#### Optional step +If completion of a step in linear stepper is not required, then the `optional` attribute can be set +on `CdkStep` in a `linear` stepper. + +#### Editable step +By default, steps are editable, which means users can return to previously completed steps and +edit their responses. `editable="true"` can be set on `CdkStep` to change the default. + +#### Completed step +By default, the `completed` attribute of a step returns `true` if the step is valid (in case of +linear stepper) and the user has interacted with the step. The user, however, can also override +this default `completed` behavior by setting the `completed` attribute as needed. + +### Stepper buttons +There are two button directives to support navigation between different steps: +`CdkStepperNext` and `CdkStepperPrevious`. When placed inside of a step, these will automatically +add click handlers to advance or rewind the workflow, respectively. + +### Keyboard interaction +- LEFT_ARROW: Focuses the previous step header +- RIGHT_ARROW: Focuses the next step header +- ENTER, SPACE: Selects the step that the focus is currently on +- TAB: Focuses the next tabbable element +- TAB+SHIFT: Focuses the previous tabbable element + +### Accessibility +The CDK stepper is treated as a tabbed view for accessibility purposes, so it is given +`role="tablist"` by default. The header of step that can be clicked to select the step +is given `role="tab"`, and the content that can be expanded upon selection is given +`role="tabpanel"`. `aria-selected` attribute of step header and `aria-expanded` attribute of +step content is automatically set based on step selection change. + +The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`. + diff --git a/src/cdk/stepper/stepper.ts b/src/cdk/stepper/stepper.ts index 9ce5937309ca..08ac87e971ec 100644 --- a/src/cdk/stepper/stepper.ts +++ b/src/cdk/stepper/stepper.ts @@ -84,27 +84,28 @@ export class CdkStep implements OnChanges { /** Label of the step. */ @Input() label: string; + /** Whether the user can return to this step once it has been marked as complted. */ @Input() - get editable() { return this._editable; } - set editable(value: any) { + get editable(): boolean { return this._editable; } + set editable(value: boolean) { this._editable = coerceBooleanProperty(value); } private _editable = true; - /** Whether the completion of step is optional or not. */ + /** Whether the completion of step is optional. */ @Input() - get optional() { return this._optional; } - set optional(value: any) { + get optional(): boolean { return this._optional; } + set optional(value: boolean) { this._optional = coerceBooleanProperty(value); } private _optional = false; - /** Return whether step is completed or not. */ + /** Whether step is marked as completed. */ @Input() - get completed() { + get completed(): boolean { return this._customCompleted == null ? this._defaultCompleted : this._customCompleted; } - set completed(value: any) { + set completed(value: boolean) { this._customCompleted = coerceBooleanProperty(value); } private _customCompleted: boolean | null = null; @@ -140,8 +141,8 @@ export class CdkStepper { /** Whether the validity of previous steps should be checked or not. */ @Input() - get linear() { return this._linear; } - set linear(value: any) { this._linear = coerceBooleanProperty(value); } + get linear(): boolean { return this._linear; } + set linear(value: boolean) { this._linear = coerceBooleanProperty(value); } private _linear = false; /** The index of the selected step. */ diff --git a/src/cdk/table/cell.ts b/src/cdk/table/cell.ts index fc677ead759b..d0d735e30176 100644 --- a/src/cdk/table/cell.ts +++ b/src/cdk/table/cell.ts @@ -14,7 +14,7 @@ import {ContentChild, Directive, ElementRef, Input, Renderer2, TemplateRef} from */ @Directive({selector: '[cdkCellDef]'}) export class CdkCellDef { - constructor(public template: TemplateRef) { } + constructor(/** @docs-private */ public template: TemplateRef) { } } /** @@ -23,7 +23,7 @@ export class CdkCellDef { */ @Directive({selector: '[cdkHeaderCellDef]'}) export class CdkHeaderCellDef { - constructor(public template: TemplateRef) { } + constructor(/** @docs-private */ public template: TemplateRef) { } } /** diff --git a/src/cdk/table/row.ts b/src/cdk/table/row.ts index 56864ae274c0..3a148a775951 100644 --- a/src/cdk/table/row.ts +++ b/src/cdk/table/row.ts @@ -37,7 +37,7 @@ export abstract class BaseRowDef { /** Differ used to check if any changes were made to the columns. */ protected _columnsDiffer: IterableDiffer; - constructor(public template: TemplateRef, + constructor(/** @docs-private */ public template: TemplateRef, protected _differs: IterableDiffers) { } ngOnChanges(changes: SimpleChanges): void { diff --git a/src/cdk/table/table.ts b/src/cdk/table/table.ts index eb70b25f5fd2..47477eac1adf 100644 --- a/src/cdk/table/table.ts +++ b/src/cdk/table/table.ts @@ -110,6 +110,7 @@ export class CdkTable implements CollectionViewer { * Accepts a function that takes two parameters, `index` and `item`. */ @Input() + get trackBy(): TrackByFunction { return this._trackByFn; } set trackBy(fn: TrackByFunction) { if (isDevMode() && fn != null && typeof fn !== 'function' && @@ -118,7 +119,6 @@ export class CdkTable implements CollectionViewer { } this._trackByFn = fn; } - get trackBy(): TrackByFunction { return this._trackByFn; } private _trackByFn: TrackByFunction; /** diff --git a/tools/dgeni/processors/component-grouper.ts b/tools/dgeni/processors/component-grouper.ts index 2564ee3e9485..3e6d1ade69cf 100644 --- a/tools/dgeni/processors/component-grouper.ts +++ b/tools/dgeni/processors/component-grouper.ts @@ -8,6 +8,15 @@ export class ComponentGroup { /** Name of the component group. */ name: string; + /** Display name of the component group */ + displayName: string; + + /** Name of the package, either material or cdk */ + packageName: string; + + /** Display name of the package. */ + packageDisplayName: string; + /** Unique id for the component group. */ id: string; @@ -60,7 +69,17 @@ export class ComponentGrouper implements Processor { // All of the component documentation is under either `src/lib` or `src/cdk`. // We group the docs up by the directory immediately under that root. - const groupName = path.relative(basePath, filePath).split(path.sep)[1]; + let packageName, packageDisplayName; + if (filePath.includes('cdk')) { + packageName = 'cdk'; + packageDisplayName = 'CDK'; + } else { + packageName = 'material'; + packageDisplayName = 'Material'; + } + + const displayName = path.relative(basePath, filePath).split(path.sep)[1]; + const groupName = packageName + '-' + displayName; // Get the group for this doc, or, if one does not exist, create it. let group; @@ -71,6 +90,10 @@ export class ComponentGrouper implements Processor { groups.set(groupName, group); } + group.packageName = packageName; + group.packageDisplayName = packageDisplayName; + group.displayName = displayName; + // Put this doc into the appropriate list in this group. if (doc.isDirective) { group.directives.push(doc); diff --git a/tools/dgeni/templates/componentGroup.template.html b/tools/dgeni/templates/componentGroup.template.html index 287b980f0c7c..61532398bec8 100644 --- a/tools/dgeni/templates/componentGroup.template.html +++ b/tools/dgeni/templates/componentGroup.template.html @@ -21,12 +21,12 @@

- API reference for Angular Material {$ doc.name $} + API reference for Angular {$ doc.packageDisplayName $} {$ doc.displayName $}

- import {{$ doc.ngModule.name $}} from '@angular/material'; + import {{$ doc.ngModule.name $}} from '@angular/{$ doc.packageName $}';

diff --git a/tools/dgeni/templates/method.template.html b/tools/dgeni/templates/method.template.html index 50b43d1223e6..8a79b1e18b4d 100644 --- a/tools/dgeni/templates/method.template.html +++ b/tools/dgeni/templates/method.template.html @@ -49,7 +49,7 @@ - {$ method.returnType $} + {$ method.type $}

diff --git a/tools/gulp/tasks/docs.ts b/tools/gulp/tasks/docs.ts index c87425db2401..e17749077865 100644 --- a/tools/gulp/tasks/docs.ts +++ b/tools/gulp/tasks/docs.ts @@ -59,9 +59,23 @@ const htmlMinifierOptions = { removeAttributeQuotes: false }; +const markdownOptions = { + // Add syntax highlight using highlight.js + highlight: (code: string, language: string): string => { + if (language) { + // highlight.js expects "typescript" written out, while Github supports "ts". + let lang = language.toLowerCase() === 'ts' ? 'typescript' : language; + return hljs.highlight(lang, code).value; + } + + return code; + } +}; + /** Generate all docs content. */ task('docs', [ 'markdown-docs', + 'markdown-docs-cdk', 'highlight-examples', 'api-docs', 'minified-api-docs', @@ -69,7 +83,7 @@ task('docs', [ 'plunker-example-assets', ]); -/** Generates html files from the markdown overviews and guides. */ +/** Generates html files from the markdown overviews and guides for material. */ task('markdown-docs', () => { // Extend the renderer for custom heading anchor rendering markdown.marked.Renderer.prototype.heading = (text: string, level: number): string => { @@ -86,19 +100,21 @@ task('markdown-docs', () => { } }; - return src(['src/lib/**/*.md', 'src/cdk/**/*.md', 'guides/*.md']) - .pipe(markdown({ - // Add syntax highlight using highlight.js - highlight: (code: string, language: string): string => { - if (language) { - // highlight.js expects "typescript" written out, while Github supports "ts". - let lang = language.toLowerCase() === 'ts' ? 'typescript' : language; - return hljs.highlight(lang, code).value; - } - - return code; - } - })) + return src(['src/lib/**/*.md', 'guides/*.md']) + .pipe(rename({prefix: 'material-'})) + .pipe(markdown(markdownOptions)) + .pipe(transform(transformMarkdownFiles)) + .pipe(dom(createTagNameAliaser('docs-markdown'))) + .pipe(dest('dist/docs/markdown')); +}); + +// TODO(jelbourn): figure out how to avoid duplicating this task w/ material while still +// disambiguating the output. +/** Generates html files from the markdown overviews and guides for the cdk. */ +task('markdown-docs-cdk', () => { + return src(['src/cdk/**/*.md']) + .pipe(rename({prefix: 'cdk-'})) + .pipe(markdown(markdownOptions)) .pipe(transform(transformMarkdownFiles)) .pipe(dom(createTagNameAliaser('docs-markdown'))) .pipe(dest('dist/docs/markdown'));