Fix kanban view when grouping select field has null value (#16998)

This commit is contained in:
Etienne
2026-01-07 18:55:54 +01:00
committed by GitHub
parent 1f0fac195c
commit 615ef1abdc
3 changed files with 98 additions and 8 deletions
@@ -0,0 +1,54 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { computeRecordGroupOptionsFilter } from '@/object-record/record-group/utils/computeRecordGroupOptionsFilter';
const mockFieldMetadata = {
id: 'field-1',
name: 'status',
} as FieldMetadataItem;
describe('computeRecordGroupOptionsFilter', () => {
it('should return empty object when recordGroupFieldMetadata is undefined', () => {
const result = computeRecordGroupOptionsFilter({
recordGroupFieldMetadata: undefined,
recordGroupValues: ['value1', 'value2'],
});
expect(result).toEqual({});
});
it('should return empty object when recordGroupFieldMetadata is null', () => {
const result = computeRecordGroupOptionsFilter({
recordGroupFieldMetadata: null,
recordGroupValues: ['value1', 'value2'],
});
expect(result).toEqual({});
});
it('should return simple IN filter when no null values present', () => {
const result = computeRecordGroupOptionsFilter({
recordGroupFieldMetadata: mockFieldMetadata,
recordGroupValues: ['value1', 'value2', 'value3'],
});
expect(result).toEqual({
status: {
in: ['value1', 'value2', 'value3'],
},
});
});
it('should return OR filter with IS NULL when null value is present', () => {
const result = computeRecordGroupOptionsFilter({
recordGroupFieldMetadata: mockFieldMetadata,
recordGroupValues: ['value1', null, 'value2'],
});
expect(result).toEqual({
or: [
{ status: { is: 'NULL' } },
{ status: { in: ['value1', 'value2'] } },
],
});
});
});
@@ -0,0 +1,39 @@
import { isNull } from '@sniptt/guards';
import { type RecordGqlOperationFilter } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
export const computeRecordGroupOptionsFilter = ({
recordGroupFieldMetadata,
recordGroupValues,
}: {
recordGroupFieldMetadata: FieldMetadataItem | null | undefined;
recordGroupValues: RecordGroupDefinition['value'][];
}): RecordGqlOperationFilter => {
if (!isDefined(recordGroupFieldMetadata) || recordGroupValues.length === 0) {
return {};
}
const fieldName = recordGroupFieldMetadata.name;
const hasNullValue = recordGroupValues.some(isNull);
const nonNullValues = recordGroupValues.filter(
(value): value is NonNullable<typeof value> => !isNull(value),
);
return hasNullValue
? {
or: [
{ [fieldName]: { is: 'NULL' } },
...(nonNullValues.length > 0
? [{ [fieldName]: { in: nonNullValues } }]
: []),
],
}
: nonNullValues.length > 0
? {
[fieldName]: { in: recordGroupValues },
}
: {};
};
@@ -5,6 +5,7 @@ import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { recordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector';
import { computeRecordGroupOptionsFilter } from '@/object-record/record-group/utils/computeRecordGroupOptionsFilter';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
@@ -12,7 +13,6 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho
import {
combineFilters,
computeRecordGqlOperationFilter,
isDefined,
turnAnyFieldFilterIntoRecordGqlFilter,
} from 'twenty-shared/utils';
@@ -73,13 +73,10 @@ export const useRecordIndexGroupCommonQueryVariables = () => {
(recordGroupDefinition) => recordGroupDefinition.value,
);
const recordGroupOptionsFilter = isDefined(recordGroupFieldMetadata)
? {
[recordGroupFieldMetadata.name]: {
in: recordGroupValues,
},
}
: {};
const recordGroupOptionsFilter = computeRecordGroupOptionsFilter({
recordGroupFieldMetadata,
recordGroupValues,
});
const combinedFilters = combineFilters([
anyFieldFilter,