fix: show active advanced filter count badge in dropdown button (#20229)
## Summary Replaces the hardcoded `0` in `ViewBarFilterDropdownAdvancedFilterButton` with the actual count of active advanced filter rules, matching the behavior of `AdvancedFilterChip` in the view bar. ## What changed In `packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx`: - Imported `useAtomComponentSelectorValue` and `rootLevelRecordFilterGroupComponentSelector` - Imported `useChildRecordFiltersAndRecordFilterGroups` - Replaced `const advancedFilterQuerySubFilterCount = 0; // TODO` with the real computed count via the same hook pattern used in `AdvancedFilterChip.tsx` The pill badge will now appear on the "Advanced filter" dropdown menu item showing the number of active advanced filter rules (e.g. "2" when two rules are active). ## References - Fixes #20207 --------- Co-authored-by: wadeKeith <wade@twenty.app> Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+21
-6
@@ -7,9 +7,12 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
|
||||
import { VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS } from '@/views/constants/ViewBarFilterBottomMenuItemIds';
|
||||
|
||||
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
||||
import { useSetRecordFilterUsedInAdvancedFilterDropdownRow } from '@/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow';
|
||||
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
|
||||
import { useCreateEmptyRecordFilterFromFieldMetadataItem } from '@/object-record/record-filter/hooks/useCreateEmptyRecordFilterFromFieldMetadataItem';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
@@ -34,7 +37,17 @@ const StyledPillContainer = styled.span`
|
||||
`;
|
||||
|
||||
export const ViewBarFilterDropdownAdvancedFilterButton = () => {
|
||||
const advancedFilterQuerySubFilterCount = 0; // TODO
|
||||
const rootRecordFilterGroup = useAtomComponentSelectorValue(
|
||||
rootLevelRecordFilterGroupComponentSelector,
|
||||
);
|
||||
|
||||
const { childRecordFiltersAndRecordFilterGroups } =
|
||||
useChildRecordFiltersAndRecordFilterGroups({
|
||||
recordFilterGroupId: rootRecordFilterGroup?.id,
|
||||
});
|
||||
|
||||
const advancedFilterQuerySubFilterCount =
|
||||
childRecordFiltersAndRecordFilterGroups.length;
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
@@ -134,12 +147,14 @@ export const ViewBarFilterDropdownAdvancedFilterButton = () => {
|
||||
onClick={handleClick}
|
||||
LeftIcon={IconFilter}
|
||||
focused={isSelectedItemId}
|
||||
RightComponent={
|
||||
advancedFilterQuerySubFilterCount > 0 ? (
|
||||
<StyledPillContainer>
|
||||
<Pill label={advancedFilterQuerySubFilterCount.toString()} />
|
||||
</StyledPillContainer>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
{advancedFilterQuerySubFilterCount > 0 && (
|
||||
<StyledPillContainer>
|
||||
<Pill label={advancedFilterQuerySubFilterCount.toString()} />
|
||||
</StyledPillContainer>
|
||||
)}
|
||||
</SelectableListItem>
|
||||
);
|
||||
};
|
||||
|
||||
+101
-2
@@ -9,16 +9,22 @@ import { RecordTableComponentInstanceContext } from '@/object-record/record-tabl
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { ViewBarFilterDropdown } from '@/views/components/ViewBarFilterDropdown';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import {
|
||||
CoreObjectNameSingular,
|
||||
RecordFilterGroupLogicalOperator,
|
||||
ViewFilterOperand,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { ViewBarFilterDropdownIds } from '@/views/constants/ViewBarFilterDropdownIds';
|
||||
import { userEvent, within } from 'storybook/test';
|
||||
import { expect, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
|
||||
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
|
||||
@@ -191,3 +197,96 @@ export const Number: Story = {
|
||||
await userEvent.click(dateFilter);
|
||||
},
|
||||
};
|
||||
|
||||
const MOCK_ROOT_FILTER_GROUP_ID = 'test-root-filter-group-id';
|
||||
|
||||
export const AdvancedFilterCountBadge: Story = {
|
||||
decorators: [
|
||||
(Story) => {
|
||||
const companyObjectMetadataItem =
|
||||
getTestEnrichedObjectMetadataItemsMock().find(
|
||||
(item) => item.nameSingular === CoreObjectNameSingular.Company,
|
||||
)!;
|
||||
const instanceId = companyObjectMetadataItem.id;
|
||||
|
||||
const setCurrentRecordFilterGroups = useSetAtomComponentState(
|
||||
currentRecordFilterGroupsComponentState,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
const setCurrentRecordFilters = useSetAtomComponentState(
|
||||
currentRecordFiltersComponentState,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
const firstFieldMetadataItem = companyObjectMetadataItem.fields[0];
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentRecordFilterGroups([
|
||||
{
|
||||
id: MOCK_ROOT_FILTER_GROUP_ID,
|
||||
logicalOperator: RecordFilterGroupLogicalOperator.AND,
|
||||
positionInRecordFilterGroup: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
setCurrentRecordFilters([
|
||||
{
|
||||
id: 'filter-1',
|
||||
fieldMetadataId: firstFieldMetadataItem.id,
|
||||
value: 'test-value-1',
|
||||
displayValue: 'Test 1',
|
||||
type: 'TEXT',
|
||||
operand: ViewFilterOperand.CONTAINS,
|
||||
label: firstFieldMetadataItem.label,
|
||||
recordFilterGroupId: MOCK_ROOT_FILTER_GROUP_ID,
|
||||
positionInRecordFilterGroup: 0,
|
||||
},
|
||||
{
|
||||
id: 'filter-2',
|
||||
fieldMetadataId: firstFieldMetadataItem.id,
|
||||
value: 'test-value-2',
|
||||
displayValue: 'Test 2',
|
||||
type: 'TEXT',
|
||||
operand: ViewFilterOperand.CONTAINS,
|
||||
label: firstFieldMetadataItem.label,
|
||||
recordFilterGroupId: MOCK_ROOT_FILTER_GROUP_ID,
|
||||
positionInRecordFilterGroup: 1,
|
||||
},
|
||||
{
|
||||
id: 'filter-3',
|
||||
fieldMetadataId: firstFieldMetadataItem.id,
|
||||
value: 'test-value-3',
|
||||
displayValue: 'Test 3',
|
||||
type: 'TEXT',
|
||||
operand: ViewFilterOperand.CONTAINS,
|
||||
label: firstFieldMetadataItem.label,
|
||||
recordFilterGroupId: MOCK_ROOT_FILTER_GROUP_ID,
|
||||
positionInRecordFilterGroup: 2,
|
||||
},
|
||||
]);
|
||||
}, [
|
||||
setCurrentRecordFilterGroups,
|
||||
setCurrentRecordFilters,
|
||||
firstFieldMetadataItem,
|
||||
]);
|
||||
|
||||
return <Story />;
|
||||
},
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement.ownerDocument.body);
|
||||
|
||||
const filterButton = await canvas.findByText('Filter');
|
||||
|
||||
await userEvent.click(filterButton);
|
||||
|
||||
const advancedFilterButton = await canvas.findByText('Advanced filter');
|
||||
|
||||
expect(advancedFilterButton).toBeVisible();
|
||||
|
||||
const pillBadge = await canvas.findByText('3');
|
||||
|
||||
expect(pillBadge).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user