Add search any field front logic with its feature flag (#13278)

This PR adds the frontend logic to handle the user input of a search any
field value.

It also adds the associated feature flag, that can be modified from the
admin panel.

This PR does not add the filtering part nor the saving on view logic,
which will come in their separate PRs.



https://github.com/user-attachments/assets/6a52c090-b957-46aa-bff7-a90b51109789
This commit is contained in:
Lucas Bordeau
2025-07-18 15:38:56 +02:00
committed by GitHub
parent ce1d1f5bdd
commit 2f9c16f8a7
23 changed files with 363 additions and 6 deletions

View File

@ -732,6 +732,7 @@ export type FeatureFlagDto = {
export enum FeatureFlagKey {
IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED',
IS_AI_ENABLED = 'IS_AI_ENABLED',
IS_ANY_FIELD_SEARCH_ENABLED = 'IS_ANY_FIELD_SEARCH_ENABLED',
IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED',
IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',

View File

@ -696,6 +696,7 @@ export type FeatureFlagDto = {
export enum FeatureFlagKey {
IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED',
IS_AI_ENABLED = 'IS_AI_ENABLED',
IS_ANY_FIELD_SEARCH_ENABLED = 'IS_ANY_FIELD_SEARCH_ENABLED',
IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED',
IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',

View File

@ -0,0 +1,27 @@
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState';
import { useLingui } from '@lingui/react/macro';
export const ObjectFilterDropdownAnyFieldSearchInput = () => {
const { t } = useLingui();
const [viewAnyFieldSearchValue, setViewAnyFieldSearchValue] =
useRecoilComponentStateV2(viewAnyFieldSearchValueComponentState);
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = event.target.value;
setViewAnyFieldSearchValue(inputValue);
};
return (
<DropdownMenuSearchInput
autoFocus
type="text"
value={viewAnyFieldSearchValue}
placeholder={t`Search any field`}
onChange={handleSearchChange}
/>
);
};

View File

@ -42,7 +42,7 @@ export const ObjectFilterDropdownTextInput = () => {
<DropdownMenuInput
instanceId="object-filter-dropdown-text-input"
ref={handleInputRef}
value={objectFilterDropdownFilterValue}
value={objectFilterDropdownFilterValue ?? ''}
autoFocus
type="text"
placeholder={fieldMetadataItemUsedInDropdown?.label}

View File

@ -1,4 +1,5 @@
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownAnyFieldSearchIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
@ -32,6 +33,12 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
componentInstanceId,
);
const objectFilterDropdownAnyFieldSearchIsSelectedCallbackState =
useRecoilComponentCallbackStateV2(
objectFilterDropdownAnyFieldSearchIsSelectedComponentState,
componentInstanceId,
);
const objectFilterDropdownIsSelectingCompositeFieldCallbackState =
useRecoilComponentCallbackStateV2(
objectFilterDropdownIsSelectingCompositeFieldComponentState,
@ -53,6 +60,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
set(objectFilterDropdownIsSelectingCompositeFieldCallbackState, false);
set(fieldMetadataItemIdUsedInDropdownCallbackState, null);
set(objectFilterDropdownCurrentRecordFilterCallbackState, null);
set(objectFilterDropdownAnyFieldSearchIsSelectedCallbackState, false);
},
[
objectFilterDropdownSearchInputCallbackState,
@ -61,6 +69,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => {
objectFilterDropdownIsSelectingCompositeFieldCallbackState,
fieldMetadataItemIdUsedInDropdownCallbackState,
objectFilterDropdownCurrentRecordFilterCallbackState,
objectFilterDropdownAnyFieldSearchIsSelectedCallbackState,
],
);

View File

@ -0,0 +1,9 @@
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const objectFilterDropdownAnyFieldSearchIsSelectedComponentState =
createComponentStateV2<boolean>({
key: 'objectFilterDropdownAnyFieldSearchIsSelectedComponentState',
defaultValue: false,
componentInstanceContext: ObjectFilterDropdownComponentInstanceContext,
});

View File

@ -0,0 +1,29 @@
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId';
import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState';
import { IconFilter } from 'twenty-ui/display';
export const AnyFieldSearchChip = () => {
const { closeDropdown } = useCloseDropdown();
const [viewAnyFieldSearchValue, setViewAnyFieldSearchValue] =
useRecoilComponentStateV2(viewAnyFieldSearchValueComponentState);
const handleRemoveClick = () => {
closeDropdown();
setViewAnyFieldSearchValue('');
};
return (
<SortOrFilterChip
testId={ADVANCED_FILTER_DROPDOWN_ID}
labelKey={'Any field :'}
labelValue={viewAnyFieldSearchValue}
Icon={IconFilter}
onRemove={handleRemoveClick}
type="filter"
/>
);
};

View File

@ -0,0 +1,28 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DROPDOWN_OFFSET_Y } from '@/ui/layout/dropdown/constants/DropdownOffsetY';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { AnyFieldSearchChip } from '@/views/components/AnyFieldSearchChip';
import { AnyFieldSearchDropdownContent } from '@/views/components/AnyFieldSearchDropdownContent';
import { ANY_FIELD_SEARCH_DROPDOWN_ID } from '@/views/constants/AnyFieldSearchDropdownId';
export const AnyFieldSearchDropdownButton = () => {
const { openDropdown } = useOpenDropdown();
const handleOpenAnyFieldSearchDropdown = () => {
openDropdown({
dropdownComponentInstanceIdFromProps: ANY_FIELD_SEARCH_DROPDOWN_ID,
});
};
return (
<Dropdown
dropdownId={ANY_FIELD_SEARCH_DROPDOWN_ID}
clickableComponent={<AnyFieldSearchChip />}
dropdownComponents={<AnyFieldSearchDropdownContent />}
dropdownOffset={{ y: DROPDOWN_OFFSET_Y, x: 0 }}
dropdownPlacement="bottom-start"
onOpen={handleOpenAnyFieldSearchDropdown}
/>
);
};

View File

@ -0,0 +1,13 @@
import { ObjectFilterDropdownAnyFieldSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { AnyFieldSearchDropdownContentMenuHeader } from '@/views/components/AnyFieldSearchDropdownContentMenuHeader';
export const AnyFieldSearchDropdownContent = () => {
return (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<AnyFieldSearchDropdownContentMenuHeader />
<ObjectFilterDropdownAnyFieldSearchInput />
</DropdownContent>
);
};

View File

@ -0,0 +1,28 @@
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useLingui } from '@lingui/react/macro';
import { IconX } from 'twenty-ui/display';
export const AnyFieldSearchDropdownContentMenuHeader = () => {
const { t } = useLingui();
const { closeDropdown } = useCloseDropdown();
const handleBackButtonClick = () => {
closeDropdown();
};
return (
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={handleBackButtonClick}
Icon={IconX}
/>
}
>
{t`Search any field`}
</DropdownMenuHeader>
);
};

View File

@ -22,12 +22,16 @@ import { useAreViewFiltersDifferentFromRecordFilters } from '@/views/hooks/useAr
import { useAreViewSortsDifferentFromRecordSorts } from '@/views/hooks/useAreViewSortsDifferentFromRecordSorts';
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { AnyFieldSearchDropdownButton } from '@/views/components/AnyFieldSearchDropdownButton';
import { ANY_FIELD_SEARCH_DROPDOWN_ID } from '@/views/constants/AnyFieldSearchDropdownId';
import { useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups } from '@/views/hooks/useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups';
import { useAreViewFilterGroupsDifferentFromRecordFilterGroups } from '@/views/hooks/useAreViewFilterGroupsDifferentFromRecordFilterGroups';
import { isViewBarExpandedComponentState } from '@/views/states/isViewBarExpandedComponentState';
import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState';
import { t } from '@lingui/core/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { LightButton } from 'twenty-ui/input';
@ -119,6 +123,10 @@ export const ViewBarDetails = ({
currentRecordSortsComponentState,
);
const viewAnyFieldSearchValue = useRecoilComponentValueV2(
viewAnyFieldSearchValueComponentState,
);
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural: objectNamePlural,
});
@ -172,7 +180,19 @@ export const ViewBarDetails = ({
toggleSoftDeleteFilterState(false);
};
const shouldShowAdvancedFilterDropdownButton =
currentRecordFilterGroups.length > 0;
const isAnyFieldSearchDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
ANY_FIELD_SEARCH_DROPDOWN_ID,
);
const shouldShowAnyFieldSearchChip =
isNonEmptyString(viewAnyFieldSearchValue) || isAnyFieldSearchDropdownOpen;
const shouldExpandViewBar =
shouldShowAnyFieldSearchChip ||
viewFiltersAreDifferentFromRecordFilters ||
viewSortsAreDifferentFromRecordSorts ||
viewFilterGroupsAreDifferentFromRecordFilterGroups ||
@ -185,9 +205,6 @@ export const ViewBarDetails = ({
return null;
}
const shouldShowAdvancedFilterDropdownButton =
currentRecordFilterGroups.length > 0;
return (
<StyledBar>
<StyledFilterContainer>
@ -220,6 +237,7 @@ export const ViewBarDetails = ({
<StyledSeperator />
</StyledSeperatorContainer>
)}
{shouldShowAnyFieldSearchChip && <AnyFieldSearchDropdownButton />}
{shouldShowAdvancedFilterDropdownButton && (
<AdvancedFilterDropdownButton />
)}

View File

@ -0,0 +1,17 @@
import { ViewBarFilterDropdownAnyFieldSearchButtonMenuItem } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem';
import { useOpenAnyFieldSearchFilterFromViewBar } from '@/views/hooks/useOpenAnyFieldSearchFilterFromViewBar';
export const ViewBarFilterDropdownAnyFieldSearchButton = () => {
const { openAnyFieldSearchFilterFromViewBar } =
useOpenAnyFieldSearchFilterFromViewBar();
const handleSearchClick = () => {
openAnyFieldSearchFilterFromViewBar();
};
return (
<ViewBarFilterDropdownAnyFieldSearchButtonMenuItem
onClick={handleSearchClick}
/>
);
};

View File

@ -0,0 +1,57 @@
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { IconSearch } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS } from '@/views/constants/ViewBarFilterBottomMenuItemIds';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
const StyledSearchText = styled.span`
color: ${({ theme }) => theme.font.color.light};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
type ViewBarFilterDropdownAnyFieldSearchButtonMenuItemProps = {
onClick?: () => void;
};
export const ViewBarFilterDropdownAnyFieldSearchButtonMenuItem = ({
onClick,
}: ViewBarFilterDropdownAnyFieldSearchButtonMenuItemProps) => {
const { t } = useLingui();
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
objectFilterDropdownSearchInputComponentState,
);
const isSelected = useRecoilComponentFamilyValueV2(
isSelectedItemIdComponentFamilySelector,
VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS.SEARCH,
);
return (
<SelectableListItem
itemId={VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS.SEARCH}
onEnter={onClick}
>
<MenuItem
focused={isSelected}
onClick={onClick}
LeftIcon={IconSearch}
text={
<>
{t`Search any field`}
{objectFilterDropdownSearchInput && (
<StyledSearchText>{t`· ${objectFilterDropdownSearchInput}`}</StyledSearchText>
)}
</>
}
/>
</SelectableListItem>
);
};

View File

@ -0,0 +1,13 @@
import { ObjectFilterDropdownAnyFieldSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader';
export const ViewBarFilterDropdownAnyFieldSearchInput = () => {
return (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader />
<ObjectFilterDropdownAnyFieldSearchInput />
</DropdownContent>
);
};

View File

@ -0,0 +1,28 @@
import { useResetFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useResetFilterDropdown';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
import { useLingui } from '@lingui/react/macro';
import { IconChevronLeft } from 'twenty-ui/display';
export const ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader = () => {
const { t } = useLingui();
const { resetFilterDropdown } = useResetFilterDropdown();
const handleBackButtonClick = () => {
resetFilterDropdown();
};
return (
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={handleBackButtonClick}
Icon={IconChevronLeft}
/>
}
>
{t`Search any field`}
</DropdownMenuHeader>
);
};

View File

@ -1,6 +1,9 @@
import { ViewBarFilterDropdownAdvancedFilterButton } from '@/views/components/ViewBarFilterDropdownAdvancedFilterButton';
import { ViewBarFilterDropdownAnyFieldSearchButton } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchButton';
import { ViewBarFilterDropdownVectorSearchButton } from '@/views/components/ViewBarFilterDropdownVectorSearchButton';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import styled from '@emotion/styled';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
const StyledContainer = styled.div`
display: flex;
@ -11,9 +14,17 @@ const StyledContainer = styled.div`
`;
export const ViewBarFilterDropdownBottomMenu = () => {
const isAnyFieldSearchEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_ANY_FIELD_SEARCH_ENABLED,
);
return (
<StyledContainer>
<ViewBarFilterDropdownVectorSearchButton />
{isAnyFieldSearchEnabled ? (
<ViewBarFilterDropdownAnyFieldSearchButton />
) : (
<ViewBarFilterDropdownVectorSearchButton />
)}
<ViewBarFilterDropdownAdvancedFilterButton />
</StyledContainer>
);

View File

@ -1,7 +1,9 @@
import { objectFilterDropdownAnyFieldSearchIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState';
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewBarFilterDropdownAnyFieldSearchInput } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchInput';
import { ViewBarFilterDropdownFieldSelectMenu } from '@/views/components/ViewBarFilterDropdownFieldSelectMenu';
import { ViewBarFilterDropdownFilterInput } from '@/views/components/ViewBarFilterDropdownFilterInput';
import { ViewBarFilterDropdownVectorSearchInput } from '@/views/components/ViewBarFilterDropdownVectorSearchInput';
@ -14,6 +16,11 @@ export const ViewBarFilterDropdownContent = () => {
VIEW_BAR_FILTER_DROPDOWN_ID,
);
const objectFilterDropdownAnyFieldSearchIsSelected =
useRecoilComponentValueV2(
objectFilterDropdownAnyFieldSearchIsSelectedComponentState,
);
const selectedOperandInDropdown = useRecoilComponentValueV2(
selectedOperandInDropdownComponentState,
);
@ -21,6 +28,12 @@ export const ViewBarFilterDropdownContent = () => {
const isVectorSearchFilter =
selectedOperandInDropdown === ViewFilterOperand.VectorSearch;
const isAnyFieldSearchFilter = objectFilterDropdownAnyFieldSearchIsSelected;
if (isAnyFieldSearchFilter) {
return <ViewBarFilterDropdownAnyFieldSearchInput />;
}
if (isVectorSearchFilter) {
return <ViewBarFilterDropdownVectorSearchInput />;
}

View File

@ -0,0 +1 @@
export const ANY_FIELD_SEARCH_DROPDOWN_ID = 'any-field-search-dropdown';

View File

@ -0,0 +1,38 @@
import { objectFilterDropdownAnyFieldSearchIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState';
import { isNonEmptyString } from '@sniptt/guards';
export const useOpenAnyFieldSearchFilterFromViewBar = () => {
const setViewAnyFieldSearchValueComponentState = useSetRecoilComponentStateV2(
viewAnyFieldSearchValueComponentState,
);
const setObjectFilterDropdownAnyFieldSearchIsSelectedComponentState =
useSetRecoilComponentStateV2(
objectFilterDropdownAnyFieldSearchIsSelectedComponentState,
);
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
objectFilterDropdownSearchInputComponentState,
);
const openAnyFieldSearchFilterFromViewBar = () => {
const userHasAlreadyEnteredSearchInputForObjectDropdownSearch =
isNonEmptyString(objectFilterDropdownSearchInput);
if (userHasAlreadyEnteredSearchInputForObjectDropdownSearch) {
const filterValue = objectFilterDropdownSearchInput;
setViewAnyFieldSearchValueComponentState(filterValue);
}
setObjectFilterDropdownAnyFieldSearchIsSelectedComponentState(true);
};
return {
openAnyFieldSearchFilterFromViewBar,
};
};

View File

@ -0,0 +1,9 @@
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const viewAnyFieldSearchValueComponentState =
createComponentStateV2<string>({
key: 'viewAnyFieldSearchValueComponentState',
defaultValue: '',
componentInstanceContext: ViewComponentInstanceContext,
});

View File

@ -11,4 +11,5 @@ export enum FeatureFlagKey {
IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED',
IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED = 'IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED',
IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED',
IS_ANY_FIELD_SEARCH_ENABLED = 'IS_ANY_FIELD_SEARCH_ENABLED',
}

View File

@ -107,6 +107,7 @@ describe('WorkspaceEntityManager', () => {
IS_RELATION_CONNECT_ENABLED: false,
IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED: false,
IS_FIELDS_PERMISSIONS_ENABLED: false,
IS_ANY_FIELD_SEARCH_ENABLED: false,
},
eventEmitterService: {
emitMutationEvent: jest.fn(),

View File

@ -55,6 +55,11 @@ export const seedFeatureFlags = async (
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_ANY_FIELD_SEARCH_ENABLED,
workspaceId: workspaceId,
value: true,
},
])
.execute();
};