` Custom Element](#regular-table-custom-element)
+ - [`.setDataListener()` Virtual Data Model](#setdatalistener-virtual-data-model)
+ - [Column and Row Headers](#column-and-row-headers)
+ - [Hierarchial/Group Headers](#hierarchialgroup-headers)
+ - [`async` Data Models](#async-data-models)
+ - [`.addStyleListener()` and `getMeta()` Styling](#addstylelistener-and-getmeta-styling)
+ - [`.invalidate()`](#invalidate)
+ - [`.addEventListener()` Interaction](#addeventlistener-interaction)
+ - [Scrolling](#scrolling)
+ - [Pivots, Filters, Sorts, and Column Expressions with `perspective`](#pivots-filters-sorts-and-column-expressions-with-perspective)
+ - [Development](#development)
+
+- [API Docs](https://github.com/finos/regular-table/blob/master/api.md)
+
+- Annotated Examples
+ - [2d_array.md](examples/2d_array/2d_array.js)
+ - [canvas_data_model.md](examples/canvas_data_model/canvas_data_model.js)
+ - [file_browser.md](examples/file_browser/file_browser.js)
+ - [minesweeper.md](examples/minesweeper/minesweeper.js)
+ - [react.md](examples/react/react.js)
+ - [spreadsheet.md](examples/spreadsheet/spreadsheet.js)
+ - [two_billion_rows.md](examples/two_billion_rows/two_billion_rows.js)
## Installation
Include via a CDN like [JSDelivr](https://cdn.jsdelivr.net/npm/regular-table):
```html
-
+
` along the non-virtual axis(es), and may cause rendering performance
degradation.
-- "both" (default) virtualizes scrolling on both axes.
-- "vertical" only virtualizes vertical (y) scrolling.
-- "horizontal" only virtualizes horizontal (x) scrolling.
-- "none" disable all scroll virtualization.
+- "both" (default) virtualizes scrolling on both axes.
+- "vertical" only virtualizes vertical (y) scrolling.
+- "horizontal" only virtualizes horizontal (x) scrolling.
+- "none" disable all scroll virtualization.
```javascript
table.setDataListener(listener, { virtual_mode: "vertical" });
@@ -383,14 +381,14 @@ field will accompany the metadata records returned by `regular-table`'s
Additional rendering options which can be set on the object returned by a
`setDataListener` callback include:
-- `column_header_merge_depth: number` configures the number of rows to include
- from `colspan` merging. This defaults to `header_length - 1`.
-- `row_height: number` configures the pixel height of a row for virtual
- scrolling calculation. This is typically auto-detected from the DOM, but can
- be overridden if needed.
-- `merge_headers: "column" | "row" | "both" | "none"` configures whether
- equivalent, contiguous `` elements are merged via `rowspan` or `colspan`
- for `"row"` and `"column"` respectively (defaults to `"both"`).
+- `column_header_merge_depth: number` configures the number of rows to include
+ from `colspan` merging. This defaults to `header_length - 1`.
+- `row_height: number` configures the pixel height of a row for virtual
+ scrolling calculation. This is typically auto-detected from the DOM, but can
+ be overridden if needed.
+- `merge_headers: "column" | "row" | "both" | "none"` configures whether
+ equivalent, contiguous ` ` elements are merged via `rowspan` or `colspan`
+ for `"row"` and `"column"` respectively (defaults to `"both"`).
### `async` Data Models
@@ -445,11 +443,11 @@ However, CSS alone cannot select on properties of your _data_ - if you scroll
this example, the 2nd row will always be the striped one. Some other
data-reliant style examples include:
-- Styling a specific column in the virtual data set, as ` ` may represent a
- different column based on horizontal scroll position.
-- Styling cells by value, +/-, heatmaps, categories, etc.
-- Styling cells based on data within-or-outside of the virtual viewport,
- grouping depth, grouping categories, etc.
+- Styling a specific column in the virtual data set, as ` ` may represent a
+ different column based on horizontal scroll position.
+- Styling cells by value, +/-, heatmaps, categories, etc.
+- Styling cells based on data within-or-outside of the virtual viewport,
+ grouping depth, grouping categories, etc.
To make CSS that is virtual-data-model-aware, you'll need to use
`addStyleListener()`, which invokes a callback whenever the `` is
@@ -605,4 +603,4 @@ The Regular Table project achieves the
## License
This software is licensed under the Apache 2.0 license. See the
-[LICENSE](LICENSE) and [AUTHORS](AUTHORS) files for details.
+[LICENSE](LICENSE) file for details.
diff --git a/api.md b/api.md
deleted file mode 100644
index 595e38ea..00000000
--- a/api.md
+++ /dev/null
@@ -1,339 +0,0 @@
-## Classes
-
-
-RegularTableElement ⇐ HTMLElement
-The <regular-table> custom element.
-This module has no exports, but importing it has a side effect: the
-RegularTableElement class is registered as a custom element, after which
-it can be used as a standard DOM element.
-The documentation in this module defines the instance structure of a
-<regular-table> DOM object instantiated typically, through HTML or any
-relevent DOM method e.g. document.createElement("perspective-viewer") or
-document.getElementsByTagName("perspective-viewer").
-
-
-
-## Typedefs
-
-
-Performance : object
-An object with performance statistics about calls to
-draw() from some time interval (captured in milliseconds by the
-elapsed proprty).
-
-MetaData : object
-An object describing virtual rendering metadata about an
-HTMLTableCellElement, use this object to map rendered <th> or <td>
-elements back to your data, row_headers or column_headers within
-listener functions for addStyleListener() and addEventListener().
-
-DataResponse : object
-The DataResponse object describes a rectangular region of a virtual
-data set, and some associated metadata. <regular-table> will use this
-object to render the <table>, though it may make multiple requests for
-different regions to achieve a compelte render as it must estimate
-certain dimensions. You must construct a DataResponse object to
-implement a DataListener.
-
-DataListener ⇒ Promise.<DataResponse>
-The DataListener is similar to a normal event listener function.
-Unlike a normal event listener, it takes regular arguments (not an
-Event); and returns a Promise for a DataResponse object for this
-region (as opposed to returning void as a standard event listener).
-
-
-
-
-
-## RegularTableElement ⇐ HTMLElement
-The `` custom element.
-
-This module has no exports, but importing it has a side effect: the
-`RegularTableElement` class is registered as a custom element, after which
-it can be used as a standard DOM element.
-
-The documentation in this module defines the instance structure of a
-`` DOM object instantiated typically, through HTML or any
-relevent DOM method e.g. `document.createElement("perspective-viewer")` or
-`document.getElementsByTagName("perspective-viewer")`.
-
-**Kind**: global class
-**Extends**: HTMLElement
-**Access**: public
-
-* [RegularTableElement](#RegularTableElement) ⇐ HTMLElement
- * [.addStyleListener(styleListener)](#RegularTableElement+addStyleListener) ⇒ number
- * [.getMeta(element)](#RegularTableElement+getMeta) ⇒ [MetaData](#MetaData)
- * [.getDrawFPS()](#RegularTableElement+getDrawFPS) ⇒ [Performance](#Performance)
- * [.scrollToCell(x, y, ncols, nrows)](#RegularTableElement+scrollToCell)
- * [.setDataListener(dataListener)](#RegularTableElement+setDataListener)
-
-
-* * *
-
-
-
-### regularTableElement.addStyleListener(styleListener) ⇒ number
-Adds a style listener callback. The style listeners are called
-whenever the is re-rendered, such as through API invocations
-of draw() and user-initiated events such as scrolling. Within this
-optionally async callback, you can select , , etc. elements
-via regular DOM API methods like querySelectorAll().
-
-**Kind**: instance method of [RegularTableElement](#RegularTableElement)
-**Returns**: number - The index of the added listener.
-**Access**: public
-
-| Param | Type | Description |
-| --- | --- | --- |
-| styleListener | function | A (possibly async) function that styles the inner . |
-
-**Example**
-```js
-table.addStyleListener(() => {
- for (const td of table.querySelectorAll("td")) {
- td.setAttribute("contenteditable", true);
- }
-});
-```
-
-* * *
-
-
-
-### regularTableElement.getMeta(element) ⇒ [MetaData](#MetaData)
-Returns the `MetaData` object associated with a `` or ` `. When
-your `StyleListener` is invoked, use this method to look up additional
-`MetaData` about any `HTMLTableCellElement` in the rendered ``.
-
-**Kind**: instance method of [RegularTableElement](#RegularTableElement)
-**Returns**: [MetaData](#MetaData) - The metadata associated with the element.
-**Access**: public
-
-| Param | Type | Description |
-| --- | --- | --- |
-| element | HTMLTableCellElement \| [Partial.<MetaData>](#MetaData) | The child element of this `` for which to look up metadata, or a coordinates-like object to refer to metadata by logical position. |
-
-**Example**
-```js
-const elems = document.querySelector("td:last-child td:last_child");
-const metadata = table.getMeta(elems);
-console.log(`Viewport corner is ${metadata.x}, ${metadata.y}`);
-```
-**Example**
-```js
-const header = table.getMeta({row_header_x: 1, y: 3}).row_header;
-```
-
-* * *
-
-
-
-### regularTableElement.getDrawFPS() ⇒ [Performance](#Performance)
-Get performance statistics about this ``. Calling this
-method resets the internal state, which makes it convenient to measure
-performance at regular intervals (see example).
-
-**Kind**: instance method of [RegularTableElement](#RegularTableElement)
-**Returns**: [Performance](#Performance) - Performance data aggregated since the last
-call to `getDrawFPS()`.
-**Access**: public
-**Example**
-```js
-const table = document.getElementById("my_regular_table");
-setInterval(() => {
- const {real_fps} = table.getDrawFPS();
- console.log(`Measured ${fps} fps`)
-});
-```
-
-* * *
-
-
-
-### regularTableElement.scrollToCell(x, y, ncols, nrows)
-Call this method to set the `scrollLeft` and `scrollTop` for this
-`` by calculating the position of this `scrollLeft`
-and `scrollTop` relative to the underlying widths of its columns
-and heights of its rows.
-
-**Kind**: instance method of [RegularTableElement](#RegularTableElement)
-**Access**: public
-
-| Param | Type | Description |
-| --- | --- | --- |
-| x | number | The left most `x` index column to scroll into view. |
-| y | number | The top most `y` index row to scroll into view. |
-| ncols | number | Total number of columns in the data model. |
-| nrows | number | Total number of rows in the data model. |
-
-**Example**
-```js
-table.scrollToCell(1, 3, 10, 30);
-```
-
-* * *
-
-
-
-### regularTableElement.setDataListener(dataListener)
-Call this method to set `DataListener` for this ``,
-which will be called whenever a new data slice is needed to render.
-Calls to `draw()` will fail if no `DataListener` has been set
-
-**Kind**: instance method of [RegularTableElement](#RegularTableElement)
-**Access**: public
-
-| Param | Type | Description |
-| --- | --- | --- |
-| dataListener | [DataListener](#DataListener) | `dataListener` is called by to request a rectangular section of data for a virtual viewport, (x0, y0, x1, y1), and returns a `DataReponse` object. |
-
-**Example**
-```js
-table.setDataListener((x0, y0, x1, y1) => {
- return {
- num_rows: num_rows = DATA[0].length,
- num_columns: DATA.length,
- data: DATA.slice(x0, x1).map(col => col.slice(y0, y1))
- };
-})
-```
-
-* * *
-
-
-
-## Performance : object
-An object with performance statistics about calls to
-`draw()` from some time interval (captured in milliseconds by the
-`elapsed` proprty).
-
-**Kind**: global typedef
-**Properties**
-
-| Name | Type | Description |
-| --- | --- | --- |
-| avg | number | Avergage milliseconds per call. |
-| real_fps | number | `num_frames` / `elapsed` |
-| virtual_fps | number | `elapsed` / `avg` |
-| num_frames | number | Number of frames rendered. |
-| elapsed | number | Number of milliseconds since last call to `getDrawFPS()`. |
-
-
-* * *
-
-
-
-## MetaData : object
-An object describing virtual rendering metadata about an
-`HTMLTableCellElement`, use this object to map rendered `` or ` `
-elements back to your `data`, `row_headers` or `column_headers` within
-listener functions for `addStyleListener()` and `addEventListener()`.
-
-**Kind**: global typedef
-**Properties**
-
-| Name | Type | Description |
-| --- | --- | --- |
-| [x] | number | The `x` index in your virtual data model. property is only generated for ` `, ` ` from `row_headers`. |
-| [y] | number | The `y` index in your virtual data model. property is only generated for ` `, ` ` from `row_headers`. |
-| [x0] | number | The `x` index of the viewport origin in your data model, e.g. what was passed to `x0` when your `dataListener` was invoked. |
-| [y0] | number | The `y` index of the viewport origin in your data model, e.g. what was passed to `y0` when your `dataListener` was invoked. |
-| [x1] | number | The `x` index of the viewport corner in your data model, e.g. what was passed to `x1` when your `dataListener` was invoked. |
-| [y1] | number | The `y` index of the viewport origin in your data model, e.g. what was passed to `y1` when your `dataListener` was invoked. |
-| [dx] | number | The `x` index in `DataResponse.data`, this property is only generated for ` `, and ` ` from `column_headers`. |
-| [dy] | number | The `y` index in `DataResponse.data`, this property is only generated for ` `, ` ` from `row_headers`. |
-| [column_header_y] | number | The `y` index in `DataResponse.column_headers[x]`, this property is only generated for ` ` from `column_headers`. |
-| [column_header_x] | number | The `x` index in `DataResponse.row_headers[y]`, this property is only generated for ` ` from `row_headers`. |
-| size_key | number | The unique index of this column in a full ``, which is `x` + (Total Row Header Columns). |
-| [row_header] | Array.<object> | The `Array` for this `y` in `DataResponse.row_headers`, if it was provided. |
-| [column_header] | Array.<object> | The `Array` for this `x` in `DataResponse.column_headers`, if it was provided. |
-
-**Example**
-```js
-MetaData (x = 0, column_header_y = 0)
- *-------------------------------------+
- | |
- | |
- +-------------------------------------+
-(row_header_x = 0, y = 0) (x = 0, y = 0)
-*------------------------+ *-------------------------------------+
-| | | |
-| | | (x0, y0) |
-| | | *---------------* |
-| | | | | |
-| | | | * (x, y) | |
-| | | | | |
-| | | *---------------* (x1, y1) |
-| | | |
-+------------------------+ +-------------------------------------+
-```
-
-* * *
-
-
-
-## DataResponse : object
-The `DataResponse` object describes a rectangular region of a virtual
-data set, and some associated metadata. `` will use this
-object to render the ``, though it may make multiple requests for
-different regions to achieve a compelte render as it must estimate
-certain dimensions. You must construct a `DataResponse` object to
-implement a `DataListener`.
-
-**Kind**: global typedef
-**Properties**
-
-| Name | Type | Description |
-| --- | --- | --- |
-| [column_headers] | Array.<Array.<object>> | A two dimensional `Array` of column group headers, in specificity order. No `` will be generated if this property is not provided. |
-| [row_headers] | Array.<Array.<object>> | A two dimensional `Array` of row group headers, in specificity order. No `` elements within ` ` will be generated if this property is not provided. |
-| data | Array.<Array.<object>> | A two dimensional `Array` representing a rectangular section of the underlying data set from (x0, y0) to (x1, y1), arranged in columnar fashion such that `data[x][y]` returns the `y`th row of the `x`th column of the slice. |
-| num_rows | number | Total number of rows in the underlying data set. |
-| num_columns | number | Total number of columns in the underlying data set. |
-
-**Example**
-```js
-{
- "num_rows": 26,
- "num_columns": 3,
- "data": [
- [0, 1],
- ["A", "B"]
- ],
- "row_headers": [
- ["Rowgroup 1", "Row 1"],
- ["Rowgroup 1", "Row 2"]
- ],
- "column_headers": [
- ["Colgroup 1", "Column 1"],
- ["Colgroup 1", "Column 2"]
- ]
-}
-```
-
-* * *
-
-
-
-## DataListener ⇒ [Promise.<DataResponse>](#DataResponse)
-The `DataListener` is similar to a normal event listener function.
-Unlike a normal event listener, it takes regular arguments (not an
-`Event`); and returns a `Promise` for a `DataResponse` object for this
-region (as opposed to returning `void` as a standard event listener).
-
-**Kind**: global typedef
-**Returns**: [Promise.<DataResponse>](#DataResponse) - The resulting `DataResponse`. Make sure
-to `resolve` or `reject` the `Promise`, or your `` will
-never render!
-
-| Param | Type | Description |
-| --- | --- | --- |
-| x0 | number | The origin `x` index (column). |
-| y0 | number | The origin `y` index (row). |
-| x1 | number | The corner `x` index (column). |
-| y1 | number | The corner `y` index (row). |
-
-
-* * *
-
diff --git a/build.js b/build.mjs
similarity index 83%
rename from build.js
rename to build.mjs
index 9524e141..d3a08c43 100644
--- a/build.js
+++ b/build.mjs
@@ -9,24 +9,28 @@
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-const esbuild = require("esbuild");
-const { BuildCss } = require("@prospective.co/procss/target/cjs/procss.js");
-const fs = require("fs");
-const path_mod = require("path");
+import esbuild from "esbuild";
+import { BuildCss } from "@prospective.co/procss/target/cjs/procss.js";
+import * as fs from "node:fs";
+import * as path_mod from "node:path";
+import { execSync } from "node:child_process";
const BUILD = [
{
- entryPoints: ["src/js/index.js"],
+ entryPoints: ["src/ts/regular-table.ts"],
format: "esm",
loader: {
".css": "text",
".html": "text",
},
outfile: "dist/esm/regular-table.js",
- target: ["es2021"],
+ target: ["ESNext"],
bundle: true,
minify: !process.env.PSP_DEBUG,
- minify: true,
+ minifySyntax: true,
+ minifyIdentifiers: true,
+ minifyWhitespace: true,
+ mangleProps: /^[_#]/,
sourcemap: true,
metafile: true,
entryNames: "[name]",
@@ -71,8 +75,20 @@ async function compile_css() {
);
}
+async function compile_types() {
+ console.log("\nCompiling TypeScript declarations...");
+ try {
+ execSync("tsc -p tsconfig.json", { stdio: "inherit" });
+ console.log("TypeScript declarations compiled successfully");
+ } catch (error) {
+ console.error("Failed to compile TypeScript declarations");
+ throw error;
+ }
+}
+
async function build_all() {
await compile_css();
+ await compile_types();
await Promise.all(
BUILD.map(async (x) => {
console.log("");
diff --git a/declarationsconfig.json b/declarationsconfig.json
deleted file mode 100644
index d5448f55..00000000
--- a/declarationsconfig.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "$schema": "http://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "allowJs": true,
- "checkJs": false,
- "declaration": true,
- "emitDeclarationOnly": true,
- "experimentalDecorators": true,
- "module": "esnext",
- "moduleResolution": "node",
- "target": "esnext",
-
- "outDir": "declarations",
- "rootDir": "src/js"
- },
- "include": ["src/js/index.js"]
- }
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index c4cb69ef..74a29f25 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -6,7 +6,7 @@ const darkCodeTheme = require("prism-react-renderer/themes/dracula");
const fs = require("fs");
-fs.cpSync("../dist/examples", "static/blocks", { recursive: true });
+fs.cpSync("../examples", "static/blocks", { recursive: true });
console.log("\n");
function make(name, type = "examples") {
@@ -25,7 +25,7 @@ function make(name, type = "examples") {
console.log(
`
- `
+`,
);
return record;
@@ -75,8 +75,7 @@ const config = {
],
stylesheets: [
{
- href:
- "https://fonts.googleapis.com/css?display=block&family=Roboto+Mono:400",
+ href: "https://fonts.googleapis.com/css?display=block&family=Roboto+Mono:400",
type: "text/css",
},
],
diff --git a/examples/2d_array.md b/examples/2d_array.md
deleted file mode 100644
index 9c82b857..00000000
--- a/examples/2d_array.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# Simple Example
-
-You'll need to create a ``. The easiest way is to just add one
-directly to your page's HTML, and we'll give it an `id` attribute to refer to it
-easily later. Fun fact - elements with `id` attributes are accessible on the
-global `window` Object in Javascript via `window.${id}`, at least
-[maybe](https://stackoverflow.com/questions/18713272/why-do-dom-elements-exist-as-properties-on-the-window-object).
-
-```html
-
-```
-
-Let's start with with a simple data model, a two dimensional `Array`. This one
-is very small at 3 columns x 26 rows, but even for very small data sets,
-`regular-table` won't read your entire dataset at once. Instead, we'll need to
-write a simple _virtual_ data model to access `DATA` and `COLUMN_NAMES`
-indirectly.
-
-```javascript
-const DATA = [
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
- ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"],
- Array.from(Array(15).keys()).map((value) => value % 2 === 0),
-];
-```
-
-When clipped by the scrollable viewport, you may end up with a `` of just
-a rectangular region of `DATA`, rather than the entire set. A simple viewport
-2x2 may yield this ``:
-
-
-
-
-0
-A
-
-
-1
-B
-
-
-
-
-```json
-{
- "num_rows": 15,
- "num_columns": 3,
- "data": [
- [0, 1],
- ["A", "B"]
- ]
-}
-```
-
-Here's a an implementation for this simple _virtual_ data model, the function
-`dataListener()`. This function is called by your `` whenever it
-needs more data, with coordinate arguments, `(x0, y0)` to `(x1, y1)`. Only this
-region is needed to render the viewport, so `dataListener()` returns this
-rectangular `slice` of `DATA`. For the window (0, 0) to (2, 2), `dataListener()`
-would generate an Object as above, containing the `data` slice, as well as the
-overall dimensions of `DATA` itself ( `num_rows`, `num_columns`), for sizing the
-scroll area. To render this virtual data model to a regular HTML ``,
-register this data model via the `setDataListener()` method:
-
-```javascript
-export function dataListener(x0, y0, x1, y1) {
- return {
- num_rows: DATA[0].length,
- num_columns: DATA.length,
- data: DATA.slice(x0, x1).map((col) => col.slice(y0, y1)),
- };
-}
-```
-
-You can register and invoke this table thusly:
-
-```javascript
-export function init() {
- window.regularTable.setDataListener(dataListener);
- window.regularTable.draw();
-}
-```
-
-... which we'll do on the Window `"load"` event.
-
-```html
-
-```
-
-# Appendix (Dependencies)
-
-```html
-
-
-```
-
-```block
-license: apache-2.0
-```
diff --git a/examples/2d_array/2d_array.js b/examples/2d_array/2d_array.js
new file mode 100644
index 00000000..9e9a5ec9
--- /dev/null
+++ b/examples/2d_array/2d_array.js
@@ -0,0 +1,56 @@
+// # Simple Example
+
+import "/dist/esm/regular-table.js";
+
+// Let's start with with a simple data model, a two dimensional `Array`. This one
+// is very small at 3 columns x 26 rows, but even for very small data sets,
+// `regular-table` won't read your entire dataset at once. Instead, we'll need to
+// write a simple _virtual_ data model to access `DATA` and `COLUMN_NAMES`
+// indirectly.
+
+const DATA = [
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
+ ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"],
+ Array.from(Array(15).keys()).map((value) => value % 2 === 0),
+];
+
+// When clipped by the scrollable viewport, you may end up with a `` of just
+// a rectangular region of `DATA`, rather than the entire set. A simple viewport
+// 2x2 may yield this ``:
+//
+// ```html
+//
+//
+//
+// 0
+// A
+//
+//
+// 1
+// B
+//
+//
+//
+// ```
+//
+// ```json
+// {
+// "num_rows": 15,
+// "num_columns": 3,
+// "data": [
+// [0, 1],
+// ["A", "B"]
+// ]
+// }
+// ```
+
+export function dataListener(x0, y0, x1, y1) {
+ return {
+ num_rows: DATA[0].length,
+ num_columns: DATA.length,
+ data: DATA.slice(x0, x1).map((col) => col.slice(y0, y1)),
+ };
+}
+
+window.regularTable.setDataListener(dataListener);
+window.regularTable.draw();
diff --git a/examples/2d_array/index.html b/examples/2d_array/index.html
new file mode 100644
index 00000000..f00324c9
--- /dev/null
+++ b/examples/2d_array/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/3d_array.html b/examples/3d_array.html
deleted file mode 100644
index 6447d150..00000000
--- a/examples/3d_array.html
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/benchmark.html b/examples/benchmark.html
index c123c5ea..e8171b18 100644
--- a/examples/benchmark.html
+++ b/examples/benchmark.html
@@ -18,7 +18,7 @@
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
-
+
-
+ this.shadowRoot.adoptedStyleSheets = [
+ containerStyleSheet,
+ this._sub_cell_style,
+ ];
+
+ const slot = ` `;
+ this.shadowRoot.innerHTML = `
${slot}
`;
- const [, style, virtual_panel, table_clip] = this.shadowRoot.children;
- this._sub_cell_style = style;
- this._table_clip = table_clip;
- this._virtual_panel = virtual_panel;
+ const [virtual_panel, table_clip] = this.shadowRoot!.children;
+ this._table_clip = table_clip as HTMLElement;
+ this._virtual_panel = virtual_panel as HTMLElement;
this._setup_virtual_scroll();
}
@@ -120,17 +157,14 @@ export class RegularVirtualTableViewModel extends HTMLElement {
/**
* Calculates the `viewport` argument for perspective's `to_columns` method.
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @param {*} nrows
* @returns
*/
- _calculate_viewport(nrows, num_columns) {
+ _calculate_viewport(nrows: number, num_columns: number): Viewport {
const { start_row, end_row } = this._calculate_row_range(nrows);
const { start_col, end_col } =
this._calculate_column_range(num_columns);
- this._nrows = nrows;
+
return { start_col, end_col, start_row, end_row };
}
@@ -167,16 +201,16 @@ export class RegularVirtualTableViewModel extends HTMLElement {
* | |
* 600px +--------------------------+
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @param {*} nrows
* @returns
*/
- _calculate_row_range(nrows) {
+ _calculate_row_range(nrows: number): {
+ start_row: number;
+ end_row: number;
+ } {
const { height, containerHeight } = this._container_size;
const row_height = this._column_sizes.row_height || 19;
- const header_levels = this._view_cache.config.column_pivots.length;
+ const header_levels = this._view_cache.column_headers_length;
const total_scroll_height = Math.max(
1,
this._virtual_panel.offsetHeight - containerHeight,
@@ -199,8 +233,8 @@ export class RegularVirtualTableViewModel extends HTMLElement {
return { start_row, end_row };
}
- _calc_start_column() {
- const scroll_index_offset = this._view_cache.config.row_pivots.length;
+ _calc_start_column(): number {
+ const scroll_index_offset = this._view_cache.row_headers_length;
let start_col = 0;
let offset_width = 0;
let diff = 0;
@@ -224,12 +258,12 @@ export class RegularVirtualTableViewModel extends HTMLElement {
* details of which are actually calculated in `_max_column`, the equivalent
* of `total_scroll_height` from `_calculate_row_range`.
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @returns
*/
- _calculate_column_range(num_columns) {
+ _calculate_column_range(num_columns: number): {
+ start_col: number;
+ end_col: number;
+ } {
if (
this._virtual_mode === "none" ||
this._virtual_mode === "vertical"
@@ -268,22 +302,20 @@ export class RegularVirtualTableViewModel extends HTMLElement {
* | | 80px | 110px | 100px |
* | | | | |
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @returns
*/
- _max_scroll_column(num_columns) {
+ _max_scroll_column(num_columns: number): number {
let width = 0;
- if (this._view_cache.config.row_pivots.length > 0) {
+ if (this._view_cache.row_headers_length > 0) {
for (const w of this._column_sizes.indices.slice(
0,
- this._view_cache.config.row_pivots.length,
+ this._view_cache.row_headers_length,
)) {
- width += w;
+ width += w || 0;
}
}
- let scroll_index_offset = this._view_cache.config.row_pivots.length;
+
+ let scroll_index_offset = this._view_cache.row_headers_length;
let max_scroll_column = num_columns;
while (width < this._container_size.width && max_scroll_column >= 0) {
max_scroll_column--;
@@ -302,13 +334,15 @@ export class RegularVirtualTableViewModel extends HTMLElement {
* e.g. when the logical (row-wise) viewport does not change, but the pixel
* viewport has moved a few px.
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @param {*} {start_col, end_col, start_row, end_row}
* @returns
*/
- _validate_viewport({ start_col, end_col, start_row, end_row }) {
+ _validate_viewport({
+ start_col,
+ end_col,
+ start_row,
+ end_row,
+ }: Viewport): ViewportValidation {
start_row = Math.floor(start_row);
end_row = Math.ceil(end_row);
start_col = Math.floor(start_col);
@@ -325,8 +359,8 @@ export class RegularVirtualTableViewModel extends HTMLElement {
return { invalid_column, invalid_row };
}
- _calc_scrollable_column_width(num_columns) {
- let scroll_index_offset = this._view_cache.config.row_pivots.length;
+ _calc_scrollable_column_width(num_columns: number): number {
+ let scroll_index_offset = this._view_cache.row_headers_length;
const max_scroll_column = this._max_scroll_column(num_columns);
let cidx = scroll_index_offset,
virtual_width = 0;
@@ -338,12 +372,12 @@ export class RegularVirtualTableViewModel extends HTMLElement {
if (cidx < this._column_sizes.indices.length) {
let viewport_width = this._column_sizes.indices
- .slice(0, this._view_cache.config.row_pivots.length)
- .reduce((x, y) => x + y, 0);
+ .slice(0, this._view_cache.row_headers_length)
+ .reduce((x, y) => (x || 0) + (y || 0), 0);
virtual_width += Math.max(
0,
- this._column_sizes.indices[cidx] -
- (this._container_size.width - viewport_width) || 0,
+ (this._column_sizes.indices[cidx] || 0) -
+ (this._container_size.width - (viewport_width || 0)) || 0,
);
}
@@ -353,20 +387,19 @@ export class RegularVirtualTableViewModel extends HTMLElement {
/**
* Updates the `virtual_panel` width based on view state.
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @param {*} invalid
*/
- _update_virtual_panel_width(invalid, num_columns) {
+ _update_virtual_panel_width(invalid: boolean, num_columns: number): void {
if (invalid) {
if (
this._virtual_mode === "vertical" ||
this._virtual_mode === "none"
) {
this._virtual_panel.style.width =
- this._column_sizes.indices.reduce((x, y) => x + y, 0) +
- "px";
+ this._column_sizes.indices.reduce(
+ (x, y) => (x || 0) + (y || 0),
+ 0,
+ ) + "px";
} else {
const virtual_width =
this._calc_scrollable_column_width(num_columns);
@@ -384,15 +417,12 @@ export class RegularVirtualTableViewModel extends HTMLElement {
/**
* Updates the `virtual_panel` height based on the view state.
*
- * @internal
- * @private
- * @memberof RegularVirtualTableViewModel
* @param {*} nrows
*/
- _update_virtual_panel_height(nrows) {
+ _update_virtual_panel_height(nrows: number): void {
const { row_height = 19 } = this._column_sizes;
const header_height =
- this._view_cache.config.column_pivots.length * row_height;
+ this._view_cache.column_headers_length * row_height;
let virtual_panel_px_size;
virtual_panel_px_size = Math.min(
BROWSER_MAX_HEIGHT,
@@ -406,14 +436,12 @@ export class RegularVirtualTableViewModel extends HTMLElement {
* the implementor to fine tune the individual render frames based on the
* interaction and previous render state.
*
- * @public
- * @memberof RegularVirtualTableViewModel
* @param {DrawOptions} [options]
* @param {boolean} [options.invalid_viewport=true]
* @param {boolean} [options.preserve_width=false]
* @param {boolean} [options.throttle=true]
*/
- async draw(options = {}) {
+ async draw(options: DrawOptions = {}): Promise {
if (typeof options.throttle !== "undefined" && !options.throttle) {
return await internal_draw.call(this, options);
} else {
@@ -423,30 +451,31 @@ export class RegularVirtualTableViewModel extends HTMLElement {
}
}
- async _draw_flush() {
+ async flush(): Promise {
await flush_tag(this);
}
- update_sub_cell_offset(viewport) {
+ update_sub_cell_offset(viewport: Viewport): void {
const y_offset =
- this._column_sizes.row_height * (viewport.start_row % 1) || 0;
+ (this._column_sizes.row_height || 20) * (viewport.start_row % 1) ||
+ 0;
+
const x_offset =
- this._column_sizes.indices[
+ (this._column_sizes.indices[
(this.table_model._row_headers_length || 0) +
Math.floor(viewport.start_col)
- ] *
+ ] || 0) *
(viewport.start_col % 1) || 0;
- let style = this._sub_cell_style.sheet?.cssRules[0].style;
- if (style) {
- style.setProperty(`--regular-table--clip-x`, `${x_offset}px`);
- style.setProperty(`--regular-table--clip-y`, `${y_offset}px`);
- style.setProperty(`--regular-table--transform-x`, `-${x_offset}px`);
- style.setProperty(`--regular-table--transform-y`, `-${y_offset}px`);
- }
+
+ const cssText = CSS_TEMPLATE(x_offset, y_offset);
+ this._sub_cell_style.replaceSync(cssText);
}
}
-async function internal_draw(options) {
+async function internal_draw(
+ this: RegularVirtualTableViewModel,
+ options: DrawOptions,
+): Promise {
const __debug_start_time__ = DEBUG && performance.now();
const { invalid_viewport = true, preserve_width = false } = options;
const {
@@ -458,66 +487,67 @@ async function internal_draw(options) {
} = await this._view_cache.view(0, 0, 0, 0);
this._column_sizes.row_height = row_height || this._column_sizes.row_height;
if (num_row_headers !== undefined) {
- this._view_cache.row_pivots = Array(num_row_headers).fill(0);
+ this._view_cache.row_headers_length = num_row_headers;
}
if (num_column_headers !== undefined) {
- this._view_cache.column_pivots = Array(num_column_headers).fill(0);
+ this._view_cache.column_headers_length = num_column_headers;
}
+ // Cache virtual mode checks and default values
+ const is_non_vertical =
+ this._virtual_mode === "none" || this._virtual_mode === "horizontal";
+ const is_non_horizontal =
+ this._virtual_mode === "none" || this._virtual_mode === "vertical";
+ const safe_num_rows = num_rows || 0;
+ const safe_num_columns = num_columns || 0;
this._container_size = {
- width:
- this._virtual_mode === "none" || this._virtual_mode === "vertical"
- ? Infinity
- : this._table_clip.clientWidth,
- height:
- this._virtual_mode === "none" || this._virtual_mode === "horizontal"
- ? Infinity
- : this._table_clip.clientHeight,
- containerHeight:
- this._virtual_mode === "none" || this._virtual_mode === "horizontal"
- ? Infinity
- : this.clientHeight,
+ width: is_non_horizontal ? Infinity : this._table_clip.clientWidth,
+ height: is_non_vertical ? Infinity : this._table_clip.clientHeight,
+ containerHeight: is_non_vertical ? Infinity : this.clientHeight,
};
- this._update_virtual_panel_height(num_rows);
+ this._update_virtual_panel_height(safe_num_rows);
if (!preserve_width) {
- this._update_virtual_panel_width(invalid_viewport, num_columns);
+ this._update_virtual_panel_width(invalid_viewport, safe_num_columns);
}
- const viewport = this._calculate_viewport(num_rows, num_columns);
+
+ const viewport = this._calculate_viewport(safe_num_rows, safe_num_columns);
+ // this.table_model.clearWidthStyles();
+ this.table_model.updateColumnWidthStyles(
+ viewport,
+ this._view_cache.row_headers_length,
+ );
+
const { invalid_row, invalid_column } = this._validate_viewport(viewport);
- if (
- this._invalid_schema ||
- invalid_row ||
- invalid_column ||
- invalid_viewport
- ) {
- let autosize_cells = [],
- needs_sub_cell_update = true;
+ const invalid_schema_or_column = this._invalid_schema || invalid_column;
+ if (invalid_schema_or_column || invalid_row || invalid_viewport) {
+ let autosize_cells: CellTuple[] = [];
+ let first_iteration = true;
for await (let last_cells of this.table_model.draw(
this._container_size,
this._view_cache,
this._selected_id,
preserve_width,
viewport,
- num_columns,
+ safe_num_columns,
)) {
if (last_cells !== undefined) {
- autosize_cells = autosize_cells.concat(last_cells);
+ autosize_cells.push(...last_cells);
}
// We want to perform this before the next event loop so there
// is no scroll jitter, but only on the first iteration as
// subsequent viewports are incorrect.
- if (needs_sub_cell_update) {
+ if (first_iteration) {
this.update_sub_cell_offset(viewport);
- needs_sub_cell_update = false;
+ first_iteration = false;
}
this._is_styling = true;
const callbacks = this._style_callbacks;
for (const callback of callbacks) {
- await callback({ detail: this });
+ await callback({ detail: this as RegularTableElement });
}
this._is_styling = false;
@@ -528,19 +558,20 @@ async function internal_draw(options) {
this._invalidated = false;
}
- const old_height = this.table_model._column_sizes.row_height;
+ const old_height = this._column_sizes.row_height;
this.table_model.autosize_cells(autosize_cells, row_height);
this.table_model.header.reset_header_cache();
- if (old_height !== this.table_model._column_sizes.row_height) {
- this._update_virtual_panel_height(num_rows);
+ if (old_height !== this._column_sizes.row_height) {
+ this._update_virtual_panel_height(safe_num_rows);
}
if (!preserve_width) {
this._update_virtual_panel_width(
- this._invalid_schema || invalid_column,
- num_columns,
+ invalid_schema_or_column,
+ safe_num_columns,
);
}
+
this._invalid_schema = false;
} else {
this.update_sub_cell_offset(viewport);
@@ -550,13 +581,3 @@ async function internal_draw(options) {
log_perf(performance.now() - __debug_start_time__);
}
}
-
-/**
- * Options for the draw method.
- *
- * @public
- * @typedef DrawOptions
- * @type {object}
- * @property {boolean} [invalid_viewport]
- * @property {boolean} [preserve_width]
- */
diff --git a/src/ts/table.ts b/src/ts/table.ts
new file mode 100644
index 00000000..dcacfb6e
--- /dev/null
+++ b/src/ts/table.ts
@@ -0,0 +1,728 @@
+// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
+// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
+// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
+// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
+// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
+// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
+// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+import { RegularHeaderViewModel } from "./thead";
+import { RegularBodyViewModel } from "./tbody";
+import {
+ BodyDrawResult,
+ CellScalar,
+ CellTuple,
+ CellMetadata,
+ ColumnState,
+ DataColumnDrawResult,
+ FetchResult,
+ RowHeadersResult,
+ ViewCache,
+ ViewFunction,
+ Viewport,
+ DataResponse,
+ ViewState,
+} from "./types";
+import { ColumnSizes } from "./types";
+
+/**
+ * Base class containing protected helper methods for table rendering.
+ * This class provides the internal implementation details for drawing
+ * and managing table view state.
+ *
+ * @class RegularTableViewModelBase
+ */
+abstract class RegularTableViewModelBase {
+ public _column_sizes!: ColumnSizes;
+ public _row_headers_length: number = 0;
+ public header!: RegularHeaderViewModel;
+ public body!: RegularBodyViewModel;
+
+ /**
+ * Initializes view state with viewport and sizing information.
+ */
+ protected _initializeViewState(
+ viewport: Viewport,
+ selected_id: number | undefined,
+ ): ViewState {
+ const {
+ start_row: ridx_offset = 0,
+ start_col: x0 = 0,
+ end_col: x1 = 0,
+ end_row: y1 = 0,
+ } = viewport;
+
+ const sub_cell_offset =
+ this._column_sizes.indices[
+ (this._row_headers_length || 0) + Math.floor(viewport.start_col)
+ ] || 0;
+
+ return {
+ viewport_width: 0,
+ selected_id,
+ ridx_offset,
+ sub_cell_offset,
+ x0: x0,
+ x1: x1,
+ y1: y1,
+ row_height: this._column_sizes.row_height,
+ row_headers_length: this._row_headers_length,
+ };
+ }
+
+ /**
+ * Draws row headers and returns updated state.
+ */
+ protected _drawRowHeaders(
+ row_headers: CellScalar[][],
+ row_headers_length: number,
+ column_headers_length: number,
+ container_height: number,
+ view_state: ViewState,
+ preserve_width: boolean,
+ x0: number,
+ column_header_merge_depth: number | undefined,
+ merge_column_headers: boolean,
+ merge_row_headers: boolean,
+ ): RowHeadersResult {
+ const column_name = [`${row_headers_length}`];
+ const last_cells: CellTuple[] = [];
+
+ const column_state: ColumnState = {
+ column_name,
+ cidx: 0,
+ column_data: row_headers,
+ row_headers,
+ first_col: true,
+ };
+
+ const size_key = Math.floor(x0);
+ const cont_body = this.body.draw(
+ container_height,
+ column_state,
+ { ...view_state, x0: 0 },
+ true,
+ undefined,
+ undefined,
+ size_key,
+ merge_row_headers,
+ );
+
+ const cont_heads = [];
+ for (let i = 0; i < row_headers_length; i++) {
+ const header = this.header.draw(
+ column_name,
+ Array(column_headers_length).fill(""),
+ 1,
+ undefined,
+ i,
+ x0,
+ i,
+ column_header_merge_depth,
+ merge_column_headers,
+ );
+ if (!!header) {
+ cont_heads.push(header);
+ }
+ }
+
+ view_state.viewport_width += cont_heads.reduce(
+ (total, { th }, i) =>
+ total + (this._column_sizes.indices[i] || th.offsetWidth),
+ 0,
+ );
+ view_state.row_height = view_state.row_height || cont_body.row_height;
+
+ const _virtual_x = row_headers[0].length;
+
+ if (!preserve_width) {
+ for (let i = 0; i < row_headers_length; i++) {
+ const { td, metadata } = cont_body.tds[i] || {};
+ const { th, metadata: hmetadata } = cont_heads[i] || {};
+ if (!!td || !!th) {
+ last_cells.push([th || td, hmetadata || metadata]);
+ }
+ }
+ }
+
+ return { cont_body, first_col: false, _virtual_x, last_cells };
+ }
+
+ /**
+ * Calculates how many additional columns are needed to fill the viewport.
+ */
+ protected _calculateViewportExtension(
+ viewport: Viewport,
+ view_state: ViewState,
+ container_width: number,
+ num_columns: number,
+ _virtual_x: number,
+ x0: number,
+ ): void {
+ let end_col_offset = 0,
+ size_extension = 0;
+
+ while (
+ this._column_sizes.indices.length >
+ _virtual_x + x0 + end_col_offset + 1 &&
+ size_extension + view_state.viewport_width < container_width
+ ) {
+ end_col_offset++;
+ size_extension +=
+ this._column_sizes.indices[_virtual_x + x0 + end_col_offset] ||
+ 0;
+ }
+
+ if (size_extension + view_state.viewport_width < container_width) {
+ const estimate = Math.min(num_columns, viewport.start_col + 5);
+ viewport.end_col = Math.max(1, Math.min(num_columns, estimate));
+ } else {
+ viewport.end_col = Math.max(
+ 1,
+ Math.min(num_columns, viewport.start_col + end_col_offset),
+ );
+ }
+ }
+
+ /**
+ * Draws a single data column and returns rendering information.
+ */
+ protected _drawDataColumn(
+ dcidx: number,
+ view_response: DataResponse,
+ _virtual_x: number,
+ x0: number,
+ container_height: number,
+ view_state: ViewState,
+ first_col: boolean,
+ column_header_merge_depth: number | undefined,
+ merge_column_headers: boolean,
+ merge_row_headers: boolean,
+ ): DataColumnDrawResult {
+ const column_name = view_response.column_headers?.[dcidx] || [];
+ const column_data = view_response.data[dcidx];
+ const column_data_listener_metadata = view_response.metadata?.[dcidx];
+ const column_state: ColumnState = {
+ column_name,
+ cidx: _virtual_x,
+ column_data,
+ column_data_listener_metadata,
+ row_headers: view_response.row_headers,
+ first_col,
+ };
+
+ const x = dcidx + x0;
+ const size_key = _virtual_x + Math.floor(x0);
+ const cont_head = this.header.draw(
+ column_name,
+ column_name,
+ undefined,
+ x,
+ size_key,
+ x0,
+ _virtual_x,
+ column_header_merge_depth,
+ merge_column_headers,
+ );
+
+ const cont_body = this.body.draw(
+ container_height,
+ column_state,
+ view_state,
+ false,
+ x,
+ x0,
+ size_key,
+ merge_row_headers,
+ );
+
+ return { cont_head, cont_body };
+ }
+
+ /**
+ * Fetches additional columns when data is missing during rendering.
+ */
+ protected async _fetchMissingColumns(
+ viewport: Viewport,
+ view: ViewFunction,
+ view_response: DataResponse,
+ dcidx: number,
+ view_state: ViewState,
+ container_width: number,
+ num_columns: number,
+ _virtual_x: number,
+ x0: number,
+ ): Promise {
+ let missing_cidx = Math.max(viewport.end_col, 0);
+ viewport.start_col = missing_cidx;
+ this._calculateViewportExtension(
+ viewport,
+ view_state,
+ container_width,
+ num_columns,
+ _virtual_x,
+ x0,
+ );
+
+ const new_col = await view(
+ Math.floor(viewport.start_col),
+ Math.floor(viewport.start_row),
+ Math.ceil(viewport.end_col),
+ Math.ceil(viewport.end_row),
+ );
+
+ let column_header_merge_depth: number | undefined;
+ let merge_headers: "both" | "row" | "column" | undefined;
+
+ if (typeof new_col.column_header_merge_depth !== "undefined") {
+ column_header_merge_depth = new_col.column_header_merge_depth;
+ }
+
+ if (typeof new_col.merge_headers !== "undefined") {
+ merge_headers = new_col.merge_headers;
+ }
+
+ if (new_col.data.length === 0) {
+ return { column_header_merge_depth, merge_headers };
+ }
+
+ viewport.end_col = viewport.start_col + new_col.data.length;
+ for (let i = 0; i < new_col.data.length; i++) {
+ view_response.data[dcidx + i] = new_col.data[i];
+ if (new_col.metadata && view_response.metadata) {
+ view_response.metadata[dcidx + i] = new_col.metadata[i];
+ }
+
+ if (view_response.column_headers && new_col.column_headers?.[i]) {
+ view_response.column_headers[dcidx + i] =
+ new_col.column_headers[i];
+ }
+ }
+
+ return { column_header_merge_depth, merge_headers };
+ }
+
+ /**
+ * Cleans up body and header after drawing.
+ */
+ protected _cleanupAfterDraw(
+ cont_body: BodyDrawResult | undefined,
+ _virtual_x: number,
+ ): void {
+ this.body.clean({ ridx: cont_body?.ridx || 0, cidx: _virtual_x });
+ this.header.clean();
+ this.body._span_factory.reset();
+ this.header._span_factory.reset();
+ }
+}
+
+/**
+ * view model. In order to handle unknown column width when `draw()`
+ * is called, this model will iteratively fetch more data to fill in columns
+ * until the page is complete, and makes some column viewport estimations
+ * when this information is not availble.
+ *
+ * @class RegularTableViewModel
+ */
+export class RegularTableViewModel extends RegularTableViewModelBase {
+ public table: HTMLTableElement;
+ public fragment: DocumentFragment;
+
+ constructor(
+ table_clip: HTMLElement,
+ column_sizes: ColumnSizes,
+ element: HTMLElement,
+ ) {
+ super();
+ this._column_sizes = column_sizes;
+ this.clear(element);
+ const [table] = element.children as HTMLCollectionOf;
+ const [thead, tbody] =
+ table.children as HTMLCollectionOf;
+
+ this.table = table;
+ this.header = new RegularHeaderViewModel(
+ column_sizes,
+ table_clip,
+ thead,
+ );
+
+ this.body = new RegularBodyViewModel(column_sizes, table_clip, tbody);
+ this.fragment = document.createDocumentFragment();
+ }
+
+ num_columns(): number {
+ return this.header.num_columns();
+ }
+
+ clear(element: HTMLElement): void {
+ element.innerHTML =
+ '';
+ }
+
+ /**
+ * Calculate amendments to auto size from this render pass.
+ * Uses adoptedStyleSheets with :nth-child selectors for optimal performance.
+ *
+ * This method separates DOM reads (getBoundingClientRect) from DOM writes
+ * (CSS stylesheet updates) to minimize forced reflows. All measurements are
+ * collected first, then column size data is updated, and finally all column
+ * widths (both auto and override) are applied via a single stylesheet update.
+ */
+ autosize_cells(
+ last_cells: CellTuple[],
+ override_row_height?: number,
+ ): void {
+ // PHASE 1: READ - Collect all layout measurements first
+ const measurements: Array<{
+ cell: HTMLElement;
+ metadata: CellMetadata | undefined;
+ box: DOMRect;
+ }> = [];
+
+ for (const [cell, metadata] of last_cells) {
+ const box = cell.getBoundingClientRect();
+ measurements.push({ cell, metadata, box });
+ }
+
+ // PHASE 2: PROCESS - Update column sizes
+ for (const { metadata, box } of measurements) {
+ this._column_sizes.row_height =
+ override_row_height ??
+ Math.max(
+ 10,
+ Math.min(
+ this._column_sizes.row_height ?? box.height,
+ box.height,
+ ),
+ );
+
+ if (metadata?.size_key !== undefined) {
+ this._column_sizes.indices[metadata.size_key] = box.width;
+ if (
+ box.width &&
+ this._column_sizes.override[metadata.size_key] === undefined
+ ) {
+ this._column_sizes.auto[metadata.size_key] = box.width;
+ }
+ }
+ }
+ }
+
+ clearWidthStyles() {
+ this._columnWidthStyleSheet?.replaceSync("");
+ }
+
+ /**
+ * Updates column width styles for all columns using adoptedStyleSheets.
+ * Generates CSS rules with :nth-child selectors for both auto-sized and
+ * overridden column widths, applying them all in a single stylesheet update.
+ *
+ * This method should be called whenever column sizes change, including:
+ * - After autosize_cells() measurements
+ * - When user resizes columns
+ * - When resetAutoSize() is called
+ */
+ updateColumnWidthStyles(
+ viewport: Viewport,
+ row_headers_length: number,
+ ): void {
+ const cssRules: string[] = [];
+
+ let row_headers_size_key;
+ for (
+ row_headers_size_key = 0;
+ row_headers_size_key < row_headers_length;
+ row_headers_size_key++
+ ) {
+ const override_width =
+ this._column_sizes.override[row_headers_size_key];
+ const auto_width = this._column_sizes.auto[row_headers_size_key];
+ if (override_width !== undefined) {
+ // Override width takes precedence
+ // CSS :nth-child is 1-indexed
+ const columnIndex = row_headers_size_key + 1; // - Math.floor(viewport.start_col);
+
+ cssRules.push(
+ `thead tr.rt-autosize th:nth-child(${columnIndex}),`,
+ `tbody td.rt-cell-clip:nth-child(${columnIndex})`,
+ `{min-width:${override_width}px;max-width:${override_width}px;}`,
+ );
+ } else if (auto_width !== undefined) {
+ // Auto width applies when no override
+ const columnIndex = row_headers_size_key + 1; // - Math.floor(viewport.start_col);
+ cssRules.push(
+ `thead tr.rt-autosize th:nth-child(${columnIndex}),`,
+ `tbody td.rt-cell-clip:nth-child(${columnIndex})`,
+ `{min-width:${auto_width}px;max-width:none;}`,
+ );
+ }
+ }
+
+ for (
+ let size_key = row_headers_size_key; //Math.floor(viewport.start_col);
+ size_key <
+ row_headers_size_key +
+ (Math.ceil(viewport.end_col) - Math.floor(viewport.start_col));
+ size_key++
+ ) {
+ const override_width =
+ this._column_sizes.override[
+ size_key + Math.floor(viewport.start_col)
+ ];
+ const auto_width =
+ this._column_sizes.auto[
+ size_key + Math.floor(viewport.start_col)
+ ];
+
+ if (override_width !== undefined) {
+ // Override width takes precedence
+ // CSS :nth-child is 1-indexed
+ const columnIndex = size_key + 1; // Math.floor(viewport.start_col);
+
+ cssRules.push(
+ `thead tr.rt-autosize th:nth-child(${columnIndex}),`,
+ `tbody td.rt-cell-clip:nth-child(${columnIndex})`,
+ `{min-width:${override_width}px;max-width:${override_width}px;}`,
+ );
+ } else if (auto_width !== undefined) {
+ // Auto width applies when no override
+ const columnIndex = size_key + 1; // Math.floor(viewport.start_col);
+ cssRules.push(
+ `thead tr.rt-autosize th:nth-child(${columnIndex}),`,
+ `tbody td.rt-cell-clip:nth-child(${columnIndex})`,
+ `{min-width:${auto_width}px;max-width:none;}`,
+ );
+ }
+ }
+
+ // Apply all rules via single stylesheet update
+ if (cssRules.length > 0) {
+ this._applyColumnWidthStyles(cssRules.join("\n"));
+ } else if (this._columnWidthStyleSheet) {
+ // Clear stylesheet if no rules
+ this._columnWidthStyleSheet.replaceSync("");
+ }
+ }
+
+ /**
+ * Applies column width styles using adoptedStyleSheets.
+ * Creates or updates a dedicated stylesheet for column widths.
+ */
+ private _applyColumnWidthStyles(css: string): void {
+ // Access the shadowRoot through the table's container
+ const shadowRoot = this.table.getRootNode() as ShadowRoot;
+
+ if (!shadowRoot || !shadowRoot.adoptedStyleSheets) {
+ return;
+ }
+
+ // Find or create the column width stylesheet
+ if (!this._columnWidthStyleSheet) {
+ this._columnWidthStyleSheet = new CSSStyleSheet();
+ shadowRoot.adoptedStyleSheets = [
+ ...shadowRoot.adoptedStyleSheets,
+ this._columnWidthStyleSheet,
+ ];
+ }
+
+ this._columnWidthStyleSheet.replaceSync(css);
+ }
+
+ private _columnWidthStyleSheet?: CSSStyleSheet;
+
+ async *draw(
+ container_size: { width: number; height: number },
+ view_cache: ViewCache,
+ selected_id: number | undefined,
+ preserve_width: boolean,
+ viewport: Viewport,
+ num_columns: number,
+ ): AsyncGenerator {
+ const { width: container_width, height: container_height } =
+ container_size;
+
+ // Fetch and prepare initial data
+ const view_response = await view_cache.view(
+ Math.floor(viewport.start_col),
+ Math.floor(viewport.start_row),
+ Math.ceil(viewport.end_col),
+ Math.ceil(viewport.end_row),
+ );
+
+ let { column_header_merge_depth, merge_headers = "both" } =
+ view_response;
+
+ const merge_row_headers =
+ merge_headers === "both" || merge_headers === "row";
+ const merge_column_headers =
+ merge_headers === "both" || merge_headers === "column";
+ const x0 = viewport.start_col ?? 0;
+
+ if (view_response.row_headers) {
+ this._row_headers_length = view_response.row_headers.reduce(
+ (max, x) => Math.max(max, x.length),
+ 0,
+ );
+
+ for (let i = 0; i < view_response.row_headers.length; i++) {
+ view_response.row_headers[i].length =
+ this._row_headers_length || 0;
+ }
+ }
+
+ // Update view cache lengths
+ view_cache.row_headers_length =
+ view_response.num_row_headers ??
+ view_response.row_headers?.[0]?.length ??
+ 0;
+
+ view_cache.column_headers_length =
+ view_response.num_column_headers ??
+ view_response.column_headers?.[0]?.length ??
+ 0;
+
+ const { view, row_headers_length, column_headers_length } = view_cache;
+ const view_state = this._initializeViewState(viewport, selected_id);
+
+ // Draw row headers
+ let cont_body: BodyDrawResult | undefined;
+ let _virtual_x = 0;
+ let last_cells: CellTuple[] = [];
+ let first_col = true;
+
+ if (view_response.row_headers?.length) {
+ const row_header_result = this._drawRowHeaders(
+ view_response.row_headers,
+ row_headers_length,
+ column_headers_length,
+ container_height,
+ view_state,
+ preserve_width,
+ x0,
+ column_header_merge_depth,
+ merge_column_headers,
+ merge_row_headers,
+ );
+ cont_body = row_header_result.cont_body;
+ first_col = row_header_result.first_col;
+ _virtual_x = row_header_result._virtual_x;
+ last_cells = row_header_result.last_cells;
+ }
+
+ // Draw data columns
+ try {
+ let dcidx = 0;
+ const num_visible_columns = num_columns - viewport.start_col;
+
+ while (dcidx < num_visible_columns) {
+ // Fetch missing columns if needed
+ if (!view_response.data[dcidx]) {
+ const fetch_result = await this._fetchMissingColumns(
+ viewport,
+ view,
+ view_response,
+ dcidx,
+ view_state,
+ container_width,
+ num_columns,
+ _virtual_x,
+ x0,
+ );
+
+ yield undefined;
+
+ if (fetch_result.column_header_merge_depth !== undefined) {
+ column_header_merge_depth =
+ fetch_result.column_header_merge_depth;
+ }
+ if (fetch_result.merge_headers !== undefined) {
+ merge_headers = fetch_result.merge_headers;
+ }
+ if (!view_response.data[dcidx]) {
+ this._cleanupAfterDraw(cont_body, _virtual_x);
+ yield last_cells;
+ return;
+ }
+ }
+
+ // Draw column
+ const { cont_head, cont_body: drawn_body } =
+ this._drawDataColumn(
+ dcidx,
+ view_response,
+ _virtual_x,
+ x0,
+ container_height,
+ view_state,
+ first_col,
+ column_header_merge_depth,
+ merge_column_headers,
+ merge_row_headers,
+ );
+
+ cont_body = drawn_body;
+ first_col = false;
+
+ // Collect cells for autosizing
+ if (!preserve_width) {
+ for (const { td, metadata } of cont_body.tds) {
+ last_cells.push([
+ cont_head?.th || td,
+ cont_head?.metadata || metadata,
+ ]);
+ }
+ }
+
+ // Update dimensions
+ const col_width =
+ this._column_sizes.indices[_virtual_x + Math.floor(x0)] ||
+ cont_head?.th?.offsetWidth ||
+ cont_body.tds.reduce(
+ (sum, { td }) => sum + (td?.offsetWidth || 0),
+ 0,
+ );
+ view_state.viewport_width += col_width;
+ view_state.row_height =
+ view_state.row_height || cont_body.row_height;
+
+ _virtual_x++;
+ dcidx++;
+
+ // Check if viewport filled
+ if (this._isViewportFilled(view_state, container_width)) {
+ this._cleanupAfterDraw(cont_body, _virtual_x);
+ yield last_cells;
+
+ // Recalculate after style listeners
+ view_state.viewport_width = last_cells.reduce(
+ (sum, [td]) => sum + td.offsetWidth,
+ 0,
+ );
+
+ if (this._isViewportFilled(view_state, container_width)) {
+ return;
+ }
+ }
+ }
+
+ this._cleanupAfterDraw(cont_body, _virtual_x);
+ yield last_cells;
+ } finally {
+ this._cleanupAfterDraw(cont_body, _virtual_x);
+ }
+ }
+
+ private _isViewportFilled(
+ view_state: ViewState,
+ container_width: number,
+ ): boolean {
+ return (
+ view_state.viewport_width - view_state.sub_cell_offset >
+ container_width
+ );
+ }
+}
diff --git a/src/js/tbody.js b/src/ts/tbody.ts
similarity index 54%
rename from src/js/tbody.js
rename to src/ts/tbody.ts
index e4a93d1a..4d460755 100644
--- a/src/js/tbody.js
+++ b/src/ts/tbody.ts
@@ -9,6 +9,13 @@
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+import {
+ BodyDrawResult,
+ CellMetadata,
+ CellScalar,
+ ColumnState,
+ ViewState,
+} from "./types";
import { ViewModel } from "./view_model";
/**
@@ -18,31 +25,32 @@ import { ViewModel } from "./view_model";
*/
export class RegularBodyViewModel extends ViewModel {
_draw_td(
- tagName,
- ridx,
- val,
- cidx,
- { column_name },
- { ridx_offset },
- size_key,
- ) {
+ tagName: string,
+ ridx: number,
+ val: unknown,
+ cidx: number,
+ { column_name }: ColumnState,
+ { ridx_offset }: ViewState,
+ size_key: number,
+ ): { td: HTMLTableCellElement; metadata: CellMetadata } {
const td = this._get_cell(tagName, ridx, cidx);
const metadata = this._get_or_create_metadata(td);
metadata.y = ridx + Math.floor(ridx_offset);
- metadata.size_key = size_key;
+ const key = (metadata.size_key = size_key || 0);
if (tagName === "TD") {
metadata.column_header = column_name;
}
- const override_width = this._column_sizes.override[metadata.size_key];
+
+ // Handle clipping class for overridden columns
+ const override_width = this._column_sizes.override[key];
if (override_width) {
- const auto_width = this._column_sizes.auto[metadata.size_key];
- td.classList.toggle("rt-cell-clip", auto_width > override_width);
- td.style.minWidth = override_width + "px";
- td.style.maxWidth = override_width + "px";
+ const auto_width = this._column_sizes.auto[key] || 0;
+ const clip = auto_width > override_width;
+ if (td.classList.contains("rt-cell-clip") !== clip) {
+ td.classList.toggle("rt-cell-clip", clip);
+ }
} else {
td.classList.remove("rt-cell-clip");
- td.style.minWidth = "";
- td.style.maxWidth = "";
}
if (metadata.value !== val) {
@@ -50,7 +58,7 @@ export class RegularBodyViewModel extends ViewModel {
td.textContent = "";
td.appendChild(val);
} else {
- td.textContent = val;
+ td.textContent = String(val ?? "");
}
}
@@ -58,16 +66,16 @@ export class RegularBodyViewModel extends ViewModel {
return { td, metadata };
}
- draw(
- container_height,
- column_state,
- view_state,
- th = false,
- x,
- x0,
- size_key,
- merge_headers,
- ) {
+ draw(
+ container_height: number,
+ column_state: ColumnState,
+ view_state: ViewState,
+ th: boolean = false,
+ x?: number,
+ x0?: number,
+ size_key?: number,
+ merge_headers?: boolean,
+ ): BodyDrawResult {
const {
cidx,
column_data,
@@ -75,29 +83,43 @@ export class RegularBodyViewModel extends ViewModel {
column_data_listener_metadata,
} = column_state;
let { row_height } = view_state;
- let metadata;
- const ridx_offset = [],
- tds = [];
+ let metadata: CellMetadata | undefined;
+ const ridx_offset: number[] = [];
+ const tds: Array<{ td: HTMLTableCellElement; metadata: CellMetadata }> =
+ [];
let ridx = 0;
- const cidx_offset = [];
- for (let i = 0; i < (th ? view_state.row_headers_length : 1); i++) {
+ const cidx_offset: number[] = [];
+ const loops = th ? (view_state.row_headers_length ?? 1) : 1;
+ const y0_floor = Math.floor(view_state.ridx_offset);
+ const y1_ceil = Math.ceil(view_state.y1);
+ const x1_ceil = Math.ceil(view_state.x1);
+ const x_floor = x === undefined ? x : Math.floor(x);
+ const x0_floor = x0 === undefined ? undefined : Math.floor(x0);
+ const dx = Math.floor((x ?? 0) - (x0 ?? 0));
+
+ for (let i = 0; i < loops; i++) {
ridx = 0;
+ const cidx_i = cidx + i;
for (const val of column_data) {
- const id = row_headers?.[ridx];
- let obj;
+ let obj:
+ | { td: HTMLTableCellElement; metadata: CellMetadata }
+ | undefined;
if (th) {
- const row_header = val[i];
+ const valArray = val as CellScalar[];
+ const row_header = valArray[i];
+ const ridx_off_i = ridx_offset[i] || 1;
const prev_row = this._fetch_cell(
- ridx - (ridx_offset[i] || 1),
- cidx + i,
+ ridx - ridx_off_i,
+ cidx_i,
);
const prev_row_metadata =
this._get_or_create_metadata(prev_row);
+ const cidx_off_ridx = cidx_offset[ridx] || 1;
const prev_col = this._fetch_cell(
ridx,
- cidx + i - (cidx_offset[ridx] || 1),
+ cidx_i - cidx_off_ridx,
);
const prev_col_metadata =
this._get_or_create_metadata(prev_col);
@@ -109,42 +131,40 @@ export class RegularBodyViewModel extends ViewModel {
row_header === undefined) &&
!prev_col.hasAttribute("rowspan")
) {
- cidx_offset[ridx] = cidx_offset[ridx]
- ? cidx_offset[ridx] + 1
- : 2;
- prev_col.setAttribute("colspan", cidx_offset[ridx]);
- this._replace_cell(ridx, cidx + i);
+ const offset = (cidx_offset[ridx] = cidx_off_ridx + 1);
+ prev_col.setAttribute("colspan", String(offset));
+ this._replace_cell(ridx, cidx_i);
} else if (
merge_headers &&
prev_row &&
prev_row_metadata.value === row_header &&
!prev_row.hasAttribute("colspan")
) {
- ridx_offset[i] = ridx_offset[i]
- ? ridx_offset[i] + 1
- : 2;
- prev_row.setAttribute("rowspan", ridx_offset[i]);
- this._replace_cell(ridx, cidx + i);
+ const offset = (ridx_offset[i] = ridx_off_i + 1);
+ prev_row.setAttribute("rowspan", String(offset));
+ this._replace_cell(ridx, cidx_i);
} else {
obj = this._draw_td(
"TH",
ridx,
row_header,
- cidx + i,
- column_state,
+ cidx_i,
+ column_state as ColumnState,
view_state,
i,
);
- obj.td.style.display = "";
- obj.td.removeAttribute("rowspan");
- obj.td.removeAttribute("colspan");
- obj.metadata.row_header = val;
- obj.metadata.row_header_x = i;
- obj.metadata.y0 = Math.floor(view_state.ridx_offset);
- obj.metadata.y1 = Math.ceil(view_state.y1);
- obj.metadata._virtual_x = i;
- if (typeof x0 !== "undefined") {
- obj.metadata.x0 = Math.floor(x0);
+ const td = obj.td;
+ const meta = obj.metadata;
+ td.style.display = "";
+ td.removeAttribute("rowspan");
+ td.removeAttribute("colspan");
+ meta.row_header = valArray;
+ meta.row_header_x = i;
+ meta.y0 = y0_floor;
+ meta.y1 = y1_ceil;
+ meta.virtual_x = i;
+ if (x0_floor !== undefined) {
+ meta.x0 = x0_floor;
}
ridx_offset[i] = 1;
cidx_offset[ridx] = 1;
@@ -156,26 +176,25 @@ export class RegularBodyViewModel extends ViewModel {
ridx,
val,
cidx,
- column_state,
+ column_state as ColumnState,
view_state,
- size_key,
+ size_key ?? 0,
);
+ const meta = obj.metadata;
if (column_data_listener_metadata) {
- obj.metadata.user = column_data_listener_metadata[ridx];
+ meta.user = column_data_listener_metadata[ridx];
}
- obj.metadata.x =
- typeof x === "undefined" ? x : Math.floor(x);
- obj.metadata.x1 = Math.ceil(view_state.x1);
- obj.metadata.row_header = id || [];
- obj.metadata.y0 = Math.floor(view_state.ridx_offset);
- obj.metadata.y1 = Math.ceil(view_state.y1);
- obj.metadata.dx = Math.floor(x - x0);
- obj.metadata.dy =
- obj.metadata.y - Math.floor(obj.metadata.y0);
- obj.metadata._virtual_x = cidx;
- if (typeof x0 !== "undefined") {
- obj.metadata.x0 = Math.floor(x0);
+ meta.x = x_floor;
+ meta.x1 = x1_ceil;
+ meta.row_header = row_headers?.[ridx] || [];
+ meta.y0 = y0_floor;
+ meta.y1 = y1_ceil;
+ meta.dx = dx;
+ meta.dy = (meta.y ?? 0) - y0_floor;
+ meta.virtual_x = cidx;
+ if (x0_floor !== undefined) {
+ meta.x0 = x0_floor;
}
tds[0] = obj;
@@ -184,16 +203,16 @@ export class RegularBodyViewModel extends ViewModel {
ridx++;
metadata = obj ? obj.metadata : metadata;
row_height = row_height || obj?.td.offsetHeight;
- if (ridx * row_height > container_height) {
+ if (ridx * (row_height ?? 0) > container_height) {
break;
}
}
}
this._clean_rows(ridx);
- return { tds, ridx, metadata, row_height };
+ return { tds, ridx, row_height };
}
- clean({ ridx, cidx }) {
+ clean({ ridx, cidx }: { ridx: number; cidx: number }): void {
this._clean_rows(ridx);
this._clean_columns(cidx);
}
diff --git a/src/js/thead.js b/src/ts/thead.ts
similarity index 70%
rename from src/js/thead.js
rename to src/ts/thead.ts
index 36f338b4..485e7f92 100644
--- a/src/js/thead.js
+++ b/src/ts/thead.ts
@@ -10,6 +10,12 @@
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
import { ViewModel } from "./view_model";
+import {
+ HeaderDrawResult,
+ ColumnSizes,
+ CellMetadata,
+ CellScalar,
+} from "./types";
/**
* view model. This model accumulates state in the form of
@@ -19,91 +25,104 @@ import { ViewModel } from "./view_model";
* @class RegularHeaderViewModel
*/
export class RegularHeaderViewModel extends ViewModel {
- constructor(...args) {
- super(...args);
+ private _group_header_cache: [CellMetadata, HTMLTableCellElement, number][];
+ private _offset_cache: number[];
+
+ constructor(
+ column_sizes: ColumnSizes,
+ container: HTMLElement,
+ table: HTMLElement,
+ ) {
+ super(column_sizes, container, table);
this._group_header_cache = [];
this._offset_cache = [];
}
- _draw_group_th(offset_cache, d, column) {
+ _draw_group_th(
+ offset_cache: number[],
+ d: number,
+ column: unknown,
+ ): HTMLTableCellElement {
const th = this._get_cell("TH", d, offset_cache[d] || 0);
offset_cache[d] += 1;
th.removeAttribute("colspan");
- th.style.minWidth = "0";
-
th.textContent = "";
if (column instanceof HTMLElement) {
th.appendChild(column);
} else {
- const span = this._span_factory.get("span");
- span.textContent = column;
+ const span = this._span_factory.get();
+ span.textContent = String(column ?? "");
th.appendChild(span);
}
- const resizeSpan = this._span_factory.get("span");
+ const resizeSpan = this._span_factory.get();
resizeSpan.className = "rt-column-resize";
th.appendChild(resizeSpan);
-
return th;
}
- _draw_group(column, column_name, th) {
+ _draw_group(
+ column: CellScalar[],
+ column_name: unknown,
+ th: HTMLTableCellElement,
+ ): CellMetadata {
const metadata = this._get_or_create_metadata(th);
metadata.column_header = column;
metadata.value = column_name;
- metadata.value = column_name;
return metadata;
}
- _draw_th(column, column_name, th, cidx, size_key) {
+ _draw_th(
+ column: CellScalar[],
+ column_name: unknown,
+ th: HTMLTableCellElement,
+ size_key: number | number[],
+ ): CellMetadata {
const metadata = this._get_or_create_metadata(th);
metadata.column_header = column;
metadata.value = column_name;
- metadata.size_key = size_key.length ? size_key[0] : size_key; // FIXME
-
- if (!(size_key.length > 1)) {
+ metadata.size_key = Array.isArray(size_key) ? size_key[0] : size_key;
+ if (!Array.isArray(size_key) || size_key.length <= 1) {
const override_width =
- this._column_sizes.override[metadata.size_key];
- const auto_width = this._column_sizes.auto[metadata.size_key];
+ this._column_sizes.override[metadata.size_key || 0];
+ const auto_width =
+ this._column_sizes.auto[metadata.size_key || 0] || 0;
+
+ // Handle clipping class for overridden columns
if (override_width) {
th.classList.toggle(
"rt-cell-clip",
auto_width > override_width,
);
- th.style.minWidth = override_width + "px";
- th.style.maxWidth = override_width + "px";
- } else if (auto_width) {
- th.classList.remove("rt-cell-clip");
- th.style.maxWidth = "";
- th.style.minWidth = auto_width + "px";
} else {
- th.style.maxWidth = "";
- th.style.maxWidth = "";
+ th.classList.remove("rt-cell-clip");
}
}
return metadata;
}
- get_column_header(cidx) {
+ get_column_header(cidx: number): HTMLTableCellElement {
return this._get_cell("TH", this.num_rows() - 1, cidx);
}
draw(
- alias,
- parts,
- colspan,
- x,
- size_key,
- x0,
- _virtual_x,
- column_header_merge_depth,
- merge_headers,
- ) {
+ alias: CellScalar[],
+ parts: CellScalar[],
+ colspan: number | undefined,
+ x: number | undefined,
+ size_key: number | number[],
+ x0: number,
+ _virtual_x: number,
+ column_header_merge_depth: number | undefined,
+ merge_headers: boolean,
+ ): HeaderDrawResult | undefined {
const header_levels = parts?.length; //config.column_pivots.length + 1;
if (header_levels === 0) return;
- let th, metadata, column_name;
- let output = undefined;
+ let th: HTMLTableCellElement | undefined;
+ let metadata: CellMetadata | undefined;
+ let column_name: unknown;
+ let output: HeaderDrawResult | undefined = undefined;
column_header_merge_depth =
typeof column_header_merge_depth === "undefined"
? header_levels - 1
@@ -120,9 +139,13 @@ export class RegularHeaderViewModel extends ViewModel {
th = this._group_header_cache[d][1];
this._group_header_cache[d][2] += 1;
if (colspan === 1) {
- this._group_header_cache[d][0].row_header_x = size_key;
+ this._group_header_cache[d][0].row_header_x =
+ Array.isArray(size_key) ? size_key[0] : size_key;
}
- th.setAttribute("colspan", this._group_header_cache[d][2]);
+ th.setAttribute(
+ "colspan",
+ String(this._group_header_cache[d][2]),
+ );
} else {
th = this._draw_group_th(
this._offset_cache,
@@ -139,12 +162,12 @@ export class RegularHeaderViewModel extends ViewModel {
// header has the same metadata coordinates of its rightmost
// column.
metadata = this._draw_th(
- alias || parts,
+ alias.length > 0 ? alias : parts,
column_name,
th,
- x,
size_key,
);
+
if (typeof output === "undefined") {
output = { th, metadata };
}
@@ -152,6 +175,7 @@ export class RegularHeaderViewModel extends ViewModel {
for (const [group_meta] of this._group_header_cache) {
group_meta.size_key = metadata.size_key;
}
+
th.removeAttribute("colspan");
}
@@ -159,28 +183,31 @@ export class RegularHeaderViewModel extends ViewModel {
"rt-autosize",
d === column_header_merge_depth,
);
+
th.classList.toggle("rt-group-corner", x === undefined);
if (metadata) {
metadata.x = typeof x === "undefined" ? x : Math.floor(x);
metadata.column_header_y = d;
metadata.x0 = Math.floor(x0);
- metadata._virtual_x = _virtual_x;
+ metadata.virtual_x = _virtual_x;
if (colspan === 1) {
- metadata.row_header_x = size_key;
+ metadata.row_header_x = Array.isArray(size_key)
+ ? size_key[0]
+ : size_key;
}
}
}
this._clean_rows(this._offset_cache.length);
- output = output || { th, metadata };
+ output = output || { th: th!, metadata: metadata! };
return output;
}
- clean() {
+ clean(): void {
this._clean_columns(this._offset_cache);
}
- reset_header_cache() {
+ reset_header_cache(): void {
this._offset_cache = [];
this._group_header_cache = [];
}
diff --git a/src/ts/types.ts b/src/ts/types.ts
new file mode 100644
index 00000000..b0a2d7d6
--- /dev/null
+++ b/src/ts/types.ts
@@ -0,0 +1,360 @@
+// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
+// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
+// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
+// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
+// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
+// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
+// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+
+import type { RegularTableElement } from "./regular-table.ts";
+
+/**
+ * The `DataListener` is similar to a normal event listener function.
+ * Unlike a normal event listener, it takes regular arguments (not an
+ * `Event`); and returns a `Promise` for a `DataResponse` object for this
+ * region (as opposed to returning `void` as a standard event listener).
+ *
+ * @param {number} x0 - The origin `x` index (column).
+ * @param {number} y0 - The origin `y` index (row).
+ * @param {number} x1 - The corner `x` index (column).
+ * @param {number} y1 - The corner `y` index (row).
+ * @returns {Promise} The resulting `DataResponse`. Make sure
+ * to `resolve` or `reject` the `Promise`, or your `` will
+ * never render!
+ */
+export type DataListener = (
+ x0: number,
+ y0: number,
+ x1: number,
+ y1: number,
+) => Promise;
+
+/**
+ * An object describing virtual rendering metadata about an
+ * `HTMLTableCellElement`, use this object to map rendered `` or ` `
+ * elements back to your `data`, `row_headers` or `column_headers` within
+ * listener functions for `addStyleListener()` and `addEventListener()`.
+ *
+ * MetaData (x = 0, column_header_y = 0)
+ * *-------------------------------------+
+ * | |
+ * | |
+ * +-------------------------------------+
+ * (row_header_x = 0, y = 0) (x = 0, y = 0)
+ * *------------------------+ *-------------------------------------+
+ * | | | |
+ * | | | (x0, y0) |
+ * | | | *---------------* |
+ * | | | | | |
+ * | | | | * (x, y) | |
+ * | | | | | |
+ * | | | *---------------* (x1, y1) |
+ * | | | |
+ * +------------------------+ +-------------------------------------+
+ *
+ * @property {number} [x] - The `x` index in your virtual data model.
+ * property is only generated for ` `, ` ` from `row_headers`.
+ * @property {number} [y] - The `y` index in your virtual data model.
+ * property is only generated for ` `, ` ` from `row_headers`.
+ * @property {number} [x0] - The `x` index of the viewport origin in
+ * your data model, e.g. what was passed to `x0` when your
+ * `dataListener` was invoked.
+ * @property {number} [y0] - The `y` index of the viewport origin in
+ * your data model, e.g. what was passed to `y0` when your
+ * `dataListener` was invoked.
+ * @property {number} [x1] - The `x` index of the viewport corner in
+ * your data model, e.g. what was passed to `x1` when your
+ * `dataListener` was invoked.
+ * @property {number} [y1] - The `y` index of the viewport corner in
+ * your data model, e.g. what was passed to `y1` when your
+ * `dataListener` was invoked.
+ * @property {number} [dx] - The `x` index in `DataResponse.data`, this
+ * property is only generated for ` `, and ` ` from `column_headers`.
+ * @property {number} [dy] - The `y` index in `DataResponse.data`, this
+ * property is only generated for ` `, ` ` from `row_headers`.
+ * @property {number} [column_header_y] - The `y` index in
+ * `DataResponse.column_headers[x]`, this property is only generated for ` `
+ * from `column_headers`.
+ * @property {number} [row_header_x] - The `x` index in
+ * `DataResponse.row_headers[y]`, this property is only generated for ` `
+ * from `row_headers`.
+ * @property {number} size_key - The unique index of this column in a full
+ * ``, which is `x` + (Total Row Header Columns).
+ * @property {(string|HTMLElement)[]} [row_header] - The `Array` for this `y` in
+ * `DataResponse.row_headers`, if it was provided.
+ * @property {(string|HTMLElement)[]} [column_header] - The `Array` for this `x`
+ * in `DataResponse.column_headers`, if it was provided.
+ * @property {(string|HTMLElement)} [value] - The value dispalyed in the cell or
+ * header.
+ */
+
+export interface CellMetadata {
+ column_header?: CellScalar[];
+ row_header?: CellScalar[];
+ value: unknown;
+ size_key?: number;
+ x?: number;
+ column_header_y?: number;
+ x0?: number;
+ virtual_x?: number;
+ row_header_x?: number;
+ y?: number;
+ y0?: number;
+ y1?: number;
+ x1?: number;
+ user?: unknown;
+ dx?: number;
+ dy?: number;
+}
+
+/**
+ * The `DataResponse` object describes a rectangular region of a virtual
+ * data set, and some associated metadata. `` will use this
+ * object to render the ``, though it may make multiple requests for
+ * different regions to achieve a compelte render as it must estimate
+ * certain dimensions. You must construct a `DataResponse` object to
+ * implement a `DataListener`.
+ *
+ * # Examples
+ *
+ * ```json
+ * {
+ * "num_rows": 26,
+ * "num_columns": 3,
+ * "data": [
+ * [0, 1],
+ * ["A", "B"]
+ * ],
+ * "row_headers": [
+ * ["Rowgroup 1", "Row 1"],
+ * ["Rowgroup 1", "Row 2"]
+ * ],
+ * "column_headers": [
+ * ["Colgroup 1", "Column 1"],
+ * ["Colgroup 1", "Column 2"]
+ * ]
+ * }
+ * ```
+ *
+ * @property {(string|HTMLElement)[][]} [column_headers] - A two dimensional
+ * `Array` of column group headers, in specificity order. No ``
+ * will be generated if this property is not provided.
+ * @property {(string|HTMLElement)[][]} [row_headers] - A two dimensional
+ * `Array` of row group headers, in specificity order. No ``
+ * elements within ` ` will be generated if this property is not
+ * provided.
+ * @property {number?} num_row_headers - Optional number of row headers.
+ * @property {number?} num_row_headers - Optional number of column headers.
+ * @property {(string|HTMLElement)[][]} data - A two dimensional `Array`
+ * representing a rectangular section of the underlying data set from
+ * (x0, y0) to (x1, y1), arranged in columnar fashion such that
+ * `data[x][y]` returns the `y`th row of the `x`th column of the slice.
+ * @property {number} num_rows - Total number of rows in the underlying
+ * data set.
+ * @property {number} num_columns - Total number of columns in the
+ * underlying data set.
+ */
+export interface DataResponse {
+ data: CellScalar[][];
+ num_columns: number;
+ num_rows: number;
+ row_height?: number;
+ row_headers?: CellScalar[][];
+ column_headers?: CellScalar[][];
+ metadata?: unknown[][];
+ num_row_headers?: number;
+ num_column_headers?: number;
+ column_header_merge_depth?: number;
+ merge_headers?: "both" | "row" | "column";
+}
+
+/**
+ *
+ * @param {boolean} options.preserve_state If `setDataListener` has already been
+ * called, setting this flag will prevent the internal state from being
+ * reset (other than the callback itself).
+ * @param {("both"|"horizontal"|"vertical"|"none")} options.virtual_mode
+ * The `virtual_mode` options flag may be one of "both", "horizontal",
+ * "vertical", or "none" indicating which dimensions of the table should be
+ * virtualized (vs. rendering completely).
+ */
+export interface SetDataListenerOptions {
+ virtual_mode?: VirtualMode;
+ preserve_state?: boolean;
+}
+
+/**
+ * An object with performance statistics about calls to
+ * `draw()` from some time interval (captured in milliseconds by the
+ * `elapsed` proprty).
+ *
+ * @property {number} avg - Avergage milliseconds per call.
+ * @property {number} real_fps - `num_frames` / `elapsed`
+ * @property {number} virtual_fps - `elapsed` / `avg`
+ * @property {number} num_frames - Number of frames rendered.
+ * @property {number} elapsed - Number of milliseconds since last call
+ * to `getDrawFPS()`.
+ */
+export interface FPSRecord {
+ avg: number;
+ real_fps: number;
+ virtual_fps: number;
+ num_frames: number;
+ elapsed: number;
+}
+
+export type StyleCallback = (event: {
+ detail: RegularTableElement;
+}) => void | Promise;
+
+/**
+ * Virtual mode type
+ */
+export type VirtualMode = "both" | "horizontal" | "vertical" | "none";
+
+/**
+ * View cache containing view function and configuration
+ */
+export interface ViewCache {
+ view: ViewFunction;
+ row_headers_length: number;
+ column_headers_length: number;
+}
+
+export type CellScalar = number | string | boolean | null;
+
+/**
+ * Container size information
+ */
+export interface ContainerSize {
+ width: number;
+ height: number;
+ containerHeight: number;
+}
+
+/**
+ * Viewport range
+ */
+export interface Viewport {
+ start_col: number;
+ end_col: number;
+ start_row: number;
+ end_row: number;
+}
+
+/**
+ * Validation result for viewport
+ */
+export interface ViewportValidation {
+ invalid_column: boolean;
+ invalid_row: boolean;
+}
+
+/**
+ * Options for the draw method
+ */
+export interface DrawOptions {
+ invalid_viewport?: boolean;
+ preserve_width?: boolean;
+ throttle?: boolean;
+}
+
+/**
+ * View state containing viewport and rendering information
+ */
+export interface ViewState {
+ viewport_width: number;
+ selected_id: number | undefined;
+ ridx_offset: number;
+ sub_cell_offset: number;
+ x0: number;
+ x1: number;
+ y1: number;
+ row_height: number | undefined;
+ row_headers_length: number | undefined;
+}
+
+/**
+ * View function type that fetches data for a given viewport range
+ */
+export type ViewFunction = (
+ start_col: number,
+ start_row: number,
+ end_col: number,
+ end_row: number,
+) => Promise;
+
+/**
+ * Column state for rendering a single column.
+ */
+export interface ColumnState {
+ column_name: CellScalar[];
+ cidx: number;
+ column_data: T[];
+ column_data_listener_metadata?: unknown[];
+ row_headers?: CellScalar[][];
+ first_col: boolean;
+}
+
+export type RowHeaderColumnState = ColumnState;
+
+/**
+ * Result from drawing row headers
+ */
+export interface RowHeadersResult {
+ cont_body: BodyDrawResult;
+ first_col: boolean;
+ _virtual_x: number;
+ last_cells: CellTuple[];
+}
+
+/**
+ * Tuple of cell element and its metadata for autosizing
+ */
+export type CellTuple = [HTMLTableCellElement, CellMetadata | undefined];
+
+/**
+ * Result from drawing header cells
+ */
+export interface HeaderDrawResult {
+ th: HTMLTableCellElement;
+ metadata: CellMetadata;
+}
+
+/**
+ * Result from drawing body cells
+ */
+export interface BodyDrawResult {
+ tds: Array<{ td: HTMLTableCellElement; metadata: CellMetadata }>;
+ row_height?: number;
+ ridx?: number;
+}
+
+/**
+ * Result from drawing a data column (header and body)
+ */
+export interface DataColumnDrawResult {
+ cont_head: HeaderDrawResult | undefined;
+ cont_body: BodyDrawResult;
+}
+
+/**
+ * Result from fetching missing columns
+ */
+export interface FetchResult {
+ column_header_merge_depth: number | undefined;
+ merge_headers: "both" | "row" | "column" | undefined;
+}
+
+/**
+ * Column sizing information for the table
+ */
+export interface ColumnSizes {
+ auto: (number | undefined)[];
+ override: Record;
+ indices: (number | undefined)[];
+ row_height?: number;
+}
diff --git a/src/js/utils.js b/src/ts/utils.ts
similarity index 70%
rename from src/js/utils.js
rename to src/ts/utils.ts
index 8a0d5b86..bccb3432 100644
--- a/src/js/utils.js
+++ b/src/ts/utils.ts
@@ -9,6 +9,8 @@
// ┃ * [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). * ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+import { FPSRecord } from "./types";
+
/******************************************************************************
*
* Profling
@@ -19,7 +21,7 @@ let AVG = 0,
TOTAL = 0,
START = performance.now();
-export function get_draw_fps() {
+export function get_draw_fps(): FPSRecord {
const now = performance.now();
const elapsed = now - START;
const avg = AVG;
@@ -32,7 +34,7 @@ export function get_draw_fps() {
return { avg, real_fps, virtual_fps, num_frames, elapsed };
}
-export function log_perf(x) {
+export function log_perf(x: number) {
AVG = (AVG * TOTAL + x) / (TOTAL + 1);
TOTAL += 1;
}
@@ -43,53 +45,19 @@ export function log_perf(x) {
*
*/
-/**
- * A class method decorate for memoizing method results. Only works on one
- * arg.
- */
-export function memoize(_target, _property, descriptor) {
- const cache = new Map();
- const func = descriptor.value;
- descriptor.value = new_func;
- return descriptor;
- function new_func(arg) {
- if (cache.has(arg)) {
- return cache.get(arg);
- } else {
- const res = func.call(this, arg);
- cache.set(arg, res);
- return res;
- }
- }
-}
-
-/**
- * Identical to a non-tagger template literal, this is only used to indicate to
- * babel that this string should be HTML-minified on production builds.
- */
-export const html = (strings, ...args) =>
- strings
- .map((str, i) => [str, args[i]])
- .flat()
- .filter((a) => !!a)
- .join("");
-
-const invertPromise = () => {
- let _resolve;
- const promise = new Promise((resolve) => {
- _resolve = resolve;
- });
- promise.resolve = _resolve;
- return promise;
-};
-const TAGS = new Map();
+const TAGS: Map> = new Map();
-export async function flush_tag(tag) {
+export async function flush_tag(
+ tag: Element,
+): Promise | undefined> {
await new Promise(requestAnimationFrame);
return await TAGS.get(tag);
}
-export async function throttle_tag(tag, f) {
+export async function throttle_tag(
+ tag: Element,
+ f: () => Promise,
+): Promise {
if (TAGS.has(tag)) {
await TAGS.get(tag);
if (TAGS.has(tag)) {
@@ -98,12 +66,12 @@ export async function throttle_tag(tag, f) {
}
}
- TAGS.set(tag, invertPromise());
+ TAGS.set(tag, Promise.withResolvers());
try {
return await f();
} finally {
const l = TAGS.get(tag);
TAGS.delete(tag);
- l.resolve();
+ l?.resolve(undefined);
}
}
diff --git a/src/js/view_model.js b/src/ts/view_model.ts
similarity index 72%
rename from src/js/view_model.js
rename to src/ts/view_model.ts
index 145979e2..01f50098 100644
--- a/src/js/view_model.js
+++ b/src/ts/view_model.ts
@@ -10,6 +10,7 @@
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
import { METADATA_MAP } from "./constants";
+import { CellMetadata, ColumnSizes } from "./types";
/******************************************************************************
*
@@ -18,7 +19,10 @@ import { METADATA_MAP } from "./constants";
*/
class ElemFactory {
- constructor(name) {
+ private _name: string;
+ private _elements: HTMLElement[];
+ private _index: number;
+ constructor(name: string) {
this._name = name;
this._elements = [];
this._index = 0;
@@ -40,7 +44,18 @@ class ElemFactory {
}
export class ViewModel {
- constructor(column_sizes, container, table) {
+ protected _column_sizes: ColumnSizes;
+ protected _container: HTMLElement;
+ public _span_factory: ElemFactory;
+ public table: HTMLElement;
+ public cells: (HTMLTableCellElement | undefined)[][];
+ public rows: HTMLTableRowElement[];
+
+ constructor(
+ column_sizes: ColumnSizes,
+ container: HTMLElement,
+ table: HTMLElement,
+ ) {
this._column_sizes = column_sizes;
this._container = container;
this._span_factory = new ElemFactory("span");
@@ -49,32 +64,37 @@ export class ViewModel {
this.rows = [];
}
- num_columns() {
+ num_columns(): number {
return this._get_row(Math.max(0, this.rows.length - 1)).row_container
.length;
}
- num_rows() {
+ num_rows(): number {
return this.cells.length;
}
- _set_metadata(td, metadata) {
+ _set_metadata(td: HTMLTableCellElement, metadata: CellMetadata): void {
METADATA_MAP.set(td, metadata);
}
- _get_or_create_metadata(td) {
+ _get_or_create_metadata(
+ td: HTMLTableCellElement | undefined,
+ ): CellMetadata {
if (td === undefined) {
- return {};
+ return { value: undefined };
} else if (METADATA_MAP.has(td)) {
return METADATA_MAP.get(td);
} else {
- const metadata = {};
+ const metadata: CellMetadata = { value: undefined };
METADATA_MAP.set(td, metadata);
return metadata;
}
}
- _replace_cell(ridx, cidx) {
+ _replace_cell(
+ ridx: number,
+ cidx: number,
+ ): HTMLTableCellElement | undefined {
const { tr, row_container } = this._get_row(ridx);
let td = row_container[cidx];
if (td) {
@@ -84,7 +104,7 @@ export class ViewModel {
return td;
}
- _fetch_cell(ridx, cidx) {
+ _fetch_cell(ridx: number, cidx: number): HTMLTableCellElement | undefined {
if (ridx < 0 || cidx < 0) {
return;
}
@@ -92,7 +112,10 @@ export class ViewModel {
return this.cells[ridx]?.[cidx];
}
- _get_row(ridx) {
+ _get_row(ridx: number): {
+ tr: HTMLTableRowElement;
+ row_container: (HTMLTableCellElement | undefined)[];
+ } {
let tr = this.rows[ridx];
if (!tr) {
tr = this.rows[ridx] = document.createElement("tr");
@@ -107,23 +130,25 @@ export class ViewModel {
return { tr, row_container };
}
- _get_cell(tag = "TD", ridx, cidx) {
+ _get_cell(tag = "TD", ridx: number, cidx: number): HTMLTableCellElement {
const { tr, row_container } = this._get_row(ridx);
let td = row_container[cidx];
if (!td) {
if (cidx < row_container.length) {
- td = row_container[cidx] = document.createElement(tag);
- tr.insertBefore(
- td,
- row_container.slice(cidx + 1).find((x) => x),
- );
+ td = row_container[cidx] = document.createElement(
+ tag,
+ ) as HTMLTableCellElement;
+ const nextCell = row_container.slice(cidx + 1).find((x) => x);
+ tr.insertBefore(td, nextCell || null);
} else {
- td = row_container[cidx] = document.createElement(tag);
+ td = row_container[cidx] = document.createElement(
+ tag,
+ ) as HTMLTableCellElement;
tr.appendChild(td);
}
}
if (td.tagName !== tag) {
- const new_td = document.createElement(tag);
+ const new_td = document.createElement(tag) as HTMLTableCellElement;
tr.replaceChild(new_td, td);
this.cells[ridx].splice(cidx, 1, new_td);
td = new_td;
@@ -131,11 +156,15 @@ export class ViewModel {
return td;
}
- _clean_columns(cidx) {
+ _clean_columns(cidx: number | number[]): void {
for (let i = 0; i < this.rows.length; i++) {
const tr = this.rows[i];
const row_container = this.cells[i];
- this.cells[i] = row_container.slice(0, cidx[i] || cidx);
+ this.cells[i] = row_container.slice(
+ 0,
+ (Array.isArray(cidx) ? cidx[i] : cidx) || 0,
+ );
+
const idx = this.cells[i].filter((x) => x !== undefined).length;
while (tr.children[idx]) {
tr.removeChild(tr.children[idx]);
@@ -143,7 +172,7 @@ export class ViewModel {
}
}
- _clean_rows(ridx) {
+ _clean_rows(ridx: number): void {
while (this.table.children[ridx]) {
this.table.removeChild(this.table.children[ridx]);
}
diff --git a/test/examples/file_browser.test.js b/test/examples/file_browser.test.js
deleted file mode 100644
index ae18e672..00000000
--- a/test/examples/file_browser.test.js
+++ /dev/null
@@ -1,96 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("file_browser.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 400, height: 100 });
- });
-
- describe("creates a `` body when attached to `document`", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/file_browser.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) =>
- x.textContent.trim(),
- ),
- first_tr,
- );
- expect(cell_values.slice(0, 1)).toEqual(["Dir_0"]);
- expect(cell_values.length).toEqual(5);
- });
- });
-
- describe("when a directory row header is clicked", () => {
- describe("in a collapsed state", () => {
- beforeAll(async () => {
- let dir = await page.$(
- "regular-table tbody tr:first-child th:last-of-type",
- );
- await dir.click();
- });
-
- afterAll(async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = 0;
- await table.draw();
- }, table);
- });
-
- test("it expands, inserting contents in the next rows", async () => {
- const first_row_header_icon = await page.$(
- "regular-table tbody tr:nth-child(2) th:last-of-type",
- );
- const icon_text = await page.evaluate(
- (x) => x.innerText,
- first_row_header_icon,
- );
- expect(icon_text).toEqual("Dir_0");
- });
- });
-
- describe("in an expanded state", () => {
- beforeAll(async () => {
- let dir = await page.$(
- "regular-table tbody tr:first-child th:last-of-type",
- );
- await dir.click();
- });
-
- afterAll(async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = 0;
- await table.draw();
- }, table);
- });
-
- test("it collapses, removing the inserted elements", async () => {
- const first_row_header_icon = await page.$(
- "regular-table tbody tr:nth-child(2) th:last-of-type",
- );
- const icon_text = await page.evaluate(
- (x) => x.innerText,
- first_row_header_icon,
- );
- expect(icon_text).toEqual("Dir_1");
- });
- });
- });
-});
diff --git a/test/examples/react.test.js b/test/examples/react.test.js
deleted file mode 100644
index 639ffb3a..00000000
--- a/test/examples/react.test.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("react.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 200, height: 100 });
- });
-
- describe("creates a `` body when attached to `document`", () => {
- beforeAll(async () => {
- await page.goto("http://localhost:8081/dist/examples/react.html");
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(5);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["Group 0", "Row 0", "0", "1"]);
- });
- });
-});
diff --git a/test/examples/spreadsheet.test.js b/test/examples/spreadsheet.test.js
deleted file mode 100644
index c8844e50..00000000
--- a/test/examples/spreadsheet.test.js
+++ /dev/null
@@ -1,400 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("spreadsheet.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 150, height: 150 });
- });
-
- describe("Navigating with the arrow keys", () => {
- const sayHello = async (table) => {
- await page.evaluate(async (table) => {
- const target = document.activeElement;
- target.textContent = "Hello, World!";
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keypress", false, true);
- event.ctrlKey = true;
- event.keyCode = 13;
- target.dispatchEvent(event);
- await table.draw();
- }, table);
- };
-
- const keypressReturn = async (table, times = 1) => {
- await Promise.all(
- Array.from(Array(times)).map(async () => {
- await page.evaluate(async (table) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keypress", false, true);
- event.ctrlKey = true;
- event.keyCode = 13;
- table.dispatchEvent(event);
- await table.draw();
- }, table);
- }),
- );
- };
-
- const keydownLeftArrow = async (table, times = 1) => {
- await Promise.all(
- Array.from(Array(times)).map(async () => {
- await page.evaluate(async (table) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.keyCode = 37;
- table.dispatchEvent(event);
- await table.draw();
- }, table);
- }),
- );
- };
-
- const keydownUpArrow = async (table, times = 1) => {
- await Promise.all(
- Array.from(Array(times)).map(async () => {
- await page.evaluate(async (table) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.keyCode = 38;
- table.dispatchEvent(event);
- await table.draw();
- }, table);
- }),
- );
- };
-
- const keydownRightArrow = async (table, times = 1) => {
- Array.from(Array(times)).forEach(async () => {
- await page.evaluate(async (table) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.keyCode = 39;
- table.dispatchEvent(event);
- await table.draw();
- }, table);
- });
- };
-
- const keydownDownArrow = async (table, times = 1) => {
- await Promise.all(
- Array.from(Array(times)).map(async () => {
- await page.evaluate(async (table) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.keyCode = 40;
- table.dispatchEvent(event);
- await table.draw();
- }, table);
- }),
- );
- };
-
- beforeEach(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/spreadsheet.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test("initializes with focus on (0,0)", async () => {
- const table = await page.$("regular-table");
- await sayHello(table);
- const tr = await page.$$(
- "regular-table tbody tr:nth-of-type(1) td",
- );
- const cell_values = [];
- for (const td of tr) {
- cell_values.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(cell_values).toEqual([
- "Hello, World!",
- "",
- "",
- "",
- "",
- "",
- "",
- ]);
- });
-
- // // Skip due to puppeteer text measure float between CI and local
- // test("scrolls as right arrow is down", async () => {
- // const table = await page.$("regular-table");
- // await keydownRightArrow(table, 5);
- // await sayHello(table);
- // const tds = await page.$$(
- // "regular-table tbody tr:nth-of-type(1) td",
- // );
- // const cells = [];
- // for (const td of tds) {
- // cells.push(await page.evaluate((td) => td.innerHTML, td));
- // }
-
- // expect(cells.slice(cells.length - 5)).toEqual([
- // "",
- // "",
- // "",
- // "",
- // "Hello, World!",
- // ]);
-
- // const ths = await page.$$(
- // "regular-table tbody tr:nth-of-type(1) th",
- // );
- // const th_value = await page.evaluate((th) => th.innerHTML, ths[0]);
- // expect(th_value).toEqual("0");
- // });
-
- test("scrolls as down arrow is down", async () => {
- const table = await page.$("regular-table");
- await keydownDownArrow(table, 5);
- await sayHello(table);
-
- const tds = await page.$$(
- "regular-table tbody tr:nth-of-type(3) td",
- );
- const cells = [];
- for (const td of tds) {
- cells.push(await page.evaluate((td) => td.innerHTML, td));
- }
-
- expect(cells).toEqual(["Hello, World!", "", "", "", "", "", ""]);
-
- const ths = await page.$$(
- "regular-table tbody tr:nth-of-type(3) th",
- );
- const th = await page.evaluate((th) => th.innerHTML, ths[0]);
- expect(th).toEqual("5");
- });
-
- test("scrolls down and back up", async () => {
- const table = await page.$("regular-table");
- await keydownDownArrow(table, 5);
- await keydownUpArrow(table, 2);
- await sayHello(table);
-
- const tds = await page.$$(
- "regular-table tbody tr:nth-of-type(3) td",
- );
- const cells = [];
- for (const td of tds) {
- cells.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(cells).toEqual(["Hello, World!", "", "", "", "", "", ""]);
-
- const ths = await page.$$(
- "regular-table tbody tr:nth-of-type(3) th",
- );
- const th = await page.evaluate((th) => th.innerHTML, ths[0]);
- expect(th).toEqual("3");
- });
-
- test("scrolls right and back left", async () => {
- const table = await page.$("regular-table");
- await keydownRightArrow(table, 10);
- await keydownLeftArrow(table, 7);
- await sayHello(table);
-
- const tds = await page.$$(
- "regular-table tbody tr:nth-of-type(1) td",
- );
- const cells = [];
- for (const td of tds) {
- cells.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(cells).toEqual(["", "", "", "Hello, World!", "", "", ""]);
-
- const ths = await page.$$(
- "regular-table tbody tr:nth-of-type(1) th",
- );
- const th = await page.evaluate((th) => th.innerHTML, ths[0]);
- expect(th).toEqual("0");
- });
-
- test("scrolls as return is pressed", async () => {
- const table = await page.$("regular-table");
- await keypressReturn(table, 4);
- await sayHello(table);
-
- const tds = await page.$$(
- "regular-table tbody tr:nth-of-type(3) td",
- );
- const cells = [];
- for (const td of tds) {
- cells.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(cells).toEqual(["Hello, World!", "", "", "", "", "", ""]);
-
- const ths = await page.$$(
- "regular-table tbody tr:nth-of-type(3) th",
- );
- const th = await page.evaluate((th) => th.innerHTML, ths[0]);
- expect(th).toEqual("4");
- });
- });
-
- describe("Makes a simple edit", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/spreadsheet.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- const cell =
- table.querySelector("table tbody").children[2].children[1];
- cell.textContent = "Hello, World!";
- cell.focus();
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keypress", false, true);
- event.ctrlKey = true;
- event.keyCode = 13;
- table.dispatchEvent(event);
- }, table);
- });
-
- test("displays edited input", async () => {
- const tr = await page.$$(
- "regular-table tbody tr:nth-of-type(3) td",
- );
- const cell_values = [];
- for (const td of tr) {
- cell_values.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(cell_values).toEqual([
- "Hello, World!",
- "",
- "",
- "",
- "",
- "",
- "",
- ]);
- });
-
- test("next cell has focus", async () => {
- const contents = await page.evaluate(
- () => document.activeElement.textContent,
- );
- expect(contents).toEqual("");
- });
-
- describe("on scroll", () => {
- beforeAll(async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = table.scrollTop + 30;
- await table.draw();
- }, table);
- });
-
- test("preserves input", async () => {
- const tr = await page.$$(
- "regular-table tbody tr:nth-of-type(2) td",
- );
- const cell_values = [];
- for (const td of tr) {
- cell_values.push(
- await page.evaluate((td) => td.innerHTML, td),
- );
- }
- expect(cell_values).toEqual([
- "Hello, World!",
- "",
- "",
- "",
- "",
- "",
- "",
- ]);
- });
- });
- });
-
- describe("Evaluates an expression", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/spreadsheet.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- for (const [x, y, v] of [
- [2, 1, "1"],
- [3, 1, "2"],
- [3, 2, "=sum(A0..A3)"],
- ]) {
- const cell =
- table.querySelector("table tbody").children[x].children[
- y
- ];
- cell.textContent = v;
- cell.focus();
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keypress", false, true);
- event.ctrlKey = true;
- event.keyCode = 13;
- table.dispatchEvent(event);
- await new Promise((x) => setTimeout(x, 100));
- }
- }, table);
- });
-
- test("displays evaluated expression", async () => {
- const tr2 = await page.$$(
- "regular-table tbody tr:nth-of-type(3) td",
- );
- const tds2 = [];
- for (const td of tr2) {
- tds2.push(await page.evaluate((td) => td.innerHTML, td));
- }
-
- expect(tds2).toEqual(["2", "", "", ""]);
- const tr3 = await page.$$(
- "regular-table tbody tr:nth-of-type(4) td",
- );
- const tds3 = [];
- for (const td of tr3) {
- tds3.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(tds3).toEqual(["", "1", "", ""]);
- });
-
- describe("on scroll", () => {
- beforeAll(async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = table.scrollTop + 25;
- await table.draw();
- }, table);
- });
-
- test("preserves evaluated expression", async () => {
- const tr1 = await page.$$(
- "regular-table tbody tr:nth-of-type(2) td",
- );
- const tds1 = [];
- for (const td of tr1) {
- tds1.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(tds1).toEqual(["2", "", "", ""]);
- const tr2 = await page.$$(
- "regular-table tbody tr:nth-of-type(3) td",
- );
- const tds2 = [];
- for (const td of tr2) {
- tds2.push(await page.evaluate((td) => td.innerHTML, td));
- }
- expect(tds2).toEqual(["", "1", "", ""]);
- });
- });
- });
-});
diff --git a/test/examples/two_billion_rows.test.js b/test/examples/two_billion_rows.test.js
deleted file mode 100644
index 53a437e5..00000000
--- a/test/examples/two_billion_rows.test.js
+++ /dev/null
@@ -1,221 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("two_billion_rows.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 200, height: 100 });
- });
-
- describe("creates a `` body when attached to `document`", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/two_billion_rows.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(5);
- });
-
- test("with the correct # of columns", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const num_cells = await page.evaluate(
- (first_tr) => first_tr.children.length,
- first_tr,
- );
- expect(num_cells).toEqual(4);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["Group 0", "Row 0", "0", "1"]);
- });
- });
-
- describe("scrolls down", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/two_billion_rows.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = 1000;
- await table._draw_flush();
- }, table);
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(4);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual([
- "Group 200,000",
- "Row 200,001",
- "200,001",
- "200,002",
- "200,003",
- ]);
- });
- });
-
- describe("scrolls right", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/two_billion_rows.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollLeft = 1000;
- await table._draw_flush();
- }, table);
- });
-
- test("with the correct # of columns", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const num_cells = await page.evaluate(
- (first_tr) => first_tr.children.length,
- first_tr,
- );
- expect(num_cells).toEqual(4);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["Group 0", "Row 0", "16", "17"]);
- });
- });
-
- describe("scrolls via scrollToCell() method", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/two_billion_rows.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test.skip("https://github.com/finos/regular-table/issues/15", async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollToCell(0, 250500, 1000, 2000000000);
- await table.draw({ invalid_viewport: true });
- }, table);
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["250,501", "250,502", "250,503"]);
- });
- });
-
- describe("scrolls down beyond bottom threshold", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/two_billion_rows.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = table.scrollHeight + 100000;
- await table._draw_flush();
- }, table);
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(3);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual([
- "Group 1,999,999,990",
- "Row 1,999,999,997",
- "1,999,999,997",
- "1,999,999,998",
- "1,999,999,999",
- ]);
- });
- });
-
- describe("scrolls rights beyond right border threshold", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/examples/two_billion_rows.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollLeft = table.scrollWidth + 100000;
- await table._draw_flush();
- }, table);
- });
-
- test("with the correct # of columns", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const num_cells = await page.evaluate(
- (first_tr) => first_tr.children.length,
- first_tr,
- );
- expect(num_cells).toEqual(4);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["Group 0", "Row 0", "998", "999"]);
- });
- });
-});
diff --git a/test/examples/web_worker.test.js b/test/examples/web_worker.test.js
deleted file mode 100644
index 5b0773ff..00000000
--- a/test/examples/web_worker.test.js
+++ /dev/null
@@ -1,71 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("web_worker.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 250, height: 100 });
- });
-
- // TODO don't run these, they depend on unpkg.com
- describe("creates a `` body when attached to `document`", () => {
- beforeAll(async () => {
- await page.goto("http://localhost:8081/examples/web_worker.html");
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(5);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["0", "1", "2", "3"]);
- });
- });
-
- describe("scrolls down", () => {
- beforeAll(async () => {
- await page.goto("http://localhost:8081/examples/web_worker.html");
- const table = await page.$("regular-table");
- await page.waitForSelector("regular-table table tbody tr td");
- await page.evaluate(async (table) => {
- table.scrollTop = 1000;
- await table.draw();
- }, table);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual([
- "200,001",
- "200,002",
- "200,003",
- "200,004",
- "200,005",
- ]);
- });
- });
-});
diff --git a/test/features/api.test.js b/test/features/api.test.js
deleted file mode 100644
index 138fb520..00000000
--- a/test/features/api.test.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("JS API", () => {
- beforeAll(async () => {
- await page.goto("http://localhost:8081/test/features/api.html");
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- test("preserve_state works", async () => {
- const table = await page.$("regular-table");
- const meta = await page.evaluate(async (table) => {
- await table.draw();
- table._column_sizes.override[0] = 200;
- await table.draw();
- table.setDataListener(window.dataListener, {
- preserve_state: true,
- });
-
- await table.draw();
- return JSON.stringify(table.getMeta(document.querySelector("td")));
- }, table);
-
- expect(JSON.parse(meta)).toEqual({
- column_header: ["Group 0", "Column 0"],
- row_header: ["Group 0", "Row 0"],
- dx: 0,
- dy: 0,
- size_key: 2,
- _virtual_x: 2,
- value: "0",
- x: 0,
- x0: 0,
- x1: 15,
- y: 0,
- y0: 0,
- y1: 29,
- });
- });
-});
diff --git a/test/features/area_clipboard.test.js b/test/features/area_clipboard.test.js
deleted file mode 100644
index eaed20ca..00000000
--- a/test/features/area_clipboard.test.js
+++ /dev/null
@@ -1,348 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe.skip("area_clipboard.html", () => {
- const cellValues = async (cssClass) => {
- const selectedCells = await page.$$(
- `regular-table tbody tr td.${cssClass}`,
- );
- const values = [];
- for (const td of selectedCells) {
- values.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return values;
- };
-
- const positions = async (cssClass) => {
- const table = await page.$("regular-table");
- let metas = [];
- const selectedCells = await page.$$(
- `regular-table tbody tr td.${cssClass}`,
- );
- for (const td of selectedCells) {
- metas.push(
- await page.evaluate(
- (table, td) => table.getMeta(td),
- table,
- td,
- ),
- );
- }
- return metas.map(({ x, y }) => [x, y]);
- };
-
- const draw = async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- await table.draw();
- }, table);
- };
-
- const copy = async (multi = true) => {
- const table = await page.$("regular-table");
- await page.evaluate(
- async (table, multi) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.ctrlKey = multi;
- event.keyCode = 67;
- table.dispatchEvent(event);
- },
- table,
- multi,
- );
- };
-
- const cut = async (multi = true) => {
- const table = await page.$("regular-table");
- await page.evaluate(
- async (table, multi) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.ctrlKey = multi;
- event.keyCode = 88;
- table.dispatchEvent(event);
- },
- table,
- multi,
- );
- };
-
- const paste = async (multi = true) => {
- const table = await page.$("regular-table");
- await page.evaluate(
- async (table, multi) => {
- const event = document.createEvent("HTMLEvents");
- event.initEvent("keydown", false, true);
- event.ctrlKey = multi;
- event.keyCode = 86;
- table.dispatchEvent(event);
- },
- table,
- multi,
- );
- };
-
- const makeSelection = async (el1, el2, multi = false) => {
- await page.evaluate(
- async (td, multi) => {
- const event = new MouseEvent("mousedown", {
- bubbles: true,
- ctrlKey: multi,
- });
- td.dispatchEvent(event);
- },
- el1,
- multi,
- );
-
- await page.evaluate(
- async (td, multi) => {
- const event = new MouseEvent("mouseup", {
- bubbles: true,
- ctrlKey: multi,
- });
- td.dispatchEvent(event);
- },
- el2,
- multi,
- );
- };
-
- beforeEach(async () => {
- await page.setViewport({ width: 100, height: 100 });
- // const context = await browser.defaultBrowserContext();
- // await context.overridePermissions("http://localhost:8081/dist/examples/area_clipboard.html", ["clipboard-write", "clipboard-read"]);
- await page.goto(
- "http://localhost:8081/dist/features/area_clipboard.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("copy/paste", () => {
- test("copies one area", async () => {
- const col1Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(1)",
- );
- const col2Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(2)",
- );
-
- makeSelection(col1Tds[0], col2Tds[1]);
- await copy();
-
- const selectedCells = await cellValues("mouse-selected-area");
- expect(selectedCells).toEqual(["0, 0", "1, 0", "0, 1", "1, 1"]);
-
- makeSelection(col1Tds[2], col1Tds[2]);
-
- await paste();
-
- expect(await positions("clipboard-paste-selected-area")).toEqual([
- [0, 2],
- [1, 2],
- [0, 3],
- [1, 3],
- ]);
-
- expect(await cellValues("clipboard-paste-selected-area")).toEqual([
- "0, 0",
- "1, 0",
- "0, 1",
- "1, 1",
- ]);
- });
-
- test("copies multi select areas to multiple targets", async () => {
- const col1Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(1)",
- );
- const col2Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(2)",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[0]);
-
- makeSelection(col2Tds[2], col2Tds[2], true);
-
- await copy();
-
- await draw();
-
- expect(await positions("clipboard-copy-selected-area")).toEqual([
- [0, 0],
- [1, 2],
- ]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[1]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[1]);
-
- makeSelection(col2Tds[1], col2Tds[1], true);
-
- await draw();
-
- expect(await positions("mouse-selected-area")).toEqual([
- [0, 1],
- [1, 1],
- ]);
-
- await paste();
-
- await draw();
-
- expect(await positions("clipboard-paste-selected-area")).toEqual([
- [0, 1],
- [1, 1],
- ]);
-
- expect(await cellValues("clipboard-paste-selected-area")).toEqual([
- "0, 0",
- "1, 2",
- ]);
- });
-
- test("repeates on multi select area", async () => {
- const col1Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(1)",
- );
- const col2Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(2)",
- );
-
- makeSelection(col1Tds[0], col1Tds[0]);
-
- await copy();
-
- await draw();
-
- expect(await positions("clipboard-copy-selected-area")).toEqual([
- [0, 0],
- ]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[1]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[1]);
-
- makeSelection(col2Tds[2], col2Tds[2], true);
-
- await draw();
-
- expect(await positions("mouse-selected-area")).toEqual([
- [0, 1],
- [1, 2],
- ]);
-
- await paste();
-
- await draw();
-
- expect(await positions("clipboard-paste-selected-area")).toEqual([
- [0, 1],
- [1, 2],
- ]);
-
- expect(await cellValues("clipboard-paste-selected-area")).toEqual([
- "0, 0",
- "0, 0",
- ]);
- });
- });
-
- describe("cut/paste", () => {
- test("cuts multi select areas to multiple targets", async () => {
- const col1Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(1)",
- );
- const col2Tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(2)",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[0]);
-
- makeSelection(col2Tds[2], col2Tds[2], true);
-
- await cut();
-
- await draw();
-
- expect(await positions("clipboard-copy-selected-area")).toEqual([
- [0, 0],
- [1, 2],
- ]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[1]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, col1Tds[1]);
-
- makeSelection(col2Tds[1], col2Tds[1], true);
-
- await draw();
-
- expect(await positions("mouse-selected-area")).toEqual([
- [0, 1],
- [1, 1],
- ]);
-
- await paste();
-
- await draw();
-
- expect(await positions("clipboard-paste-selected-area")).toEqual([
- [0, 1],
- [1, 1],
- ]);
-
- expect(await cellValues("clipboard-paste-selected-area")).toEqual([
- "0, 0",
- "1, 2",
- ]);
- expect(await cellValues("clipboard-copy-selected-area")).toEqual([
- "",
- "",
- ]);
- });
- });
-});
diff --git a/test/features/area_mouse_selection.test.js b/test/features/area_mouse_selection.test.js
deleted file mode 100644
index 29dbe4cb..00000000
--- a/test/features/area_mouse_selection.test.js
+++ /dev/null
@@ -1,180 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("area_mouse_selection.html", () => {
- const selectedCellValues = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-area",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 300, height: 300 });
- await page.goto(
- "http://localhost:8081/dist/features/area_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("initial view", () => {
- test("includes no selection", async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-area",
- );
- expect(selectedCells.length).toEqual(0);
- });
- });
-
- describe("selecting one cell", () => {
- test("includes one selection", async () => {
- const tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(1)",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, tds[0]);
-
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-area",
- );
- expect(selectedCells.length).toEqual(1);
- });
- });
-
- describe("selecting an area", () => {
- test("selects along a row", async () => {
- const row1Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(1) td",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, row1Tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, row1Tds[2]);
-
- expect(await selectedCellValues()).toEqual(["0", "1", "2"]);
- });
-
- test("selects along a column, bottom to top", async () => {
- const row1Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(1) td",
- );
- const row5Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(5) td",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, row5Tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, row1Tds[0]);
-
- expect(await selectedCellValues()).toEqual([
- "0",
- "1",
- "2",
- "3",
- "4",
- ]);
- });
-
- test("selects in a rectangle, bottom-right to top-left", async () => {
- const row2Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(2) td",
- );
- const row5Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(5) td",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, row5Tds[2]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, row2Tds[1]);
-
- expect(await selectedCellValues()).toEqual([
- "2",
- "3",
- "3",
- "4",
- "4",
- "5",
- "5",
- "6",
- ]);
- });
-
- test("keeps selection on CTRL", async () => {
- const row2Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(2) td",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, row2Tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, row2Tds[1]);
-
- expect(await selectedCellValues()).toEqual(["1", "2"]);
-
- const row5Tds = await page.$$(
- "regular-table tbody tr:nth-of-type(5) td",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", {
- bubbles: true,
- ctrlKey: true,
- });
- td.dispatchEvent(event);
- }, row5Tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", {
- bubbles: true,
- ctrlKey: true,
- });
- td.dispatchEvent(event);
- }, row5Tds[1]);
-
- expect(await selectedCellValues()).toEqual(["1", "2", "4", "5"]);
- });
- });
-});
diff --git a/test/features/column_mouse_selection/selecting_column_headers.test.js b/test/features/column_mouse_selection/selecting_column_headers.test.js
deleted file mode 100644
index 74b3a8dd..00000000
--- a/test/features/column_mouse_selection/selecting_column_headers.test.js
+++ /dev/null
@@ -1,123 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("column_mouse_selection.html", () => {
- const selectedColumns = async () => {
- const selectedCells = await page.$$(
- "regular-table thead th.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate((td) => td.firstChild.innerHTML, td),
- );
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/column_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("initial view", () => {
- test("includes defaults", async () => {
- expect(await selectedColumns()).toEqual([
- "Column 6",
- "Column 8",
- "Column 9",
- ]);
- });
- });
-
- describe("column selection", () => {
- describe("selecting the origin header", () => {
- test("includes no selection", async () => {
- const ths = await page.$$("regular-table thead th");
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[0]);
-
- expect(await selectedColumns()).toEqual([]);
- });
- });
-
- describe("selecting the first column group", () => {
- let ths;
-
- beforeAll(async () => {
- ths = await page.$$("regular-table thead th");
- });
-
- test("includes the group and the columns", async () => {
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[1]);
-
- expect(await selectedColumns()).toEqual([
- "Group 0",
- "Column 0",
- "Column 1",
- "Column 2",
- "Column 3",
- "Column 4",
- "Column 5",
- "Column 6",
- "Column 7",
- "Column 8",
- "Column 9",
- ]);
- });
-
- test("splitting the group with ctrl", async () => {
- expect(await selectedColumns()).toEqual([
- "Group 0",
- "Column 0",
- "Column 1",
- "Column 2",
- "Column 3",
- "Column 4",
- "Column 5",
- "Column 6",
- "Column 7",
- "Column 8",
- "Column 9",
- ]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, ths[9]);
-
- expect(await selectedColumns()).toEqual([
- "Column 0",
- "Column 1",
- "Column 3",
- "Column 4",
- "Column 5",
- "Column 6",
- "Column 7",
- "Column 8",
- "Column 9",
- ]);
- });
- });
- });
-});
diff --git a/test/features/column_mouse_selection/selecting_one_column.test.js b/test/features/column_mouse_selection/selecting_one_column.test.js
deleted file mode 100644
index ed07618b..00000000
--- a/test/features/column_mouse_selection/selecting_one_column.test.js
+++ /dev/null
@@ -1,69 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("column_mouse_selection.html", () => {
- const selectedColumns = async () => {
- const selectedCells = await page.$$(
- "regular-table thead th.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate((td) => td.firstChild.innerHTML, td),
- );
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/column_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting one column", () => {
- beforeAll(async () => {
- const ths = await page.$$(
- "regular-table thead tr:nth-of-type(2) th",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[4]);
- });
-
- test("selects the cells", async () => {
- expect(await selectedColumns()).toEqual(["Column 2"]);
-
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate(
- (td) =>
- td.innerHTML
- .trim()
- .split(" ")
- .slice(0, 2)
- .join(" "),
- td,
- ),
- );
- }
- expect(selectedValues.length).toEqual(129);
- });
- });
-});
diff --git a/test/features/column_mouse_selection/selecting_one_column_range.test.js b/test/features/column_mouse_selection/selecting_one_column_range.test.js
deleted file mode 100644
index 1a27b7a9..00000000
--- a/test/features/column_mouse_selection/selecting_one_column_range.test.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("column_mouse_selection.html", () => {
- const selectedColumns = async () => {
- const selectedCells = await page.$$(
- "regular-table thead th.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate((td) => td.firstChild.innerHTML, td),
- );
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/column_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting a column range", () => {
- let ths;
-
- beforeAll(async () => {
- ths = await page.$$("regular-table thead th");
- });
-
- test("selects the columns' headers and cells", async () => {
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[9]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, ths[11]);
-
- await page.waitForSelector(
- "regular-table td.mouse-selected-column",
- );
- expect(await selectedColumns()).toEqual([
- "Column 2",
- "Column 3",
- "Column 4",
- ]);
- });
- });
-});
diff --git a/test/features/column_mouse_selection/selecting_two_columns.test.js b/test/features/column_mouse_selection/selecting_two_columns.test.js
deleted file mode 100644
index 72eeb4e1..00000000
--- a/test/features/column_mouse_selection/selecting_two_columns.test.js
+++ /dev/null
@@ -1,84 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("column_mouse_selection.html", () => {
- const selectedColumns = async () => {
- const selectedCells = await page.$$(
- "regular-table thead th.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate((td) => td.firstChild.innerHTML, td),
- );
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/column_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting two columns", () => {
- describe("without CTRL pressed", () => {
- test("includes only the most recent selection", async () => {
- const ths = await page.$$(
- "regular-table thead tr:nth-of-type(2) th",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[3]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: false,
- });
- th.dispatchEvent(event);
- }, ths[5]);
-
- expect(await selectedColumns()).toEqual(["Column 3"]);
- });
- });
-
- describe("with CTRL pressed", () => {
- test("includes both columns", async () => {
- const ths = await page.$$(
- "regular-table thead tr:nth-of-type(2) th",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[3]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, ths[5]);
-
- expect(await selectedColumns()).toEqual([
- "Column 1",
- "Column 3",
- ]);
- });
- });
- });
-});
diff --git a/test/features/column_mouse_selection/splitting_one_column_range.test.js b/test/features/column_mouse_selection/splitting_one_column_range.test.js
deleted file mode 100644
index 3c740764..00000000
--- a/test/features/column_mouse_selection/splitting_one_column_range.test.js
+++ /dev/null
@@ -1,87 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("column_mouse_selection.html", () => {
- const selectedColumns = async () => {
- const selectedCells = await page.$$(
- "regular-table thead th.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate((td) => td.firstChild.innerHTML, td),
- );
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/column_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("splitting a column range", () => {
- let ths;
-
- beforeAll(async () => {
- ths = await page.$$("regular-table thead th");
- });
-
- test("selects the columns' headers and cells", async () => {
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, ths[17]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, ths[13]);
-
- await page.waitForSelector(
- "regular-table td.mouse-selected-column",
- );
- expect(await selectedColumns()).toEqual([
- "Column 6",
- "Column 7",
- "Column 8",
- "Column 9",
- "Column 10",
- ]);
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, ths[15]);
-
- await page.waitForSelector(
- "regular-table td.mouse-selected-column",
- );
- expect(await selectedColumns()).toEqual([
- "Column 6",
- "Column 7",
- "Column 9",
- "Column 10",
- ]);
- });
- });
-});
diff --git a/test/features/fixed_column_widths.test.js b/test/features/fixed_column_widths.test.js
deleted file mode 100644
index 4dc6a9aa..00000000
--- a/test/features/fixed_column_widths.test.js
+++ /dev/null
@@ -1,119 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe.skip("fixed_column_widths.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 400, height: 100 });
- });
-
- describe("creates a `` with fixed column widths", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/dist/features/fixed_column_widths.html",
- );
- await page.waitForSelector("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,
- );
-
- // The viewport is 400px wide and the fixed columns are 100px
- // accross, and ~20px of padding is four columns.
- expect(user_selects).toEqual(["none", "none", "none", "none"]);
- });
- });
-});
diff --git a/test/features/row_column_area_selection/area_selection.test.js b/test/features/row_column_area_selection/area_selection.test.js
deleted file mode 100644
index a0ebbd27..00000000
--- a/test/features/row_column_area_selection/area_selection.test.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe.skip("row_column_area_selection.html", () => {
- beforeEach(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/examples/row_column_area_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting one cell", () => {
- test("includes one selection", async () => {
- const tds = await page.$$(
- "regular-table tbody tr td:nth-of-type(1)",
- );
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mousedown", { bubbles: true });
- td.dispatchEvent(event);
- }, tds[0]);
-
- await page.evaluate(async (td) => {
- const event = new MouseEvent("mouseup", { bubbles: true });
- td.dispatchEvent(event);
- }, tds[0]);
-
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-area",
- );
- expect(selectedCells.length).toEqual(1);
- });
- });
-});
diff --git a/test/features/row_column_area_selection/column_selection.test.js b/test/features/row_column_area_selection/column_selection.test.js
deleted file mode 100644
index 392da98e..00000000
--- a/test/features/row_column_area_selection/column_selection.test.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe.skip("row_column_area_selection.html", () => {
- const selectedColumns = async () => {
- const selectedCells = await page.$$(
- "regular-table thead th.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate((td) => td.firstChild.innerHTML, td),
- );
- }
- return selectedValues;
- };
-
- beforeEach(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/examples/row_column_area_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting one column", () => {
- test("selects the cells", async () => {
- const ths = await page.$$(
- "regular-table thead tr:nth-of-type(2) th",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[4]);
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-column",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate(
- (td) =>
- td.innerHTML
- .trim()
- .split(" ")
- .slice(0, 2)
- .join(" "),
- td,
- ),
- );
- }
- expect(selectedValues.length > 0).toEqual(true);
- expect(await selectedColumns()).toEqual(["Column 2"]);
- });
- });
-});
diff --git a/test/features/row_column_area_selection/row_selection.test.js b/test/features/row_column_area_selection/row_selection.test.js
deleted file mode 100644
index fd9307c7..00000000
--- a/test/features/row_column_area_selection/row_selection.test.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe.skip("row_column_area_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- let ths;
-
- beforeEach(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/examples/row_column_area_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- ths = await page.$$("regular-table tbody tr th:nth-of-type(2)");
- });
-
- describe("selecting one row", () => {
- test("selects the cells", async () => {
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[0]);
-
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(
- await page.evaluate(
- (td) =>
- td.innerHTML
- .trim()
- .split(" ")
- .slice(0, 2)
- .join(" "),
- td,
- ),
- );
- }
- expect(selectedValues.length > 0).toEqual(true);
- expect(await selectedRows()).toEqual(["Row 0"]);
- });
- });
-});
diff --git a/test/features/row_mouse_selection/selecting_grouped_row_headers.test.js b/test/features/row_mouse_selection/selecting_grouped_row_headers.test.js
deleted file mode 100644
index 98bc562e..00000000
--- a/test/features/row_mouse_selection/selecting_grouped_row_headers.test.js
+++ /dev/null
@@ -1,131 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_mouse_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/row_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting a group range", () => {
- describe("both selections are group headers", () => {
- test("selects the groups' headers, rows' headers and cells", async () => {
- const groupHeader0 = await page.$(
- "regular-table tbody tr th:nth-of-type(1)",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, groupHeader0);
-
- const ths = await page.$$("regular-table tbody th");
- const groupHeader10 = ths[11];
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, groupHeader10);
-
- await page.waitForSelector(
- "regular-table td.mouse-selected-row",
- );
- expect(await selectedRows()).toEqual([
- "Group 0",
- "Row 0",
- "Row 1",
- "Row 2",
- "Row 3",
- "Row 4",
- "Row 5",
- "Row 6",
- "Row 7",
- "Row 8",
- "Row 9",
- "Group 10",
- "Row 10",
- "Row 11",
- "Row 12",
- "Row 13",
- "Row 14",
- "Row 15",
- "Row 16",
- "Row 17",
- "Row 18",
- "Row 19",
- ]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[8]);
- });
- });
-
- describe("second selection is a row header", () => {
- test("selects the rows' headers and cells", async () => {
- const groupHeader0 = await page.$(
- "regular-table tbody tr th:nth-of-type(1)",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, groupHeader0);
-
- const rowHeader11 = await page.$(
- "regular-table tbody tr:nth-of-type(12) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, rowHeader11);
-
- await page.waitForSelector(
- "regular-table td.mouse-selected-row",
- );
- expect(await selectedRows()).toEqual([
- "Row 0",
- "Row 1",
- "Row 2",
- "Row 3",
- "Row 4",
- "Row 5",
- "Row 6",
- "Row 7",
- "Row 8",
- "Row 9",
- "Row 10",
- "Row 11",
- ]);
- });
- });
- });
-});
diff --git a/test/features/row_mouse_selection/selecting_one_row.test.js b/test/features/row_mouse_selection/selecting_one_row.test.js
deleted file mode 100644
index 74f07256..00000000
--- a/test/features/row_mouse_selection/selecting_one_row.test.js
+++ /dev/null
@@ -1,58 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_mouse_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/row_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting one row", () => {
- test("selects the row header and cells then deselects", async () => {
- const rowHeader1 = await page.$(
- "regular-table tbody tr:nth-of-type(2) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, rowHeader1);
- await page.waitForSelector("regular-table td.mouse-selected-row");
- const selectedCells = await page.$$(
- "regular-table tbody tr td.mouse-selected-row",
- );
- expect(await selectedRows()).toEqual(["Row 1"]);
- expect(selectedCells.length > 0).toEqual(true);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, rowHeader1);
- expect(await selectedRows()).toEqual([]);
- });
- });
-});
diff --git a/test/features/row_mouse_selection/selecting_one_row_range.test.js b/test/features/row_mouse_selection/selecting_one_row_range.test.js
deleted file mode 100644
index 8cc3ed77..00000000
--- a/test/features/row_mouse_selection/selecting_one_row_range.test.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_mouse_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/row_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting a row range", () => {
- test("selects the rows' headers and cells", async () => {
- const rowHeader1 = await page.$(
- "regular-table tbody tr:nth-of-type(2) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, rowHeader1);
-
- const rowHeader3 = await page.$(
- "regular-table tbody tr:nth-of-type(4) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, rowHeader3);
-
- await page.waitForSelector("regular-table td.mouse-selected-row");
- expect(await selectedRows()).toEqual(["Row 1", "Row 2", "Row 3"]);
- });
- });
-});
diff --git a/test/features/row_mouse_selection/selecting_row_headers.test.js b/test/features/row_mouse_selection/selecting_row_headers.test.js
deleted file mode 100644
index 9a0a016c..00000000
--- a/test/features/row_mouse_selection/selecting_row_headers.test.js
+++ /dev/null
@@ -1,105 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_mouse_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/row_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("initial view", () => {
- test("includes no selection", async () => {
- expect(await selectedRows()).toEqual([]);
- });
- });
-
- describe("row selection", () => {
- describe("selecting one row group", () => {
- test("includes the group and the rows", async () => {
- const groupHeader0 = await page.$(
- "regular-table tbody tr th:nth-of-type(1)",
- );
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, groupHeader0);
-
- expect(await selectedRows()).toEqual([
- "Group 0",
- "Row 0",
- "Row 1",
- "Row 2",
- "Row 3",
- "Row 4",
- "Row 5",
- "Row 6",
- "Row 7",
- "Row 8",
- "Row 9",
- ]);
- });
-
- test("splitting the group with ctrl", async () => {
- expect(await selectedRows()).toEqual([
- "Group 0",
- "Row 0",
- "Row 1",
- "Row 2",
- "Row 3",
- "Row 4",
- "Row 5",
- "Row 6",
- "Row 7",
- "Row 8",
- "Row 9",
- ]);
-
- const rowHeader3 = await page.$(
- "regular-table tbody tr:nth-of-type(4) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, rowHeader3);
-
- expect(await selectedRows()).toEqual([
- "Row 0",
- "Row 1",
- "Row 2",
- "Row 4",
- "Row 5",
- "Row 6",
- "Row 7",
- "Row 8",
- "Row 9",
- ]);
- });
- });
- });
-});
diff --git a/test/features/row_mouse_selection/selecting_two_rows.test.js b/test/features/row_mouse_selection/selecting_two_rows.test.js
deleted file mode 100644
index 40f98174..00000000
--- a/test/features/row_mouse_selection/selecting_two_rows.test.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_mouse_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/row_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("selecting two rows", () => {
- describe("without CTRL pressed", () => {
- test("includes only the most recent selection", async () => {
- const ths = await page.$$("regular-table tbody tr th");
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[3]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: false,
- });
- th.dispatchEvent(event);
- }, ths[5]);
-
- expect(await selectedRows()).toEqual(["Row 4"]);
- });
- });
-
- describe("with CTRL pressed", () => {
- test("includes the rows", async () => {
- const ths = await page.$$("regular-table tbody tr th");
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, ths[3]);
-
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, ths[5]);
-
- expect(await selectedRows()).toEqual(["Row 2", "Row 4"]);
- });
- });
- });
-});
diff --git a/test/features/row_mouse_selection/splitting_one_row_range.test.js b/test/features/row_mouse_selection/splitting_one_row_range.test.js
deleted file mode 100644
index 41551e8d..00000000
--- a/test/features/row_mouse_selection/splitting_one_row_range.test.js
+++ /dev/null
@@ -1,68 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_mouse_selection.html", () => {
- const selectedRows = async () => {
- const selectedCells = await page.$$(
- "regular-table tbody tr th.mouse-selected-row",
- );
- const selectedValues = [];
- for (const td of selectedCells) {
- selectedValues.push(await page.evaluate((td) => td.innerHTML, td));
- }
- return selectedValues;
- };
-
- beforeAll(async () => {
- await page.setViewport({ width: 2500, height: 2500 });
- await page.goto(
- "http://localhost:8081/dist/features/row_mouse_selection.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("splitting a row range", () => {
- test("selects the rows' headers and cells", async () => {
- const rowHeader1 = await page.$(
- "regular-table tbody tr:nth-of-type(2) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", { bubbles: true });
- th.dispatchEvent(event);
- }, rowHeader1);
-
- const rowHeader3 = await page.$(
- "regular-table tbody tr:nth-of-type(4) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- shiftKey: true,
- });
- th.dispatchEvent(event);
- }, rowHeader3);
-
- const rowHeader2 = await page.$(
- "regular-table tbody tr:nth-of-type(3) th",
- );
- await page.evaluate(async (th) => {
- const event = new MouseEvent("click", {
- bubbles: true,
- ctrlKey: true,
- });
- th.dispatchEvent(event);
- }, rowHeader2);
-
- await page.waitForSelector("regular-table td.mouse-selected-row");
- expect(await selectedRows()).toEqual(["Row 1", "Row 3"]);
- });
- });
-});
diff --git a/test/features/row_stripes.test.js b/test/features/row_stripes.test.js
deleted file mode 100644
index 4b176913..00000000
--- a/test/features/row_stripes.test.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("row_stripes.html", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 200, height: 100 });
- await page.goto("http://localhost:8081/dist/features/row_stripes.html");
- await page.waitForSelector("regular-table table tbody tr td");
- });
-
- describe("initial view", () => {
- test("row style alternates", async () => {
- const tds1 = await page.$$(
- "regular-table tbody tr:nth-of-type(1) td",
- );
- const backgroundColor1 = await page.evaluate(
- (td) =>
- getComputedStyle(td).getPropertyValue("background-color"),
- tds1[0],
- );
- expect(backgroundColor1).toEqual("rgb(234, 237, 239)");
-
- const tds2 = await page.$$(
- "regular-table tbody tr:nth-of-type(2) td",
- );
- const backgroundColor2 = await page.evaluate(
- (td) =>
- getComputedStyle(td).getPropertyValue("background-color"),
- tds2[0],
- );
- expect(backgroundColor2).toEqual("rgb(255, 255, 255)");
- });
- });
-
- describe("initial view", () => {
- beforeAll(async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = table.scrollTop + 23;
- await table._draw_flush();
- }, table);
- });
-
- test("row style alternates in reverse", async () => {
- const tds1 = await page.$$(
- "regular-table tbody tr:nth-of-type(1) td",
- );
- const backgroundColor1 = await page.evaluate(
- (td) =>
- getComputedStyle(td).getPropertyValue("background-color"),
- tds1[0],
- );
- expect(backgroundColor1).toEqual("rgb(255, 255, 255)");
-
- const tds2 = await page.$$(
- "regular-table tbody tr:nth-of-type(2) td",
- );
- const backgroundColor2 = await page.evaluate(
- (td) =>
- getComputedStyle(td).getPropertyValue("background-color"),
- tds2[0],
- );
- expect(backgroundColor2).toEqual("rgb(234, 237, 239)");
- });
- });
-
- test("removes style listener", async () => {
- await page.evaluate(() => {
- window.removeStripes();
- });
-
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- // Scroll a few pages down to verify that the style listener wasn't called
- table.scrollBy(0, window.innerHeight * 10);
- await table._draw_flush();
- }, table);
-
- const tds1 = await page.$$("regular-table tbody tr:nth-of-type(1) td");
- const backgroundColor1 = await page.evaluate(
- (td) => getComputedStyle(td).getPropertyValue("background-color"),
- tds1[0],
- );
- expect(backgroundColor1).toEqual("rgba(0, 0, 0, 0)");
-
- const tds2 = await page.$$("regular-table tbody tr:nth-of-type(2) td");
- const backgroundColor2 = await page.evaluate(
- (td) => getComputedStyle(td).getPropertyValue("background-color"),
- tds2[0],
- );
- expect(backgroundColor2).toEqual("rgba(0, 0, 0, 0)");
- });
-});
diff --git a/test/features/scrollTo.test.js b/test/features/scrollTo.test.js
deleted file mode 100644
index 2efa7c4a..00000000
--- a/test/features/scrollTo.test.js
+++ /dev/null
@@ -1,86 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("scrollToCell", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 200, height: 200 });
- await page.goto(
- "http://localhost:8081/test/features/2_row_2_column_headers.html",
- );
- await page.evaluate(async () => {
- await document.querySelector("regular-table").draw();
- });
- });
-
- afterEach(async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = 0;
- await table._draw_flush();
- }, table);
- });
-
- describe("sets the correct position", () => {
- test("for scrollToCell position {x: 2, y: 0}", async () => {
- const table = await page.$("regular-table");
-
- await page.evaluate(async (table) => {
- await table.scrollToCell(2, 0, 1000, 1000);
- }, table);
-
- const meta = await page.evaluate((table) => {
- return table.getMeta(document.querySelector("td"));
- }, table);
- expect(meta.x).toEqual(2);
- });
-
- test("for scrollToCell position {x: 1000, y: 0}", async () => {
- const table = await page.$("regular-table");
-
- await page.evaluate(async (table) => {
- await table.scrollToCell(1000, 0, 1000, 1000);
- await table.draw();
- }, table);
-
- const meta = await page.evaluate((table) => {
- return table.getMeta(document.querySelector("td"));
- }, table);
- expect(meta.x).toEqual(998);
- });
-
- test("for scrollToCell position {x: 0, y: 1}", async () => {
- const table = await page.$("regular-table");
-
- await page.evaluate(async (table) => {
- await table.scrollToCell(0, 1, 1000, 1000);
- }, table);
-
- const meta = await page.evaluate((table) => {
- return table.getMeta(document.querySelector("td"));
- }, table);
- expect(meta.y).toEqual(1);
- });
-
- test("for scrollToCell position {x: 211, y: 647}", async () => {
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- await table.scrollToCell(211, 647, 1000, 1000);
- }, table);
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.children).map((x) => x.textContent),
- first_tr,
- );
- expect(cell_values).toEqual(["Group 640", "Row 647", "858", "859"]);
- });
- });
-});
diff --git a/test/features/scrolling.test.js b/test/features/scrolling.test.js
deleted file mode 100644
index 263e5f75..00000000
--- a/test/features/scrolling.test.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ░░░░░░░░░░▄▀░█▀▄░█▀▀░█▀▀░█░█░█░░░█▀█░█▀▄░░░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░▀▄░░░░░░░░░░
-// ░░░░░░░░░▀▄░░█▀▄░█▀▀░█░█░█░█░█░░░█▀█░█▀▄░▀▀▀░░█░░█▀█░█▀▄░█░░░█▀▀░░▄▀░░░░░░░░░
-// ░░░░░░░░░░░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀░▀░░░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░▀░░░░░░░░░░░
-// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ * 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](https://www.apache.org/licenses/LICENSE-2.0). * ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-describe("scrolling", () => {
- beforeAll(async () => {
- await page.setViewport({ width: 260, height: 200 });
- });
-
- describe("scrolls down", () => {
- beforeAll(async () => {
- await page.goto(
- "http://localhost:8081/test/features/2_row_2_column_headers.html",
- );
- await page.waitForSelector("regular-table table tbody tr td");
- const table = await page.$("regular-table");
- await page.evaluate(async (table) => {
- table.scrollTop = 1000;
- await table._draw_flush();
- }, table);
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(9);
- });
-
- test("with the correct # of columns", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const num_cells = await page.evaluate(
- (first_tr) => first_tr.children.length,
- first_tr,
- );
- expect(num_cells).toEqual(6);
- });
-
- test("with the first row's test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.querySelectorAll("td")).map(
- (x) => x.textContent,
- ),
- first_tr,
- );
- expect(cell_values).toEqual(["52", "53", "54", "55"]);
- });
- });
-
- describe("scrolls right", () => {
- beforeAll(async () => {
- const table = await page.$("regular-table");
- await page.waitForSelector("regular-table table tbody tr td");
- await page.evaluate(async (table) => {
- table.scrollTop = 0;
- table.scrollLeft = 1000;
- await table._draw_flush();
- }, table);
- });
-
- test("with the correct # of rows", async () => {
- const tbody = await page.$("regular-table tbody");
- const num_rows = await page.evaluate(
- (tbody) => tbody.children.length,
- tbody,
- );
- expect(num_rows).toEqual(8);
- });
-
- test("with the correct # of columns", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const num_cells = await page.evaluate(
- (first_tr) => first_tr.children.length,
- first_tr,
- );
- expect(num_cells).toEqual(4);
- });
-
- test("with the first row's cell test correct", async () => {
- const first_tr = await page.$("regular-table tbody tr:first-child");
- const cell_values = await page.evaluate(
- (first_tr) =>
- Array.from(first_tr.querySelectorAll("td")).map(
- (x) => x.textContent,
- ),
- first_tr,
- );
- expect(cell_values).toEqual(["16", "17"]);
- });
- });
-});
diff --git a/test/features/2_row_2_column_headers.html b/tests/2_row_2_column_headers.html
similarity index 97%
rename from test/features/2_row_2_column_headers.html
rename to tests/2_row_2_column_headers.html
index 6cf25ca5..ed46af90 100644
--- a/test/features/2_row_2_column_headers.html
+++ b/tests/2_row_2_column_headers.html
@@ -18,13 +18,13 @@
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
-
-
-