fix(front): apply advanced filter value and operand edits after a fresh page load (#23819)
## Problem On a freshly loaded page (e.g. opening a saved view with an advanced filter), editing an advanced filter rule silently fails: - Toggling a record in a relation value dropdown (e.g. Account Owner \`Is Me\` → adding a workspace member) does nothing: the checkbox does not stick and the filter is never updated. - Changing the operand (e.g. \`Is\` → \`Is not\`) is also a no-op. The edits only work in the session where the rule was just created, which is why this slips through manual testing of new filters. Found while QAing #23718: the new runtime-computed relation chip made the stale value visible enough to notice the edit was never applied. ## Root cause The object-filter-dropdown component states for an advanced filter row live under the row's instance id (\`advanced-filter-<recordFilterId>\`, provided by \`AdvancedFilterRecordFilterRow\`). They are hydrated by \`useSetRecordFilterUsedInAdvancedFilterDropdownRow\` when a rule is created — but never on a later page load. \`AdvancedFilterValueInput\` did write \`objectFilterDropdownCurrentRecordFilter\` & co on dropdown open, but under a different instance id (\`advanced-filter-view-filter-value-input-<recordFilterId>\`) that no dropdown content ever reads — dead writes. So after a reload, \`selectedOperandInDropdown\` is undefined in the instance the dropdown reads, and \`ObjectFilterDropdownRecordSelect.handleMultipleRecordSelectChange\` (gated on it) silently drops the selection. Same story for \`useApplyObjectFilterDropdownOperand\`, which sees no current record filter and never upserts. ## Fix - \`useSetRecordFilterUsedInAdvancedFilterDropdownRow\` now also hydrates \`subFieldNameUsedInDropdown\` and \`relationTargetFieldMetadataIdUsedInDropdown\`, mirroring \`useSetEditableFilterChipDropdownStates\` (the regular filter chip flow, which does not have this bug). - \`AdvancedFilterValueInput\` calls it on value-dropdown open instead of the phantom-instance writes, and its search-input/subFieldName states now target the row instance actually read by the dropdown content. - \`AdvancedFilterRecordFilterOperandSelectContent\` hydrates the same states on operand-dropdown open. ## Test Verified locally against a seeded workspace, on a saved view \`Account Owner Is Me\` reloaded in a fresh session: - Adding a member in the value dropdown now applies immediately: chip updates to \`Me, Aaron Munoz\`, results re-query, Update view appears. - Unchecking \`Me\` leaves \`Aaron Munoz\` with the record-name chip and the filter applied. - Changing the operand to \`Is not\` applies (count flipped from owned-by-me to the complement). - Regular (non-advanced) filter chips unchanged. Ran \`lint:diff-with-main\`, \`typecheck\` and the advanced-filter jest suites. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23819?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+9
@@ -1,4 +1,5 @@
|
||||
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
|
||||
import { useSetRecordFilterUsedInAdvancedFilterDropdownRow } from '@/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow';
|
||||
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
|
||||
import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand';
|
||||
|
||||
@@ -42,12 +43,19 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({
|
||||
const { applyObjectFilterDropdownOperand } =
|
||||
useApplyObjectFilterDropdownOperand();
|
||||
|
||||
const { setRecordFilterUsedInAdvancedFilterDropdownRow } =
|
||||
useSetRecordFilterUsedInAdvancedFilterDropdownRow();
|
||||
|
||||
const handleOperandChange = (operand: ViewFilterOperand) => {
|
||||
closeDropdown(dropdownId);
|
||||
|
||||
applyObjectFilterDropdownOperand(operand);
|
||||
};
|
||||
|
||||
const handleDropdownOpen = () => {
|
||||
setRecordFilterUsedInAdvancedFilterDropdownRow(filter);
|
||||
};
|
||||
|
||||
const selectedItemId = useAtomComponentStateValue(
|
||||
selectedItemIdComponentState,
|
||||
dropdownId,
|
||||
@@ -111,6 +119,7 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({
|
||||
}
|
||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||
dropdownPlacement="bottom-start"
|
||||
onOpen={handleDropdownOpen}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+12
-26
@@ -2,11 +2,10 @@ import { AdvancedFilterDropdownFilterInput } from '@/object-record/advanced-filt
|
||||
import { AdvancedFilterDropdownTextInput } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput';
|
||||
import { AdvancedFilterValueInputDropdownButtonClickableSelect } from '@/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect';
|
||||
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
|
||||
import { useSetRecordFilterUsedInAdvancedFilterDropdownRow } from '@/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow';
|
||||
import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId';
|
||||
import { shouldShowFilterTextInput } from '@/object-record/advanced-filter/utils/shouldShowFilterTextInput';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
@@ -31,13 +30,16 @@ export const AdvancedFilterValueInput = ({
|
||||
}: AdvancedFilterValueInputProps) => {
|
||||
const dropdownId = `advanced-filter-view-filter-value-input-${recordFilterId}`;
|
||||
|
||||
const objectFilterDropdownInstanceId =
|
||||
getAdvancedFilterObjectFilterDropdownComponentInstanceId(recordFilterId);
|
||||
|
||||
const currentRecordFilters = useAtomComponentStateValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const subFieldNameUsedInDropdown = useAtomComponentStateValue(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
dropdownId,
|
||||
objectFilterDropdownInstanceId,
|
||||
);
|
||||
|
||||
const recordFilter = currentRecordFilters.find(
|
||||
@@ -48,24 +50,11 @@ export const AdvancedFilterValueInput = ({
|
||||
|
||||
const setObjectFilterDropdownSearchInput = useSetAtomComponentState(
|
||||
objectFilterDropdownSearchInputComponentState,
|
||||
dropdownId,
|
||||
objectFilterDropdownInstanceId,
|
||||
);
|
||||
|
||||
const setFieldMetadataItemIdUsedInDropdown = useSetAtomComponentState(
|
||||
fieldMetadataItemIdUsedInDropdownComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setRelationTargetFieldMetadataIdUsedInDropdown =
|
||||
useSetAtomComponentState(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setObjectFilterDropdownCurrentRecordFilter = useSetAtomComponentState(
|
||||
objectFilterDropdownCurrentRecordFilterComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
const { setRecordFilterUsedInAdvancedFilterDropdownRow } =
|
||||
useSetRecordFilterUsedInAdvancedFilterDropdownRow();
|
||||
|
||||
const operandHasNoInput =
|
||||
recordFilter && !configurableViewFilterOperands.has(recordFilter.operand);
|
||||
@@ -79,11 +68,7 @@ export const AdvancedFilterValueInput = ({
|
||||
};
|
||||
|
||||
const handleFilterValueDropdownOpen = () => {
|
||||
setObjectFilterDropdownCurrentRecordFilter(recordFilter);
|
||||
setFieldMetadataItemIdUsedInDropdown(recordFilter.fieldMetadataId);
|
||||
setRelationTargetFieldMetadataIdUsedInDropdown(
|
||||
recordFilter.relationTargetFieldMetadataId ?? null,
|
||||
);
|
||||
setRecordFilterUsedInAdvancedFilterDropdownRow(recordFilter);
|
||||
};
|
||||
|
||||
const filterType = recordFilter.type;
|
||||
@@ -95,7 +80,8 @@ export const AdvancedFilterValueInput = ({
|
||||
|
||||
const showFilterTextInputInsteadOfDropdown = shouldShowFilterTextInput({
|
||||
recordFilter,
|
||||
subFieldNameUsedInDropdown,
|
||||
subFieldNameUsedInDropdown:
|
||||
subFieldNameUsedInDropdown ?? recordFilter.subFieldName,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
+16
@@ -1,7 +1,9 @@
|
||||
import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId';
|
||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||
import { relationTargetFieldMetadataIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/relationTargetFieldMetadataIdUsedInDropdownComponentState';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { useCallback } from 'react';
|
||||
import { useStore } from 'jotai';
|
||||
@@ -35,6 +37,20 @@ export const useSetRecordFilterUsedInAdvancedFilterDropdownRow = () => {
|
||||
}),
|
||||
recordFilter,
|
||||
);
|
||||
|
||||
store.set(
|
||||
subFieldNameUsedInDropdownComponentState.atomFamily({
|
||||
instanceId: advancedFilterRowObjectFilterDropdownComponentInstanceId,
|
||||
}),
|
||||
recordFilter.subFieldName,
|
||||
);
|
||||
|
||||
store.set(
|
||||
relationTargetFieldMetadataIdUsedInDropdownComponentState.atomFamily({
|
||||
instanceId: advancedFilterRowObjectFilterDropdownComponentInstanceId,
|
||||
}),
|
||||
recordFilter.relationTargetFieldMetadataId ?? null,
|
||||
);
|
||||
},
|
||||
[store],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user