Skip to content

Commit

Permalink
Custom row (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
billy-addepar authored Aug 28, 2017
1 parent 5dfd88b commit eea733d
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 76 deletions.
20 changes: 20 additions & 0 deletions addon/components/ember-table-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import $ from 'jquery';
import layout from '../templates/components/ember-table-2';
import { htmlSafe } from '@ember/string';
import { run } from '@ember/runloop';
import { isNone } from '@ember/utils';
import { get, set } from '@ember/object';

const { Component } = Ember;

Expand Down Expand Up @@ -86,6 +88,15 @@ export default class EmberTable2 extends Component {
*/
@property estimateRowHeight = 20;

/**
* A string value that defines table row component. By default, this is using ember table row
* component but outer application can override this value to use their custom row. This is
* useful when user wants to have custom action on entire row instead of cell (e.g. highlight row
* when mouse hovers, select & unselect rows). However, the custom row component must extend the
* EmberTableRow class and should not override handlebar template.
*/
@property rowComponent = 'ember-table-row';

init() {
super.init(...arguments);

Expand All @@ -94,6 +105,15 @@ export default class EmberTable2 extends Component {
// Create a unique CellProxy class for this table instance, that way transient data won't
// pollute the prototype of the main proxy class.
this.cellProxyClass = class extends CellProxy {};

const columns = this.get('columns');
for (const column of columns) {
if (isNone(get(column, 'headerComponent'))) {
set(column, '_headerComponent', 'ember-table-header');
} else {
set(column, '_headerComponent', get(column, 'headerComponent'));
}
}
}

didInsertElement() {
Expand Down
38 changes: 18 additions & 20 deletions addon/templates/components/ember-table-2.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@
<thead class="et2-thead">
<tr>
{{#each columns as |column columnIndex|}}
{{#if column.customHeader}}
{{component column.customHeader column=column columnIndex=columnIndex}}
{{else}}
{{#ember-table-header
columnIndex=columnIndex
hasFixedColumn=hasFixedColumn
enableColumnResize=enableColumnResize
enableColumnReorder=enableColumnReorder
onHeaderClicked="onHeaderClicked"
onColumnResized="onColumnResized"
onColumnResizeEnded="onColumnResizeEnded"
onColumnReorder="onColumnReorder"
onColumnReorderEnds="onColumnReorderEnds"
width=column.width
}}
{{#component column._headerComponent
column=column
columnIndex=columnIndex
hasFixedColumn=hasFixedColumn
enableColumnResize=enableColumnResize
enableColumnReorder=enableColumnReorder
onHeaderClicked="onHeaderClicked"
onColumnResized="onColumnResized"
onColumnResizeEnded="onColumnResizeEnded"
onColumnReorder="onColumnReorder"
onColumnReorderEnds="onColumnReorderEnds"
width=column.width
}}
{{#unless column.headerComponent}}
{{column.columnName}}
{{/ember-table-header}}
{{/if}}
{{/unless}}
{{/component}}
{{/each}}
</tr>
</thead>
Expand All @@ -35,7 +34,7 @@

as |row rowIndex|
}}
{{#ember-table-row
{{#component rowComponent
row=row
columns=columns
cellCache=cellCache
Expand All @@ -52,12 +51,11 @@
}}
{{yield cell}}
{{/ember-table-cell}}
{{/ember-table-row}}
{{/component}}
{{/vertical-collection}}
</div>
</div>


<div class="et2-bottom-wrapper">
{{#if hasFooter}}
<tfoot class="et2-tfooter">
Expand Down
5 changes: 5 additions & 0 deletions tests/dummy/app/components/custom-row.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import EmberTableRow from '../components/ember-table-row';

export default class CustomRow extends EmberTableRow {
classNames = ['custom-row']
}
2 changes: 1 addition & 1 deletion tests/dummy/app/components/image-cell.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import EmberTableCell from '../components/ember-table-cell';

export default class ImageCellComponent extends EmberTableCell {
export default class ImageCell extends EmberTableCell {
}
2 changes: 1 addition & 1 deletion tests/dummy/app/controllers/custom-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default Controller.extend({
width: 180
}));
arr.push(EmberObject.create({
customHeader: 'custom-text-header',
headerComponent: 'custom-text-header',
customCell: 'image-cell',
valuePath: 'url',
width: 180
Expand Down
4 changes: 4 additions & 0 deletions tests/dummy/app/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,7 @@ table tfoot th {
height: 40px;
width: 40px;
}

.custom-row {
background: green;
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This is custom text header.
Custom header {{column.columnName}}
1 change: 1 addition & 0 deletions tests/dummy/app/templates/custom-component.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{{#ember-table-2
rows=rows
columns=columns
rowComponent="custom-row"
as |cell|
}}

Expand Down
52 changes: 51 additions & 1 deletion tests/helpers/test-scenarios.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import hbs from 'htmlbars-inline-precompile';
import { A as emberA } from '@ember/array';
import EmberObject from '@ember/object';
import waitForRender from 'dummy/tests/helpers/wait-for-render';
import {
find
} from 'ember-native-dom-helpers';
import {
pressElement,
moveMouse,
releasePress
} from './drag-helper';

export function generateRows(rowCount, columnCount) {
const arr = emberA();
Expand Down Expand Up @@ -63,6 +71,7 @@ export const fullTable = hbs`
enableColumnReorder=true
hasFixedColumn=true
columns=tableColumns
rowComponent=customRow
rows=tableRows
as |cell|
}}
Expand All @@ -71,11 +80,52 @@ export const fullTable = hbs`
</div>
`;

export async function setupCustomComponentTable(testContext, customHeader, customRow) {
const rowCount = 5;
const columnCount = 3;
const columns = generateColumns(columnCount);
for (const column of columns) {
column.set('headerComponent', customHeader);
}
testContext.set('tableColumns', columns);
testContext.set('tableRows', generateRows(rowCount, columnCount));
testContext.set('customRow', customRow);

testContext.render(fullTable);
await waitForRender();
}

export async function setupFullTable(testContext) {
const rowCount = 20;
const columnCount = 56;
const columnCount = 10;
testContext.set('tableColumns', generateColumns(columnCount));
testContext.set('tableRows', generateRows(rowCount, columnCount));
testContext.render(fullTable);
await waitForRender();
}

export async function moveTableColumn(columnIndex, deltaPosition) {
const header = find(`.et2-thead tr th:nth-child(${columnIndex})`);
const box = header.getBoundingClientRect();
const width = header.offsetLeft;
const startX = (box.right + box.left) / 2;
const deltaX = deltaPosition * width;

await pressElement(header, startX, header.clientHeight / 2);
await moveMouse(header, startX + deltaX / 2, header.clientHeight / 2);
await moveMouse(header, startX + deltaX, header.clientHeight / 2);
await releasePress(header, startX + deltaX, header.clientHeight / 2);
}

export async function resizeColumn(columnIndex, deltaX) {
const header = getHeaderElement(columnIndex);
const box = header.getBoundingClientRect();
const startX = box.right - 5;
await pressElement(header, startX, header.clientHeight / 2);
await moveMouse(header, startX + deltaX / 2, header.clientHeight / 2);
await moveMouse(header, startX + deltaX, header.clientHeight / 2);
}

export function getHeaderElement(headerIndex) {
return find(`.et2-thead tr th:nth-child(${headerIndex})`);
}
87 changes: 35 additions & 52 deletions tests/integration/components/ember-table-2-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import {
simpleTable,
generateColumns,
generateRows,
setupFullTable
setupFullTable,
setupCustomComponentTable,
moveTableColumn,
getHeaderElement,
resizeColumn
} from '../../helpers/test-scenarios';
import {
pressElement,
moveMouse,
releasePress
} from '../../helpers/drag-helper';
import EmberObject from '@ember/object';
import waitForRender from 'dummy/tests/helpers/wait-for-render';
import {
find,
Expand Down Expand Up @@ -44,67 +42,52 @@ test('Ember table renders', async function(assert) {
test('Test resizing column', async function(assert) {
setupFullTable(this);

let header = find('.et2-thead tr th:nth-child(2)');
const originalWidth = header.offsetWidth;
const box = header.getBoundingClientRect();
const deltaX = 30;
const startX = box.right - 5;
await pressElement(header, startX, header.clientHeight / 2);
await moveMouse(header, startX + deltaX / 2, header.clientHeight / 2);
await moveMouse(header, startX + deltaX, header.clientHeight / 2);

assert.equal(header.offsetWidth - originalWidth, 30, 'Column size is updated');
let originalWidth = getHeaderElement(2).offsetWidth;
await resizeColumn(2, 30);
assert.equal(getHeaderElement(2).offsetWidth - originalWidth, 30, 'Column size is updated');

// Fixed column can also be resized
header = find('.et2-thead tr th:nth-child(1)');
await pressElement(header, startX, header.clientHeight / 2);
await moveMouse(header, startX + deltaX / 2, header.clientHeight / 2);
await moveMouse(header, startX + deltaX, header.clientHeight / 2);

assert.equal(header.offsetWidth - originalWidth, 30, 'Column size is updated');
originalWidth = getHeaderElement(1).offsetWidth;
await resizeColumn(1, 30);
assert.equal(getHeaderElement(1).offsetWidth - originalWidth, 30, 'Column size is updated');
});

test('Test reodering columns', async function(assert) {
setupFullTable(this);

const header = find('.et2-thead tr th:nth-child(2)');
const box = header.getBoundingClientRect();
const width = header.offsetLeft;
const startX = (box.right + box.left) / 2;

// Case 1: Try to swap column A with fixed column. The table should prevent that action.
await pressElement(header, startX, header.clientHeight / 2);
await moveMouse(header, startX - width / 2, header.clientHeight / 2);
await moveMouse(header, startX - width, header.clientHeight / 2);
await releasePress(header, startX - width, header.clientHeight / 2);
assert.equal(find('.et2-thead tr th:nth-child(2)').innerText.trim(), 'Col A',
'Second column does not change');
assert.equal(find('.et2-thead tr th:nth-child(1)').innerText.trim(), 'Column id',
'First column does not change');
await moveTableColumn(2, -1);

assert.equal(getHeaderElement(2).innerText.trim(), 'Col A', 'Second column does not change');
assert.equal(getHeaderElement(1).innerText.trim(), 'Column id', 'First column does not change');

// Case 2: Move column A -> B
await pressElement(header, startX, header.clientHeight / 2);
await moveMouse(header, startX + width / 2, header.clientHeight / 2);
await moveMouse(header, startX + width, header.clientHeight / 2);
await releasePress(header, startX + width, header.clientHeight / 2);
await moveTableColumn(2, 1);

assert.equal(find('.et2-thead tr th:nth-child(2)').innerText.trim(), 'Col B',
assert.equal(find(getHeaderElement(2)).innerText.trim(), 'Col B',
'Second column is swapped');
assert.equal(find('.et2-thead tr th:nth-child(3)').innerText.trim(), 'Col A',
'Third column is swapped');
});

test('Test custom header', async function(assert) {
this.set('tableColumns', [
EmberObject.create({
customHeader: 'custom-text-header',
width: 180
})
]);
this.set('tableRows', []);
this.render(simpleTable);
await waitForRender();
setupCustomComponentTable(this, 'custom-text-header', 'custom-row');
assert.equal(getHeaderElement(1).innerText.trim(), 'Custom header Column id',
'Custom header is correct');

// Veritfy that you can reorder custom header.
await moveTableColumn(2, 1);

assert.equal(getHeaderElement(2).innerText.trim(), 'Custom header Col B',
'Custom column can be swapped');
});

test('Test custom row', async function(assert) {
setupCustomComponentTable(this, null, 'custom-row');

// TODO(Billy): Because of the slow table rendering, we need to scroll a bit for the table to
// be fully rendered. Remove this scroll when table rendering issue is fixed.
await scrollTo('.et2-body-outer-wrapper', 0, 10);

assert.equal(find('.et2-thead tr th:nth-child(1)').innerText.trim(),
'This is custom text header.', 'Custom header is correct');
assert.ok(find('tbody tr').className.indexOf('custom-row') >= 0, 'Table has custom row');
});

0 comments on commit eea733d

Please sign in to comment.