Skip to content
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
163 changes: 163 additions & 0 deletions examples/fixed_column_width.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<!--

Copyright (c) 2020, the Regular Table Authors.

This file is part of the Regular Table library, distributed under the terms of
the Apache License 2.0. The full license can be found in the LICENSE file.

-->

<!--
This is an example of how to set up columns with fixed width just using
Javascript and CSS rules.

The default behaviour that regular-table implements for this feature includes:
* The default width of the cells corresponding to a column is calculated
taking into account the size of the content of the visible cells for that column.
* It is only possible to increase the width of a cell up to the size of the
content currently visible for that column.
* It is possible to manually shrink the column width up to the limit of 10 pixels.
* Column widths are calculated by the library using the max-width css rule. Which
means that settings the max-width from a css rule will lead to a fixed width
behavior for the cells of that column.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these list-item hard wraps need to be indented https://www.markdownguide.org/basic-syntax/#adding-elements-in-lists

-->

<!DOCTYPE html>
<html>

<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<script src="../dist/umd/regular-table.js"></script>
<link rel='stylesheet' href="../dist/css/material.css">
</head>

<body>

<regular-table></regular-table>

<script>
class CreateDataModel {
constructor(baseColumns, columnCount, cellCount) {
this._baseColumns = baseColumns;
this._columnCount = columnCount;
this._cellCount = cellCount;
this._dataset = this._createDataset();
this.columns = this._createColumns();
this._data = this.columns.map(({key}) => this._dataset[key]);
this._columnHeaders = this.columns.map(({value}) => [value]);
}

_createTextCells(text) {
return Array.from(Array(this._cellCount).keys()).map((idx) => `${text} ${idx}`);
}

_createColumns() {
return Array.from(Array(this._columnCount)).map((_, idx) => {
const key = this._baseColumns[idx % this._baseColumns.length];
return {
key,
value: `${key} Column ${idx}`,
};
});
}

_createDataset() {
return this._baseColumns.reduce(
(prev, curr) => ({
...prev,
[curr]: this._createTextCells(curr),
}),
{}
);
}

dataListener = (x0, y0, x1, y1) => {
const data = this._data.slice(x0, x1).map((col) => col.slice(y0, y1));
const column_headers = this._columnHeaders.slice(x0, x1);
const num_columns = this._data.length;
const num_rows = this._data[0].length;
return {
num_rows,
num_columns,
column_headers,
data,
};
};
}

const baseColumns = ["Fixed", "Not Set"];
const columnCount = 20;
const cellCount = 1000;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are also CONSTANTS and belong at the top of the script


// Create data model that returns a dataListener function and a columns array.
const dataModel = new CreateDataModel(baseColumns, columnCount, cellCount);

// Get Regular Table element.
const tableApi = document.getElementsByTagName("regular-table")[0];
const FIXED_CLASS = "fixed";

// Clear previous cell manipulations done by this api.
function clear(cellElement) {
cellElement.classList.remove(FIXED_CLASS);
cellElement.style.minWidth = "";
}

function getColumnName(index) {
return dataModel.columns[index].key;
}

// Check if cell should apply fixed min-width.
function isFixed(cellElement) {
// Use regular-table api to get cell metadata.
const metadata = tableApi.getMeta(cellElement);
const name = getColumnName(metadata.cidx);
return name.includes("Fixed");
}

// Add "fixed" class to cell element.
function setFixedClass(cellElement) {
cellElement.classList.add(FIXED_CLASS);
}

// Set fixed min-width to cells when appropiate.
function styleListener() {
const ths = tableApi.querySelectorAll("thead th");
const tds = tableApi.querySelectorAll("tbody td");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be

const elems = tableApi.querySelectorAll("thead th, tbody td");

The DOM API is very expressive already!

// Iterate over all rendered cells.
for (const cellElement of [...ths, ...tds]) {
clear(cellElement);
if (isFixed(cellElement)) setFixedClass(cellElement);
}
}

window.addEventListener("DOMContentLoaded", async () => {
// Pass dataListener function to regular-table api.
tableApi.setDataListener(dataModel.dataListener);

// Attach styleListener function to a listener to apply changes
// after the grid is updated.
tableApi.addStyleListener(styleListener);

// Trigger table draw method to make listeners run.
await tableApi.draw();
});
</script>
<style>
/* Set fixed width for cells. */
.fixed {
min-width: 100px !important;
max-width: 100px !important;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems important - I'd put it at the top!

}
/* Disable default header text selection. */
thead tr th {
user-select: none;
}
/* Do not allow content to overflow cell limit. */
tr th, tr td {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
</body>
</html>
86 changes: 86 additions & 0 deletions test/fixed_column_width.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/******************************************************************************
*
* Copyright (c) 2020, the Regular Table Authors.
*
* This file is part of the Regular Table library, distributed under the terms
* of the Apache License 2.0. The full license can be found in the LICENSE
* file.
*
*/

describe("fixed_column_width.html", () => {
beforeAll(async () => {
await page.setViewport({width: 400, height: 100});
});

describe("creates a `<table>` with fixed column widths", () => {
beforeAll(async () => {
await page.goto("http://localhost:8081/examples/fixed_column_width.html");
await page.waitFor("regular-table table tbody tr td");
});

test("fixed th has min-width", async () => {
const first_tr = await page.$("regular-table thead tr:first-child");
const minWidths = await page.evaluate((first_tr) => Array.from(first_tr.children)
.map((x) => getComputedStyle(x).getPropertyValue('min-width')), first_tr);
const fixedWidth = minWidths[0];
const notSetWidth = minWidths[1];
expect(fixedWidth).toEqual("100px");
expect(notSetWidth).toEqual("0px");
});

test("fixed th has max-width", async () => {
const first_tr = await page.$("regular-table thead tr:first-child");
const max_widths = await page.evaluate((first_tr) => Array.from(first_tr.children)
.map((x) => getComputedStyle(x).getPropertyValue('max-width')), first_tr);
const fixed_width = max_widths[0];
const not_set_width = max_widths[1];
expect(fixed_width).toEqual("100px");
expect(not_set_width).toEqual("none");
});


test("fixed td has min-width", async () => {
const first_tr = await page.$("regular-table tbody tr:first-child");
const minWidths = await page.evaluate((first_tr) => Array.from(first_tr.children)
.map((x) => getComputedStyle(x).getPropertyValue('min-width')), first_tr);
const fixedWidth = minWidths[0];
const notSetWidth = minWidths[1];
expect(fixedWidth).toEqual("100px");
expect(notSetWidth).toEqual("0px");
});

test("fixed td has max-width", async () => {
const first_tr = await page.$("regular-table tbody tr:first-child");
const max_widths = await page.evaluate((first_tr) => Array.from(first_tr.children)
.map((x) => getComputedStyle(x).getPropertyValue('max-width')), first_tr);
const fixed_width = max_widths[0];
const not_set_width = max_widths[1];
expect(fixed_width).toEqual("100px");
expect(not_set_width).toEqual("none");
});

test("cell value do not overflow", async () => {
const first_td = await page.$("regular-table tbody tr td:first-child");
const { text_overflow, overflow, white_space } = await page.evaluate((first_td) => {
first_td.text_content = 'ABCDEFGHABCDEFGHABCDEFGHABCDEFGH';
const styles = getComputedStyle(first_td);
return {
text_overflow: styles.getPropertyValue('text-overflow'),
overflow: styles.getPropertyValue('overflow'),
white_space: styles.getPropertyValue('white-space')
};
}, first_td);
expect(text_overflow).toEqual("ellipsis");
expect(overflow).toEqual("hidden");
expect(white_space).toEqual("nowrap");
});

test("ths do not allow text selection", async () => {
const first_tr = await page.$("regular-table thead tr:first-child");
const user_selects = await page.evaluate((first_tr) => Array.from(first_tr.children)
.map((x) => getComputedStyle(x).getPropertyValue('user-select')), first_tr);
expect(user_selects).toEqual(["none", "none", "none", "none"]);
});
});
});