blob: 1b74f13c5495fc003c06ee3899e75dc474ff6348 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* 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.
*/
package com.google.gwt.user.cellview.client;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.dom.builder.shared.TableRowBuilder;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.AbstractHasData.DefaultKeyboardSelectionHandler;
import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy.KeyboardPagingPolicy;
import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.Range;
import java.util.ArrayList;
import java.util.List;
/**
* Bases tests for subclasses of {@link AbstractCellTable}.
*
* @param <T> the subclass type
*/
public abstract class AbstractCellTableTestBase<T extends AbstractCellTable<String>> extends
AbstractHasDataTestBase {
/**
* A concrete column that implements a getter that always returns null.
*
* @param <T> the row type
* @param <C> the column type
*/
private static class MockColumn<T, C> extends Column<T, C> {
public MockColumn() {
super(new AbstractCell<C>() {
@Override
public void render(Context context, C value, SafeHtmlBuilder sb) {
}
});
}
@Override
public C getValue(T object) {
return null;
}
}
/**
* Test that calls to addColumn results in only one redraw.
*/
public void testAddColumnSingleRedraw() {
final List<LoadingState> loadingStates = new ArrayList<LoadingState>();
T table = createAbstractHasData();
table.setPageSize(10);
table.addLoadingStateChangeHandler(new LoadingStateChangeEvent.Handler() {
@Override
public void onLoadingStateChanged(LoadingStateChangeEvent event) {
if (LoadingState.LOADED == event.getLoadingState()) {
loadingStates.add(event.getLoadingState());
}
}
});
table.addColumn(new Column<String, String>(new TextCell()) {
@Override
public String getValue(String object) {
return object + "-3";
}
});
table.addColumn(new Column<String, String>(new TextCell()) {
@Override
public String getValue(String object) {
return object + "-4";
}
});
table.setRowData(0, createData(0, 10));
table.getPresenter().flush();
assertEquals(1, loadingStates.size());
}
/**
* Test that an error is thrown if the builder ends the table body element
* instead of the table row element.
*/
public void testBuildTooManyEnds() {
final List<Integer> builtRows = new ArrayList<Integer>();
T table = createAbstractHasData(new TextCell());
CellTableBuilder<String> builder = new DefaultCellTableBuilder<String>(table) {
@Override
public void buildRowImpl(String rowValue, int absRowIndex) {
builtRows.add(absRowIndex);
TableRowBuilder tr = startRow();
tr.endTR(); // End the tr.
tr.end(); // Accidentally end the table section.
// Try to start another row.
try {
startRow();
fail("Expected IllegalStateException: tbody is already ended");
} catch (IllegalStateException e) {
// Expected.
}
}
};
table.setTableBuilder(builder);
table.setVisibleRange(0, 1);
populateData(table);
table.getPresenter().flush();
assertEquals(1, builtRows.size());
assertEquals(0, builtRows.get(0).intValue());
}
/**
* Test that the table works if a row value is rendered into multiple rows.
*/
public void testBuildMultipleRows() {
T table = createAbstractHasData(new TextCell());
CellTableBuilder<String> builder = new DefaultCellTableBuilder<String>(table) {
@Override
public void buildRowImpl(String rowValue, int absRowIndex) {
super.buildRowImpl(rowValue, absRowIndex);
// Add child rows to row five.
if (absRowIndex == 5) {
for (int i = 0; i < 4; i++) {
TableRowBuilder tr = startRow();
tr.startTD().colSpan(2).text("child " + i).endTD();
tr.endTR();
}
}
}
};
table.setTableBuilder(builder);
table.setVisibleRange(0, 10);
populateData(table);
table.getPresenter().flush();
// Verify the structure.
TableSectionElement tbody = table.getTableBodyElement();
assertEquals(14, tbody.getChildCount());
assertEquals("child 0", getBodyElement(table, 6, 0).getInnerText());
assertEquals("child 1", getBodyElement(table, 7, 0).getInnerText());
assertEquals("child 2", getBodyElement(table, 8, 0).getInnerText());
assertEquals("child 3", getBodyElement(table, 9, 0).getInnerText());
// Verify the row values are accessible.
assertEquals("test 5", table.getVisibleItem(5));
assertEquals("test 9", table.getVisibleItem(9));
// Verify the child elements map correctly.
assertEquals(4, table.getChildElement(4).getSectionRowIndex());
assertEquals(5, table.getChildElement(5).getSectionRowIndex());
assertEquals(10, table.getChildElement(6).getSectionRowIndex());
}
/**
* Test that the table works if a row value is rendered into zero rows.
*/
public void testBuildZeroRows() {
T table = createAbstractHasData(new TextCell());
CellTableBuilder<String> builder = new DefaultCellTableBuilder<String>(table) {
@Override
public void buildRowImpl(String rowValue, int absRowIndex) {
// Skip row index 5.
if (absRowIndex != 5) {
super.buildRowImpl(rowValue, absRowIndex);
}
}
};
table.setTableBuilder(builder);
table.setVisibleRange(0, 10);
populateData(table);
table.getPresenter().flush();
// Verify the structure.
TableSectionElement tbody = table.getTableBodyElement();
assertEquals(9, tbody.getChildCount());
// Verify the row values are accessible.
assertEquals("test 5", table.getVisibleItem(5));
assertEquals("test 9", table.getVisibleItem(9));
// Verify the child elements map correctly.
assertEquals(4, table.getChildElement(4).getSectionRowIndex());
assertNull(table.getChildElement(5));
assertEquals(5, table.getChildElement(6).getSectionRowIndex());
}
public void testCellAlignment() {
T table = createAbstractHasData(new TextCell());
Column<String, String> column = new Column<String, String>(new TextCell()) {
@Override
public String getValue(String object) {
return object;
}
};
table.addColumn(column);
/*
* No alignment. Some browsers (FF) return a default value when alignment is
* not specified, others (IE/HtmlUnit) return an empty string.
*/
table.setRowData(0, createData(0, 1));
table.getPresenter().flush();
TableCellElement td = getBodyElement(table, 0, 2);
String hAlign = td.getAlign();
String vAlign = td.getVAlign();
assertTrue("".equals(hAlign) || "left".equals(hAlign));
assertTrue("".equals(vAlign) || "middle".equals(vAlign));
// Horizontal alignment.
column.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
table.setRowData(0, createData(0, 1));
table.getPresenter().flush();
td = getBodyElement(table, 0, 2);
hAlign = td.getAlign();
vAlign = td.getVAlign();
assertTrue("right".equals(hAlign));
assertTrue("".equals(vAlign) || "middle".equals(vAlign));
// Vertical alignment.
column.setHorizontalAlignment(null);
column.setVerticalAlignment(HasVerticalAlignment.ALIGN_BOTTOM);
table.setRowData(0, createData(0, 1));
table.getPresenter().flush();
td = getBodyElement(table, 0, 2);
hAlign = td.getAlign();
vAlign = td.getVAlign();
assertTrue("".equals(hAlign) || "left".equals(hAlign));
assertTrue("bottom".equals(vAlign));
// Horizontal and vertical alignment.
column.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
table.setRowData(0, createData(0, 1));
table.getPresenter().flush();
td = getBodyElement(table, 0, 2);
hAlign = td.getAlign();
vAlign = td.getVAlign();
assertTrue("right".equals(hAlign));
assertTrue("bottom".equals(vAlign));
}
public void testCellEvent() {
IndexCell<String> cell = new IndexCell<String>("click");
T table = createAbstractHasData(cell);
RootPanel.get().add(table);
table.setRowData(createData(0, 10));
table.getPresenter().flush();
// Trigger an event at index 5 inside the child div.
NativeEvent event = Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
getBodyElement(table, 5, 0).getFirstChildElement().dispatchEvent(event);
cell.assertLastBrowserEventIndex(5);
cell.assertLastEditingIndex(5);
// Trigger an event at index 3 outside the child div.
event = Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
getBodyElement(table, 3, 0).dispatchEvent(event);
cell.assertLastBrowserEventIndex(3);
cell.assertLastEditingIndex(3);
RootPanel.get().remove(table);
}
public void testCellStyles() {
T table = createAbstractHasData();
// A column that return a static cell style.
TextColumn<String> col0 = new TextColumn<String>() {
@Override
public String getValue(String object) {
return object;
}
};
col0.setCellStyleNames("col0");
table.addColumn(col0);
// A column that returns dynamic cell styles.
TextColumn<String> col1 = new TextColumn<String>() {
@Override
public String getCellStyleNames(Context context, String object) {
return object.replace(" ", "_");
}
@Override
public String getValue(String object) {
return object;
}
};
table.addColumn(col1);
// Populate the table.
table.setRowData(createData(0, 10));
table.flush();
assertTrue(getBodyElement(table, 1, 0).getClassName().contains(" col0"));
assertFalse(getBodyElement(table, 1, 0).getClassName().contains(" test_1"));
assertFalse(getBodyElement(table, 1, 1).getClassName().contains(" col0"));
assertTrue(getBodyElement(table, 1, 1).getClassName().contains(" test_1"));
}
public void testClearColumnWidth() {
T table = createAbstractHasData();
assertEquals(0, table.getRealColumnCount());
table.setColumnWidth(0, "100px");
assertEquals(1, table.getRealColumnCount());
table.setColumnWidth(2, "300px");
assertEquals(3, table.getRealColumnCount());
table.clearColumnWidth(2);
assertEquals(1, table.getRealColumnCount());
}
public void testDefaultKeyboardSelectionHandlerChangePage() {
T table = createAbstractHasData();
DefaultKeyboardSelectionHandler<String> keyHandler =
new DefaultKeyboardSelectionHandler<String>(table);
table.setKeyboardSelectionHandler(keyHandler);
HasDataPresenter<String> presenter = table.getPresenter();
table.setRowCount(100, true);
table.setVisibleRange(new Range(50, 10));
populateData(table);
presenter.flush();
table.setKeyboardPagingPolicy(KeyboardPagingPolicy.CHANGE_PAGE);
// keyboardPrev in middle.
table.setKeyboardSelectedRow(1);
presenter.flush();
assertEquals(1, table.getKeyboardSelectedRow());
keyHandler.prevRow();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
// keyboardPrev at beginning goes to previous page.
keyHandler.prevRow();
populateData(table);
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
assertEquals(new Range(40, 10), table.getVisibleRange());
// keyboardNext in middle.
table.setKeyboardSelectedRow(8);
presenter.flush();
assertEquals(8, table.getKeyboardSelectedRow());
keyHandler.nextRow();
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
// keyboardNext at end.
keyHandler.nextRow();
populateData(table);
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardPrevPage.
table.setKeyboardSelectedRow(5);
presenter.flush();
assertEquals(5, table.getKeyboardSelectedRow());
keyHandler.prevPage();
populateData(table);
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(40, 10), table.getVisibleRange());
// keyboardNextPage.
table.setKeyboardSelectedRow(5);
presenter.flush();
assertEquals(5, table.getKeyboardSelectedRow());
keyHandler.nextPage();
populateData(table);
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardHome.
keyHandler.home();
populateData(table);
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(0, 10), table.getVisibleRange());
// keyboardPrev at first row.
keyHandler.prevRow();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
// keyboardEnd.
keyHandler.end();
populateData(table);
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
assertEquals(new Range(90, 10), table.getVisibleRange());
// keyboardNext at last row.
keyHandler.nextRow();
presenter.flush();
}
public void testDefaultKeyboardSelectionHandlerCurrentPage() {
T table = createAbstractHasData();
DefaultKeyboardSelectionHandler<String> keyHandler =
new DefaultKeyboardSelectionHandler<String>(table);
table.setKeyboardSelectionHandler(keyHandler);
HasDataPresenter<String> presenter = table.getPresenter();
table.setRowCount(100, true);
table.setVisibleRange(new Range(50, 10));
populateData(table);
presenter.flush();
table.setKeyboardPagingPolicy(KeyboardPagingPolicy.CURRENT_PAGE);
// keyboardPrev in middle.
table.setKeyboardSelectedRow(1);
presenter.flush();
assertEquals(1, table.getKeyboardSelectedRow());
keyHandler.prevRow();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
// keyboardPrev at beginning.
keyHandler.prevRow();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardNext in middle.
table.setKeyboardSelectedRow(8);
presenter.flush();
assertEquals(8, table.getKeyboardSelectedRow());
keyHandler.nextRow();
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
// keyboardNext at end.
keyHandler.nextRow();
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardPrevPage.
keyHandler.prevPage(); // ignored.
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardNextPage.
keyHandler.nextPage(); // ignored.
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardHome.
keyHandler.home();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
// keyboardEnd.
keyHandler.end();
presenter.flush();
assertEquals(9, table.getKeyboardSelectedRow());
assertEquals(new Range(50, 10), table.getVisibleRange());
}
public void testDefaultKeyboardSelectionHandlerIncreaseRange() {
int pageStart = 150;
int pageSize = 10;
int increment = HasDataPresenter.PAGE_INCREMENT;
T table = createAbstractHasData();
DefaultKeyboardSelectionHandler<String> keyHandler =
new DefaultKeyboardSelectionHandler<String>(table);
table.setKeyboardSelectionHandler(keyHandler);
HasDataPresenter<String> presenter = table.getPresenter();
table.setRowCount(300, true);
table.setVisibleRange(new Range(pageStart, pageSize));
populateData(table);
presenter.flush();
table.setKeyboardPagingPolicy(KeyboardPagingPolicy.INCREASE_RANGE);
// keyboardPrev in middle.
table.setKeyboardSelectedRow(1);
presenter.flush();
assertEquals(1, table.getKeyboardSelectedRow());
keyHandler.prevRow();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
// keyboardPrev at beginning.
keyHandler.prevRow();
populateData(table);
presenter.flush();
pageStart -= increment;
pageSize += increment;
assertEquals(increment - 1, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardNext in middle.
table.setKeyboardSelectedRow(pageSize - 2);
presenter.flush();
assertEquals(pageSize - 2, table.getKeyboardSelectedRow());
keyHandler.nextRow();
presenter.flush();
assertEquals(pageSize - 1, table.getKeyboardSelectedRow());
// keyboardNext at end.
keyHandler.nextRow();
populateData(table);
presenter.flush();
pageSize += increment;
assertEquals(pageSize - increment, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardPrevPage within range.
table.setKeyboardSelectedRow(increment);
presenter.flush();
assertEquals(increment, table.getKeyboardSelectedRow());
keyHandler.prevPage();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardPrevPage outside range.
keyHandler.prevPage();
populateData(table);
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
pageStart -= increment;
pageSize += increment;
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardNextPage inside range.
keyHandler.nextPage();
presenter.flush();
assertEquals(increment, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardNextPage outside range.
table.setKeyboardSelectedRow(pageSize - 1);
presenter.flush();
assertEquals(pageSize - 1, table.getKeyboardSelectedRow());
keyHandler.nextPage();
populateData(table);
presenter.flush();
pageSize += increment;
assertEquals(pageSize - 1, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardHome.
keyHandler.home();
populateData(table);
presenter.flush();
pageSize += pageStart;
pageStart = 0;
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardPrev at first row.
keyHandler.prevRow();
presenter.flush();
assertEquals(0, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
// keyboardEnd.
keyHandler.end();
pageSize = 300;
populateData(table);
presenter.flush();
assertEquals(pageSize - 1, table.getKeyboardSelectedRow());
assertEquals(new Range(0, pageSize), table.getVisibleRange());
// keyboardNext at last row.
keyHandler.nextRow();
presenter.flush();
assertEquals(pageSize - 1, table.getKeyboardSelectedRow());
assertEquals(new Range(pageStart, pageSize), table.getVisibleRange());
}
public void testGetColumnIndex() {
T table = createAbstractHasData();
Column<String, String> col0 = new IdentityColumn<String>(new TextCell());
table.addColumn(col0);
Column<String, String> col1 = new IdentityColumn<String>(new TextCell());
table.addColumn(col1);
Column<String, String> col2 = new IdentityColumn<String>(new TextCell());
table.addColumn(col2);
assertEquals(0, table.getColumnIndex(col0));
assertEquals(1, table.getColumnIndex(col1));
assertEquals(2, table.getColumnIndex(col2));
// Test a column that is not in the table.
Column<String, String> other = new IdentityColumn<String>(new TextCell());
assertEquals(-1, table.getColumnIndex(other));
// Test null.
assertEquals(-1, table.getColumnIndex(null));
}
public void testGetColumnOutOfBounds() {
T table = createAbstractHasData();
// Get column when there are no columns.
try {
table.getColumn(0);
fail("Expected IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
// Expected.
}
// Add some columns.
table.addColumn(new MockColumn<String, String>());
table.addColumn(new MockColumn<String, String>());
// Negative index.
try {
table.getColumn(-1);
fail("Expected IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
// Expected.
}
// Index too high.
try {
table.getColumn(2);
fail("Expected IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException e) {
// Expected.
}
}
public void testGetRowElement() {
AbstractCellTable<String> table = createAbstractHasData(new TextCell());
table.setRowData(0, createData(0, 10));
// Ensure that calling getRowElement() flushes all pending changes.
assertNotNull(table.getRowElement(9));
}
public void testGetSubRowElement() {
T table = createAbstractHasData(new TextCell());
CellTableBuilder<String> builder = new DefaultCellTableBuilder<String>(table) {
@Override
public void buildRowImpl(String rowValue, int absRowIndex) {
super.buildRowImpl(rowValue, absRowIndex);
// Add some children.
for (int i = 0; i < 4; i++) {
TableRowBuilder tr = startRow();
tr.startTD().colSpan(2).text("child " + absRowIndex + ":" + i).endTD();
tr.endTR();
}
}
};
table.setTableBuilder(builder);
table.setVisibleRange(0, 5);
populateData(table);
table.getPresenter().flush();
// Verify the structure.
TableSectionElement tbody = table.getTableBodyElement();
assertEquals(25, tbody.getChildCount());
// Test sub rows within range.
assertEquals(0, table.getSubRowElement(0, 0).getSectionRowIndex());
assertEquals(1, table.getSubRowElement(0, 1).getSectionRowIndex());
assertEquals(4, table.getSubRowElement(0, 4).getSectionRowIndex());
assertEquals(5, table.getSubRowElement(1, 0).getSectionRowIndex());
assertEquals(8, table.getSubRowElement(1, 3).getSectionRowIndex());
assertEquals(20, table.getSubRowElement(4, 0).getSectionRowIndex());
assertEquals(24, table.getSubRowElement(4, 4).getSectionRowIndex());
// Sub row does not exist within the row.
assertNull(table.getSubRowElement(0, 5));
assertNull(table.getSubRowElement(4, 5));
// Row index out of bounds.
try {
assertNull(table.getSubRowElement(5, 0));
fail("Expected IndexOutOfBoundsException: row index is out of bounds");
} catch (IndexOutOfBoundsException e) {
// Expected.
}
}
public void testHeaderEvent() {
T table = createAbstractHasData();
IndexCell<String> cell = new IndexCell<String>("click");
table.addColumn(new TextColumn<String>() {
@Override
public String getValue(String object) {
return object;
}
}, new Header<String>(cell) {
@Override
public String getValue() {
return "header0";
}
});
RootPanel.get().add(table);
table.setRowData(createData(0, 10));
table.getPresenter().flush();
// Trigger an event on the header.
NativeEvent event = Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
getHeaderElement(table, 0).dispatchEvent(event);
cell.assertLastBrowserEventIndex(0);
RootPanel.get().remove(table);
}
public void testSetHeaderBuilder() {
T table = createAbstractHasData();
HeaderBuilder<String> headerBuilder = new AbstractHeaderOrFooterBuilder<String>(table, false) {
@Override
protected boolean buildHeaderOrFooterImpl() {
TableRowBuilder tr = startRow();
tr.startTH().text("Col 0").endTH();
tr.startTH().text("Col 1").endTH();
tr.startTH().text("Col 2").endTH();
tr.endTR();
return true;
}
};
// Change the header builder.
table.setHeaderBuilder(headerBuilder);
assertEquals(headerBuilder, table.getHeaderBuilder());
table.getPresenter().flush();
// Verify the new header.
NodeList<TableRowElement> rows = table.getTableHeadElement().getRows();
assertEquals(1, rows.getLength());
NodeList<TableCellElement> cells = rows.getItem(0).getCells();
assertEquals(3, cells.getLength());
assertEquals("Col 0", cells.getItem(0).getInnerText());
assertEquals("Col 1", cells.getItem(1).getInnerText());
assertEquals("Col 2", cells.getItem(2).getInnerText());
}
public void testSetFooterBuilder() {
T table = createAbstractHasData();
FooterBuilder<String> footerBuilder = new AbstractHeaderOrFooterBuilder<String>(table, true) {
@Override
protected boolean buildHeaderOrFooterImpl() {
TableRowBuilder tr = startRow();
tr.startTH().text("Col 0").endTH();
tr.startTH().text("Col 1").endTH();
tr.startTH().text("Col 2").endTH();
tr.endTR();
return true;
}
};
// Change the header builder.
table.setFooterBuilder(footerBuilder);
assertEquals(footerBuilder, table.getFooterBuilder());
table.getPresenter().flush();
// Verify the new header.
NodeList<TableRowElement> rows = table.getTableFootElement().getRows();
assertEquals(1, rows.getLength());
NodeList<TableCellElement> cells = rows.getItem(0).getCells();
assertEquals(3, cells.getLength());
assertEquals("Col 0", cells.getItem(0).getInnerText());
assertEquals("Col 1", cells.getItem(1).getInnerText());
assertEquals("Col 2", cells.getItem(2).getInnerText());
}
public void testInsertColumn() {
T table = createAbstractHasData();
assertEquals(0, table.getColumnCount());
// Insert first column.
Column<String, ?> a = new MockColumn<String, String>();
table.insertColumn(0, a);
assertEquals(1, table.getColumnCount());
assertEquals(a, table.getColumn(0));
// Insert column at beginning.
Column<String, ?> b = new MockColumn<String, String>();
table.insertColumn(0, b);
assertEquals(2, table.getColumnCount());
assertEquals(b, table.getColumn(0));
assertEquals(a, table.getColumn(1));
// Insert column at end.
Column<String, ?> c = new MockColumn<String, String>();
table.insertColumn(2, c);
assertEquals(3, table.getColumnCount());
assertEquals(b, table.getColumn(0));
assertEquals(a, table.getColumn(1));
assertEquals(c, table.getColumn(2));
// Insert column in middle.
Column<String, ?> d = new MockColumn<String, String>();
table.insertColumn(1, d);
assertEquals(4, table.getColumnCount());
assertEquals(b, table.getColumn(0));
assertEquals(d, table.getColumn(1));
assertEquals(a, table.getColumn(2));
assertEquals(c, table.getColumn(3));
// Insert column at invalid index.
try {
table.insertColumn(-1, d);
fail("Expected IndexOutOfBoundsExecltion");
} catch (IndexOutOfBoundsException e) {
// Expected.
}
try {
table.insertColumn(6, d);
fail("Expected IndexOutOfBoundsExecltion");
} catch (IndexOutOfBoundsException e) {
// Expected.
}
}
public void testSetAutoFooterRefreshDisabled() {
AbstractCellTable<String> table = createAbstractHasData();
assertFalse(table.isAutoHeaderRefreshDisabled());
assertFalse(table.isAutoFooterRefreshDisabled());
table.setAutoFooterRefreshDisabled(true);
assertFalse(table.isAutoHeaderRefreshDisabled());
assertTrue(table.isAutoFooterRefreshDisabled());
/*
* Inserting a column should render the headers and footers, even if auto
* refresh is disabled.
*/
final List<String> log = new ArrayList<String>();
Column<String, ?> col0 = new MockColumn<String, String>();
TextHeader header0 = new TextHeader("header0") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("header0 rendered");
}
};
TextHeader footer0 = new TextHeader("footer0") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("footer0 rendered");
}
};
table.addColumn(col0, header0, footer0);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header0 rendered", log.remove(0));
assertEquals("footer0 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Inserting another column should render the headers and footers, even if
* auto refresh is disabled.
*/
Column<String, ?> col1 = new MockColumn<String, String>();
TextHeader header1 = new TextHeader("header1") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("header1 rendered");
}
};
TextHeader footer1 = new TextHeader("footer1") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("footer1 rendered");
}
};
table.addColumn(col1, header1, footer1);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header0 rendered", log.remove(0));
assertEquals("header1 rendered", log.remove(0));
assertEquals("footer0 rendered", log.remove(0));
assertEquals("footer1 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Removing a column should render the headers and footers, even if auto
* refresh is disabled.
*/
table.removeColumn(col0);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header1 rendered", log.remove(0));
assertEquals("footer1 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Setting data only causes footers to render if auto refresh is enabled,
* which it is not. Header refresh is still enabled.
*/
populateData(table);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header1 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Sorting a column forces the headers only to refresh. The footers are not
* refreshed.
*/
table.getColumnSortList().push(col1);
assertEquals("header1 rendered", log.remove(0));
assertEquals(0, log.size());
}
public void testSetAutoHeaderRefreshDisabled() {
AbstractCellTable<String> table = createAbstractHasData();
assertFalse(table.isAutoHeaderRefreshDisabled());
assertFalse(table.isAutoFooterRefreshDisabled());
table.setAutoHeaderRefreshDisabled(true);
assertTrue(table.isAutoHeaderRefreshDisabled());
assertFalse(table.isAutoFooterRefreshDisabled());
/*
* Inserting a column should render the headers and footers, even if auto
* refresh is disabled.
*/
final List<String> log = new ArrayList<String>();
Column<String, ?> col0 = new MockColumn<String, String>();
TextHeader header0 = new TextHeader("header0") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("header0 rendered");
}
};
TextHeader footer0 = new TextHeader("footer0") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("footer0 rendered");
}
};
table.addColumn(col0, header0, footer0);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header0 rendered", log.remove(0));
assertEquals("footer0 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Inserting another column should render the headers and footers, even if
* auto refresh is disabled.
*/
Column<String, ?> col1 = new MockColumn<String, String>();
TextHeader header1 = new TextHeader("header1") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("header1 rendered");
}
};
TextHeader footer1 = new TextHeader("footer1") {
@Override
public void render(Context context, SafeHtmlBuilder sb) {
super.render(context, sb);
log.add("footer1 rendered");
}
};
table.addColumn(col1, header1, footer1);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header0 rendered", log.remove(0));
assertEquals("header1 rendered", log.remove(0));
assertEquals("footer0 rendered", log.remove(0));
assertEquals("footer1 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Removing a column should render the headers and footers, even if auto
* refresh is disabled.
*/
table.removeColumn(col0);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("header1 rendered", log.remove(0));
assertEquals("footer1 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Setting data only causes headers to render if auto refresh is enabled,
* which it is not. Footer refresh is still enabled.
*/
populateData(table);
assertEquals(0, log.size()); // Headers are rendered asynchronously.
table.getPresenter().flush(); // Force headers to render.
assertEquals("footer1 rendered", log.remove(0));
assertEquals(0, log.size());
/*
* Sorting a column forces the headers only to refresh. The footers are not
* refreshed.
*/
table.getColumnSortList().push(col1);
assertEquals("header1 rendered", log.remove(0));
assertEquals(0, log.size());
}
public void testSetColumnWidth() {
AbstractCellTable<String> table = createAbstractHasData(new TextCell());
Column<String, ?> col0 = new MockColumn<String, String>();
Column<String, ?> col1 = new MockColumn<String, String>();
Column<String, ?> col2 = new MockColumn<String, String>();
// Set a column width.
table.setColumnWidth(col0, "100px");
table.setColumnWidth(col1, 200.0, Unit.EM);
assertEquals("100px", table.getColumnWidth(col0));
// Some browsers return 200.0, others 200.
assertTrue(table.getColumnWidth(col1).contains("200"));
// Check a column that has not been set.
assertNull(table.getColumnWidth(col2));
// Check a column that has been cleared.
table.clearColumnWidth(col0);
assertNull(table.getColumnWidth(col0));
}
/**
* Test that setting column widths using columns and column indexes works
* correctly.
*/
public void testSetColumnWidthMixed() {
AbstractCellTable<String> table = createAbstractHasData(new TextCell());
Column<String, ?> col0 = new MockColumn<String, String>();
Column<String, ?> col1 = new MockColumn<String, String>();
Column<String, ?> col2 = new MockColumn<String, String>();
// Column 0 set by Column.
table.setColumnWidth(col0, "100px");
// Column 1 set by Column and column index.
table.setColumnWidth(col1, 200.0, Unit.EM);
table.setColumnWidth(1, "210em");
// Column 2 set by column index.
table.setColumnWidth(2, "300px");
assertEquals("100px", table.getColumnWidth(col0));
assertEquals("300px", table.getColumnWidth(2));
/*
* Some browsers return 200.0, others 200. Column takes precendence over
* column index.
*/
assertTrue(table.getColumnWidth(col1).contains("200"));
// Check a column that has not been set.
assertNull(table.getColumnWidth(col2));
// Check a column that has been cleared.
table.clearColumnWidth(col0);
assertNull(table.getColumnWidth(col0));
}
public void testSetEmptyTableWidget() {
AbstractCellTable<String> table = createAbstractHasData(new TextCell());
// Set a widget.
Label l = new Label("Empty");
table.setEmptyTableWidget(l);
assertEquals(l, table.getEmptyTableWidget());
// Null widget.
table.setEmptyTableWidget(null);
assertNull(table.getEmptyTableWidget());
}
public void testSetKeyboardSelectedRow() {
AbstractCellTable<String> table = createAbstractHasData(new TextCell());
table.setVisibleRange(0, 10);
// Without a subrow.
table.setKeyboardSelectedRow(5);
assertEquals(5, table.getKeyboardSelectedRow());
assertEquals(0, table.getKeyboardSelectedSubRow());
// Specify a subrow.
table.setKeyboardSelectedRow(6, 2, false);
assertEquals(6, table.getKeyboardSelectedRow());
assertEquals(2, table.getKeyboardSelectedSubRow());
// Change the subrow.
table.setKeyboardSelectedRow(6, 5, false);
assertEquals(6, table.getKeyboardSelectedRow());
assertEquals(5, table.getKeyboardSelectedSubRow());
// Change the row.
table.setKeyboardSelectedRow(7);
assertEquals(7, table.getKeyboardSelectedRow());
assertEquals(0, table.getKeyboardSelectedSubRow());
}
public void testSetLoadingIndicator() {
AbstractCellTable<String> table = createAbstractHasData(new TextCell());
// Set a widget.
Label l = new Label("Loading");
table.setLoadingIndicator(l);
assertEquals(l, table.getLoadingIndicator());
// Null widget.
table.setLoadingIndicator(null);
assertNull(table.getLoadingIndicator());
}
public void testSortableColumn() {
T table = createAbstractHasData(new TextCell());
table.getColumn(0).setSortable(true);
table.getPresenter().flush();
RootPanel.get().add(table);
// Add a column sort handler.
final List<Column<?, ?>> lastSorted = new ArrayList<Column<?, ?>>();
table.addColumnSortHandler(new ColumnSortEvent.Handler() {
@Override
public void onColumnSort(ColumnSortEvent event) {
lastSorted.clear();
lastSorted.add(event.getColumn());
}
});
// Default sort order is empty.
ColumnSortList sortList = table.getColumnSortList();
assertEquals(0, sortList.size());
// Sort a column that is sortable.
NativeEvent click = Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
getHeaderElement(table, 0).dispatchEvent(click);
assertEquals(1, sortList.size());
assertEquals(table.getColumn(0), sortList.get(0).getColumn());
assertTrue(sortList.get(0).isAscending());
assertEquals(1, lastSorted.size());
lastSorted.clear();
// Sort the same column again.
getHeaderElement(table, 0).dispatchEvent(click);
assertEquals(1, sortList.size());
assertEquals(table.getColumn(0), sortList.get(0).getColumn());
assertFalse(sortList.get(0).isAscending());
assertEquals(1, lastSorted.size());
lastSorted.clear();
// Sort the same column again, this time using the Enter key.
NativeEvent enter = Document.get().createKeyDownEvent(false, false, false, false,
KeyCodes.KEY_ENTER);
getHeaderElement(table, 0).dispatchEvent(enter);
assertEquals(1, sortList.size());
assertEquals(table.getColumn(0), sortList.get(0).getColumn());
assertTrue(sortList.get(0).isAscending());
assertEquals(1, lastSorted.size());
lastSorted.clear();
// Sort the same column using the Enter key again.
getHeaderElement(table, 0).dispatchEvent(enter);
assertEquals(1, sortList.size());
assertEquals(table.getColumn(0), sortList.get(0).getColumn());
assertFalse(sortList.get(0).isAscending());
assertEquals(1, lastSorted.size());
lastSorted.clear();
// Sort a column that is not sortable.
getHeaderElement(table, 1).dispatchEvent(click);
assertEquals(1, sortList.size());
assertEquals(table.getColumn(0), sortList.get(0).getColumn());
assertFalse(sortList.get(0).isAscending());
assertEquals(0, lastSorted.size());
// Cleanup.
RootPanel.get().remove(table);
}
/**
* Create an empty cell table.
*/
protected abstract T createAbstractHasData();
@Override
protected final T createAbstractHasData(Cell<String> cell) {
T table = createAbstractHasData();
table.addColumn(new Column<String, String>(cell) {
@Override
public String getValue(String object) {
return object;
}
}, "Column 0");
table.addColumn(new Column<String, String>(new TextCell()) {
@Override
public String getValue(String object) {
return object + "-2";
}
}, "Column 1");
return table;
}
/**
* Get a td element from the table body.
*
* @param table the {@link CellTable}
* @param row the row index
* @param column the column index
* @return the column header
*/
protected abstract TableCellElement getBodyElement(T table, int row, int column);
/**
* Get the number of column headers in the table.
*
* @param table the {@link CellTable}
* @return the number of column headers
*/
protected abstract int getHeaderCount(T table);
/**
* Get a column header from the table.
*
* @param table the {@link CellTable}
* @param column the column index
* @return the column header
*/
protected abstract TableCellElement getHeaderElement(T table, int column);
}