Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge branch 'master' into 2-x #652

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion addon/components/light-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ const LightTable = Component.extend({
*/
estimatedRowHeight: 0,

/**
* Whether `vertical-collection` should recycle table rows. This speeds up performance with occlusion
* rendering but may cause problems if any components expect to reset their state to the initial state
* with every rerender of the list.
*
* @property shouldRecycle
* @type Boolean
* @default true
*/
shouldRecycle: true,

/**
* Table component shared options
*
Expand All @@ -193,7 +204,8 @@ const LightTable = Component.extend({
fixedHeader: false,
fixedFooter: false,
occlusion: this.get('occlusion'),
estimatedRowHeight: this.get('estimatedRowHeight')
estimatedRowHeight: this.get('estimatedRowHeight'),
shouldRecycle: this.get('shouldRecycle')
};
}).readOnly(),

Expand Down
1 change: 1 addition & 0 deletions addon/components/lt-body.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ export default Component.extend({
onRowClick() {},
onRowDoubleClick() {},
onScroll() {},
firstVisibleChanged() {},
lastVisibleChanged() {},
firstReached() {},
lastReached() {},
Expand Down
2 changes: 1 addition & 1 deletion addon/templates/components/columns/base.hbs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{{#if column.component}}
{{component column.component column=column table=table tableActions=tableActions extra=extra sortIcons=sortIcons}}
{{else}}
{{label}}
{{#if (and sortIcons.iconComponent sortIconProperty)}}
{{component sortIcons.iconComponent sortIcons=sortIcons sortIconProperty=sortIconProperty}}
{{else if sortIconProperty}}
<i class="lt-sort-icon {{get sortIcons sortIconProperty}}"></i>
{{/if}}
{{label}}
{{/if}}

{{#if isResizable}}
Expand Down
1 change: 1 addition & 0 deletions addon/templates/components/lt-body.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
rows
tagName='vertical-collection'
estimateHeight=sharedOptions.estimatedRowHeight
shouldRecycle=sharedOptions.shouldRecycle
bufferSize=scrollBufferRows
containerSelector='.lt-scrollable'
firstVisibleChanged=(action 'firstVisibleChanged')
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"ember-cli-inject-live-reload": "^1.7.0",
"ember-cli-less": "^1.5.5",
"ember-cli-mirage": "^0.4.1",
"ember-cli-qunit": "^4.1.1",
"ember-qunit": "^4.0.0",
"ember-cli-release": "1.0.0-beta.2",
"ember-cli-shims": "^1.2.0",
"ember-cli-sri": "^2.1.0",
Expand Down Expand Up @@ -79,6 +79,9 @@
"loader.js": "^4.6.0",
"yuidoc-ember-theme": "^2.0.1"
},
"resolutions": {
"ember-element-resize-detector": "0.3.0"
},
"engines": {
"node": "^4.5 || 6.* || >= 7.*"
},
Expand Down
236 changes: 236 additions & 0 deletions tests/integration/components/light-table-occlusion-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { scrollTo } from 'ember-native-dom-helpers';
import { setupRenderingTest } from 'ember-qunit';
import { render, findAll, find, click } from '@ember/test-helpers';
import { module, test } from 'qunit';
import hbs from 'htmlbars-inline-precompile';
import setupMirageTest from 'ember-cli-mirage/test-support/setup-mirage';
import Table from 'ember-light-table';
import Columns from '../../helpers/table-columns';
import hasClass from '../../helpers/has-class';
import RowComponent from 'ember-light-table/components/lt-row';
import Component from '@ember/component';
import { get, computed } from '@ember/object';

module('Integration | Component | light table | occlusion', function(hooks) {
setupRenderingTest(hooks);
setupMirageTest(hooks);

hooks.beforeEach(function() {
this.actions = {};
this.send = (actionName, ...args) => this.actions[actionName].apply(this, args);
});

test('it renders', async function(assert) {
this.set('table', new Table());
await render(hbs `{{light-table table height="40vh" occlusion=true estimatedRowHeight=30}}`);

assert.equal(find('*').textContent.trim(), '');
});

test('takes 50 rows, renders some rows with scrollbar and occludes the rest', async function(assert) {
assert.expect(3);

this.set('table', new Table(Columns, this.server.createList('user', 50)));

await render(hbs `
{{#light-table table height='40vh' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.head fixed=true}}
{{t.body}}
{{/light-table}}
`);

assert.ok(findAll('.vertical-collection tbody.lt-body tr.lt-row').length < 30, 'only some rows are rendered');

let scrollContainer = 'vertical-collection';
let { scrollHeight } = find(scrollContainer);

assert.ok(findAll(scrollContainer).length > 0, 'scroll container was rendered');
assert.ok(scrollHeight > 1500, 'scroll height is 50 rows * 30 px per row + header size');

await scrollTo(scrollContainer, 0, scrollHeight);
});

test('fixed header', async function(assert) {
assert.expect(2);
this.set('table', new Table(Columns, this.server.createList('user', 5)));

await render(hbs `
{{#light-table table height='500px' id='lightTable' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.head fixed=true}}
{{t.body}}
{{/light-table}}
`);

assert.equal(findAll('#lightTable_inline_head thead').length, 0);

await render(hbs `
{{#light-table table height='500px' id='lightTable' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.head fixed=false}}
{{t.body}}
{{/light-table}}
`);

assert.equal(findAll('#lightTable_inline_head thead').length, 1);
});

test('fixed footer', async function(assert) {
assert.expect(2);
this.set('table', new Table(Columns, this.server.createList('user', 5)));

await render(hbs `
{{#light-table table height='500px' id='lightTable' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.body}}
{{t.foot fixed=true}}
{{/light-table}}
`);

assert.equal(findAll('#lightTable_inline_foot tfoot').length, 0);

await render(hbs `
{{#light-table table height='500px' id='lightTable' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.body}}
{{t.foot fixed=false}}
{{/light-table}}
`);

assert.equal(findAll('#lightTable_inline_foot tfoot').length, 1);
});

test('table assumes height of container', async function(assert) {

this.set('table', new Table(Columns, this.server.createList('user', 5)));
this.set('fixed', true);

await render(hbs `
<div style="height: 500px">
{{#light-table table id='lightTable' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.body}}
{{t.foot fixed=fixed}}
{{/light-table}}
</div>
`);

assert.equal(find('#lightTable').offsetHeight, 500, 'table is 500px height');

});

test('table body should consume all available space when not enough content to fill it', async function(assert) {
this.set('table', new Table(Columns, this.server.createList('user', 1)));
this.set('fixed', true);

await render(hbs `
<div style="height: 500px">
{{#light-table table id='lightTable' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.head fixed=true}}
{{t.body}}
{{#t.foot fixed=true}}
Hello World
{{/t.foot}}
{{/light-table}}
</div>
`);

const bodyHeight = find('.lt-body-wrap').offsetHeight;
const headHeight = find('.lt-head-wrap').offsetHeight;
const footHeight = find('.lt-foot-wrap').offsetHeight;

assert.equal(bodyHeight + headHeight + footHeight, 500, 'combined table content is 500px tall');
assert.ok(bodyHeight > (headHeight + footHeight), 'body is tallest element');
});

test('accepts components that are used in the body', async function(assert) {

this.owner.register('component:custom-row', RowComponent);

this.set('table', new Table(Columns, this.server.createList('user', 1)));

await render(hbs `
{{#light-table table occlusion=true estimatedRowHeight=30 as |t|}}
{{t.body rowComponent=(component "custom-row" classNames="custom-row")}}
{{/light-table}}
`);

assert.equal(findAll('.lt-row.custom-row').length, 1, 'row has custom-row class');
});

test('passed in components can have computed properties', async function(assert) {

this.owner.register('component:custom-row', RowComponent.extend({
classNameBindings: ['isActive'],
current: null,
isActive: computed('row.content', 'current', function() {
return this.get('row.content') === this.get('current');
})
}));

let users = this.server.createList('user', 3);
this.set('table', new Table(Columns, users));

await render(hbs `
{{#light-table table height='500px' occlusion=true estimatedRowHeight=30 as |t|}}
{{t.body
rowComponent=(component "custom-row" classNames="custom-row" current=current)
}}
{{/light-table}}
`);

assert.equal(findAll('.custom-row').length, 3, 'three custom rows were rendered');
assert.notOk(find('.custom-row.is-active'), 'none of the items are active');

this.set('current', users[0]);
let firstRow = find('.custom-row:nth-child(2)');
assert.ok(hasClass(firstRow, 'is-active'), 'first custom row is active');

this.set('current', users[2]);
let thirdRow = find('.custom-row:nth-child(4)');
assert.ok(hasClass(thirdRow, 'is-active'), 'third custom row is active');

this.set('current', null);

assert.notOk(find('.custom-row.is-active'), 'none of the items are active');
});

test('extra data and tableActions', async function(assert) {
assert.expect(4);

this.owner.register('component:some-component', Component.extend({
classNames: 'some-component',
didReceiveAttrs() {
assert.equal(get(this, 'extra.someData'), 'someValue', 'extra data is passed');
},
click() {
get(this, 'tableActions.someAction')();
}
}));

const columns = [{
component: 'some-component',
cellComponent: 'some-component'
}];

this.set('table', new Table(columns, [{}]));

this.actions.someAction = () => {
assert.ok(true, 'table action is passed');
};

await render(hbs `
{{#light-table table
occlusion=true
estimatedRowHeight=30
extra=(hash someData="someValue")
tableActions=(hash
someAction=(action "someAction")
)
as |t|
}}
{{t.head}}
{{t.body}}
{{/light-table}}
`);

for (const element of findAll('.some-component')) {
await click(element);
}
});
});
Loading