@blueprintjs/table
Version:
Scalable interactive table component
392 lines (322 loc) • 15.8 kB
text/typescript
/*
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it } from "@blueprintjs/test-commons/vitest";
import { type CellCoordinate, type Region, RegionCardinality, Regions } from "./regions";
describe("Regions", () => {
describe("factories", () => {
it("creates cell regions", () => {
const region = Regions.cell(0, 1, 2, 3);
expect(Regions.isValid(region)).to.be.true;
expect(Regions.getRegionCardinality(region)).to.equal(RegionCardinality.CELLS);
expect(region).to.deep.equal({
cols: [1, 3],
rows: [0, 2],
});
expect(Regions.cell(0, 1)).to.deep.equal(Regions.cell(0, 1, 0, 1));
});
it("creates column regions", () => {
const region = Regions.column(7, 11);
expect(Regions.isValid(region)).to.be.true;
expect(Regions.getRegionCardinality(region)).to.equal(RegionCardinality.FULL_COLUMNS);
expect(region).to.deep.equal({
cols: [7, 11],
});
expect(Regions.column(1)).to.deep.equal(Regions.column(1, 1));
});
it("creates row regions", () => {
const region = Regions.row(3, 14);
expect(Regions.isValid(region)).to.be.true;
expect(Regions.getRegionCardinality(region)).to.equal(RegionCardinality.FULL_ROWS);
expect(region).to.deep.equal({
rows: [3, 14],
});
expect(Regions.row(1)).to.deep.equal(Regions.row(1, 1));
});
});
describe("array manipulation", () => {
it("adds regions", () => {
const regions = [Regions.row(1, 37)];
const added = Regions.add(regions, Regions.column(3, 14));
expect(added).to.not.equal(regions);
expect(added).to.have.lengthOf(regions.length + 1);
expect(Regions.lastRegionIsEqual(added, Regions.column(14, 3)));
});
it("updates regions at last index", () => {
const regions = [Regions.row(1, 37)];
const updated = Regions.update(regions, Regions.column(3, 14));
expect(updated).to.not.equal(regions);
expect(updated).to.have.lengthOf(regions.length);
expect(Regions.lastRegionIsEqual(updated, Regions.column(14, 3)));
});
it("updates regions at specified index", () => {
const INDEX = 1;
const regions = [Regions.row(1), Regions.column(1), Regions.cell(1, 1)];
const updated = Regions.update(regions, Regions.column(2), INDEX);
expect(updated).to.not.equal(regions);
expect(updated).to.have.lengthOf(regions.length);
expect(updated[INDEX]).to.deep.equal(Regions.column(2));
});
});
describe("isRegionValidForTable", () => {
const N = 3;
const VALID_INDEX_LOW = 0;
const VALID_INDEX_HIGH = N - 1;
const INVALID_INDEX_LOW = -1;
const INVALID_INDEX_HIGH = N;
const isValid = Regions.isRegionValidForTable;
describe("in an NxN table", () => {
const expectTrue = (region: Region, msg?: string) => expect(isValid(region, N, N), msg).to.be.true;
const expectFalse = (region: Region, msg?: string) => expect(isValid(region, N, N), msg).to.be.false;
describe("cell regions", () => {
it("returns false if row index out-of-bounds", () => {
expectFalse(Regions.cell(INVALID_INDEX_LOW, VALID_INDEX_LOW), "invalid low");
expectFalse(Regions.cell(INVALID_INDEX_HIGH, VALID_INDEX_LOW), "invalid high");
});
it("returns false if column index out-of-bounds", () => {
expectFalse(Regions.cell(VALID_INDEX_LOW, INVALID_INDEX_LOW), "invalid low");
expectFalse(Regions.cell(VALID_INDEX_LOW, INVALID_INDEX_HIGH), "invalid high");
});
it("returns true if both row and column indices in bounds", () => {
expectTrue(Regions.cell(VALID_INDEX_LOW, VALID_INDEX_LOW), "valid low");
expectTrue(Regions.cell(VALID_INDEX_HIGH, VALID_INDEX_HIGH), "valid high");
});
});
describe("column regions", () => {
it("returns false if column index out-of-bounds", () => {
expectFalse(Regions.column(INVALID_INDEX_LOW), "invalid low");
expectFalse(Regions.column(INVALID_INDEX_HIGH), "invalid high");
});
it("returns true if both row and column indices in bounds", () => {
expectTrue(Regions.column(VALID_INDEX_LOW), "valid low");
expectTrue(Regions.column(VALID_INDEX_HIGH), "valid high");
});
});
describe("row regions", () => {
it("returns false if row index out-of-bounds", () => {
expectFalse(Regions.row(INVALID_INDEX_LOW), "invalid low");
expectFalse(Regions.row(INVALID_INDEX_HIGH), "invalid high");
});
it("returns true if both row and column indices in bounds", () => {
expectTrue(Regions.row(VALID_INDEX_LOW), "valid low");
expectTrue(Regions.row(VALID_INDEX_HIGH), "valid high");
});
});
describe("table regions", () => {
it("always returns true", () => {
expectTrue(Regions.table());
});
});
});
describe("in an N-row, 0-column table", () => {
const expectFalse = (region: Region, msg?: string) =>
expect(isValid(region, N, VALID_INDEX_LOW), msg).to.be.false;
it("always returns false", () => {
expectFalse(Regions.cell(VALID_INDEX_LOW, VALID_INDEX_LOW));
expectFalse(Regions.column(INVALID_INDEX_LOW));
expectFalse(Regions.column(VALID_INDEX_LOW));
expectFalse(Regions.row(INVALID_INDEX_LOW));
expectFalse(Regions.row(VALID_INDEX_LOW));
expectFalse(Regions.table());
});
});
describe("in an N-column, 0-row table", () => {
const expectFalse = (region: Region, msg?: string) =>
expect(isValid(region, VALID_INDEX_LOW, N), msg).to.be.false;
it("always returns false", () => {
expectFalse(Regions.cell(INVALID_INDEX_LOW, INVALID_INDEX_LOW));
expectFalse(Regions.column(INVALID_INDEX_LOW));
expectFalse(Regions.column(VALID_INDEX_LOW));
expectFalse(Regions.row(INVALID_INDEX_LOW));
expectFalse(Regions.row(VALID_INDEX_LOW));
expectFalse(Regions.table());
});
});
describe("in a 0-column, 0-row table", () => {
const expectFalse = (region: Region, msg?: string) =>
expect(isValid(region, VALID_INDEX_LOW, VALID_INDEX_LOW), msg).to.be.false;
it("always returns false", () => {
expectFalse(Regions.cell(INVALID_INDEX_LOW, INVALID_INDEX_LOW));
expectFalse(Regions.cell(VALID_INDEX_LOW, VALID_INDEX_LOW));
expectFalse(Regions.column(INVALID_INDEX_LOW));
expectFalse(Regions.column(VALID_INDEX_LOW));
expectFalse(Regions.row(INVALID_INDEX_LOW));
expectFalse(Regions.row(VALID_INDEX_LOW));
expectFalse(Regions.table());
});
});
});
it("searches", () => {
const regions = [Regions.row(1, 37), Regions.column(3, 14), Regions.cell(1, 2, 3, 4)];
expect(Regions.findMatchingRegion(null, Regions.column(14, 3))).to.equal(-1);
expect(Regions.findMatchingRegion([], Regions.column(14, 3))).to.equal(-1);
expect(Regions.findMatchingRegion(regions, Regions.column(4, 14))).to.equal(-1);
expect(Regions.findMatchingRegion(regions, Regions.column(3, 14))).to.equal(1);
expect(Regions.findMatchingRegion(regions, Regions.column(14, 3))).to.equal(1);
});
it("containment", () => {
expect(Regions.hasFullColumn(null, 5)).to.be.false;
expect(Regions.hasFullColumn([Regions.row(0, 10)], 5)).to.be.false;
expect(Regions.hasFullColumn([Regions.column(0, 10)], 15)).to.be.false;
expect(Regions.hasFullColumn([Regions.column(0, 10)], 5)).to.be.true;
expect(Regions.hasFullRow(null, 5)).to.be.false;
expect(Regions.hasFullRow([Regions.column(0, 10)], 5)).to.be.false;
expect(Regions.hasFullRow([Regions.row(0, 10)], 15)).to.be.false;
expect(Regions.hasFullRow([Regions.row(0, 10)], 5)).to.be.true;
});
it("validates", () => {
expect(Regions.isValid(null)).to.be.false;
expect(Regions.isValid(Regions.column(3, 14))).to.be.true;
expect(Regions.isValid(Regions.column(14, 3))).to.be.true;
expect(Regions.isValid(Regions.column(-14, 3))).to.be.false;
expect(Regions.isValid(Regions.row(3, 14))).to.be.true;
expect(Regions.isValid(Regions.row(14, 3))).to.be.true;
expect(Regions.isValid(Regions.row(-14, 3))).to.be.false;
});
it("combines styled region groups", () => {
const myGroups = [
{
className: "my-region",
regions: [Regions.column(1)],
},
];
const joinedGroups = Regions.joinStyledRegionGroups([Regions.row(2)], myGroups, undefined);
expect(joinedGroups).to.have.lengthOf(2);
expect(joinedGroups[1].regions[0]).to.deep.equal(Regions.row(2));
});
it("iterates", () => {
const hits: string[] = [];
const append = () => {
hits.push("X");
};
Regions.eachUniqueFullColumn([], append);
expect(hits).to.have.lengthOf(0);
Regions.eachUniqueFullColumn([Regions.row(2)], append);
expect(hits).to.have.lengthOf(0);
Regions.eachUniqueFullColumn([Regions.row(2), Regions.column(2, 5)], append);
expect(hits).to.have.lengthOf(4);
});
it("enumerates cells", () => {
const invalid = Regions.enumerateUniqueCells(null, 3, 2);
expect(invalid).to.have.lengthOf(0);
const cells = Regions.enumerateUniqueCells([Regions.column(0), Regions.row(0)], 3, 2);
expect(cells).to.deep.equal([
[0, 0],
[0, 1],
[1, 0],
[2, 0],
]);
});
it("sparsely maps cells", () => {
const cells = [
[0, 0],
[0, 1],
[1, 0],
[2, 0],
] as CellCoordinate[];
const sparse = Regions.sparseMapCells(cells, () => "X");
// normal deep equals doesn't work here so we use JSON.stringify
expect(JSON.stringify(sparse)).to.equal(
JSON.stringify([
["X", "X"],
["X", null],
["X", null],
]),
);
});
describe("clampRegion", () => {
const fn = Regions.clampRegion;
it("returns a deep copy of the region", () => {
const validRegion = Regions.table();
const clampedRegion = fn(validRegion, 1, 1);
expect(clampedRegion === validRegion).to.be.false;
});
it("clamps regions whose start indices are < 0", () => {
const cellRegion = Regions.cell(-1, 1);
expect(fn(cellRegion, 1, 1)).to.deep.equal(Regions.cell(0, 1));
const columnRegion = Regions.column(-1, 1);
expect(fn(columnRegion, 1, 1)).to.deep.equal(Regions.column(0, 1));
const rowRegion = Regions.row(-1, 1);
expect(fn(rowRegion, 1, 1)).to.deep.equal(Regions.row(0, 1));
});
it("clamps regions whose end indices are > max", () => {
const cellRegion = Regions.cell(0, 2);
expect(fn(cellRegion, 1, 1)).to.deep.equal(Regions.cell(0, 1));
const columnRegion = Regions.column(0, 2);
expect(fn(columnRegion, 1, 1)).to.deep.equal(Regions.column(0, 1));
const rowRegion = Regions.row(0, 2);
expect(fn(rowRegion, 1, 1)).to.deep.equal(Regions.row(0, 1));
});
it("returns a new FULL_TABLE region if provided", () => {
const tableRegion = Regions.table();
expect(fn(tableRegion, 1, 1)).to.deep.equal(Regions.table());
});
});
describe("copy", () => {
it("copies CELLS regions", () => {
const region = Regions.cell(0, 1, 2, 3);
const regionCopy = Regions.cell(0, 1, 2, 3);
expect(Regions.copy(region)).to.deep.equal(regionCopy);
});
it("copies FULL_COLUMNS regions", () => {
const region = Regions.column(0, 1);
const regionCopy = Regions.column(0, 1);
expect(Regions.copy(region)).to.deep.equal(regionCopy);
});
it("copies FULL_ROWS regions", () => {
const region = Regions.row(0, 1);
const regionCopy = Regions.row(0, 1);
expect(Regions.copy(region)).to.deep.equal(regionCopy);
});
it("copies FULL_TABLE regions", () => {
const region = Regions.table();
const regionCopy = Regions.table();
expect(Regions.copy(region)).to.deep.equal(regionCopy);
});
});
describe("expandRegion", () => {
it("returns new region if cardinalities are different", () => {
const oldRegion = Regions.cell(0, 0);
const newRegion = Regions.table();
const result = Regions.expandRegion(oldRegion, newRegion);
// should have returned the newRegion instance
expect(result).to.equal(newRegion);
});
it("expands a FULL_ROWS region", () => {
const oldRegion = Regions.row(1, 2);
const newRegion = Regions.row(9, 10);
const result = Regions.expandRegion(oldRegion, newRegion);
expect(result).to.deep.equal(Regions.row(1, 10));
});
it("expands a FULL_COLUMNS region", () => {
const oldRegion = Regions.column(9, 10);
const newRegion = Regions.column(1, 2);
const result = Regions.expandRegion(oldRegion, newRegion);
expect(result).to.deep.equal(Regions.column(1, 10));
});
it("expands a CELLS region", () => {
const oldRegion = Regions.cell(1, 2);
const newRegion = Regions.cell(9, 10);
const result = Regions.expandRegion(oldRegion, newRegion);
expect(result).to.deep.equal(Regions.cell(1, 2, 9, 10));
});
it("expands a FULL_TABLE region", () => {
const oldRegion = Regions.table();
const newRegion = Regions.table();
const result = Regions.expandRegion(oldRegion, newRegion);
expect(result).to.deep.equal(Regions.table());
});
});
});