openstack-uicore-foundation 5.0.17-beta.3 → 5.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/.claude/rules/openstack-uicore-foundation-components.md +62 -0
  2. package/.claude/rules/openstack-uicore-foundation-project.md +64 -0
  3. package/.claude/rules/openstack-uicore-foundation-security.md +39 -0
  4. package/.codegraph/config.json +140 -0
  5. package/docs/plans/2026-04-09-showconfirmdialog-react16-compat.md +83 -0
  6. package/docs/plans/2026-04-22-dropzone-pooling-ux.md +129 -0
  7. package/docs/plans/2026-04-23-uploadv3-duplicate-file-max-reached.md +109 -0
  8. package/docs/plans/2026-04-23-uploadv3-premature-complete-on-202.md +99 -0
  9. package/lib/components/index.js +1 -1
  10. package/lib/components/index.js.map +1 -1
  11. package/lib/components/mui/form-item-table.js +1 -1
  12. package/lib/components/mui/form-item-table.js.map +1 -1
  13. package/lib/components/mui/formik-inputs/additional-input-list.js +1 -1
  14. package/lib/components/mui/formik-inputs/additional-input-list.js.map +1 -1
  15. package/lib/components/mui/formik-inputs/additional-input.js +1 -1
  16. package/lib/components/mui/formik-inputs/additional-input.js.map +1 -1
  17. package/lib/components/mui/formik-inputs/select.js +1 -1
  18. package/lib/components/mui/formik-inputs/select.js.map +1 -1
  19. package/lib/components/mui/formik-inputs/upload.js +1 -1
  20. package/lib/components/mui/formik-inputs/upload.js.map +1 -1
  21. package/lib/components/mui/search-input.js +1 -1
  22. package/lib/components/mui/search-input.js.map +1 -1
  23. package/lib/components/mui/table/extra-rows.js +1 -1
  24. package/lib/components/mui/table/extra-rows.js.map +1 -1
  25. package/lib/i18n.js +1 -1
  26. package/lib/i18n.js.map +1 -1
  27. package/package.json +1 -1
  28. package/.claude/settings.local.json +0 -13
@@ -0,0 +1,99 @@
1
+ # UploadInputV3 Premature Completion on HTTP 202 Fix Plan
2
+
3
+ Created: 2026-04-23
4
+ Author: smarcet@gmail.com
5
+ Status: VERIFIED
6
+ Approved: Yes
7
+ Iterations: 0
8
+ Worktree: No
9
+ Type: Bugfix
10
+
11
+ ## Summary
12
+
13
+ **Symptom:** After uploading a file via UploadInputV3 that triggers HTTP 202 (async server processing), the file immediately shows as "Complete" instead of waiting for polling to confirm the server has finished processing.
14
+ **Trigger:** Upload a file via UploadInputV3. Server returns HTTP 202 with `file_id`. Dropzone's `success` event fires immediately (before polling starts or completes), and V3's `handleFileCompleted` marks the file as `complete: true`.
15
+ **Root Cause:** `src/components/inputs/upload-input-v3/index.js:153` — `handleFileCompleted()` unconditionally marks the file as `complete: true` when Dropzone's `success` event fires, without checking `file._asyncProcessing`. For non-chunked 202 uploads, `_finished()` is called directly by Dropzone (`dropzone.js:2707`), bypassing the `chunksUploaded` override that defers completion for chunked uploads.
16
+
17
+ **Secondary issue:** `wrappedOnUploadComplete` at line 190 is a passthrough — it doesn't mark uploading files as complete. When polling finishes, `onUploadComplete` fires but the `uploadingFiles` entry isn't cleaned up because `complete` was never set (if the guard blocks `handleFileCompleted`).
18
+
19
+ ## Investigation
20
+
21
+ - **V3-specific bug.** V2 has zero handling for `success`/`onFileCompleted`/`complete` — it renders only from the `value` prop, which is updated after `onUploadComplete` (correctly waits for polling in the 202 case).
22
+ - **Flow for non-chunked 202:** `xhr.onload` → `file._asyncProcessing = true` → `dropzoneOnLoad(e)` → `_finishedUploading` → `!chunked` → `_finished(files, response, e)` → emits `success` → DropzoneV3 `success` handler → `onFileCompleted` → `handleFileCompleted` marks `complete: true` → file shows "Complete" immediately. Polling starts AFTER `dropzoneOnLoad` returns, but the file already appears done.
23
+ - **Chunking decision:** `file.upload.chunked = options.chunking && (options.forceChunking || file.size > options.chunkSize)`. Config sets `chunking: true` but not `forceChunking` (default `false`), and `chunkSize` defaults to 2MB. Files ≤ 2MB are uploaded as non-chunked, bypassing the `chunksUploaded` override entirely.
24
+ - **`chunksUploaded` override** at `dropzone/index.js:164` correctly defers `done()` for chunked 202 uploads, but is never called for non-chunked uploads.
25
+ - **`wrappedOnUploadComplete`** at `upload-input-v3/index.js:190` just passes through to the parent — it doesn't update `uploadingFiles` state at all.
26
+
27
+ ## Behavior Contract
28
+
29
+ **Given:** UploadInputV3 with a file upload that returns HTTP 202 (async processing), where `file._asyncProcessing` is set to `true` on the Dropzone file object
30
+ **When:** Dropzone's `success` event fires (immediately after `_finishedUploading` calls `_finished` for non-chunked uploads, or via `chunksUploaded` for chunked uploads)
31
+ **Currently (bug):** `handleFileCompleted` marks the file as `complete: true` immediately, showing "Complete" in the UI before polling confirms server processing is done
32
+ **Expected (fix):** When `file._asyncProcessing` is true, `handleFileCompleted` does NOT mark the file as complete. The file stays in "Loading" state. When polling finishes and `onUploadComplete` fires (via `wrappedOnUploadComplete`), uploading files are marked complete, enabling the existing useEffect cleanup to remove them when `value` updates.
33
+ **Anti-regression:** Synchronous uploads (HTTP 200) must still mark files as "Complete" immediately via `handleFileCompleted`. File deletion, error display, progress tracking, and the existing server-renamed-filename cleanup must remain intact.
34
+
35
+ ## Fix Approach
36
+
37
+ **Chosen:** Guard in V3 handlers
38
+ **Why:** The `_asyncProcessing` flag is already set on the Dropzone file object before `success` fires. V3 just needs to check it in `handleFileCompleted` and mark files complete in `wrappedOnUploadComplete`. No changes to DropzoneJS or DropzoneV3 — purely V3 state management.
39
+ **Alternatives considered:**
40
+ - *Guard in DropzoneV3 success handler* — would work but touches the shared wrapper file used by all upload versions, increasing regression surface.
41
+ - *Override _finishedUploading in DropzoneJS* — fixes at the Dropzone level but patches library internals, fragile across Dropzone upgrades.
42
+
43
+ **Files:**
44
+ - `src/components/inputs/upload-input-v3/index.js` (primary fix — handleFileCompleted guard + wrappedOnUploadComplete)
45
+ - `src/components/inputs/upload-input-v3/__tests__/upload-input-v3.test.js` (reproducing test)
46
+
47
+ **Strategy:**
48
+ 1. In `handleFileCompleted`: check `file._asyncProcessing` — if true, return early (don't mark complete)
49
+ 2. In `wrappedOnUploadComplete`: before calling the parent callback, mark all uploading files as `complete: true` via `setUploadingFiles`. This ensures cleanup works for both sync (where `handleFileCompleted` already set it) and async (where it was skipped).
50
+
51
+ **Tests:** Add test to `upload-input-v3.test.js` that simulates: file added → file completed with `_asyncProcessing: true` → verify file still shows "Loading" (not "Complete").
52
+
53
+ ## Verification Scenario
54
+
55
+ ### TS-001: Async Upload Processing State
56
+ **Preconditions:** UploadInputV3 with `maxFiles=1`, server configured to return HTTP 202 for uploads
57
+
58
+ | Step | Action | Expected Result (after fix) |
59
+ |------|--------|-----------------------------|
60
+ | 1 | Upload a file that triggers HTTP 202 async processing | File shows "Loading" with progress bar, NOT "Complete" |
61
+ | 2 | Wait for polling to return `status: 'complete'` and parent to update value | File transitions to "Complete" (from value section), no duplicate entries |
62
+
63
+ ## Progress
64
+
65
+ - [x] Task 1: Write Reproducing Test (RED)
66
+ - [x] Task 2: Implement Fix at Root Cause
67
+ - [x] Task 3: Quality Gate
68
+ **Tasks:** 3 | **Done:** 3
69
+
70
+ ## Tasks
71
+
72
+ ### Task 1: Write Reproducing Test (RED)
73
+
74
+ **Objective:** Encode the Behavior Contract as a failing test BEFORE writing any fix code.
75
+ **Files:** `src/components/inputs/upload-input-v3/__tests__/upload-input-v3.test.js`
76
+ **Entry point:** `UploadInputV3` component (rendered with mock DropzoneV3)
77
+ **Test scenario:**
78
+ 1. Render UploadInputV3 with `maxFiles=1` and `value=[]`
79
+ 2. Simulate `onAddedFile({ name: 'video.mp4', size: 5000000 })`
80
+ 3. Simulate `onFileCompleted({ name: 'video.mp4', size: 5000000, _asyncProcessing: true })`
81
+ 4. Assert: file still shows "Loading" (not "Complete") — the `_asyncProcessing` flag should prevent marking as complete
82
+ **DoD:** Test exists, named `test('does not mark file as complete when _asyncProcessing is true')`, runs, fails because `handleFileCompleted` currently ignores `_asyncProcessing` and marks complete unconditionally.
83
+ **Verify:** `npx jest src/components/inputs/upload-input-v3/__tests__/upload-input-v3.test.js --verbose`
84
+
85
+ ### Task 2: Implement Fix at Root Cause
86
+
87
+ **Objective:** Minimal change to `handleFileCompleted` and `wrappedOnUploadComplete` to prevent premature completion on HTTP 202.
88
+ **Files:** `src/components/inputs/upload-input-v3/index.js`
89
+ **Strategy:**
90
+ 1. In `handleFileCompleted` (line 153): add early return when `file._asyncProcessing` is true
91
+ 2. In `wrappedOnUploadComplete` (line 190): add `setUploadingFiles(prev => prev.map(f => ({ ...f, complete: true })))` before calling the parent callback, so all uploading files are marked complete when the server confirms processing is done
92
+ **DoD:** Reproducing test PASSES. Full test suite PASSES. Diff touches root-cause file only.
93
+ **Verify:** `npx jest --verbose`
94
+
95
+ ### Task 3: Quality Gate
96
+
97
+ **Objective:** Full suite re-run, build clean.
98
+ **DoD:** Full suite green, build succeeds, no performance regressions.
99
+ **Verify:** `npx jest --verbose && npm run build-dev`