Refactored table filters to consume new currentRecordFilters component state (#9652)
This PR implements a first real use case, now currentRecordFilters component state acts as the global record filter reference. It is set by the view initially and can be reset to view filters state at any point. This new state is also modified by two new upsertRecordFilter / removeRecordFilter hooks that will be drop-in replacement of the actual upsertCombinedViewFilter and removeCombinediewFilter hooks. This PR implements the logic to manipulate record filters but only reads it to make the table find many request, all other features are still relying on the old view filter implementation. Advanced filters are ignored because they are hidden and because this effort is made precisely to allow the completion of the advanced filters feature.
This commit is contained in:
@ -0,0 +1,198 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { useApplyCurrentViewFiltersToCurrentRecordFilters } from '../useApplyCurrentViewFiltersToCurrentRecordFilters';
|
||||
|
||||
jest.mock('@/prefetch/hooks/usePrefetchedData');
|
||||
|
||||
describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
|
||||
const mockFilterDefinition: RecordFilterDefinition = {
|
||||
fieldMetadataId: 'field-1',
|
||||
label: 'Test Field',
|
||||
type: 'TEXT',
|
||||
iconName: 'IconText',
|
||||
};
|
||||
|
||||
const mockViewFilter: ViewFilter = {
|
||||
__typename: 'ViewFilter',
|
||||
id: 'filter-1',
|
||||
fieldMetadataId: 'field-1',
|
||||
operand: ViewFilterOperand.Contains,
|
||||
value: 'test',
|
||||
displayValue: 'test',
|
||||
viewFilterGroupId: 'group-1',
|
||||
positionInViewFilterGroup: 0,
|
||||
definition: mockFilterDefinition,
|
||||
};
|
||||
|
||||
const mockView = {
|
||||
id: 'view-1',
|
||||
name: 'Test View',
|
||||
objectMetadataId: 'object-1',
|
||||
viewFilters: [mockViewFilter],
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
(usePrefetchedData as jest.Mock).mockReturnValue({
|
||||
records: [mockView],
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply filters from current view', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { applyCurrentViewFiltersToCurrentRecordFilters } =
|
||||
useApplyCurrentViewFiltersToCurrentRecordFilters();
|
||||
|
||||
const currentFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
applyCurrentViewFiltersToCurrentRecordFilters,
|
||||
currentFilters,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: getJestMetadataAndApolloMocksWrapper({
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
currentViewIdComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
mockView.id,
|
||||
);
|
||||
snapshot.set(
|
||||
availableFilterDefinitionsComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
[mockFilterDefinition],
|
||||
);
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.applyCurrentViewFiltersToCurrentRecordFilters();
|
||||
});
|
||||
|
||||
expect(result.current.currentFilters).toEqual([
|
||||
{
|
||||
id: mockViewFilter.id,
|
||||
fieldMetadataId: mockViewFilter.fieldMetadataId,
|
||||
value: mockViewFilter.value,
|
||||
displayValue: mockViewFilter.displayValue,
|
||||
operand: mockViewFilter.operand,
|
||||
viewFilterGroupId: mockViewFilter.viewFilterGroupId,
|
||||
positionInViewFilterGroup: mockViewFilter.positionInViewFilterGroup,
|
||||
definition: mockFilterDefinition,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not apply filters when current view is not found', () => {
|
||||
(usePrefetchedData as jest.Mock).mockReturnValue({
|
||||
records: [],
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { applyCurrentViewFiltersToCurrentRecordFilters } =
|
||||
useApplyCurrentViewFiltersToCurrentRecordFilters();
|
||||
|
||||
const currentFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
applyCurrentViewFiltersToCurrentRecordFilters,
|
||||
currentFilters,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: getJestMetadataAndApolloMocksWrapper({
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
currentViewIdComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
mockView.id,
|
||||
);
|
||||
snapshot.set(
|
||||
availableFilterDefinitionsComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
[mockFilterDefinition],
|
||||
);
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.applyCurrentViewFiltersToCurrentRecordFilters();
|
||||
});
|
||||
|
||||
expect(result.current.currentFilters).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle view with empty filters', () => {
|
||||
const viewWithNoFilters = {
|
||||
...mockView,
|
||||
viewFilters: [],
|
||||
};
|
||||
|
||||
(usePrefetchedData as jest.Mock).mockReturnValue({
|
||||
records: [viewWithNoFilters],
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { applyCurrentViewFiltersToCurrentRecordFilters } =
|
||||
useApplyCurrentViewFiltersToCurrentRecordFilters();
|
||||
|
||||
const currentFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
return {
|
||||
applyCurrentViewFiltersToCurrentRecordFilters,
|
||||
currentFilters,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: getJestMetadataAndApolloMocksWrapper({
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
currentViewIdComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
mockView.id,
|
||||
);
|
||||
snapshot.set(
|
||||
availableFilterDefinitionsComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
[mockFilterDefinition],
|
||||
);
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.applyCurrentViewFiltersToCurrentRecordFilters();
|
||||
});
|
||||
|
||||
expect(result.current.currentFilters).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,108 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { useApplyViewFiltersToCurrentRecordFilters } from '../useApplyViewFiltersToCurrentRecordFilters';
|
||||
|
||||
describe('useApplyViewFiltersToCurrentRecordFilters', () => {
|
||||
const mockAvailableFilterDefinition: RecordFilterDefinition = {
|
||||
fieldMetadataId: 'field-1',
|
||||
label: 'Test Field',
|
||||
type: 'TEXT',
|
||||
iconName: 'IconText',
|
||||
};
|
||||
|
||||
const mockViewFilter: ViewFilter = {
|
||||
__typename: 'ViewFilter',
|
||||
id: 'filter-1',
|
||||
fieldMetadataId: 'field-1',
|
||||
operand: ViewFilterOperand.Contains,
|
||||
value: 'test',
|
||||
displayValue: 'test',
|
||||
viewFilterGroupId: 'group-1',
|
||||
positionInViewFilterGroup: 0,
|
||||
definition: mockAvailableFilterDefinition,
|
||||
};
|
||||
|
||||
it('should apply view filters to current record filters', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { applyViewFiltersToCurrentRecordFilters } =
|
||||
useApplyViewFiltersToCurrentRecordFilters();
|
||||
|
||||
const currentFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
return { applyViewFiltersToCurrentRecordFilters, currentFilters };
|
||||
},
|
||||
{
|
||||
wrapper: getJestMetadataAndApolloMocksWrapper({
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
availableFilterDefinitionsComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
[mockAvailableFilterDefinition],
|
||||
);
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.applyViewFiltersToCurrentRecordFilters([mockViewFilter]);
|
||||
});
|
||||
|
||||
expect(result.current.currentFilters).toEqual([
|
||||
{
|
||||
id: mockViewFilter.id,
|
||||
fieldMetadataId: mockViewFilter.fieldMetadataId,
|
||||
value: mockViewFilter.value,
|
||||
displayValue: mockViewFilter.displayValue,
|
||||
operand: mockViewFilter.operand,
|
||||
viewFilterGroupId: mockViewFilter.viewFilterGroupId,
|
||||
positionInViewFilterGroup: mockViewFilter.positionInViewFilterGroup,
|
||||
definition: mockAvailableFilterDefinition,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty view filters array', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { applyViewFiltersToCurrentRecordFilters } =
|
||||
useApplyViewFiltersToCurrentRecordFilters();
|
||||
|
||||
const currentFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
return { applyViewFiltersToCurrentRecordFilters, currentFilters };
|
||||
},
|
||||
{
|
||||
wrapper: getJestMetadataAndApolloMocksWrapper({
|
||||
onInitializeRecoilSnapshot: (snapshot) => {
|
||||
snapshot.set(
|
||||
availableFilterDefinitionsComponentState.atomFamily({
|
||||
instanceId: 'instanceId',
|
||||
}),
|
||||
[mockAvailableFilterDefinition],
|
||||
);
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.applyViewFiltersToCurrentRecordFilters([]);
|
||||
});
|
||||
|
||||
expect(result.current.currentFilters).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,42 @@
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
|
||||
import { View } from '@/views/types/View';
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const useApplyCurrentViewFiltersToCurrentRecordFilters = () => {
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
|
||||
const currentViewId = useRecoilComponentValueV2(currentViewIdComponentState);
|
||||
|
||||
const setCurrentRecordFilters = useSetRecoilComponentStateV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const availableFilterDefinitions = useRecoilComponentValueV2(
|
||||
availableFilterDefinitionsComponentState,
|
||||
);
|
||||
|
||||
const applyCurrentViewFiltersToCurrentRecordFilters = () => {
|
||||
const currentView = views.find((view) => view.id === currentViewId);
|
||||
|
||||
if (isDefined(currentView)) {
|
||||
setCurrentRecordFilters(
|
||||
mapViewFiltersToFilters(
|
||||
currentView.viewFilters,
|
||||
availableFilterDefinitions,
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
applyCurrentViewFiltersToCurrentRecordFilters,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
|
||||
export const useApplyViewFiltersToCurrentRecordFilters = () => {
|
||||
const setCurrentRecordFilters = useSetRecoilComponentStateV2(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const availableFilterDefinitions = useRecoilComponentValueV2(
|
||||
availableFilterDefinitionsComponentState,
|
||||
);
|
||||
|
||||
const applyViewFiltersToCurrentRecordFilters = (
|
||||
viewFilters: ViewFilter[],
|
||||
) => {
|
||||
const recordFiltersToApply = mapViewFiltersToFilters(
|
||||
viewFilters,
|
||||
availableFilterDefinitions,
|
||||
);
|
||||
|
||||
setCurrentRecordFilters(recordFiltersToApply);
|
||||
};
|
||||
|
||||
return {
|
||||
applyViewFiltersToCurrentRecordFilters,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user