From 4c43813bde118e38a9262baf6e95760735d0e62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Myl=C3=A8ne?= <187286904+mleroy-pass@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:30:52 +0100 Subject: [PATCH] (PC-33528)[PRO] feat: design adjustement --- .../MultiSelect/MultiSelect.module.scss | 9 +- .../ui-kit/MultiSelect/MultiSelectPanel.tsx | 70 ++++----- pro/src/ui-kit/MultiSelect/TODO.md | 4 +- .../__specs__/MultiSelectPanel.spec.tsx | 76 +++++++++- .../__specs__/MultiSelectTrigger.spec.tsx | 140 ++++++++++++++++++ 5 files changed, 257 insertions(+), 42 deletions(-) create mode 100644 pro/src/ui-kit/MultiSelect/__specs__/MultiSelectTrigger.spec.tsx diff --git a/pro/src/ui-kit/MultiSelect/MultiSelect.module.scss b/pro/src/ui-kit/MultiSelect/MultiSelect.module.scss index 4d1e7866930..729d533208e 100644 --- a/pro/src/ui-kit/MultiSelect/MultiSelect.module.scss +++ b/pro/src/ui-kit/MultiSelect/MultiSelect.module.scss @@ -115,15 +115,18 @@ } .panel { - max-height: rem.torem(340px); position: absolute; background-color: var(--color-white); left: 0; right: 0; top: rem.torem(60px); box-shadow: 0 rem.torem(3px) rem.torem(4px) var(--color-medium-shadow); - padding: rem.torem(24px) 0 rem.torem(16px); + padding: rem.torem(24px) 0 rem.torem(24px); border-radius: rem.torem(8px); +} + +.panel-scrollable { + max-height: rem.torem(340px); overflow: auto; } @@ -165,4 +168,4 @@ height: rem.torem(1px); background: var(--color-grey-medium); margin: 0 rem.torem(24px); -} +} \ No newline at end of file diff --git a/pro/src/ui-kit/MultiSelect/MultiSelectPanel.tsx b/pro/src/ui-kit/MultiSelect/MultiSelectPanel.tsx index fdc5a94d487..88771d9a5c1 100644 --- a/pro/src/ui-kit/MultiSelect/MultiSelectPanel.tsx +++ b/pro/src/ui-kit/MultiSelect/MultiSelectPanel.tsx @@ -64,40 +64,42 @@ export const MultiSelectPanel = ({ )} - {filteredOptions.length > 0 ? ( - - ) : ( - - {'Aucun résultat trouvé pour votre recherche.'} - - )} +
+ {filteredOptions.length > 0 ? ( + + ) : ( + + {'Aucun résultat trouvé pour votre recherche.'} + + )} +
) } diff --git a/pro/src/ui-kit/MultiSelect/TODO.md b/pro/src/ui-kit/MultiSelect/TODO.md index 03a83fe749f..c28a7e34d12 100644 --- a/pro/src/ui-kit/MultiSelect/TODO.md +++ b/pro/src/ui-kit/MultiSelect/TODO.md @@ -8,7 +8,7 @@ Fonctionnel Design -- [] gérer le responsive pour un titre long +- [x] gérer le responsive pour un titre long - [x] centrer le aucun résultat - [x] Passer les selected tag en violet (refacto des tags dans un autre ticket PC-33762) - [x] utiliser les fonts du design system (devrait être pris en compte avec les nouvelles maj prévues) @@ -17,4 +17,4 @@ Design Bonus -- [] typage des props hasSearch et searchExample +- [x] typage des props hasSearch et searchExample diff --git a/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectPanel.spec.tsx b/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectPanel.spec.tsx index 7be2d49905a..8b4d95df884 100644 --- a/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectPanel.spec.tsx +++ b/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectPanel.spec.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react' +import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { axe } from 'vitest-axe' @@ -75,6 +75,76 @@ describe('', () => { expect(screen.queryByText(/Exemple: Nantes/i)).not.toBeInTheDocument() }) + it('should filter options based on the search input', async () => { + render( + + ) + + expect(screen.getByText('Option 1')).toBeInTheDocument() + expect(screen.getByText('Option 2')).toBeInTheDocument() + expect(screen.getByText('Option 3')).toBeInTheDocument() + + const searchInput = screen.getByRole('searchbox') + + await userEvent.type(searchInput, 'Option 1') + + await waitFor(() => { + expect(screen.queryByText('Option 2')).not.toBeInTheDocument() + expect(screen.queryByText('Option 3')).not.toBeInTheDocument() + expect(screen.getByText('Option 1')).toBeInTheDocument() + }) + + await userEvent.clear(searchInput) + + await waitFor(() => { + expect(screen.getByText('Option 1')).toBeInTheDocument() + expect(screen.getByText('Option 2')).toBeInTheDocument() + expect(screen.getByText('Option 3')).toBeInTheDocument() + }) + }) + + it('should show "No results found" when no options match the search', async () => { + render( + + ) + + const searchInput = screen.getByRole('searchbox') + + await userEvent.type(searchInput, 'Non-matching option') + + await waitFor(() => + expect( + screen.getByText('Aucun résultat trouvé pour votre recherche.') + ).toBeInTheDocument() + ) + }) + it('should not have accessibility violations', async () => { const { container } = render( ', () => { ) const option2Checkbox = screen.getByLabelText(/Option 2/i) - await userEvent.click(option2Checkbox) + await userEvent.click(option2Checkbox) expect(onOptionSelect).toHaveBeenCalledWith(options[1]) - await userEvent.click(option2Checkbox) + await userEvent.click(option2Checkbox) expect(onOptionSelect).toHaveBeenCalledWith(options[1]) }) }) diff --git a/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectTrigger.spec.tsx b/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectTrigger.spec.tsx new file mode 100644 index 00000000000..227cf4d916e --- /dev/null +++ b/pro/src/ui-kit/MultiSelect/__specs__/MultiSelectTrigger.spec.tsx @@ -0,0 +1,140 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { axe } from 'vitest-axe' + +import { MultiSelectTrigger } from '../MultiSelectTrigger' + +describe('', () => { + const mockToggleDropdown = vi.fn() + + const props = { + isOpen: false, + selectedCount: 2, + toggleDropdown: mockToggleDropdown, + legend: 'Select Options', + label: 'Options Label', + } + + it('should render correctly', () => { + render() + + expect(screen.getByText('Options Label')).toBeInTheDocument() + expect(screen.getByText('2')).toBeInTheDocument() // This is the selectedCount badge + }) + + it('should have no accessibility violations', async () => { + const { container } = render() + + const results = await axe(container) + expect(results).toHaveNoViolations() + }) + + it('should disable the button when disabled prop is passed', () => { + render() + + const button = screen.getByRole('button') + + expect(button).toBeDisabled() + }) + + it('should display the chevron open icon if panel is opened', () => { + const { container } = render( + + ) + + let chevronIcon = container.querySelector('svg') + + expect(chevronIcon).toHaveClass('chevron chevronOpen') + }) + + it('should not display the chevron icon open if panel is closed', () => { + const { container } = render( + + ) + + let chevronIcon = container.querySelector('svg') + + chevronIcon = container.querySelector('svg') + + expect(chevronIcon).not.toHaveClass('chevron chevronOpen') + }) + + it('should render badge with correct count when options are selected', () => { + // Simulate a selected count of 3 + render( + + ) + + // Check if the badge is rendered with the correct count + expect(screen.getByText('3')).toBeInTheDocument() + }) + + it('should not render badge when no options are selected', () => { + // Simulate a selected count of 0 (no options selected) + render( + + ) + + // Check that the badge is not rendered + const badge = screen.queryByText('0') + expect(badge).toBeNull() + }) + + it('should update badge count when selecting and deselecting options', () => { + const { rerender } = render( + + ) + + // Check if the badge displays the correct count initially + expect(screen.getByText('2')).toBeInTheDocument() + + // Rerender with updated selected count + rerender( + + ) + + // Check if the badge now displays the updated count + expect(screen.getByText('5')).toBeInTheDocument() + }) + + it('should call toggleDropdown when the button is clicked', () => { + render( + + ) + + const button = screen.getByRole('button') + fireEvent.click(button) + + // Verify if toggleDropdown was called when the button is clicked + expect(mockToggleDropdown).toHaveBeenCalledTimes(1) + }) +})