Skip to content

Commit daaaa5f

Browse files
authored
feat(ui): add hideFileInputOnCreate and hideRemoveFile to collection upload config (#11217)
### What? Two new configuration properties added for upload enabled collections. - *hideFileInputOnCreate* - Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation. - *hideRemoveFile* - Set to `true` to prevent the admin UI having a way to remove an existing file while editing. ### Why? When using file uploads that get created programmatically in `beforeOperation` hooks or files created using `jobs`, or when `filesRequiredOnCreate` is false, you may want to use these new flags to prevent users from interacting with these controls. ### How? The new properties only impact the admin UI components to dial in the UX for various use cases. Screenshot showing that the upload controls are not available on create: ![image](https://github.com/user-attachments/assets/5560b9ac-271d-4ee0-8bcf-6080012ff75f) Screenshot showing hideRemoveFile has removed the ability to remove the existing file: ![image](https://github.com/user-attachments/assets/71c562dd-c425-40e6-b980-f65895979885) Prerequisite for #10795
1 parent ee0ac7f commit daaaa5f

File tree

12 files changed

+221
-32
lines changed

12 files changed

+221
-32
lines changed

docs/upload/overview.mdx

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,29 @@ export const Media: CollectionConfig = {
8888

8989
_An asterisk denotes that an option is required._
9090

91-
| Option | Description |
92-
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
93-
| **`adminThumbnail`** | Set the way that the [Admin Panel](../admin/overview) will display thumbnails for this Collection. [More](#admin-thumbnails) |
94-
| **`bulkUpload`** | Allow users to upload in bulk from the list view, default is true |
95-
| **`cacheTags`** | Set to `false` to disable the cache tag set in the UI for the admin thumbnail component. Useful for when CDNs don't allow certain cache queries. |
96-
| **`crop`** | Set to `false` to disable the cropping tool in the [Admin Panel](../admin/overview). Crop is enabled by default. [More](#crop-and-focal-point-selector) |
97-
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
98-
| **`displayPreview`** | Enable displaying preview of the uploaded file in Upload fields related to this Collection. Can be locally overridden by `displayPreview` option in Upload field. [More](/docs/fields/upload#config-options). |
99-
| **`externalFileHeaderFilter`** | Accepts existing headers and returns the headers after filtering or modifying. |
100-
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
101-
| **`filenameCompoundIndex`** | Field slugs to use for a compound index instead of the default filename index. |
102-
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the [Admin Panel](../admin/overview). The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
103-
| **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) |
104-
| **`handlers`** | Array of Request handlers to execute when fetching a file, if a handler returns a Response it will be sent to the client. Otherwise Payload will retrieve and send back the file. |
105-
| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |
106-
| **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) |
91+
| Option | Description |
92+
|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
93+
| **`adminThumbnail`** | Set the way that the [Admin Panel](../admin/overview) will display thumbnails for this Collection. [More](#admin-thumbnails) |
94+
| **`bulkUpload`** | Allow users to upload in bulk from the list view, default is true |
95+
| **`cacheTags`** | Set to `false` to disable the cache tag set in the UI for the admin thumbnail component. Useful for when CDNs don't allow certain cache queries. |
96+
| **`crop`** | Set to `false` to disable the cropping tool in the [Admin Panel](../admin/overview). Crop is enabled by default. [More](#crop-and-focal-point-selector) |
97+
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
98+
| **`displayPreview`** | Enable displaying preview of the uploaded file in Upload fields related to this Collection. Can be locally overridden by `displayPreview` option in Upload field. [More](/docs/fields/upload#config-options). |
99+
| **`externalFileHeaderFilter`** | Accepts existing headers and returns the headers after filtering or modifying. |
100+
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
101+
| **`filenameCompoundIndex`** | Field slugs to use for a compound index instead of the default filename index. |
102+
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the [Admin Panel](../admin/overview). The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
103+
| **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) |
104+
| **`handlers`** | Array of Request handlers to execute when fetching a file, if a handler returns a Response it will be sent to the client. Otherwise Payload will retrieve and send back the file. |
105+
| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |
106+
| **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) |
107107
| **`pasteURL`** | Controls whether files can be uploaded from remote URLs by pasting them into the Upload field. **Enabled by default.** Accepts `false` to disable or an object with an `allowList` of valid remote URLs. [More](#uploading-files-from-remote-urls) |
108-
| **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) |
109-
| **`staticDir`** | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. Defaults to your collection slug |
110-
| **`trimOptions`** | An object passed to the the Sharp image library to trim the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize#trim) |
111-
| **`withMetadata`** | If specified, appends metadata to the output image file. Accepts a boolean or a function that receives `metadata` and `req`, returning a boolean. |
108+
| **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) |
109+
| **`staticDir`** | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. Defaults to your collection slug |
110+
| **`trimOptions`** | An object passed to the the Sharp image library to trim the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize#trim) |
111+
| **`withMetadata`** | If specified, appends metadata to the output image file. Accepts a boolean or a function that receives `metadata` and `req`, returning a boolean. |
112+
| **`hideFileInputOnCreate`** | Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation. |
113+
| **`hideRemoveFile`** | Set to `true` to prevent the admin UI having a way to remove an existing file while editing. |
112114

113115

114116
### Payload-wide Upload Options

packages/payload/src/uploads/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ export type UploadConfig = {
181181
params: { collection: string; filename: string }
182182
},
183183
) => Promise<Response> | Promise<void> | Response | void)[]
184+
/**
185+
* Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation.
186+
*/
187+
hideFileInputOnCreate?: boolean
188+
/**
189+
* Set to `true` to prevent the admin UI having a way to remove an existing file while editing.
190+
*/
191+
hideRemoveFile?: boolean
184192
imageSizes?: ImageSize[]
185193
/**
186194
* Restrict mimeTypes in the file picker. Array of valid mime types or mimetype wildcards

packages/ui/src/elements/FileDetails/DraggableFileDetails/index.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type DraggableFileDetailsProps = {
2323
enableAdjustments?: boolean
2424
hasImageSizes?: boolean
2525
hasMany: boolean
26+
hideRemoveFile?: boolean
2627
imageCacheTag?: string
2728
isSortable?: boolean
2829
removeItem?: (index: number) => void
@@ -31,8 +32,16 @@ export type DraggableFileDetailsProps = {
3132
}
3233

3334
export const DraggableFileDetails: React.FC<DraggableFileDetailsProps> = (props) => {
34-
const { collectionSlug, doc, imageCacheTag, isSortable, removeItem, rowIndex, uploadConfig } =
35-
props
35+
const {
36+
collectionSlug,
37+
doc,
38+
hideRemoveFile,
39+
imageCacheTag,
40+
isSortable,
41+
removeItem,
42+
rowIndex,
43+
uploadConfig,
44+
} = props
3645

3746
const { id, filename, thumbnailURL, url } = doc
3847

@@ -84,7 +93,7 @@ export const DraggableFileDetails: React.FC<DraggableFileDetailsProps> = (props)
8493
<DocumentDrawerToggler>
8594
<EditIcon />
8695
</DocumentDrawerToggler>
87-
{removeItem && (
96+
{!hideRemoveFile && removeItem && (
8897
<Button
8998
buttonStyle="icon-label"
9099
className={`${baseClass}__remove`}

packages/ui/src/elements/FileDetails/StaticFileDetails/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type StaticFileDetailsProps = {
1919
enableAdjustments?: boolean
2020
handleRemove?: () => void
2121
hasImageSizes?: boolean
22+
hideRemoveFile?: boolean
2223
imageCacheTag?: string
2324
uploadConfig: SanitizedCollectionConfig['upload']
2425
}
@@ -30,6 +31,7 @@ export const StaticFileDetails: React.FC<StaticFileDetailsProps> = (props) => {
3031
enableAdjustments,
3132
handleRemove,
3233
hasImageSizes,
34+
hideRemoveFile,
3335
imageCacheTag,
3436
uploadConfig,
3537
} = props
@@ -66,7 +68,7 @@ export const StaticFileDetails: React.FC<StaticFileDetailsProps> = (props) => {
6668
/>
6769
)}
6870
</div>
69-
{handleRemove && (
71+
{!hideRemoveFile && handleRemove && (
7072
<Button
7173
buttonStyle="icon-label"
7274
className={`${baseClass}__remove`}

packages/ui/src/elements/FileDetails/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type SharedFileDetailsProps = {
1414
} & Data
1515
enableAdjustments?: boolean
1616
hasImageSizes?: boolean
17+
hideRemoveFile?: boolean
1718
imageCacheTag?: string
1819
uploadConfig: SanitizedCollectionConfig['upload']
1920
}

packages/ui/src/elements/Upload/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ export const Upload: React.FC<UploadProps> = (props) => {
270270

271271
const imageCacheTag = uploadConfig?.cacheTags && savedDocumentData?.updatedAt
272272

273+
if (uploadConfig.hideFileInputOnCreate && !savedDocumentData?.filename) {
274+
return null
275+
}
276+
273277
return (
274278
<div className={[fieldBaseClass, baseClass].filter(Boolean).join(' ')}>
275279
<FieldError message={errorMessage} showError={showError} />
@@ -281,11 +285,12 @@ export const Upload: React.FC<UploadProps> = (props) => {
281285
enableAdjustments={showCrop || showFocalPoint}
282286
handleRemove={canRemoveUpload ? handleFileRemoval : undefined}
283287
hasImageSizes={hasImageSizes}
288+
hideRemoveFile={uploadConfig.hideRemoveFile}
284289
imageCacheTag={imageCacheTag}
285290
uploadConfig={uploadConfig}
286291
/>
287292
)}
288-
{(!savedDocumentData?.filename || removedFile) && (
293+
{((!uploadConfig.hideFileInputOnCreate && !savedDocumentData?.filename) || removedFile) && (
289294
<div className={`${baseClass}__upload`}>
290295
{!value && !showUrlInput && (
291296
<Dropzone onChange={handleFileSelection}>

packages/ui/src/fields/Upload/Input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export type UploadInputProps = {
5858
readonly Error?: React.ReactNode
5959
readonly filterOptions?: FilterOptionsResult
6060
readonly hasMany?: boolean
61+
readonly hideRemoveFile?: boolean
6162
readonly isSortable?: boolean
6263
readonly Label?: React.ReactNode
6364
readonly label?: StaticLabel

packages/ui/src/views/List/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export function DefaultListView(props: ListViewClientProps) {
177177
}
178178
hasCreatePermission={hasCreatePermission}
179179
i18n={i18n}
180-
isBulkUploadEnabled={isBulkUploadEnabled}
180+
isBulkUploadEnabled={isBulkUploadEnabled && !upload.hideFileInputOnCreate}
181181
newDocumentURL={newDocumentURL}
182182
openBulkUpload={openBulkUpload}
183183
smallBreak={smallBreak}

test/uploads/config.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
customFileNameMediaSlug,
1818
enlargeSlug,
1919
focalNoSizesSlug,
20+
hideFileInputOnCreateSlug,
2021
mediaSlug,
2122
mediaWithoutCacheTagsSlug,
2223
mediaWithoutRelationPreviewSlug,
@@ -50,6 +51,11 @@ export default buildConfigWithDefaults({
5051
type: 'upload',
5152
relationTo: versionSlug,
5253
},
54+
{
55+
name: 'hideFileInputOnCreate',
56+
type: 'upload',
57+
relationTo: hideFileInputOnCreateSlug,
58+
},
5359
],
5460
},
5561
{
@@ -693,6 +699,36 @@ export default buildConfigWithDefaults({
693699
},
694700
],
695701
},
702+
{
703+
slug: hideFileInputOnCreateSlug,
704+
upload: {
705+
hideFileInputOnCreate: true,
706+
hideRemoveFile: true,
707+
staticDir: path.resolve(dirname, 'uploads'),
708+
},
709+
hooks: {
710+
beforeOperation: [
711+
({ req, operation }) => {
712+
if (operation !== 'create') {
713+
return
714+
}
715+
const buffer = Buffer.from('This file was generated by a hook', 'utf-8')
716+
req.file = {
717+
name: `${new Date().toISOString()}.txt`,
718+
data: buffer,
719+
mimetype: 'text/plain',
720+
size: buffer.length,
721+
}
722+
},
723+
],
724+
},
725+
fields: [
726+
{
727+
name: 'title',
728+
type: 'text',
729+
},
730+
],
731+
},
696732
],
697733
onInit: async (payload) => {
698734
const uploadsDir = path.resolve(dirname, './media')

0 commit comments

Comments
 (0)