Skip to content

Commit da6bc55

Browse files
authored
fix(ui): ensures admin.disableListFilter is disabled despite url search params (#9874)
Continuation of #9846 and partial fix for #9774. When setting `admin.disableListFilter` retroactively, it remains active within the list filter controls. Same for when the URL search query contains one of these fields, except this will actually display the _wrong_ field, falling back to the _first_ field from the config. The fix is to properly disable the condition for this field if it's an active filter, while still preventing it from ever rendering as an option within the field selector itself.
1 parent f7172b5 commit da6bc55

File tree

12 files changed

+147
-66
lines changed

12 files changed

+147
-66
lines changed

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client'
2-
import React, { useEffect, useState } from 'react'
2+
import React, { useEffect, useMemo, useState } from 'react'
33

44
import type { FieldCondition } from '../types.js'
55

@@ -64,6 +64,11 @@ export const Condition: React.FC<Props> = (props) => {
6464
RenderedFilter,
6565
updateCondition,
6666
} = props
67+
68+
const options = useMemo(() => {
69+
return fields.filter(({ field }) => !field.admin.disableListFilter)
70+
}, [fields])
71+
6772
const [internalField, setInternalField] = useState<FieldCondition>(() =>
6873
fields.find((field) => fieldName === field.value),
6974
)
@@ -113,25 +118,34 @@ export const Condition: React.FC<Props> = (props) => {
113118
valueOptions = internalField.field.options
114119
}
115120

121+
const disabled =
122+
(!internalField?.value && typeof internalField?.value !== 'number') ||
123+
internalField?.field?.admin?.disableListFilter
124+
116125
return (
117126
<div className={baseClass}>
118127
<div className={`${baseClass}__wrap`}>
119128
<div className={`${baseClass}__inputs`}>
120129
<div className={`${baseClass}__field`}>
121130
<ReactSelect
131+
disabled={disabled}
122132
isClearable={false}
123133
onChange={(field: Option) => {
124134
setInternalField(fields.find((f) => f.value === field.value))
125135
setInternalOperatorOption(undefined)
126136
setInternalQueryValue(undefined)
127137
}}
128-
options={fields}
129-
value={fields.find((field) => internalField?.value === field.value) || fields[0]}
138+
options={options}
139+
value={
140+
fields.find((field) => internalField?.value === field.value) || {
141+
value: internalField?.value,
142+
}
143+
}
130144
/>
131145
</div>
132146
<div className={`${baseClass}__operator`}>
133147
<ReactSelect
134-
disabled={!internalField?.value && typeof internalField?.value !== 'number'}
148+
disabled={disabled}
135149
isClearable={false}
136150
onChange={(operator: Option<Operator>) => {
137151
setInternalOperatorOption(operator.value)
@@ -148,7 +162,7 @@ export const Condition: React.FC<Props> = (props) => {
148162
{RenderedFilter || (
149163
<DefaultFilter
150164
booleanSelect={booleanSelect}
151-
disabled={!internalOperatorOption}
165+
disabled={!internalOperatorOption || internalField?.field?.admin?.disableListFilter}
152166
internalField={internalField}
153167
onChange={setInternalQueryValue}
154168
operator={internalOperatorOption}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const reduceClientFields = ({
2929
pathPrefix,
3030
}: ReduceClientFieldsArgs): FieldCondition[] => {
3131
return fields.reduce((reduced, field) => {
32-
if (field.admin?.disableListFilter || (fieldIsHiddenOrDisabled(field) && !fieldIsID(field))) {
32+
if (fieldIsHiddenOrDisabled(field) && !fieldIsID(field)) {
3333
return reduced
3434
}
3535

packages/ui/src/providers/ListQuery/index.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,19 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
124124
}
125125
},
126126
[
127-
modifySearchParams,
128127
currentQuery?.page,
129128
currentQuery?.limit,
129+
currentQuery?.search,
130130
currentQuery?.sort,
131131
currentQuery?.where,
132-
currentQuery?.search,
133132
preferenceKey,
134-
router,
135-
setPreference,
133+
defaultLimit,
134+
defaultSort,
135+
modifySearchParams,
136136
onQueryChange,
137137
onQueryChangeFromProps,
138+
setPreference,
139+
router,
138140
],
139141
)
140142

test/admin/e2e/2/e2e.spec.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ const description = 'Description'
2626

2727
let payload: PayloadTestSDK<Config>
2828

29-
import { goToFirstCell, navigateToDoc } from 'helpers/e2e/navigateToDoc.js'
30-
import { openListColumns, toggleColumn } from 'helpers/e2e/toggleColumn.js'
29+
import { goToFirstCell } from 'helpers/e2e/navigateToDoc.js'
30+
import { openListColumns } from 'helpers/e2e/openListColumns.js'
31+
import { openListFilters } from 'helpers/e2e/openListFilters.js'
32+
import { toggleColumn } from 'helpers/e2e/toggleColumn.js'
3133
import path from 'path'
3234
import { wait } from 'payload/shared'
3335
import { fileURLToPath } from 'url'
@@ -273,9 +275,7 @@ describe('admin2', () => {
273275

274276
await expect(page.locator(tableRowLocator)).toHaveCount(2)
275277

276-
await page.locator('.list-controls__toggle-where').click()
277-
// wait until the filter UI is visible and fully expanded
278-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
278+
await openListFilters(page, {})
279279

280280
await page.locator('.where-builder__add-first-filter').click()
281281

@@ -313,8 +313,7 @@ describe('admin2', () => {
313313

314314
// open the column controls
315315
await page.locator('.list-controls__toggle-columns').click()
316-
await page.locator('.list-controls__toggle-where').click()
317-
await page.waitForSelector('.list-controls__where.rah-static--height-auto')
316+
await openListFilters(page, {})
318317
await page.locator('.where-builder__add-first-filter').click()
319318

320319
const operatorField = page.locator('.condition__operator')
@@ -461,7 +460,7 @@ describe('admin2', () => {
461460
await page.goto(`${postsUrl.list}?limit=10&page=2`)
462461

463462
// add filter
464-
await page.locator('.list-controls__toggle-where').click()
463+
await openListFilters(page, {})
465464
await page.locator('.where-builder__add-first-filter').click()
466465
await page.locator('.condition__field .rs__control').click()
467466
const options = page.locator('.rs__option')
@@ -776,7 +775,7 @@ describe('admin2', () => {
776775
).toHaveText('Title')
777776

778777
// filters
779-
await page.locator('.list-controls__toggle-where').click()
778+
await openListFilters(page, {})
780779
await page.locator('.where-builder__add-first-filter').click()
781780
await page.locator('.condition__field .rs__control').click()
782781
const options = page.locator('.rs__option')

test/fields-relationship/e2e.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
44
import { openDocControls } from 'helpers/e2e/openDocControls.js'
5+
import { openListFilters } from 'helpers/e2e/openListFilters.js'
56
import path from 'path'
67
import { wait } from 'payload/shared'
78
import { fileURLToPath } from 'url'
@@ -485,8 +486,7 @@ describe('fields - relationship', () => {
485486
await page.goto(versionedRelationshipFieldURL.list)
486487

487488
await page.locator('.list-controls__toggle-columns').click()
488-
await page.locator('.list-controls__toggle-where').click()
489-
await page.waitForSelector('.list-controls__where.rah-static--height-auto')
489+
await openListFilters(page, {})
490490
await page.locator('.where-builder__add-first-filter').click()
491491

492492
const conditionField = page.locator('.condition__field')

test/fields/collections/Email/e2e.spec.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
4+
import { openListFilters } from 'helpers/e2e/openListFilters.js'
45
import path from 'path'
56
import { wait } from 'payload/shared'
67
import { fileURLToPath } from 'url'
@@ -89,7 +90,7 @@ describe('Email', () => {
8990

9091
test('should show field in filter when admin.disableListColumn is true', async () => {
9192
await page.goto(url.list)
92-
await page.locator('.list-controls__toggle-where').click()
93+
await openListFilters(page, {})
9394
await page.locator('.where-builder__add-first-filter').click()
9495

9596
const initialField = page.locator('.condition__field')
@@ -100,7 +101,7 @@ describe('Email', () => {
100101
).toBeVisible()
101102
})
102103

103-
test('should display field in list view column selector if admin.disableListColumn is false and admin.disableListFilter is true', async () => {
104+
test('should display field in list view column selector despite admin.disableListFilter', async () => {
104105
await page.goto(url.list)
105106
await page.locator('.list-controls__toggle-columns').click()
106107

@@ -116,7 +117,7 @@ describe('Email', () => {
116117

117118
test('should hide field in filter when admin.disableListFilter is true', async () => {
118119
await page.goto(url.list)
119-
await page.locator('.list-controls__toggle-where').click()
120+
await openListFilters(page, {})
120121
await page.locator('.where-builder__add-first-filter').click()
121122

122123
const initialField = page.locator('.condition__field')
@@ -188,8 +189,7 @@ describe('Email', () => {
188189
await page.goto(url.list)
189190

190191
// open the first filter options
191-
await page.locator('.list-controls__toggle-where').click()
192-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
192+
await openListFilters(page, {})
193193
await page.locator('.where-builder__add-first-filter').click()
194194

195195
const firstInitialField = page.locator('.condition__field')
@@ -230,8 +230,7 @@ describe('Email', () => {
230230
await page.goto(url.list)
231231

232232
// open the first filter options
233-
await page.locator('.list-controls__toggle-where').click()
234-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
233+
await openListFilters(page, {})
235234
await page.locator('.where-builder__add-first-filter').click()
236235

237236
const firstInitialField = page.locator('.condition__field')
@@ -261,8 +260,7 @@ describe('Email', () => {
261260
await page.goto(url.list)
262261

263262
// open the first filter options
264-
await page.locator('.list-controls__toggle-where').click()
265-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
263+
await openListFilters(page, {})
266264
await page.locator('.where-builder__add-first-filter').click()
267265

268266
const firstInitialField = page.locator('.condition__field')

test/fields/collections/Number/e2e.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
4+
import { openListFilters } from 'helpers/e2e/openListFilters.js'
45
import path from 'path'
56
import { wait } from 'payload/shared'
67
import { fileURLToPath } from 'url'
@@ -73,8 +74,7 @@ describe('Number', () => {
7374
test('should filter Number fields in the collection view - greaterThanOrEqual', async () => {
7475
await page.goto(url.list)
7576
await expect(page.locator('table >> tbody >> tr')).toHaveCount(3)
76-
await page.locator('.list-controls__toggle-where').click()
77-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
77+
await openListFilters(page, {})
7878
await page.locator('.where-builder__add-first-filter').click()
7979
const initialField = page.locator('.condition__field')
8080
const operatorField = page.locator('.condition__operator')

test/fields/collections/Relationship/e2e.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Page } from '@playwright/test'
33
import { expect, test } from '@playwright/test'
44
import { navigateToDoc } from 'helpers/e2e/navigateToDoc.js'
55
import { openDocControls } from 'helpers/e2e/openDocControls.js'
6+
import { openListFilters } from 'helpers/e2e/openListFilters.js'
67
import path from 'path'
78
import { wait } from 'payload/shared'
89
import { fileURLToPath } from 'url'
@@ -614,8 +615,7 @@ describe('relationship', () => {
614615
await page.locator('.list-controls__toggle-columns').click()
615616
await wait(400)
616617

617-
await page.locator('.list-controls__toggle-where').click()
618-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
618+
await openListFilters(page, {})
619619
await wait(400)
620620

621621
await page.locator('.where-builder__add-first-filter').click()

test/fields/collections/Text/e2e.spec.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import type { Page } from '@playwright/test'
22
import type { GeneratedTypes } from 'helpers/sdk/types.js'
33

44
import { expect, test } from '@playwright/test'
5-
import { openListColumns, toggleColumn } from 'helpers/e2e/toggleColumn.js'
5+
import { openListColumns } from 'helpers/e2e/openListColumns.js'
6+
import { openListFilters } from 'helpers/e2e/openListFilters.js'
7+
import { toggleColumn } from 'helpers/e2e/toggleColumn.js'
68
import { upsertPrefs } from 'helpers/e2e/upsertPrefs.js'
79
import path from 'path'
810
import { wait } from 'payload/shared'
11+
import * as qs from 'qs-esm'
912
import { fileURLToPath } from 'url'
1013

1114
import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
@@ -180,7 +183,7 @@ describe('Text', () => {
180183

181184
test('should show field in filter when admin.disableListColumn is true', async () => {
182185
await page.goto(url.list)
183-
await page.locator('.list-controls__toggle-where').click()
186+
await openListFilters(page, {})
184187
await page.locator('.where-builder__add-first-filter').click()
185188

186189
const initialField = page.locator('.condition__field')
@@ -191,7 +194,7 @@ describe('Text', () => {
191194
).toBeVisible()
192195
})
193196

194-
test('should display field in list view column selector if admin.disableListColumn is false and admin.disableListFilter is true', async () => {
197+
test('should display field in list view column selector despite admin.disableListFilter', async () => {
195198
await page.goto(url.list)
196199
await page.locator('.list-controls__toggle-columns').click()
197200

@@ -205,6 +208,43 @@ describe('Text', () => {
205208
).toBeVisible()
206209
})
207210

211+
test('should disable field when admin.disableListFilter is true but still exists in the query', async () => {
212+
await page.goto(
213+
`${url.list}${qs.stringify(
214+
{
215+
where: {
216+
or: [
217+
{
218+
and: [
219+
{
220+
disableListFilterText: {
221+
equals: 'Disable List Filter Text',
222+
},
223+
},
224+
],
225+
},
226+
],
227+
},
228+
},
229+
{ addQueryPrefix: true },
230+
)}`,
231+
)
232+
233+
await openListFilters(page, {})
234+
235+
const condition = page.locator('.condition__field')
236+
await expect(condition.locator('input.rs__input')).toBeDisabled()
237+
await expect(page.locator('.condition__operator input.rs__input')).toBeDisabled()
238+
await expect(page.locator('.condition__value input.condition-value-text')).toBeDisabled()
239+
await expect(condition.locator('.rs__single-value')).toHaveText('Disable List Filter Text')
240+
await page.locator('button.condition__actions-add').click()
241+
const condition2 = page.locator('.condition__field').nth(1)
242+
await condition2.click()
243+
await expect(
244+
condition2?.locator('.rs__menu-list:has-text("Disable List Filter Text")'),
245+
).toBeHidden()
246+
})
247+
208248
test('should respect admin.disableListColumn despite preferences', async () => {
209249
await upsertPrefs<Config, GeneratedTypes<any>>({
210250
payload,
@@ -233,7 +273,7 @@ describe('Text', () => {
233273

234274
test('should hide field in filter when admin.disableListFilter is true', async () => {
235275
await page.goto(url.list)
236-
await page.locator('.list-controls__toggle-where').click()
276+
await openListFilters(page, {})
237277
await page.locator('.where-builder__add-first-filter').click()
238278

239279
const initialField = page.locator('.condition__field')
@@ -298,8 +338,7 @@ describe('Text', () => {
298338
await page.goto(url.list)
299339

300340
// open the first filter options
301-
await page.locator('.list-controls__toggle-where').click()
302-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
341+
await openListFilters(page, {})
303342
await page.locator('.where-builder__add-first-filter').click()
304343

305344
const firstInitialField = page.locator('.condition__field')
@@ -340,8 +379,7 @@ describe('Text', () => {
340379
await page.goto(url.list)
341380

342381
// open the first filter options
343-
await page.locator('.list-controls__toggle-where').click()
344-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
382+
await openListFilters(page, {})
345383
await page.locator('.where-builder__add-first-filter').click()
346384

347385
const firstInitialField = page.locator('.condition__field')
@@ -371,8 +409,7 @@ describe('Text', () => {
371409
await page.goto(url.list)
372410

373411
// open the first filter options
374-
await page.locator('.list-controls__toggle-where').click()
375-
await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible()
412+
await openListFilters(page, {})
376413
await page.locator('.where-builder__add-first-filter').click()
377414

378415
const firstInitialField = page.locator('.condition__field')

0 commit comments

Comments
 (0)