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:
@ -10,6 +10,7 @@ import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBar
|
|||||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
|
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
|
||||||
import { expect, jest } from '@storybook/jest';
|
import { expect, jest } from '@storybook/jest';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
@ -25,58 +26,63 @@ const meta: Meta<typeof RecordIndexActionMenuBar> = {
|
|||||||
decorators: [
|
decorators: [
|
||||||
RouterDecorator,
|
RouterDecorator,
|
||||||
(Story) => (
|
(Story) => (
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId: 'story-action-menu' }}
|
value={{ instanceId: 'story-action-menu' }}
|
||||||
>
|
>
|
||||||
<RecoilRoot
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
initializeState={({ set }) => {
|
value={{ instanceId: 'story-action-menu' }}
|
||||||
set(
|
|
||||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
|
||||||
instanceId: 'story-action-menu',
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
mode: 'selection',
|
|
||||||
selectedRecordIds: ['1', '2', '3'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
set(
|
|
||||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
|
||||||
instanceId: 'story-action-menu',
|
|
||||||
}),
|
|
||||||
3,
|
|
||||||
);
|
|
||||||
const map = new Map<string, ActionMenuEntry>();
|
|
||||||
map.set('delete', {
|
|
||||||
isPinned: true,
|
|
||||||
scope: ActionMenuEntryScope.RecordSelection,
|
|
||||||
type: ActionMenuEntryType.Standard,
|
|
||||||
key: 'delete',
|
|
||||||
label: 'Delete',
|
|
||||||
position: 0,
|
|
||||||
Icon: IconTrash,
|
|
||||||
onClick: deleteMock,
|
|
||||||
});
|
|
||||||
set(
|
|
||||||
actionMenuEntriesComponentState.atomFamily({
|
|
||||||
instanceId: 'story-action-menu',
|
|
||||||
}),
|
|
||||||
map,
|
|
||||||
);
|
|
||||||
set(
|
|
||||||
isBottomBarOpenedComponentState.atomFamily({
|
|
||||||
instanceId: getActionBarIdFromActionMenuId('story-action-menu'),
|
|
||||||
}),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<RecoilRoot
|
||||||
value={{ instanceId: 'story-action-menu' }}
|
initializeState={({ set }) => {
|
||||||
|
set(
|
||||||
|
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||||
|
instanceId: 'story-action-menu',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
mode: 'selection',
|
||||||
|
selectedRecordIds: ['1', '2', '3'],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
set(
|
||||||
|
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||||
|
instanceId: 'story-action-menu',
|
||||||
|
}),
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
const map = new Map<string, ActionMenuEntry>();
|
||||||
|
map.set('delete', {
|
||||||
|
isPinned: true,
|
||||||
|
scope: ActionMenuEntryScope.RecordSelection,
|
||||||
|
type: ActionMenuEntryType.Standard,
|
||||||
|
key: 'delete',
|
||||||
|
label: 'Delete',
|
||||||
|
position: 0,
|
||||||
|
Icon: IconTrash,
|
||||||
|
onClick: deleteMock,
|
||||||
|
});
|
||||||
|
set(
|
||||||
|
actionMenuEntriesComponentState.atomFamily({
|
||||||
|
instanceId: 'story-action-menu',
|
||||||
|
}),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
set(
|
||||||
|
isBottomBarOpenedComponentState.atomFamily({
|
||||||
|
instanceId:
|
||||||
|
getActionBarIdFromActionMenuId('story-action-menu'),
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Story />
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
value={{ instanceId: 'story-action-menu' }}
|
||||||
</RecoilRoot>
|
>
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
<Story />
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</RecoilRoot>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
args: {
|
args: {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKey
|
|||||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||||
import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant';
|
import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
import { workflowReactFlowRefState } from '@/workflow/workflow-diagram/states/workflowReactFlowRefState';
|
import { workflowReactFlowRefState } from '@/workflow/workflow-diagram/states/workflowReactFlowRefState';
|
||||||
@ -74,37 +75,41 @@ export const CommandMenuContainer = ({
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId: 'command-menu' }}
|
value={{ instanceId: 'command-menu' }}
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{ instanceId: 'command-menu' }}
|
value={{ instanceId: 'command-menu' }}
|
||||||
>
|
>
|
||||||
<ActionMenuContext.Provider
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
value={{
|
value={{ instanceId: 'command-menu' }}
|
||||||
isInRightDrawer: false,
|
|
||||||
onActionExecutedCallback: toggleCommandMenu,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<RecordActionMenuEntriesSetter />
|
<ActionMenuContext.Provider
|
||||||
{isWorkflowEnabled && <RecordAgnosticActionsSetterEffect />}
|
value={{
|
||||||
<ActionMenuConfirmationModals />
|
isInRightDrawer: false,
|
||||||
{isCommandMenuOpened && (
|
onActionExecutedCallback: toggleCommandMenu,
|
||||||
<StyledCommandMenu
|
}}
|
||||||
data-testid="command-menu"
|
>
|
||||||
ref={commandMenuRef}
|
<RecordActionMenuEntriesSetter />
|
||||||
className="command-menu"
|
{isWorkflowEnabled && <RecordAgnosticActionsSetterEffect />}
|
||||||
animate={targetVariantForAnimation}
|
<ActionMenuConfirmationModals />
|
||||||
initial="closed"
|
{isCommandMenuOpened && (
|
||||||
exit="closed"
|
<StyledCommandMenu
|
||||||
variants={COMMAND_MENU_ANIMATION_VARIANTS}
|
data-testid="command-menu"
|
||||||
transition={{ duration: theme.animation.duration.normal }}
|
ref={commandMenuRef}
|
||||||
>
|
className="command-menu"
|
||||||
{children}
|
animate={targetVariantForAnimation}
|
||||||
</StyledCommandMenu>
|
initial="closed"
|
||||||
)}
|
exit="closed"
|
||||||
</ActionMenuContext.Provider>
|
variants={COMMAND_MENU_ANIMATION_VARIANTS}
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
transition={{ duration: theme.animation.duration.normal }}
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
>
|
||||||
|
{children}
|
||||||
|
</StyledCommandMenu>
|
||||||
|
)}
|
||||||
|
</ActionMenuContext.Provider>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context
|
|||||||
import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
|
import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
|
||||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||||
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
|
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
|
||||||
import { CommandMenu } from '../CommandMenu';
|
import { CommandMenu } from '../CommandMenu';
|
||||||
@ -29,17 +30,21 @@ const openTimeout = 50;
|
|||||||
|
|
||||||
const ContextStoreDecorator: Decorator = (Story) => {
|
const ContextStoreDecorator: Decorator = (Story) => {
|
||||||
return (
|
return (
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId: 'command-menu' }}
|
value={{ instanceId: 'command-menu' }}
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{ instanceId: 'command-menu' }}
|
value={{ instanceId: 'command-menu' }}
|
||||||
>
|
>
|
||||||
<JestContextStoreSetter contextStoreCurrentObjectMetadataNameSingular="company">
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
<Story />
|
value={{ instanceId: 'command-menu' }}
|
||||||
</JestContextStoreSetter>
|
>
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
<JestContextStoreSetter contextStoreCurrentObjectMetadataNameSingular="company">
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
<Story />
|
||||||
|
</JestContextStoreSetter>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
|||||||
import { useReadFindManyRecordsQueryInCache } from '@/object-record/cache/hooks/useReadFindManyRecordsQueryInCache';
|
import { useReadFindManyRecordsQueryInCache } from '@/object-record/cache/hooks/useReadFindManyRecordsQueryInCache';
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
|
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
|
||||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
import { useUpsertRecordsInCacheForPrefetchKey } from '@/prefetch/hooks/internal/useUpsertRecordsInCacheForPrefetchKey';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
|
|
||||||
export const useDeleteFavoriteFolder = () => {
|
export const useDeleteFavoriteFolder = () => {
|
||||||
@ -12,9 +12,10 @@ export const useDeleteFavoriteFolder = () => {
|
|||||||
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
objectNameSingular: CoreObjectNameSingular.FavoriteFolder,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { upsertRecordsInCache } = usePrefetchRunQuery<Favorite>({
|
const { upsertRecordsInCache } =
|
||||||
prefetchKey: PrefetchKey.AllFavorites,
|
useUpsertRecordsInCacheForPrefetchKey<Favorite>({
|
||||||
});
|
prefetchKey: PrefetchKey.AllFavorites,
|
||||||
|
});
|
||||||
|
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular:
|
objectNameSingular:
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
import { Favorite } from '@/favorites/types/Favorite';
|
import { Favorite } from '@/favorites/types/Favorite';
|
||||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
import { useUpsertRecordsInCacheForPrefetchKey } from '@/prefetch/hooks/internal/useUpsertRecordsInCacheForPrefetchKey';
|
||||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
@ -33,7 +33,7 @@ export const usePrefetchedFavoritesData = (): PrefetchedFavoritesData => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { upsertRecordsInCache: upsertFavorites } =
|
const { upsertRecordsInCache: upsertFavorites } =
|
||||||
usePrefetchRunQuery<Favorite>({
|
useUpsertRecordsInCacheForPrefetchKey<Favorite>({
|
||||||
prefetchKey: PrefetchKey.AllFavorites,
|
prefetchKey: PrefetchKey.AllFavorites,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
||||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
import { useUpsertRecordsInCacheForPrefetchKey } from '@/prefetch/hooks/internal/useUpsertRecordsInCacheForPrefetchKey';
|
||||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
@ -26,7 +26,7 @@ export const usePrefetchedFavoritesFoldersData =
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { upsertRecordsInCache: upsertFavoriteFolders } =
|
const { upsertRecordsInCache: upsertFavoriteFolders } =
|
||||||
usePrefetchRunQuery<FavoriteFolder>({
|
useUpsertRecordsInCacheForPrefetchKey<FavoriteFolder>({
|
||||||
prefetchKey: PrefetchKey.AllFavoritesFolders,
|
prefetchKey: PrefetchKey.AllFavoritesFolders,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { selectedFilterComponentState } from '@/object-record/object-filter-drop
|
|||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions';
|
import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions';
|
||||||
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
|
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
|
||||||
|
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
||||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||||
@ -61,6 +62,7 @@ export const ObjectFilterDropdownSourceSelect = ({
|
|||||||
const { currentViewWithCombinedFiltersAndSorts } =
|
const { currentViewWithCombinedFiltersAndSorts } =
|
||||||
useGetCurrentView(viewComponentId);
|
useGetCurrentView(viewComponentId);
|
||||||
|
|
||||||
|
// TODO: this should be removed as it is not consistent across re-renders
|
||||||
const [fieldId] = useState(v4());
|
const [fieldId] = useState(v4());
|
||||||
|
|
||||||
const sourceTypes = getActorSourceMultiSelectOptions(
|
const sourceTypes = getActorSourceMultiSelectOptions(
|
||||||
@ -73,6 +75,8 @@ export const ObjectFilterDropdownSourceSelect = ({
|
|||||||
|
|
||||||
const { emptyRecordFilter } = useEmptyRecordFilter();
|
const { emptyRecordFilter } = useEmptyRecordFilter();
|
||||||
|
|
||||||
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
|
||||||
const handleMultipleItemSelectChange = (
|
const handleMultipleItemSelectChange = (
|
||||||
itemToSelect: SelectableItem,
|
itemToSelect: SelectableItem,
|
||||||
newSelectedValue: boolean,
|
newSelectedValue: boolean,
|
||||||
@ -83,8 +87,13 @@ export const ObjectFilterDropdownSourceSelect = ({
|
|||||||
(id) => id !== itemToSelect.id,
|
(id) => id !== itemToSelect.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!filterDefinitionUsedInDropdown) {
|
||||||
|
throw new Error('Filter definition used in dropdown should be defined');
|
||||||
|
}
|
||||||
|
|
||||||
if (newSelectedItemIds.length === 0) {
|
if (newSelectedItemIds.length === 0) {
|
||||||
emptyRecordFilter();
|
emptyRecordFilter();
|
||||||
|
removeRecordFilter(filterDefinitionUsedInDropdown.fieldMetadataId);
|
||||||
deleteCombinedViewFilter(fieldId);
|
deleteCombinedViewFilter(fieldId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlur
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton';
|
import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton';
|
||||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
||||||
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
|
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
|
||||||
@ -107,17 +108,21 @@ const meta: Meta<typeof MultipleFiltersDropdownButton> = {
|
|||||||
recordIndexId: instanceId,
|
recordIndexId: instanceId,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId }}
|
value={{ instanceId }}
|
||||||
>
|
>
|
||||||
<RecordTableComponentInstanceContext.Provider
|
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||||
value={{ instanceId: instanceId, onColumnsChange: () => {} }}
|
value={{ instanceId }}
|
||||||
>
|
>
|
||||||
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
|
<RecordTableComponentInstanceContext.Provider
|
||||||
<Story />
|
value={{ instanceId: instanceId, onColumnsChange: () => {} }}
|
||||||
</ViewComponentInstanceContext.Provider>
|
>
|
||||||
</RecordTableComponentInstanceContext.Provider>
|
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
|
||||||
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
<Story />
|
||||||
|
</ViewComponentInstanceContext.Provider>
|
||||||
|
</RecordTableComponentInstanceContext.Provider>
|
||||||
|
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
</RecordIndexContextProvider>
|
</RecordIndexContextProvider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
||||||
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
|
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
|
||||||
|
|
||||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||||
|
|
||||||
export const availableFilterDefinitionsComponentState = createComponentStateV2<
|
export const availableFilterDefinitionsComponentState = createComponentStateV2<
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { ObjectOptionsDropdownContent } from '@/object-record/object-options-dro
|
|||||||
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
|
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
|
||||||
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
|
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
|
||||||
import { ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
|
import { ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
||||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||||
@ -37,22 +38,26 @@ const meta: Meta<typeof ObjectOptionsDropdownContent> = {
|
|||||||
}, [setObjectMetadataItems]);
|
}, [setObjectMetadataItems]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordTableComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId, onColumnsChange: () => {} }}
|
value={{ instanceId: 'object-options-dropdown' }}
|
||||||
>
|
>
|
||||||
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
|
<RecordTableComponentInstanceContext.Provider
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
value={{ instanceId, onColumnsChange: () => {} }}
|
||||||
value={{ instanceId }}
|
>
|
||||||
>
|
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
|
||||||
<MemoryRouter
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
value={{ instanceId }}
|
||||||
initialIndex={1}
|
|
||||||
>
|
>
|
||||||
<Story />
|
<MemoryRouter
|
||||||
</MemoryRouter>
|
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
initialIndex={1}
|
||||||
</ViewComponentInstanceContext.Provider>
|
>
|
||||||
</RecordTableComponentInstanceContext.Provider>
|
<Story />
|
||||||
|
</MemoryRouter>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</ViewComponentInstanceContext.Provider>
|
||||||
|
</RecordTableComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ObjectMetadataItemsDecorator,
|
ObjectMetadataItemsDecorator,
|
||||||
|
|||||||
@ -0,0 +1,117 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useRemoveRecordFilter } from '../useRemoveRecordFilter';
|
||||||
|
import { useUpsertRecordFilter } from '../useUpsertRecordFilter';
|
||||||
|
|
||||||
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useRemoveRecordFilter', () => {
|
||||||
|
it('should remove an existing filter', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
|
||||||
|
return {
|
||||||
|
upsertRecordFilter,
|
||||||
|
removeRecordFilter,
|
||||||
|
currentRecordFilters,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const filter: RecordFilter = {
|
||||||
|
id: 'filter-1',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
value: 'test-value',
|
||||||
|
operand: ViewFilterOperand.Contains,
|
||||||
|
displayValue: 'test-value',
|
||||||
|
definition: {
|
||||||
|
type: 'TEXT',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
label: 'Test Field',
|
||||||
|
iconName: 'IconText',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// First add a filter
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilter(filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilters[0]).toEqual(filter);
|
||||||
|
|
||||||
|
// Then remove it
|
||||||
|
act(() => {
|
||||||
|
result.current.removeRecordFilter(filter.fieldMetadataId);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify filters when trying to remove a non-existent filter', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
return {
|
||||||
|
upsertRecordFilter,
|
||||||
|
removeRecordFilter,
|
||||||
|
currentRecordFilters,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const filter: RecordFilter = {
|
||||||
|
id: 'filter-1',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
value: 'test-value',
|
||||||
|
operand: ViewFilterOperand.Contains,
|
||||||
|
displayValue: 'test-value',
|
||||||
|
definition: {
|
||||||
|
type: 'TEXT',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
label: 'Test Field',
|
||||||
|
iconName: 'IconText',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a filter
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilter(filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
|
||||||
|
// Try to remove a non-existent filter
|
||||||
|
act(() => {
|
||||||
|
result.current.removeRecordFilter('non-existent-field');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter list should remain unchanged
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilters[0]).toEqual(filter);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
import { useUpsertRecordFilter } from '../useUpsertRecordFilter';
|
||||||
|
|
||||||
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
|
apolloMocks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('useUpsertRecordFilter', () => {
|
||||||
|
it('should add a new filter when fieldMetadataId does not exist', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
|
return { upsertRecordFilter, currentRecordFilters };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const newFilter: RecordFilter = {
|
||||||
|
id: 'filter-1',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
value: 'test-value',
|
||||||
|
operand: ViewFilterOperand.Contains,
|
||||||
|
displayValue: 'test-value',
|
||||||
|
definition: {
|
||||||
|
type: 'TEXT',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
label: 'Test Field',
|
||||||
|
iconName: 'IconText',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilter(newFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilters[0]).toEqual(newFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update an existing filter when fieldMetadataId exists', () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => {
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
|
return { upsertRecordFilter, currentRecordFilters };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wrapper: Wrapper,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialFilter: RecordFilter = {
|
||||||
|
id: 'filter-1',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
value: 'initial-value',
|
||||||
|
operand: ViewFilterOperand.Contains,
|
||||||
|
displayValue: 'initial-value',
|
||||||
|
definition: {
|
||||||
|
type: 'TEXT',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
label: 'Test Field',
|
||||||
|
iconName: 'IconText',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatedFilter: RecordFilter = {
|
||||||
|
id: 'filter-1',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
value: 'updated-value',
|
||||||
|
operand: ViewFilterOperand.Contains,
|
||||||
|
displayValue: 'updated-value',
|
||||||
|
definition: {
|
||||||
|
type: 'TEXT',
|
||||||
|
fieldMetadataId: 'field-1',
|
||||||
|
label: 'Test Field',
|
||||||
|
iconName: 'IconText',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilter(initialFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilters[0]).toEqual(initialFilter);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.upsertRecordFilter(updatedFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.currentRecordFilters).toHaveLength(1);
|
||||||
|
expect(result.current.currentRecordFilters[0]).toEqual(updatedFilter);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { onFilterSelectComponentState } from '@/object-record/object-filter-dropdown/states/onFilterSelectComponentState';
|
|
||||||
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
|
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
|
||||||
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
|
||||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
|
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
@ -14,32 +13,19 @@ export const useApplyRecordFilter = (componentInstanceId?: string) => {
|
|||||||
componentInstanceId,
|
componentInstanceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const onFilterSelectCallbackState = useRecoilComponentCallbackStateV2(
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
onFilterSelectComponentState,
|
|
||||||
componentInstanceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const applyRecordFilter = useRecoilCallback(
|
const applyRecordFilter = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set }) =>
|
||||||
(filter: RecordFilter | null) => {
|
(filter: RecordFilter | null) => {
|
||||||
set(selectedFilterCallbackState, filter);
|
set(selectedFilterCallbackState, filter);
|
||||||
|
|
||||||
const onFilterSelect = getSnapshotValue(
|
|
||||||
snapshot,
|
|
||||||
onFilterSelectCallbackState,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isDefined(filter)) {
|
if (isDefined(filter)) {
|
||||||
upsertCombinedViewFilter(filter);
|
upsertCombinedViewFilter(filter);
|
||||||
|
upsertRecordFilter(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterSelect?.(filter);
|
|
||||||
},
|
},
|
||||||
[
|
[selectedFilterCallbackState, upsertCombinedViewFilter, upsertRecordFilter],
|
||||||
selectedFilterCallbackState,
|
|
||||||
onFilterSelectCallbackState,
|
|
||||||
upsertCombinedViewFilter,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
export const useRemoveRecordFilter = () => {
|
||||||
|
const currentRecordFiltersCallbackState = useRecoilComponentCallbackStateV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeRecordFilter = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(fieldMetadataId: string) => {
|
||||||
|
const currentRecordFilters = getSnapshotValue(
|
||||||
|
snapshot,
|
||||||
|
currentRecordFiltersCallbackState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const foundRecordFilterInCurrentRecordFilters =
|
||||||
|
currentRecordFilters.some(
|
||||||
|
(existingFilter) =>
|
||||||
|
existingFilter.fieldMetadataId === fieldMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundRecordFilterInCurrentRecordFilters) {
|
||||||
|
set(currentRecordFiltersCallbackState, (currentRecordFilters) => {
|
||||||
|
const newCurrentRecordFilters = [...currentRecordFilters];
|
||||||
|
|
||||||
|
const indexOfFilterToRemove = newCurrentRecordFilters.findIndex(
|
||||||
|
(existingFilter) =>
|
||||||
|
existingFilter.fieldMetadataId === fieldMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
newCurrentRecordFilters.splice(indexOfFilterToRemove, 1);
|
||||||
|
|
||||||
|
return newCurrentRecordFilters;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentRecordFiltersCallbackState],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
removeRecordFilter,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
|
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
export const useUpsertRecordFilter = () => {
|
||||||
|
const currentRecordFiltersCallbackState = useRecoilComponentCallbackStateV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const upsertRecordFilter = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(filter: RecordFilter) => {
|
||||||
|
const currentRecordFilters = getSnapshotValue(
|
||||||
|
snapshot,
|
||||||
|
currentRecordFiltersCallbackState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const foundRecordFilterInCurrentRecordFilters =
|
||||||
|
currentRecordFilters.some(
|
||||||
|
(existingFilter) =>
|
||||||
|
existingFilter.fieldMetadataId === filter.fieldMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!foundRecordFilterInCurrentRecordFilters) {
|
||||||
|
set(currentRecordFiltersCallbackState, [
|
||||||
|
...currentRecordFilters,
|
||||||
|
filter,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
set(currentRecordFiltersCallbackState, (currentRecordFilters) => {
|
||||||
|
const newCurrentRecordFilters = [...currentRecordFilters];
|
||||||
|
|
||||||
|
const indexOfFilterToUpdate = newCurrentRecordFilters.findIndex(
|
||||||
|
(existingFilter) =>
|
||||||
|
existingFilter.fieldMetadataId === filter.fieldMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
newCurrentRecordFilters[indexOfFilterToUpdate] = {
|
||||||
|
...filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
return newCurrentRecordFilters;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentRecordFiltersCallbackState],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
upsertRecordFilter,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||||
|
|
||||||
|
export const RecordFiltersComponentInstanceContext =
|
||||||
|
createComponentInstanceContext();
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
|
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||||
|
import { RecordFilter } from '../../record-filter/types/RecordFilter';
|
||||||
|
|
||||||
|
export const currentRecordFiltersComponentState = createComponentStateV2<
|
||||||
|
RecordFilter[]
|
||||||
|
>({
|
||||||
|
key: 'currentRecordFiltersComponentState',
|
||||||
|
defaultValue: [],
|
||||||
|
componentInstanceContext: RecordFiltersComponentInstanceContext,
|
||||||
|
});
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
import { isMatchingArrayFilter } from '../isMatchingArrayFilter';
|
||||||
|
|
||||||
|
describe('isMatchingArrayFilter', () => {
|
||||||
|
describe('is filter', () => {
|
||||||
|
it('should return true when checking for NULL and value is null', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { is: 'NULL' },
|
||||||
|
value: null,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when checking for NULL and value is not null', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { is: 'NULL' },
|
||||||
|
value: ['test'],
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when checking for NOT_NULL and value is not null', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { is: 'NOT_NULL' },
|
||||||
|
value: ['test'],
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when checking for NOT_NULL and value is null', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { is: 'NOT_NULL' },
|
||||||
|
value: null,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isEmptyArray filter', () => {
|
||||||
|
it('should return true when array is empty and checking for empty array', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { isEmptyArray: true },
|
||||||
|
value: [],
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when array is not empty and checking for empty array', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { isEmptyArray: true },
|
||||||
|
value: ['test'],
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when value is null and checking for empty array', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { isEmptyArray: true },
|
||||||
|
value: null,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('containsIlike filter', () => {
|
||||||
|
it('should return true when array contains item matching case-insensitive search', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { containsIlike: 'TEST' },
|
||||||
|
value: ['test item'],
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when array does not contain item matching search', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { containsIlike: 'missing' },
|
||||||
|
value: ['test item'],
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when value is null and using containsIlike', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { containsIlike: 'test' },
|
||||||
|
value: null,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match partial strings case-insensitively', () => {
|
||||||
|
expect(
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { containsIlike: 'TE' },
|
||||||
|
value: ['Test Item', 'Another Item'],
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('error handling', () => {
|
||||||
|
it('should throw error for invalid filter', () => {
|
||||||
|
expect(() =>
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: {},
|
||||||
|
value: [],
|
||||||
|
}),
|
||||||
|
).toThrow('Unexpected value for array filter');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for unknown filter type', () => {
|
||||||
|
expect(() =>
|
||||||
|
isMatchingArrayFilter({
|
||||||
|
arrayFilter: { unknownFilter: 'test' } as any,
|
||||||
|
value: [],
|
||||||
|
}),
|
||||||
|
).toThrow('Unexpected value for array filter');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||||
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||||
import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState';
|
|
||||||
import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState';
|
import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState';
|
||||||
import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState';
|
import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
@ -27,20 +27,21 @@ export const useFindManyRecordIndexTableParams = (
|
|||||||
tableViewFilterGroupsComponentState,
|
tableViewFilterGroupsComponentState,
|
||||||
recordTableId,
|
recordTableId,
|
||||||
);
|
);
|
||||||
const tableFilters = useRecoilComponentValueV2(
|
|
||||||
tableFiltersComponentState,
|
|
||||||
recordTableId,
|
|
||||||
);
|
|
||||||
const tableSorts = useRecoilComponentValueV2(
|
const tableSorts = useRecoilComponentValueV2(
|
||||||
tableSortsComponentState,
|
tableSortsComponentState,
|
||||||
recordTableId,
|
recordTableId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
const { filterValueDependencies } = useFilterValueDependencies();
|
const { filterValueDependencies } = useFilterValueDependencies();
|
||||||
|
|
||||||
const stateFilter = computeViewRecordGqlOperationFilter(
|
const stateFilter = computeViewRecordGqlOperationFilter(
|
||||||
filterValueDependencies,
|
filterValueDependencies,
|
||||||
tableFilters,
|
currentRecordFilters,
|
||||||
objectMetadataItem?.fields ?? [],
|
objectMetadataItem?.fields ?? [],
|
||||||
tableViewFilterGroups,
|
tableViewFilterGroups,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/u
|
|||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown';
|
import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown';
|
||||||
|
|
||||||
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
|
import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
|
||||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||||
@ -33,6 +34,7 @@ export const useHandleToggleColumnFilter = ({
|
|||||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||||
|
|
||||||
const { upsertCombinedViewFilter } = useUpsertCombinedViewFilters(viewBarId);
|
const { upsertCombinedViewFilter } = useUpsertCombinedViewFilters(viewBarId);
|
||||||
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
const openDropdown = useRecoilCallback(({ set }) => {
|
const openDropdown = useRecoilCallback(({ set }) => {
|
||||||
return (dropdownId: string) => {
|
return (dropdownId: string) => {
|
||||||
@ -93,6 +95,8 @@ export const useHandleToggleColumnFilter = ({
|
|||||||
value: '',
|
value: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
upsertRecordFilter(newFilter);
|
||||||
|
|
||||||
await upsertCombinedViewFilter(newFilter);
|
await upsertCombinedViewFilter(newFilter);
|
||||||
|
|
||||||
selectFilterDefinitionUsedInDropdown({ filterDefinition });
|
selectFilterDefinitionUsedInDropdown({ filterDefinition });
|
||||||
@ -107,6 +111,7 @@ export const useHandleToggleColumnFilter = ({
|
|||||||
selectFilterDefinitionUsedInDropdown,
|
selectFilterDefinitionUsedInDropdown,
|
||||||
currentViewWithCombinedFiltersAndSorts,
|
currentViewWithCombinedFiltersAndSorts,
|
||||||
availableFilterDefinitions,
|
availableFilterDefinitions,
|
||||||
|
upsertRecordFilter,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { v4 } from 'uuid';
|
|||||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
|
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { isSoftDeleteFilterActiveComponentState } from '@/object-record/record-table/states/isSoftDeleteFilterActiveComponentState';
|
import { isSoftDeleteFilterActiveComponentState } from '@/object-record/record-table/states/isSoftDeleteFilterActiveComponentState';
|
||||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
@ -36,6 +37,8 @@ export const useHandleToggleTrashColumnFilter = ({
|
|||||||
viewBarId,
|
viewBarId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { upsertRecordFilter } = useUpsertRecordFilter();
|
||||||
|
|
||||||
const handleToggleTrashColumnFilter = useCallback(() => {
|
const handleToggleTrashColumnFilter = useCallback(() => {
|
||||||
const trashFieldMetadata = objectMetadataItem.fields.find(
|
const trashFieldMetadata = objectMetadataItem.fields.find(
|
||||||
(field: { name: string }) => field.name === 'deletedAt',
|
(field: { name: string }) => field.name === 'deletedAt',
|
||||||
@ -69,8 +72,14 @@ export const useHandleToggleTrashColumnFilter = ({
|
|||||||
value: '',
|
value: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
upsertRecordFilter(newFilter);
|
||||||
upsertCombinedViewFilter(newFilter);
|
upsertCombinedViewFilter(newFilter);
|
||||||
}, [columnDefinitions, objectMetadataItem, upsertCombinedViewFilter]);
|
}, [
|
||||||
|
columnDefinitions,
|
||||||
|
objectMetadataItem,
|
||||||
|
upsertCombinedViewFilter,
|
||||||
|
upsertRecordFilter,
|
||||||
|
]);
|
||||||
|
|
||||||
const toggleSoftDeleteFilterState = useRecoilCallback(
|
const toggleSoftDeleteFilterState = useRecoilCallback(
|
||||||
({ set }) =>
|
({ set }) =>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading';
|
import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading';
|
||||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||||
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
||||||
@ -42,29 +43,33 @@ export const RightDrawerRecord = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{
|
value={{ instanceId: `record-show-${objectRecordId}` }}
|
||||||
instanceId: `record-show-${objectRecordId}`,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{ instanceId: `record-show-${objectRecordId}` }}
|
value={{
|
||||||
|
instanceId: `record-show-${objectRecordId}`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<StyledRightDrawerRecord isMobile={isMobile}>
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
<RecordFieldValueSelectorContextProvider>
|
value={{ instanceId: `record-show-${objectRecordId}` }}
|
||||||
{!isNewViewableRecordLoading && (
|
>
|
||||||
<RecordValueSetterEffect recordId={objectRecordId} />
|
<StyledRightDrawerRecord isMobile={isMobile}>
|
||||||
)}
|
<RecordFieldValueSelectorContextProvider>
|
||||||
<RecordShowContainer
|
{!isNewViewableRecordLoading && (
|
||||||
objectNameSingular={objectNameSingular}
|
<RecordValueSetterEffect recordId={objectRecordId} />
|
||||||
objectRecordId={objectRecordId}
|
)}
|
||||||
loading={false}
|
<RecordShowContainer
|
||||||
isInRightDrawer={true}
|
objectNameSingular={objectNameSingular}
|
||||||
isNewRightDrawerItemLoading={isNewViewableRecordLoading}
|
objectRecordId={objectRecordId}
|
||||||
/>
|
loading={false}
|
||||||
</RecordFieldValueSelectorContextProvider>
|
isInRightDrawer={true}
|
||||||
</StyledRightDrawerRecord>
|
isNewRightDrawerItemLoading={isNewViewableRecordLoading}
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
/>
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
</RecordFieldValueSelectorContextProvider>
|
||||||
|
</StyledRightDrawerRecord>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { IconFilterOff } from 'twenty-ui';
|
import { IconFilterOff } from 'twenty-ui';
|
||||||
|
|
||||||
import { useObjectLabel } from '@/object-metadata/hooks/useObjectLabel';
|
import { useObjectLabel } from '@/object-metadata/hooks/useObjectLabel';
|
||||||
|
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
import { RecordTableEmptyStateDisplay } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateDisplay';
|
import { RecordTableEmptyStateDisplay } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateDisplay';
|
||||||
@ -25,14 +26,22 @@ export const RecordTableEmptyStateSoftDelete = () => {
|
|||||||
viewBarId: recordTableId,
|
viewBarId: recordTableId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
const handleButtonClick = async () => {
|
||||||
deleteCombinedViewFilter(
|
const deletedFilter = tableFilters.find(
|
||||||
tableFilters.find(
|
(filter) =>
|
||||||
(filter) =>
|
filter.definition.label === 'Deleted' &&
|
||||||
filter.definition.label === 'Deleted' &&
|
filter.operand === 'isNotEmpty',
|
||||||
filter.operand === 'isNotEmpty',
|
|
||||||
)?.id ?? '',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!deletedFilter) {
|
||||||
|
throw new Error('Deleted filter not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRecordFilter(deletedFilter.fieldMetadataId);
|
||||||
|
deleteCombinedViewFilter(deletedFilter.id);
|
||||||
|
|
||||||
toggleSoftDeleteFilterState(false);
|
toggleSoftDeleteFilterState(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
|||||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||||
import { useCombinedFindManyRecords } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecords';
|
import { useCombinedFindManyRecords } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecords';
|
||||||
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
|
import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
|
||||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
import { useUpsertRecordsInCacheForPrefetchKey } from '@/prefetch/hooks/internal/useUpsertRecordsInCacheForPrefetchKey';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { View } from '@/views/types/View';
|
import { View } from '@/views/types/View';
|
||||||
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
||||||
@ -19,16 +19,16 @@ export const PrefetchRunQueriesEffect = () => {
|
|||||||
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
|
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
|
||||||
|
|
||||||
const { upsertRecordsInCache: upsertViewsInCache } =
|
const { upsertRecordsInCache: upsertViewsInCache } =
|
||||||
usePrefetchRunQuery<View>({
|
useUpsertRecordsInCacheForPrefetchKey<View>({
|
||||||
prefetchKey: PrefetchKey.AllViews,
|
prefetchKey: PrefetchKey.AllViews,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { upsertRecordsInCache: upsertFavoritesInCache } =
|
const { upsertRecordsInCache: upsertFavoritesInCache } =
|
||||||
usePrefetchRunQuery<Favorite>({
|
useUpsertRecordsInCacheForPrefetchKey<Favorite>({
|
||||||
prefetchKey: PrefetchKey.AllFavorites,
|
prefetchKey: PrefetchKey.AllFavorites,
|
||||||
});
|
});
|
||||||
const { upsertRecordsInCache: upsertFavoritesFoldersInCache } =
|
const { upsertRecordsInCache: upsertFavoritesFoldersInCache } =
|
||||||
usePrefetchRunQuery<FavoriteFolder>({
|
useUpsertRecordsInCacheForPrefetchKey<FavoriteFolder>({
|
||||||
prefetchKey: PrefetchKey.AllFavoritesFolders,
|
prefetchKey: PrefetchKey.AllFavoritesFolders,
|
||||||
});
|
});
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export type UsePrefetchRunQuery = {
|
|||||||
prefetchKey: PrefetchKey;
|
prefetchKey: PrefetchKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePrefetchRunQuery = <T extends ObjectRecord>({
|
export const useUpsertRecordsInCacheForPrefetchKey = <T extends ObjectRecord>({
|
||||||
prefetchKey,
|
prefetchKey,
|
||||||
}: UsePrefetchRunQuery) => {
|
}: UsePrefetchRunQuery) => {
|
||||||
const setPrefetchDataIsLoaded = useSetRecoilState(
|
const setPrefetchDataIsLoaded = useSetRecoilState(
|
||||||
@ -45,7 +45,6 @@ export const usePrefetchRunQuery = <T extends ObjectRecord>({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
setPrefetchDataIsLoaded,
|
|
||||||
upsertRecordsInCache,
|
upsertRecordsInCache,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -3,6 +3,7 @@ import styled from '@emotion/styled';
|
|||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers';
|
import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers';
|
||||||
import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect';
|
import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect';
|
||||||
@ -41,30 +42,36 @@ export const SignInBackgroundMockContainer = () => {
|
|||||||
<ViewComponentInstanceContext.Provider
|
<ViewComponentInstanceContext.Provider
|
||||||
value={{ instanceId: recordIndexId }}
|
value={{ instanceId: recordIndexId }}
|
||||||
>
|
>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId: recordIndexId }}
|
value={{ instanceId: recordIndexId }}
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{ instanceId: recordIndexId }}
|
value={{
|
||||||
|
instanceId: recordIndexId,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ViewBar
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
viewBarId={viewBarId}
|
value={{ instanceId: recordIndexId }}
|
||||||
onCurrentViewChange={() => {}}
|
>
|
||||||
optionsDropdownButton={<></>}
|
<ViewBar
|
||||||
/>
|
viewBarId={viewBarId}
|
||||||
<SignInBackgroundMockContainerEffect
|
onCurrentViewChange={() => {}}
|
||||||
objectNamePlural={objectNamePlural}
|
optionsDropdownButton={<></>}
|
||||||
recordTableId={recordIndexId}
|
/>
|
||||||
viewId={viewBarId}
|
<SignInBackgroundMockContainerEffect
|
||||||
/>
|
objectNamePlural={objectNamePlural}
|
||||||
<RecordTableWithWrappers
|
recordTableId={recordIndexId}
|
||||||
objectNameSingular={objectNameSingular}
|
viewId={viewBarId}
|
||||||
recordTableId={recordIndexId}
|
/>
|
||||||
viewBarId={viewBarId}
|
<RecordTableWithWrappers
|
||||||
updateRecordMutation={() => {}}
|
objectNameSingular={objectNameSingular}
|
||||||
/>
|
recordTableId={recordIndexId}
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
viewBarId={viewBarId}
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
updateRecordMutation={() => {}}
|
||||||
|
/>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
</ViewComponentInstanceContext.Provider>
|
</ViewComponentInstanceContext.Provider>
|
||||||
</RecordIndexContextProvider>
|
</RecordIndexContextProvider>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { ObjectFilterOperandSelectAndInput } from '@/object-record/object-filter
|
|||||||
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
|
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
|
||||||
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
|
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
|
||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
|
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
|
import { useDeleteCombinedViewFilters } from '@/views/hooks/useDeleteCombinedViewFilters';
|
||||||
@ -73,14 +74,17 @@ export const EditableFilterDropdownButton = ({
|
|||||||
viewFilterDropdownId,
|
viewFilterDropdownId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
|
||||||
const handleRemove = () => {
|
const handleRemove = () => {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
|
|
||||||
deleteCombinedViewFilter(viewFilter.id);
|
deleteCombinedViewFilter(viewFilter.id);
|
||||||
|
removeRecordFilter(viewFilter.fieldMetadataId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDropdownClickOutside = useCallback(() => {
|
const handleDropdownClickOutside = useCallback(() => {
|
||||||
const { id: fieldId, value, operand } = viewFilter;
|
const { id: fieldId, value, operand, fieldMetadataId } = viewFilter;
|
||||||
if (
|
if (
|
||||||
!value &&
|
!value &&
|
||||||
![
|
![
|
||||||
@ -91,9 +95,10 @@ export const EditableFilterDropdownButton = ({
|
|||||||
RecordFilterOperand.IsToday,
|
RecordFilterOperand.IsToday,
|
||||||
].includes(operand)
|
].includes(operand)
|
||||||
) {
|
) {
|
||||||
|
removeRecordFilter(fieldMetadataId);
|
||||||
deleteCombinedViewFilter(fieldId);
|
deleteCombinedViewFilter(fieldId);
|
||||||
}
|
}
|
||||||
}, [viewFilter, deleteCombinedViewFilter]);
|
}, [viewFilter, deleteCombinedViewFilter, removeRecordFilter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useEffect } from 'react';
|
|||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2';
|
import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2';
|
||||||
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
|
import { useApplyViewFiltersToCurrentRecordFilters } from '@/views/hooks/useApplyViewFiltersToCurrentRecordFilters';
|
||||||
import { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates';
|
import { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates';
|
||||||
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
|
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
|
||||||
import { unsavedToUpsertViewFiltersComponentFamilyState } from '@/views/states/unsavedToUpsertViewFiltersComponentFamilyState';
|
import { unsavedToUpsertViewFiltersComponentFamilyState } from '@/views/states/unsavedToUpsertViewFiltersComponentFamilyState';
|
||||||
@ -20,6 +21,9 @@ export const QueryParamsFiltersEffect = () => {
|
|||||||
|
|
||||||
const { resetUnsavedViewStates } = useResetUnsavedViewStates();
|
const { resetUnsavedViewStates } = useResetUnsavedViewStates();
|
||||||
|
|
||||||
|
const { applyViewFiltersToCurrentRecordFilters } =
|
||||||
|
useApplyViewFiltersToCurrentRecordFilters();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hasFiltersQueryParams) {
|
if (!hasFiltersQueryParams) {
|
||||||
return;
|
return;
|
||||||
@ -27,10 +31,12 @@ export const QueryParamsFiltersEffect = () => {
|
|||||||
|
|
||||||
getFiltersFromQueryParams().then((filtersFromParams) => {
|
getFiltersFromQueryParams().then((filtersFromParams) => {
|
||||||
if (Array.isArray(filtersFromParams)) {
|
if (Array.isArray(filtersFromParams)) {
|
||||||
|
applyViewFiltersToCurrentRecordFilters(filtersFromParams);
|
||||||
setUnsavedViewFilter(filtersFromParams);
|
setUnsavedViewFilter(filtersFromParams);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
|
applyViewFiltersToCurrentRecordFilters,
|
||||||
getFiltersFromQueryParams,
|
getFiltersFromQueryParams,
|
||||||
hasFiltersQueryParams,
|
hasFiltersQueryParams,
|
||||||
resetUnsavedViewStates,
|
resetUnsavedViewStates,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useIcons } from 'twenty-ui';
|
import { useIcons } from 'twenty-ui';
|
||||||
|
|
||||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||||
|
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||||
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
||||||
@ -29,10 +30,14 @@ export const VariantFilterChip = ({
|
|||||||
viewBarId,
|
viewBarId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { removeRecordFilter } = useRemoveRecordFilter();
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
const handleRemoveClick = () => {
|
const handleRemoveClick = () => {
|
||||||
deleteCombinedViewFilter(viewFilter.id);
|
deleteCombinedViewFilter(viewFilter.id);
|
||||||
|
removeRecordFilter(viewFilter.fieldMetadataId);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
viewFilter.definition.label === 'Deleted' &&
|
viewFilter.definition.label === 'Deleted' &&
|
||||||
viewFilter.operand === 'isNotEmpty'
|
viewFilter.operand === 'isNotEmpty'
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
|||||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||||
import { VIEW_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ViewSortDropdownId';
|
import { VIEW_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/constants/ViewSortDropdownId';
|
||||||
import { ObjectSortDropdownComponentInstanceContext } from '@/object-record/object-sort-dropdown/states/context/ObjectSortDropdownComponentInstanceContext';
|
import { ObjectSortDropdownComponentInstanceContext } from '@/object-record/object-sort-dropdown/states/context/ObjectSortDropdownComponentInstanceContext';
|
||||||
|
import { ViewBarRecordFilterEffect } from '@/views/components/ViewBarRecordFilterEffect';
|
||||||
import { ViewEventContext } from '@/views/events/contexts/ViewEventContext';
|
import { ViewEventContext } from '@/views/events/contexts/ViewEventContext';
|
||||||
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
||||||
import { ViewBarDetails } from './ViewBarDetails';
|
import { ViewBarDetails } from './ViewBarDetails';
|
||||||
@ -53,6 +54,7 @@ export const ViewBar = ({
|
|||||||
value={{ instanceId: VIEW_SORT_DROPDOWN_ID }}
|
value={{ instanceId: VIEW_SORT_DROPDOWN_ID }}
|
||||||
>
|
>
|
||||||
<ViewEventContext.Provider value={{ onCurrentViewChange }}>
|
<ViewEventContext.Provider value={{ onCurrentViewChange }}>
|
||||||
|
<ViewBarRecordFilterEffect />
|
||||||
<ViewBarEffect viewBarId={viewBarId} />
|
<ViewBarEffect viewBarId={viewBarId} />
|
||||||
<ViewBarFilterEffect filterDropdownId={filterDropdownId} />
|
<ViewBarFilterEffect filterDropdownId={filterDropdownId} />
|
||||||
<ViewBarSortEffect />
|
<ViewBarSortEffect />
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import { EditableFilterDropdownButton } from '@/views/components/EditableFilterD
|
|||||||
import { EditableSortChip } from '@/views/components/EditableSortChip';
|
import { EditableSortChip } from '@/views/components/EditableSortChip';
|
||||||
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
||||||
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
|
|
||||||
|
import { useApplyCurrentViewFiltersToCurrentRecordFilters } from '@/views/hooks/useApplyCurrentViewFiltersToCurrentRecordFilters';
|
||||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
import { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates';
|
import { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates';
|
||||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||||
@ -167,9 +169,13 @@ export const ViewBarDetails = ({
|
|||||||
};
|
};
|
||||||
}, [currentViewWithCombinedFiltersAndSorts]);
|
}, [currentViewWithCombinedFiltersAndSorts]);
|
||||||
|
|
||||||
|
const { applyCurrentViewFiltersToCurrentRecordFilters } =
|
||||||
|
useApplyCurrentViewFiltersToCurrentRecordFilters();
|
||||||
|
|
||||||
const handleCancelClick = () => {
|
const handleCancelClick = () => {
|
||||||
if (isDefined(viewId)) {
|
if (isDefined(viewId)) {
|
||||||
resetUnsavedViewStates(viewId);
|
resetUnsavedViewStates(viewId);
|
||||||
|
applyCurrentViewFiltersToCurrentRecordFilters();
|
||||||
toggleSoftDeleteFilterState(false);
|
toggleSoftDeleteFilterState(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
|
||||||
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
|
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
|
||||||
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
|
import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState';
|
||||||
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
|
import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState';
|
||||||
import { onFilterSelectComponentState } from '@/object-record/object-filter-dropdown/states/onFilterSelectComponentState';
|
|
||||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters';
|
|
||||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||||
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
||||||
import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema';
|
import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema';
|
||||||
@ -23,19 +20,12 @@ type ViewBarFilterEffectProps = {
|
|||||||
export const ViewBarFilterEffect = ({
|
export const ViewBarFilterEffect = ({
|
||||||
filterDropdownId,
|
filterDropdownId,
|
||||||
}: ViewBarFilterEffectProps) => {
|
}: ViewBarFilterEffectProps) => {
|
||||||
const { upsertCombinedViewFilter } = useUpsertCombinedViewFilters();
|
|
||||||
|
|
||||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||||
|
|
||||||
const availableFilterDefinitions = useRecoilComponentValueV2(
|
const availableFilterDefinitions = useRecoilComponentValueV2(
|
||||||
availableFilterDefinitionsComponentState,
|
availableFilterDefinitionsComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setOnFilterSelect = useSetRecoilComponentStateV2(
|
|
||||||
onFilterSelectComponentState,
|
|
||||||
filterDropdownId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const filterDefinitionUsedInDropdown = useRecoilComponentValueV2(
|
const filterDefinitionUsedInDropdown = useRecoilComponentValueV2(
|
||||||
filterDefinitionUsedInDropdownComponentState,
|
filterDefinitionUsedInDropdownComponentState,
|
||||||
filterDropdownId,
|
filterDropdownId,
|
||||||
@ -62,17 +52,7 @@ export const ViewBarFilterEffect = ({
|
|||||||
if (isDefined(availableFilterDefinitions)) {
|
if (isDefined(availableFilterDefinitions)) {
|
||||||
setAvailableFilterDefinitions(availableFilterDefinitions);
|
setAvailableFilterDefinitions(availableFilterDefinitions);
|
||||||
}
|
}
|
||||||
setOnFilterSelect(() => (filter: RecordFilter | null) => {
|
}, [availableFilterDefinitions, setAvailableFilterDefinitions]);
|
||||||
if (isDefined(filter)) {
|
|
||||||
upsertCombinedViewFilter(filter);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
availableFilterDefinitions,
|
|
||||||
setAvailableFilterDefinitions,
|
|
||||||
setOnFilterSelect,
|
|
||||||
upsertCombinedViewFilter,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filterDefinitionUsedInDropdown?.type === 'RELATION') {
|
if (filterDefinitionUsedInDropdown?.type === 'RELATION') {
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
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 { useEffect } from 'react';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const ViewBarRecordFilterEffect = () => {
|
||||||
|
const { records: views, isDataPrefetched } = usePrefetchedData<View>(
|
||||||
|
PrefetchKey.AllViews,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentViewId = useRecoilComponentValueV2(currentViewIdComponentState);
|
||||||
|
|
||||||
|
const setCurrentRecordFilters = useSetRecoilComponentStateV2(
|
||||||
|
currentRecordFiltersComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const availableFilterDefinitions = useRecoilComponentValueV2(
|
||||||
|
availableFilterDefinitionsComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDataPrefetched) {
|
||||||
|
const currentView = views.find((view) => view.id === currentViewId);
|
||||||
|
|
||||||
|
if (isDefined(currentView)) {
|
||||||
|
setCurrentRecordFilters(
|
||||||
|
mapViewFiltersToFilters(
|
||||||
|
currentView.viewFilters,
|
||||||
|
availableFilterDefinitions,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
isDataPrefetched,
|
||||||
|
views,
|
||||||
|
availableFilterDefinitions,
|
||||||
|
currentViewId,
|
||||||
|
setCurrentRecordFilters,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -9,6 +9,7 @@ import { ContextStoreComponentInstanceContext } from '@/context-store/states/con
|
|||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||||
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
|
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { RecordIndexContainer } from '@/object-record/record-index/components/RecordIndexContainer';
|
import { RecordIndexContainer } from '@/object-record/record-index/components/RecordIndexContainer';
|
||||||
import { RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect } from '@/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect';
|
import { RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect } from '@/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect';
|
||||||
import { RecordIndexContainerContextStoreObjectMetadataEffect } from '@/object-record/record-index/components/RecordIndexContainerContextStoreObjectMetadataEffect';
|
import { RecordIndexContainerContextStoreObjectMetadataEffect } from '@/object-record/record-index/components/RecordIndexContainerContextStoreObjectMetadataEffect';
|
||||||
@ -81,28 +82,32 @@ export const RecordIndexPage = () => {
|
|||||||
<ViewComponentInstanceContext.Provider
|
<ViewComponentInstanceContext.Provider
|
||||||
value={{ instanceId: recordIndexId }}
|
value={{ instanceId: recordIndexId }}
|
||||||
>
|
>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{
|
value={{ instanceId: recordIndexId }}
|
||||||
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{
|
value={{
|
||||||
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
|
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PageTitle title={`${capitalize(objectNamePlural)}`} />
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
<RecordIndexPageHeader />
|
value={{
|
||||||
<PageBody>
|
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
|
||||||
<StyledIndexContainer>
|
}}
|
||||||
<RecordIndexContainerContextStoreObjectMetadataEffect />
|
>
|
||||||
<RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect />
|
<PageTitle title={`${capitalize(objectNamePlural)}`} />
|
||||||
<MainContextStoreComponentInstanceIdSetterEffect />
|
<RecordIndexPageHeader />
|
||||||
<RecordIndexContainer />
|
<PageBody>
|
||||||
</StyledIndexContainer>
|
<StyledIndexContainer>
|
||||||
</PageBody>
|
<RecordIndexContainerContextStoreObjectMetadataEffect />
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
<RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect />
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
<MainContextStoreComponentInstanceIdSetterEffect />
|
||||||
|
<RecordIndexContainer />
|
||||||
|
</StyledIndexContainer>
|
||||||
|
</PageBody>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
</ViewComponentInstanceContext.Provider>
|
</ViewComponentInstanceContext.Provider>
|
||||||
</RecordIndexContextProvider>
|
</RecordIndexContextProvider>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context
|
|||||||
import { TimelineActivityContext } from '@/activities/timeline-activities/contexts/TimelineActivityContext';
|
import { TimelineActivityContext } from '@/activities/timeline-activities/contexts/TimelineActivityContext';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer';
|
import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer';
|
||||||
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
|
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
|
||||||
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
|
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
|
||||||
@ -46,62 +47,68 @@ export const RecordShowPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordFieldValueSelectorContextProvider>
|
<RecordFieldValueSelectorContextProvider>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId: `record-show-${objectRecordId}` }}
|
value={{ instanceId: `record-show-${objectRecordId}` }}
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{ instanceId: `record-show-${objectRecordId}` }}
|
value={{ instanceId: `record-show-${objectRecordId}` }}
|
||||||
>
|
>
|
||||||
<RecordValueSetterEffect recordId={objectRecordId} />
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
<PageContainer>
|
value={{ instanceId: `record-show-${objectRecordId}` }}
|
||||||
<PageTitle title={pageTitle} />
|
>
|
||||||
<RecordShowPageHeader
|
<RecordValueSetterEffect recordId={objectRecordId} />
|
||||||
objectNameSingular={objectNameSingular}
|
<PageContainer>
|
||||||
objectRecordId={objectRecordId}
|
<PageTitle title={pageTitle} />
|
||||||
headerIcon={headerIcon}
|
<RecordShowPageHeader
|
||||||
>
|
objectNameSingular={objectNameSingular}
|
||||||
<>
|
objectRecordId={objectRecordId}
|
||||||
{!isCommandMenuV2Enabled &&
|
headerIcon={headerIcon}
|
||||||
objectNameSingular === CoreObjectNameSingular.Workflow && (
|
>
|
||||||
<RecordShowPageWorkflowHeader workflowId={objectRecordId} />
|
<>
|
||||||
)}
|
{!isCommandMenuV2Enabled &&
|
||||||
{!isCommandMenuV2Enabled &&
|
objectNameSingular === CoreObjectNameSingular.Workflow && (
|
||||||
objectNameSingular ===
|
<RecordShowPageWorkflowHeader
|
||||||
CoreObjectNameSingular.WorkflowVersion && (
|
workflowId={objectRecordId}
|
||||||
<RecordShowPageWorkflowVersionHeader
|
/>
|
||||||
workflowVersionId={objectRecordId}
|
)}
|
||||||
|
{!isCommandMenuV2Enabled &&
|
||||||
|
objectNameSingular ===
|
||||||
|
CoreObjectNameSingular.WorkflowVersion && (
|
||||||
|
<RecordShowPageWorkflowVersionHeader
|
||||||
|
workflowVersionId={objectRecordId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(isCommandMenuV2Enabled ||
|
||||||
|
(objectNameSingular !== CoreObjectNameSingular.Workflow &&
|
||||||
|
objectNameSingular !==
|
||||||
|
CoreObjectNameSingular.WorkflowVersion)) && (
|
||||||
|
<RecordShowActionMenu
|
||||||
|
{...{
|
||||||
|
isFavorite,
|
||||||
|
record,
|
||||||
|
handleFavoriteButtonClick,
|
||||||
|
objectMetadataItem,
|
||||||
|
objectNameSingular,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(isCommandMenuV2Enabled ||
|
</>
|
||||||
(objectNameSingular !== CoreObjectNameSingular.Workflow &&
|
</RecordShowPageHeader>
|
||||||
objectNameSingular !==
|
<PageBody>
|
||||||
CoreObjectNameSingular.WorkflowVersion)) && (
|
<TimelineActivityContext.Provider
|
||||||
<RecordShowActionMenu
|
value={{ labelIdentifierValue: pageName }}
|
||||||
{...{
|
>
|
||||||
isFavorite,
|
<RecordShowContainer
|
||||||
record,
|
objectNameSingular={objectNameSingular}
|
||||||
handleFavoriteButtonClick,
|
objectRecordId={objectRecordId}
|
||||||
objectMetadataItem,
|
loading={loading}
|
||||||
objectNameSingular,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
</TimelineActivityContext.Provider>
|
||||||
</>
|
</PageBody>
|
||||||
</RecordShowPageHeader>
|
</PageContainer>
|
||||||
<PageBody>
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
<TimelineActivityContext.Provider
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
value={{ labelIdentifierValue: pageName }}
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
>
|
|
||||||
<RecordShowContainer
|
|
||||||
objectNameSingular={objectNameSingular}
|
|
||||||
objectRecordId={objectRecordId}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
|
||||||
</TimelineActivityContext.Provider>
|
|
||||||
</PageBody>
|
|
||||||
</PageContainer>
|
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
|
||||||
</RecordFieldValueSelectorContextProvider>
|
</RecordFieldValueSelectorContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { mockedApolloClient } from '~/testing/mockedApolloClient';
|
|||||||
|
|
||||||
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
||||||
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
|
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
|
||||||
import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect';
|
import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect';
|
||||||
import { i18n } from '@lingui/core';
|
import { i18n } from '@lingui/core';
|
||||||
@ -88,7 +89,13 @@ const Providers = () => {
|
|||||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||||
<IconsProvider>
|
<IconsProvider>
|
||||||
<PrefetchDataProvider>
|
<PrefetchDataProvider>
|
||||||
<Outlet />
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
|
value={{
|
||||||
|
instanceId: 'storybook-test-record-filters',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Outlet />
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
</PrefetchDataProvider>
|
</PrefetchDataProvider>
|
||||||
</IconsProvider>
|
</IconsProvider>
|
||||||
</SnackBarProviderScope>
|
</SnackBarProviderScope>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||||
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { MockedResponse } from '@apollo/client/testing';
|
import { MockedResponse } from '@apollo/client/testing';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { MutableSnapshot } from 'recoil';
|
import { MutableSnapshot } from 'recoil';
|
||||||
@ -33,25 +34,33 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
|
|||||||
|
|
||||||
return ({ children }: { children: ReactNode }) => (
|
return ({ children }: { children: ReactNode }) => (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<ContextStoreComponentInstanceContext.Provider
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
value={{ instanceId: componentInstanceId }}
|
value={{
|
||||||
|
instanceId: componentInstanceId,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ActionMenuComponentInstanceContext.Provider
|
<ContextStoreComponentInstanceContext.Provider
|
||||||
value={{ instanceId: componentInstanceId }}
|
value={{ instanceId: componentInstanceId }}
|
||||||
>
|
>
|
||||||
<JestContextStoreSetter
|
<ActionMenuComponentInstanceContext.Provider
|
||||||
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
value={{
|
||||||
contextStoreNumberOfSelectedRecords={
|
instanceId: componentInstanceId,
|
||||||
contextStoreNumberOfSelectedRecords
|
}}
|
||||||
}
|
|
||||||
contextStoreCurrentObjectMetadataNameSingular={
|
|
||||||
contextStoreCurrentObjectMetadataNameSingular
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{children}
|
<JestContextStoreSetter
|
||||||
</JestContextStoreSetter>
|
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
||||||
</ActionMenuComponentInstanceContext.Provider>
|
contextStoreNumberOfSelectedRecords={
|
||||||
</ContextStoreComponentInstanceContext.Provider>
|
contextStoreNumberOfSelectedRecords
|
||||||
|
}
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular={
|
||||||
|
contextStoreCurrentObjectMetadataNameSingular
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</JestContextStoreSetter>
|
||||||
|
</ActionMenuComponentInstanceContext.Provider>
|
||||||
|
</ContextStoreComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,14 +2,16 @@ import { MockedProvider, MockedResponse } from '@apollo/client/testing';
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { MutableSnapshot, RecoilRoot } from 'recoil';
|
import { MutableSnapshot, RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||||
|
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||||
|
|
||||||
export const getJestMetadataAndApolloMocksWrapper = ({
|
export const getJestMetadataAndApolloMocksWrapper = ({
|
||||||
apolloMocks,
|
apolloMocks,
|
||||||
onInitializeRecoilSnapshot,
|
onInitializeRecoilSnapshot,
|
||||||
}: {
|
}: {
|
||||||
apolloMocks:
|
apolloMocks?:
|
||||||
| readonly MockedResponse<Record<string, any>, Record<string, any>>[]
|
| readonly MockedResponse<Record<string, any>, Record<string, any>>[]
|
||||||
| undefined;
|
| undefined;
|
||||||
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
|
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
|
||||||
@ -18,9 +20,17 @@ export const getJestMetadataAndApolloMocksWrapper = ({
|
|||||||
<RecoilRoot initializeState={onInitializeRecoilSnapshot}>
|
<RecoilRoot initializeState={onInitializeRecoilSnapshot}>
|
||||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||||
<MockedProvider mocks={apolloMocks} addTypename={false}>
|
<MockedProvider mocks={apolloMocks} addTypename={false}>
|
||||||
<JestObjectMetadataItemSetter>
|
<RecordFiltersComponentInstanceContext.Provider
|
||||||
{children}
|
value={{ instanceId: 'instanceId' }}
|
||||||
</JestObjectMetadataItemSetter>
|
>
|
||||||
|
<ViewComponentInstanceContext.Provider
|
||||||
|
value={{ instanceId: 'instanceId' }}
|
||||||
|
>
|
||||||
|
<JestObjectMetadataItemSetter>
|
||||||
|
{children}
|
||||||
|
</JestObjectMetadataItemSetter>
|
||||||
|
</ViewComponentInstanceContext.Provider>
|
||||||
|
</RecordFiltersComponentInstanceContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
</SnackBarProviderScope>
|
</SnackBarProviderScope>
|
||||||
</RecoilRoot>
|
</RecoilRoot>
|
||||||
|
|||||||
Reference in New Issue
Block a user