diff --git a/frontend/e2e/create-invoice.spec.ts b/frontend/e2e/create-invoice.spec.ts index 42522b7..200ce1b 100644 --- a/frontend/e2e/create-invoice.spec.ts +++ b/frontend/e2e/create-invoice.spec.ts @@ -37,6 +37,20 @@ test('a new item starts with a blank price, not a literal 0', async ({ page }) = await expect(page.locator('[id^="item-price-"]:visible')).toHaveValue(''); }); +test('clearing the quantity field leaves it empty, not stuck on 0', async ({ page }) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'New Invoice' }).click(); + await visibleButton(page, '+ Add New Item').click(); + + const qty = page.locator('[id^="item-qty-"]:visible'); + await expect(qty).toHaveValue('1'); + + await qty.fill(''); + + await expect(qty).toHaveValue(''); +}); + test('shows the save error inside the still-open drawer, not on the page', async ({ page }) => { await page.goto('/'); diff --git a/frontend/src/features/invoices/components/invoice-form-drawer/invoice-form-drawer.tsx b/frontend/src/features/invoices/components/invoice-form-drawer/invoice-form-drawer.tsx index 8ddd46e..0401373 100644 --- a/frontend/src/features/invoices/components/invoice-form-drawer/invoice-form-drawer.tsx +++ b/frontend/src/features/invoices/components/invoice-form-drawer/invoice-form-drawer.tsx @@ -373,7 +373,12 @@ const InvoiceFormDrawer = (props: IInvoiceFormDrawerProps): JSX.Element => { id={`item-qty-${index}`} min={0} type="number" - value={item.quantity} + // Backspacing to empty fires onChange with '', and + // Number('') is 0, not NaN, so without this the + // controlled value snaps straight back to a literal + // "0" and the field never visibly clears. Same fix + // already used for price, below. + value={item.quantity || ''} // appearance-none drops the spinner in Firefox and // current Chrome/Safari; the two arbitrary-variant // rules clear WebKit's older spin-button pseudo