@salesforce/webapp-template-feature-react-file-upload-experimental 1.90.1 → 1.90.2

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 (45) hide show
  1. package/README.md +245 -219
  2. package/dist/CHANGELOG.md +8 -0
  3. package/dist/force-app/main/default/webapplications/feature-react-file-upload/package.json +3 -3
  4. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{api → features/fileupload/api}/fileUpload.ts +153 -0
  5. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{components → features/fileupload/components}/FileUploadDialog.tsx +2 -2
  6. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{components → features/fileupload/components}/FileUploadFileItem.tsx +1 -1
  7. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +25 -39
  8. package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +2 -2
  9. package/dist/package.json +1 -1
  10. package/package.json +14 -9
  11. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/appLayout.tsx +9 -0
  12. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/__inherit__button.tsx +39 -0
  13. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/__inherit__dialog.tsx +102 -0
  14. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/api/fileUpload.ts +299 -0
  15. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/assets/icon-image-close.svg +3 -0
  16. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/assets/image.svg +3 -0
  17. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/assets/success.svg +3 -0
  18. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/assets/symbols.svg +1 -0
  19. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/components/FileUpload.tsx +100 -0
  20. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/components/FileUploadDialog.tsx +79 -0
  21. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/components/FileUploadDropZone.tsx +90 -0
  22. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/components/FileUploadFileItem.tsx +99 -0
  23. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/components/FileUploadIcons.tsx +90 -0
  24. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/hooks/useFileUpload.ts +312 -0
  25. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/hooks/useFileUploadDialog.ts +70 -0
  26. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/pages/UploadTest.tsx +56 -0
  27. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/types/fileUpload.ts +28 -0
  28. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/utils/fileUploadUtils.ts +54 -0
  29. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/features/fileupload/utils/labels.ts +23 -0
  30. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +60 -0
  31. package/src/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +22 -0
  32. package/src/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +43 -0
  33. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{assets → features/fileupload/assets}/icon-image-close.svg +0 -0
  34. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{assets → features/fileupload/assets}/image.svg +0 -0
  35. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{assets → features/fileupload/assets}/success.svg +0 -0
  36. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{assets → features/fileupload/assets}/symbols.svg +0 -0
  37. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{components → features/fileupload/components}/FileUpload.tsx +0 -0
  38. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{components → features/fileupload/components}/FileUploadDropZone.tsx +0 -0
  39. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{components → features/fileupload/components}/FileUploadIcons.tsx +0 -0
  40. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{hooks → features/fileupload/hooks}/useFileUpload.ts +0 -0
  41. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{hooks → features/fileupload/hooks}/useFileUploadDialog.ts +0 -0
  42. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{pages → features/fileupload/pages}/UploadTest.tsx +0 -0
  43. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{types → features/fileupload/types}/fileUpload.ts +0 -0
  44. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{utils → features/fileupload/utils}/fileUploadUtils.ts +0 -0
  45. /package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/{utils → features/fileupload/utils}/labels.ts +0 -0
package/README.md CHANGED
@@ -1,266 +1,252 @@
1
- # Feature: File Upload
1
+ # Feature: File Upload API
2
2
 
3
- File upload feature: use the **FileUpload** component as-is with out-of-the-box UI, or build a **custom UI** with the `useFileUpload` hook (custom trigger, drop zone, or both).
3
+ File upload API package that provides programmatic APIs for uploading files to Salesforce with progress tracking and ContentVersion integration. This package serves as a **reference implementation** with APIs that allow you to **build your own custom UI**.
4
4
 
5
- ## Exports
6
-
7
- ### Components & Hooks
8
-
9
- - **FileUpload** – Component with OOTB drop zone, dialog, and progress. Props: `accept?`, `multiple?`, `recordId?`, `onUploadComplete?`, `onUploadError?`, `className?`, `dropZoneClassName?`, `formatHint?`, `maxFileSize?`.
10
- - **useFileUpload** – Headless hook for custom UIs. Returns `getInputProps`, `openFilePicker`, `fileItems`, `getDropZoneProps`, `cancelFile`, `reset`, etc.
11
- - **UseFileUploadOptions** – Type for the `useFileUpload` options.
12
-
13
- ### API Functions
14
-
15
- - **createContentVersion** – Manually create a ContentVersion record from a contentBodyId.
16
-
17
- ## Agent Configuration Guide
5
+ ## Package Purpose
18
6
 
19
- When implementing the FileUpload component, agents should prompt users for the following properties to configure the component appropriately:
7
+ This package exposes APIs only - **no components or hooks are exported**. You can:
20
8
 
21
- ### Required Information
9
+ - Use the `upload()` API to handle the complete upload flow
10
+ - Build your own custom UI with full control over the user experience
11
+ - Track upload progress for each file in real-time
12
+ - Optionally create ContentVersion records linked to Salesforce records
22
13
 
23
- 1. **Multiple File Selection** (`multiple`):
24
- - Prompt: "Should users be able to upload multiple files at once?"
25
- - Type: Boolean (true/false)
26
- - Default: false (single file only)
14
+ The included components in the source code are for demonstration and testing purposes only.
27
15
 
28
- 2. **Upload Context** (`recordId`):
29
- - Prompt: "Do you want to link uploaded files to a specific Salesforce record?"
30
- - Type: String (Salesforce record ID) or omit
31
- - Note: If provided, creates ContentVersion and links to record immediately. If omitted, only returns `contentBodyId`.
32
- - **Record Form Pattern**: If used in a record creation form where the record doesn't exist yet, omit `recordId` during upload, then use `createContentVersion` with the `contentBodyId` and the newly created record ID after form submission.
33
-
34
- 3. **Completion Handler** (`onUploadComplete`):
35
- - Prompt: "What should happen after files are successfully uploaded?"
36
- - Type: Function that receives array of uploaded files
37
- - Required properties to handle: `name`, `size`, `contentBodyId`, `contentVersionId`
16
+ ## Exports
38
17
 
39
- ### Optional Configuration
18
+ ### API Functions
40
19
 
41
- 4. **File Type Restrictions** (`accept`):
42
- - Prompt: "What file types should be accepted? (e.g., 'image/_', '.pdf', 'image/_,.pdf')"
43
- - Type: String (MIME types or file extensions)
44
- - Default: All file types accepted
20
+ - **upload** Unified upload API that handles config, upload with progress tracking, and optionally creates ContentVersion records
21
+ - **createContentVersion** Manually create a ContentVersion record from a contentBodyId
22
+ - **getCurrentUserId** Get the current user's Salesforce ID
45
23
 
46
- 5. **File Size Limit** (`maxFileSize`):
47
- - Prompt: "What is the maximum file size in MB?"
48
- - Type: Number (in megabytes)
49
- - Default: 2048 MB (2 GB)
24
+ ### Types
50
25
 
51
- 6. **User Guidance** (`formatHint`):
52
- - Prompt: "What hint text should be displayed to users? (e.g., 'JPEG, PNG, PDF, up to 50MB')"
53
- - Type: String
54
- - Default: None
26
+ - **UploadOptions** – Configuration options for the upload function
27
+ - **FileUploadResult** Result object containing file info and IDs
28
+ - **FileUploadProgress** – Progress callback data for tracking upload status
29
+ - **UploadStatus** – Upload status enum: "pending" | "uploading" | "processing" | "success" | "error"
55
30
 
56
- 7. **Styling** (`dropZoneClassName`):
57
- - Prompt: "Do you need custom CSS classes for the drop zone?"
58
- - Type: String (CSS class names)
59
- - Default: Standard styling
31
+ ## Usage Guide
60
32
 
61
- 8. **Error Handling** (`onUploadError`):
62
- - Prompt: "How should upload errors be handled?"
63
- - Type: Function that receives error information
64
- - Default: Standard error display
33
+ ### Basic Upload with Progress Tracking
65
34
 
66
- 9. **Container Styling** (`className`):
67
- - Prompt: "Do you need custom CSS classes for the container?"
68
- - Type: String (CSS class names)
69
- - Default: Standard styling
35
+ Upload files to Salesforce with real-time progress tracking for each file:
70
36
 
71
- ### Example Agent Flow
37
+ ```tsx
38
+ import { upload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
39
+
40
+ async function handleUpload(files: File[]) {
41
+ const results = await upload({
42
+ files,
43
+ onProgress: (progress) => {
44
+ console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
45
+ // Update your UI with progress.status and progress.progress
46
+ },
47
+ });
72
48
 
73
- ```
74
- Agent: "I'll help you set up the FileUpload component. Let me ask you a few questions:"
75
-
76
- 1. "Should users be able to upload multiple files at once?" → User: "Yes"
77
- 2. "Do you want to link uploaded files to a specific Salesforce record?" → User: "Yes, to an Account record"
78
- 3. "What file types should be accepted?" → User: "Images and PDFs only"
79
- 4. "What is the maximum file size in MB?" → User: "50 MB"
80
- 5. "What hint text should be displayed to users?" → User: "JPEG, PNG, PDF, up to 50MB"
81
- 6. "How should files be handled after upload?" → User: "Log them to console"
82
-
83
- Agent generates:
84
- <FileUpload
85
- multiple
86
- accept="image/*,.pdf"
87
- recordId={accountId}
88
- maxFileSize={50}
89
- formatHint="JPEG, PNG, PDF, up to 50MB"
90
- onUploadComplete={(files) => console.log("Uploaded:", files)}
91
- />
49
+ console.log("Upload complete:", results);
50
+ // results[0].contentBodyId: "069..." (always available)
51
+ // results[0].contentVersionId: undefined (no record linked)
52
+ }
92
53
  ```
93
54
 
94
- ## Usage
55
+ ### Upload with Record Linking
95
56
 
96
- ### Option 1: FileUpload component (OOTB UI)
97
-
98
- Use the component as-is for a ready-made drop zone, dialog, and progress display.
57
+ Link uploaded files to a Salesforce record immediately by providing `recordId`:
99
58
 
100
59
  ```tsx
101
- import { FileUpload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
102
-
103
- <FileUpload
104
- multiple
105
- accept="image/*,.pdf"
106
- recordId={accountId} // Creates ContentVersion and links to this record
107
- maxFileSize={50} // 50 MB limit
108
- formatHint="JPEG, PNG, PDF, up to 50MB"
109
- onUploadComplete={(files) => {
110
- // files[0].contentBodyId: "069..."
111
- // files[0].contentVersionId: "068..."
112
- console.log("Uploaded:", files);
113
- }}
114
- />;
115
- ```
116
-
117
- ### Option 2: Upload file only (no ContentVersion)
118
-
119
- Omit `recordId` to only upload the file and get the `contentBodyId`:
60
+ import { upload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
61
+
62
+ async function uploadToRecord(files: File[], recordId: string) {
63
+ const results = await upload({
64
+ files,
65
+ recordId, // Links files to this record (Account, Opportunity, etc.)
66
+ onProgress: (progress) => {
67
+ // Track progress: pending uploading → processing → success
68
+ updateUI(progress.fileName, progress.status, progress.progress);
69
+ },
70
+ });
120
71
 
121
- ```tsx
122
- <FileUpload
123
- multiple
124
- onUploadComplete={(files) => {
125
- // Only contentBodyId is returned, no ContentVersion created
126
- console.log("Content Body ID:", files[0].contentBodyId);
127
- console.log("Content Version ID:", files[0].contentVersionId); // undefined
128
- }}
129
- />
72
+ // Both contentBodyId and contentVersionId are available
73
+ console.log("Uploaded to record:", results);
74
+ }
130
75
  ```
131
76
 
132
- ### Option 3: Upload files before record exists (Record Form pattern)
77
+ ### Deferred Record Linking (Record Creation Pattern)
133
78
 
134
- When using FileUpload in a record creation form, omit `recordId` during upload, then create ContentVersion after the form is submitted and the record ID is available:
79
+ **When creating a new record**: Upload files first without `recordId`, then link them after the record is created:
135
80
 
136
81
  ```tsx
137
82
  import {
138
- FileUpload,
83
+ upload,
139
84
  createContentVersion,
140
85
  } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
141
86
 
142
- function CreateRecordForm() {
143
- const [uploadedFiles, setUploadedFiles] = useState([]);
87
+ async function createRecordWithFiles(formData: any, files: File[]) {
88
+ // Step 1: Upload files (no recordId yet)
89
+ const uploadResults = await upload({
90
+ files,
91
+ onProgress: (progress) => console.log(progress),
92
+ });
144
93
 
145
- const handleFileUploadComplete = (files) => {
146
- // Store contentBodyId for later use
147
- setUploadedFiles(files);
148
- console.log("Files uploaded, contentBodyIds stored:", files);
149
- };
94
+ // Step 2: Create the record
95
+ const newRecordId = await createRecord(formData);
96
+
97
+ // Step 3: Link uploaded files to the new record
98
+ for (const file of uploadResults) {
99
+ const contentVersionId = await createContentVersion(
100
+ new File([""], file.fileName),
101
+ file.contentBodyId,
102
+ newRecordId,
103
+ );
104
+ console.log("ContentVersion created:", contentVersionId);
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### Cancel Upload
110
+
111
+ Use an AbortController to cancel uploads:
150
112
 
151
- const handleFormSubmit = async (formData) => {
152
- // 1. Create the record first
153
- const newRecordId = await createRecord(formData);
154
-
155
- // 2. Now link uploaded files to the newly created record
156
- for (const file of uploadedFiles) {
157
- const contentVersionId = await createContentVersion(
158
- new File([""], file.name),
159
- file.contentBodyId,
160
- newRecordId, // Use the newly created record ID
161
- );
162
- console.log("ContentVersion created:", contentVersionId);
113
+ ```tsx
114
+ import { upload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
115
+
116
+ function UploadComponent() {
117
+ const abortController = new AbortController();
118
+
119
+ const handleUpload = async (files: File[]) => {
120
+ try {
121
+ await upload({
122
+ files,
123
+ signal: abortController.signal,
124
+ onProgress: (progress) => console.log(progress),
125
+ });
126
+ } catch (error) {
127
+ console.error("Upload failed or cancelled:", error);
163
128
  }
164
129
  };
165
130
 
166
- return (
167
- <form onSubmit={handleFormSubmit}>
168
- {/* Form fields */}
169
- <input name="name" placeholder="Record Name" />
170
-
171
- {/* File upload without recordId */}
172
- <FileUpload multiple onUploadComplete={handleFileUploadComplete} />
131
+ const cancelUpload = () => {
132
+ abortController.abort();
133
+ };
173
134
 
174
- <button type="submit">Create Record</button>
175
- </form>
135
+ return (
136
+ <div>
137
+ <button onClick={() => handleUpload(selectedFiles)}>Upload</button>
138
+ <button onClick={cancelUpload}>Cancel</button>
139
+ </div>
176
140
  );
177
141
  }
178
142
  ```
179
143
 
180
- ### Option 4: Manual ContentVersion creation
144
+ ## API Reference
181
145
 
182
- Upload file first, then manually create ContentVersion:
146
+ ### upload(options)
183
147
 
184
- ```tsx
185
- import {
186
- FileUpload,
187
- createContentVersion,
188
- getCurrentUserId,
189
- } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
148
+ Unified upload API that handles the complete upload flow with progress tracking.
190
149
 
191
- function MyComponent() {
192
- const handleComplete = async (files) => {
193
- const userId = await getCurrentUserId();
194
-
195
- for (const file of files) {
196
- const contentVersionId = await createContentVersion(
197
- new File([""], file.name),
198
- file.contentBodyId,
199
- userId,
200
- );
201
- console.log("Created ContentVersion:", contentVersionId);
202
- }
203
- };
150
+ **Parameters:**
151
+
152
+ ```typescript
153
+ interface UploadOptions {
154
+ /** Files to upload */
155
+ files: File[];
156
+
157
+ /**
158
+ * Record ID to link files to (FirstPublishLocationId).
159
+ * - If provided: Creates ContentVersion and links to this record
160
+ * - If null/undefined: Only uploads file, returns contentBodyId only
161
+ */
162
+ recordId?: string | null;
163
+
164
+ /** Callback for upload progress of each file */
165
+ onProgress?: (progress: FileUploadProgress) => void;
166
+
167
+ /** Optional abort signal to cancel all uploads */
168
+ signal?: AbortSignal;
169
+ }
204
170
 
205
- return <FileUpload onUploadComplete={handleComplete} />;
171
+ interface FileUploadProgress {
172
+ fileName: string;
173
+ status: "pending" | "uploading" | "processing" | "success" | "error";
174
+ progress: number; // 0-100 for uploading, 0 for other states
175
+ error?: string;
206
176
  }
207
177
  ```
208
178
 
209
- ### Option 5: Custom UI with your own trigger
179
+ **Returns:** `Promise<FileUploadResult[]>`
210
180
 
211
- Use the `useFileUpload` hook to control when the file picker opens. Render a hidden input and your own button or action.
181
+ ```typescript
182
+ interface FileUploadResult {
183
+ fileName: string;
184
+ size: number;
185
+ contentBodyId: string; // Always available
186
+ contentVersionId?: string; // Only if recordId was provided
187
+ }
188
+ ```
189
+
190
+ ### createContentVersion(file, contentBodyId, recordId)
191
+
192
+ Manually create a ContentVersion record from a previously uploaded file.
193
+
194
+ **Parameters:**
195
+
196
+ - `file` (File): The file object (used for metadata like name)
197
+ - `contentBodyId` (string): The ContentBody ID from a previous upload
198
+ - `recordId` (string): The record ID for FirstPublishLocationId
199
+
200
+ **Returns:** `Promise<string | undefined>` - The ContentVersion ID if successful
212
201
 
213
202
  ```tsx
214
- import {
215
- useFileUpload,
216
- type UseFileUploadOptions,
217
- } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
203
+ const contentVersionId = await createContentVersion(file, "069xx000000xxxx", "001xx000000yyyy");
204
+ ```
218
205
 
219
- function CustomUploadButton() {
220
- const { getInputProps, openFilePicker } = useFileUpload({
221
- multiple: true,
222
- maxFileSize: 50, // 50 MB limit
223
- onUploadComplete: (files) => console.log("Uploaded:", files),
224
- });
206
+ ### getCurrentUserId()
225
207
 
226
- return (
227
- <>
228
- <input {...getInputProps()} style={{ display: "none" }} />
229
- <button type="button" onClick={openFilePicker}>
230
- Upload files
231
- </button>
232
- </>
233
- );
234
- }
208
+ Get the current user's Salesforce ID.
209
+
210
+ **Returns:** `Promise<string>` - The current user ID
211
+
212
+ ```tsx
213
+ const userId = await getCurrentUserId();
235
214
  ```
236
215
 
237
- ### Option 6: Custom UI with drop zone and progress
216
+ ## Building Your Own UI
238
217
 
239
- Build a custom drop zone and inline progress list (no dialog) using `getDropZoneProps` and `fileItems`:
218
+ This package provides the backend APIs - you can build any UI you want. Here's a basic example:
240
219
 
241
220
  ```tsx
242
- import { useFileUpload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
243
-
244
- function MyUpload() {
245
- const { fileItems, getInputProps, getDropZoneProps, reset } = useFileUpload({
246
- multiple: true,
247
- recordId: accountId,
248
- maxFileSize: 100, // 100 MB limit
249
- onUploadComplete: (files) => console.log("Uploaded files:", files),
250
- });
221
+ import {
222
+ upload,
223
+ type FileUploadProgress,
224
+ } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
225
+ import { useState } from "react";
251
226
 
252
- const input = getInputProps();
253
- const drop = getDropZoneProps();
227
+ function CustomFileUpload({ recordId }: { recordId?: string }) {
228
+ const [progress, setProgress] = useState<Map<string, FileUploadProgress>>(new Map());
229
+
230
+ const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
231
+ const files = Array.from(event.target.files || []);
232
+
233
+ await upload({
234
+ files,
235
+ recordId,
236
+ onProgress: (fileProgress) => {
237
+ setProgress((prev) => new Map(prev).set(fileProgress.fileName, fileProgress));
238
+ },
239
+ });
240
+ };
254
241
 
255
242
  return (
256
243
  <div>
257
- <div {...drop}>
258
- <input {...input} style={{ display: "none" }} />
259
- Drop files here
260
- </div>
261
- {fileItems.map((item) => (
262
- <div key={item.file.name}>
263
- {item.file.name}: {item.state} {item.progress}%
244
+ <input type="file" multiple onChange={handleFileSelect} />
245
+
246
+ {Array.from(progress.entries()).map(([fileName, fileProgress]) => (
247
+ <div key={fileName}>
248
+ {fileName}: {fileProgress.status} - {fileProgress.progress}%
249
+ {fileProgress.error && <span>Error: {fileProgress.error}</span>}
264
250
  </div>
265
251
  ))}
266
252
  </div>
@@ -268,38 +254,78 @@ function MyUpload() {
268
254
  }
269
255
  ```
270
256
 
271
- ## API Reference
257
+ ## Agent/Vibe Integration Guide
272
258
 
273
- ### createContentVersion(file, contentBodyId, recordId)
259
+ When building file upload functionality with an AI agent like Vibe, follow this pattern:
274
260
 
275
- Manually create a ContentVersion record from a previously uploaded file.
261
+ ### 1. Ask User for Record Context
276
262
 
277
- **Parameters:**
263
+ ```
264
+ Agent: "Do you want to link uploaded files to a specific record, or upload them first and link later?"
278
265
 
279
- - `file` (File): The file object (used for metadata like name)
280
- - `contentBodyId` (string): The ContentBody ID from a previous upload
281
- - `recordId` (string): The record ID for FirstPublishLocationId (ex: Salesforce record id to attach file)
266
+ Options:
267
+ A) Link to existing record (provide recordId)
268
+ B) Upload only (link later with createContentVersion)
269
+ C) Link to current user
270
+ ```
282
271
 
283
- **Returns:** `Promise<string | undefined>` - The ContentVersion ID if successful
272
+ ### 2. Implement Based on Response
273
+
274
+ **Option A: Link to existing record**
284
275
 
285
276
  ```tsx
286
- const contentVersionId = await createContentVersion(file, "069xx000000xxxx", "005xx000000yyyy");
277
+ await upload({ files, recordId: "001xx000000yyyy" });
287
278
  ```
288
279
 
289
- ## Dependencies
280
+ **Option B: Upload only, link later**
281
+
282
+ ```tsx
283
+ const results = await upload({ files }); // No recordId
284
+ // Later: await createContentVersion(file, results[0].contentBodyId, newRecordId);
285
+ ```
286
+
287
+ **Option C: Link to current user**
290
288
 
291
- This feature depends on:
289
+ ```tsx
290
+ const userId = await getCurrentUserId();
291
+ await upload({ files, recordId: userId });
292
+ ```
293
+
294
+ ### 3. Track Progress in Your UI
295
+
296
+ Always provide progress feedback to users:
297
+
298
+ ```tsx
299
+ onProgress: (progress) => {
300
+ // Update your UI based on:
301
+ // - progress.fileName
302
+ // - progress.status (pending, uploading, processing, success, error)
303
+ // - progress.progress (0-100)
304
+ // - progress.error (if status is 'error')
305
+ };
306
+ ```
307
+
308
+ ## Reference Implementation
309
+
310
+ This package includes a reference implementation with UI components for demonstration purposes. The components are located in `src/features/fileupload/` and include:
311
+
312
+ - FileUpload component with drop zone and progress dialog
313
+ - useFileUpload hook for custom implementations
314
+ - Test pages showing various usage patterns
315
+
316
+ These are **not exported** from the package but can be viewed as examples of how to build your own UI using the APIs.
317
+
318
+ ## Dependencies
292
319
 
293
- - **base-react-app** (built-in) – UI components (Button, Dialog, etc.) via shadcn are included in the base React app.
294
320
  - **@salesforce/webapp-experimental** – For API client and Salesforce integration
321
+ - **@salesforce/sdk-data** – For data SDK integration
295
322
 
296
- ## Build & Testing
323
+ ## Testing
297
324
 
298
- You can test the extension of the base app and the components via the following:
325
+ Test the reference implementation locally:
299
326
 
300
327
  ```bash
301
- npx nx build
302
- npx nx start
328
+ npx nx dev @salesforce/webapp-template-feature-react-file-upload-experimental
303
329
  ```
304
330
 
305
- Open the app and navigate to the **Upload Test** page to test the FileUpload component with manual ContentVersion creation.
331
+ Open the app and navigate to the test pages to see the APIs in action.
package/dist/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.90.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.90.1...v1.90.2) (2026-03-11)
7
+
8
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
9
+
10
+
11
+
12
+
13
+
6
14
  ## [1.90.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.90.0...v1.90.1) (2026-03-11)
7
15
 
8
16
  **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
@@ -15,8 +15,8 @@
15
15
  "graphql:schema": "node scripts/get-graphql-schema.mjs"
16
16
  },
17
17
  "dependencies": {
18
- "@salesforce/sdk-data": "^1.90.1",
19
- "@salesforce/webapp-experimental": "^1.90.1",
18
+ "@salesforce/sdk-data": "^1.90.2",
19
+ "@salesforce/webapp-experimental": "^1.90.2",
20
20
  "@tailwindcss/vite": "^4.1.17",
21
21
  "@tanstack/react-form": "^1.28.4",
22
22
  "class-variance-authority": "^0.7.1",
@@ -40,7 +40,7 @@
40
40
  "@graphql-eslint/eslint-plugin": "^4.1.0",
41
41
  "@graphql-tools/utils": "^11.0.0",
42
42
  "@playwright/test": "^1.49.0",
43
- "@salesforce/vite-plugin-webapp-experimental": "^1.90.1",
43
+ "@salesforce/vite-plugin-webapp-experimental": "^1.90.2",
44
44
  "@testing-library/jest-dom": "^6.6.3",
45
45
  "@testing-library/react": "^16.1.0",
46
46
  "@testing-library/user-event": "^14.5.2",