import React from 'react'; import { render, screen, userEvent, waitFor } from '../test-utils'; import SegmentedControl, { SegmentedControlProps } from './SegmentedControl'; const defaultSegments = [ { id: '1', value: 'accounting', label: 'Accounting', }, { id: '2', value: 'payroll', label: 'Payroll', }, { id: '3', value: 'reporting', label: 'Reporting', }, ]; const defaultSegmentsWithControls = defaultSegments.map((segment, index) => ({ ...segment, controls: `aControlId${index}`, })); const onChange = jest.fn(); const defaultProps: SegmentedControlProps = { name: 'segmentedControl', value: defaultSegments[0].value, mode: 'input', segments: defaultSegments, onChange, }; const renderSegmentedControl = (overrides: Partial = {}) => { return render(); }; describe('SegmentedControl', () => { it('dynamically adds inline style props for animated segment overlay', () => { renderSegmentedControl(); const segmentedControls = screen.getByTestId('segmented-control'); expect(segmentedControls).toHaveStyle( '--segment-highlight-width: 0px; --segment-highlight-x: 0px;', ); }); it('default value is selected', () => { renderSegmentedControl({ mode: 'view', segments: defaultSegmentsWithControls }); const accountingTab = screen.getByRole('tab', { name: 'Accounting' }); expect(accountingTab).toHaveAttribute('aria-selected', 'true'); }); it('lets the user pick through the segments when it is set to input', async () => { renderSegmentedControl(); const payroll = screen.getByRole('radio', { name: 'Payroll' }); await userEvent.click(payroll); expect(onChange).toHaveBeenCalledWith('payroll'); const reporting = screen.getByRole('radio', { name: 'Reporting' }); await userEvent.click(reporting); expect(onChange).toHaveBeenCalledWith('reporting'); }); it('lets the user pick through the segments when it is set to view', async () => { renderSegmentedControl({ mode: 'view', segments: defaultSegmentsWithControls }); const payroll = screen.getByRole('tab', { name: 'Payroll' }); await userEvent.click(payroll); expect(onChange).toHaveBeenCalledWith('payroll'); const reporting = screen.getByRole('tab', { name: 'Reporting' }); await userEvent.click(reporting); expect(onChange).toHaveBeenCalledWith('reporting'); }); it('does not call onChange on mount', async () => { let onChangeCallCount = 0; const ParentComponent = () => { const [_, simulateRerender] = React.useState({}); // new function is created on every render const onChange = () => { onChangeCallCount += 1; simulateRerender({}); }; return ; }; render(); await waitFor(() => { expect(onChangeCallCount).toBe(0); }); }); it('does not repeatedly call the onChange when the provided onChange prop changes on every render of its parent component', async () => { let onChangeCallCount = 0; const ParentComponent = () => { const [_, simulateRerender] = React.useState({}); // a new onChange function is created on every render const onChange = () => { onChangeCallCount += 1; simulateRerender({}); }; return ; }; const { rerender } = render(); rerender(); await waitFor(() => { expect(onChangeCallCount).toBe(0); }); }); it('updates the selected segment when the selectedValue prop changes', async () => { const { rerender } = render(); const payroll = screen.getByRole('radio', { name: 'Payroll' }); await userEvent.click(payroll); expect(onChange).toHaveBeenCalledWith('payroll'); rerender(); const reporting = screen.getByRole('radio', { name: 'Reporting' }); expect(reporting).toBeChecked(); }); it('updates the options when the segments prop changes', async () => { const { rerender } = render(); const newSegments = [ { id: '1', value: 'payroll', label: 'Payroll', }, { id: '3', value: 'anotherOne', label: 'Another One', }, ]; rerender(); const anotherOne = screen.getByRole('radio', { name: 'Another One' }); await userEvent.click(anotherOne); expect(onChange).toHaveBeenCalledWith('anotherOne'); }); it('throws error if user tries to add too many segments', () => { expect(() => { renderSegmentedControl({ mode: 'input', segments: [ ...defaultSegments, { id: '4', value: 'anotherOne', label: 'Another One', }, ], }); }).toThrow( 'SegmentedControl only supports up to 3 segments. Please refer to: https://wise.design/components/segmented-control', ); }); });