// @vitest-environment jsdom
/**
* What the InlineChip page claims, pinned: without `href` it is a label, with
* one it is a link a router can take; every tone keeps the shape and its own
* size and colour through `cn()`; the icon sits in its 16px box; and the class
* function a string-only renderer uses gives the same classes as the component.
*/
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { INLINE_CHIP_CLASSES, INLINE_CHIP_TONE_CLASSES, InlineChip, inlineChipClassName } from './InlineChip'
afterEach(cleanup)
const classesOf = (el: Element) => el.getAttribute('class')?.split(' ').filter(Boolean) ?? []
describe('InlineChip', () => {
it('without href it is a label, not a link', () => {
render(Label)
const chip = screen.getByText('Label')
expect(chip.tagName).toBe('SPAN')
expect(screen.queryByRole('link')).toBeNull()
})
it('with href it is a link, and a router app takes the click with the href intact', async () => {
const onClick = vi.fn((event: { preventDefault: () => void }) => event.preventDefault())
render(
Label
,
)
const link = screen.getByRole('link', { name: 'Label' })
await userEvent.click(link)
expect(onClick).toHaveBeenCalledTimes(1)
expect(link.getAttribute('href')).toBe('/somewhere')
})
it('is one line high and pinned to the top of the line, in every tone', () => {
for (const tone of Object.keys(INLINE_CHIP_TONE_CLASSES) as (keyof typeof INLINE_CHIP_TONE_CLASSES)[]) {
const classes = inlineChipClassName(tone).split(' ')
expect(classes).toContain('align-top')
expect(classes).toContain('mx-0.5')
// One height only: 1.4em of the body size, or the same 19.6px written out where the text is smaller.
const heights = classes.filter((c) => c.startsWith('h-'))
expect(heights).toEqual([tone === 'quiet' ? 'h-[19.6px]' : 'h-[1.4em]'])
}
})
it('each tone keeps its size and its colour through cn()', () => {
const neutral = inlineChipClassName('neutral').split(' ')
expect(neutral).toContain('text-body-2')
expect(neutral).toContain('text-text-primary')
const person = inlineChipClassName('person').split(' ')
expect(person).toContain('text-body-2')
expect(person).toContain('text-accent-text')
const urgent = inlineChipClassName('urgent').split(' ')
expect(urgent).toContain('text-warning-default')
// quiet is smaller: its caption size replaces the body size, and its colour survives beside it
const quiet = inlineChipClassName('quiet').split(' ')
expect(quiet).toContain('text-caption')
expect(quiet).not.toContain('text-body-2')
expect(quiet).toContain('text-text-muted')
})
it('the component and the class function give the same classes', () => {
render(
Label
,
)
expect(classesOf(screen.getByText('Label'))).toEqual(inlineChipClassName('person', 'max-w-[24ch]').split(' '))
expect(INLINE_CHIP_CLASSES.split(' ').every((c) => classesOf(screen.getByText('Label')).includes(c))).toBe(true)
})
it('draws the icon first, in its 16px box', () => {
render(}>Label)
const box = screen.getByTestId('icon').parentElement!
expect(classesOf(box)).toEqual(expect.arrayContaining(['size-4', 'shrink-0']))
expect(box.parentElement!.firstElementChild).toBe(box)
})
})