diff --git a/web/src/lib/components/HorizontalScrollRow.svelte b/web/src/lib/components/HorizontalScrollRow.svelte new file mode 100644 index 00000000..fe953700 --- /dev/null +++ b/web/src/lib/components/HorizontalScrollRow.svelte @@ -0,0 +1,106 @@ + + +
+ + +
+ {#each items as it, i (i)} + {@render item?.(it, i)} + {/each} +
+ + +
+ + diff --git a/web/src/lib/components/HorizontalScrollRow.test.ts b/web/src/lib/components/HorizontalScrollRow.test.ts new file mode 100644 index 00000000..55ee2e9d --- /dev/null +++ b/web/src/lib/components/HorizontalScrollRow.test.ts @@ -0,0 +1,32 @@ +import { afterEach, describe, expect, test, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/svelte'; +import HorizontalScrollRow from './HorizontalScrollRow.svelte'; + +afterEach(() => vi.clearAllMocks()); + +describe('HorizontalScrollRow', () => { + test('renders left and right scroll buttons', () => { + render(HorizontalScrollRow, { props: { items: ['a', 'b', 'c'] } }); + expect(screen.getByRole('button', { name: /scroll left/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /scroll right/i })).toBeInTheDocument(); + }); + + test('left button starts disabled (scrollLeft is 0)', () => { + render(HorizontalScrollRow, { props: { items: ['a'] } }); + expect(screen.getByRole('button', { name: /scroll left/i })).toBeDisabled(); + }); + + test('clicking right button calls scrollBy on the container', async () => { + const { container } = render(HorizontalScrollRow, { props: { items: ['a', 'b'] } }); + const scroller = container.querySelector('[data-testid="scroll-container"]') as HTMLElement; + const spy = vi.fn(); + scroller.scrollBy = spy as unknown as HTMLElement['scrollBy']; + Object.defineProperty(scroller, 'clientWidth', { value: 800, configurable: true }); + Object.defineProperty(scroller, 'scrollWidth', { value: 2000, configurable: true }); + Object.defineProperty(scroller, 'scrollLeft', { value: 0, configurable: true, writable: true }); + + await fireEvent.click(screen.getByRole('button', { name: /scroll right/i })); + expect(spy).toHaveBeenCalledWith({ left: expect.any(Number), behavior: 'smooth' }); + expect(spy.mock.calls[0][0].left).toBeGreaterThan(0); + }); +});