Skip to content

Commit 6094ce8

Browse files
authored
5527 upload panel state reset accepted files (#5579)
* clear file after sucessful submission * clear file name after sucessfull submission * added more test * improve test coverage * frontend linting * keeping files with error * update file submission form * linting from another develop
1 parent c576d9c commit 6094ce8

File tree

9 files changed

+248
-34
lines changed

9 files changed

+248
-34
lines changed

tdrs-frontend/src/components/FileUpload/FileUpload.jsx

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,20 @@ const load = (file, section, input, dropTarget, dispatch) => {
6767

6868
filereader.onload = () => {
6969
let error = false
70+
const basePayload = {
71+
file,
72+
fileName: file.name,
73+
fileType: file.type,
74+
}
7075
const re = /(\.txt|\.ms\d{2}|\.ts\d{2,3})$/i
7176
if (!re.exec(file.name)) {
72-
createFileInputErrorState(input, dropTarget)
77+
createFileInputErrorState(input, dropTarget, { preservePreview: true })
7378

7479
dispatch({
7580
type: FILE_EXT_ERROR,
7681
payload: {
7782
error: { message: INVALID_EXT_ERROR },
83+
...basePayload,
7884
section,
7985
},
8086
})
@@ -84,12 +90,13 @@ const load = (file, section, input, dropTarget, dispatch) => {
8490
const isImg = fileTypeChecker.validateFileType(filereader.result, types)
8591

8692
if (!error && isImg) {
87-
createFileInputErrorState(input, dropTarget)
93+
createFileInputErrorState(input, dropTarget, { preservePreview: true })
8894

8995
dispatch({
9096
type: SET_FILE_ERROR,
9197
payload: {
9298
error: { message: INVALID_FILE_ERROR },
99+
...basePayload,
93100
section,
94101
},
95102
})
@@ -142,6 +149,15 @@ function FileUpload({
142149
? `Selected File ${selectedFile?.fileName}. To change the selected file, click this button.`
143150
: `Drag file here or choose from folder.`
144151

152+
// Reset the underlying input and preview when fiscal params change
153+
useEffect(() => {
154+
if (inputRef.current) {
155+
inputRef.current.value = null
156+
}
157+
const deps = checkPreviewDependencies(targetClassName)
158+
if (deps.rendered) removeOldPreviews(deps.dropTarget, deps.instructions)
159+
}, [year, quarter, targetClassName])
160+
145161
useEffect(() => {
146162
const trySettingPreview = () => {
147163
const previewState = handlePreview(fileName, targetClassName)
@@ -170,13 +186,19 @@ function FileUpload({
170186
const { name: section } = event.target
171187
const file = event.target.files[0]
172188

189+
if (!file) return
190+
191+
const basePayload = {
192+
file,
193+
fileName: file.name,
194+
fileType: file.type,
195+
}
196+
173197
// Clear existing errors and the current
174198
// file in the state if the user is re-uploading
175199
dispatch(clearError({ section }))
176200
dispatch(clearFile({ section }))
177201

178-
if (!file) return
179-
180202
const input = inputRef.current
181203
const dropTarget = inputRef.current.parentNode
182204

@@ -187,13 +209,15 @@ function FileUpload({
187209
dropTarget,
188210
dispatch
189211
)
212+
let hasValidationError = error
190213

191214
const dispatchProgramTypeError = (
192215
programTypeResult,
193216
selectedProgramType,
194217
input,
195218
dropTarget,
196-
section
219+
section,
220+
basePayload
197221
) => {
198222
let formattedFileProgramType = programTypeResult.progType
199223
let formattedSelectedProgramType = selectedProgramType
@@ -209,7 +233,8 @@ function FileUpload({
209233

210234
const programTypeError = `File may correspond to ${formattedFileProgramType} instead of ${formattedSelectedProgramType}. Please verify the file type.`
211235
// Handle specific program type cases
212-
createFileInputErrorState(input, dropTarget)
236+
createFileInputErrorState(input, dropTarget, { preservePreview: true })
237+
hasValidationError = true
213238
dispatch({
214239
type: SET_FILE_ERROR,
215240
payload: {
@@ -218,6 +243,7 @@ function FileUpload({
218243
? programTypeError
219244
: NULL_PROGRAM_TYPE_ERROR,
220245
},
246+
...basePayload,
221247
section,
222248
},
223249
})
@@ -227,7 +253,8 @@ function FileUpload({
227253
calendarFiscalResult,
228254
input,
229255
dropTarget,
230-
section
256+
section,
257+
basePayload
231258
) => {
232259
// Handle fiscal year and quarter mismatch
233260
let error_period
@@ -257,7 +284,8 @@ function FileUpload({
257284
default:
258285
error_period = ''
259286
}
260-
createFileInputErrorState(input, dropTarget)
287+
createFileInputErrorState(input, dropTarget, { preservePreview: true })
288+
hasValidationError = true
261289
dispatch({
262290
type: SET_FILE_ERROR,
263291
payload: {
@@ -272,6 +300,7 @@ function FileUpload({
272300
`. Adjust your search parameters or upload a different file.`,
273301
link: link,
274302
},
303+
...basePayload,
275304
section,
276305
},
277306
})
@@ -291,18 +320,28 @@ function FileUpload({
291320
selectedProgramType,
292321
input,
293322
dropTarget,
294-
section
323+
section,
324+
{
325+
file: encodedFile,
326+
fileName: encodedFile.name,
327+
fileType: encodedFile.type,
328+
}
295329
)
296330
} else if (!calendarFiscalResult.isValid) {
297331
dispatchCalendarFiscalError(
298332
calendarFiscalResult,
299333
input,
300334
dropTarget,
301-
section
335+
section,
336+
{
337+
file: encodedFile,
338+
fileName: encodedFile.name,
339+
fileType: encodedFile.type,
340+
}
302341
)
303342
}
304343
}
305-
if (inputRef.current) {
344+
if (inputRef.current && !hasValidationError) {
306345
inputRef.current.value = null
307346
}
308347
}

tdrs-frontend/src/components/FileUploadForms/QuarterFileUploadForm.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ReportsProvider } from '../Reports/ReportsContext'
66
import { useFormSubmission } from '../../hooks/useFormSubmission'
77
import { useEventLogger } from '../../utils/eventLogger'
88
import { MemoryRouter } from 'react-router-dom'
9+
import * as reportsActions from '../../actions/reports'
910

1011
// Mock dependencies
1112
jest.mock('../../hooks/useFormSubmission')
@@ -76,12 +77,38 @@ describe('QuarterFileUploadForm', () => {
7677

7778
// Mock scrollIntoView
7879
window.HTMLElement.prototype.scrollIntoView = jest.fn()
80+
81+
jest
82+
.spyOn(reportsActions, 'clearFileList')
83+
.mockImplementation(({ fileType }) => ({
84+
type: 'CLEAR_FILE_LIST',
85+
payload: { fileType },
86+
}))
87+
88+
jest
89+
.spyOn(reportsActions, 'getAvailableFileList')
90+
.mockImplementation(({ file_type }, onSuccess = () => null) => () => {
91+
onSuccess()
92+
return Promise.resolve({
93+
type: 'SET_FILE_LIST',
94+
payload: { data: [] },
95+
file_type,
96+
})
97+
})
98+
99+
jest
100+
.spyOn(reportsActions, 'submit')
101+
.mockImplementation((payload, onComplete = () => null) => () => {
102+
onComplete(['file-id-1'])
103+
return Promise.resolve()
104+
})
79105
})
80106

81107
afterEach(() => {
82108
jest.runOnlyPendingTimers()
83109
jest.useRealTimers()
84110
jest.clearAllMocks()
111+
jest.restoreAllMocks()
85112
})
86113

87114
const renderComponent = (storeState = initialState, stt = { id: 1 }) => {
@@ -182,6 +209,9 @@ describe('QuarterFileUploadForm', () => {
182209
await waitFor(() => {
183210
expect(mockExecuteSubmission).toHaveBeenCalled()
184211
})
212+
213+
// After successful submission the upload panel should be cleared via handleClearFilesOnly
214+
expect(reportsActions.clearFileList).toHaveBeenCalled()
185215
})
186216

187217
it('handles submission errors gracefully', async () => {

tdrs-frontend/src/components/FileUploadForms/SectionFileUploadForm.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ReportsProvider } from '../Reports/ReportsContext'
77
import { useFormSubmission } from '../../hooks/useFormSubmission'
88
import { useEventLogger } from '../../utils/eventLogger'
99
import { MemoryRouter } from 'react-router-dom'
10+
import * as reportsActions from '../../actions/reports'
1011

1112
// Mock dependencies
1213
jest.mock('../../hooks/useFormSubmission')
@@ -84,12 +85,38 @@ describe('SectionFileUploadForm', () => {
8485

8586
// Mock scrollIntoView
8687
window.HTMLElement.prototype.scrollIntoView = jest.fn()
88+
89+
jest
90+
.spyOn(reportsActions, 'clearFileList')
91+
.mockImplementation(({ fileType }) => ({
92+
type: 'CLEAR_FILE_LIST',
93+
payload: { fileType },
94+
}))
95+
96+
jest
97+
.spyOn(reportsActions, 'getAvailableFileList')
98+
.mockImplementation(({ file_type }, onSuccess = () => null) => () => {
99+
onSuccess()
100+
return Promise.resolve({
101+
type: 'SET_FILE_LIST',
102+
payload: { data: [] },
103+
file_type,
104+
})
105+
})
106+
107+
jest
108+
.spyOn(reportsActions, 'submit')
109+
.mockImplementation((payload, onComplete = () => null) => () => {
110+
onComplete(['file-id-1'])
111+
return Promise.resolve()
112+
})
87113
})
88114

89115
afterEach(() => {
90116
jest.runOnlyPendingTimers()
91117
jest.useRealTimers()
92118
jest.clearAllMocks()
119+
jest.restoreAllMocks()
93120
})
94121

95122
const renderComponent = (
@@ -212,6 +239,8 @@ describe('SectionFileUploadForm', () => {
212239
await fn()
213240
})
214241

242+
const clearFileListSpy = jest.spyOn(reportsActions, 'clearFileList')
243+
215244
const { getByText } = renderComponent(storeState)
216245

217246
const submitButton = getByText('Submit Data Files')
@@ -220,6 +249,9 @@ describe('SectionFileUploadForm', () => {
220249
await waitFor(() => {
221250
expect(mockExecuteSubmission).toHaveBeenCalled()
222251
})
252+
253+
// After successful submission the upload panel should be cleared via handleClearFilesOnly
254+
expect(clearFileListSpy).toHaveBeenCalled()
223255
})
224256

225257
it('passes correct parameters to submit action', async () => {

tdrs-frontend/src/components/Reports/FRAReports.jsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ const UploadForm = ({
161161
setError,
162162
isSubmitting,
163163
fraHasUploadedFile,
164+
year,
165+
quarter,
164166
}) => {
165167
const inputRef = useRef(null)
166168

@@ -192,6 +194,18 @@ const UploadForm = ({
192194
}
193195
}, [file])
194196

197+
useEffect(() => {
198+
// Clear the input and any previews when fiscal params change
199+
if (inputRef.current) {
200+
inputRef.current.value = null
201+
}
202+
const targetClassName = '.usa-file-input__target input#fra-file-upload'
203+
const deps = checkPreviewDependencies(targetClassName)
204+
if (deps.rendered) removeOldPreviews(deps.dropTarget, deps.instructions)
205+
setSelectedFile(null)
206+
setError(null)
207+
}, [year, quarter, setError, setSelectedFile])
208+
195209
const onFileChanged = async (e) => {
196210
setSelectedFile(null)
197211
setError(null)
@@ -228,14 +242,16 @@ const UploadForm = ({
228242
const isXlsx = xlsxExtension.exec(fileInputValue.name)
229243

230244
if (!isCsv && !isXlsx) {
231-
createFileInputErrorState(input, dropTarget)
245+
createFileInputErrorState(input, dropTarget, { preservePreview: true })
246+
setSelectedFile(fileInputValue)
232247
setError(INVALID_EXT_ERROR)
233248
return
234249
}
235250

236251
const isImg = fileTypeChecker.validateFileType(result, imgFileTypes)
237252
if (isImg) {
238-
createFileInputErrorState(input, dropTarget)
253+
createFileInputErrorState(input, dropTarget, { preservePreview: true })
254+
setSelectedFile(fileInputValue)
239255
setError(INVALID_FILE_ERROR)
240256
return
241257
}
@@ -622,11 +638,10 @@ const FRAReportsContent = () => {
622638
onSubmitStart()
623639

624640
const onFileUploadSuccess = (datafile) => {
625-
setFraSelectedFile({
626-
name: selectedFile.name,
627-
fileName: selectedFile.name,
628-
id: datafile.id,
629-
})
641+
// Clear the upload panel after a successful submission; the record will be
642+
// visible/trackable in Submission History.
643+
setFraSelectedFile(null)
644+
setFraUploadError(null)
630645
setLocalAlertState({
631646
active: true,
632647
type: 'success',
@@ -845,6 +860,8 @@ const FRAReportsContent = () => {
845860
setError={setFraUploadError}
846861
isSubmitting={isSubmitting}
847862
fraHasUploadedFile={fraHasUploadedFile}
863+
year={yearInputValue}
864+
quarter={quarterInputValue}
848865
/>
849866
</>
850867
)}

0 commit comments

Comments
 (0)