import { useCallback, FormEvent } from 'react' import OLButton from '@/features/ui/components/ol/ol-button' import OLForm from '@/features/ui/components/ol/ol-form' import OLFormControl from '@/features/ui/components/ol/ol-form-control' import { Select, SelectProps, } from '../../../../frontend/js/shared/components/select' const testData = [1, 2, 3].map(index => ({ key: index, value: `Demo item ${index}`, sub: `Subtitle ${index}`, })) type RenderProps = Partial> & { onSubmit?: (formData: object) => void } function render(props: RenderProps) { const submitHandler = (event: FormEvent) => { event.preventDefault() if (props.onSubmit) { const formData = new FormData(event.target as HTMLFormElement) // a plain object is more convenient to work later with assertions props.onSubmit(Object.fromEntries(formData.entries())) } } cy.mount(
', function () { describe('initial rendering', function () { it('renders default text', function () { render({ defaultText: 'Choose an item' }) cy.findByTestId('spinner').should('not.exist') cy.findByRole('textbox', { name: 'Choose an item' }) }) it('renders default item', function () { render({ defaultItem: testData[2] }) cy.findByRole('textbox', { name: 'Demo item 3' }) }) it('default item takes precedence over default text', function () { render({ defaultText: 'Choose an item', defaultItem: testData[2] }) cy.findByRole('textbox', { name: 'Demo item 3' }) }) it('renders label', function () { render({ defaultText: 'Choose an item', label: 'test label', optionalLabel: false, }) cy.findByRole('textbox', { name: 'test label' }) cy.findByRole('textbox', { name: '(Optional)' }).should('not.exist') }) it('renders optional label', function () { render({ defaultText: 'Choose an item', label: 'test label', optionalLabel: true, }) cy.findByRole('textbox', { name: 'test label (Optional)' }) }) it('renders a spinner while loading when there is a label', function () { render({ defaultText: 'Choose an item', label: 'test label', loading: true, }) cy.findByTestId('spinner') }) it('does not render a spinner while loading if there is no label', function () { render({ defaultText: 'Choose an item', loading: true, }) cy.findByTestId('spinner').should('not.exist') }) }) describe('items rendering', function () { it('renders all items', function () { render({ defaultText: 'Choose an item' }) cy.findByRole('textbox', { name: 'Choose an item' }).click() cy.findByRole('option', { name: 'Demo item 1' }) cy.findByRole('option', { name: 'Demo item 2' }) cy.findByRole('option', { name: 'Demo item 3' }) }) it('renders subtitles', function () { render({ defaultText: 'Choose an item', itemToSubtitle: x => String(x?.sub), }) cy.findByRole('textbox', { name: 'Choose an item' }).click() cy.findByRole('option', { name: 'Demo item 1 Subtitle 1' }) cy.findByRole('option', { name: 'Demo item 2 Subtitle 2' }) cy.findByRole('option', { name: 'Demo item 3 Subtitle 3' }) }) }) describe('item selection', function () { it('cannot select an item when disabled', function () { render({ defaultText: 'Choose an item', disabled: true }) cy.findByRole('textbox', { name: 'Choose an item' }).click({ force: true, }) cy.findByRole('option', { name: 'Demo item 1' }).should('not.exist') cy.findByRole('option', { name: 'Demo item 2' }).should('not.exist') cy.findByRole('option', { name: 'Demo item 3' }).should('not.exist') cy.findByRole('textbox', { name: 'Choose an item' }) }) it('renders only the selected item after selection', function () { render({ defaultText: 'Choose an item' }) cy.findByRole('textbox', { name: 'Choose an item' }).click() cy.findByRole('option', { name: 'Demo item 1' }) cy.findByRole('option', { name: 'Demo item 2' }) cy.findByRole('option', { name: 'Demo item 3' }).click() cy.findByRole('textbox', { name: 'Choose an item' }).should('not.exist') cy.findByRole('option', { name: 'Demo item 1' }).should('not.exist') cy.findByRole('option', { name: 'Demo item 2' }).should('not.exist') cy.findByRole('textbox', { name: 'Demo item 3' }) }) it('invokes callback after selection', function () { const selectionHandler = cy.stub().as('selectionHandler') render({ defaultText: 'Choose an item', onSelectedItemChanged: selectionHandler, }) cy.findByRole('textbox', { name: 'Choose an item' }).click() cy.findByRole('option', { name: 'Demo item 2' }).click() cy.get('@selectionHandler').should( 'have.been.calledOnceWith', testData[1] ) }) }) describe('when the form is submitted', function () { it('populates FormData with the default selected item', function () { const submitHandler = cy.stub().as('submitHandler') render({ defaultItem: testData[1], onSubmit: submitHandler }) cy.findByText('submit').click() cy.get('@submitHandler').should('have.been.calledOnceWith', { select_control: 'Demo item 2', }) }) it('populates FormData with the selected item', function () { const submitHandler = cy.stub().as('submitHandler') render({ defaultItem: testData[1], onSubmit: submitHandler }) cy.findByRole('textbox', { name: 'Demo item 2' }).click() // open dropdown cy.findByText('Demo item 3').click() // choose a different item cy.findByText('submit').click() cy.get('@submitHandler').should('have.been.calledOnceWith', { select_control: 'Demo item 3', }) }) it('does not populate FormData when no item is selected', function () { const submitHandler = cy.stub().as('submitHandler') render({ defaultText: 'Choose an item', onSubmit: submitHandler }) cy.findByText('submit').click() cy.get('@submitHandler').should('have.been.calledOnceWith', {}) }) }) describe('with react-bootstrap forms', function () { type FormWithSelectProps = { onSubmit: (formData: object) => void } const FormWithSelect = ({ onSubmit }: FormWithSelectProps) => { const selectComponent = useCallback( () => (