Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/angry-boats-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---

fix(search-filters): prevent nested filter dropdowns from disappearing on reopen
209 changes: 101 additions & 108 deletions packages/app/src/components/DBSearchPageFilters/NestedFilterGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,122 +149,115 @@ export const NestedFilterGroup = ({
pb={0}
pl="xs"
>
{isExpanded && (
<div className={classes.filterGroupPanel}>
{childFilters.length === 0 ? (
<Group m={6} gap="xs">
<Text c="dimmed" size="xs">
No properties found
</Text>
</Group>
) : (
<div className={classes.filterGroupPanel}>
{childFilters.length === 0 ? (
<Group m={6} gap="xs">
<Text c="dimmed" size="xs">
No properties found
</Text>
</Group>
) : (
<div
ref={scrollContainerRef}
style={{
maxHeight: MAX_VIRTUAL_LIST_HEIGHT,
overflow: 'auto',
}}
>
<div
ref={scrollContainerRef}
style={{
maxHeight: MAX_VIRTUAL_LIST_HEIGHT,
overflow: 'auto',
height: `${rowVirtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
>
<div
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
>
{rowVirtualizer.getVirtualItems().map(virtualRow => {
const child = childFilters[virtualRow.index];
const childSelectedValues = selectedValues[
child.key
] || {
included: new Set(),
excluded: new Set(),
};
const childHasSelections =
childSelectedValues.included.size +
childSelectedValues.excluded.size >
0;
{rowVirtualizer.getVirtualItems().map(virtualRow => {
const child = childFilters[virtualRow.index];
const childSelectedValues = selectedValues[child.key] || {
included: new Set(),
excluded: new Set(),
};
const childHasSelections =
childSelectedValues.included.size +
childSelectedValues.excluded.size >
0;

return (
<div
key={child.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`,
paddingTop: 4,
paddingBottom: 4,
return (
<div
key={child.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`,
paddingTop: 4,
paddingBottom: 4,
}}
>
<FilterGroup
data-testid={`nested-filter-group-${child.key}`}
name={child.propertyPath}
distributionKey={child.key}
options={child.value.map(value => ({
value: value,
label: value.toString(),
}))}
optionsLoading={false}
selectedValues={childSelectedValues}
onChange={value => onChange(child.key, value)}
onClearClick={() => onClearClick(child.key)}
onOnlyClick={value => onOnlyClick(child.key, value)}
onExcludeClick={value =>
onExcludeClick(child.key, value)
}
valuePins={{
onPinClick: value => onPinClick(child.key, value),
isPinned: value => isPinned(child.key, value),
onSharedPinClick: onSharedPinClick
? value => onSharedPinClick(child.key, value)
: undefined,
isSharedPinned: isSharedPinned
? value => isSharedPinned(child.key, value)
: undefined,
}}
>
<FilterGroup
data-testid={`nested-filter-group-${child.key}`}
name={child.propertyPath}
distributionKey={child.key}
options={child.value.map(value => ({
value: value,
label: value.toString(),
}))}
optionsLoading={false}
selectedValues={childSelectedValues}
onChange={value => onChange(child.key, value)}
onClearClick={() => onClearClick(child.key)}
onOnlyClick={value =>
onOnlyClick(child.key, value)
}
onExcludeClick={value =>
onExcludeClick(child.key, value)
}
valuePins={{
onPinClick: value =>
onPinClick(child.key, value),
isPinned: value => isPinned(child.key, value),
onSharedPinClick: onSharedPinClick
? value => onSharedPinClick(child.key, value)
: undefined,
isSharedPinned: isSharedPinned
? value => isSharedPinned(child.key, value)
: undefined,
}}
fieldPins={{
onFieldPinClick: () =>
onFieldPinClick?.(child.key),
isFieldPinned: isFieldPinned?.(child.key),
onToggleSharedFieldPin: () =>
onToggleSharedFieldPin?.(child.key),
isSharedFieldPinned: isSharedFieldPinned?.(
child.key,
),
}}
onColumnToggle={
onColumnToggle
? () => onColumnToggle(child.key)
: undefined
}
isColumnDisplayed={displayedColumns?.includes(
fieldPins={{
onFieldPinClick: () =>
onFieldPinClick?.(child.key),
isFieldPinned: isFieldPinned?.(child.key),
onToggleSharedFieldPin: () =>
onToggleSharedFieldPin?.(child.key),
isSharedFieldPinned: isSharedFieldPinned?.(
child.key,
)}
onLoadMore={() => onLoadMore(child.key)}
loadMoreLoading={
loadMoreLoading[child.key] || false
}
hasLoadedMore={hasLoadedMore[child.key] || false}
showFilterCounts={showFilterCounts}
isDefaultExpanded={childHasSelections}
chartConfig={chartConfig}
isLive={isLive}
/>
</div>
);
})}
</div>
),
}}
onColumnToggle={
onColumnToggle
? () => onColumnToggle(child.key)
: undefined
}
isColumnDisplayed={displayedColumns?.includes(
child.key,
)}
onLoadMore={() => onLoadMore(child.key)}
loadMoreLoading={
loadMoreLoading[child.key] || false
}
hasLoadedMore={hasLoadedMore[child.key] || false}
showFilterCounts={showFilterCounts}
isDefaultExpanded={childHasSelections}
chartConfig={chartConfig}
isLive={isLive}
/>
</div>
);
})}
</div>
)}
</div>
)}
</div>
)}
</div>
</Accordion.Panel>
</div>
</Accordion.Item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,27 @@ describe('NestedFilterGroup', () => {

expect(screen.queryAllByTestId(/nested-filter-group-attr/)).toHaveLength(0);
});

it('should allow reopening after close (panel stays accessible)', async () => {
renderWithMantine(
<NestedFilterGroup {...defaultNestedProps} isDefaultExpanded={false} />,
);

// Initially closed
const panel = screen.getByTestId('nested-filter-group-panel');
expect(panel).toHaveAttribute('aria-hidden', 'true');

// Open the group
const control = screen.getByTestId('nested-filter-group-control');
await userEvent.click(control);
expect(panel).toHaveAttribute('aria-hidden', 'false');

// Close the group
await userEvent.click(control);
expect(panel).toHaveAttribute('aria-hidden', 'true');

// Reopen the group — should not be stuck closed
await userEvent.click(control);
expect(panel).toHaveAttribute('aria-hidden', 'false');
});
});
Loading