import * as React from "react"; import { render } from "react-dom"; import { VirtualList, ALIGNMENT } from "../src/"; // import { ALIGNMENT } from "../src/components/constants"; const HEIGHT = 100; const ITEM_HEIGHT = 10; interface ItemAttributes { index: number; style: React.CSSProperties; className?: string; } describe("VirtualList", () => { let node: HTMLDivElement; function renderItem({ index, style, ...props }: ItemAttributes) { return (
Item #{index}
); } function getComponent(props = {}) { return ( ); } beforeEach(() => { node = document.createElement("div"); }); describe("number of rendered children", () => { it("renders enough children to fill the view", () => { render(getComponent(), node); expect(node.querySelectorAll(".item")).toHaveLength(HEIGHT / ITEM_HEIGHT); }); it("does not render more children than available if the list is not filled", () => { render(getComponent({ itemCount: 5 }), node); expect(node.querySelectorAll(".item")).toHaveLength(5); }); it("handles dynamically updating the number of items", () => { for (let itemCount = 0; itemCount < 5; itemCount++) { render(getComponent({ itemCount }), node); expect(node.querySelectorAll(".item")).toHaveLength(itemCount); } }); describe("stickyIndices", () => { const stickyIndices = [0, 10, 20, 30, 50]; function itemRenderer({ index, style }: { index: number; style: any }) { return renderItem({ index, style, className: stickyIndices.includes(index) ? "item sticky" : "item" }); } it("renders all sticky indices when scrollTop is zero", () => { render( getComponent({ itemCount: 100, stickyIndices, renderItem: itemRenderer }), node ); expect(node.querySelectorAll(".sticky")).toHaveLength( stickyIndices.length ); }); it("keeps sticky indices rendered when scrolling", () => { render( getComponent({ itemCount: 100, stickyIndices, renderItem: itemRenderer, scrollOffset: 500 }), node ); expect(node.querySelectorAll(".sticky")).toHaveLength( stickyIndices.length ); }); }); }); /** Test scrolling via initial props */ describe("scrollToIndex", () => { it("scrolls to the top", () => { render(getComponent({ scrollToIndex: 0 }), node); expect(node.textContent).toContain("Item #0"); }); it("scrolls down to the middle", () => { render(getComponent({ scrollToIndex: 49 }), node); expect(node.textContent).toContain("Item #49"); }); it("scrolls to the bottom", () => { render(getComponent({ scrollToIndex: 99 }), node); expect(node.textContent).toContain("Item #99"); }); it('scrolls to the correct position for :scrollToAlignment "start"', () => { render( getComponent({ scrollToAlignment: "start", scrollToIndex: 49 }), node ); // 100 items * 10 item height = 1,000 total item height; 10 items can be visible at a time. expect(node.textContent).toContain("Item #49"); expect(node.textContent).toContain("Item #58"); }); it('scrolls to the correct position for :scrollToAlignment "end"', () => { // render( // getComponent({ // scrollToIndex: 99 // }), // node // ); render( getComponent({ scrollToAlignment: ALIGNMENT.END, scrollToIndex: 49 }), node ); // 100 items * 10 item height = 1,000 total item height; 10 items can be visible at a time. expect(node.textContent).toContain("Item #40"); expect(node.textContent).toContain("Item #49"); }); it('scrolls to the correct position for :scrollToAlignment "center"', () => { // render( // getComponent({ // scrollToIndex: 99 // }), // node // ); // render( // getComponent({ // scrollToAlignment: "center", // scrollToIndex: 49 // }), // node // ); render( getComponent({ scrollToAlignment: ALIGNMENT.CENTER, scrollToIndex: 49 }), node ); // 100 items * 10 item height = 1,000 total item height; 11 items can be visible at a time (the first and last item are only partially visible) expect(node.textContent).toContain("Item #44"); expect(node.textContent).toContain("Item #54"); }); }); describe("property updates", () => { it("updates :scrollToIndex position when :itemSize changes", () => { render(getComponent({ scrollToIndex: 50 }), node); expect(node.textContent).toContain("Item #50"); // Making rows taller pushes name off/beyond the scrolled area // render(getComponent({ scrollToIndex: 50, itemSize: 20 }), node); // expect(node.textContent).toContain("Item #50"); }); it("updates :scrollToIndex position when :itemSize changes", () => { // Making rows taller pushes name off/beyond the scrolled area render(getComponent({ scrollToIndex: 50, itemSize: 20 }), node); expect(node.textContent).toContain("Item #50"); }); it("updates :scrollToIndex position when :height changes", () => { render(getComponent({ scrollToIndex: 50 }), node); expect(node.textContent).toContain("Item #50"); // Making the list shorter leaves only room for 1 item render(getComponent({ scrollToIndex: 50, height: 20 }), node); expect(node.textContent).toContain("Item #50"); }); it("updates :scrollToIndex position when :scrollToIndex changes", () => { render(getComponent(), node); expect(node.textContent).not.toContain("Item #50"); // render(getComponent({ scrollToIndex: 50 }), node); // expect(node.textContent).toContain("Item #50"); }); it("updates :scrollToIndex position when :scrollToIndex changes", () => { // render(getComponent(), node); // expect(node.textContent).not.toContain("Item #50"); render(getComponent({ scrollToIndex: 50 }), node); expect(node.textContent).toContain("Item #50"); }); it("updates scroll position if size shrinks smaller than the current scroll", () => { render(getComponent({ scrollToIndex: 500 }), node); render(getComponent({ scrollToIndex: 500, itemCount: 10 }), node); expect(node.textContent).toContain("Item #9"); }); }); describe(":scrollOffset property", () => { it("renders correctly when an initial :scrollOffset property is specified", () => { render( getComponent({ scrollOffset: 100 }), node ); const items = node.querySelectorAll(".item"); const first = items[0]; const last = items[items.length - 1]; expect(first.textContent).toContain("Item #10"); expect(last.textContent).toContain("Item #19"); }); it("renders correctly when an :scrollOffset property is specified after the component has initialized", () => { render(getComponent(), node); let items = node.querySelectorAll(".item"); let first = items[0]; let last = items[items.length - 1]; expect(first.textContent).toContain("Item #0"); expect(last.textContent).toContain("Item #9"); // render( // getComponent({ // scrollOffset: 100 // }), // node // ); // items = node.querySelectorAll(".item"); // first = items[0]; // last = items[items.length - 1]; // expect(first.textContent).toContain("Item #10"); // expect(last.textContent).toContain("Item #19"); }); it("renders correctly when an :scrollOffset property is specified after the component has initialized", () => { render( getComponent({ scrollOffset: 100 }), node ); const items = node.querySelectorAll(".item"); const first = items[0]; const last = items[items.length - 1]; expect(first.textContent).toContain("Item #10"); expect(last.textContent).toContain("Item #19"); }); }); });