diff --git a/client/src/mobile/components/MUserPickerList.test.tsx b/client/src/mobile/components/MUserPickerList.test.tsx new file mode 100644 index 000000000..cd2a9908f --- /dev/null +++ b/client/src/mobile/components/MUserPickerList.test.tsx @@ -0,0 +1,39 @@ +// FE-COMP-MUSERPICK-001 to FE-COMP-MUSERPICK-003 +import { fireEvent, render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import MUserPickerList from './MUserPickerList'; + +const users = [ + { id: 2, username: 'bob' }, + { id: 3, username: 'carol' }, +]; + +describe('MUserPickerList', () => { + it('FE-COMP-MUSERPICK-001: renders one button per user and tints none while nothing is picked', () => { + render(); + + const rows = screen.getAllByRole('button'); + expect(rows.map((b) => b.textContent)).toEqual(['bob', 'carol']); + for (const row of rows) { + expect(row).toHaveAttribute('type', 'button'); + expect(row.className).not.toContain('bg-[color:var(--m-ic)]'); + } + }); + + it('FE-COMP-MUSERPICK-002: tints only the picked user', () => { + render(); + + expect(screen.getByRole('button', { name: 'carol' }).className).toContain('bg-[color:var(--m-ic)]'); + expect(screen.getByRole('button', { name: 'bob' }).className).not.toContain('bg-[color:var(--m-ic)]'); + }); + + it('FE-COMP-MUSERPICK-003: hands the tapped user id to onPick', () => { + const onPick = vi.fn(); + render(); + + fireEvent.click(screen.getByRole('button', { name: 'bob' })); + + expect(onPick).toHaveBeenCalledWith(2); + }); +}); diff --git a/client/src/mobile/components/MUserPickerList.tsx b/client/src/mobile/components/MUserPickerList.tsx new file mode 100644 index 000000000..d0ef6945a --- /dev/null +++ b/client/src/mobile/components/MUserPickerList.tsx @@ -0,0 +1,28 @@ +interface MUserPickerListProps { + users: { id: number; username: string }[]; + selected: number | null; + onPick: (id: number) => void; +} + +/** + * The unfolded user list under a phone sheet's user picker (the vacay invite + * and share sheets): one row per user, the picked one tinted. + */ +export default function MUserPickerList({ users, selected, onPick }: MUserPickerListProps) { + return ( +
+ {users.map((u) => ( + + ))} +
+ ); +} diff --git a/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx b/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx index 251f9f2b7..1f258d664 100644 --- a/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx +++ b/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx @@ -1,6 +1,7 @@ import { ChevronDown, Clock, Loader2, X } from 'lucide-react' import MSheet from '../../components/MSheet' import MIconBtn from '../../components/MIconBtn' +import MUserPickerList from '../../components/MUserPickerList' import { useVacayStore } from '../../../store/vacayStore' import { useTranslation } from '../../../i18n' import { type VacayUser } from '../../../types' @@ -59,22 +60,7 @@ export default function MVacayInviteSheet({ open, onClose }: MVacayInviteSheetPr - {pickerOpen && ( -
- {available.map(u => ( - - ))} -
- )} + {pickerOpen && } )} diff --git a/client/src/mobile/screens/vacay/MVacayShareSheet.tsx b/client/src/mobile/screens/vacay/MVacayShareSheet.tsx index 712b917b5..b1ab621f0 100644 --- a/client/src/mobile/screens/vacay/MVacayShareSheet.tsx +++ b/client/src/mobile/screens/vacay/MVacayShareSheet.tsx @@ -1,6 +1,7 @@ import { ChevronDown, Eye, EyeOff, Loader2, Share2, X } from 'lucide-react' import MSheet from '../../components/MSheet' import MIconBtn from '../../components/MIconBtn' +import MUserPickerList from '../../components/MUserPickerList' import { useVacayStore } from '../../../store/vacayStore' import { useTranslation } from '../../../i18n' import { useVacayShareActions } from '../../../components/Vacay/useVacayShareActions' @@ -71,22 +72,7 @@ export default function MVacayShareSheet({ open, onClose }: MVacayShareSheetProp {t('vacay.share')} - {pickerOpen && ( -
- {available.map(u => ( - - ))} -
- )} + {pickerOpen && } )}