import { describe, expect, it } from 'vitest'; import { ScrollContainer, Spacer, Theme, VStack } from '../../src/ui'; /** Row tops after a layout; an unlaid-out stack leaves every child at 0. */ const tops = (stack: VStack) => stack.children.map((c) => c.y); describe('a scroll container told its content changed', () => { it('lays out rows that leave the height pinned to the viewport', () => { const scroll = new ScrollContainer(); scroll.resize(200, 300); const rows = new VStack(); // The list tab hands over the stack before filling it, so the empty content is measured first // and pinned to the viewport height. scroll.setContent(rows); rows.add(new Spacer(20)); rows.add(new Spacer(20)); rows.add(new Spacer(20)); // 60 < 300, so the content keeps the height it already had: `resize` alone would see no // change and skip the layout, leaving all three rows stacked at the top. scroll.relayoutContent(); expect(tops(rows)).toEqual([0, 20, 40]); }); it('still lays out rows that make the content taller than the viewport', () => { const scroll = new ScrollContainer(); scroll.resize(200, 50); const rows = new VStack(); scroll.setContent(rows); rows.add(new Spacer(20)); rows.add(new Spacer(20)); rows.add(new Spacer(20)); scroll.relayoutContent(); expect(tops(rows)).toEqual([0, 20, 40]); expect(scroll.maxScroll).toBe(10); }); }); describe('a scroll container and its scrollbar', () => { const filled = (viewportH: number, rowCount: number) => { const scroll = new ScrollContainer(); scroll.resize(200, viewportH); const rows = new VStack(); for (let i = 0; i < rowCount; i++) rows.add(new Spacer(20)); scroll.setContent(rows); return { scroll, rows }; }; it('gives content that fits the full width', () => { expect(filled(300, 3).rows.w).toBe(200); }); it('keeps the gutter free for the bar while the content scrolls', () => { expect(filled(50, 3).rows.w).toBe(200 - Theme.scrollbarWidth); }); it('hands the gutter back once the content fits again', () => { const { scroll, rows } = filled(50, 3); scroll.resize(200, 300); expect(rows.w).toBe(200); }); });