Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

feat(snackbar): Convert JS to TypeScript #4363

Merged
merged 10 commits into from
Feb 9, 2019
3 changes: 1 addition & 2 deletions packages/mdc-list/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
*/

/**
* Adapter for MDC List. Provides an interface for managing focus.
*
* Defines the shape of the adapter expected by the foundation.
* Implement this adapter for your framework of choice to delegate updates to
* the component in your framework of choice. See architecture documentation
* for more details.
Expand Down
16 changes: 8 additions & 8 deletions packages/mdc-snackbar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ Event Name | `event.detail` | Description
--- | --- | ---
`MDCSnackbar:opening` | `{}` | Indicates when the snackbar begins its opening animation.
`MDCSnackbar:opened` | `{}` | Indicates when the snackbar finishes its opening animation.
`MDCSnackbar:closing` | `{reason: ?string}` | Indicates when the snackbar begins its closing animation. `reason` contains the reason why the snackbar closed (`dismiss` or `action`).
`MDCSnackbar:closed` | `{reason: ?string}` | Indicates when the snackbar finishes its closing animation. `reason` contains the reason why the snackbar closed (`dismiss` or `action`).
`MDCSnackbar:closing` | `{reason?: string}` | Indicates when the snackbar begins its closing animation. `reason` contains the reason why the snackbar closed (`'dismiss'`, `'action'`, or `undefined`).
`MDCSnackbar:closed` | `{reason?: string}` | Indicates when the snackbar finishes its closing animation. `reason` contains the reason why the snackbar closed (`'dismiss'`, `'action'`, or `undefined`).

### Usage Within Frameworks

Expand All @@ -202,8 +202,8 @@ Method Signature | Description
`announce() => void` | Announces the snackbar's label text to screen reader users.
`notifyOpening() => void` | Broadcasts an event denoting that the snackbar has just started opening.
`notifyOpened() => void` | Broadcasts an event denoting that the snackbar has finished opening.
`notifyClosing(reason: string) {}` | Broadcasts an event denoting that the snackbar has just started closing. If a non-empty `reason` is passed, the event's `detail` object should include its value in the `reason` property.
`notifyClosed(reason: string) {}` | Broadcasts an event denoting that the snackbar has finished closing. If a non-empty `reason` is passed, the event's `detail` object should include its value in the `reason` property.
`notifyClosing(reason: string) => void` | Broadcasts an event denoting that the snackbar has just started closing. If a non-empty `reason` is passed, the event's `detail` object should include its value in the `reason` property.
`notifyClosed(reason: string) => void` | Broadcasts an event denoting that the snackbar has finished closing. If a non-empty `reason` is passed, the event's `detail` object should include its value in the `reason` property.

#### `MDCSnackbarFoundation` Methods

Expand All @@ -216,9 +216,9 @@ Method Signature | Description
`setTimeoutMs(timeoutMs: number)` | Sets the automatic dismiss timeout in milliseconds. Value must be between `4000` and `10000` or an error will be thrown.
`getCloseOnEscape() => boolean` | Returns whether the snackbar closes when it is focused and the user presses the <kbd>ESC</kbd> key.
`setCloseOnEscape(closeOnEscape: boolean) => void` | Sets whether the snackbar closes when it is focused and the user presses the <kbd>ESC</kbd> key.
`handleKeyDown(event: !KeyEvent)` | Handles `keydown` events on or within the snackbar's root element.
`handleActionButtonClick(event: !MouseEvent)` | Handles `click` events on or within the action button.
`handleActionIconClick(event: !MouseEvent)` | Handles `click` events on or within the dismiss icon.
`handleKeyDown(event: KeyEvent)` | Handles `keydown` events on or within the snackbar's root element.
`handleActionButtonClick(event: MouseEvent)` | Handles `click` events on or within the action button.
`handleActionIconClick(event: MouseEvent)` | Handles `click` events on or within the dismiss icon.

#### Event Handlers

Expand All @@ -236,7 +236,7 @@ External frameworks and libraries can use the following utility methods from the

Method Signature | Description
--- | ---
`announce(ariaEl: !HTMLElement, labelEl: !HTMLElement=) => void` | Announces the label text to screen reader users.
`announce(ariaEl: Element, labelEl?: Element) => void` | Announces the label text to screen reader users.

> Alternatively, frameworks can use [Closure Library's `goog.a11y.aria.Announcer#say()` method](https://github.com/google/closure-library/blob/bee9ced776b4700e8076a3466bd9d3f9ade2fb54/closure/goog/a11y/aria/announcer.js#L80).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,21 @@
* THE SOFTWARE.
*/

/* eslint no-unused-vars: [2, {"args": "none"}] */

/**
* Adapter for MDC Snackbar. Provides an interface for managing:
* - CSS classes
* - Event handlers
*
* Additionally, provides type information for the adapter to the Closure
* compiler.
*
* Defines the shape of the adapter expected by the foundation.
* Implement this adapter for your framework of choice to delegate updates to
* the component in your framework of choice. See architecture documentation
* for more details.
* https://github.com/material-components/material-components-web/blob/master/docs/code/architecture.md
*
* @record
*/
class MDCSnackbarAdapter {
/** @param {string} className */
addClass(className) {}

/** @param {string} className */
removeClass(className) {}

announce() {}

notifyOpening() {}
notifyOpened() {}

/**
* @param {string} reason
*/
notifyClosing(reason) {}

/**
* @param {string} reason
*/
notifyClosed(reason) {}
interface MDCSnackbarAdapter {
addClass(className: string): void;
announce(): void;
notifyClosed(reason: string): void;
notifyClosing(reason: string): void;
notifyOpened(): void;
notifyOpening(): void;
removeClass(className: string): void;
}

export default MDCSnackbarAdapter;
export {MDCSnackbarAdapter as default, MDCSnackbarAdapter};
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,33 @@
*/

const cssClasses = {
OPENING: 'mdc-snackbar--opening',
OPEN: 'mdc-snackbar--open',
CLOSING: 'mdc-snackbar--closing',
OPEN: 'mdc-snackbar--open',
OPENING: 'mdc-snackbar--opening',
};

const strings = {
SURFACE_SELECTOR: '.mdc-snackbar__surface',
LABEL_SELECTOR: '.mdc-snackbar__label',
ACTION_SELECTOR: '.mdc-snackbar__action',
ARIA_LIVE_LABEL_TEXT_ATTR: 'data-mdc-snackbar-label-text',
CLOSED_EVENT: 'MDCSnackbar:closed',
CLOSING_EVENT: 'MDCSnackbar:closing',
DISMISS_SELECTOR: '.mdc-snackbar__dismiss',

OPENING_EVENT: 'MDCSnackbar:opening',
LABEL_SELECTOR: '.mdc-snackbar__label',
OPENED_EVENT: 'MDCSnackbar:opened',
CLOSING_EVENT: 'MDCSnackbar:closing',
CLOSED_EVENT: 'MDCSnackbar:closed',

OPENING_EVENT: 'MDCSnackbar:opening',
REASON_ACTION: 'action',
REASON_DISMISS: 'dismiss',

ARIA_LIVE_LABEL_TEXT_ATTR: 'data-mdc-snackbar-label-text',
SURFACE_SELECTOR: '.mdc-snackbar__surface',
};

const numbers = {
MIN_AUTO_DISMISS_TIMEOUT_MS: 4000,
MAX_AUTO_DISMISS_TIMEOUT_MS: 10000,
DEFAULT_AUTO_DISMISS_TIMEOUT_MS: 5000,
MAX_AUTO_DISMISS_TIMEOUT_MS: 10000,
MIN_AUTO_DISMISS_TIMEOUT_MS: 4000,

// These variables need to be kept in sync with the values in _variables.scss.
SNACKBAR_ANIMATION_OPEN_TIME_MS: 150,
SNACKBAR_ANIMATION_CLOSE_TIME_MS: 75,
SNACKBAR_ANIMATION_OPEN_TIME_MS: 150,

/**
* Number of milliseconds to wait between temporarily clearing the label text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,14 @@
* THE SOFTWARE.
*/

/* eslint no-unused-vars: ["error", {"argsIgnorePattern": "evt", "varsIgnorePattern": "Adapter$"}] */

import {MDCFoundation} from '@material/base/foundation';
import MDCSnackbarAdapter from './adapter';
import {MDCSnackbarAdapter} from './adapter';
import {cssClasses, numbers, strings} from './constants';

const {OPENING, OPEN, CLOSING} = cssClasses;
const {REASON_ACTION, REASON_DISMISS} = strings;

class MDCSnackbarFoundation extends MDCFoundation {
class MDCSnackbarFoundation extends MDCFoundation<MDCSnackbarAdapter> {
static get cssClasses() {
return cssClasses;
}
Expand All @@ -43,44 +41,27 @@ class MDCSnackbarFoundation extends MDCFoundation {
return numbers;
}

/**
* @return {!MDCSnackbarAdapter}
*/
static get defaultAdapter() {
return /** @type {!MDCSnackbarAdapter} */ ({
addClass: (/* className: string */) => {},
removeClass: (/* className: string */) => {},
announce: () => {},
notifyOpening: () => {},
notifyOpened: () => {},
notifyClosing: (/* reason: string */) => {},
notifyClosed: (/* reason: string */) => {},
});
static get defaultAdapter(): MDCSnackbarAdapter {
return {
addClass: () => undefined,
announce: () => undefined,
notifyClosed: () => undefined,
notifyClosing: () => undefined,
notifyOpened: () => undefined,
notifyOpening: () => undefined,
removeClass: () => undefined,
};
}

/**
* @param {!MDCSnackbarAdapter=} adapter
*/
constructor(adapter) {
super(Object.assign(MDCSnackbarFoundation.defaultAdapter, adapter));

/** @private {boolean} */
this.isOpen_ = false;
private isOpen_ = false;
private animationFrame_ = 0;
private animationTimer_ = 0;
private autoDismissTimer_ = 0;
private autoDismissTimeoutMs_ = numbers.DEFAULT_AUTO_DISMISS_TIMEOUT_MS;
private closeOnEscape_ = true;

/** @private {number} */
this.animationFrame_ = 0;

/** @private {number} */
this.animationTimer_ = 0;

/** @private {number} */
this.autoDismissTimer_ = 0;

/** @private {number} */
this.autoDismissTimeoutMs_ = numbers.DEFAULT_AUTO_DISMISS_TIMEOUT_MS;

/** @private {boolean} */
this.closeOnEscape_ = true;
constructor(adapter?: MDCSnackbarAdapter) {
super(Object.assign(MDCSnackbarFoundation.defaultAdapter, adapter));
}

destroy() {
Expand Down Expand Up @@ -117,7 +98,7 @@ class MDCSnackbarFoundation extends MDCFoundation {
}

/**
* @param {string=} reason Why the snackbar was closed. Value will be passed to CLOSING_EVENT and CLOSED_EVENT via the
* @param reason Why the snackbar was closed. Value will be passed to CLOSING_EVENT and CLOSED_EVENT via the
* `event.detail.reason` property. Standard values are REASON_ACTION and REASON_DISMISS, but custom
* client-specific values may also be used if desired.
*/
Expand All @@ -144,24 +125,15 @@ class MDCSnackbarFoundation extends MDCFoundation {
}, numbers.SNACKBAR_ANIMATION_CLOSE_TIME_MS);
}

/**
* @return {boolean}
*/
isOpen() {
isOpen(): boolean {
return this.isOpen_;
}

/**
* @return {number}
*/
getTimeoutMs() {
getTimeoutMs(): number {
return this.autoDismissTimeoutMs_;
}

/**
* @param {number} timeoutMs
*/
setTimeoutMs(timeoutMs) {
setTimeoutMs(timeoutMs: number) {
// Use shorter variable names to make the code more readable
const minValue = numbers.MIN_AUTO_DISMISS_TIMEOUT_MS;
const maxValue = numbers.MAX_AUTO_DISMISS_TIMEOUT_MS;
Expand All @@ -173,62 +145,44 @@ class MDCSnackbarFoundation extends MDCFoundation {
}
}

/**
* @return {boolean}
*/
getCloseOnEscape() {
getCloseOnEscape(): boolean {
return this.closeOnEscape_;
}

/**
* @param {boolean} closeOnEscape
*/
setCloseOnEscape(closeOnEscape) {
setCloseOnEscape(closeOnEscape: boolean) {
this.closeOnEscape_ = closeOnEscape;
}

/**
* @param {!KeyboardEvent} evt
*/
handleKeyDown(evt) {
if (this.getCloseOnEscape() && (evt.key === 'Escape' || evt.keyCode === 27)) {
handleKeyDown(evt: KeyboardEvent) {
const isEscapeKey = evt.key === 'Escape' || evt.keyCode === 27;
if (isEscapeKey && this.getCloseOnEscape()) {
this.close(REASON_DISMISS);
}
}

/**
* @param {!MouseEvent} evt
*/
handleActionButtonClick(evt) {
handleActionButtonClick(_evt: MouseEvent) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you remove this argument since you're not using it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered doing removing it, but I thought it would be more future-proof to keep it.

If we end up needing to inspect the event object in the future—e.g., to avoid taking an action if the shift/meta keys are pressed—we won't need to introduce a breaking change, because we'll already have access to the event.

Removing the param would technically be a breaking change too, so we'd have to create a separate PR to make the change in master as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah... from the perspective of this being a straight conversion, might as well leave it, since I guess it was unused even on master then, right?

this.close(REASON_ACTION);
}

/**
* @param {!MouseEvent} evt
*/
handleActionIconClick(evt) {
handleActionIconClick(_evt: MouseEvent) {
this.close(REASON_DISMISS);
}

/** @private */
clearAutoDismissTimer_() {
private clearAutoDismissTimer_() {
clearTimeout(this.autoDismissTimer_);
this.autoDismissTimer_ = 0;
}

/** @private */
handleAnimationTimerEnd_() {
private handleAnimationTimerEnd_() {
this.animationTimer_ = 0;
this.adapter_.removeClass(cssClasses.OPENING);
this.adapter_.removeClass(cssClasses.CLOSING);
}

/**
* Runs the given logic on the next animation frame, using setTimeout to factor in Firefox reflow behavior.
* @param {Function} callback
* @private
*/
runNextAnimationFrame_(callback) {
private runNextAnimationFrame_(callback: () => void) {
cancelAnimationFrame(this.animationFrame_);
this.animationFrame_ = requestAnimationFrame(() => {
this.animationFrame_ = 0;
Expand All @@ -238,4 +192,4 @@ class MDCSnackbarFoundation extends MDCFoundation {
}
}

export default MDCSnackbarFoundation;
export {MDCSnackbarFoundation as default, MDCSnackbarFoundation};
Loading