From 298988503eabfae361d25133138f01b0ec6047ff Mon Sep 17 00:00:00 2001 From: Roland Date: Fri, 9 Oct 2026 14:15:50 -0600 Subject: [PATCH] app: a download frequency of "Once" no longer blocks submission useForm treated any falsy value as a missing required field. "Once" is frequency 0, so the Channel/Playlist, RSS, and Zim download forms never became ready and the Download button stayed disabled. Only null, undefined, '', false, and NaN count as missing now. Co-Authored-By: Claude Opus 5.5 --- app/src/components/Download.cy.js | 9 +++++++++ app/src/hooks/useForm.js | 6 +++++- app/src/hooks/useForm.test.js | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/src/components/Download.cy.js b/app/src/components/Download.cy.js index cf61eedc..4f33edf8 100644 --- a/app/src/components/Download.cy.js +++ b/app/src/components/Download.cy.js @@ -127,6 +127,15 @@ describe('', () => { shouldBeInvalid('#url_input'); }); + it('can be submitted with a frequency of Once', () => { + cy.get('#url_input').type('https://example.com/playlist').wait(500); + cy.get('#frequency_select').click(); + cy.contains('[role="option"]:visible', 'Once').should('be.visible').click({force: true}); + cy.get('#frequency_select').should('have.value', 'Once'); + + cy.get('#download_form_download_button').should('not.be.disabled'); + }); + it('can choose different options', () => { /* * `#download_frequency_selector` and `.item` named the old dropdown and its menu rows. diff --git a/app/src/hooks/useForm.js b/app/src/hooks/useForm.js index 0c03ec7c..8959a43d 100644 --- a/app/src/hooks/useForm.js +++ b/app/src/hooks/useForm.js @@ -67,7 +67,11 @@ export function useForm({ setReady(false); return } - const missingValues = Object.keys(memoizedRequires).filter(i => !_.get(formData, i)); + // 0 is a real value (a download frequency of "Once"), only empty values are missing. + const missingValues = Object.keys(memoizedRequires).filter(i => { + const value = _.get(formData, i); + return value === null || value === undefined || value === '' || value === false || Number.isNaN(value); + }); if (missingValues.length > 0) { console.debug(`Form invalid because it is missing required value: ${missingValues[0]}`); setReady(false); diff --git a/app/src/hooks/useForm.test.js b/app/src/hooks/useForm.test.js index 443f0411..9d37ea62 100644 --- a/app/src/hooks/useForm.test.js +++ b/app/src/hooks/useForm.test.js @@ -727,6 +727,29 @@ describe('useForm', () => { }); }); + it('zero satisfies a required field', async () => { + // A download frequency of 0 means "Once". + const {result} = renderHook(() => useForm({ + defaultFormData: {frequency: null}, + submitter: jest.fn(), + })); + + act(() => { + result.current.getCustomProps({name: 'frequency', required: true}); + }); + await waitFor(() => { + expect(result.current.ready).toBe(false); + }); + + act(() => { + result.current.setValue('frequency', 0); + }); + await waitFor(() => { + expect(result.current.formData.frequency).toBe(0); + expect(result.current.ready).toBe(true); + }); + }); + it('addRequires marks field as required', async () => { const {result} = renderHook(() => useForm({ defaultFormData: {field: ''},