mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-27 09:08:30 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35fb59e64e | |||
| ee3fff1dab |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"src": {
|
||||
"@typescript-eslint/no-non-null-assertion": 382,
|
||||
"@typescript-eslint/no-misused-promises": 849,
|
||||
"@typescript-eslint/no-misused-promises": 848,
|
||||
"no-console": 88,
|
||||
"complexity": 220,
|
||||
"@typescript-eslint/no-explicit-any": 146,
|
||||
|
||||
@@ -485,6 +485,17 @@ describe('KategorieGruppe — inline add item', () => {
|
||||
expect(onAddItem).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('FE-W5CAT-028a: Enter on the first item typed into the placeholder row opens the add field (#2825)', async () => {
|
||||
server.use(http.put('/api/trips/1/packing/1', () => HttpResponse.json({ item: buildPackingItem({ id: 1, name: 'Tent', category: 'Gear' }) })))
|
||||
setup({ items: [buildPackingItem({ id: 1, name: '...', category: 'Gear' })] })
|
||||
|
||||
fireEvent.click(screen.getByText('...'))
|
||||
fireEvent.change(screen.getByPlaceholderText('...'), { target: { value: 'Tent' } })
|
||||
fireEvent.keyDown(screen.getByPlaceholderText('...'), { key: 'Enter' })
|
||||
|
||||
await waitFor(() => expect(screen.getByPlaceholderText('Item name...')).toHaveFocus())
|
||||
})
|
||||
|
||||
it('FE-W5CAT-028: the confirm button adds the item, and stays inert while empty', () => {
|
||||
const onAddItem = vi.fn(async () => {})
|
||||
const { container } = setup({ onAddItem })
|
||||
|
||||
@@ -86,6 +86,7 @@ export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRen
|
||||
const [showAddItem, setShowAddItem] = useState(false)
|
||||
const [newItemName, setNewItemName] = useState('')
|
||||
const addItemRef = useRef<HTMLInputElement>(null)
|
||||
const openAddItem = () => { setShowAddItem(true); setTimeout(() => addItemRef.current?.focus(), 30) }
|
||||
const menuBtnRef = useRef<HTMLButtonElement>(null)
|
||||
const assigneeDropdownRef = useRef<HTMLDivElement>(null)
|
||||
const { togglePackingItem } = useTripStore()
|
||||
@@ -292,7 +293,7 @@ export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRen
|
||||
const contributions = contribFor(item.id)
|
||||
return (
|
||||
<React.Fragment key={item.id}>
|
||||
<ArtikelZeile item={item} tripId={tripId} categories={allCategories} onCategoryChange={() => {}} onDelete={onDeleteItem} bagTrackingEnabled={bagTrackingEnabled} bags={bags} onCreateBag={onCreateBag} canEdit={canEdit} divider={index > 0}
|
||||
<ArtikelZeile item={item} tripId={tripId} categories={allCategories} onCategoryChange={() => {}} onDelete={onDeleteItem} onPlaceholderFilled={openAddItem} bagTrackingEnabled={bagTrackingEnabled} bags={bags} onCreateBag={onCreateBag} canEdit={canEdit} divider={index > 0}
|
||||
tripMembers={tripMembers} currentUserId={currentUserId} onSetSharing={onSetSharing} onClone={onClone} onJoin={onJoin} onLeave={onLeave}
|
||||
drag={canEdit && reorderable ? {
|
||||
isDragging: dragId === item.id,
|
||||
@@ -343,7 +344,7 @@ export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRen
|
||||
// A row of its own on a faint tint, so it closes the card instead
|
||||
// of trailing off: a dashed box in the checkbox column (past the drag
|
||||
// grip's width) and the label where an item's name sits.
|
||||
<button type="button" onClick={() => { setShowAddItem(true); setTimeout(() => addItemRef.current?.focus(), 30) }}
|
||||
<button type="button" onClick={openAddItem}
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 9, width: 'calc(100% - 8px)', margin: '6px 4px 0', paddingBlock: 8, paddingInlineEnd: 10, paddingInlineStart: 28, borderRadius: 10, border: 'none', background: 'var(--bg-secondary)', cursor: 'pointer', fontSize: 'calc(13px * var(--fs-scale-body, 1))', fontWeight: 500, color: 'var(--text-faint)', fontFamily: 'inherit', textAlign: 'start' }}
|
||||
onMouseEnter={e => e.currentTarget.style.color = 'var(--text-secondary)'}
|
||||
onMouseLeave={e => e.currentTarget.style.color = 'var(--text-faint)'}>
|
||||
|
||||
@@ -257,6 +257,23 @@ describe('ArtikelZeile — renaming', () => {
|
||||
await waitFor(() => expect(screen.getByText('...')).toBeInTheDocument())
|
||||
})
|
||||
|
||||
it('FE-W5ROW-008b: Enter on a filled placeholder row hands the typing on, a blur does not (#2825)', async () => {
|
||||
server.use(http.put('/api/trips/1/packing/1', () => HttpResponse.json({ item: buildPackingItem({ id: 1, name: 'Tent' }) })))
|
||||
const onPlaceholderFilled = vi.fn()
|
||||
setup({ item: buildPackingItem({ id: 1, name: '...', category: 'Gear' }), onPlaceholderFilled })
|
||||
|
||||
fireEvent.click(screen.getByText('...'))
|
||||
fireEvent.change(screen.getByPlaceholderText('...'), { target: { value: 'Tent' } })
|
||||
fireEvent.blur(screen.getByPlaceholderText('...'))
|
||||
await waitFor(() => expect(screen.queryByPlaceholderText('...')).toBeNull())
|
||||
expect(onPlaceholderFilled).not.toHaveBeenCalled()
|
||||
|
||||
fireEvent.click(screen.getByText('...'))
|
||||
fireEvent.change(screen.getByPlaceholderText('...'), { target: { value: 'Tent' } })
|
||||
fireEvent.keyDown(screen.getByPlaceholderText('...'), { key: 'Enter' })
|
||||
await waitFor(() => expect(onPlaceholderFilled).toHaveBeenCalledTimes(1))
|
||||
})
|
||||
|
||||
it('FE-W5ROW-009: a failing rename surfaces a save error', async () => {
|
||||
server.use(http.put('/api/trips/1/packing/1', () => new HttpResponse(null, { status: 500 })))
|
||||
setup()
|
||||
|
||||
@@ -25,6 +25,8 @@ interface ArtikelZeileProps {
|
||||
categories: string[]
|
||||
onCategoryChange: () => void
|
||||
onDelete?: (item: PackingItem) => Promise<void>
|
||||
/** Enter on the filled '...' row, so the category's add field can take over the typing (#2825). */
|
||||
onPlaceholderFilled?: () => void
|
||||
bagTrackingEnabled?: boolean
|
||||
bags?: PackingBag[]
|
||||
onCreateBag: (name: string) => Promise<PackingBag | undefined>
|
||||
@@ -49,7 +51,7 @@ interface ArtikelZeileProps {
|
||||
}
|
||||
}
|
||||
|
||||
export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCategoryChange, onDelete, bagTrackingEnabled, bags = [], onCreateBag, canEdit = true, tripMembers = [], currentUserId, onSetSharing, onClone, onJoin, onLeave, divider = false, drag }: ArtikelZeileProps) {
|
||||
export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCategoryChange, onDelete, onPlaceholderFilled, bagTrackingEnabled, bags = [], onCreateBag, canEdit = true, tripMembers = [], currentUserId, onSetSharing, onClone, onJoin, onLeave, divider = false, drag }: ArtikelZeileProps) {
|
||||
const isPlaceholder = item.name === PACKING_PLACEHOLDER_NAME
|
||||
const [editing, setEditing] = useState(false)
|
||||
const [editName, setEditName] = useState(isPlaceholder ? '' : item.name)
|
||||
@@ -80,9 +82,12 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa
|
||||
|
||||
const handleToggle = () => togglePackingItem(tripId, item.id, !item.checked)
|
||||
|
||||
const handleSaveName = async () => {
|
||||
const handleSaveName = async (viaEnter = false) => {
|
||||
if (!editName.trim()) { setEditing(false); setEditName(isPlaceholder ? '' : item.name); return }
|
||||
try { await updatePackingItem(tripId, item.id, { name: editName.trim() }); setEditing(false) }
|
||||
try {
|
||||
await updatePackingItem(tripId, item.id, { name: editName.trim() }); setEditing(false)
|
||||
if (viaEnter && isPlaceholder) onPlaceholderFilled?.()
|
||||
}
|
||||
catch { toast.error(t('packing.toast.saveError')) }
|
||||
}
|
||||
|
||||
@@ -188,8 +193,8 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa
|
||||
type="text" value={editName} autoFocus
|
||||
placeholder={isPlaceholder ? '...' : undefined}
|
||||
onChange={e => setEditName(e.target.value)}
|
||||
onBlur={handleSaveName}
|
||||
onKeyDown={e => { if (e.key === 'Enter') void handleSaveName(); if (e.key === 'Escape') { setEditing(false); setEditName(isPlaceholder ? '' : item.name) } }}
|
||||
onBlur={() => void handleSaveName()}
|
||||
onKeyDown={e => { if (e.key === 'Enter') void handleSaveName(true); if (e.key === 'Escape') { setEditing(false); setEditName(isPlaceholder ? '' : item.name) } }}
|
||||
style={{ flex: 1, minWidth: 0, fontSize: 'calc(13.5px * var(--fs-scale-body, 1))', padding: '2px 8px', borderRadius: 6, border: '1px solid var(--border-primary)', outline: 'none', fontFamily: 'inherit' }}
|
||||
/>
|
||||
) : canEdit ? (
|
||||
|
||||
Reference in New Issue
Block a user