mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-12 20:06:57 +02:00
fix: checklist Enter key focuses new item on reopened notes
The addItem() focus logic used positional indexing (inputs[afterIndex+1])
but afterIndex came from the full items[] array while DOM inputs only
render active (unchecked) items. On reopened notes, checked items are
interleaved in the parsed content, causing the index offset to be wrong.
Fix: use data-item-id attribute for targeted querySelector lookup.
Test: reopened note with interleaved checked/unchecked items.
Introduced in e5688c9 (active/done split).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Fred Cabassut
co-authored by
Claude Opus 4.6
parent
b33d3d3f72
commit
e0eca4228b
@@ -73,9 +73,12 @@
|
||||
items.splice(afterIndex + 1, 0, newItem);
|
||||
items = [...items];
|
||||
emitChange();
|
||||
// Focus by unique ID — positional indexing breaks when checked items
|
||||
// are interleaved in items[] (e.g. reopened notes) because the DOM
|
||||
// only renders active items as inputs
|
||||
const newId = newItem.id;
|
||||
setTimeout(() => {
|
||||
const inputs = document.querySelectorAll<HTMLInputElement>('[data-testid="checklist-input"]');
|
||||
inputs[afterIndex + 1]?.focus();
|
||||
document.querySelector<HTMLInputElement>(`[data-item-id="${newId}"]`)?.focus();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -140,6 +143,7 @@
|
||||
class="flex-1 min-w-0 bg-transparent text-sm outline-none {item.checked ? 'text-[var(--text-muted)] line-through' : 'text-[var(--text)]'}"
|
||||
placeholder="List item"
|
||||
data-testid="checklist-input"
|
||||
data-item-id={item.id}
|
||||
/>
|
||||
<button
|
||||
onclick={() => removeItem(item.id)}
|
||||
|
||||
@@ -73,6 +73,31 @@ test.describe('Checklist', () => {
|
||||
await expect(page.getByTestId('checklist-input')).toHaveCount(2);
|
||||
});
|
||||
|
||||
test('Scenario: Enter key focuses new item on reopened note with interleaved checked items', async ({ authenticatedPage: page }) => {
|
||||
// Given a saved checklist note with interleaved checked/unchecked items
|
||||
// (this is the state when reopening an existing note — checked items
|
||||
// are scattered in the content, not grouped at the end)
|
||||
await createChecklistNote(page, 'Shopping', ['Milk', 'Eggs', 'Bread', 'Butter']);
|
||||
|
||||
// Reopen and check items 1 and 2 so content becomes interleaved
|
||||
await noteCard(page, 'Shopping').click();
|
||||
await page.getByTestId('checklist-checkbox').nth(0).click(); // Milk → done
|
||||
await page.getByTestId('checklist-checkbox').nth(0).click(); // Eggs (now first active) → done
|
||||
await page.getByTestId('close-editor-btn').click();
|
||||
|
||||
// Reopen — parseChecklist restores interleaved order: [x]Milk, [x]Eggs, [ ]Bread, [ ]Butter
|
||||
await noteCard(page, 'Shopping').click();
|
||||
await expect(page.getByTestId('checklist-input')).toHaveCount(2); // Bread, Butter active
|
||||
|
||||
// When the user presses Enter on the first active item (Bread)
|
||||
await page.getByTestId('checklist-input').first().press('Enter');
|
||||
|
||||
// Then focus moves to the newly created empty item
|
||||
await expect(page.getByTestId('checklist-input')).toHaveCount(3);
|
||||
await expect(page.getByTestId('checklist-input').nth(1)).toBeFocused();
|
||||
await expect(page.getByTestId('checklist-input').nth(1)).toHaveValue('');
|
||||
});
|
||||
|
||||
test('Scenario: Backspace on empty item removes it from the list', async ({ authenticatedPage: page }) => {
|
||||
// Given a checklist with two items where the second is empty
|
||||
await page.getByTestId('new-note-btn').click();
|
||||
|
||||
Reference in New Issue
Block a user