feat(ui): allow array fields to be filtered in list view (#11925)
### What? Allows array fields to be filtered in the list view. ### Why? Array fields were not filterable in the list view although all other field types were filterable already. ### How? Adds handling for array fields as filter option. 
This commit is contained in:
@@ -99,7 +99,7 @@ export const reduceFields = ({
|
|||||||
return reduced
|
return reduced
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === 'group' && 'fields' in field) {
|
if ((field.type === 'group' || field.type === 'array') && 'fields' in field) {
|
||||||
const translatedLabel = getTranslation(field.label || '', i18n)
|
const translatedLabel = getTranslation(field.label || '', i18n)
|
||||||
|
|
||||||
const labelWithPrefix = labelPrefix
|
const labelWithPrefix = labelPrefix
|
||||||
|
|||||||
19
test/admin/collections/Array.ts
Normal file
19
test/admin/collections/Array.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
|
import { arrayCollectionSlug } from '../slugs.js'
|
||||||
|
|
||||||
|
export const Array: CollectionConfig = {
|
||||||
|
slug: arrayCollectionSlug,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'array',
|
||||||
|
type: 'array',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import path from 'path'
|
|||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||||
|
import { Array } from './collections/Array.js'
|
||||||
import { BaseListFilter } from './collections/BaseListFilter.js'
|
import { BaseListFilter } from './collections/BaseListFilter.js'
|
||||||
import { CustomFields } from './collections/CustomFields/index.js'
|
import { CustomFields } from './collections/CustomFields/index.js'
|
||||||
import { CustomViews1 } from './collections/CustomViews1.js'
|
import { CustomViews1 } from './collections/CustomViews1.js'
|
||||||
@@ -158,6 +159,7 @@ export default buildConfigWithDefaults({
|
|||||||
CollectionGroup2A,
|
CollectionGroup2A,
|
||||||
CollectionGroup2B,
|
CollectionGroup2B,
|
||||||
Geo,
|
Geo,
|
||||||
|
Array,
|
||||||
DisableDuplicate,
|
DisableDuplicate,
|
||||||
DisableCopyToLocale,
|
DisableCopyToLocale,
|
||||||
BaseListFilter,
|
BaseListFilter,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js'
|
|||||||
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
|
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
|
||||||
import { customAdminRoutes } from '../../shared.js'
|
import { customAdminRoutes } from '../../shared.js'
|
||||||
import {
|
import {
|
||||||
|
arrayCollectionSlug,
|
||||||
customViews1CollectionSlug,
|
customViews1CollectionSlug,
|
||||||
geoCollectionSlug,
|
geoCollectionSlug,
|
||||||
listDrawerSlug,
|
listDrawerSlug,
|
||||||
@@ -57,6 +58,7 @@ const dirname = path.resolve(currentFolder, '../../')
|
|||||||
describe('List View', () => {
|
describe('List View', () => {
|
||||||
let page: Page
|
let page: Page
|
||||||
let geoUrl: AdminUrlUtil
|
let geoUrl: AdminUrlUtil
|
||||||
|
let arrayUrl: AdminUrlUtil
|
||||||
let postsUrl: AdminUrlUtil
|
let postsUrl: AdminUrlUtil
|
||||||
let baseListFiltersUrl: AdminUrlUtil
|
let baseListFiltersUrl: AdminUrlUtil
|
||||||
let customViewsUrl: AdminUrlUtil
|
let customViewsUrl: AdminUrlUtil
|
||||||
@@ -79,6 +81,7 @@ describe('List View', () => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
geoUrl = new AdminUrlUtil(serverURL, geoCollectionSlug)
|
geoUrl = new AdminUrlUtil(serverURL, geoCollectionSlug)
|
||||||
|
arrayUrl = new AdminUrlUtil(serverURL, arrayCollectionSlug)
|
||||||
postsUrl = new AdminUrlUtil(serverURL, postsCollectionSlug)
|
postsUrl = new AdminUrlUtil(serverURL, postsCollectionSlug)
|
||||||
with300DocumentsUrl = new AdminUrlUtil(serverURL, with300DocumentsSlug)
|
with300DocumentsUrl = new AdminUrlUtil(serverURL, with300DocumentsSlug)
|
||||||
baseListFiltersUrl = new AdminUrlUtil(serverURL, 'base-list-filters')
|
baseListFiltersUrl = new AdminUrlUtil(serverURL, 'base-list-filters')
|
||||||
@@ -389,6 +392,32 @@ describe('List View', () => {
|
|||||||
await expect(page.locator(tableRowLocator)).toHaveCount(2)
|
await expect(page.locator(tableRowLocator)).toHaveCount(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should allow to filter in array field', async () => {
|
||||||
|
await createArray()
|
||||||
|
|
||||||
|
await page.goto(arrayUrl.list)
|
||||||
|
await expect(page.locator(tableRowLocator)).toHaveCount(1)
|
||||||
|
|
||||||
|
await addListFilter({
|
||||||
|
page,
|
||||||
|
fieldLabel: 'Array > Text',
|
||||||
|
operatorLabel: 'equals',
|
||||||
|
value: 'test',
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(page.locator(tableRowLocator)).toHaveCount(1)
|
||||||
|
|
||||||
|
await page.locator('.condition__actions .btn.condition__actions-remove').click()
|
||||||
|
await addListFilter({
|
||||||
|
page,
|
||||||
|
fieldLabel: 'Array > Text',
|
||||||
|
operatorLabel: 'equals',
|
||||||
|
value: 'not-matching',
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(page.locator(tableRowLocator)).toHaveCount(0)
|
||||||
|
})
|
||||||
|
|
||||||
test('should reset filter value when a different field is selected', async () => {
|
test('should reset filter value when a different field is selected', async () => {
|
||||||
const id = (await page.locator('.cell-id').first().innerText()).replace('ID: ', '')
|
const id = (await page.locator('.cell-id').first().innerText()).replace('ID: ', '')
|
||||||
|
|
||||||
@@ -1405,3 +1434,12 @@ async function createGeo(overrides?: Partial<Geo>): Promise<Geo> {
|
|||||||
},
|
},
|
||||||
}) as unknown as Promise<Geo>
|
}) as unknown as Promise<Geo>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createArray() {
|
||||||
|
return payload.create({
|
||||||
|
collection: arrayCollectionSlug,
|
||||||
|
data: {
|
||||||
|
array: [{ text: 'test' }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export interface Config {
|
|||||||
'group-two-collection-ones': GroupTwoCollectionOne;
|
'group-two-collection-ones': GroupTwoCollectionOne;
|
||||||
'group-two-collection-twos': GroupTwoCollectionTwo;
|
'group-two-collection-twos': GroupTwoCollectionTwo;
|
||||||
geo: Geo;
|
geo: Geo;
|
||||||
|
array: Array;
|
||||||
'disable-duplicate': DisableDuplicate;
|
'disable-duplicate': DisableDuplicate;
|
||||||
'disable-copy-to-locale': DisableCopyToLocale;
|
'disable-copy-to-locale': DisableCopyToLocale;
|
||||||
'base-list-filters': BaseListFilter;
|
'base-list-filters': BaseListFilter;
|
||||||
@@ -108,6 +109,7 @@ export interface Config {
|
|||||||
'group-two-collection-ones': GroupTwoCollectionOnesSelect<false> | GroupTwoCollectionOnesSelect<true>;
|
'group-two-collection-ones': GroupTwoCollectionOnesSelect<false> | GroupTwoCollectionOnesSelect<true>;
|
||||||
'group-two-collection-twos': GroupTwoCollectionTwosSelect<false> | GroupTwoCollectionTwosSelect<true>;
|
'group-two-collection-twos': GroupTwoCollectionTwosSelect<false> | GroupTwoCollectionTwosSelect<true>;
|
||||||
geo: GeoSelect<false> | GeoSelect<true>;
|
geo: GeoSelect<false> | GeoSelect<true>;
|
||||||
|
array: ArraySelect<false> | ArraySelect<true>;
|
||||||
'disable-duplicate': DisableDuplicateSelect<false> | DisableDuplicateSelect<true>;
|
'disable-duplicate': DisableDuplicateSelect<false> | DisableDuplicateSelect<true>;
|
||||||
'disable-copy-to-locale': DisableCopyToLocaleSelect<false> | DisableCopyToLocaleSelect<true>;
|
'disable-copy-to-locale': DisableCopyToLocaleSelect<false> | DisableCopyToLocaleSelect<true>;
|
||||||
'base-list-filters': BaseListFiltersSelect<false> | BaseListFiltersSelect<true>;
|
'base-list-filters': BaseListFiltersSelect<false> | BaseListFiltersSelect<true>;
|
||||||
@@ -414,6 +416,21 @@ export interface Geo {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "array".
|
||||||
|
*/
|
||||||
|
export interface Array {
|
||||||
|
id: string;
|
||||||
|
array?:
|
||||||
|
| {
|
||||||
|
text?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "disable-duplicate".
|
* via the `definition` "disable-duplicate".
|
||||||
@@ -534,6 +551,10 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'geo';
|
relationTo: 'geo';
|
||||||
value: string | Geo;
|
value: string | Geo;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'array';
|
||||||
|
value: string | Array;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'disable-duplicate';
|
relationTo: 'disable-duplicate';
|
||||||
value: string | DisableDuplicate;
|
value: string | DisableDuplicate;
|
||||||
@@ -818,6 +839,20 @@ export interface GeoSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "array_select".
|
||||||
|
*/
|
||||||
|
export interface ArraySelect<T extends boolean = true> {
|
||||||
|
array?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "disable-duplicate_select".
|
* via the `definition` "disable-duplicate_select".
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export const usersCollectionSlug = 'users'
|
|||||||
export const customViews1CollectionSlug = 'custom-views-one'
|
export const customViews1CollectionSlug = 'custom-views-one'
|
||||||
export const customViews2CollectionSlug = 'custom-views-two'
|
export const customViews2CollectionSlug = 'custom-views-two'
|
||||||
export const geoCollectionSlug = 'geo'
|
export const geoCollectionSlug = 'geo'
|
||||||
|
export const arrayCollectionSlug = 'array'
|
||||||
export const postsCollectionSlug = 'posts'
|
export const postsCollectionSlug = 'posts'
|
||||||
export const group1Collection1Slug = 'group-one-collection-ones'
|
export const group1Collection1Slug = 'group-one-collection-ones'
|
||||||
export const group1Collection2Slug = 'group-one-collection-twos'
|
export const group1Collection2Slug = 'group-one-collection-twos'
|
||||||
@@ -23,6 +24,7 @@ export const collectionSlugs = [
|
|||||||
customViews1CollectionSlug,
|
customViews1CollectionSlug,
|
||||||
customViews2CollectionSlug,
|
customViews2CollectionSlug,
|
||||||
geoCollectionSlug,
|
geoCollectionSlug,
|
||||||
|
arrayCollectionSlug,
|
||||||
postsCollectionSlug,
|
postsCollectionSlug,
|
||||||
group1Collection1Slug,
|
group1Collection1Slug,
|
||||||
group1Collection2Slug,
|
group1Collection2Slug,
|
||||||
|
|||||||
Reference in New Issue
Block a user