import { render } from '@testing-library/react' import { describe, expect, it } from 'vitest' import { axe } from 'vitest-axe' import { ScrollArea } from './scroll-area' function Body() { return ( <> {Array.from({ length: 40 }, (_, i) => (

Item {i + 1}

))} ) } describe('ScrollArea', () => { it('renders viewport + content + a single vertical scrollbar by default', () => { const { container } = render( , ) expect(container.querySelector('[data-slot="scroll-area"]')).not.toBeNull() expect(container.querySelector('[data-slot="scroll-area-viewport"]')).not.toBeNull() expect(container.querySelector('[data-slot="scroll-area-content"]')).not.toBeNull() expect(container.querySelector('[data-slot="scroll-area-corner"]')).toBeNull() }) it('pins content to the viewport width in the default vertical orientation', () => { const { container } = render( , ) const content = container.querySelector('[data-slot="scroll-area-content"]') as HTMLElement expect(content.style.minWidth).toBe('0px') }) it('keeps Base UI content sizing for horizontal orientations', () => { const { container } = render( , ) const content = container.querySelector('[data-slot="scroll-area-content"]') as HTMLElement expect(content.style.minWidth).toBe('fit-content') }) it('does not apply mask classes when scrollShadow is omitted', () => { const { container } = render( , ) const viewport = container.querySelector('[data-slot="scroll-area-viewport"]') as HTMLElement expect(viewport.className).not.toContain('mask-intersect') }) it('applies mask classes when scrollShadow is true', () => { const { container } = render( , ) const viewport = container.querySelector('[data-slot="scroll-area-viewport"]') as HTMLElement expect(viewport.className).toContain('mask-intersect') expect(viewport.className).toContain('mask-no-repeat') expect(viewport.className).toContain('mask-image:linear-gradient') }) it('omits hover-reveal classes when scrollbarVisibility defaults to "always"', () => { const { container } = render( , ) const scrollbar = container.querySelector('[data-slot="scroll-area-scrollbar"]') as HTMLElement | null // Scrollbar may not mount in jsdom; if it does, verify classes. if (scrollbar) { expect(scrollbar.getAttribute('data-visibility')).toBe('always') expect(scrollbar.className).not.toContain('opacity-0') } }) it('applies auto-reveal classes when scrollbarVisibility="auto"', () => { const { container } = render( , ) const scrollbar = container.querySelector('[data-slot="scroll-area-scrollbar"]') as HTMLElement | null if (scrollbar) { expect(scrollbar.getAttribute('data-visibility')).toBe('auto') expect(scrollbar.className).toContain('opacity-0') expect(scrollbar.className).toContain('transition-[width,height,opacity]') expect(scrollbar.className).toContain('motion-reduce:transition-none') expect(scrollbar.className).toContain('data-[hovering]:opacity-100') expect(scrollbar.className).toContain('data-[scrolling]:opacity-100') } }) it('does not render the scrollbar when scrollbarVisibility="never"', () => { const { container } = render( , ) expect(container.querySelector('[data-slot="scroll-area-scrollbar"]')).toBeNull() expect(container.querySelector('[data-slot="scroll-area-corner"]')).toBeNull() // Viewport stays scrollable - it's still in the DOM. expect(container.querySelector('[data-slot="scroll-area-viewport"]')).not.toBeNull() }) it('forwards className to the root element', () => { const { container } = render( , ) const root = container.querySelector('[data-slot="scroll-area"]') as HTMLElement expect(root.className).toContain('custom-area') expect(root.className).toContain('relative') }) it('has no accessibility violations', async () => { const { container } = render( , ) expect(await axe(container)).toHaveNoViolations() }) })