diff --git a/packages/material-components-web/index.js b/packages/material-components-web/index.js
index 883f1c20042..21e14e68bf4 100644
--- a/packages/material-components-web/index.js
+++ b/packages/material-components-web/index.js
@@ -17,6 +17,7 @@
import * as base from '@material/base';
import * as checkbox from '@material/checkbox';
import * as formField from '@material/form-field';
+import * as gridList from '@material/grid-list';
import * as iconToggle from '@material/icon-toggle';
import * as radio from '@material/radio';
import * as ripple from '@material/ripple';
@@ -31,6 +32,7 @@ import autoInit from '@material/auto-init';
autoInit.register('MDCCheckbox', checkbox.MDCCheckbox);
autoInit.register('MDCTemporaryDrawer', drawer.MDCTemporaryDrawer);
autoInit.register('MDCRipple', ripple.MDCRipple);
+autoInit.register('MDCGridList', gridList.MDCGridList);
autoInit.register('MDCIconToggle', iconToggle.MDCIconToggle);
autoInit.register('MDCRadio', radio.MDCRadio);
autoInit.register('MDCSnackbar', snackbar.MDCSnackbar);
@@ -43,6 +45,7 @@ export {
base,
checkbox,
formField,
+ gridList,
iconToggle,
radio,
ripple,
diff --git a/packages/material-components-web/material-components-web.scss b/packages/material-components-web/material-components-web.scss
index 06a2ecd1d6a..4d5c691d6bf 100644
--- a/packages/material-components-web/material-components-web.scss
+++ b/packages/material-components-web/material-components-web.scss
@@ -22,6 +22,7 @@
@import "@material/elevation/mdc-elevation";
@import "@material/fab/mdc-fab";
@import "@material/form-field/mdc-form-field";
+@import "@material/grid-list/mdc-grid-list";
@import "@material/icon-toggle/mdc-icon-toggle";
@import "@material/layout-grid/mdc-layout-grid";
@import "@material/list/mdc-list";
diff --git a/packages/material-components-web/package.json b/packages/material-components-web/package.json
index 9d179f6a4e1..150f5719fda 100644
--- a/packages/material-components-web/package.json
+++ b/packages/material-components-web/package.json
@@ -23,6 +23,7 @@
"@material/elevation": "^0.1.3",
"@material/fab": "^0.3.3",
"@material/form-field": "^0.2.1",
+ "@material/grid-list": "^0.0.0",
"@material/icon-toggle": "^0.1.5",
"@material/layout-grid": "^0.1.1",
"@material/list": "^0.2.3",
diff --git a/packages/mdc-grid-list/README.md b/packages/mdc-grid-list/README.md
new file mode 100644
index 00000000000..6c655620702
--- /dev/null
+++ b/packages/mdc-grid-list/README.md
@@ -0,0 +1,264 @@
+# MDC Grid list
+
+MDC Grid list provides a RTL-aware Material Design Grid list component adhering to the
+[Material Design Grid list spec](https://material.io/guidelines/components/grid-lists.html).
+Grid Lists are best suited for presenting homogeneous data, typically images.
+Each item in a grid list is called a **tile**. Tiles maintain consistent width, height, and padding
+across screen sizes.
+
+
+## Installation
+
+```
+npm install --save @material/grid-list
+```
+
+
+## Usage
+
+Basic Grid list has the following structure:
+
+```html
+
+
+
+
+
+
+
+ Title
+
+
+
+
+
+
+
+ Title
+
+
+
+
+```
+
+The above markup will give you a Grid list of tiles that:
+
+- Have 4px padding in between themselves
+- Have a 1x1 aspect ratio
+- Have a one-line footer caption with no icon
+
+You just need to put the content you want to load in `src` of
+``. However, if your
+assets don't have the same aspect ratio you as specified in the tile, it will
+distort those assets. We provide a solution of that case in
+[Using a div in place of an img](#using-a-div-in-place-of-an-img) section.
+
+
+### Setting the tile width
+
+The tile width is set to 200px by default. There are three ways that you can
+overwrite the default value for your grid list:
+
+1. Using CSS variables
+
+ ```css
+ .mdc-grid-tile {
+ --mdc-grid-list-tile-width: 300px;
+ }
+ ```
+
+2. Overwriting SCSS variable
+
+ You can overwrite the scss variable by
+
+ ```scss
+ @import "@material/grid-list/mdc-grid-list";
+ $mdc-grid-list-tile-width: 300px;
+ ```
+
+3. Add own style to tile
+
+ ```html
+
+
+
+
+ ...
+
+
+ ```
+
+### Change tile padding
+
+Grid list tiles can have 1px padding instead of 4px by adding
+`mdc-grid-list--tile-gutter-1` modifier.
+
+```html
+
+
+ ...
+
+
+```
+
+### Image only tile
+
+Grid lists support image only tile. You can remove `mdc-grid-tile__secondary`
+and create a image only grid list.
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+### Header caption
+
+Grid lists support header caption. You can change the footer caption to be a
+header caption by adding `mdc-grid-list--header-caption` modifier.
+
+```html
+
+
+ ...
+
+
+```
+
+### Add support text to secondary content (caption)
+
+Grid lists support a one-line caption by default. You can add an additional line of support
+text if needed by adding the `mdc-grid-list--twoline-caption` modifier and additional
+markup
+
+```html
+
+
+
+
+
+
+
+ Title
+ Support text
+
+
+
+
+```
+
+### Add icon to secondary content (caption)
+
+You can add an icon to a caption by adding `mdc-grid-list--with-icon-align-start` or
+`mdc-grid-list--with-icon-align-end` and changing the markup.
+
+```html
+
+
+
+
+
+
+
+ star_border
+ Title
+
+
+
+
+```
+
+### Change aspect ratio of tile
+
+Grid list tiles support all material guideline recommended aspect ratio:
+
+- 1x1
+- 16x9
+- 2x3
+- 3x2
+- 4x3
+- 3x4
+
+You can use the modifier class `mdc-grid-list--tile-aspect-$ASPECT_RATIO` to apply these aspect
+ratios to your grid list. Simply replace `$ASPECT_RATIO` with any of the predefined ratios.
+
+```html
+
+
+
+ ...
+
+
+```
+
+As pointed out in the previous section, if your
+assets don't have the same aspect ratio you as specified in the tile, it will
+distort those assets. We provide a solution of that case in
+[Using a div in place of an img](#using-a-div-in-place-of-an-img) section.
+
+### Using a div in place of an img
+
+In case you cannot ensure all your assets will have the same aspect ratio, you
+can use `div` instead of `img` markup. It will resize the assets to cover the tile
+and crop the assets to display the center part.
+
+```html
+
+
+
+
+
+
+
+
+ Title
+
+
+
+
+```
+
+However, the method results in a less semantic markup, so we don't use this method by
+default.
+
+
+### RTL Support
+
+`mdc-grid-list` is automatically RTL-aware, and will re-position elements whenever
+it, or its ancestors, have a `dir="rtl"` attribute.
+
+
+### Theme
+
+`mdc-grid-list` supports theming. `mdc-grid-tile__primary` uses the theme's background
+color for its background color. `mdc-grid-tile__secondary` uses the theme's primary
+color for it's background color, and the theme's `text-primary-on-primary` color for its text color.
+
+### Using the Foundation Class
+
+MDCGridList ships with an `MDCGridListFoundation` class that external frameworks and libraries
+can use to build their own MDCGridList components with minimal effort. As with all foundation
+classes, an adapter object must be provided. The adapter for Grid list must provide the following
+functions, with correct signatures:
+
+| Method Signature | Description |
+| --- | --- |
+| `getOffsetWidth() => number` | Get root element `mdc-grid-list` offsetWidth. |
+| `getOffsetWidthForTileAtIndex(index: number) => number` | Get offsetWidth of `mdc-grid-tile` at specified index. |
+| `setStyleForTilesElement(property: string, value: number) => void` | Set `mdc-grid-list__tiles` style property to provided value. |
+| `registerResizeHandler(handler: Function) => void` | Registers a handler to be called when the surface (or its viewport) resizes. Our default implementation adds the handler as a listener to the window's `resize()` event. |
+| `deregisterResizeHandler(handler: Function) => void` | Unregisters a handler to be called when the surface (or its viewport) resizes. Our default implementation removes the handler as a listener to the window's `resize()` event. |
\ No newline at end of file
diff --git a/packages/mdc-grid-list/constants.js b/packages/mdc-grid-list/constants.js
new file mode 100644
index 00000000000..e766f0b624c
--- /dev/null
+++ b/packages/mdc-grid-list/constants.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export const strings = {
+ TILES_SELECTOR: '.mdc-grid-list__tiles',
+ TILE_SELECTOR: '.mdc-grid-tile',
+};
diff --git a/packages/mdc-grid-list/foundation.js b/packages/mdc-grid-list/foundation.js
new file mode 100644
index 00000000000..5748c652b4b
--- /dev/null
+++ b/packages/mdc-grid-list/foundation.js
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {MDCFoundation} from '@material/base';
+import {strings} from './constants';
+
+export default class MDCGridListFoundation extends MDCFoundation {
+ static get strings() {
+ return strings;
+ }
+
+ static get defaultAdapter() {
+ return {
+ getOffsetWidth: () => /* number */ 0,
+ getOffsetWidthForTileAtIndex: (/* index: number */) => /* number */ 0,
+ setStyleForTilesElement: (/* property: string, value: string */) => {},
+ registerResizeHandler: (/* handler: EventListener */) => {},
+ deregisterResizeHandler: (/* handler: EventListener */) => {},
+ };
+ }
+ constructor(adapter) {
+ super(Object.assign(MDCGridListFoundation.defaultAdapter, adapter));
+ this.resizeHandler_ = () => this.alignCenter();
+ this.resizeFrame_ = 0;
+ }
+ init() {
+ this.alignCenter();
+ this.adapter_.registerResizeHandler(this.resizeHandler_);
+ }
+ destroy() {
+ this.adapter_.deregisterResizeHandler(this.resizeHandler_);
+ }
+ alignCenter() {
+ if (this.resizeFrame_ !== 0) {
+ cancelAnimationFrame(this.resizeFrame_);
+ }
+ this.resizeFrame_ = requestAnimationFrame(() => {
+ this.alignCenter_();
+ this.resizeFrame_ = 0;
+ });
+ }
+ alignCenter_() {
+ const gridWidth = this.adapter_.getOffsetWidth();
+ const itemWidth = this.adapter_.getOffsetWidthForTileAtIndex(0);
+ const tilesWidth = itemWidth * Math.floor(gridWidth / itemWidth);
+ this.adapter_.setStyleForTilesElement('width', `${tilesWidth}px`);
+ }
+}
diff --git a/packages/mdc-grid-list/index.js b/packages/mdc-grid-list/index.js
new file mode 100644
index 00000000000..5716d005e33
--- /dev/null
+++ b/packages/mdc-grid-list/index.js
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {MDCComponent} from '@material/base';
+
+import MDCGridListFoundation from './foundation';
+
+export {MDCGridListFoundation};
+
+export class MDCGridList extends MDCComponent {
+ static attachTo(root) {
+ return new MDCGridList(root);
+ }
+
+ getDefaultFoundation() {
+ return new MDCGridListFoundation({
+ getOffsetWidth: () => this.root_.offsetWidth,
+ getOffsetWidthForTileAtIndex: (index) => {
+ return this.root_.querySelectorAll(MDCGridListFoundation.strings.TILE_SELECTOR)[index].offsetWidth;
+ },
+ setStyleForTilesElement: (property, value) => {
+ this.root_.querySelector(MDCGridListFoundation.strings.TILES_SELECTOR).style[property] = value;
+ },
+ registerResizeHandler: (handler) => window.addEventListener('resize', handler),
+ deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
+ });
+ }
+}
diff --git a/packages/mdc-grid-list/mdc-grid-list.scss b/packages/mdc-grid-list/mdc-grid-list.scss
new file mode 100644
index 00000000000..fb46345291c
--- /dev/null
+++ b/packages/mdc-grid-list/mdc-grid-list.scss
@@ -0,0 +1,194 @@
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+@import "@material/rtl/mixins";
+@import "@material/theme/mixins";
+@import "@material/typography/mixins";
+
+$mdc-grid-list-tile-width: 200px !default;
+$mdc-grid-list-tile-secondary-padding: 16px;
+$mdc-grid-list-tile-secondary-padding-narrow: 8px;
+$mdc-grid-list-oneline-cap-secondary-height: 48px;
+$mdc-grid-list-twoline-cap-secondary-height: 68px;
+$mdc-grid-list-tile-secondary-icon-size: 24px;
+
+@mixin mdc-grid-list-tile-aspect($width-height-ratio) {
+ .mdc-grid-tile__primary {
+ padding-bottom: calc(100% / #{$width-height-ratio});
+ }
+}
+
+@mixin mdc-grid-list-tile-gutter($gutter-width) {
+ .mdc-grid-tile {
+ margin: $gutter-width/2 0;
+ padding: 0 $gutter-width/2;
+
+ &__secondary {
+ left: $gutter-width/2;
+ width: calc(100% - #{$gutter-width});
+ }
+ }
+
+ .mdc-grid-list__tiles {
+ margin: $gutter-width/2 auto;
+ }
+}
+
+// postcss-bem-linter: define grid-list
+.mdc-grid-list {
+ @include mdc-grid-list-tile-aspect(1);
+ @include mdc-grid-list-tile-gutter(4px);
+
+ &__tiles {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ margin: 0;
+ padding: 0;
+ }
+}
+
+.mdc-grid-list--tile-gutter-1 {
+ @include mdc-grid-list-tile-gutter(1px);
+}
+
+.mdc-grid-list--tile-aspect-16x9 {
+ @include mdc-grid-list-tile-aspect(16 / 9);
+}
+
+.mdc-grid-list--tile-aspect-3x2 {
+ @include mdc-grid-list-tile-aspect(3 / 2);
+}
+
+.mdc-grid-list--tile-aspect-2x3 {
+ @include mdc-grid-list-tile-aspect(2 / 3);
+}
+
+.mdc-grid-list--tile-aspect-4x3 {
+ @include mdc-grid-list-tile-aspect(4 / 3);
+}
+
+.mdc-grid-list--tile-aspect-3x4 {
+ @include mdc-grid-list-tile-aspect(3 / 4);
+}
+
+// stylelint-disable plugin/selector-bem-pattern
+// We need to disable linter here because we are nesting grid-tile under grid list here.
+.mdc-grid-list--twoline-caption {
+ .mdc-grid-tile__secondary {
+ height: $mdc-grid-list-twoline-cap-secondary-height;
+ }
+}
+
+.mdc-grid-list--header-caption {
+ .mdc-grid-tile__secondary {
+ top: 0;
+ bottom: auto;
+ }
+}
+
+.mdc-grid-list--with-icon-align-start {
+ .mdc-grid-tile__secondary {
+ @include mdc-rtl-reflexive-property(padding, $mdc-grid-list-tile-secondary-padding * 2 + $mdc-grid-list-tile-secondary-icon-size, $mdc-grid-list-tile-secondary-padding-narrow, ".mdc-grid-list");
+ }
+
+ .mdc-grid-tile__icon {
+ @include mdc-rtl-reflexive-position(left, $mdc-grid-list-tile-secondary-padding, ".mdc-grid-list");
+
+ font-size: $mdc-grid-list-tile-secondary-icon-size;
+ }
+}
+
+.mdc-grid-list--with-icon-align-end {
+ .mdc-grid-tile__secondary {
+ @include mdc-rtl-reflexive-property(padding, $mdc-grid-list-tile-secondary-padding, $mdc-grid-list-tile-secondary-padding * 2 + $mdc-grid-list-tile-secondary-icon-size, ".mdc-grid-list");
+ }
+
+ .mdc-grid-tile__icon {
+ @include mdc-rtl-reflexive-position(right, $mdc-grid-list-tile-secondary-padding, ".mdc-grid-list");
+
+ font-size: $mdc-grid-list-tile-secondary-icon-size;
+ }
+}
+// stylelint-enable plugin/selector-bem-pattern
+// postcss-bem-linter: end
+
+// postcss-bem-linter: define grid-tile
+.mdc-grid-tile {
+ display: block;
+ position: relative;
+ width: var(--mdc-grid-list-tile-width, $mdc-grid-list-tile-width);
+
+ &__primary {
+ position: relative;
+ height: 0;
+
+ @include mdc-theme-prop(background-color, background);
+ @include mdc-theme-prop(color, text-primary-on-background);
+
+ &-content {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: cover;
+ }
+ }
+
+ &__secondary {
+ position: absolute;
+ box-sizing: border-box;
+ bottom: 0;
+ height: $mdc-grid-list-oneline-cap-secondary-height;
+ padding: $mdc-grid-list-tile-secondary-padding;
+
+ @include mdc-theme-prop(background-color, primary);
+ @include mdc-theme-prop(color, text-primary-on-primary);
+ }
+
+ &__title {
+ display: block;
+ margin: 0;
+ padding: 0;
+ font-size: 1rem;
+ font-weight: #{map-get($mdc-typography-font-weight-values, medium)};
+ line-height: 1rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
+ &__support-text {
+ @include mdc-typography(body1);
+
+ display: block;
+ margin: 0;
+ margin-top: 4px;
+ padding: 0;
+ }
+
+ &__icon {
+ position: absolute;
+ top: calc(50% - #{$mdc-grid-list-tile-secondary-icon-size} / 2);
+ font-size: 0;
+ }
+}
+// postcss-bem-linter: end
diff --git a/packages/mdc-grid-list/package.json b/packages/mdc-grid-list/package.json
new file mode 100644
index 00000000000..923e8bd7789
--- /dev/null
+++ b/packages/mdc-grid-list/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@material/grid-list",
+ "version": "0.0.0",
+ "description": "The Material Components for the web grid list component",
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/material-components/material-components-web.git"
+ },
+ "keywords": [
+ "material components",
+ "material design",
+ "grid list"
+ ],
+ "dependencies": {
+ "@material/base": "^0.1.2",
+ "@material/rtl": "^0.1.2",
+ "@material/theme": "^0.1.2",
+ "@material/typography": "^0.2.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/test/unit/mdc-grid-list/foundation.test.js b/test/unit/mdc-grid-list/foundation.test.js
new file mode 100644
index 00000000000..8414bb192c4
--- /dev/null
+++ b/test/unit/mdc-grid-list/foundation.test.js
@@ -0,0 +1,96 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {assert} from 'chai';
+import td from 'testdouble';
+
+import {createMockRaf} from '../helpers/raf';
+import {setupFoundationTest} from '../helpers/setup';
+import {verifyDefaultAdapter} from '../helpers/foundation';
+import MDCGridListFoundation from '../../../packages/mdc-grid-list/foundation';
+
+suite('MDCGridListFoundation');
+
+test('exports strings', () => {
+ assert.isOk('strings' in MDCGridListFoundation);
+});
+
+test('defaultAdapter returns a complete adapter implementation', () => {
+ verifyDefaultAdapter(MDCGridListFoundation, [
+ 'getOffsetWidth', 'getOffsetWidthForTileAtIndex', 'setStyleForTilesElement',
+ 'registerResizeHandler', 'deregisterResizeHandler',
+ ]);
+});
+
+const setupTest = () => setupFoundationTest(MDCGridListFoundation);
+
+test('#init calls component event registrations and align center function', () => {
+ const {foundation, mockAdapter} = setupTest();
+
+ foundation.init();
+ td.verify(mockAdapter.registerResizeHandler(td.matchers.isA(Function)));
+});
+
+test('#destroy calls component event deregistrations', () => {
+ const {foundation, mockAdapter} = setupTest();
+
+ let resizeHandler;
+ td.when(mockAdapter.registerResizeHandler(td.matchers.isA(Function))).thenDo((handler) => {
+ resizeHandler = handler;
+ });
+
+ foundation.init();
+ foundation.destroy();
+ td.verify(mockAdapter.deregisterResizeHandler(resizeHandler));
+});
+
+test('#align center sets the container width to fit tiles inside', () => {
+ const {foundation, mockAdapter} = setupTest();
+ const mockRaf = createMockRaf();
+ const listOffsetWidth = 1005;
+ const tileOffsetWidth = 200;
+ const tilesWidth = Math.floor(listOffsetWidth / tileOffsetWidth) * tileOffsetWidth;
+ td.when(mockAdapter.getOffsetWidth()).thenReturn(listOffsetWidth);
+ td.when(mockAdapter.getOffsetWidthForTileAtIndex(0)).thenReturn(tileOffsetWidth);
+ foundation.init();
+
+ foundation.alignCenter();
+ mockRaf.flush();
+
+ td.verify(mockAdapter.setStyleForTilesElement('width', `${tilesWidth}px`));
+ mockRaf.restore();
+});
+
+test('#alignCenter debounces calls within the same frame', () => {
+ const {foundation} = setupTest();
+ const mockRaf = createMockRaf();
+ foundation.alignCenter();
+ foundation.alignCenter();
+ foundation.alignCenter();
+ assert.equal(mockRaf.pendingFrames.length, 1);
+ mockRaf.restore();
+});
+
+test('#alignCenter resets debounce latch when alignCenter frame is run', () => {
+ const {foundation} = setupTest();
+ const mockRaf = createMockRaf();
+
+ foundation.alignCenter();
+ mockRaf.flush();
+ foundation.alignCenter();
+ assert.equal(mockRaf.pendingFrames.length, 1);
+ mockRaf.restore();
+});
diff --git a/test/unit/mdc-grid-list/mdc-grid-list.test.js b/test/unit/mdc-grid-list/mdc-grid-list.test.js
new file mode 100644
index 00000000000..2bbe4e08ad9
--- /dev/null
+++ b/test/unit/mdc-grid-list/mdc-grid-list.test.js
@@ -0,0 +1,69 @@
+/**
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {assert} from 'chai';
+import bel from 'bel';
+import domEvents from 'dom-events';
+import td from 'testdouble';
+
+import {MDCGridList} from '../../../packages/mdc-grid-list';
+
+function getFixture() {
+ return bel`
+