Implements record filter group upsert and remove (#10514)
This PR implements a parallel code path to upsert and remove record filter groups. Those record filter groups aren't keeping track of the view id but since they are in a view context, it's implicit that the advanced filter feature can keep track of view for record filter groups. We'll need record filter group for an upcoming feature without views, so we need to abstract record filter group from view. We have viewFilterGroup.id === recordFilterGroup.id, so it's easy to map each other.
This commit is contained in:
@ -2,6 +2,9 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMeta
|
|||||||
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
|
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
|
||||||
|
import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup';
|
||||||
|
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
@ -31,6 +34,8 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
|||||||
const { currentViewId } = useGetCurrentView();
|
const { currentViewId } = useGetCurrentView();
|
||||||
|
|
||||||
const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup();
|
const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup();
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
|
||||||
const { upsertRecordFilter } = useUpsertRecordFilter();
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
const newPositionInViewFilterGroup = lastChildPosition + 1;
|
const newPositionInViewFilterGroup = lastChildPosition + 1;
|
||||||
@ -108,15 +113,26 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
|||||||
throw new Error('Missing view id');
|
throw new Error('Missing view id');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newViewFilterGroup = {
|
const newRecordFilterGroupId = v4();
|
||||||
id: v4(),
|
|
||||||
|
const newViewFilterGroup: ViewFilterGroup = {
|
||||||
|
__typename: 'ViewFilterGroup',
|
||||||
|
id: newRecordFilterGroupId,
|
||||||
viewId: currentViewId,
|
viewId: currentViewId,
|
||||||
logicalOperator: ViewFilterGroupLogicalOperator.AND,
|
logicalOperator: ViewFilterGroupLogicalOperator.AND,
|
||||||
parentViewFilterGroupId: viewFilterGroup.id,
|
parentViewFilterGroupId: viewFilterGroup.id,
|
||||||
positionInViewFilterGroup: newPositionInViewFilterGroup,
|
positionInViewFilterGroup: newPositionInViewFilterGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const newRecordFilterGroup: RecordFilterGroup = {
|
||||||
|
id: newRecordFilterGroupId,
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||||
|
parentRecordFilterGroupId: viewFilterGroup.id,
|
||||||
|
positionInRecordFilterGroup: newPositionInViewFilterGroup,
|
||||||
|
};
|
||||||
|
|
||||||
upsertCombinedViewFilterGroup(newViewFilterGroup);
|
upsertCombinedViewFilterGroup(newViewFilterGroup);
|
||||||
|
upsertRecordFilterGroup(newRecordFilterGroup);
|
||||||
|
|
||||||
const defaultFieldMetadataItem = getDefaultFieldMetadataItem();
|
const defaultFieldMetadataItem = getDefaultFieldMetadataItem();
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { ADVANCED_FILTER_LOGICAL_OPERATOR_OPTIONS } from '@/object-record/advanced-filter/constants/AdvancedFilterLogicalOperatorOptions';
|
import { ADVANCED_FILTER_LOGICAL_OPERATOR_OPTIONS } from '@/object-record/advanced-filter/constants/AdvancedFilterLogicalOperatorOptions';
|
||||||
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
|
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
|
||||||
|
import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup';
|
||||||
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
import { Select } from '@/ui/input/components/Select';
|
import { Select } from '@/ui/input/components/Select';
|
||||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||||
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
|
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
|
||||||
@ -12,12 +14,23 @@ export const AdvancedFilterLogicalOperatorDropdown = ({
|
|||||||
viewFilterGroup,
|
viewFilterGroup,
|
||||||
}: AdvancedFilterLogicalOperatorDropdownProps) => {
|
}: AdvancedFilterLogicalOperatorDropdownProps) => {
|
||||||
const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup();
|
const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup();
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
|
||||||
const handleChange = (value: ViewFilterGroupLogicalOperator) => {
|
const handleChange = (value: ViewFilterGroupLogicalOperator) => {
|
||||||
upsertCombinedViewFilterGroup({
|
upsertCombinedViewFilterGroup({
|
||||||
...viewFilterGroup,
|
...viewFilterGroup,
|
||||||
logicalOperator: value,
|
logicalOperator: value,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
upsertRecordFilterGroup({
|
||||||
|
id: viewFilterGroup.id,
|
||||||
|
parentRecordFilterGroupId: viewFilterGroup.parentViewFilterGroupId,
|
||||||
|
positionInRecordFilterGroup: viewFilterGroup.positionInViewFilterGroup,
|
||||||
|
logicalOperator:
|
||||||
|
value === ViewFilterGroupLogicalOperator.AND
|
||||||
|
? RecordFilterGroupLogicalOperator.AND
|
||||||
|
: RecordFilterGroupLogicalOperator.OR,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { AdvancedFilterRuleOptionsDropdownButton } from '@/object-record/advance
|
|||||||
|
|
||||||
import { useCurrentViewViewFilterGroup } from '@/object-record/advanced-filter/hooks/useCurrentViewViewFilterGroup';
|
import { useCurrentViewViewFilterGroup } from '@/object-record/advanced-filter/hooks/useCurrentViewViewFilterGroup';
|
||||||
import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup';
|
import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup';
|
||||||
|
import { useRemoveRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup';
|
||||||
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({
|
|||||||
|
|
||||||
const { removeRecordFilter } = useRemoveRecordFilter();
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup();
|
const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup();
|
||||||
|
const { removeRecordFilterGroup } = useRemoveRecordFilterGroup();
|
||||||
|
|
||||||
const { currentViewFilterGroup, childViewFiltersAndViewFilterGroups } =
|
const { currentViewFilterGroup, childViewFiltersAndViewFilterGroups } =
|
||||||
useCurrentViewViewFilterGroup({
|
useCurrentViewViewFilterGroup({
|
||||||
@ -56,9 +58,11 @@ export const AdvancedFilterRuleOptionsDropdown = ({
|
|||||||
isDefined(currentRecordFilter?.viewFilterGroupId)
|
isDefined(currentRecordFilter?.viewFilterGroupId)
|
||||||
) {
|
) {
|
||||||
deleteCombinedViewFilterGroup(currentRecordFilter.viewFilterGroupId);
|
deleteCombinedViewFilterGroup(currentRecordFilter.viewFilterGroupId);
|
||||||
|
removeRecordFilterGroup(currentRecordFilter.viewFilterGroupId);
|
||||||
}
|
}
|
||||||
} else if (isDefined(currentViewFilterGroup)) {
|
} else if (isDefined(currentViewFilterGroup)) {
|
||||||
deleteCombinedViewFilterGroup(currentViewFilterGroup.id);
|
deleteCombinedViewFilterGroup(currentViewFilterGroup.id);
|
||||||
|
removeRecordFilterGroup(currentViewFilterGroup.id);
|
||||||
|
|
||||||
// TODO: This is a temporary fix view filter group will be removed soon.
|
// TODO: This is a temporary fix view filter group will be removed soon.
|
||||||
const childViewFilters = childViewFiltersAndViewFilterGroups.filter(
|
const childViewFilters = childViewFiltersAndViewFilterGroups.filter(
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-met
|
|||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
|
import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup';
|
||||||
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId';
|
||||||
|
import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup';
|
||||||
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
|
|
||||||
@ -58,6 +60,7 @@ export const AdvancedFilterButton = () => {
|
|||||||
useGetCurrentView();
|
useGetCurrentView();
|
||||||
|
|
||||||
const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup();
|
const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup();
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
|
||||||
const { upsertRecordFilter } = useUpsertRecordFilter();
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
@ -96,6 +99,11 @@ export const AdvancedFilterButton = () => {
|
|||||||
|
|
||||||
upsertCombinedViewFilterGroup(newViewFilterGroup);
|
upsertCombinedViewFilterGroup(newViewFilterGroup);
|
||||||
|
|
||||||
|
upsertRecordFilterGroup({
|
||||||
|
id: newViewFilterGroup.id,
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||||
|
});
|
||||||
|
|
||||||
const defaultFieldMetadataItem =
|
const defaultFieldMetadataItem =
|
||||||
availableFieldMetadataItemsForFilter.find(
|
availableFieldMetadataItemsForFilter.find(
|
||||||
(fieldMetadataItem) =>
|
(fieldMetadataItem) =>
|
||||||
|
|||||||
@ -0,0 +1,104 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||||
|
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useRemoveRecordFilterGroup } from '../useRemoveRecordFilterGroup';
|
||||||
|
import { useUpsertRecordFilterGroup } from '../useUpsertRecordFilterGroup';
|
||||||
|
|
||||||
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useRemoveRecordFilterGroup', () => {
|
||||||
|
it('should remove an existing record filter group', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilterGroups = useRecoilComponentValueV2(
|
||||||
|
currentRecordFilterGroupsComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
const { removeRecordFilterGroup } = useRemoveRecordFilterGroup();
|
||||||
|
|
||||||
|
return {
|
||||||
|
upsertRecordFilterGroup,
|
||||||
|
removeRecordFilterGroup,
|
||||||
|
currentRecordFilterGroups,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockRecordFilterGroup: RecordFilterGroup = {
|
||||||
|
id: 'record-filter-group-1',
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||||
|
parentRecordFilterGroupId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilterGroup(mockRecordFilterGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilterGroups[0]).toEqual(
|
||||||
|
mockRecordFilterGroup,
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.removeRecordFilterGroup(mockRecordFilterGroup.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify record filter groups when trying to remove a non-existent record filter group', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilterGroups = useRecoilComponentValueV2(
|
||||||
|
currentRecordFilterGroupsComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
const { removeRecordFilterGroup } = useRemoveRecordFilterGroup();
|
||||||
|
|
||||||
|
return {
|
||||||
|
upsertRecordFilterGroup,
|
||||||
|
removeRecordFilterGroup,
|
||||||
|
currentRecordFilterGroups,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockRecordFilterGroup: RecordFilterGroup = {
|
||||||
|
id: 'record-filter-group-1',
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||||
|
parentRecordFilterGroupId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilterGroup(mockRecordFilterGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(1);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.removeRecordFilterGroup(
|
||||||
|
'non-existent-record-filter-group-id',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilterGroups[0]).toEqual(
|
||||||
|
mockRecordFilterGroup,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react';
|
||||||
|
|
||||||
|
import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup';
|
||||||
|
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||||
|
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useUpsertRecordFilterGroup', () => {
|
||||||
|
it('should add a new record filter group', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilterGroups = useRecoilComponentValueV2(
|
||||||
|
currentRecordFilterGroupsComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
|
||||||
|
return { upsertRecordFilterGroup, currentRecordFilterGroups };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockRecordFilterGroup: RecordFilterGroup = {
|
||||||
|
id: 'record-filter-group-1',
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||||
|
parentRecordFilterGroupId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilterGroup(mockRecordFilterGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilterGroups[0]).toEqual(
|
||||||
|
mockRecordFilterGroup,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update an existing record filter group', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilterGroups = useRecoilComponentValueV2(
|
||||||
|
currentRecordFilterGroupsComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup();
|
||||||
|
|
||||||
|
return { upsertRecordFilterGroup, currentRecordFilterGroups };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockInitialRecordFilterGroup: RecordFilterGroup = {
|
||||||
|
id: 'record-filter-group-1',
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||||
|
parentRecordFilterGroupId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockUpdatedRecordFilterGroup: RecordFilterGroup = {
|
||||||
|
id: 'record-filter-group-1',
|
||||||
|
logicalOperator: RecordFilterGroupLogicalOperator.OR,
|
||||||
|
parentRecordFilterGroupId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilterGroup(mockInitialRecordFilterGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilterGroups[0]).toEqual(
|
||||||
|
mockInitialRecordFilterGroup,
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilterGroup(mockUpdatedRecordFilterGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilterGroups).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilterGroups[0]).toEqual(
|
||||||
|
mockUpdatedRecordFilterGroup,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||||
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
export const useRemoveRecordFilterGroup = () => {
|
||||||
|
const currentRecordFilterGroupsCallbackState =
|
||||||
|
useRecoilComponentCallbackStateV2(currentRecordFilterGroupsComponentState);
|
||||||
|
|
||||||
|
const removeRecordFilterGroup = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(recordFilterGroupIdToRemove: string) => {
|
||||||
|
const currentRecordFilterGroups = getSnapshotValue(
|
||||||
|
snapshot,
|
||||||
|
currentRecordFilterGroupsCallbackState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasFoundRecordFilterGroupInCurrentRecordFilterGroups =
|
||||||
|
currentRecordFilterGroups.some(
|
||||||
|
(existingRecordFilterGroup) =>
|
||||||
|
existingRecordFilterGroup.id === recordFilterGroupIdToRemove,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasFoundRecordFilterGroupInCurrentRecordFilterGroups) {
|
||||||
|
set(
|
||||||
|
currentRecordFilterGroupsCallbackState,
|
||||||
|
(currentRecordFilterGroups) => {
|
||||||
|
const newCurrentRecordFilterGroups = [
|
||||||
|
...currentRecordFilterGroups,
|
||||||
|
];
|
||||||
|
|
||||||
|
const indexOfRecordFilterGroupToRemove =
|
||||||
|
newCurrentRecordFilterGroups.findIndex(
|
||||||
|
(existingRecordFilterGroup) =>
|
||||||
|
existingRecordFilterGroup.id ===
|
||||||
|
recordFilterGroupIdToRemove,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (indexOfRecordFilterGroupToRemove === -1) {
|
||||||
|
return newCurrentRecordFilterGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
newCurrentRecordFilterGroups.splice(
|
||||||
|
indexOfRecordFilterGroupToRemove,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
return newCurrentRecordFilterGroups;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentRecordFilterGroupsCallbackState],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
removeRecordFilterGroup,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||||
|
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
export const useUpsertRecordFilterGroup = () => {
|
||||||
|
const currentRecordFilterGroupsCallbackState =
|
||||||
|
useRecoilComponentCallbackStateV2(currentRecordFilterGroupsComponentState);
|
||||||
|
|
||||||
|
const upsertRecordFilterGroup = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(recordFilterGroupToSet: RecordFilterGroup) => {
|
||||||
|
const currentRecordFilterGroups = getSnapshotValue(
|
||||||
|
snapshot,
|
||||||
|
currentRecordFilterGroupsCallbackState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasFoundRecordFilterGroupInCurrentRecordFilterGroups =
|
||||||
|
currentRecordFilterGroups.some(
|
||||||
|
(existingRecordFilterGroup) =>
|
||||||
|
existingRecordFilterGroup.id === recordFilterGroupToSet.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasFoundRecordFilterGroupInCurrentRecordFilterGroups) {
|
||||||
|
set(currentRecordFilterGroupsCallbackState, [
|
||||||
|
...currentRecordFilterGroups,
|
||||||
|
recordFilterGroupToSet,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
set(
|
||||||
|
currentRecordFilterGroupsCallbackState,
|
||||||
|
(currentRecordFilterGroups) => {
|
||||||
|
const newCurrentRecordFilterGroups = [
|
||||||
|
...currentRecordFilterGroups,
|
||||||
|
];
|
||||||
|
|
||||||
|
const indexOfRecordFilterGroupToUpdate =
|
||||||
|
newCurrentRecordFilterGroups.findIndex(
|
||||||
|
(existingRecordFilterGroup) =>
|
||||||
|
existingRecordFilterGroup.id === recordFilterGroupToSet.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (indexOfRecordFilterGroupToUpdate === -1) {
|
||||||
|
return newCurrentRecordFilterGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
newCurrentRecordFilterGroups[indexOfRecordFilterGroupToUpdate] = {
|
||||||
|
...recordFilterGroupToSet,
|
||||||
|
};
|
||||||
|
|
||||||
|
return newCurrentRecordFilterGroups;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentRecordFilterGroupsCallbackState],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
upsertRecordFilterGroup,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { renderHook } from '@testing-library/react';
|
import { renderHook } from '@testing-library/react';
|
||||||
import { act } from 'react-dom/test-utils';
|
|
||||||
|
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
|
import { act } from 'react';
|
||||||
import { FieldMetadataType } from '~/generated/graphql';
|
import { FieldMetadataType } from '~/generated/graphql';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
import { useRemoveRecordFilter } from '../useRemoveRecordFilter';
|
import { useRemoveRecordFilter } from '../useRemoveRecordFilter';
|
||||||
@ -15,7 +15,7 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('useRemoveRecordFilter', () => {
|
describe('useRemoveRecordFilter', () => {
|
||||||
it('should remove an existing filter', () => {
|
it('should remove an existing record filter', () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => {
|
() => {
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
@ -36,7 +36,7 @@ describe('useRemoveRecordFilter', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const filter: RecordFilter = {
|
const mockRecordFilter: RecordFilter = {
|
||||||
id: 'filter-1',
|
id: 'filter-1',
|
||||||
fieldMetadataId: 'field-1',
|
fieldMetadataId: 'field-1',
|
||||||
value: 'test-value',
|
value: 'test-value',
|
||||||
@ -46,17 +46,15 @@ describe('useRemoveRecordFilter', () => {
|
|||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
};
|
};
|
||||||
|
|
||||||
// First add a filter
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.upsertRecordFilter(filter);
|
result.current.upsertRecordFilter(mockRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
expect(result.current.currentRecordFilters[0]).toEqual(filter);
|
expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter);
|
||||||
|
|
||||||
// Then remove it
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.removeRecordFilter(filter.fieldMetadataId);
|
result.current.removeRecordFilter(mockRecordFilter.fieldMetadataId);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(0);
|
expect(result.current.currentRecordFilters).toHaveLength(0);
|
||||||
@ -81,7 +79,7 @@ describe('useRemoveRecordFilter', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const filter: RecordFilter = {
|
const mockRecordFilter: RecordFilter = {
|
||||||
id: 'filter-1',
|
id: 'filter-1',
|
||||||
fieldMetadataId: 'field-1',
|
fieldMetadataId: 'field-1',
|
||||||
value: 'test-value',
|
value: 'test-value',
|
||||||
@ -91,20 +89,17 @@ describe('useRemoveRecordFilter', () => {
|
|||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add a filter
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.upsertRecordFilter(filter);
|
result.current.upsertRecordFilter(mockRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
|
||||||
// Try to remove a non-existent filter
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.removeRecordFilter('non-existent-field');
|
result.current.removeRecordFilter('non-existent-field-metadata-id');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter list should remain unchanged
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
expect(result.current.currentRecordFilters[0]).toEqual(filter);
|
expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { renderHook } from '@testing-library/react';
|
import { renderHook } from '@testing-library/react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react';
|
||||||
|
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
@ -30,7 +30,7 @@ describe('useUpsertRecordFilter', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const newFilter: RecordFilter = {
|
const mockNewRecordFilter: RecordFilter = {
|
||||||
id: 'filter-1',
|
id: 'filter-1',
|
||||||
fieldMetadataId: 'field-1',
|
fieldMetadataId: 'field-1',
|
||||||
value: 'test-value',
|
value: 'test-value',
|
||||||
@ -41,11 +41,11 @@ describe('useUpsertRecordFilter', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.upsertRecordFilter(newFilter);
|
result.current.upsertRecordFilter(mockNewRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
expect(result.current.currentRecordFilters[0]).toEqual(newFilter);
|
expect(result.current.currentRecordFilters[0]).toEqual(mockNewRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update an existing filter when fieldMetadataId exists', () => {
|
it('should update an existing filter when fieldMetadataId exists', () => {
|
||||||
@ -64,7 +64,7 @@ describe('useUpsertRecordFilter', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialFilter: RecordFilter = {
|
const mockInitialRecordFilter: RecordFilter = {
|
||||||
id: 'filter-1',
|
id: 'filter-1',
|
||||||
fieldMetadataId: 'field-1',
|
fieldMetadataId: 'field-1',
|
||||||
value: 'initial-value',
|
value: 'initial-value',
|
||||||
@ -74,7 +74,7 @@ describe('useUpsertRecordFilter', () => {
|
|||||||
type: FieldMetadataType.TEXT,
|
type: FieldMetadataType.TEXT,
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatedFilter: RecordFilter = {
|
const mockUpdatedRecordFilter: RecordFilter = {
|
||||||
id: 'filter-1',
|
id: 'filter-1',
|
||||||
fieldMetadataId: 'field-1',
|
fieldMetadataId: 'field-1',
|
||||||
value: 'updated-value',
|
value: 'updated-value',
|
||||||
@ -85,17 +85,21 @@ describe('useUpsertRecordFilter', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.upsertRecordFilter(initialFilter);
|
result.current.upsertRecordFilter(mockInitialRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
expect(result.current.currentRecordFilters[0]).toEqual(initialFilter);
|
expect(result.current.currentRecordFilters[0]).toEqual(
|
||||||
|
mockInitialRecordFilter,
|
||||||
|
);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current.upsertRecordFilter(updatedFilter);
|
result.current.upsertRecordFilter(mockUpdatedRecordFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current.currentRecordFilters).toHaveLength(1);
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
expect(result.current.currentRecordFilters[0]).toEqual(updatedFilter);
|
expect(result.current.currentRecordFilters[0]).toEqual(
|
||||||
|
mockUpdatedRecordFilter,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
|||||||
|
|
||||||
import { AdvancedFilterRootLevelViewFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRootLevelViewFilterGroup';
|
import { AdvancedFilterRootLevelViewFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRootLevelViewFilterGroup';
|
||||||
import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup';
|
import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup';
|
||||||
|
import { useRemoveRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup';
|
||||||
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
@ -14,6 +15,7 @@ import { isDefined } from 'twenty-shared';
|
|||||||
|
|
||||||
export const AdvancedFilterDropdownButton = () => {
|
export const AdvancedFilterDropdownButton = () => {
|
||||||
const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup();
|
const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup();
|
||||||
|
const { removeRecordFilterGroup } = useRemoveRecordFilterGroup();
|
||||||
|
|
||||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||||
|
|
||||||
@ -43,6 +45,7 @@ export const AdvancedFilterDropdownButton = () => {
|
|||||||
|
|
||||||
for (const viewFilterGroupId of viewFilterGroupIds) {
|
for (const viewFilterGroupId of viewFilterGroupIds) {
|
||||||
await deleteCombinedViewFilterGroup(viewFilterGroupId);
|
await deleteCombinedViewFilterGroup(viewFilterGroupId);
|
||||||
|
removeRecordFilterGroup(viewFilterGroupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const recordFilterId of advancedRecordFilterIds) {
|
for (const recordFilterId of advancedRecordFilterIds) {
|
||||||
@ -50,6 +53,7 @@ export const AdvancedFilterDropdownButton = () => {
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
advancedRecordFilterIds,
|
advancedRecordFilterIds,
|
||||||
|
removeRecordFilterGroup,
|
||||||
removeRecordFilter,
|
removeRecordFilter,
|
||||||
deleteCombinedViewFilterGroup,
|
deleteCombinedViewFilterGroup,
|
||||||
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups,
|
currentViewWithCombinedFiltersAndSorts?.viewFilterGroups,
|
||||||
|
|||||||
Reference in New Issue
Block a user