Handle no value options in filters (#11351)
Fixes #11323 - see description to reproduce The issue was that the filter was malformed for no value options: <img width="649" alt="Capture d’écran 2025-04-02 à 14 56 25" src="https://github.com/user-attachments/assets/5287c4f8-7eaf-4488-b692-4d7634236d3d" /> causing <img width="333" alt="Capture d’écran 2025-04-02 à 14 56 43" src="https://github.com/user-attachments/assets/aa1b7333-50da-4b7d-979b-70dab9a1ab41" /> after fix: <img width="653" alt="Capture d’écran 2025-04-02 à 14 39 56" src="https://github.com/user-attachments/assets/1777c068-7231-4e14-bc41-84ef7909cf10" />
This commit is contained in:
@ -141,6 +141,7 @@ mutation UpdateOneFavorite(
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
internalCompetitions
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
@ -510,6 +511,7 @@ export const mocks = [
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
internalCompetitions
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
|
||||
@ -67,6 +67,6 @@ describe('useColumnDefinitionsFromFieldMetadata', () => {
|
||||
|
||||
const { columnDefinitions } = result.current;
|
||||
|
||||
expect(columnDefinitions.length).toBe(21);
|
||||
expect(columnDefinitions.length).toBe(22);
|
||||
});
|
||||
});
|
||||
|
||||
@ -127,6 +127,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = `
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
internalCompetitions
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
|
||||
@ -124,6 +124,7 @@ const mocks: MockedResponse[] = [
|
||||
}
|
||||
id
|
||||
idealCustomerProfile
|
||||
internalCompetitions
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
|
||||
@ -933,4 +933,156 @@ describe('should work as expected for the different field types', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('select field type with empty options', () => {
|
||||
const selectFieldMetadata = companyMockObjectMetadataItem.fields.find(
|
||||
(field) => field.type === FieldMetadataType.SELECT,
|
||||
);
|
||||
|
||||
if (!selectFieldMetadata) {
|
||||
throw new Error(
|
||||
`Select field metadata not found ${companyMockObjectMetadataItem.fields.map((field) => [field.name, field.type])}`,
|
||||
);
|
||||
}
|
||||
|
||||
const selectFilterIs: RecordFilter = {
|
||||
id: 'company-select-filter-is',
|
||||
value: '["option1",""]',
|
||||
fieldMetadataId: selectFieldMetadata?.id,
|
||||
displayValue: '["option1",""]',
|
||||
operand: ViewFilterOperand.Is,
|
||||
label: 'Select',
|
||||
type: FieldMetadataType.SELECT,
|
||||
};
|
||||
|
||||
const selectFilterIsNot: RecordFilter = {
|
||||
id: 'company-select-filter-is-not',
|
||||
value: '["option1",""]',
|
||||
fieldMetadataId: selectFieldMetadata.id,
|
||||
displayValue: '["option1",""]',
|
||||
operand: ViewFilterOperand.IsNot,
|
||||
label: 'Select',
|
||||
type: FieldMetadataType.SELECT,
|
||||
};
|
||||
|
||||
const result = computeRecordGqlOperationFilter({
|
||||
filterValueDependencies: mockFilterValueDependencies,
|
||||
recordFilters: [selectFilterIs, selectFilterIsNot],
|
||||
recordFilterGroups: [],
|
||||
fields: companyMockObjectMetadataItem.fields,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
and: [
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[selectFieldMetadata.name]: {
|
||||
in: ['option1'],
|
||||
},
|
||||
},
|
||||
{
|
||||
[selectFieldMetadata.name]: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
and: [
|
||||
{
|
||||
not: {
|
||||
[selectFieldMetadata.name]: {
|
||||
in: ['option1'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
not: {
|
||||
[selectFieldMetadata.name]: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('multi-select field type with empty options', () => {
|
||||
const multiSelectFieldMetadata = companyMockObjectMetadataItem.fields.find(
|
||||
(field) => field.type === FieldMetadataType.MULTI_SELECT,
|
||||
)!;
|
||||
|
||||
const multiSelectFilterContains: RecordFilter = {
|
||||
id: 'company-multi-select-filter-contains',
|
||||
value: '["option1",""]',
|
||||
fieldMetadataId: multiSelectFieldMetadata.id,
|
||||
displayValue: '["option1",""]',
|
||||
operand: ViewFilterOperand.Contains,
|
||||
label: 'MultiSelect',
|
||||
type: FieldMetadataType.MULTI_SELECT,
|
||||
};
|
||||
|
||||
const multiSelectFilterDoesNotContain: RecordFilter = {
|
||||
id: 'company-multi-select-filter-does-not-contain',
|
||||
value: '["option1",""]',
|
||||
fieldMetadataId: multiSelectFieldMetadata.id,
|
||||
displayValue: '["option1",""]',
|
||||
operand: ViewFilterOperand.DoesNotContain,
|
||||
label: 'MultiSelect',
|
||||
type: FieldMetadataType.MULTI_SELECT,
|
||||
};
|
||||
|
||||
const result = computeRecordGqlOperationFilter({
|
||||
filterValueDependencies: mockFilterValueDependencies,
|
||||
recordFilters: [
|
||||
multiSelectFilterContains,
|
||||
multiSelectFilterDoesNotContain,
|
||||
],
|
||||
recordFilterGroups: [],
|
||||
fields: companyMockObjectMetadataItem.fields,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
and: [
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[multiSelectFieldMetadata.name]: {
|
||||
containsAny: ['option1'],
|
||||
},
|
||||
},
|
||||
{
|
||||
[multiSelectFieldMetadata.name]: {
|
||||
isEmptyArray: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[multiSelectFieldMetadata.name]: {
|
||||
containsAny: ['option1'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[multiSelectFieldMetadata.name]: {
|
||||
isEmptyArray: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
[multiSelectFieldMetadata.name]: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -584,20 +584,38 @@ export const computeFilterRecordGqlOperationFilter = ({
|
||||
|
||||
if (options.length === 0) return;
|
||||
|
||||
const emptyOptions = options.filter((option: string) => option === '');
|
||||
const nonEmptyOptions = options.filter((option: string) => option !== '');
|
||||
|
||||
switch (filter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
[correspondingField.name]: {
|
||||
containsAny: options,
|
||||
} as MultiSelectFilter,
|
||||
};
|
||||
case RecordFilterOperand.Contains: {
|
||||
const conditions = [];
|
||||
|
||||
if (nonEmptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingField.name]: {
|
||||
containsAny: nonEmptyOptions,
|
||||
} as MultiSelectFilter,
|
||||
});
|
||||
}
|
||||
|
||||
if (emptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingField.name]: {
|
||||
isEmptyArray: true,
|
||||
} as MultiSelectFilter,
|
||||
});
|
||||
}
|
||||
|
||||
return conditions.length === 1 ? conditions[0] : { or: conditions };
|
||||
}
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingField.name]: {
|
||||
containsAny: options,
|
||||
containsAny: nonEmptyOptions,
|
||||
} as MultiSelectFilter,
|
||||
},
|
||||
},
|
||||
@ -624,21 +642,56 @@ export const computeFilterRecordGqlOperationFilter = ({
|
||||
|
||||
if (options.length === 0) return;
|
||||
|
||||
const emptyOptions = options.filter((option: string) => option === '');
|
||||
const nonEmptyOptions = options.filter((option: string) => option !== '');
|
||||
|
||||
switch (filter.operand) {
|
||||
case RecordFilterOperand.Is:
|
||||
return {
|
||||
[correspondingField.name]: {
|
||||
in: options,
|
||||
} as SelectFilter,
|
||||
};
|
||||
case RecordFilterOperand.IsNot:
|
||||
return {
|
||||
not: {
|
||||
case RecordFilterOperand.Is: {
|
||||
const conditions = [];
|
||||
|
||||
if (nonEmptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingField.name]: {
|
||||
in: options,
|
||||
in: nonEmptyOptions,
|
||||
} as SelectFilter,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (emptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingField.name]: {
|
||||
is: 'NULL',
|
||||
} as SelectFilter,
|
||||
});
|
||||
}
|
||||
|
||||
return conditions.length === 1 ? conditions[0] : { or: conditions };
|
||||
}
|
||||
case RecordFilterOperand.IsNot: {
|
||||
const conditions = [];
|
||||
|
||||
if (nonEmptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
not: {
|
||||
[correspondingField.name]: {
|
||||
in: nonEmptyOptions,
|
||||
} as SelectFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (emptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
not: {
|
||||
[correspondingField.name]: {
|
||||
is: 'NULL',
|
||||
} as SelectFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return conditions.length === 1 ? conditions[0] : { and: conditions };
|
||||
}
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${filter.operand} for ${filterType} filter`,
|
||||
|
||||
@ -124,6 +124,7 @@ const companyMocks = [
|
||||
}
|
||||
id
|
||||
idealCustomerProfile
|
||||
internalCompetitions
|
||||
introVideo {
|
||||
primaryLinkUrl
|
||||
primaryLinkLabel
|
||||
|
||||
@ -3876,6 +3876,42 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
label: "Work Policy",
|
||||
description: "Company's Work Policy"
|
||||
},
|
||||
{
|
||||
__typename: "Field",
|
||||
id: "c6dfcc2d-dc84-4553-94df-3b75cccee53c",
|
||||
type: "SELECT",
|
||||
name: "internalCompetitions",
|
||||
icon: "IconHome",
|
||||
isCustom: true,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
createdAt: "2025-02-11T09:14:38.892Z",
|
||||
updatedAt: "2025-02-11T09:14:38.892Z",
|
||||
defaultValue: null,
|
||||
options: [
|
||||
{
|
||||
id: "ee1b741b-0359-4ffd-b866-506e7b9c0cd9",
|
||||
color: "green",
|
||||
label: "Best employy",
|
||||
value: "BEST_EMPLOYEE",
|
||||
position: 0
|
||||
},
|
||||
{
|
||||
id: "3b2ed882-ec07-43fd-96e6-0fca8669c1f5",
|
||||
color: "turquoise",
|
||||
label: "Ultimate debugger",
|
||||
value: "ULTIMATE_DEBUGGER",
|
||||
position: 1
|
||||
},
|
||||
],
|
||||
settings: null,
|
||||
isLabelSyncedWithName: false,
|
||||
relationDefinition: null,
|
||||
label: "Internal competitions",
|
||||
description: "Internal competitions"
|
||||
},
|
||||
{
|
||||
__typename: "Field",
|
||||
id: "8b1b88c0-a802-4c5d-8632-a4e343f3c8f1",
|
||||
|
||||
Reference in New Issue
Block a user