import { describe, expect, test, vi } from 'vitest'
import { render, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { ListDataUI } from './list-data'
describe('ListDataUI', () => {
const data = [
{ id: '1', label: 'Population', value: '8.4M' },
{ id: '2', label: 'Median Age', value: '32 years' },
{ id: '3', label: 'GDP', value: '$1.2T' },
{ id: '4', label: 'Life Expectancy', value: '78.5 years' },
{ id: '5', label: 'Population Density', value: '345/km²' },
]
test('renders correctly', () => {
const { container } = render()
const rows = within(container).getAllByRole('listitem')
expect(rows).toHaveLength(5)
})
test('renders skeleton when loading', () => {
const { getByLabelText } = render()
expect(getByLabelText('List Data skeleton')).toBeTruthy()
})
test('with maxItems', async () => {
const { container } = render()
const rows = within(container).getAllByRole('listitem')
expect(rows).toHaveLength(3)
const showMoreButton = within(container).getByText('Show More')
expect(showMoreButton).toBeTruthy()
await userEvent.click(showMoreButton)
const updatedRows = within(container).getAllByRole('listitem')
expect(updatedRows).toHaveLength(5)
const showLessButton = within(container).getByText('Show Less')
expect(showLessButton).toBeTruthy()
await userEvent.click(showLessButton)
const finalRows = within(container).getAllByRole('listitem')
expect(finalRows).toHaveLength(3)
})
test('with disabled items', () => {
const disabledData = data.map((item) => ({ ...item, disabled: true }))
const { container } = render()
const rows = within(container).getAllByRole('listitem')
for (const row of rows) {
const computedStyle = window.getComputedStyle(row)
expect(computedStyle.pointerEvents).toBe('none')
}
})
test('with custom expand button labels', async () => {
const { container } = render(
,
)
const moreButton = within(container).getByText('More')
expect(moreButton).toBeTruthy()
await userEvent.click(moreButton)
const lessButton = within(container).getByText('Less')
expect(lessButton).toBeTruthy()
})
test('with item click handler', async () => {
const mockOnItemClick = vi.fn()
const { container } = render(
,
)
const rows = within(container).getAllByRole('listitem')
for (const [idx, row] of rows.entries()) {
await userEvent.click(row)
const foundItem = data.find((item) => item.id === data[idx]?.id)
expect(mockOnItemClick).toHaveBeenCalledWith(foundItem)
mockOnItemClick.mockClear()
}
})
test('renders NoDataAlert when data is empty', () => {
const { container } = render()
// No
elements when there's no data
expect(within(container).queryAllByRole('listitem')).toHaveLength(0)
})
test('uses EMPTY_DATA default when data prop is omitted', () => {
// `data` is required by the type but has a runtime default of EMPTY_DATA.
// Pass undefined to exercise the default branch.
const { container } = render(
,
)
expect(within(container).queryAllByRole('listitem')).toHaveLength(0)
})
test('onExpand fires with the previous showAll value when toggled', async () => {
const onExpand = vi.fn()
const { container } = render(
,
)
const showMore = within(container).getByText('Show More')
await userEvent.click(showMore)
// Called with prior showAll value (false)
expect(onExpand).toHaveBeenCalledWith(false)
onExpand.mockClear()
const showLess = within(container).getByText('Show Less')
await userEvent.click(showLess)
// Now called with prior showAll value (true)
expect(onExpand).toHaveBeenCalledWith(true)
})
test('applies `selected` style to the row matching selectedItemId', () => {
const { container } = render()
const rows = within(container).getAllByRole('listitem')
expect(rows).toHaveLength(5)
// Smoke-check that the selected branch executed without throwing
})
test('respects showDivider=false (skips the divider style branch)', () => {
expect(() =>
render(),
).not.toThrow()
})
test('clicking a non-disabled sibling of a disabled item still fires onItemClick', async () => {
// disabled items get pointer-events: none via the disabled style branch,
// so userEvent can't synthesize a click on them anyway. We assert the
// happy-path sibling fires correctly to confirm the disabled-row
// rendering branch ran.
const onItemClick = vi.fn()
const mixedData = [
{ id: '1', label: 'A', value: '1', disabled: true },
{ id: '2', label: 'B', value: '2' },
]
const { container } = render(
,
)
const rows = within(container).getAllByRole('listitem')
await userEvent.click(rows[1]!)
expect(onItemClick).toHaveBeenCalledWith(mixedData[1])
})
test('threads tooltipProps overrides into SmartTooltip', () => {
expect(() =>
render(
,
),
).not.toThrow()
})
})