feat(plugin-import-export): adds sort order control and sync with sort-by field (#13478)

### What?

This PR adds a dedicated `sortOrder` select field (Ascending /
Descending) to the import-export plugin, alongside updates to the
existing `SortBy` component. The new field and component logic keep the
sort field (`sort`) in sync with the selected sort direction.

### Why?

Previously, descending sorting did not work. While the `SortBy` field
could read the list view’s `query.sort` param, if the value contained a
leading dash (e.g. `-title`), it would not be handled correctly. Only
ascending sorts such as `sort=title` worked, and the preview table would
not reflect a descending order.

### How?

- Added a new `sortOrder` select field to the export options schema.
- Implemented a `SortOrder` custom component using ReactSelect:
- On new exports, reads `query.sort` from the list view and sets both
`sortOrder` and `sort` (combined value with or without a leading dash).
  - Handles external changes to `sort` that include a leading dash.
- Updated `SortBy`:
- No longer writes to `sort` during initial hydration—`SortOrder` owns
initial value setting.
- On user field changes, writes the combined value using the current
`sortOrder`.
This commit is contained in:
Patrik
2025-08-15 11:27:54 -04:00
committed by GitHub
parent 217606ac20
commit efdf00200a
49 changed files with 271 additions and 14 deletions

View File

@@ -11,8 +11,9 @@ import {
useField,
useListQuery,
} from '@payloadcms/ui'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import { applySortOrder, stripSortDash } from '../../utilities/sortHelpers.js'
import { reduceFields } from '../FieldsToExport/reduceFields.js'
import { useImportExport } from '../ImportExportProvider/index.js'
import './index.scss'
@@ -21,12 +22,19 @@ const baseClass = 'sort-by-fields'
export const SortBy: SelectFieldClientComponent = (props) => {
const { id } = useDocumentInfo()
const { setValue, value } = useField<string>()
// The "sort" text field that stores 'title' or '-title'
const { setValue: setSort, value: sortRaw } = useField<string>()
// Sibling order field ('asc' | 'desc') used when writing sort on change
const { value: sortOrder = 'asc' } = useField<string>({ path: 'sortOrder' })
const { value: collectionSlug } = useField<string>({ path: 'collectionSlug' })
const { query } = useListQuery()
const { getEntityConfig } = useConfig()
const { collection } = useImportExport()
// ReactSelect's displayed option
const [displayedValue, setDisplayedValue] = useState<{
id: string
label: ReactNode
@@ -34,40 +42,52 @@ export const SortBy: SelectFieldClientComponent = (props) => {
} | null>(null)
const collectionConfig = getEntityConfig({ collectionSlug: collectionSlug ?? collection })
const fieldOptions = reduceFields({ fields: collectionConfig?.fields })
const fieldOptions = useMemo(
() => reduceFields({ fields: collectionConfig?.fields }),
[collectionConfig?.fields],
)
// Sync displayedValue with value from useField
// Normalize the stored value for display (strip the '-') and pick the option
useEffect(() => {
if (!value) {
const clean = stripSortDash(sortRaw)
if (!clean) {
setDisplayedValue(null)
return
}
const option = fieldOptions.find((field) => field.value === value)
if (option && (!displayedValue || displayedValue.value !== value)) {
const option = fieldOptions.find((f) => f.value === clean)
if (option && (!displayedValue || displayedValue.value !== clean)) {
setDisplayedValue(option)
}
}, [displayedValue, fieldOptions, value])
}, [sortRaw, fieldOptions, displayedValue])
// Sync the visible select from list-view query sort,
// but no need to write to the "sort" field here — SortOrder owns initial combined value.
useEffect(() => {
if (id || !query?.sort || value) {
if (id || !query?.sort || sortRaw) {
return
}
const option = fieldOptions.find((field) => field.value === query.sort)
if (!query.sort) {
return
}
const clean = stripSortDash(query.sort as string)
const option = fieldOptions.find((f) => f.value === clean)
if (option) {
setValue(option.value)
setDisplayedValue(option)
}
}, [fieldOptions, id, query?.sort, value, setValue])
}, [id, query?.sort, sortRaw, fieldOptions])
// When user selects a different field, store it with the current order applied
const onChange = (option: { id: string; label: ReactNode; value: string } | null) => {
if (!option) {
setValue('')
setSort('')
setDisplayedValue(null)
} else {
setValue(option.value)
setDisplayedValue(option)
const next = applySortOrder(option.value, String(sortOrder) as 'asc' | 'desc')
setSort(next)
}
}

View File

@@ -0,0 +1,3 @@
.sort-order-field {
--field-width: 25%;
}

View File

@@ -0,0 +1,109 @@
'use client'
import type { SelectFieldClientComponent } from 'payload'
import { FieldLabel, ReactSelect, useDocumentInfo, useField, useListQuery } from '@payloadcms/ui'
import React, { useEffect, useMemo, useState } from 'react'
import { applySortOrder, stripSortDash } from '../../utilities/sortHelpers.js'
import './index.scss'
const baseClass = 'sort-order-field'
type Order = 'asc' | 'desc'
type OrderOption = { label: string; value: Order }
const options = [
{ label: 'Ascending', value: 'asc' as const },
{ label: 'Descending', value: 'desc' as const },
] as const
const defaultOption: OrderOption = options[0]
// Safely coerce query.sort to a string (ignore arrays)
const normalizeSortParam = (v: unknown): string | undefined => {
if (typeof v === 'string') {
return v
}
if (Array.isArray(v) && typeof v[0] === 'string') {
return v[0]
}
return undefined
}
export const SortOrder: SelectFieldClientComponent = (props) => {
const { id } = useDocumentInfo()
const { query } = useListQuery()
// 'sortOrder' select field: 'asc' | 'desc'
const { setValue: setOrder, value: orderValueRaw } = useField<Order>()
// 'sort' text field: 'title' | '-title'
const { setValue: setSort, value: sortRaw } = useField<string>({ path: 'sort' })
// The current order value, defaulting to 'asc' for UI
const orderValue: Order = orderValueRaw || 'asc'
// Map 'asc' | 'desc' to the option object for ReactSelect
const currentOption = useMemo<OrderOption>(
() => options.find((o) => o.value === orderValue) ?? defaultOption,
[orderValue],
)
const [displayed, setDisplayed] = useState<null | OrderOption>(currentOption)
// Derive from list-view query.sort if present
useEffect(() => {
if (id) {
return
}
const qs = normalizeSortParam(query?.sort)
if (!qs) {
return
}
const isDesc = qs.startsWith('-')
const base = stripSortDash(qs)
const order: Order = isDesc ? 'desc' : 'asc'
setOrder(order)
setSort(applySortOrder(base, order))
}, [id, query?.sort, setOrder, setSort])
// Keep the select's displayed option in sync with the stored order
useEffect(() => {
setDisplayed(currentOption ?? defaultOption)
}, [currentOption])
// Handle manual order changes via ReactSelect:
// - update the order field
// - rewrite the combined "sort" string to add/remove the leading '-'
const onChange = (option: null | OrderOption) => {
const next = option?.value ?? 'asc'
setOrder(next)
const base = stripSortDash(sortRaw)
if (base) {
setSort(applySortOrder(base, next))
}
setDisplayed(option ?? defaultOption)
}
return (
<div className={baseClass}>
<FieldLabel label={props.field.label} path={props.path} />
<ReactSelect
className={baseClass}
disabled={props.readOnly}
inputId={`field-${props.path.replace(/\./g, '__')}`}
isClearable={false}
isSearchable={false}
// @ts-expect-error react-select option typing differs from our local type
onChange={onChange}
options={options as unknown as OrderOption[]}
// @ts-expect-error react-select option typing differs from our local type
value={displayed}
/>
</div>
)
}

View File

@@ -118,6 +118,21 @@ export const getFields = (config: Config, pluginConfig?: ImportExportPluginConfi
// @ts-expect-error - this is not correctly typed in plugins right now
label: ({ t }) => t('plugin-import-export:field-sort-label'),
},
{
name: 'sortOrder',
type: 'select',
admin: {
components: {
Field: '@payloadcms/plugin-import-export/rsc#SortOrder',
},
},
// @ts-expect-error - this is not correctly typed in plugins right now
label: ({ t }) => t('plugin-import-export:field-sort-order-label'),
options: [
{ label: 'Ascending', value: 'asc' },
{ label: 'Descending', value: 'desc' },
],
},
...(localeField ? [localeField] : []),
{
name: 'drafts',

View File

@@ -7,3 +7,4 @@ export { Page } from '../components/Page/index.js'
export { Preview } from '../components/Preview/index.js'
export { SelectionToUseField } from '../components/SelectionToUseField/index.js'
export { SortBy } from '../components/SortBy/index.js'
export { SortOrder } from '../components/SortOrder/index.js'

View File

@@ -15,6 +15,7 @@ export const arTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'صفحة',
'field-selectionToUse-label': 'اختيار للاستخدام',
'field-sort-label': 'ترتيب حسب',
'field-sort-order-label': 'ترتيب',
'selectionToUse-allDocuments': 'استخدم جميع الوثائق',
'selectionToUse-currentFilters': 'استخدم الفلاتر الحالية',
'selectionToUse-currentSelection': 'استخدم الاختيار الحالي',

View File

@@ -15,6 +15,7 @@ export const azTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Səhifə',
'field-selectionToUse-label': 'İstifadə etmək üçün seçim',
'field-sort-label': 'Sırala',
'field-sort-order-label': 'Sıralama',
'selectionToUse-allDocuments': 'Bütün sənədlərdən istifadə edin',
'selectionToUse-currentFilters': 'Cari filtrlərdən istifadə edin',
'selectionToUse-currentSelection': 'Cari seçimi istifadə edin',

View File

@@ -15,6 +15,7 @@ export const bgTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Страница',
'field-selectionToUse-label': 'Избор за използване',
'field-sort-label': 'Сортирай по',
'field-sort-order-label': 'Ред на сортиране',
'selectionToUse-allDocuments': 'Използвайте всички документи',
'selectionToUse-currentFilters': 'Използвайте текущите филтри',
'selectionToUse-currentSelection': 'Използвайте текущия избор',

View File

@@ -15,6 +15,7 @@ export const caTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Pàgina',
'field-selectionToUse-label': 'Selecció per utilitzar',
'field-sort-label': 'Ordena per',
'field-sort-order-label': 'Ordre de classificació',
'selectionToUse-allDocuments': 'Utilitzeu tots els documents',
'selectionToUse-currentFilters': 'Utilitza els filtres actuals',
'selectionToUse-currentSelection': 'Utilitza la selecció actual',

View File

@@ -15,6 +15,7 @@ export const csTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Stránka',
'field-selectionToUse-label': 'Výběr k použití',
'field-sort-label': 'Seřadit podle',
'field-sort-order-label': 'Řazení',
'selectionToUse-allDocuments': 'Použijte všechny dokumenty',
'selectionToUse-currentFilters': 'Použijte aktuální filtry',
'selectionToUse-currentSelection': 'Použijte aktuální výběr',

View File

@@ -15,6 +15,7 @@ export const daTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Side',
'field-selectionToUse-label': 'Valg til brug',
'field-sort-label': 'Sorter efter',
'field-sort-order-label': 'Sorteringsrækkefølge',
'selectionToUse-allDocuments': 'Brug alle dokumenter',
'selectionToUse-currentFilters': 'Brug nuværende filtre',
'selectionToUse-currentSelection': 'Brug nuværende valg',

View File

@@ -15,6 +15,7 @@ export const deTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Seite',
'field-selectionToUse-label': 'Auswahl zur Verwendung',
'field-sort-label': 'Sortieren nach',
'field-sort-order-label': 'Sortierreihenfolge',
'selectionToUse-allDocuments': 'Verwenden Sie alle Dokumente.',
'selectionToUse-currentFilters': 'Verwenden Sie aktuelle Filter',
'selectionToUse-currentSelection': 'Verwenden Sie die aktuelle Auswahl',

View File

@@ -14,6 +14,7 @@ export const enTranslations = {
'field-page-label': 'Page',
'field-selectionToUse-label': 'Selection to use',
'field-sort-label': 'Sort by',
'field-sort-order-label': 'Sort order',
'selectionToUse-allDocuments': 'Use all documents',
'selectionToUse-currentFilters': 'Use current filters',
'selectionToUse-currentSelection': 'Use current selection',

View File

@@ -15,6 +15,7 @@ export const esTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Página',
'field-selectionToUse-label': 'Selección para usar',
'field-sort-label': 'Ordenar por',
'field-sort-order-label': 'Orden de clasificación',
'selectionToUse-allDocuments': 'Utilice todos los documentos',
'selectionToUse-currentFilters': 'Utilice los filtros actuales',
'selectionToUse-currentSelection': 'Usar selección actual',

View File

@@ -15,6 +15,7 @@ export const etTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Leht',
'field-selectionToUse-label': 'Valiku kasutamine',
'field-sort-label': 'Sorteeri järgi',
'field-sort-order-label': 'Sorteerimise järjekord',
'selectionToUse-allDocuments': 'Kasutage kõiki dokumente',
'selectionToUse-currentFilters': 'Kasuta praeguseid filtreid',
'selectionToUse-currentSelection': 'Kasuta praegust valikut',

View File

@@ -15,6 +15,7 @@ export const faTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'صفحه',
'field-selectionToUse-label': 'انتخاب برای استفاده',
'field-sort-label': 'مرتب سازی بر اساس',
'field-sort-order-label': 'ترتیب',
'selectionToUse-allDocuments': 'از تمام مستندات استفاده کنید',
'selectionToUse-currentFilters': 'از فیلترهای فعلی استفاده کنید',
'selectionToUse-currentSelection': 'از انتخاب فعلی استفاده کنید',

View File

@@ -15,6 +15,7 @@ export const frTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Page',
'field-selectionToUse-label': 'Sélection à utiliser',
'field-sort-label': 'Trier par',
'field-sort-order-label': 'Ordre de tri',
'selectionToUse-allDocuments': 'Utilisez tous les documents',
'selectionToUse-currentFilters': 'Utilisez les filtres actuels',
'selectionToUse-currentSelection': 'Utilisez la sélection actuelle',

View File

@@ -15,6 +15,7 @@ export const heTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'עמוד',
'field-selectionToUse-label': 'בחירה לשימוש',
'field-sort-label': 'מיין לפי',
'field-sort-order-label': 'סדר מיון',
'selectionToUse-allDocuments': 'השתמש בכל המסמכים',
'selectionToUse-currentFilters': 'השתמש במסננים הנוכחיים',
'selectionToUse-currentSelection': 'השתמש בבחירה הנוכחית',

View File

@@ -15,6 +15,7 @@ export const hrTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Stranica',
'field-selectionToUse-label': 'Odabir za upotrebu',
'field-sort-label': 'Sortiraj po',
'field-sort-order-label': 'Redoslijed sortiranja',
'selectionToUse-allDocuments': 'Koristite sve dokumente',
'selectionToUse-currentFilters': 'Koristite trenutne filtre',
'selectionToUse-currentSelection': 'Koristite trenutni odabir',

View File

@@ -15,6 +15,7 @@ export const huTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Oldal',
'field-selectionToUse-label': 'Használatra kiválasztva',
'field-sort-label': 'Rendezés szerint',
'field-sort-order-label': 'Rendezési sorrend',
'selectionToUse-allDocuments': 'Használjon minden dokumentumot',
'selectionToUse-currentFilters': 'Használja az aktuális szűrőket',
'selectionToUse-currentSelection': 'Használja a jelenlegi kiválasztást',

View File

@@ -15,6 +15,7 @@ export const hyTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Էջ',
'field-selectionToUse-label': 'Օգտագործման ընտրություն',
'field-sort-label': 'Դասավորել ըստ',
'field-sort-order-label': 'Դասավորության կարգ',
'selectionToUse-allDocuments': 'Օգտագործեք բոլոր փաստաթղթերը',
'selectionToUse-currentFilters': 'Օգտագործեք ընթացիկ ֆիլտրերը',
'selectionToUse-currentSelection': 'Օգտագործել ընթացիկ ընտրությունը',

View File

@@ -15,6 +15,7 @@ export const itTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Pagina',
'field-selectionToUse-label': 'Selezione da utilizzare',
'field-sort-label': 'Ordina per',
'field-sort-order-label': 'Ordine di sort',
'selectionToUse-allDocuments': 'Utilizza tutti i documenti',
'selectionToUse-currentFilters': 'Utilizza i filtri correnti',
'selectionToUse-currentSelection': 'Utilizza la selezione corrente',

View File

@@ -15,6 +15,7 @@ export const jaTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'ページ',
'field-selectionToUse-label': '使用する選択',
'field-sort-label': '並び替える',
'field-sort-order-label': '並び替えの順序',
'selectionToUse-allDocuments': 'すべての文書を使用してください。',
'selectionToUse-currentFilters': '現在のフィルターを使用してください',
'selectionToUse-currentSelection': '現在の選択を使用する',

View File

@@ -15,6 +15,7 @@ export const koTranslations: PluginDefaultTranslationsObject = {
'field-page-label': '페이지',
'field-selectionToUse-label': '사용할 선택',
'field-sort-label': '정렬 방식',
'field-sort-order-label': '정렬 순서',
'selectionToUse-allDocuments': '모든 문서를 사용하십시오.',
'selectionToUse-currentFilters': '현재 필터를 사용하십시오.',
'selectionToUse-currentSelection': '현재 선택 항목을 사용하십시오.',

View File

@@ -15,6 +15,7 @@ export const ltTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Puslapis',
'field-selectionToUse-label': 'Naudojimo pasirinkimas',
'field-sort-label': 'Rūšiuoti pagal',
'field-sort-order-label': 'Rūšiavimo tvarka',
'selectionToUse-allDocuments': 'Naudokite visus dokumentus.',
'selectionToUse-currentFilters': 'Naudoti esamus filtrus',
'selectionToUse-currentSelection': 'Naudoti dabartinį pasirinkimą',

View File

@@ -15,6 +15,7 @@ export const lvTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Lapa',
'field-selectionToUse-label': 'Izvēles lietošana',
'field-sort-label': 'Kārtot pēc',
'field-sort-order-label': 'Kārtot pēc secības',
'selectionToUse-allDocuments': 'Izmantojiet visus dokumentus',
'selectionToUse-currentFilters': 'Izmantot pašreizējos filtrus',
'selectionToUse-currentSelection': 'Izmantot pašreizējo izvēli',

View File

@@ -15,6 +15,7 @@ export const myTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'စာမျက်နှာ',
'field-selectionToUse-label': 'Pilihan untuk digunakan',
'field-sort-label': 'စီမံအလိုက်',
'field-sort-order-label': 'Sorteringsrækkefølge',
'selectionToUse-allDocuments': 'Gunakan semua dokumen',
'selectionToUse-currentFilters': 'Gunakan penapis semasa',
'selectionToUse-currentSelection': 'Gunakan pilihan semasa',

View File

@@ -15,6 +15,7 @@ export const nbTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Side',
'field-selectionToUse-label': 'Valg til bruk',
'field-sort-label': 'Sorter etter',
'field-sort-order-label': 'Sorteringsrekkefølge',
'selectionToUse-allDocuments': 'Bruk alle dokumentene',
'selectionToUse-currentFilters': 'Bruk gjeldende filtre',
'selectionToUse-currentSelection': 'Bruk gjeldende utvalg',

View File

@@ -15,6 +15,7 @@ export const nlTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Pagina',
'field-selectionToUse-label': 'Selectie om te gebruiken',
'field-sort-label': 'Sorteer op',
'field-sort-order-label': 'Sorteer volgorde',
'selectionToUse-allDocuments': 'Gebruik alle documenten',
'selectionToUse-currentFilters': 'Gebruik huidige filters',
'selectionToUse-currentSelection': 'Gebruik huidige selectie',

View File

@@ -15,6 +15,7 @@ export const plTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Strona',
'field-selectionToUse-label': 'Wybór do użycia',
'field-sort-label': 'Sortuj według',
'field-sort-order-label': 'Sortowanie według',
'selectionToUse-allDocuments': 'Użyj wszystkich dokumentów.',
'selectionToUse-currentFilters': 'Użyj aktualnych filtrów',
'selectionToUse-currentSelection': 'Użyj aktualnego wyboru',

View File

@@ -15,6 +15,7 @@ export const ptTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Página',
'field-selectionToUse-label': 'Seleção para usar',
'field-sort-label': 'Ordenar por',
'field-sort-order-label': 'Ordem de classificação',
'selectionToUse-allDocuments': 'Use todos os documentos',
'selectionToUse-currentFilters': 'Use os filtros atuais',
'selectionToUse-currentSelection': 'Use a seleção atual',

View File

@@ -15,6 +15,7 @@ export const roTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Pagina',
'field-selectionToUse-label': 'Selectarea pentru utilizare',
'field-sort-label': 'Sortează după',
'field-sort-order-label': 'Ordine de sortare',
'selectionToUse-allDocuments': 'Utilizați toate documentele.',
'selectionToUse-currentFilters': 'Utilizați filtrele curente',
'selectionToUse-currentSelection': 'Utilizați selecția curentă',

View File

@@ -15,6 +15,7 @@ export const rsTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Strana',
'field-selectionToUse-label': 'Izbor za upotrebu',
'field-sort-label': 'Sortiraj po',
'field-sort-order-label': 'Redoslijed sortiranja',
'selectionToUse-allDocuments': 'Koristite sve dokumente',
'selectionToUse-currentFilters': 'Koristite trenutne filtere',
'selectionToUse-currentSelection': 'Koristite trenutni izbor',

View File

@@ -15,6 +15,7 @@ export const rsLatinTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Strana',
'field-selectionToUse-label': 'Izbor za upotrebu',
'field-sort-label': 'Sortiraj po',
'field-sort-order-label': 'Redoslijed sortiranja',
'selectionToUse-allDocuments': 'Koristite sve dokumente',
'selectionToUse-currentFilters': 'Koristite trenutne filtere',
'selectionToUse-currentSelection': 'Koristi trenutni izbor',

View File

@@ -15,6 +15,7 @@ export const ruTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Страница',
'field-selectionToUse-label': 'Выбор использования',
'field-sort-label': 'Сортировать по',
'field-sort-order-label': 'Порядок сортировки',
'selectionToUse-allDocuments': 'Используйте все документы',
'selectionToUse-currentFilters': 'Использовать текущие фильтры',
'selectionToUse-currentSelection': 'Использовать текущий выбор',

View File

@@ -15,6 +15,7 @@ export const skTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Stránka',
'field-selectionToUse-label': 'Výber na použitie',
'field-sort-label': 'Triediť podľa',
'field-sort-order-label': 'Poradie triedenia',
'selectionToUse-allDocuments': 'Použite všetky dokumenty',
'selectionToUse-currentFilters': 'Použiť aktuálne filtre',
'selectionToUse-currentSelection': 'Použiť aktuálny výber',

View File

@@ -15,6 +15,7 @@ export const slTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Stran',
'field-selectionToUse-label': 'Izbor za uporabo',
'field-sort-label': 'Razvrsti po',
'field-sort-order-label': 'Razvrsti po vrstnem redu',
'selectionToUse-allDocuments': 'Uporabite vse dokumente',
'selectionToUse-currentFilters': 'Uporabite trenutne filtre.',
'selectionToUse-currentSelection': 'Uporabi trenutno izbiro',

View File

@@ -15,6 +15,7 @@ export const svTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Sida',
'field-selectionToUse-label': 'Val att använda',
'field-sort-label': 'Sortera efter',
'field-sort-order-label': 'Sortera i ordning',
'selectionToUse-allDocuments': 'Använd alla dokument',
'selectionToUse-currentFilters': 'Använd aktuella filter',
'selectionToUse-currentSelection': 'Använd nuvarande urval',

View File

@@ -15,6 +15,7 @@ export const thTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'หน้า',
'field-selectionToUse-label': 'การเลือกใช้',
'field-sort-label': 'เรียงตาม',
'field-sort-order-label': 'เรียงลำดับตาม',
'selectionToUse-allDocuments': 'ใช้เอกสารทั้งหมด',
'selectionToUse-currentFilters': 'ใช้ตัวกรองปัจจุบัน',
'selectionToUse-currentSelection': 'ใช้การเลือกปัจจุบัน',

View File

@@ -15,6 +15,7 @@ export const trTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Sayfa',
'field-selectionToUse-label': 'Kullanılacak seçim',
'field-sort-label': 'Sırala',
'field-sort-order-label': 'Sıralama düzeni',
'selectionToUse-allDocuments': 'Tüm belgeleri kullanın',
'selectionToUse-currentFilters': 'Mevcut filtreleri kullanın',
'selectionToUse-currentSelection': 'Mevcut seçimi kullanın',

View File

@@ -58,6 +58,9 @@
"field-sort-label": {
"type": "string"
},
"field-sort-order-label": {
"type": "string"
},
"no": {
"type": "string"
},
@@ -96,6 +99,7 @@
"field-name-label",
"field-selectionToUse-label",
"field-sort-label",
"field-sort-order-label",
"no",
"preview",
"selectionToUse-allDocuments",

View File

@@ -15,6 +15,7 @@ export const ukTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Сторінка',
'field-selectionToUse-label': 'Вибір для використання',
'field-sort-label': 'Сортувати за',
'field-sort-order-label': 'Сортувати за порядком',
'selectionToUse-allDocuments': 'Використовуйте всі документи',
'selectionToUse-currentFilters': 'Використовувати поточні фільтри',
'selectionToUse-currentSelection': 'Використовуйте поточний вибір',

View File

@@ -15,6 +15,7 @@ export const viTranslations: PluginDefaultTranslationsObject = {
'field-page-label': 'Trang',
'field-selectionToUse-label': 'Lựa chọn để sử dụng',
'field-sort-label': 'Sắp xếp theo',
'field-sort-order-label': 'Sắp xếp theo thứ tự',
'selectionToUse-allDocuments': 'Sử dụng tất cả các tài liệu',
'selectionToUse-currentFilters': 'Sử dụng bộ lọc hiện tại',
'selectionToUse-currentSelection': 'Sử dụng lựa chọn hiện tại',

View File

@@ -15,6 +15,7 @@ export const zhTranslations: PluginDefaultTranslationsObject = {
'field-page-label': '页面',
'field-selectionToUse-label': '选择范围',
'field-sort-label': '排序方式',
'field-sort-order-label': '排序顺序',
'selectionToUse-allDocuments': '使用所有文档',
'selectionToUse-currentFilters': '使用当前过滤条件',
'selectionToUse-currentSelection': '使用当前选择',

View File

@@ -15,6 +15,7 @@ export const zhTwTranslations: PluginDefaultTranslationsObject = {
'field-page-label': '頁面',
'field-selectionToUse-label': '使用的選取範圍',
'field-sort-label': '排序方式',
'field-sort-order-label': '排序順序',
'selectionToUse-allDocuments': '使用所有文件',
'selectionToUse-currentFilters': '使用目前篩選條件',
'selectionToUse-currentSelection': '使用目前選取內容',

View File

@@ -16,6 +16,7 @@ export type PluginLanguage = Language<{
'field-page-label': string
'field-selectionToUse-label': string
'field-sort-label': string
'field-sort-order-label': string
'selectionToUse-allDocuments': string
'selectionToUse-currentFilters': string
'selectionToUse-currentSelection': string

View File

@@ -0,0 +1,6 @@
/** Remove a leading '-' from a sort value (e.g. "-title" -> "title") */
export const stripSortDash = (v?: null | string): string => (v ? v.replace(/^-/, '') : '')
/** Apply order to a base field (("title","desc") -> "-title") */
export const applySortOrder = (field: string, order: 'asc' | 'desc'): string =>
order === 'desc' ? `-${field}` : field

View File

@@ -227,6 +227,60 @@ describe('@payloadcms/plugin-import-export', () => {
).rejects.toThrow(/Limit/)
})
it('should export results sorted ASC by title when sort="title"', async () => {
let doc = await payload.create({
collection: 'exports',
user,
data: {
collectionSlug: 'pages',
format: 'csv',
sort: 'title',
where: {
or: [{ title: { contains: 'Title' } }, { title: { contains: 'Array' } }],
},
},
})
doc = await payload.findByID({
collection: 'exports',
id: doc.id,
})
expect(doc.filename).toBeDefined()
const expectedPath = path.join(dirname, './uploads', doc.filename as string)
const data = await readCSV(expectedPath)
expect(data[0].id).toBeDefined()
expect(data[0].title).toStrictEqual('Array 0')
})
it('should export results sorted DESC by title when sort="-title"', async () => {
let doc = await payload.create({
collection: 'exports',
user,
data: {
collectionSlug: 'pages',
format: 'csv',
sort: '-title',
where: {
or: [{ title: { contains: 'Title' } }, { title: { contains: 'Array' } }],
},
},
})
doc = await payload.findByID({
collection: 'exports',
id: doc.id,
})
expect(doc.filename).toBeDefined()
const expectedPath = path.join(dirname, './uploads', doc.filename as string)
const data = await readCSV(expectedPath)
expect(data[0].id).toBeDefined()
expect(data[0].title).toStrictEqual('Title 4')
})
it('should create a file for collection csv with draft data', async () => {
const draftPage = await payload.create({
collection: 'pages',

View File

@@ -270,6 +270,7 @@ export interface Export {
limit?: number | null;
page?: number | null;
sort?: string | null;
sortOrder?: ('asc' | 'desc') | null;
locale?: ('all' | 'en' | 'es' | 'de') | null;
drafts?: ('yes' | 'no') | null;
selectionToUse?: ('currentSelection' | 'currentFilters' | 'all') | null;
@@ -307,6 +308,7 @@ export interface ExportsTask {
limit?: number | null;
page?: number | null;
sort?: string | null;
sortOrder?: ('asc' | 'desc') | null;
locale?: ('all' | 'en' | 'es' | 'de') | null;
drafts?: ('yes' | 'no') | null;
selectionToUse?: ('currentSelection' | 'currentFilters' | 'all') | null;
@@ -609,6 +611,7 @@ export interface ExportsSelect<T extends boolean = true> {
limit?: T;
page?: T;
sort?: T;
sortOrder?: T;
locale?: T;
drafts?: T;
selectionToUse?: T;
@@ -637,6 +640,7 @@ export interface ExportsTasksSelect<T extends boolean = true> {
limit?: T;
page?: T;
sort?: T;
sortOrder?: T;
locale?: T;
drafts?: T;
selectionToUse?: T;
@@ -729,6 +733,7 @@ export interface TaskCreateCollectionExport {
limit?: number | null;
page?: number | null;
sort?: string | null;
sortOrder?: ('asc' | 'desc') | null;
locale?: ('all' | 'en' | 'es' | 'de') | null;
drafts?: ('yes' | 'no') | null;
selectionToUse?: ('currentSelection' | 'currentFilters' | 'all') | null;