/* * Copyright 2017 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 { mount, type ReactWrapper } from "enzyme"; import sinon from "sinon"; import { afterEach, beforeEach, describe, expect, it } from "@blueprintjs/test-commons/vitest"; import * as Classes from "../common/classes"; import * as Errors from "../common/errors"; import { Grid } from "../common/grid"; import { QuadrantType, TableQuadrant, type TableQuadrantProps } from "./tableQuadrant"; /** * is responsible for showing a single table "instance" of both * header and body cells. */ describe("TableQuadrant", () => { const NUM_ROWS = 5; const NUM_COLUMNS = 3; const ROW_HEIGHT = 10; const COLUMN_WIDTH = 100; const ROW_HEIGHTS = Array(NUM_ROWS).fill(ROW_HEIGHT); const COLUMN_WIDTHS = Array(NUM_COLUMNS).fill(COLUMN_WIDTH); let grid: Grid; const bodyRenderer = sinon.spy(); beforeEach(() => { grid = new Grid(ROW_HEIGHTS, COLUMN_WIDTHS); }); afterEach(() => { bodyRenderer.resetHistory(); }); describe("Event callbacks", () => { it("adds onScroll to the TABLE_QUADRANT_SCROLL_CONTAINER", () => { const onScroll = sinon.spy(); const component = mountTableQuadrant({ onScroll }); component.find(`.${Classes.TABLE_QUADRANT_SCROLL_CONTAINER}`).simulate("scroll"); expect(onScroll.called).to.be.true; }); it("adds onWheel to the TABLE_QUADRANT_SCROLL_CONTAINER", () => { const onWheel = sinon.spy(); const component = mountTableQuadrant({ onWheel }); component.find(`.${Classes.TABLE_QUADRANT_SCROLL_CONTAINER}`).simulate("wheel"); expect(onWheel.called).to.be.true; }); it("prints a console warning if onScroll is provided when quadrantType != MAIN", () => { const consoleWarn = sinon.stub(console, "warn"); mountTableQuadrant({ onScroll: sinon.spy(), quadrantType: QuadrantType.LEFT }); expect(consoleWarn.calledOnce); expect(consoleWarn.firstCall.args[0]).to.equal(Errors.QUADRANT_ON_SCROLL_UNNECESSARILY_DEFINED); consoleWarn.restore(); }); }); describe("refs", () => { it("bodyRef returns TABLE_QUADRANT_BODY_CONTAINER element", () => { runTest("bodyRef", Classes.TABLE_QUADRANT_BODY_CONTAINER); }); it("quadrantRef returns top-level TABLE_QUADRANT element", () => { runTest("quadrantRef", Classes.TABLE_QUADRANT); }); it("scrollContainerRef returns TABLE_QUADRANT_SCROLL_CONTAINER element", () => { runTest("scrollContainerRef", Classes.TABLE_QUADRANT_SCROLL_CONTAINER); }); function runTest(propKey: "bodyRef" | "quadrantRef" | "scrollContainerRef", expectedClassName: string) { const refHandler = sinon.spy(); mountTableQuadrant({ [propKey]: refHandler }); expect(refHandler.calledOnce).to.be.true; const ref = refHandler.firstCall.args[0] as HTMLElement; expect(ref.classList.contains(expectedClassName)).to.be.true; } }); describe("style", () => { it("applies custom props.style to the top-level element", () => { // need to use `rgb()` syntax, because colors are expressed that way // in rendered style object const style = { background: "rgb(1, 2, 3)", color: "rgb(4, 5, 6)" }; const component = mountTableQuadrant({ style }); const renderedStyle = component.getDOMNode().style; expect(renderedStyle.background).to.deep.equal(style.background); expect(renderedStyle.color).to.deep.equal(style.color); }); }); /** * knows which portions of the body should be rendered based on the quadrantType, * and it passes those opinions to the bodyRenderer() callback via flags. */ describe("bodyRenderer", () => { it("invokes with proper params for QuarantType.MAIN", () => { runTest(QuadrantType.MAIN, [QuadrantType.MAIN, false, false]); }); it("invokes with proper params for QuarantType.TOP", () => { runTest(QuadrantType.TOP, [QuadrantType.TOP, true, false]); }); it("invokes with proper params for QuarantType.LEFT", () => { runTest(QuadrantType.LEFT, [QuadrantType.LEFT, false, true]); }); it("invokes with proper params for QuarantType.TOP_LEFT", () => { runTest(QuadrantType.TOP_LEFT, [QuadrantType.TOP_LEFT, true, true]); }); function runTest(quadrantType: QuadrantType, expectedArgs: any[]) { mountTableQuadrant({ quadrantType }); expect(bodyRenderer.calledOnce).to.be.true; expect(bodyRenderer.firstCall.args).to.deep.equal(expectedArgs); } }); describe("Render logic", () => { describe("Menu", () => { const MENU_CLASS = "foo"; it("renders menu if menuRenderer provided", () => { const menuRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ menuRenderer }); expect(menuRenderer.called).to.be.true; expect(component.find(`.${Classes.TABLE_TOP_CONTAINER} > .${MENU_CLASS}`)).to.have.lengthOf(1); }); it("does not render menu if menuRenderer not provided", () => { const component = mountTableQuadrant(); expect(component.find(`.${Classes.TABLE_TOP_CONTAINER}`).children()).to.be.empty; }); it("does not render menu if enableRowHeader=false", () => { const menuRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ enableRowHeader: false, menuRenderer }); expect(menuRenderer.called).to.be.false; expect(component.find(`.${Classes.TABLE_TOP_CONTAINER}`).children()).to.be.empty; }); }); describe("Row header", () => { const ROW_HEADER_CLASS = "foo"; it("renders row header if rowHeaderCellRenderer provided", () => { const rowHeaderCellRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ rowHeaderCellRenderer }); expect(rowHeaderCellRenderer.called).to.be.true; expect(component.find(`.${Classes.TABLE_BOTTOM_CONTAINER} > .${ROW_HEADER_CLASS}`)).to.have.lengthOf(1); }); it("does not render row header if rowHeaderCellRenderer not provided", () => { const component = mountTableQuadrant(); // just the body should exist expect(component.find(`.${Classes.TABLE_BOTTOM_CONTAINER}`).children()).to.have.lengthOf(1); }); it("does not render row header if enableRowHeader=false", () => { const rowHeaderCellRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ enableRowHeader: false, rowHeaderCellRenderer, }); expect(rowHeaderCellRenderer.called).to.be.false; expect(component.find(`.${Classes.TABLE_BOTTOM_CONTAINER}`).children()).to.have.lengthOf(1); }); }); describe("Column header", () => { const COLUMN_HEADER_CLASS = "foo"; it("renders column header if columnHeaderCellRenderer provided", () => { const columnHeaderCellRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ columnHeaderCellRenderer }); expect(columnHeaderCellRenderer.called).to.be.true; expect(component.find(`.${Classes.TABLE_TOP_CONTAINER} > .${COLUMN_HEADER_CLASS}`)).to.have.lengthOf(1); }); it("does not render column header if columnHeaderCellRenderer not provided", () => { const component = mountTableQuadrant(); expect(component.find(`.${Classes.TABLE_TOP_CONTAINER}`).children()).to.be.empty; }); it("still renders column header if enableRowHeader=false", () => { const columnHeaderCellRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ columnHeaderCellRenderer, enableRowHeader: false, }); expect(columnHeaderCellRenderer.called).to.be.true; expect(component.find(`.${Classes.TABLE_TOP_CONTAINER} > .${COLUMN_HEADER_CLASS}`)).to.have.lengthOf(1); }); it("does not render column header if enableColumnHeader=false", () => { const columnHeaderCellRenderer = sinon.stub().returns(
); const component = mountTableQuadrant({ columnHeaderCellRenderer, enableColumnHeader: false, }); expect(component.find(`.${Classes.TABLE_TOP_CONTAINER}`).children()).to.be.empty; }); }); }); describe("CSS classes", () => { it("renders outermost element with TABLE_QUADRANT_MAIN class if quadrantType=MAIN", () => { runTest(QuadrantType.MAIN, Classes.TABLE_QUADRANT_MAIN); }); it("renders outermost element with TABLE_QUADRANT_TOP class if quadrantType=TOP", () => { runTest(QuadrantType.TOP, Classes.TABLE_QUADRANT_TOP); }); it("renders outermost element with TABLE_QUADRANT_LEFT class if quadrantType=LEFT", () => { runTest(QuadrantType.LEFT, Classes.TABLE_QUADRANT_LEFT); }); it("renders outermost element with TABLE_QUADRANT_TOP_LEFT class if quadrantType=TOP_LEFT", () => { runTest(QuadrantType.TOP_LEFT, Classes.TABLE_QUADRANT_TOP_LEFT); }); it("renders outermost element with no custom class if quadrantType not provided", () => { const component = mountTableQuadrant(); const element = getDomNode(component); expect(element.classList.toString()).to.equal(Classes.TABLE_QUADRANT); }); it("applies custom props.className to outermost element", () => { const CUSTOM_CLASS = "foo"; const component = mountTableQuadrant({ className: CUSTOM_CLASS }); const element = getDomNode(component); expect(element.classList.contains(CUSTOM_CLASS)).to.be.true; }); function runTest(quadrantType: QuadrantType, expectedCssClass: string) { const component = mountTableQuadrant({ quadrantType }); const element = getDomNode(component); expect(element.classList.contains(expectedCssClass)).to.be.true; } }); function getDomNode(component: ReactWrapper) { return component.getDOMNode(); } function mountTableQuadrant(props: Partial = {}) { return mount(); } });