fix(planner): open the places filters across the whole sidebar row

Hung off an icon near the end of the row, the category and rating lists ran
out of the sidebar and were cut off.
This commit is contained in:
Maurice
2026-10-14 10:45:10 +02:00
committed by Maurice
parent 341520f98b
commit 7c6b7622fc
2 changed files with 21 additions and 5 deletions
@@ -497,6 +497,20 @@ describe('Category filter dropdown', () => {
expect(screen.getByRole('button', { name: 'Museum' })).toHaveAttribute('aria-pressed', 'false');
});
it('FE-PLANNER-SIDEBAR-023b: the list opens across the whole filter row, not off its icon', async () => {
const user = userEvent.setup();
const cat = buildCategory({ name: 'Museum', color: '#3b82f6' });
render(<PlacesSidebar {...defaultProps} categories={[cat]} />);
await openCategories(user);
// Hung off an icon near the end of the row, the list ran out of the sidebar.
const list = screen.getByRole('button', { name: 'Museum' }).closest('.trek-popover-enter') as HTMLElement;
expect(list).toHaveClass('inset-x-0');
// Its positioning ancestor is the row that holds every filter.
let anchor = list.parentElement;
while (anchor && !anchor.classList.contains('relative')) anchor = anchor.parentElement;
expect(anchor).toContainElement(screen.getByRole('button', { name: 'Show' }));
});
it('FE-PLANNER-SIDEBAR-024: selecting a category filters places', async () => {
const user = userEvent.setup();
const cat = buildCategory({ name: 'Park', color: '#22c55e' });
@@ -87,7 +87,8 @@ export function PlacesHeader(S: SidebarState) {
</BandButton>
)}
</div>
<div className="flex items-center gap-1.5">
{/* The filters' lists open across this whole row (see FilterDropdown). */}
<div className="relative flex items-center gap-1.5">
<ShowDropdown {...S} />
<CategoryFilter {...S} />
<RatingFilter {...S} />
@@ -172,9 +173,10 @@ function ShowDropdown(S: SidebarState) {
}
/**
* A filter as one icon on the tabs row, opening a small list under it; closes on a
* A filter as one icon on the tabs row, opening a list under the row; closes on a
* click outside or Escape. A badge marks it while it is narrowing the list, and the
* tooltip says to what.
* tooltip says to what. The list spans the whole row rather than hanging off its
* trigger: hung off an icon near the end, it ran out of the sidebar and was cut off.
*/
function FilterDropdown({ label, active, badge, icon, open, setOpen, name, children, wide = false, count }: {
/** What is in force: on the trigger when `wide`, else in the tooltip ("All categories", "4+"). */
@@ -210,7 +212,7 @@ function FilterDropdown({ label, active, badge, icon, open, setOpen, name, child
}
}, [open, setOpen])
return (
<div ref={wrapRef} className={`relative ${wide ? 'min-w-0 flex-1' : 'flex-none'}`}>
<div ref={wrapRef} className={wide ? 'min-w-0 flex-1' : 'flex-none'}>
{wide ? (
<button type="button" onClick={() => setOpen(v => !v)} aria-expanded={open} aria-haspopup="listbox" aria-label={name}
className={`${CONTROL} flex w-full min-w-0 items-center gap-1.5 bg-surface-card px-2.5 font-semibold transition-colors ${active || open ? 'text-content' : 'text-content-secondary hover:text-content'}`}
@@ -233,7 +235,7 @@ function FilterDropdown({ label, active, badge, icon, open, setOpen, name, child
</Tooltip>
)}
{open && (
<div className={`trek-popover-enter absolute ${wide ? 'start-0' : 'end-0'} top-full z-50 mt-1.5 flex max-h-[min(60vh,360px)] flex-col overflow-y-auto rounded-[12px] border border-edge-secondary bg-surface-card p-1.5 shadow-popover`} style={{ width: 'max-content', minWidth: 180, maxWidth: 260 }}>
<div className="trek-popover-enter absolute inset-x-0 top-full z-50 mt-1.5 flex max-h-[min(60vh,360px)] flex-col overflow-y-auto rounded-[12px] border border-edge-secondary bg-surface-card p-1.5 shadow-popover">
{children}
</div>
)}