Skip to content

Commit

Permalink
initial scrolling tests
Browse files Browse the repository at this point in the history
  • Loading branch information
heswell committed Jan 24, 2024
1 parent e0633ee commit cc64472
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 48 deletions.
1 change: 1 addition & 0 deletions vuu-ui/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default defineConfig({
viewportHeight: 1024,
video: false,
component: {
scrollBehavior: false,
setupNodeEvents(on, config) {
// installCoverageTask(on, config);
//Setting up a log task to allow logging to the console during an axe test because console.log() does not work directly in a test
Expand Down
14 changes: 1 addition & 13 deletions vuu-ui/packages/vuu-table/src/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,11 @@ import {
RowSelected,
} from "@finos/vuu-utils";
import cx from "clsx";
import { CSSProperties, memo, MouseEvent, useCallback, useEffect } from "react";
import { CSSProperties, memo, MouseEvent, useCallback } from "react";
import { TableCell, TableGroupCell } from "./table-cell";

import "./Row.css";

// const cellStyle = { background: "green", width: 150 };
// const MyCell = () => {
// useEffect(() => {
// console.log("MyCell mounted");
// return () => {
// console.log("MyCell unmounted");
// };
// }, []);

// return <div className="vuuTableCell" style={cellStyle} />;
// };

export interface RowProps {
className?: string;
columnMap: ColumnMap;
Expand Down
27 changes: 25 additions & 2 deletions vuu-ui/packages/vuu-table/src/__tests__/__component__/Table.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
import React from "react";
// TODO try and get TS path alias working to avoid relative paths like this
import { Instruments } from "../../../../../showcase/src/examples/Table/SIMUL.examples";
import { SimulTable } from "../../../../../showcase/src/examples/Table/SIMUL.examples";
import { TestTable } from "../../../../../showcase/src/examples/Table/Table.examples";
import { assertRenderedRows } from "./table-test-utils";

const withAriaIndex = (index: number) => ({
name: (_: string, el: Element) => el.ariaRowIndex === `${index}`,
});

describe("WHEN it initially renders", () => {
const RENDER_BUFFER = 5;
const ROW_COUNT = 1000;
const tableConfig = {
renderBufferSize: RENDER_BUFFER,
headerHeight: 25,
height: 625,
rowCount: ROW_COUNT,
rowHeight: 20,
width: 1000,
};

it("THEN expected classname is present", () => {
cy.mount(
<Instruments
<SimulTable
data-testid="table"
renderBufferSize={5}
height={625}
tableName="instruments"
width={800}
/>
);
const container = cy.findByTestId("table");
container.should("have.class", "vuuTable");
});

it("THEN expected number of rows are present, with buffered rows, all with correct aria index", () => {
cy.mount(<TestTable {...tableConfig} />);
assertRenderedRows({ from: 0, to: 30 }, RENDER_BUFFER, ROW_COUNT);
});
});
Original file line number Diff line number Diff line change
@@ -1,31 +1,184 @@
import React from "react";
// TODO try and get TS path alias working to avoid relative paths like this
import { TestTable } from "../../../../../showcase/src/examples/Table/Table.examples";
import { assertRenderedRows, withAriaIndex } from "./table-test-utils";

const withAriaIndex = (index: number) => ({
name: (_: string, el: HTMLElement) => el.ariaRowIndex === `${index}`,
});
describe("Table scrolling and keyboard navigation", () => {
const RENDER_BUFFER = 5;
const ROW_COUNT = 1000;
const tableConfig = {
renderBufferSize: RENDER_BUFFER,
headerHeight: 25,
height: 625,
rowCount: ROW_COUNT,
rowHeight: 20,
width: 1000,
};
describe("Page Keys", () => {
describe("WHEN first cell is focussed and page down pressed", () => {
it("THEN table scrolls down and next page of rows are rendered, first cell of new page is focussed", () => {
cy.mount(<TestTable {...tableConfig} />);

// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 1" }).click();
cy.findByRole("cell", { name: "row 1" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 1" }).should("be.focused");
cy.realPress("PageDown");

cy.findByRole("row", withAriaIndex(25)).should("not.exist");
cy.findByRole("row", withAriaIndex(26)).should("exist");

cy.get(".vuuTable-contentContainer")
.then((el) => el[0].scrollTop)
.should("equal", 600);

// row 31 should be top row in viewport
cy.findByRole("row", withAriaIndex(31)).should(
"have.css",
"transform",
"matrix(1, 0, 0, 1, 0, 600)"
);

cy.findByRole("cell", { name: "row 31" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 31" }).should("be.focused");
});

describe("AND WHEN page up is then pressed", () => {
it("THEN table is back to original state, and first cell is once again focussed", () => {
cy.mount(<TestTable {...tableConfig} />);

// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 1" }).click();

cy.realPress("PageDown");
cy.wait(60);
cy.realPress("PageUp");

cy.findByRole("cell", { name: "row 1" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 1" }).should("be.focused");

assertRenderedRows({ from: 0, to: 30 }, RENDER_BUFFER, ROW_COUNT);
});
});
});
});

describe("Home / End Keys", () => {
describe("WHEN topmost rows are in viewport, first cell is focussed and Home key pressed ", () => {
it("THEN nothing changes", () => {
cy.mount(<TestTable {...tableConfig} />);
// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 1" }).click();
cy.realPress("Home");
cy.findByRole("cell", { name: "row 1" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 1" }).should("be.focused");
assertRenderedRows({ from: 0, to: 30 }, RENDER_BUFFER, ROW_COUNT);
});
});
describe("WHEN topmost rows are in viewport, cell in middle of viewport is focussed and Home key pressed ", () => {
it("THEN no scrolling, but focus moves to first cell", () => {
cy.mount(<TestTable {...tableConfig} />);
// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 5" }).click();
cy.realPress("Home");
cy.findByRole("cell", { name: "row 1" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 1" }).should("be.focused");
assertRenderedRows({ from: 0, to: 30 }, RENDER_BUFFER, ROW_COUNT);
});
});

describe("WHEN topmost rows are in viewport, first cell is focussed and End key pressed ", () => {
it("THEN scrolls to end of data, last cell is focussed (same column)", () => {
cy.mount(<TestTable {...tableConfig} />);
// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 1" }).click();
cy.realPress("End");
cy.findByRole("cell", { name: "row 1,000" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 1,000" }).should("be.focused");
assertRenderedRows({ from: 970, to: 1000 }, RENDER_BUFFER, ROW_COUNT);
});
});

describe("WHEN topmost rows are in viewport, cell mid viewport focussed and End key pressed ", () => {
it("THEN scrolls to end of data, last cell is focussed (same column)", () => {
cy.mount(<TestTable {...tableConfig} />);
// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 10" }).click();
cy.realPress("End");
cy.findByRole("cell", { name: "row 1,000" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 1,000" }).should("be.focused");
assertRenderedRows({ from: 970, to: 1000 }, RENDER_BUFFER, ROW_COUNT);
});
});
});

describe("Arrow Up / Down Keys", () => {
describe("WHEN topmost rows are in viewport, first cell is focussed and Down Arrow key pressed ", () => {
it("THEN no scrolling, focus moved down to next cell", () => {
cy.mount(<TestTable {...tableConfig} />);
// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 1" }).click();
cy.realPress("ArrowDown");
cy.findByRole("cell", { name: "row 2" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 2" }).should("be.focused");
assertRenderedRows({ from: 0, to: 30 }, RENDER_BUFFER, ROW_COUNT);
});
});
describe("WHEN topmost rows are in viewport, first cell in last row is focussed and Down Arrow key pressed ", () => {
it("THEN scroll down by 1 row, cell in bottom row has focus", () => {
cy.mount(<TestTable {...tableConfig} />);
// interestingly, realClick doesn't work here
cy.findByRole("cell", { name: "row 30" }).click();
cy.realPress("ArrowDown");
cy.findByRole("cell", { name: "row 31" }).should(
"have.attr",
"tabindex",
"0"
);
cy.findByRole("cell", { name: "row 31" }).should("be.focused");
assertRenderedRows({ from: 1, to: 31 }, RENDER_BUFFER, ROW_COUNT);
});
});
});

describe("WHEN it initially renders", () => {
it("THEN expected number of rows are present, with buffered rows, all with correct aria index", () => {
cy.mount(
<TestTable
data-testid="table"
renderBufferSize={5}
headerHeight={25}
height={625}
rowHeight={20}
width={1000}
/>
);

// Note the Table Headers row is included in count
const container = cy.findAllByRole("row").should("have.length", 36);
cy.findByRole("row", withAriaIndex(0)).should("not.exist");
cy.findByRole("row", withAriaIndex(1)).should("be.visible");
cy.findByRole("row", withAriaIndex(30)).should("be.visible");
cy.findByRole("row", withAriaIndex(31)).should("not.be.visible");
cy.findByRole("row", withAriaIndex(35)).should("not.be.visible");
cy.findByRole("row", withAriaIndex(36)).should("not.exist");
describe("scrolling with Scrollbar", () => {
describe("WHEN scrolled down by a distance equating to 500 rows", () => {
it("THEN correct rows are within viewport", () => {
cy.mount(<TestTable {...tableConfig} />);
cy.get(".vuuTable-scrollbarContainer").scrollTo(0, 10000);
assertRenderedRows({ from: 500, to: 530 }, RENDER_BUFFER, ROW_COUNT);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { VuuRange } from "packages/vuu-protocol-types";

export const withAriaIndex = (index: number) => ({
name: (_: string, el: Element) => el.ariaRowIndex === `${index}`,
});

export const assertRenderedRows = (
{ from, to }: VuuRange,
renderBufferSize: number,
totalRowCount: number
) => {
const leadingBufferedRows = from < renderBufferSize ? from : renderBufferSize;
const offsetFromEnd = totalRowCount - to;
const trailingBufferedRows =
offsetFromEnd < renderBufferSize
? Math.min(0, offsetFromEnd)
: renderBufferSize;
const renderedRowCount =
to - from + leadingBufferedRows + trailingBufferedRows;

// Note the Table Headers row is included in count, hence the + 1
cy.findAllByRole("row").should("have.length", renderedRowCount + 1);

// we use the aria index for locators, which is 1 based
const firstRenderedRow = from - leadingBufferedRows + 1;
const firstVisibleRow = from + 1;
const lastVisibleRow = to;
const lastRenderedRow = to + trailingBufferedRows;

cy.findByRole("row", withAriaIndex(firstRenderedRow - 1)).should("not.exist");
cy.findByRole("row", withAriaIndex(firstVisibleRow)).should("be.visible");
cy.findByRole("row", withAriaIndex(lastVisibleRow)).should("be.visible");

if (trailingBufferedRows > 0) {
cy.findByRole("row", withAriaIndex(lastVisibleRow + 1)).should(
"not.be.visible"
);
cy.findByRole("row", withAriaIndex(lastRenderedRow)).should(
"not.be.visible"
);
}
cy.findByRole("row", withAriaIndex(lastRenderedRow + 1)).should("not.exist");
};
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-table/src/useDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const useDataSource = ({

const setRange = useCallback(
(range: VuuRange) => {
console.log(`set Range ${range.from} ${range.to}`);
if (!rangesAreSame(range, rangeRef.current)) {
const fullRange = getFullRange(range, renderBufferSize);
dataWindow.setRange(fullRange);
Expand Down
4 changes: 2 additions & 2 deletions vuu-ui/packages/vuu-table/src/useTableScroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export const useTableScroll = ({
const firstRow = getRowAtPosition(scrollTop);
if (firstRow !== firstRowRef.current) {
firstRowRef.current = firstRow;
setRange({ from: firstRow, to: firstRow + viewportRowCount + 1 });
setRange({ from: firstRow, to: firstRow + viewportRowCount });
}
},
[getRowAtPosition, onVerticalScroll, setRange, viewportRowCount]
Expand Down Expand Up @@ -368,7 +368,7 @@ export const useTableScroll = ({
onVerticalScrollInSitu?.(offset);
const firstRow = firstRowRef.current + offset;
firstRowRef.current = firstRow;
setRange({ from: firstRow, to: firstRow + viewportRowCount + 1 });
setRange({ from: firstRow, to: firstRow + viewportRowCount });
} else {
const scrollBy =
direction === "down" ? appliedPageSize : -appliedPageSize;
Expand Down
10 changes: 5 additions & 5 deletions vuu-ui/showcase/src/examples/Table/SIMUL.examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ const getDefaultColumnConfig = (
}
};

const SimulTable = ({
export const SimulTable = ({
getDefaultColumnConfig,
height = 625,
renderBufferSize = 0,
tableName,
tableName = "instruments",
...props
}: Partial<TableProps> & {
getDefaultColumnConfig?: DefaultColumnConfiguration;
Expand Down Expand Up @@ -96,6 +97,7 @@ const SimulTable = ({
<DemoTableContainer>
<Table
{...tableProps}
height={height}
onConfigChange={handleConfigChange}
renderBufferSize={renderBufferSize}
{...props}
Expand All @@ -104,9 +106,7 @@ const SimulTable = ({
</ContextMenuProvider>
);
};

export const Instruments = () => <SimulTable tableName="instruments" />;
Instruments.displaySequence = displaySequence++;
SimulTable.displaySequence = displaySequence++;

export const InstrumentsExtended = () => (
<SimulTable
Expand Down
2 changes: 1 addition & 1 deletion vuu-ui/showcase/src/examples/Table/Table.examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const TestTable = ({
headerHeight={headerHeight}
height={height}
renderBufferSize={renderBufferSize}
rowHeight={20}
rowHeight={rowHeight}
width={width}
/>
);
Expand Down

0 comments on commit cc64472

Please sign in to comment.