Skip to content

Commit ee083f0

Browse files
authored
fix(plugin-import-export): automatically inherit locale and limit from URL queries (#15812)
This fixes the behaviour so that we automatically inherit the locale and limit from the URL queries, most commonly from users filtering in the list view and then exporting directly. Other fields can already inherit so these were the two remaining ones not doing that.
1 parent 6aff717 commit ee083f0

5 files changed

Lines changed: 147 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use client'
2+
3+
import type { NumberFieldClientComponent } from 'payload'
4+
5+
import { NumberField, useDocumentInfo, useField, useListQuery } from '@payloadcms/ui'
6+
import React, { useEffect, useRef } from 'react'
7+
8+
export const LimitField: NumberFieldClientComponent = (props) => {
9+
const { id } = useDocumentInfo()
10+
const { setValue, value } = useField<number>()
11+
const { query } = useListQuery()
12+
13+
const didInitRef = useRef(false)
14+
15+
useEffect(() => {
16+
if (didInitRef.current) {
17+
return
18+
}
19+
if (id) {
20+
didInitRef.current = true
21+
return
22+
}
23+
if (typeof value === 'number') {
24+
didInitRef.current = true
25+
return
26+
}
27+
28+
const queryLimit = query?.limit
29+
if (typeof queryLimit === 'number' && queryLimit > 0) {
30+
setValue(queryLimit)
31+
}
32+
33+
didInitRef.current = true
34+
}, [id, query?.limit, value, setValue])
35+
36+
return <NumberField field={props.field} path={props.path} />
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use client'
2+
3+
import type { SelectFieldClientComponent } from 'payload'
4+
5+
import { SelectField, useDocumentInfo, useField, useLocale } from '@payloadcms/ui'
6+
import React, { useEffect, useRef } from 'react'
7+
8+
export const LocaleField: SelectFieldClientComponent = (props) => {
9+
const { id } = useDocumentInfo()
10+
const { setValue, value } = useField<string>()
11+
const locale = useLocale()
12+
13+
const didInitRef = useRef(false)
14+
15+
useEffect(() => {
16+
if (didInitRef.current) {
17+
return
18+
}
19+
if (id) {
20+
didInitRef.current = true
21+
return
22+
}
23+
if (typeof value === 'string' && value !== 'all') {
24+
didInitRef.current = true
25+
return
26+
}
27+
28+
if (locale?.code) {
29+
setValue(locale.code)
30+
}
31+
32+
didInitRef.current = true
33+
}, [id, locale?.code, value, setValue])
34+
35+
return <SelectField field={props.field} path={props.path} />
36+
}

packages/plugin-import-export/src/export/getFields.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export const getFields = (options: GetFieldsOptions): Field[] => {
2727
name: 'locale',
2828
type: 'select',
2929
admin: {
30+
components: {
31+
Field: '@payloadcms/plugin-import-export/rsc#LocaleField',
32+
},
3033
width: '25%',
3134
},
3235
defaultValue: 'all',
@@ -87,6 +90,9 @@ export const getFields = (options: GetFieldsOptions): Field[] => {
8790
name: 'limit',
8891
type: 'number',
8992
admin: {
93+
components: {
94+
Field: '@payloadcms/plugin-import-export/rsc#LimitField',
95+
},
9096
placeholder: 'No limit',
9197
step: 100,
9298
width: '33.3333%',

packages/plugin-import-export/src/exports/rsc.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export { ImportExportProvider } from '../components/ImportExportProvider/index.j
88
export { ImportListMenuItem } from '../components/ImportListMenuItem/index.js'
99
export { ImportPreview } from '../components/ImportPreview/index.js'
1010
export { ImportSaveButton } from '../components/ImportSaveButton/index.js'
11+
export { LimitField } from '../components/LimitField/index.js'
12+
export { LocaleField } from '../components/LocaleField/index.js'
1113
export { Page } from '../components/Page/index.js'
1214
export { SelectionToUseField } from '../components/SelectionToUseField/index.js'
1315
export { SortBy } from '../components/SortBy/index.js'

test/plugin-import-export/e2e.spec.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,68 @@ test.describe('Import Export Plugin', () => {
130130
}).toPass({ timeout: POLL_TOPASS_TIMEOUT })
131131
})
132132

133+
test('should inherit limit from list view URL', async () => {
134+
await page.goto(postsURL.list)
135+
await expect(page.locator('.collection-list')).toBeVisible()
136+
137+
// Change per-page to 25
138+
const perPageButton = page.locator('.per-page .popup-button')
139+
await expect(perPageButton).toBeVisible()
140+
await perPageButton.click()
141+
142+
const perPage25 = page.locator('.popup__content button.per-page__button', { hasText: '25' })
143+
await expect(perPage25).toBeVisible()
144+
await perPage25.click()
145+
146+
// Wait for URL to contain limit=25
147+
await expect(() => {
148+
expect(page.url()).toContain('limit=25')
149+
}).toPass({ timeout: POLL_TOPASS_TIMEOUT })
150+
151+
// Open export from list menu
152+
const listMenuButton = page.locator('#list-menu')
153+
await expect(listMenuButton).toBeVisible()
154+
await listMenuButton.click()
155+
156+
const createExportButton = page.locator('.popup__scroll-container button', {
157+
hasText: 'Export Posts',
158+
})
159+
await expect(createExportButton).toBeVisible()
160+
await createExportButton.click()
161+
162+
await expect(async () => {
163+
await expect(page.locator('.export-preview')).toBeVisible()
164+
}).toPass({ timeout: POLL_TOPASS_TIMEOUT })
165+
166+
// Verify limit field inherited the value from URL
167+
const limitField = page.locator('input[name="limit"]')
168+
await expect(limitField).toHaveValue('25')
169+
})
170+
171+
test('should inherit locale from list view URL', async () => {
172+
await page.goto(`${postsURL.list}?locale=es`)
173+
await expect(page.locator('.collection-list')).toBeVisible()
174+
175+
// Open export from list menu
176+
const listMenuButton = page.locator('#list-menu')
177+
await expect(listMenuButton).toBeVisible()
178+
await listMenuButton.click()
179+
180+
const createExportButton = page.locator('.popup__scroll-container button', {
181+
hasText: 'Export Posts',
182+
})
183+
await expect(createExportButton).toBeVisible()
184+
await createExportButton.click()
185+
186+
await expect(async () => {
187+
await expect(page.locator('.export-preview')).toBeVisible()
188+
}).toPass({ timeout: POLL_TOPASS_TIMEOUT })
189+
190+
// Verify locale field inherited the value from URL
191+
const localeField = page.locator('#field-locale')
192+
await expect(localeField.locator('.rs__single-value')).toHaveText('Spanish')
193+
})
194+
133195
test('should download directly in the browser', async () => {
134196
await page.goto(exportsURL.create)
135197
await expect(page.locator('.collection-edit')).toBeVisible()
@@ -1096,6 +1158,10 @@ test.describe('Import Export Plugin', () => {
10961158
await expect(page.locator('.export-preview')).toBeVisible()
10971159
}).toPass({ timeout: POLL_TOPASS_TIMEOUT })
10981160

1161+
// Clear any inherited limit so all records are exported
1162+
const limitField = page.locator('input[name="limit"]')
1163+
await limitField.clear()
1164+
10991165
const downloadButton = page.locator('.doc-controls__controls button', {
11001166
hasText: 'Download',
11011167
})

0 commit comments

Comments
 (0)