feat(ui): allows filtering on group and tab fields from list controls (#6647)

## Description

Adds the ability to filter by fields within a `group` or **named** `tab`
via the list controls.

Note: added missing translations for the `within` and `intersects`
operator options, these are displayed in the filters for `point` and
`JSON` fields.

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] New feature (non-breaking change which adds functionality)

## Checklist:

- [X] Existing test suite passes locally with my changes
This commit is contained in:
Jessica Chowdhury
2024-07-02 13:32:17 -04:00
committed by GitHub
parent 2a2ab53189
commit d4d410141c
40 changed files with 159 additions and 33 deletions

View File

@@ -251,6 +251,8 @@ export const clientTranslationKeys = createClientTranslationKeys([
'operators:isLessThan', 'operators:isLessThan',
'operators:isGreaterThanOrEqualTo', 'operators:isGreaterThanOrEqualTo',
'operators:isLessThanOrEqualTo', 'operators:isLessThanOrEqualTo',
'operators:within',
'operators:intersects',
'upload:crop', 'upload:crop',
'upload:cropToolDescription', 'upload:cropToolDescription',

View File

@@ -302,6 +302,7 @@ export const arTranslations: DefaultTranslationsObject = {
contains: 'يحتوي', contains: 'يحتوي',
equals: 'يساوي', equals: 'يساوي',
exists: 'موجود', exists: 'موجود',
intersects: 'يتقاطع',
isGreaterThan: 'أكبر من', isGreaterThan: 'أكبر من',
isGreaterThanOrEqualTo: 'أكبر أو يساوي', isGreaterThanOrEqualTo: 'أكبر أو يساوي',
isIn: 'موجود في', isIn: 'موجود في',
@@ -311,6 +312,7 @@ export const arTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'لا يساوي', isNotEqualTo: 'لا يساوي',
isNotIn: 'غير موجود في', isNotIn: 'غير موجود في',
near: 'قريب من', near: 'قريب من',
within: 'في غضون',
}, },
upload: { upload: {
crop: 'محصول', crop: 'محصول',

View File

@@ -305,6 +305,7 @@ export const azTranslations: DefaultTranslationsObject = {
contains: 'daxilində', contains: 'daxilində',
equals: 'bərabərdir', equals: 'bərabərdir',
exists: 'mövcuddur', exists: 'mövcuddur',
intersects: 'kəsişir',
isGreaterThan: 'dən böyük', isGreaterThan: 'dən böyük',
isGreaterThanOrEqualTo: 'böyük və ya bərabər', isGreaterThanOrEqualTo: 'böyük və ya bərabər',
isIn: 'daxildir', isIn: 'daxildir',
@@ -314,6 +315,7 @@ export const azTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'bərabər deyil', isNotEqualTo: 'bərabər deyil',
isNotIn: 'daxil deyil', isNotIn: 'daxil deyil',
near: 'yaxın', near: 'yaxın',
within: 'daxilinde',
}, },
upload: { upload: {
crop: 'Məhsul', crop: 'Məhsul',

View File

@@ -303,6 +303,7 @@ export const bgTranslations: DefaultTranslationsObject = {
contains: 'съдържа', contains: 'съдържа',
equals: 'е равно на', equals: 'е равно на',
exists: 'съществува', exists: 'съществува',
intersects: 'пресича',
isGreaterThan: 'е по-голямо от', isGreaterThan: 'е по-голямо от',
isGreaterThanOrEqualTo: 'е по-голямо от или равно на', isGreaterThanOrEqualTo: 'е по-голямо от или равно на',
isIn: 'е в', isIn: 'е в',
@@ -312,6 +313,7 @@ export const bgTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'не е равно на', isNotEqualTo: 'не е равно на',
isNotIn: 'не е в', isNotIn: 'не е в',
near: 'близко', near: 'близко',
within: 'в рамките на',
}, },
upload: { upload: {
crop: 'Изрязване', crop: 'Изрязване',

View File

@@ -303,6 +303,7 @@ export const csTranslations: DefaultTranslationsObject = {
contains: 'obsahuje', contains: 'obsahuje',
equals: 'rovná se', equals: 'rovná se',
exists: 'existuje', exists: 'existuje',
intersects: 'protíná se',
isGreaterThan: 'je větší než', isGreaterThan: 'je větší než',
isGreaterThanOrEqualTo: 'je větší nebo rovno', isGreaterThanOrEqualTo: 'je větší nebo rovno',
isIn: 'je v', isIn: 'je v',
@@ -312,6 +313,7 @@ export const csTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'není rovno', isNotEqualTo: 'není rovno',
isNotIn: 'není v', isNotIn: 'není v',
near: 'blízko', near: 'blízko',
within: 'uvnitř',
}, },
upload: { upload: {
crop: 'Ořez', crop: 'Ořez',

View File

@@ -309,6 +309,7 @@ export const deTranslations: DefaultTranslationsObject = {
contains: 'enthält', contains: 'enthält',
equals: 'gleich', equals: 'gleich',
exists: 'existiert', exists: 'existiert',
intersects: 'schneidet sich',
isGreaterThan: 'ist größer als', isGreaterThan: 'ist größer als',
isGreaterThanOrEqualTo: 'ist größer oder gleich', isGreaterThanOrEqualTo: 'ist größer oder gleich',
isIn: 'ist drin', isIn: 'ist drin',
@@ -318,6 +319,7 @@ export const deTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'ist nicht gleich', isNotEqualTo: 'ist nicht gleich',
isNotIn: 'ist nicht drin', isNotIn: 'ist nicht drin',
near: 'in der Nähe', near: 'in der Nähe',
within: 'innerhalb',
}, },
upload: { upload: {
crop: 'Zuschneiden', crop: 'Zuschneiden',

View File

@@ -306,6 +306,7 @@ export const enTranslations = {
contains: 'contains', contains: 'contains',
equals: 'equals', equals: 'equals',
exists: 'exists', exists: 'exists',
intersects: 'intersects',
isGreaterThan: 'is greater than', isGreaterThan: 'is greater than',
isGreaterThanOrEqualTo: 'is greater than or equal to', isGreaterThanOrEqualTo: 'is greater than or equal to',
isIn: 'is in', isIn: 'is in',
@@ -315,6 +316,7 @@ export const enTranslations = {
isNotEqualTo: 'is not equal to', isNotEqualTo: 'is not equal to',
isNotIn: 'is not in', isNotIn: 'is not in',
near: 'near', near: 'near',
within: 'within',
}, },
upload: { upload: {
crop: 'Crop', crop: 'Crop',

View File

@@ -308,6 +308,7 @@ export const esTranslations: DefaultTranslationsObject = {
contains: 'contiene', contains: 'contiene',
equals: 'igual', equals: 'igual',
exists: 'existe', exists: 'existe',
intersects: 'interseca',
isGreaterThan: 'es mayor que', isGreaterThan: 'es mayor que',
isGreaterThanOrEqualTo: 'es mayor o igual que', isGreaterThanOrEqualTo: 'es mayor o igual que',
isIn: 'está en', isIn: 'está en',
@@ -317,6 +318,7 @@ export const esTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'no es igual a', isNotEqualTo: 'no es igual a',
isNotIn: 'no está en', isNotIn: 'no está en',
near: 'cerca', near: 'cerca',
within: 'dentro de',
}, },
upload: { upload: {
crop: 'Cultivo', crop: 'Cultivo',

View File

@@ -303,6 +303,7 @@ export const faTranslations: DefaultTranslationsObject = {
contains: 'شامل', contains: 'شامل',
equals: 'برابر با', equals: 'برابر با',
exists: 'وجود دارد', exists: 'وجود دارد',
intersects: 'تلاقی',
isGreaterThan: 'بزرگتر است از', isGreaterThan: 'بزرگتر است از',
isGreaterThanOrEqualTo: 'بزرگتر یا مساوی است', isGreaterThanOrEqualTo: 'بزرگتر یا مساوی است',
isIn: 'هست در', isIn: 'هست در',
@@ -312,6 +313,7 @@ export const faTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'برابر نیست', isNotEqualTo: 'برابر نیست',
isNotIn: 'در این نیست', isNotIn: 'در این نیست',
near: 'نزدیک', near: 'نزدیک',
within: 'در داخل',
}, },
upload: { upload: {
crop: 'محصول', crop: 'محصول',

View File

@@ -312,6 +312,7 @@ export const frTranslations: DefaultTranslationsObject = {
contains: 'contient', contains: 'contient',
equals: 'est égal à', equals: 'est égal à',
exists: 'existe', exists: 'existe',
intersects: 'intersecte',
isGreaterThan: 'est supérieur à', isGreaterThan: 'est supérieur à',
isGreaterThanOrEqualTo: 'est supérieur ou égal à', isGreaterThanOrEqualTo: 'est supérieur ou égal à',
isIn: 'est dans', isIn: 'est dans',
@@ -321,6 +322,7 @@ export const frTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nest pas égal à', isNotEqualTo: 'nest pas égal à',
isNotIn: 'nest pas dans', isNotIn: 'nest pas dans',
near: 'proche', near: 'proche',
within: 'dans',
}, },
upload: { upload: {
crop: 'Recadrer', crop: 'Recadrer',

View File

@@ -298,6 +298,7 @@ export const heTranslations: DefaultTranslationsObject = {
contains: 'מכיל', contains: 'מכיל',
equals: 'שווה ל', equals: 'שווה ל',
exists: 'קיים', exists: 'קיים',
intersects: 'מצטלב',
isGreaterThan: 'גדול מ', isGreaterThan: 'גדול מ',
isGreaterThanOrEqualTo: 'גדול או שווה ל', isGreaterThanOrEqualTo: 'גדול או שווה ל',
isIn: 'נמצא ב', isIn: 'נמצא ב',
@@ -307,6 +308,7 @@ export const heTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'לא שווה ל', isNotEqualTo: 'לא שווה ל',
isNotIn: 'לא נמצא ב', isNotIn: 'לא נמצא ב',
near: 'קרוב ל', near: 'קרוב ל',
within: 'בתוך',
}, },
upload: { upload: {
crop: 'חתוך', crop: 'חתוך',

View File

@@ -304,6 +304,7 @@ export const hrTranslations: DefaultTranslationsObject = {
contains: 'sadrži', contains: 'sadrži',
equals: 'jednako', equals: 'jednako',
exists: 'postoji', exists: 'postoji',
intersects: 'presijeca',
isGreaterThan: 'je veće od', isGreaterThan: 'je veće od',
isGreaterThanOrEqualTo: 'je veće od ili jednako', isGreaterThanOrEqualTo: 'je veće od ili jednako',
isIn: 'je u', isIn: 'je u',
@@ -313,6 +314,7 @@ export const hrTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nije jednako', isNotEqualTo: 'nije jednako',
isNotIn: 'nije unutra', isNotIn: 'nije unutra',
near: 'blizu', near: 'blizu',
within: 'unutar',
}, },
upload: { upload: {
crop: 'Usjev', crop: 'Usjev',

View File

@@ -306,6 +306,7 @@ export const huTranslations: DefaultTranslationsObject = {
contains: 'tartalmaz', contains: 'tartalmaz',
equals: 'egyenlő', equals: 'egyenlő',
exists: 'létezik', exists: 'létezik',
intersects: 'metszéspontokban',
isGreaterThan: 'nagyobb, mint', isGreaterThan: 'nagyobb, mint',
isGreaterThanOrEqualTo: 'nagyobb vagy egyenlő, mint', isGreaterThanOrEqualTo: 'nagyobb vagy egyenlő, mint',
isIn: 'benne van', isIn: 'benne van',
@@ -315,6 +316,7 @@ export const huTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nem egyenlő', isNotEqualTo: 'nem egyenlő',
isNotIn: 'nincs benne', isNotIn: 'nincs benne',
near: 'közel', near: 'közel',
within: 'belül',
}, },
upload: { upload: {
crop: 'Termés', crop: 'Termés',

View File

@@ -306,6 +306,7 @@ export const itTranslations: DefaultTranslationsObject = {
contains: 'contiene', contains: 'contiene',
equals: 'uguale', equals: 'uguale',
exists: 'esiste', exists: 'esiste',
intersects: 'interseca',
isGreaterThan: 'è maggiore di', isGreaterThan: 'è maggiore di',
isGreaterThanOrEqualTo: 'è maggiore o uguale a', isGreaterThanOrEqualTo: 'è maggiore o uguale a',
isIn: 'è in', isIn: 'è in',
@@ -315,6 +316,7 @@ export const itTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'non è uguale a', isNotEqualTo: 'non è uguale a',
isNotIn: 'non è in', isNotIn: 'non è in',
near: 'vicino', near: 'vicino',
within: "all'interno",
}, },
upload: { upload: {
crop: 'Raccolto', crop: 'Raccolto',

View File

@@ -304,6 +304,7 @@ export const jaTranslations: DefaultTranslationsObject = {
contains: '含む', contains: '含む',
equals: '等しい', equals: '等しい',
exists: '存在す', exists: '存在す',
intersects: '交差する',
isGreaterThan: 'より大きい', isGreaterThan: 'より大きい',
isGreaterThanOrEqualTo: '以上', isGreaterThanOrEqualTo: '以上',
isIn: 'あります', isIn: 'あります',
@@ -313,6 +314,7 @@ export const jaTranslations: DefaultTranslationsObject = {
isNotEqualTo: '等しくない', isNotEqualTo: '等しくない',
isNotIn: '入っていません', isNotIn: '入っていません',
near: '近く', near: '近く',
within: '内で',
}, },
upload: { upload: {
crop: 'クロップ', crop: 'クロップ',

View File

@@ -303,6 +303,7 @@ export const koTranslations: DefaultTranslationsObject = {
contains: '포함', contains: '포함',
equals: '같음', equals: '같음',
exists: '존재', exists: '존재',
intersects: '교차합니다',
isGreaterThan: '보다 큼', isGreaterThan: '보다 큼',
isGreaterThanOrEqualTo: '보다 크거나 같음', isGreaterThanOrEqualTo: '보다 크거나 같음',
isIn: '포함됨', isIn: '포함됨',
@@ -312,6 +313,7 @@ export const koTranslations: DefaultTranslationsObject = {
isNotEqualTo: '같지 않음', isNotEqualTo: '같지 않음',
isNotIn: '포함되지 않음', isNotIn: '포함되지 않음',
near: '근처', near: '근처',
within: '내에서',
}, },
upload: { upload: {
crop: '자르기', crop: '자르기',

View File

@@ -307,6 +307,7 @@ export const myTranslations: DefaultTranslationsObject = {
contains: 'ပါဝင်သည်', contains: 'ပါဝင်သည်',
equals: 'ညီမျှ', equals: 'ညီမျှ',
exists: 'တည်ရှိသည်', exists: 'တည်ရှိသည်',
intersects: 'ကြောက်ခြင်း',
isGreaterThan: 'ထက်ကြီးသည်', isGreaterThan: 'ထက်ကြီးသည်',
isGreaterThanOrEqualTo: 'ထက်ကြီးသည် သို့မဟုတ် ညီမျှသည်', isGreaterThanOrEqualTo: 'ထက်ကြီးသည် သို့မဟုတ် ညီမျှသည်',
isIn: 'ရှိ', isIn: 'ရှိ',
@@ -316,6 +317,7 @@ export const myTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'ညီမျှသည်', isNotEqualTo: 'ညီမျှသည်',
isNotIn: 'မဝင်ပါ', isNotIn: 'မဝင်ပါ',
near: 'နီး', near: 'နီး',
within: 'အတွင်း',
}, },
upload: { upload: {
crop: 'သုန်း', crop: 'သုန်း',

View File

@@ -304,6 +304,7 @@ export const nbTranslations: DefaultTranslationsObject = {
contains: 'contains', contains: 'contains',
equals: 'lik', equals: 'lik',
exists: 'eksisterer', exists: 'eksisterer',
intersects: 'krysser',
isGreaterThan: 'er større enn', isGreaterThan: 'er større enn',
isGreaterThanOrEqualTo: 'er større enn eller lik', isGreaterThanOrEqualTo: 'er større enn eller lik',
isIn: 'er i', isIn: 'er i',
@@ -313,6 +314,7 @@ export const nbTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'er ikke lik', isNotEqualTo: 'er ikke lik',
isNotIn: 'er ikke med', isNotIn: 'er ikke med',
near: 'nær', near: 'nær',
within: 'innen',
}, },
upload: { upload: {
crop: 'Beskjær', crop: 'Beskjær',

View File

@@ -306,6 +306,7 @@ export const nlTranslations: DefaultTranslationsObject = {
contains: 'bevat', contains: 'bevat',
equals: 'is gelijk aan', equals: 'is gelijk aan',
exists: 'bestaat', exists: 'bestaat',
intersects: 'kruist',
isGreaterThan: 'is groter dan', isGreaterThan: 'is groter dan',
isGreaterThanOrEqualTo: 'is groter dan of gelijk aan', isGreaterThanOrEqualTo: 'is groter dan of gelijk aan',
isIn: 'is binnen', isIn: 'is binnen',
@@ -315,6 +316,7 @@ export const nlTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'is niet gelijk aan', isNotEqualTo: 'is niet gelijk aan',
isNotIn: 'zit er niet in', isNotIn: 'zit er niet in',
near: 'nabij', near: 'nabij',
within: 'binnen',
}, },
upload: { upload: {
crop: 'Bijsnijden', crop: 'Bijsnijden',

View File

@@ -304,6 +304,7 @@ export const plTranslations: DefaultTranslationsObject = {
contains: 'zawiera', contains: 'zawiera',
equals: 'równe', equals: 'równe',
exists: 'istnieje', exists: 'istnieje',
intersects: 'przecina się',
isGreaterThan: 'jest większy niż', isGreaterThan: 'jest większy niż',
isGreaterThanOrEqualTo: 'jest większe lub równe', isGreaterThanOrEqualTo: 'jest większe lub równe',
isIn: 'jest w', isIn: 'jest w',
@@ -313,6 +314,7 @@ export const plTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nie jest równe', isNotEqualTo: 'nie jest równe',
isNotIn: 'nie ma go w', isNotIn: 'nie ma go w',
near: 'blisko', near: 'blisko',
within: 'w ciągu',
}, },
upload: { upload: {
crop: 'Przytnij', crop: 'Przytnij',

View File

@@ -305,6 +305,7 @@ export const ptTranslations: DefaultTranslationsObject = {
contains: 'contém', contains: 'contém',
equals: 'igual', equals: 'igual',
exists: 'existe', exists: 'existe',
intersects: 'intersecciona',
isGreaterThan: 'é maior que', isGreaterThan: 'é maior que',
isGreaterThanOrEqualTo: 'é maior ou igual a', isGreaterThanOrEqualTo: 'é maior ou igual a',
isIn: 'está em', isIn: 'está em',
@@ -314,6 +315,7 @@ export const ptTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'não é igual a', isNotEqualTo: 'não é igual a',
isNotIn: 'não está em', isNotIn: 'não está em',
near: 'perto', near: 'perto',
within: 'dentro',
}, },
upload: { upload: {
crop: 'Cultura', crop: 'Cultura',

View File

@@ -308,6 +308,7 @@ export const roTranslations: DefaultTranslationsObject = {
contains: 'conține', contains: 'conține',
equals: 'egal cu', equals: 'egal cu',
exists: 'există', exists: 'există',
intersects: 'se intersectează',
isGreaterThan: 'este mai mare decât', isGreaterThan: 'este mai mare decât',
isGreaterThanOrEqualTo: 'este mai mare sau egal cu', isGreaterThanOrEqualTo: 'este mai mare sau egal cu',
isIn: 'este în', isIn: 'este în',
@@ -317,6 +318,7 @@ export const roTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nu este egal cu', isNotEqualTo: 'nu este egal cu',
isNotIn: 'nu este în', isNotIn: 'nu este în',
near: 'în apropiere de', near: 'în apropiere de',
within: 'înăuntru',
}, },
upload: { upload: {
crop: 'Cultură', crop: 'Cultură',

View File

@@ -303,6 +303,7 @@ export const rsTranslations: DefaultTranslationsObject = {
contains: 'садржи', contains: 'садржи',
equals: 'једнако', equals: 'једнако',
exists: 'постоји', exists: 'постоји',
intersects: 'preseca',
isGreaterThan: 'је веће од', isGreaterThan: 'је веће од',
isGreaterThanOrEqualTo: 'је веће од или једнако', isGreaterThanOrEqualTo: 'је веће од или једнако',
isIn: 'је у', isIn: 'је у',
@@ -312,6 +313,7 @@ export const rsTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'није једнако', isNotEqualTo: 'није једнако',
isNotIn: 'није у', isNotIn: 'није у',
near: 'близу', near: 'близу',
within: 'unutar',
}, },
upload: { upload: {
crop: 'Исеците слику', crop: 'Исеците слику',

View File

@@ -303,6 +303,7 @@ export const rsLatinTranslations: DefaultTranslationsObject = {
contains: 'sadrži', contains: 'sadrži',
equals: 'jednako', equals: 'jednako',
exists: 'postoji', exists: 'postoji',
intersects: 'seče',
isGreaterThan: 'je veće od', isGreaterThan: 'je veće od',
isGreaterThanOrEqualTo: 'je veće od ili jednako', isGreaterThanOrEqualTo: 'je veće od ili jednako',
isIn: 'je u', isIn: 'je u',
@@ -312,6 +313,7 @@ export const rsLatinTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nije jednako', isNotEqualTo: 'nije jednako',
isNotIn: 'nije unutra', isNotIn: 'nije unutra',
near: 'blizu', near: 'blizu',
within: 'unutar',
}, },
upload: { upload: {
crop: 'Isecite sliku', crop: 'Isecite sliku',

View File

@@ -307,6 +307,7 @@ export const ruTranslations: DefaultTranslationsObject = {
contains: 'содержит', contains: 'содержит',
equals: 'равно', equals: 'равно',
exists: 'существует', exists: 'существует',
intersects: 'пересекает',
isGreaterThan: 'больше чем', isGreaterThan: 'больше чем',
isGreaterThanOrEqualTo: 'больше или равно', isGreaterThanOrEqualTo: 'больше или равно',
isIn: 'находится', isIn: 'находится',
@@ -316,6 +317,7 @@ export const ruTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'не равно', isNotEqualTo: 'не равно',
isNotIn: 'нет в', isNotIn: 'нет в',
near: 'рядом', near: 'рядом',
within: 'в пределах',
}, },
upload: { upload: {
crop: 'Обрезать', crop: 'Обрезать',

View File

@@ -305,6 +305,7 @@ export const skTranslations: DefaultTranslationsObject = {
contains: 'obsahuje', contains: 'obsahuje',
equals: 'rovná sa', equals: 'rovná sa',
exists: 'existuje', exists: 'existuje',
intersects: 'pretína sa',
isGreaterThan: 'je väčšie ako', isGreaterThan: 'je väčšie ako',
isGreaterThanOrEqualTo: 'je väčšie alebo rovné', isGreaterThanOrEqualTo: 'je väčšie alebo rovné',
isIn: 'je v', isIn: 'je v',
@@ -314,6 +315,7 @@ export const skTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'nie je rovné', isNotEqualTo: 'nie je rovné',
isNotIn: 'nie je v', isNotIn: 'nie je v',
near: 'blízko', near: 'blízko',
within: 'vnútri',
}, },
upload: { upload: {
crop: 'Orezať', crop: 'Orezať',

View File

@@ -304,6 +304,7 @@ export const svTranslations: DefaultTranslationsObject = {
contains: 'innehåller', contains: 'innehåller',
equals: 'likar med', equals: 'likar med',
exists: 'finns', exists: 'finns',
intersects: 'korsar',
isGreaterThan: 'är större än', isGreaterThan: 'är större än',
isGreaterThanOrEqualTo: 'är större än eller lika med', isGreaterThanOrEqualTo: 'är större än eller lika med',
isIn: 'är med', isIn: 'är med',
@@ -313,6 +314,7 @@ export const svTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'är inte lika med', isNotEqualTo: 'är inte lika med',
isNotIn: 'är inte med', isNotIn: 'är inte med',
near: 'nära', near: 'nära',
within: 'inom',
}, },
upload: { upload: {
crop: 'Skörd', crop: 'Skörd',

View File

@@ -300,6 +300,7 @@ export const thTranslations: DefaultTranslationsObject = {
contains: 'มี', contains: 'มี',
equals: 'เท่ากับ', equals: 'เท่ากับ',
exists: 'มีอยู่', exists: 'มีอยู่',
intersects: 'ตัดกัน',
isGreaterThan: 'มากกว่า', isGreaterThan: 'มากกว่า',
isGreaterThanOrEqualTo: 'มากกว่าหรือเท่ากับ', isGreaterThanOrEqualTo: 'มากกว่าหรือเท่ากับ',
isIn: 'อยู่ใน', isIn: 'อยู่ใน',
@@ -309,6 +310,7 @@ export const thTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'ไม่เท่ากับ', isNotEqualTo: 'ไม่เท่ากับ',
isNotIn: 'ไม่ได้อยู่ใน', isNotIn: 'ไม่ได้อยู่ใน',
near: 'ใกล้', near: 'ใกล้',
within: 'ภายใน',
}, },
upload: { upload: {
crop: 'พืชผล', crop: 'พืชผล',

View File

@@ -308,6 +308,7 @@ export const trTranslations: DefaultTranslationsObject = {
contains: 'içerir', contains: 'içerir',
equals: 'eşittir', equals: 'eşittir',
exists: 'var', exists: 'var',
intersects: 'kesişir',
isGreaterThan: 'şundan büyüktür', isGreaterThan: 'şundan büyüktür',
isGreaterThanOrEqualTo: 'büyüktür veya eşittir', isGreaterThanOrEqualTo: 'büyüktür veya eşittir',
isIn: 'içinde', isIn: 'içinde',
@@ -317,6 +318,7 @@ export const trTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'eşit değildir', isNotEqualTo: 'eşit değildir',
isNotIn: 'içinde değil', isNotIn: 'içinde değil',
near: 'yakın', near: 'yakın',
within: 'içinde',
}, },
upload: { upload: {
crop: 'Mahsulat', crop: 'Mahsulat',

View File

@@ -304,6 +304,7 @@ export const ukTranslations: DefaultTranslationsObject = {
contains: 'містить', contains: 'містить',
equals: 'дорівнює', equals: 'дорівнює',
exists: 'існує', exists: 'існує',
intersects: 'перетинається',
isGreaterThan: 'більше ніж', isGreaterThan: 'більше ніж',
isGreaterThanOrEqualTo: 'більше або дорівнює', isGreaterThanOrEqualTo: 'більше або дорівнює',
isIn: 'є в', isIn: 'є в',
@@ -313,6 +314,7 @@ export const ukTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'не дорівнює', isNotEqualTo: 'не дорівнює',
isNotIn: 'не в', isNotIn: 'не в',
near: 'поруч', near: 'поруч',
within: 'в межах',
}, },
upload: { upload: {
crop: 'Обрізати', crop: 'Обрізати',

View File

@@ -302,6 +302,7 @@ export const viTranslations: DefaultTranslationsObject = {
contains: 'có chứa', contains: 'có chứa',
equals: 'bằng', equals: 'bằng',
exists: 'tồn tại', exists: 'tồn tại',
intersects: 'giao nhau',
isGreaterThan: 'lớn hơn', isGreaterThan: 'lớn hơn',
isGreaterThanOrEqualTo: 'lớn hơn hoặc bằng', isGreaterThanOrEqualTo: 'lớn hơn hoặc bằng',
isIn: 'có trong', isIn: 'có trong',
@@ -311,6 +312,7 @@ export const viTranslations: DefaultTranslationsObject = {
isNotEqualTo: 'không bằng', isNotEqualTo: 'không bằng',
isNotIn: 'không có trong', isNotIn: 'không có trong',
near: 'gần', near: 'gần',
within: 'trong',
}, },
upload: { upload: {
crop: 'Mùa vụ', crop: 'Mùa vụ',

View File

@@ -296,6 +296,7 @@ export const zhTranslations: DefaultTranslationsObject = {
contains: '包含', contains: '包含',
equals: '等于', equals: '等于',
exists: '存在', exists: '存在',
intersects: '相交',
isGreaterThan: '大于', isGreaterThan: '大于',
isGreaterThanOrEqualTo: '大于等于', isGreaterThanOrEqualTo: '大于等于',
isIn: '在', isIn: '在',
@@ -305,6 +306,7 @@ export const zhTranslations: DefaultTranslationsObject = {
isNotEqualTo: '不等于', isNotEqualTo: '不等于',
isNotIn: '不在', isNotIn: '不在',
near: '附近', near: '附近',
within: '在...之内',
}, },
upload: { upload: {
crop: '作物', crop: '作物',

View File

@@ -296,6 +296,7 @@ export const zhTwTranslations: DefaultTranslationsObject = {
contains: '包含', contains: '包含',
equals: '等於', equals: '等於',
exists: '存在', exists: '存在',
intersects: '交叉點',
isGreaterThan: '大於', isGreaterThan: '大於',
isGreaterThanOrEqualTo: '大於等於', isGreaterThanOrEqualTo: '大於等於',
isIn: '在', isIn: '在',
@@ -305,6 +306,7 @@ export const zhTwTranslations: DefaultTranslationsObject = {
isNotEqualTo: '不等於', isNotEqualTo: '不等於',
isNotIn: '不在', isNotIn: '不在',
near: '附近', near: '附近',
within: '在...之內',
}, },
upload: { upload: {
crop: '裁剪', crop: '裁剪',

View File

@@ -19,7 +19,7 @@ export type FieldSelectProps = {
setSelected: (fields: FieldWithPath[]) => void setSelected: (fields: FieldWithPath[]) => void
} }
const combineLabel = ({ export const combineLabel = ({
customLabel, customLabel,
field, field,
prefix, prefix,

View File

@@ -164,6 +164,7 @@ export const ListControls: React.FC<ListControlsProps> = (props) => {
<WhereBuilder <WhereBuilder
collectionPluralLabel={collectionConfig?.labels?.plural} collectionPluralLabel={collectionConfig?.labels?.plural}
collectionSlug={collectionConfig.slug} collectionSlug={collectionConfig.slug}
fieldMap={fieldMap}
key={String(hasWhereParam.current && !searchParams?.where)} key={String(hasWhereParam.current && !searchParams?.where)}
/> />
</AnimateHeight> </AnimateHeight>

View File

@@ -164,10 +164,10 @@ export const Condition: React.FC<Props> = (props) => {
options: valueOptions, options: valueOptions,
relationTo: relationTo:
internalField?.props?.type === 'relationship' && internalField?.props?.type === 'relationship' &&
'cellProps' in internalField.props && 'cellComponentProps' in internalField.props &&
typeof internalField.props.cellProps === 'object' && typeof internalField.props.cellComponentProps === 'object' &&
'relationTo' in internalField.props.cellProps 'relationTo' in internalField.props.cellComponentProps
? internalField.props.cellProps?.relationTo ? internalField.props.cellComponentProps?.relationTo
: undefined, : undefined,
value: internalQueryValue ?? '', value: internalQueryValue ?? '',
}} }}

View File

@@ -7,10 +7,10 @@ import React, { useEffect, useState } from 'react'
import type { WhereBuilderProps } from './types.js' import type { WhereBuilderProps } from './types.js'
import { useListQuery } from '../../providers/ListQuery/index.js' import { useListQuery } from '../../providers/ListQuery/index.js'
import { useLocale } from '../../providers/Locale/index.js'
import { useSearchParams } from '../../providers/SearchParams/index.js' import { useSearchParams } from '../../providers/SearchParams/index.js'
import { useTranslation } from '../../providers/Translation/index.js' import { useTranslation } from '../../providers/Translation/index.js'
import { Button } from '../Button/index.js' import { Button } from '../Button/index.js'
import { useTableColumns } from '../TableColumns/index.js'
import { Condition } from './Condition/index.js' import { Condition } from './Condition/index.js'
import './index.scss' import './index.scss'
import { reduceFieldMap } from './reduceFieldMap.js' import { reduceFieldMap } from './reduceFieldMap.js'
@@ -26,15 +26,15 @@ export { WhereBuilderProps }
* It is part of the {@link ListControls} component which is used to render the controls (search, filter, where). * It is part of the {@link ListControls} component which is used to render the controls (search, filter, where).
*/ */
export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => { export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
const { collectionPluralLabel } = props const { collectionPluralLabel, fieldMap } = props
const { i18n, t } = useTranslation() const { i18n, t } = useTranslation()
const { columns } = useTableColumns() const { code: currentLocale } = useLocale()
const [reducedFields, setReducedColumns] = useState(() => reduceFieldMap(columns, i18n)) const [reducedFields, setReducedColumns] = useState(() => reduceFieldMap(fieldMap, i18n))
useEffect(() => { useEffect(() => {
setReducedColumns(reduceFieldMap(columns, i18n)) setReducedColumns(reduceFieldMap(fieldMap, i18n, undefined, undefined, currentLocale))
}, [columns, i18n]) }, [fieldMap, i18n, currentLocale])
const { searchParams } = useSearchParams() const { searchParams } = useSearchParams()
const { handleWhereChange } = useListQuery() const { handleWhereChange } = useListQuery()
@@ -74,7 +74,6 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
if (validateWhereQuery(whereFromSearch)) { if (validateWhereQuery(whereFromSearch)) {
return whereFromSearch.or return whereFromSearch.or
} }
// Transform the where query to be in the right format. This will transform something simple like [text][equals]=example%20post to the right format // Transform the where query to be in the right format. This will transform something simple like [text][equals]=example%20post to the right format
const transformedWhere = transformWhereQuery(whereFromSearch) const transformedWhere = transformWhereQuery(whereFromSearch)

View File

@@ -1,42 +1,86 @@
'use client' 'use client'
import type { Column } from '../Table/index.js' import type { FieldMap } from '../../utilities/buildComponentMap.js'
import { createNestedClientFieldPath } from '../../forms/Form/createNestedFieldPath.js'
import { combineLabel } from '../FieldSelect/index.js'
import fieldTypes from './field-types.js' import fieldTypes from './field-types.js'
export const reduceFieldMap = (fieldMap: Column[], i18n) => export const reduceFieldMap = (
fieldMap.reduce((reduced, field) => { fieldMap: FieldMap,
i18n,
labelPrefix?: string,
pathPrefix?: string,
locale?: string,
) => {
return fieldMap.reduce((reduced, field) => {
if (field.disableListFilter) return reduced
if (field.type === 'tabs' && 'tabs' in field.fieldComponentProps) {
const tabs = field.fieldComponentProps.tabs
tabs.forEach((tab) => {
if (tab.name && typeof tab.label === 'string' && tab.fieldMap) {
reduced.push(...reduceFieldMap(tab.fieldMap, i18n, tab.label, tab.name))
}
})
return reduced
}
if (field.type === 'group' && 'fieldMap' in field.fieldComponentProps) {
reduced.push(
...reduceFieldMap(
field.fieldComponentProps.fieldMap,
i18n,
field.fieldComponentProps.label as string,
field.name,
),
)
return reduced
}
if (typeof fieldTypes[field.type] === 'object') { if (typeof fieldTypes[field.type] === 'object') {
const operatorKeys = new Set() const operatorKeys = new Set()
const operators = fieldTypes[field.type].operators.reduce((acc, operator) => { const operators = fieldTypes[field.type].operators.reduce((acc, operator) => {
if (!operatorKeys.has(operator.value)) { if (!operatorKeys.has(operator.value)) {
operatorKeys.add(operator.value) operatorKeys.add(operator.value)
return [ acc.push({
...acc, ...operator,
{ label: i18n.t(`operators:${operator.label}`),
...operator, })
label: i18n.t(`operators:${operator.label}`),
},
]
} }
return acc return acc
}, []) }, [])
const localizedLabel =
locale && typeof field.fieldComponentProps.label === 'object'
? field.fieldComponentProps.label[locale]
: field.fieldComponentProps.label
const formattedLabel = labelPrefix
? combineLabel({
field,
prefix: labelPrefix,
})
: localizedLabel
const formattedValue = pathPrefix
? createNestedClientFieldPath(pathPrefix, field)
: field.name
const formattedField = { const formattedField = {
label: field.Label, label: formattedLabel,
value: field.name, value: formattedValue,
...fieldTypes[field.type], ...fieldTypes[field.type],
operators, operators,
props: { props: {
...field, ...field,
...(field?.cellProps || {}), ...(field?.cellComponentProps || {}),
}, },
} }
if (field.admin?.disableListFilter) return reduced reduced.push(formattedField)
return reduced
return [...reduced, formattedField]
} }
return reduced return reduced
}, []) }, [])
}

View File

@@ -1,8 +1,11 @@
import type { Field, Operator, SanitizedCollectionConfig, Where } from 'payload' import type { Field, Operator, SanitizedCollectionConfig, Where } from 'payload'
import type { FieldMap } from '../../utilities/buildComponentMap.js'
export type WhereBuilderProps = { export type WhereBuilderProps = {
collectionPluralLabel: SanitizedCollectionConfig['labels']['plural'] collectionPluralLabel: SanitizedCollectionConfig['labels']['plural']
collectionSlug: SanitizedCollectionConfig['slug'] collectionSlug: SanitizedCollectionConfig['slug']
fieldMap?: FieldMap
} }
export type FieldCondition = { export type FieldCondition = {

View File

@@ -246,6 +246,14 @@ describe('admin2', () => {
await page.locator('.where-builder__add-first-filter').click() await page.locator('.where-builder__add-first-filter').click()
const conditionField = page.locator('.condition__field')
await conditionField.click()
const dropdownFieldOption = conditionField.locator('.rs__option', {
hasText: exactText('ID'),
})
await dropdownFieldOption.click()
await expect(page.locator('.condition__field')).toContainText('ID')
const operatorField = page.locator('.condition__operator') const operatorField = page.locator('.condition__operator')
const valueField = page.locator('.condition__value input') const valueField = page.locator('.condition__value input')
@@ -256,7 +264,9 @@ describe('admin2', () => {
await valueField.fill(id) await valueField.fill(id)
await expect(page.locator(tableRowLocator)).toHaveCount(1) const tableRows = page.locator(tableRowLocator)
await expect(tableRows).toHaveCount(1)
const firstId = await page.locator(tableRowLocator).first().locator('.cell-id').innerText() const firstId = await page.locator(tableRowLocator).first().locator('.cell-id').innerText()
expect(firstId).toEqual(`ID: ${id}`) expect(firstId).toEqual(`ID: ${id}`)
@@ -288,12 +298,15 @@ describe('admin2', () => {
await filterField.click() await filterField.click()
// select new filter field of Number // select new filter field of Number
const dropdownFieldOptions = filterField.locator('.rs__option') const dropdownFieldOption = filterField.locator('.rs__option', {
await dropdownFieldOptions.locator('text=Number').click() hasText: exactText('Status'),
})
await dropdownFieldOption.click()
await expect(filterField).toContainText('Status')
// expect operator & value field to reset (be empty) // expect operator & value field to reset (be empty)
await expect(operatorField.locator('.rs__placeholder')).toContainText('Select a value') await expect(operatorField.locator('.rs__placeholder')).toContainText('Select a value')
await expect(valueField).toHaveValue('') await expect(page.locator('.condition__value input')).toHaveValue('')
}) })
test('should accept where query from valid URL where parameter', async () => { test('should accept where query from valid URL where parameter', async () => {