fix(ui): the filter pills open their menu on screen again

The pill trigger paints its own label spans and mounts no SelectValue,
and Radix's item-aligned placement silently skips all positioning when
the value node is absent — the open menu laid out below the
height-locked app shell, fully off-viewport, which reads as a dead
control. The mood and type/source pills on the mood and measurements
lists were affected since the unified filter rail landed in v1.16.1.
Popper placement has no value-node dependency and anchors the menu
under the pill like the date-range popover next to it.
This commit is contained in:
Marc-André Bombeck committed 2026-06-12 19:35:11 +02:00
1 parent 07fe14c6ae
commit 5142823b7e
3 files changed
+28 -1

No files matched your search

@@ -1,4 +1,6 @@
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { renderToStaticMarkup } from "react-dom/server";
import { I18nProvider } from "@/lib/i18n/context";
@@ -141,3 +143,21 @@ describe("FilterBar shell", () => {
expect(html).toContain('aria-label="Filter entfernen: Quelle"');
});
});
describe("FilterBarSelect — menu positioning (source pin)", () => {
// The pill trigger paints its own label spans and mounts no
// `<SelectValue>`. Radix's default item-aligned placement silently
// skips ALL positioning when the value node is absent — the open
// menu then lays out below the height-locked app shell, fully
// off-viewport, which users report as "clicking does nothing". SSR
// markup cannot exercise an open portal, so this pins the source:
// the content must request popper placement, which has no
// value-node dependency.
it("requests popper placement on the select content", () => {
const src = readFileSync(
join(process.cwd(), "src/components/ui/filter-bar.tsx"),
"utf8",
);
expect(src).toMatch(/<SelectContent\s+position="popper"\s+align="start">/);
});
});
+8 -1
View File
@@ -175,7 +175,14 @@ export function FilterBarSelect({
<span className="text-muted-foreground">{label}</span>
)}
</SelectTrigger>
<SelectContent>
{/* `position="popper"` is load-bearing: this trigger paints its
own label spans and mounts no `SelectValue`, and Radix's
default item-aligned placement silently skips ALL positioning
when the value node is absent — the open menu then lays out
below the height-locked app shell, fully off-viewport. Popper
placement has no value-node dependency and anchors the menu
under the pill like the date-range popover next to it. */}
<SelectContent position="popper" align="start">
<SelectItem value={allValue}>{allLabel}</SelectItem>
{options.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>