@salesforce/webapp-template-feature-react-file-upload-experimental 1.60.1 → 1.61.0
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.
- package/README.md +97 -11
- package/dist/CHANGELOG.md +16 -0
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/package-lock.json +61 -44
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +4 -12
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +20 -3
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +2 -2
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +48 -40
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +1 -1
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +2 -2
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +36 -23
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +18 -0
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/pages/Home.tsx +10 -23
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/pages/UploadTest.tsx +56 -0
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +8 -3
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +2 -0
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +12 -2
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +2 -0
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +2 -1
- package/dist/package.json +1 -1
- package/package.json +3 -3
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +4 -12
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +20 -3
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +2 -2
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +48 -40
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +1 -1
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +2 -2
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/__inherit__button.tsx +45 -0
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/__inherit__dialog.tsx +102 -0
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +36 -23
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +18 -0
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/pages/UploadTest.tsx +56 -0
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +8 -3
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +2 -0
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +12 -2
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +2 -0
- package/src/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +2 -1
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/pages/Home.tsx +0 -25
package/README.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
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).
|
|
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
|
+
### FileUpload component Props
|
|
18
|
+
|
|
19
|
+
- **`recordId`** (optional): When provided, creates ContentVersion and links to this record. When omitted, only uploads file and returns `contentBodyId` without creating ContentVersion.
|
|
20
|
+
- **`maxFileSize`** (optional): Maximum file size in MB. Files exceeding this limit are rejected. Default: 2 GB.
|
|
21
|
+
- **`dropZoneClassName`** (optional): CSS classes for the drop zone (e.g., `"h-full"` for flex layouts).
|
|
22
|
+
- **`formatHint`** (optional): Text hint shown in drop zone (e.g., "JPEG, PNG, PDF, up to 50MB").
|
|
23
|
+
- **`onUploadComplete`**: Receives array of uploaded files with `name`, `size`, `contentBodyId`, and `contentVersionId` (if ContentVersion was created).
|
|
24
|
+
|
|
5
25
|
## Usage
|
|
6
26
|
|
|
7
27
|
### Option 1: FileUpload component (OOTB UI)
|
|
@@ -14,11 +34,62 @@ import { FileUpload } from "@salesforce/webapp-template-feature-react-file-uploa
|
|
|
14
34
|
<FileUpload
|
|
15
35
|
multiple
|
|
16
36
|
accept="image/*,.pdf"
|
|
17
|
-
|
|
37
|
+
recordId={accountId} // Creates ContentVersion and links to this record
|
|
38
|
+
maxFileSize={50} // 50 MB limit
|
|
39
|
+
formatHint="JPEG, PNG, PDF, up to 50MB"
|
|
40
|
+
onUploadComplete={(files) => {
|
|
41
|
+
// files[0].contentBodyId: "069..."
|
|
42
|
+
// files[0].contentVersionId: "068..."
|
|
43
|
+
console.log("Uploaded:", files);
|
|
44
|
+
}}
|
|
18
45
|
/>;
|
|
19
46
|
```
|
|
20
47
|
|
|
21
|
-
### Option 2:
|
|
48
|
+
### Option 2: Upload file only (no ContentVersion)
|
|
49
|
+
|
|
50
|
+
Omit `recordId` to only upload the file and get the `contentBodyId`:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<FileUpload
|
|
54
|
+
multiple
|
|
55
|
+
onUploadComplete={(files) => {
|
|
56
|
+
// Only contentBodyId is returned, no ContentVersion created
|
|
57
|
+
console.log("Content Body ID:", files[0].contentBodyId);
|
|
58
|
+
console.log("Content Version ID:", files[0].contentVersionId); // undefined
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Option 3: Manual ContentVersion creation
|
|
64
|
+
|
|
65
|
+
Upload file first, then manually create ContentVersion:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import {
|
|
69
|
+
FileUpload,
|
|
70
|
+
createContentVersion,
|
|
71
|
+
getCurrentUserId,
|
|
72
|
+
} from "@salesforce/webapp-template-feature-react-file-upload-experimental";
|
|
73
|
+
|
|
74
|
+
function MyComponent() {
|
|
75
|
+
const handleComplete = async (files) => {
|
|
76
|
+
const userId = await getCurrentUserId();
|
|
77
|
+
|
|
78
|
+
for (const file of files) {
|
|
79
|
+
const contentVersionId = await createContentVersion(
|
|
80
|
+
new File([""], file.name),
|
|
81
|
+
file.contentBodyId,
|
|
82
|
+
userId,
|
|
83
|
+
);
|
|
84
|
+
console.log("Created ContentVersion:", contentVersionId);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return <FileUpload onUploadComplete={handleComplete} />;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Option 4: Custom UI with your own trigger
|
|
22
93
|
|
|
23
94
|
Use the `useFileUpload` hook to control when the file picker opens. Render a hidden input and your own button or action.
|
|
24
95
|
|
|
@@ -31,6 +102,7 @@ import {
|
|
|
31
102
|
function CustomUploadButton() {
|
|
32
103
|
const { getInputProps, openFilePicker } = useFileUpload({
|
|
33
104
|
multiple: true,
|
|
105
|
+
maxFileSize: 50, // 50 MB limit
|
|
34
106
|
onUploadComplete: (files) => console.log("Uploaded:", files),
|
|
35
107
|
});
|
|
36
108
|
|
|
@@ -45,7 +117,7 @@ function CustomUploadButton() {
|
|
|
45
117
|
}
|
|
46
118
|
```
|
|
47
119
|
|
|
48
|
-
### Option
|
|
120
|
+
### Option 5: Custom UI with drop zone and progress
|
|
49
121
|
|
|
50
122
|
Build a custom drop zone and inline progress list (no dialog) using `getDropZoneProps` and `fileItems`:
|
|
51
123
|
|
|
@@ -55,6 +127,8 @@ import { useFileUpload } from "@salesforce/webapp-template-feature-react-file-up
|
|
|
55
127
|
function MyUpload() {
|
|
56
128
|
const { fileItems, getInputProps, getDropZoneProps, reset } = useFileUpload({
|
|
57
129
|
multiple: true,
|
|
130
|
+
recordId: accountId,
|
|
131
|
+
maxFileSize: 100, // 100 MB limit
|
|
58
132
|
onUploadComplete: (files) => console.log("Uploaded files:", files),
|
|
59
133
|
});
|
|
60
134
|
|
|
@@ -77,17 +151,29 @@ function MyUpload() {
|
|
|
77
151
|
}
|
|
78
152
|
```
|
|
79
153
|
|
|
80
|
-
##
|
|
154
|
+
## API Reference
|
|
81
155
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
156
|
+
### createContentVersion(file, contentBodyId, recordId)
|
|
157
|
+
|
|
158
|
+
Manually create a ContentVersion record from a previously uploaded file.
|
|
159
|
+
|
|
160
|
+
**Parameters:**
|
|
161
|
+
|
|
162
|
+
- `file` (File): The file object (used for metadata like name)
|
|
163
|
+
- `contentBodyId` (string): The ContentBody ID from a previous upload
|
|
164
|
+
- `recordId` (string): The record ID for FirstPublishLocationId (ex: Salesforce record id to attach file)
|
|
165
|
+
|
|
166
|
+
**Returns:** `Promise<string | undefined>` - The ContentVersion ID if successful
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
const contentVersionId = await createContentVersion(file, "069xx000000xxxx", "005xx000000yyyy");
|
|
170
|
+
```
|
|
85
171
|
|
|
86
172
|
## Dependencies
|
|
87
173
|
|
|
88
174
|
This feature depends on:
|
|
89
175
|
|
|
90
|
-
-
|
|
176
|
+
- **@salesforce/webapp-template-feature-react-shadcn-experimental** – For UI components (Button, Dialog, etc.)
|
|
91
177
|
- **@salesforce/webapp-experimental** – For API client and Salesforce integration
|
|
92
178
|
|
|
93
179
|
## Build & Testing
|
|
@@ -95,8 +181,8 @@ This feature depends on:
|
|
|
95
181
|
You can test the extension of the base app and the components via the following:
|
|
96
182
|
|
|
97
183
|
```bash
|
|
98
|
-
|
|
99
|
-
|
|
184
|
+
npx nx build
|
|
185
|
+
npx nx start
|
|
100
186
|
```
|
|
101
187
|
|
|
102
|
-
Open the app and
|
|
188
|
+
Open the app and navigate to the **Upload Test** page to test the FileUpload component with manual ContentVersion creation.
|
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.61.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.60.2...v1.61.0) (2026-03-02)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [1.60.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.60.1...v1.60.2) (2026-02-27)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## [1.60.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.60.0...v1.60.1) (2026-02-27)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
package/dist/force-app/main/default/webapplications/feature-react-file-upload/package-lock.json
CHANGED
|
@@ -6071,19 +6071,19 @@
|
|
|
6071
6071
|
}
|
|
6072
6072
|
},
|
|
6073
6073
|
"node_modules/@salesforce/sdk-core": {
|
|
6074
|
-
"version": "1.60.
|
|
6075
|
-
"resolved": "https://registry.npmjs.org/@salesforce/sdk-core/-/sdk-core-1.60.
|
|
6076
|
-
"integrity": "sha512-
|
|
6074
|
+
"version": "1.60.2",
|
|
6075
|
+
"resolved": "https://registry.npmjs.org/@salesforce/sdk-core/-/sdk-core-1.60.2.tgz",
|
|
6076
|
+
"integrity": "sha512-XZJYBxJwHPbahfyL86FI2ChYT7is+ZoiM1M4lTNZtt9me7j9AhMTVf7kpIbAX6ye1/ROEaLlAz9TAdCQMll2Rg==",
|
|
6077
6077
|
"license": "SEE LICENSE IN LICENSE.txt"
|
|
6078
6078
|
},
|
|
6079
6079
|
"node_modules/@salesforce/sdk-data": {
|
|
6080
|
-
"version": "1.60.
|
|
6081
|
-
"resolved": "https://registry.npmjs.org/@salesforce/sdk-data/-/sdk-data-1.60.
|
|
6082
|
-
"integrity": "sha512-
|
|
6080
|
+
"version": "1.60.2",
|
|
6081
|
+
"resolved": "https://registry.npmjs.org/@salesforce/sdk-data/-/sdk-data-1.60.2.tgz",
|
|
6082
|
+
"integrity": "sha512-q4frWaRD2J3yOT81NPuq/qck8W3JfzdOmRrBvM8/aQ6RwPlxkJXwhq1PR3PgdmKWX5n49VksmxorZA23ttbdjA==",
|
|
6083
6083
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6084
6084
|
"dependencies": {
|
|
6085
6085
|
"@conduit-client/salesforce-lightning-service-worker": "^3.7.0",
|
|
6086
|
-
"@salesforce/sdk-core": "^1.60.
|
|
6086
|
+
"@salesforce/sdk-core": "^1.60.2"
|
|
6087
6087
|
}
|
|
6088
6088
|
},
|
|
6089
6089
|
"node_modules/@salesforce/ts-types": {
|
|
@@ -6096,15 +6096,15 @@
|
|
|
6096
6096
|
}
|
|
6097
6097
|
},
|
|
6098
6098
|
"node_modules/@salesforce/vite-plugin-webapp-experimental": {
|
|
6099
|
-
"version": "1.60.
|
|
6100
|
-
"resolved": "https://registry.npmjs.org/@salesforce/vite-plugin-webapp-experimental/-/vite-plugin-webapp-experimental-1.60.
|
|
6101
|
-
"integrity": "sha512
|
|
6099
|
+
"version": "1.60.2",
|
|
6100
|
+
"resolved": "https://registry.npmjs.org/@salesforce/vite-plugin-webapp-experimental/-/vite-plugin-webapp-experimental-1.60.2.tgz",
|
|
6101
|
+
"integrity": "sha512-/woKAsjTLIjrVWNCRy5PQS+I84RbBDdIvX84X0YEBF6mKrD5oQPTOw9U6ERxsVQGwux3K3vNwHyof2HmdHMIZg==",
|
|
6102
6102
|
"dev": true,
|
|
6103
6103
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6104
6104
|
"dependencies": {
|
|
6105
6105
|
"@babel/core": "^7.28.4",
|
|
6106
6106
|
"@babel/helper-plugin-utils": "^7.28.3",
|
|
6107
|
-
"@salesforce/webapp-experimental": "^1.60.
|
|
6107
|
+
"@salesforce/webapp-experimental": "^1.60.2"
|
|
6108
6108
|
},
|
|
6109
6109
|
"engines": {
|
|
6110
6110
|
"node": ">=20.0.0"
|
|
@@ -6114,13 +6114,13 @@
|
|
|
6114
6114
|
}
|
|
6115
6115
|
},
|
|
6116
6116
|
"node_modules/@salesforce/webapp-experimental": {
|
|
6117
|
-
"version": "1.60.
|
|
6118
|
-
"resolved": "https://registry.npmjs.org/@salesforce/webapp-experimental/-/webapp-experimental-1.60.
|
|
6119
|
-
"integrity": "sha512-
|
|
6117
|
+
"version": "1.60.2",
|
|
6118
|
+
"resolved": "https://registry.npmjs.org/@salesforce/webapp-experimental/-/webapp-experimental-1.60.2.tgz",
|
|
6119
|
+
"integrity": "sha512-n96PoDAVdFt1G9fJezcor0r/WMnS8UktvqBsOEetp/v9NxhZISUJo5fQUgl3mW876FnILaCyV1muxkKKXuH7/A==",
|
|
6120
6120
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6121
6121
|
"dependencies": {
|
|
6122
6122
|
"@salesforce/core": "^8.23.4",
|
|
6123
|
-
"@salesforce/sdk-data": "^1.60.
|
|
6123
|
+
"@salesforce/sdk-data": "^1.60.2",
|
|
6124
6124
|
"axios": "^1.7.7",
|
|
6125
6125
|
"micromatch": "^4.0.8",
|
|
6126
6126
|
"path-to-regexp": "^8.3.0"
|
|
@@ -6755,9 +6755,9 @@
|
|
|
6755
6755
|
"license": "MIT"
|
|
6756
6756
|
},
|
|
6757
6757
|
"node_modules/@types/node": {
|
|
6758
|
-
"version": "24.
|
|
6759
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.
|
|
6760
|
-
"integrity": "sha512-
|
|
6758
|
+
"version": "24.11.0",
|
|
6759
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.11.0.tgz",
|
|
6760
|
+
"integrity": "sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==",
|
|
6761
6761
|
"devOptional": true,
|
|
6762
6762
|
"license": "MIT",
|
|
6763
6763
|
"dependencies": {
|
|
@@ -8206,9 +8206,9 @@
|
|
|
8206
8206
|
}
|
|
8207
8207
|
},
|
|
8208
8208
|
"node_modules/caniuse-lite": {
|
|
8209
|
-
"version": "1.0.
|
|
8210
|
-
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.
|
|
8211
|
-
"integrity": "sha512-
|
|
8209
|
+
"version": "1.0.30001775",
|
|
8210
|
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001775.tgz",
|
|
8211
|
+
"integrity": "sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==",
|
|
8212
8212
|
"funding": [
|
|
8213
8213
|
{
|
|
8214
8214
|
"type": "opencollective",
|
|
@@ -8378,14 +8378,14 @@
|
|
|
8378
8378
|
}
|
|
8379
8379
|
},
|
|
8380
8380
|
"node_modules/cli-truncate": {
|
|
8381
|
-
"version": "5.
|
|
8382
|
-
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.
|
|
8383
|
-
"integrity": "sha512-
|
|
8381
|
+
"version": "5.2.0",
|
|
8382
|
+
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
|
|
8383
|
+
"integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
|
|
8384
8384
|
"dev": true,
|
|
8385
8385
|
"license": "MIT",
|
|
8386
8386
|
"dependencies": {
|
|
8387
|
-
"slice-ansi": "^
|
|
8388
|
-
"string-width": "^8.
|
|
8387
|
+
"slice-ansi": "^8.0.0",
|
|
8388
|
+
"string-width": "^8.2.0"
|
|
8389
8389
|
},
|
|
8390
8390
|
"engines": {
|
|
8391
8391
|
"node": ">=20"
|
|
@@ -8980,9 +8980,9 @@
|
|
|
8980
8980
|
"license": "MIT"
|
|
8981
8981
|
},
|
|
8982
8982
|
"node_modules/dedent": {
|
|
8983
|
-
"version": "1.7.
|
|
8984
|
-
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.
|
|
8985
|
-
"integrity": "sha512-
|
|
8983
|
+
"version": "1.7.2",
|
|
8984
|
+
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
|
|
8985
|
+
"integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
|
|
8986
8986
|
"license": "MIT",
|
|
8987
8987
|
"peerDependencies": {
|
|
8988
8988
|
"babel-plugin-macros": "^3.1.0"
|
|
@@ -9317,9 +9317,9 @@
|
|
|
9317
9317
|
}
|
|
9318
9318
|
},
|
|
9319
9319
|
"node_modules/enhanced-resolve": {
|
|
9320
|
-
"version": "5.
|
|
9321
|
-
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.
|
|
9322
|
-
"integrity": "sha512
|
|
9320
|
+
"version": "5.20.0",
|
|
9321
|
+
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
|
|
9322
|
+
"integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==",
|
|
9323
9323
|
"license": "MIT",
|
|
9324
9324
|
"dependencies": {
|
|
9325
9325
|
"graceful-fs": "^4.2.4",
|
|
@@ -13238,6 +13238,23 @@
|
|
|
13238
13238
|
"dev": true,
|
|
13239
13239
|
"license": "MIT"
|
|
13240
13240
|
},
|
|
13241
|
+
"node_modules/log-update/node_modules/slice-ansi": {
|
|
13242
|
+
"version": "7.1.2",
|
|
13243
|
+
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
|
|
13244
|
+
"integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
|
|
13245
|
+
"dev": true,
|
|
13246
|
+
"license": "MIT",
|
|
13247
|
+
"dependencies": {
|
|
13248
|
+
"ansi-styles": "^6.2.1",
|
|
13249
|
+
"is-fullwidth-code-point": "^5.0.0"
|
|
13250
|
+
},
|
|
13251
|
+
"engines": {
|
|
13252
|
+
"node": ">=18"
|
|
13253
|
+
},
|
|
13254
|
+
"funding": {
|
|
13255
|
+
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
|
13256
|
+
}
|
|
13257
|
+
},
|
|
13241
13258
|
"node_modules/log-update/node_modules/string-width": {
|
|
13242
13259
|
"version": "7.2.0",
|
|
13243
13260
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
|
@@ -14921,9 +14938,9 @@
|
|
|
14921
14938
|
"license": "MIT"
|
|
14922
14939
|
},
|
|
14923
14940
|
"node_modules/pump": {
|
|
14924
|
-
"version": "3.0.
|
|
14925
|
-
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.
|
|
14926
|
-
"integrity": "sha512-
|
|
14941
|
+
"version": "3.0.4",
|
|
14942
|
+
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
|
14943
|
+
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
|
14927
14944
|
"license": "MIT",
|
|
14928
14945
|
"dependencies": {
|
|
14929
14946
|
"end-of-stream": "^1.1.0",
|
|
@@ -15690,9 +15707,9 @@
|
|
|
15690
15707
|
"license": "MIT"
|
|
15691
15708
|
},
|
|
15692
15709
|
"node_modules/sax": {
|
|
15693
|
-
"version": "1.
|
|
15694
|
-
"resolved": "https://registry.npmjs.org/sax/-/sax-1.
|
|
15695
|
-
"integrity": "sha512-
|
|
15710
|
+
"version": "1.5.0",
|
|
15711
|
+
"resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz",
|
|
15712
|
+
"integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==",
|
|
15696
15713
|
"license": "BlueOak-1.0.0",
|
|
15697
15714
|
"engines": {
|
|
15698
15715
|
"node": ">=11.0.0"
|
|
@@ -16353,17 +16370,17 @@
|
|
|
16353
16370
|
}
|
|
16354
16371
|
},
|
|
16355
16372
|
"node_modules/slice-ansi": {
|
|
16356
|
-
"version": "
|
|
16357
|
-
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-
|
|
16358
|
-
"integrity": "sha512-
|
|
16373
|
+
"version": "8.0.0",
|
|
16374
|
+
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
|
|
16375
|
+
"integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
|
|
16359
16376
|
"dev": true,
|
|
16360
16377
|
"license": "MIT",
|
|
16361
16378
|
"dependencies": {
|
|
16362
|
-
"ansi-styles": "^6.2.
|
|
16363
|
-
"is-fullwidth-code-point": "^5.
|
|
16379
|
+
"ansi-styles": "^6.2.3",
|
|
16380
|
+
"is-fullwidth-code-point": "^5.1.0"
|
|
16364
16381
|
},
|
|
16365
16382
|
"engines": {
|
|
16366
|
-
"node": ">=
|
|
16383
|
+
"node": ">=20"
|
|
16367
16384
|
},
|
|
16368
16385
|
"funding": {
|
|
16369
16386
|
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
package/dist/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts
CHANGED
|
@@ -3,16 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { createRecord, getCurrentUser } from "@salesforce/webapp-experimental/api";
|
|
6
|
-
import { createDataSDK, type DataSDK } from "@salesforce/sdk-data";
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
let sdkInstance: DataSDK | null = null;
|
|
10
|
-
async function getSDK() {
|
|
11
|
-
if (!sdkInstance) {
|
|
12
|
-
sdkInstance = await createDataSDK();
|
|
13
|
-
}
|
|
14
|
-
return sdkInstance;
|
|
15
|
-
}
|
|
7
|
+
import { getDataSDK } from "@salesforce/sdk-data";
|
|
16
8
|
|
|
17
9
|
declare const __SF_API_VERSION__: string;
|
|
18
10
|
|
|
@@ -25,7 +17,7 @@ export interface UploadConfig {
|
|
|
25
17
|
* Get upload config (token, uploadUrl) from /connect/file/upload/config.
|
|
26
18
|
*/
|
|
27
19
|
export async function getUploadConfig(): Promise<UploadConfig> {
|
|
28
|
-
const sdk = await
|
|
20
|
+
const sdk = await getDataSDK();
|
|
29
21
|
if (!sdk?.fetch) {
|
|
30
22
|
throw new Error("Failed to initialize SDK");
|
|
31
23
|
}
|
|
@@ -120,10 +112,10 @@ function parseJsonResponse(text: string): Record<string, unknown> {
|
|
|
120
112
|
export async function createContentVersion(
|
|
121
113
|
file: File,
|
|
122
114
|
contentBodyId: string,
|
|
123
|
-
|
|
115
|
+
recordId: string,
|
|
124
116
|
): Promise<string | undefined> {
|
|
125
117
|
const fields = {
|
|
126
|
-
FirstPublishLocationId:
|
|
118
|
+
FirstPublishLocationId: recordId,
|
|
127
119
|
Title: fileNameWithoutExtension(file.name),
|
|
128
120
|
PathOnClient: file.name,
|
|
129
121
|
ContentBodyId: contentBodyId,
|
|
@@ -9,13 +9,21 @@ export interface FileUploadProps {
|
|
|
9
9
|
accept?: string;
|
|
10
10
|
/** Whether to allow multiple file selection. Default: false */
|
|
11
11
|
multiple?: boolean;
|
|
12
|
-
/** Record Id for FirstPublishLocationId (e.g. Account, Opportunity). When provided, files are linked to this record.
|
|
12
|
+
/** Record Id for FirstPublishLocationId (e.g. Account, Opportunity). When provided, files are linked to this record and ContentVersion is created. When null/undefined, only uploads file and returns contentBodyId without creating ContentVersion. */
|
|
13
13
|
recordId?: string;
|
|
14
|
-
/** Called when uploads complete. Receives array of successfully uploaded files with name, size, and contentVersionId. */
|
|
15
|
-
onUploadComplete?: (
|
|
14
|
+
/** Called when uploads complete. Receives array of successfully uploaded files with name, size, contentBodyId, and contentVersionId (if ContentVersion was created). */
|
|
15
|
+
onUploadComplete?: (
|
|
16
|
+
files: { name: string; size: number; contentBodyId: string; contentVersionId?: string }[],
|
|
17
|
+
) => void;
|
|
16
18
|
onUploadError?: (file: File, error: string) => void;
|
|
17
19
|
/** Optional CSS class for the wrapper div */
|
|
18
20
|
className?: string;
|
|
21
|
+
/** Optional CSS class for the drop zone (e.g. "h-full" for flex layouts) */
|
|
22
|
+
dropZoneClassName?: string;
|
|
23
|
+
/** Optional format hint for drop zone (e.g. "JPEG, PNG, PDF, and MP4 formats, up to 50MB") */
|
|
24
|
+
formatHint?: string;
|
|
25
|
+
/** Maximum file size in MB. Files exceeding this limit are rejected with an error. Omit for default (2 GB). */
|
|
26
|
+
maxFileSize?: number;
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
/**
|
|
@@ -30,6 +38,9 @@ export function FileUpload({
|
|
|
30
38
|
onUploadComplete,
|
|
31
39
|
onUploadError,
|
|
32
40
|
className = "",
|
|
41
|
+
dropZoneClassName = "",
|
|
42
|
+
formatHint,
|
|
43
|
+
maxFileSize,
|
|
33
44
|
}: FileUploadProps) {
|
|
34
45
|
const { fileItems, getInputProps, getDropZoneProps, isDragging, reset, cancelFile, allDone } =
|
|
35
46
|
useFileUpload({
|
|
@@ -38,6 +49,7 @@ export function FileUpload({
|
|
|
38
49
|
recordId,
|
|
39
50
|
onUploadComplete,
|
|
40
51
|
onUploadError,
|
|
52
|
+
maxFileSize,
|
|
41
53
|
});
|
|
42
54
|
|
|
43
55
|
const { dialogOpen, uploadedFileNames, handleOpenChange } = useFileUploadDialog({
|
|
@@ -45,6 +57,9 @@ export function FileUpload({
|
|
|
45
57
|
reset,
|
|
46
58
|
});
|
|
47
59
|
|
|
60
|
+
const effectiveFormatHint =
|
|
61
|
+
formatHint ?? (maxFileSize != null ? `Up to ${maxFileSize}MB` : undefined);
|
|
62
|
+
|
|
48
63
|
const inputProps = getInputProps();
|
|
49
64
|
const dropZoneProps = getDropZoneProps();
|
|
50
65
|
|
|
@@ -55,6 +70,8 @@ export function FileUpload({
|
|
|
55
70
|
inputProps={inputProps}
|
|
56
71
|
dropZoneProps={dropZoneProps}
|
|
57
72
|
isDragging={isDragging}
|
|
73
|
+
formatHint={effectiveFormatHint}
|
|
74
|
+
className={dropZoneClassName}
|
|
58
75
|
/>
|
|
59
76
|
|
|
60
77
|
<FileUploadDialog
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { Button } from "./ui/button";
|
|
1
2
|
import {
|
|
2
|
-
Button,
|
|
3
3
|
Dialog,
|
|
4
4
|
DialogClose,
|
|
5
5
|
DialogContent,
|
|
6
6
|
DialogFooter,
|
|
7
7
|
DialogHeader,
|
|
8
8
|
DialogTitle,
|
|
9
|
-
} from "
|
|
9
|
+
} from "./ui/dialog";
|
|
10
10
|
import { FileUploadFileItem } from "./FileUploadFileItem";
|
|
11
11
|
import { formatUploadSummary } from "../utils/fileUploadUtils";
|
|
12
12
|
import { LABELS } from "../utils/labels";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { UtilityIcon } from "./FileUploadIcons";
|
|
2
1
|
import { LABELS } from "../utils/labels";
|
|
3
2
|
|
|
3
|
+
import { UtilityIcon } from "./FileUploadIcons";
|
|
4
|
+
|
|
4
5
|
export interface FileUploadDropZoneProps {
|
|
5
6
|
/** Props for the hidden file input (ref, type, accept, multiple, onChange) */
|
|
6
7
|
inputProps: {
|
|
@@ -20,63 +21,70 @@ export interface FileUploadDropZoneProps {
|
|
|
20
21
|
};
|
|
21
22
|
/** Whether the user is currently dragging over the drop zone */
|
|
22
23
|
isDragging: boolean;
|
|
24
|
+
/** Optional format hint (e.g. "JPEG, PNG, PDF, and MP4 formats, up to 50MB"). Defaults to LABELS.formatHint. */
|
|
25
|
+
formatHint?: string;
|
|
26
|
+
/** Optional CSS class for the drop zone (e.g. "h-full" for flex layouts) */
|
|
27
|
+
className?: string;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
const DROP_ZONE_BASE_CLASSES =
|
|
26
|
-
"mb-2 flex cursor-pointer flex-
|
|
31
|
+
"mb-2 flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed bg-white px-6 pt-4 pb-8 transition-colors";
|
|
27
32
|
const DROP_ZONE_DRAGGING_CLASSES = "border-blue-500 bg-blue-50";
|
|
28
|
-
const DROP_ZONE_IDLE_CLASSES = "hover:bg-gray-50";
|
|
29
|
-
const BORDER_COLOR_IDLE = "rgb(116, 116, 116)";
|
|
33
|
+
const DROP_ZONE_IDLE_CLASSES = "border-gray-300 hover:border-gray-400 hover:bg-gray-50";
|
|
30
34
|
|
|
31
35
|
/**
|
|
32
|
-
* Drop zone for file selection. Renders a dashed border area with
|
|
33
|
-
*
|
|
36
|
+
* Drop zone for file selection. Renders a dashed border area with document icon,
|
|
37
|
+
* primary instruction text, and format hint. Accepts click and drag-and-drop.
|
|
34
38
|
* Uses a hidden file input; props come from useFileUpload.
|
|
35
39
|
*/
|
|
36
40
|
export function FileUploadDropZone({
|
|
37
41
|
inputProps,
|
|
38
42
|
dropZoneProps,
|
|
39
43
|
isDragging,
|
|
44
|
+
formatHint = LABELS.formatHint,
|
|
45
|
+
className = "",
|
|
40
46
|
}: FileUploadDropZoneProps) {
|
|
41
47
|
const dropZoneClassName = [
|
|
42
48
|
DROP_ZONE_BASE_CLASSES,
|
|
43
49
|
isDragging ? DROP_ZONE_DRAGGING_CLASSES : DROP_ZONE_IDLE_CLASSES,
|
|
44
|
-
|
|
50
|
+
className,
|
|
51
|
+
]
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.join(" ");
|
|
45
54
|
|
|
46
55
|
return (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
<div
|
|
57
|
+
role="button"
|
|
58
|
+
tabIndex={0}
|
|
59
|
+
onClick={dropZoneProps.onClick}
|
|
60
|
+
onDragOver={dropZoneProps.onDragOver}
|
|
61
|
+
onDragLeave={dropZoneProps.onDragLeave}
|
|
62
|
+
onDrop={dropZoneProps.onDrop}
|
|
63
|
+
onKeyDown={dropZoneProps.onKeyDown}
|
|
64
|
+
className={dropZoneClassName}
|
|
65
|
+
aria-label={LABELS.dropZone}
|
|
66
|
+
data-testid="file-upload-drop-zone"
|
|
67
|
+
>
|
|
68
|
+
<input
|
|
69
|
+
id="file-upload-input-id"
|
|
70
|
+
ref={inputProps.ref}
|
|
71
|
+
type="file"
|
|
72
|
+
accept={inputProps.accept}
|
|
73
|
+
multiple={inputProps.multiple}
|
|
74
|
+
onChange={inputProps.onChange}
|
|
75
|
+
className="sr-only"
|
|
76
|
+
aria-hidden
|
|
77
|
+
/>
|
|
78
|
+
<span
|
|
79
|
+
className="inline-flex h-12 w-12 shrink-0 items-center justify-center text-gray-500"
|
|
80
|
+
aria-hidden
|
|
61
81
|
>
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
className="sr-only"
|
|
70
|
-
aria-hidden
|
|
71
|
-
/>
|
|
72
|
-
<span className="inline-flex items-center gap-2 rounded-[4px] border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-blue-600 hover:bg-gray-50">
|
|
73
|
-
<UtilityIcon id="upload" size="sm" />
|
|
74
|
-
{LABELS.uploadFiles}
|
|
75
|
-
</span>
|
|
76
|
-
<span className="text-sm text-gray-900">
|
|
77
|
-
{isDragging ? LABELS.dropFilesHere : LABELS.orDropFiles}
|
|
78
|
-
</span>
|
|
79
|
-
</div>
|
|
80
|
-
</>
|
|
82
|
+
<UtilityIcon id="image" />
|
|
83
|
+
</span>
|
|
84
|
+
<p className="text-center text-sm font-medium text-gray-900">
|
|
85
|
+
{isDragging ? LABELS.dropFilesHere : LABELS.chooseFileOrDrop}
|
|
86
|
+
</p>
|
|
87
|
+
<p className="text-center text-xs text-gray-500">{formatHint}</p>
|
|
88
|
+
</div>
|
|
81
89
|
);
|
|
82
90
|
}
|