diff --git a/client/src/components/Packing/ApplyTemplateButton.tsx b/client/src/components/Packing/ApplyTemplateButton.tsx index 70bd13187..239137927 100644 --- a/client/src/components/Packing/ApplyTemplateButton.tsx +++ b/client/src/components/Packing/ApplyTemplateButton.tsx @@ -4,6 +4,7 @@ import { packingApi } from '../../api/client' import { useTripStore } from '../../store/tripStore' import { useToast } from '../shared/Toast' import { useTranslation } from '../../i18n' +import { PackingTemplateOption } from './PackingTemplateOption' interface Template { id: number @@ -79,23 +80,7 @@ export default function ApplyTemplateButton({ tripId, visibility, style, classNa }} > {templates.map(tmpl => ( - + handleApply(tmpl.id)} t={t} /> ))} )} diff --git a/client/src/components/Packing/PackingListPanelHeader.tsx b/client/src/components/Packing/PackingListPanelHeader.tsx index aa11c5b86..d445a2eab 100644 --- a/client/src/components/Packing/PackingListPanelHeader.tsx +++ b/client/src/components/Packing/PackingListPanelHeader.tsx @@ -3,6 +3,7 @@ import { } from 'lucide-react' import type { PackingState } from './usePackingListPanel' import NameDialog from '../shared/NameDialog' +import { PackingTemplateOption } from './PackingTemplateOption' export function PackingHeader(S: PackingState) { const { @@ -63,22 +64,14 @@ export function PackingHeader(S: PackingState) { boxShadow: '0 4px 16px rgba(0,0,0,0.12)', padding: 4, minWidth: 200, }}> {availableTemplates.map(tmpl => ( - + handleApplyTemplate(tmpl.id)} + t={t} + fade + /> ))} )} diff --git a/client/src/components/Packing/PackingTemplateOption.test.tsx b/client/src/components/Packing/PackingTemplateOption.test.tsx new file mode 100644 index 000000000..17eddb1e4 --- /dev/null +++ b/client/src/components/Packing/PackingTemplateOption.test.tsx @@ -0,0 +1,38 @@ +import { describe, expect, it, vi } from 'vitest'; +import { fireEvent, render, screen } from '../../../tests/helpers/render'; +import { PackingTemplateOption } from './PackingTemplateOption'; + +const t = (key: string, params?: Record) => + key === 'admin.packingTemplates.items' ? `items(${params?.count})` : key; + +describe('PackingTemplateOption', () => { + it('shows the template name over its item count', () => { + render(); + expect(screen.getByText('Beach')).toBeInTheDocument(); + expect(screen.getByText('12 items(12)')).toBeInTheDocument(); + }); + + it('applies the template on click', () => { + const onClick = vi.fn(); + render(); + fireEvent.click(screen.getByRole('button')); + expect(onClick).toHaveBeenCalledTimes(1); + }); + + it('tints the row while hovered and clears it on leave', () => { + render(); + const row = screen.getByRole('button'); + fireEvent.mouseEnter(row); + expect(row.style.background).toBe('var(--bg-tertiary)'); + fireEvent.mouseLeave(row); + expect(row.style.background).toBe('transparent'); + }); + + it('fades the hover only when asked', () => { + const { unmount } = render(); + expect(screen.getByRole('button').style.transition).toBe(''); + unmount(); + render(); + expect(screen.getByRole('button').style.transition).toBe('background 0.1s'); + }); +}); diff --git a/client/src/components/Packing/PackingTemplateOption.tsx b/client/src/components/Packing/PackingTemplateOption.tsx new file mode 100644 index 000000000..2b9cd6e75 --- /dev/null +++ b/client/src/components/Packing/PackingTemplateOption.tsx @@ -0,0 +1,46 @@ +import { Package } from 'lucide-react'; +import type { TranslationFn } from '../../types'; + +interface PackingTemplateOptionProps { + name: string; + itemCount: number; + onClick: () => void; + t: TranslationFn; + /** Fade the hover background in rather than switching it. */ + fade?: boolean; +} + +/** One packing template in an apply menu: its name over how many items it adds. */ +export function PackingTemplateOption({ name, itemCount, onClick, t, fade = false }: PackingTemplateOptionProps) { + return ( + + ); +}