Skip to content

Commit 8a7124a

Browse files
authored
fix(next): resolve filterOptions by path (#13779)
Follow up to #11375. When setting `filterOptions` on relationship or upload fields _that are nested within a named field_, those options won't be applied to the `Filter` component in the list view. This is because of how we key the results when resolving `filterOptions` on the server. Instead of using the field path as expected, we were using the field name, causing a failed lookup on the front-end. This also solves an issue where two fields with the same name would override each other's `filterOptions`, since field names alone are not unique. Unrelated: this PR also does some general housekeeping to e2e test helpers. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211332845301583
1 parent 8282031 commit 8a7124a

File tree

36 files changed

+313
-207
lines changed

36 files changed

+313
-207
lines changed

packages/next/src/views/List/resolveAllFilterOptions.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import type { Field, PayloadRequest, ResolvedFilterOptions } from 'payload'
22

33
import { resolveFilterOptions } from '@payloadcms/ui/rsc'
4-
import { fieldHasSubFields, fieldIsHiddenOrDisabled } from 'payload/shared'
4+
import {
5+
fieldAffectsData,
6+
fieldHasSubFields,
7+
fieldIsHiddenOrDisabled,
8+
tabHasName,
9+
} from 'payload/shared'
510

611
export const resolveAllFilterOptions = async ({
712
fields,
13+
pathPrefix,
814
req,
915
result,
1016
}: {
1117
fields: Field[]
18+
pathPrefix?: string
1219
req: PayloadRequest
1320
result?: Map<string, ResolvedFilterOptions>
1421
}): Promise<Map<string, ResolvedFilterOptions>> => {
@@ -20,6 +27,12 @@ export const resolveAllFilterOptions = async ({
2027
return
2128
}
2229

30+
const fieldPath = fieldAffectsData(field)
31+
? pathPrefix
32+
? `${pathPrefix}.${field.name}`
33+
: field.name
34+
: pathPrefix
35+
2336
if (
2437
(field.type === 'relationship' || field.type === 'upload') &&
2538
'filterOptions' in field &&
@@ -28,33 +41,41 @@ export const resolveAllFilterOptions = async ({
2841
const options = await resolveFilterOptions(field.filterOptions, {
2942
id: undefined,
3043
blockData: undefined,
31-
data: {}, // use empty object to prevent breaking queries when accessing properties of data
44+
data: {}, // use empty object to prevent breaking queries when accessing properties of `data`
3245
relationTo: field.relationTo,
3346
req,
34-
siblingData: {}, // use empty object to prevent breaking queries when accessing properties of data
47+
siblingData: {}, // use empty object to prevent breaking queries when accessing properties of `siblingData`
3548
user: req.user,
3649
})
3750

38-
resolvedFilterOptions.set(field.name, options)
51+
resolvedFilterOptions.set(fieldPath, options)
3952
}
4053

4154
if (fieldHasSubFields(field)) {
4255
await resolveAllFilterOptions({
4356
fields: field.fields,
57+
pathPrefix: fieldPath,
4458
req,
4559
result: resolvedFilterOptions,
4660
})
4761
}
4862

4963
if (field.type === 'tabs') {
5064
await Promise.all(
51-
field.tabs.map((tab) =>
52-
resolveAllFilterOptions({
65+
field.tabs.map(async (tab) => {
66+
const tabPath = tabHasName(tab)
67+
? fieldPath
68+
? `${fieldPath}.${tab.name}`
69+
: tab.name
70+
: fieldPath
71+
72+
await resolveAllFilterOptions({
5373
fields: tab.fields,
74+
pathPrefix: tabPath,
5475
req,
5576
result: resolvedFilterOptions,
56-
}),
57-
),
77+
})
78+
}),
5879
)
5980
}
6081
}),

packages/ui/src/elements/WhereBuilder/Condition/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
export type Props = {
1313
readonly addCondition: AddCondition
1414
readonly andIndex: number
15-
readonly fieldName: string
15+
readonly fieldPath: string
1616
readonly filterOptions: ResolvedFilterOptions
1717
readonly operator: Operator
1818
readonly orIndex: number
@@ -42,7 +42,7 @@ export const Condition: React.FC<Props> = (props) => {
4242
const {
4343
addCondition,
4444
andIndex,
45-
fieldName,
45+
fieldPath,
4646
filterOptions,
4747
operator,
4848
orIndex,
@@ -55,7 +55,7 @@ export const Condition: React.FC<Props> = (props) => {
5555

5656
const { t } = useTranslation()
5757

58-
const reducedField = reducedFields.find((field) => field.value === fieldName)
58+
const reducedField = reducedFields.find((field) => field.value === fieldPath)
5959

6060
const [internalValue, setInternalValue] = useState<Value>(value)
6161

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,12 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
143143
{Array.isArray(or?.and) &&
144144
or.and.map((_, andIndex) => {
145145
const condition = conditions[orIndex].and[andIndex]
146-
const fieldName = Object.keys(condition)[0]
146+
const fieldPath = Object.keys(condition)[0]
147147

148148
const operator =
149-
(Object.keys(condition?.[fieldName] || {})?.[0] as Operator) || undefined
149+
(Object.keys(condition?.[fieldPath] || {})?.[0] as Operator) || undefined
150150

151-
const value = condition?.[fieldName]?.[operator] || undefined
151+
const value = condition?.[fieldPath]?.[operator] || undefined
152152

153153
return (
154154
<li key={andIndex}>
@@ -158,13 +158,13 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
158158
<Condition
159159
addCondition={addCondition}
160160
andIndex={andIndex}
161-
fieldName={fieldName}
162-
filterOptions={resolvedFilterOptions?.get(fieldName)}
161+
fieldPath={fieldPath}
162+
filterOptions={resolvedFilterOptions?.get(fieldPath)}
163163
operator={operator}
164164
orIndex={orIndex}
165165
reducedFields={reducedFields}
166166
removeCondition={removeCondition}
167-
RenderedFilter={renderedFilters?.get(fieldName)}
167+
RenderedFilter={renderedFilters?.get(fieldPath)}
168168
updateCondition={updateCondition}
169169
value={value}
170170
/>

packages/ui/src/utilities/reduceFieldsToOptions.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type ReduceFieldOptionsArgs = {
1919
}
2020

2121
/**
22-
* Reduces a field map to a flat array of fields with labels and values.
23-
* Used in the WhereBuilder component to render the fields in the dropdown.
22+
* Transforms a fields schema into a flattened array of fields with labels and values.
23+
* Used in the `WhereBuilder` component to render the fields in the dropdown.
2424
*/
2525
export const reduceFieldsToOptions = ({
2626
fields,

test/admin/e2e/list-view/e2e.spec.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ let payload: PayloadTestSDK<Config>
3636

3737
import { listViewSelectAPISlug } from 'admin/collections/ListViewSelectAPI/index.js'
3838
import { devUser } from 'credentials.js'
39-
import { addListFilter } from 'helpers/e2e/addListFilter.js'
40-
import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js'
39+
import {
40+
openListColumns,
41+
reorderColumns,
42+
sortColumn,
43+
toggleColumn,
44+
waitForColumnInURL,
45+
} from 'helpers/e2e/columns/index.js'
46+
import { addListFilter, openListFilters } from 'helpers/e2e/filters/index.js'
4147
import { goToNextPage, goToPreviousPage } from 'helpers/e2e/goToNextPage.js'
4248
import { goToFirstCell } from 'helpers/e2e/navigateToDoc.js'
43-
import { openListColumns } from 'helpers/e2e/openListColumns.js'
44-
import { openListFilters } from 'helpers/e2e/openListFilters.js'
4549
import { deletePreferences } from 'helpers/e2e/preferences.js'
46-
import { sortColumn } from 'helpers/e2e/sortColumn.js'
47-
import { toggleColumn, waitForColumnInURL } from 'helpers/e2e/toggleColumn.js'
4850
import { openDocDrawer } from 'helpers/e2e/toggleDocDrawer.js'
4951
import { closeListDrawer } from 'helpers/e2e/toggleListDrawer.js'
5052
import path from 'path'
@@ -53,9 +55,8 @@ import { fileURLToPath } from 'url'
5355

5456
import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
5557

56-
import { reorderColumns } from '../../../helpers/e2e/reorderColumns.js'
5758
import { reInitializeDB } from '../../../helpers/reInitializeDB.js'
58-
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
59+
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
5960

6061
const filename = fileURLToPath(import.meta.url)
6162
const currentFolder = path.dirname(filename)
@@ -719,7 +720,6 @@ describe('List View', () => {
719720
page,
720721
fieldLabel: 'Tab 1 > Title',
721722
operatorLabel: 'equals',
722-
skipValueInput: true,
723723
})
724724

725725
const valueInput = whereBuilder.locator('.condition__value >> input')
@@ -857,7 +857,6 @@ describe('List View', () => {
857857
page,
858858
fieldLabel: 'Self Relation',
859859
operatorLabel: 'equals',
860-
skipValueInput: true,
861860
})
862861

863862
const valueField = whereBuilder.locator('.condition__value')

test/bulk-edit/e2e.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { BrowserContext, Locator, Page } from '@playwright/test'
22
import type { PayloadTestSDK } from 'helpers/sdk/index.js'
33

44
import { expect, test } from '@playwright/test'
5-
import { addListFilter } from 'helpers/e2e/addListFilter.js'
65
import { addArrayRow } from 'helpers/e2e/fields/array/index.js'
6+
import { addListFilter } from 'helpers/e2e/filters/index.js'
77
import { selectInput } from 'helpers/e2e/selectInput.js'
88
import { toggleBlockOrArrayRow } from 'helpers/e2e/toggleCollapsible.js'
99
import * as path from 'path'
@@ -642,7 +642,6 @@ test.describe('Bulk Edit', () => {
642642
fieldLabel: 'ID',
643643
operatorLabel: 'equals',
644644
value: originalDoc.id,
645-
skipValueInput: false,
646645
})
647646

648647
// select first item

test/fields-relationship/collections/Relationship/index.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,30 @@ export const Relationship: CollectionConfig = {
9393
label: 'Collapsible',
9494
fields: [
9595
{
96-
name: 'nestedRelationshipFilteredByField',
96+
name: 'filteredByFieldInCollapsible',
97+
filterOptions: () => {
98+
return {
99+
filter: {
100+
equals: 'Include me',
101+
},
102+
}
103+
},
104+
admin: {
105+
description:
106+
'This will filter the relationship options if the filter field in this document is set to "Include me"',
107+
},
108+
relationTo: slug,
109+
type: 'relationship',
110+
},
111+
],
112+
},
113+
{
114+
name: 'array',
115+
type: 'array',
116+
label: 'Array',
117+
fields: [
118+
{
119+
name: 'filteredByFieldInArray',
97120
filterOptions: () => {
98121
return {
99122
filter: {

0 commit comments

Comments
 (0)