diff --git a/addon/components/paper-grid-list.js b/addon/components/paper-grid-list.js
index 4fd24ef17..ec0000753 100644
--- a/addon/components/paper-grid-list.js
+++ b/addon/components/paper-grid-list.js
@@ -47,9 +47,11 @@ export default Component.extend(ParentMixin, {
this._installMediaListener();
},
- didUpdateAttrs() {
+ didUpdate() {
this._super(...arguments);
- this.updateGrid();
+
+ // Debounces until the next run loop
+ run.debounce(this, this.updateGrid, 0);
},
willDestroyElement() {
@@ -66,13 +68,14 @@ export default Component.extend(ParentMixin, {
// Sets mediaList to a property so removeListener can access it
this.set(`${listenerName}List`, mediaList);
+
// Creates a function based on mediaName so that removeListener can remove it.
this.set(listenerName, run.bind(this, (result) => {
this._mediaDidChange(mediaName, result.matches);
}));
- // Calls '_mediaDidChange' once
- this[listenerName](mediaList);
+ // Trigger initial grid calculations
+ this._mediaDidChange(mediaName, mediaList.matches);
mediaList.addListener(this[listenerName]);
}
@@ -88,23 +91,23 @@ export default Component.extend(ParentMixin, {
_mediaDidChange(mediaName, matches) {
this.set(mediaName, matches);
- run.debounce(this, this._updateCurrentMedia, 50);
+
+ // Debounces until the next run loop
+ run.debounce(this, this._updateCurrentMedia, 0);
},
_updateCurrentMedia() {
let mediaPriorities = this.get('constants.MEDIA_PRIORITY');
- let currentMedia = mediaPriorities.filter((mediaName) => {
- return this.get(mediaName);
- });
+ let currentMedia = mediaPriorities.filter((mediaName) => this.get(mediaName));
this.set('currentMedia', currentMedia);
this.updateGrid();
},
+ // Updates styles and triggers onUpdate callbacks
updateGrid() {
this.$().css(this._gridStyle());
- this.get('tiles').forEach((tile) => {
- tile.$().css(tile._tileStyle());
- });
+ this.get('tiles').forEach((tile) => tile.updateTile());
+ this.sendAction('onUpdate');
},
_gridStyle() {
@@ -135,7 +138,8 @@ export default Component.extend(ParentMixin, {
break;
}
case 'fit': {
- // noop, as the height is user set
+ // rowHeight is container height
+ style.height = '100%';
break;
}
}
@@ -145,16 +149,22 @@ export default Component.extend(ParentMixin, {
// Calculates tile positions
_setTileLayout() {
- let tiles = this.get('tiles');
+ let tiles = this.orderedTiles();
let layoutInfo = gridLayout(this.get('currentCols'), tiles);
- tiles.forEach((tile, i) => {
- tile.set('position', layoutInfo.positions[i]);
- });
+ tiles.forEach((tile, i) => tile.set('position', layoutInfo.positions[i]));
this.set('rowCount', layoutInfo.rowCount);
},
+ // Sorts tiles by their order in the dom
+ orderedTiles() {
+ let domTiles = this.$('md-grid-tile').toArray();
+ return this.get('tiles').sort((a, b) => {
+ return domTiles.indexOf(a.get('element')) > domTiles.indexOf(b.get('element')) ? 1 : -1;
+ });
+ },
+
// Parses attribute string and returns hash of media sizes
_extractResponsiveSizes(string, regex = mediaRegex) {
let matches = {};
diff --git a/addon/components/paper-grid-tile.js b/addon/components/paper-grid-tile.js
index 192951ccd..5663d84e9 100644
--- a/addon/components/paper-grid-tile.js
+++ b/addon/components/paper-grid-tile.js
@@ -33,12 +33,15 @@ export default Component.extend(ChildMixin, {
didUpdateAttrs() {
this._super(...arguments);
- this.updateTile();
+ let gridList = this.get('gridList');
+
+ // Debounces until the next run loop
+ run.debounce(gridList, gridList.updateGrid, 0);
},
updateTile() {
- let gridList = this.get('gridList');
- run.debounce(gridList, gridList.updateGrid, 50);
+ this.$().css(this._tileStyle());
+ this.sendAction('onUpdate');
},
colspanMedia: computed('colspan', function() {
diff --git a/tests/dummy/app/templates/demo/grid-list.hbs b/tests/dummy/app/templates/demo/grid-list.hbs
index a9466aa89..6db7b300e 100644
--- a/tests/dummy/app/templates/demo/grid-list.hbs
+++ b/tests/dummy/app/templates/demo/grid-list.hbs
@@ -80,7 +80,7 @@
Responsive and Animated Usage
{{! BEGIN-SNIPPET grid-list.responsive }}
- {{#paper-content class="md-whiteframe-z1 grid-list-demo-responsiveTiles" layout-padding=''}}
+ {{#paper-content class="md-whiteframe-z1 grid-list-demo-responsiveTiles"}}
{{#paper-grid-list
cols="3 md-8 gt-md-12"
diff --git a/tests/integration/components/paper-grid-list-test.js b/tests/integration/components/paper-grid-list-test.js
index 21e9b6c5d..41f76acde 100644
--- a/tests/integration/components/paper-grid-list-test.js
+++ b/tests/integration/components/paper-grid-list-test.js
@@ -1,28 +1,69 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
-import { render, settled } from '@ember/test-helpers';
+import { render, find, findAll, waitUntil } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
+import { run } from '@ember/runloop';
+import { A } from "@ember/array"
+
+function getStyle(selector, property) {
+ let el = find(selector);
+ let style = getComputedStyle(el);
+ return style.getPropertyValue(property);
+}
+
+function tilePosition(selector) {
+ let left = getStyle(selector, 'left');
+
+ switch (left) {
+ case '0px':
+ return 1;
+ case '50px':
+ return 2;
+ case '100px':
+ return 3;
+ case '150px':
+ return 4;
+ default:
+ return 'grid sizing wrong: grid not ready yet?';
+ }
+}
+
+function tileRow(selector) {
+ let marginTop = getStyle(selector, 'margin-top');
+
+ switch (marginTop) {
+ case '0px':
+ return 1;
+ case '150.25px':
+ return 2;
+ case '300.5px':
+ return 3;
+ default:
+ return 'grid sizing wrong: grid not ready yet?';
+ }
+}
+
+function createTiles() {
+ return A(['ONE', 'TWO', 'THREE']);
+}
module('Integration | Component | paper grid list', function(hooks) {
setupRenderingTest(hooks);
test('it renders tiles with tag name', async function(assert) {
assert.expect(1);
-
await render(hbs`
{{#paper-grid-list cols="1" rowHeight="4:3" as |grid|}}
{{#grid.tile}}
{{/grid.tile}}
{{/paper-grid-list}}
`);
- return settled().then(() => {
- assert.equal(this.$('md-grid-tile').length, 1);
- });
+
+ assert.equal(this.$('md-grid-tile').length, 1);
});
test('it renders tiles with footer', async function(assert) {
assert.expect(1);
-
await render(hbs`
{{#paper-grid-list cols="1" rowHeight="4:3" as |grid|}}
{{#grid.tile as |tile|}}
@@ -31,8 +72,243 @@ module('Integration | Component | paper grid list', function(hooks) {
{{/grid.tile}}
{{/paper-grid-list}}
`);
- return settled().then(() => {
- assert.equal(this.$('md-grid-tile-footer').length, 1);
- });
+
+ assert.equal(findAll('md-grid-tile-footer').length, 1);
+ });
+
+ test('it applies a gutter', async function(assert) {
+ assert.expect(1);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list gutter="20px" cols="3" rowHeight="4:3" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(getStyle('.TWO', 'left'), '72.9844px');
+ });
+
+ test('it applies a fixed row height', async function(assert) {
+ assert.expect(1);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="3" rowHeight="75px" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(getStyle('.TWO', 'height'), '75px');
+ });
+
+ test('it applies a row height ratio', async function(assert) {
+ assert.expect(2);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="2" rowHeight="2:1" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(getStyle('.TWO', 'height'), '49.5px');
+ assert.equal(getStyle('.TWO', 'width'), '99.5px');
+ });
+
+ test('it applies a row height fit', async function(assert) {
+ assert.expect(1);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="1" rowHeight="fit" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(getStyle('.TWO', 'height').substr(0, 2), '39');
+ });
+
+ test('it applies tile colspan', async function(assert) {
+ assert.expect(1);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="3" rowHeight="4:3" as |grid|}}
+ {{#grid.tile colspan="3" class="COLSPAN" as |tile|}}
+ COLSPAN
+ {{/grid.tile}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(getStyle('.COLSPAN', 'width'), '199px');
+ });
+
+ test('it applies tile rowspan', async function(assert) {
+ assert.expect(2);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="1" rowHeight="4:3" as |grid|}}
+ {{#grid.tile rowspan="2" class="ROWSPAN" as |tile|}}
+ ROWSPAN
+ {{/grid.tile}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(getStyle('.ONE', 'height'), '149.25px');
+ assert.equal(getStyle('.ROWSPAN', 'height'), '299.5px');
+ });
+
+ test('it recalculates when cols changes', async function(assert) {
+ assert.expect(6);
+ this.set('tiles', createTiles());
+ this.set('cols', 1);
+
+ await render(hbs`
+
+ {{#paper-grid-list cols=cols rowHeight="4:3" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(tileRow('.ONE'), 1);
+ assert.equal(tileRow('.TWO'), 2);
+ assert.equal(tileRow('.THREE'), 3);
+
+ this.set('cols', 3);
+ await waitUntil(() => find('.THREE'));
+
+ assert.equal(tileRow('.ONE'), 1, 'ONE');
+ assert.equal(tileRow('.TWO'), 1, 'TWO');
+ assert.equal(tileRow('.THREE'), 1, 'THREE');
+ });
+
+ test('it recalculates when tile is added', async function(assert) {
+ assert.expect(7);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="4" rowHeight="4:3" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(tilePosition('.ONE'), 1);
+ assert.equal(tilePosition('.TWO'), 2);
+ assert.equal(tilePosition('.THREE'), 3);
+
+ run(() => this.get('tiles').insertAt(2, 'FOUR'));
+ await waitUntil(() => find('.FOUR'));
+
+ assert.equal(tilePosition('.ONE'), 1, 'ONE');
+ assert.equal(tilePosition('.TWO'), 2, 'TWO');
+ assert.equal(tilePosition('.FOUR'), 3, 'FOUR');
+ assert.equal(tilePosition('.THREE'), 4, 'THREE');
+ });
+
+ test('it recalculates when tile is removed', async function(assert) {
+ assert.expect(6);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="4" rowHeight="4:3" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(tilePosition('.ONE'), 1);
+ assert.equal(tilePosition('.TWO'), 2);
+ assert.equal(tilePosition('.THREE'), 3);
+
+ run(() => this.get('tiles').removeAt(1));
+ await waitUntil(() => !find('.TWO'));
+
+ assert.equal(find('.TWO'), null);
+ assert.equal(tilePosition('.ONE'), 1);
+ assert.equal(tilePosition('.THREE'), 2);
+ });
+
+ test('it reorders tiles when dom order changes', async function(assert) {
+ assert.expect(6);
+ this.set('tiles', createTiles());
+
+ await render(hbs`
+
+ {{#paper-grid-list cols="4" rowHeight="4:3" as |grid|}}
+ {{#each tiles as |item|}}
+ {{#grid.tile class=item as |tile|}}
+ {{item}}
+ {{/grid.tile}}
+ {{/each}}
+ {{/paper-grid-list}}
+
+ `);
+
+ assert.equal(tilePosition('.ONE'), 1);
+ assert.equal(tilePosition('.TWO'), 2);
+ assert.equal(tilePosition('.THREE'), 3);
+
+ run(() => this.get('tiles').reverseObjects());
+ await waitUntil(() => find('.TWO'));
+
+ assert.equal(tilePosition('.ONE'), 3);
+ assert.equal(tilePosition('.TWO'), 2);
+ assert.equal(tilePosition('.THREE'), 1);
});
});