feat(plugin-import-export): adds support for forcing export format via plugin config (#13160)

### What?

Adds a new `format` option to the `plugin-import-export` config that
allows users to force the export format (`csv` or `json`) and hide the
format dropdown from the export UI.

### Why?

In some use cases, allowing the user to select between CSV and JSON is
unnecessary or undesirable. This new option allows plugin consumers to
lock the format and simplify the export interface.

### How?

- Added a `format?: 'csv' | 'json'` field to `ImportExportPluginConfig`.
- When defined, the `format` field in the export UI is:
  - Hidden via `admin.condition`
  - Pre-filled via `defaultValue`
- Updated `getFields` to accept the plugin config and apply logic
accordingly.

### Example

```ts
importExportPlugin({
  format: 'json',
})
This commit is contained in:
Patrik
2025-07-14 16:19:52 -04:00
committed by GitHub
parent 4831bae6b5
commit 7294cf561d
6 changed files with 27 additions and 8 deletions

View File

@@ -1,5 +1,6 @@
import type { Config, TaskConfig, TypedUser } from 'payload' import type { Config, TaskConfig, TypedUser } from 'payload'
import type { ImportExportPluginConfig } from '../types.js'
import type { CreateExportArgs, Export } from './createExport.js' import type { CreateExportArgs, Export } from './createExport.js'
import { createExport } from './createExport.js' import { createExport } from './createExport.js'
@@ -7,11 +8,12 @@ import { getFields } from './getFields.js'
export const getCreateCollectionExportTask = ( export const getCreateCollectionExportTask = (
config: Config, config: Config,
pluginConfig?: ImportExportPluginConfig,
): TaskConfig<{ ): TaskConfig<{
input: Export input: Export
output: object output: object
}> => { }> => {
const inputSchema = getFields(config).concat( const inputSchema = getFields(config, pluginConfig).concat(
{ {
name: 'user', name: 'user',
type: 'text', type: 'text',

View File

@@ -1,8 +1,10 @@
import type { Config, Field, SelectField } from 'payload' import type { Config, Field, SelectField } from 'payload'
import type { ImportExportPluginConfig } from '../types.js'
import { getFilename } from './getFilename.js' import { getFilename } from './getFilename.js'
export const getFields = (config: Config): Field[] => { export const getFields = (config: Config, pluginConfig?: ImportExportPluginConfig): Field[] => {
let localeField: SelectField | undefined let localeField: SelectField | undefined
if (config.localization) { if (config.localization) {
localeField = { localeField = {
@@ -45,9 +47,14 @@ export const getFields = (config: Config): Field[] => {
name: 'format', name: 'format',
type: 'select', type: 'select',
admin: { admin: {
// Hide if a forced format is set via plugin config
condition: () => !pluginConfig?.format,
width: '33%', width: '33%',
}, },
defaultValue: 'csv', defaultValue: (() => {
// Default to plugin-defined format, otherwise 'csv'
return pluginConfig?.format ?? 'csv'
})(),
// @ts-expect-error - this is not correctly typed in plugins right now // @ts-expect-error - this is not correctly typed in plugins right now
label: ({ t }) => t('plugin-import-export:field-format-label'), label: ({ t }) => t('plugin-import-export:field-format-label'),
options: [ options: [

View File

@@ -51,7 +51,7 @@ export const getExportCollection = ({
path: '/download', path: '/download',
}, },
], ],
fields: getFields(config), fields: getFields(config, pluginConfig),
hooks: { hooks: {
afterChange, afterChange,
beforeOperation, beforeOperation,

View File

@@ -32,7 +32,7 @@ export const importExportPlugin =
) )
// inject the createExport job into the config // inject the createExport job into the config
;((config.jobs ??= {}).tasks ??= []).push(getCreateCollectionExportTask(config)) ;((config.jobs ??= {}).tasks ??= []).push(getCreateCollectionExportTask(config, pluginConfig))
let collectionsToUpdate = config.collections let collectionsToUpdate = config.collections

View File

@@ -29,6 +29,16 @@ export type ImportExportPluginConfig = {
* @default false * @default false
*/ */
disableSave?: boolean disableSave?: boolean
/**
* Forces a specific export format (`csv` or `json`) and hides the format dropdown from the UI.
*
* When defined, this overrides the user's ability to choose a format manually. The export will
* always use the specified format, and the format selection field will be hidden.
*
* If not set, the user can choose between CSV and JSON in the export UI.
* @default undefined
*/
format?: 'csv' | 'json'
/** /**
* This function takes the default export collection configured in the plugin and allows you to override it by modifying and returning it * This function takes the default export collection configured in the plugin and allows you to override it by modifying and returning it
* @param collection * @param collection

View File

@@ -264,7 +264,7 @@ export interface Post {
export interface Export { export interface Export {
id: string; id: string;
name?: string | null; name?: string | null;
format: 'csv' | 'json'; format?: ('csv' | 'json') | null;
limit?: number | null; limit?: number | null;
sort?: string | null; sort?: string | null;
locale?: ('all' | 'en' | 'es' | 'de') | null; locale?: ('all' | 'en' | 'es' | 'de') | null;
@@ -300,7 +300,7 @@ export interface Export {
export interface ExportsTask { export interface ExportsTask {
id: string; id: string;
name?: string | null; name?: string | null;
format: 'csv' | 'json'; format?: ('csv' | 'json') | null;
limit?: number | null; limit?: number | null;
sort?: string | null; sort?: string | null;
locale?: ('all' | 'en' | 'es' | 'de') | null; locale?: ('all' | 'en' | 'es' | 'de') | null;
@@ -717,7 +717,7 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
export interface TaskCreateCollectionExport { export interface TaskCreateCollectionExport {
input: { input: {
name?: string | null; name?: string | null;
format: 'csv' | 'json'; format?: ('csv' | 'json') | null;
limit?: number | null; limit?: number | null;
sort?: string | null; sort?: string | null;
locale?: ('all' | 'en' | 'es' | 'de') | null; locale?: ('all' | 'en' | 'es' | 'de') | null;