@amsterdam/design-system-react
Version:
All React components from the Amsterdam Design System. Use it to compose pages in your website or application.
212 lines (211 loc) • 14.8 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/
import { fireEvent, render, screen } from '@testing-library/react';
import { createRef } from 'react';
import { describe, expect, it, vi } from 'vitest';
import { ProgressList } from './ProgressList';
describe('ProgressListStep', () => {
it('renders', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toBeInTheDocument();
expect(step).toBeVisible();
});
it('renders a design system BEM class name', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveClass('ams-progress-list__step');
});
it('renders the marker', () => {
const { container } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const marker = container.querySelector('.ams-progress-list__marker');
expect(marker).toBeInTheDocument();
});
it('renders the connector', () => {
const { container } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const connector = container.querySelector('.ams-progress-list__connector');
expect(connector).toBeInTheDocument();
});
it('renders the heading', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "My Step Heading", children: "Content" }) }));
const heading = screen.getByRole('heading', { level: 3, name: 'My Step Heading' });
expect(heading).toBeInTheDocument();
expect(heading).toHaveClass('ams-progress-list__heading');
});
it('renders an extra class name', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { className: "extra", heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveClass('ams-progress-list__step extra');
});
it('sets aria-current and current class when status is current', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "current", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveAttribute('aria-current', 'step');
expect(step).toHaveClass('ams-progress-list__step--current');
});
it('adds completed class and does not set aria-current when status is completed', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "completed", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveClass('ams-progress-list__step--completed');
expect(step).not.toHaveAttribute('aria-current');
});
it('adds has-substeps class when hasSubsteps is true', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { hasSubsteps: true, heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveClass('ams-progress-list__step--has-substeps');
});
it('does not add has-substeps modifier when hasSubsteps is not set', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).not.toHaveClass('ams-progress-list__step--has-substeps');
});
it('renders children inside the panel', () => {
const { container, getByText } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: _jsx("span", { children: "Inner child" }) }) }));
const panel = container.querySelector('.ams-progress-list__panel');
expect(panel).toBeInTheDocument();
expect(panel).toContainElement(getByText('Inner child'));
});
it('renders the arrow icon only when status is current', () => {
const { container: currentContainer } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Current", status: "current", children: "Content" }) }));
const currentMarkerShape = currentContainer.querySelector('.ams-progress-list__marker-shape');
expect(currentMarkerShape?.querySelector('svg')).toBeInTheDocument();
const { container: nonCurrentContainer } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Not current", children: "Content" }) }));
const nonCurrentMarkerShape = nonCurrentContainer.querySelector('.ams-progress-list__marker-shape');
expect(nonCurrentMarkerShape?.querySelector('svg')).not.toBeInTheDocument();
});
it('renders substep markers inside the panel', () => {
const { container } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { hasSubsteps: true, heading: "Test Step", status: "current", children: _jsxs(ProgressList.Substeps, { children: [_jsx(ProgressList.Substep, { children: "Substep one" }), _jsx(ProgressList.Substep, { children: "Substep two" })] }) }) }));
const step = container.querySelector('.ams-progress-list__step');
const substepMarkers = container.querySelectorAll('.ams-progress-list-substeps__marker');
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
expect(substepMarkers).toHaveLength(2);
substepMarkers.forEach((marker) => expect(marker).toBeInTheDocument());
});
it('supports ForwardRef in React', () => {
const ref = createRef();
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", ref: ref, children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(ref.current).toBe(step);
});
it('does not render a button by default', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
expect(screen.queryByRole('button')).not.toBeInTheDocument();
expect(screen.getByRole('heading', { level: 3, name: 'Test Step' })).toBeInTheDocument();
});
it('does not render the chevron icon by default', () => {
const { container } = render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const heading = screen.getByRole('heading', { name: /Test Step/i });
expect(heading).toBeInTheDocument();
const icon = container.querySelector('.ams-progress-list__heading .ams-progress-list__icon');
expect(icon).not.toBeInTheDocument();
});
it('always shows content by default, even for completed steps', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "completed", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
});
it('ignores defaultCollapsed by default', () => {
render(_jsx(ProgressList, { headingLevel: 3, children: _jsx(ProgressList.Step, { defaultCollapsed: true, heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
});
it('passes additional props', () => {
render(_jsx(ProgressList.Step, { "aria-hidden": "false", "data-test": "data-test", heading: "test step", id: "id" }));
const component = screen.getByRole('listitem');
expect(component).toHaveAttribute('aria-hidden', 'false');
expect(component).toHaveAttribute('id', 'id');
expect(component).toHaveAttribute('data-test', 'data-test');
});
describe('when collapsible', () => {
it('renders a button inside the heading', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const button = screen.getByRole('button', { name: /Test Step/ });
expect(button).toBeInTheDocument();
expect(button).toHaveClass('ams-progress-list__button');
});
it('renders the chevron icon inside the button', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const button = screen.getByRole('button', { name: /Test Step/ });
const icon = button.querySelector('svg');
expect(icon).toBeInTheDocument();
});
it('adds the collapsed class when the step is collapsed', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "completed", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveClass('ams-progress-list__step--collapsed');
});
it('does not add the collapsed class when the step is expanded', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
});
it('does not collapse when status is current', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "current", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
});
it('expands a completed step when defaultCollapsed is false', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { defaultCollapsed: false, heading: "Test Step", status: "completed", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
});
it('collapses when defaultCollapsed is true', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { defaultCollapsed: true, heading: "Test Step", children: "Content" }) }));
const step = screen.getByRole('listitem');
expect(step).toHaveClass('ams-progress-list__step--collapsed');
});
it('removes the collapsed class when the button is clicked', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "completed", children: "Content" }) }));
const step = screen.getByRole('listitem');
const button = screen.getByRole('button', { name: /Test Step/ });
expect(step).toHaveClass('ams-progress-list__step--collapsed');
fireEvent.click(button);
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
});
it('toggles collapsed state when the button is clicked', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "completed", children: "Content" }) }));
const step = screen.getByRole('listitem');
const button = screen.getByRole('button', { name: /Test Step/ });
expect(step).toHaveClass('ams-progress-list__step--collapsed');
fireEvent.click(button);
expect(step).not.toHaveClass('ams-progress-list__step--collapsed');
fireEvent.click(button);
expect(step).toHaveClass('ams-progress-list__step--collapsed');
});
it('sets aria-expanded on the button', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", status: "completed", children: "Content" }) }));
const button = screen.getByRole('button', { name: /Test Step/ });
expect(button).toHaveAttribute('aria-expanded', 'false');
fireEvent.click(button);
expect(button).toHaveAttribute('aria-expanded', 'true');
});
it('links the button to the panel via aria-controls', () => {
const { container } = render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const button = screen.getByRole('button', { name: /Test Step/ });
const panelId = button.getAttribute('aria-controls');
const panel = container.querySelector(`[id="${panelId}"]`);
expect(panelId).toBeTruthy();
expect(panel).toBeInTheDocument();
expect(panel).toHaveClass('ams-progress-list__panel');
});
it('calls onToggle with the new expanded state when the button is clicked', () => {
const onToggle = vi.fn();
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", onToggle: onToggle, status: "completed", children: "Content" }) }));
const button = screen.getByRole('button', { name: /Test Step/ });
fireEvent.click(button);
expect(onToggle).toHaveBeenCalledTimes(1);
expect(onToggle).toHaveBeenCalledWith(true);
fireEvent.click(button);
expect(onToggle).toHaveBeenCalledTimes(2);
expect(onToggle).toHaveBeenLastCalledWith(false);
});
it('does not throw when onToggle is not provided', () => {
render(_jsx(ProgressList, { collapsible: true, headingLevel: 3, children: _jsx(ProgressList.Step, { heading: "Test Step", children: "Content" }) }));
const button = screen.getByRole('button', { name: /Test Step/ });
expect(() => fireEvent.click(button)).not.toThrow();
});
});
});