Skip to content

Commit 9b70193

Browse files
dengzhaofunclaude
andauthored
fix: show display labels in Select triggers instead of raw values (#167)
* fix: show display labels in Select triggers instead of raw values @base-ui/react Select.Value does not auto-mirror SelectItem text the way Radix UI does — it renders the raw value string. Resolve the label explicitly and pass it as children so ActivityScopeFilter (__standalone__, __all__, activity name), FilterSelect (regular, __all__), and FilterBar all display human-readable text. Fixes display in shop, check-in, task, banner, lottery filter bars. https://claude.ai/code/session_01NjUj8jpfA6rBuCYzqKaZmD * fix: add keepMounted to SelectContent so Select.Value auto-mirrors labels Without keepMounted, SelectItem children (and their ItemText registrations) are unmounted when the popup is closed. On initial render with a programmatic value, no items have ever mounted, so Select.Value falls back to showing the raw value string. keepMounted keeps items always mounted in the background, giving Select.Value access to every option's label at all times — fixes all remaining raw-value display issues across the codebase without touching individual callsites. https://claude.ai/code/session_01NjUj8jpfA6rBuCYzqKaZmD --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7f55b5d commit 9b70193

4 files changed

Lines changed: 19 additions & 5 deletions

File tree

apps/admin/src/components/activity/ActivityScopeFilter.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ export function ActivityScopeFilter({
4343
? ALL
4444
: value.activityId
4545

46+
const selectLabel =
47+
value.kind === "standalone"
48+
? m.activity_scope_standalone()
49+
: value.kind === "all"
50+
? m.activity_scope_all()
51+
: (activities ?? []).find((a) => a.id === value.activityId)?.name ?? selectValue
52+
4653
return (
4754
<div className="flex items-center gap-2">
4855
<span className="text-xs text-muted-foreground">{label ?? m.activity_scope_label()}</span>
@@ -55,7 +62,7 @@ export function ActivityScopeFilter({
5562
}}
5663
>
5764
<SelectTrigger className="h-8 w-auto min-w-48">
58-
<SelectValue />
65+
<SelectValue>{selectLabel}</SelectValue>
5966
</SelectTrigger>
6067
<SelectContent>
6168
<SelectItem value={STANDALONE}>{m.activity_scope_standalone()}</SelectItem>

apps/admin/src/components/patterns/FilterBar.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,18 @@ export function FilterBar({ search, filters = [], actions, className }: FilterBa
6464
</div>
6565
)}
6666

67-
{filters.map((filter) => (
67+
{filters.map((filter) => {
68+
const allOption = { value: filter.allValue ?? "all", label: filter.label }
69+
const allOptions = [allOption, ...filter.options]
70+
const displayLabel = allOptions.find((o) => o.value === filter.value)?.label ?? filter.label
71+
return (
6872
<Select
6973
key={filter.key}
7074
value={filter.value}
7175
onValueChange={((v: string | null) => filter.onChange(v ?? filter.allValue ?? "all")) as SelectChangeHandler}
7276
>
7377
<SelectTrigger className="w-auto min-w-[120px] shrink-0">
74-
<SelectValue placeholder={filter.label} />
78+
<SelectValue>{displayLabel}</SelectValue>
7579
</SelectTrigger>
7680
<SelectContent>
7781
<SelectItem value={filter.allValue ?? "all"}>
@@ -84,7 +88,8 @@ export function FilterBar({ search, filters = [], actions, className }: FilterBa
8488
))}
8589
</SelectContent>
8690
</Select>
87-
))}
91+
)
92+
})}
8893

8994
{actions && (
9095
<div className="ml-auto flex shrink-0 items-center gap-2">

apps/admin/src/components/ui/select.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ function SelectContent({
8181
className="isolate z-50"
8282
>
8383
<SelectPrimitive.Popup
84+
keepMounted
8485
data-slot="select-content"
8586
data-align-trigger={alignItemWithTrigger}
8687
className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}

apps/admin/src/routes/_dashboard/o.$orgSlug/p.$projectSlug/shop/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,15 @@ interface FilterSelectProps {
321321
}
322322

323323
function FilterSelect({ label, value, onChange, options }: FilterSelectProps) {
324+
const displayLabel = options.find((o) => o.value === value)?.label ?? label
324325
return (
325326
<div className="space-y-1">
326327
<label className="text-xs font-medium text-muted-foreground">
327328
{label}
328329
</label>
329330
<Select value={value} onValueChange={(v) => onChange(v ?? "")}>
330331
<SelectTrigger className="w-44">
331-
<SelectValue />
332+
<SelectValue>{displayLabel}</SelectValue>
332333
</SelectTrigger>
333334
<SelectContent>
334335
{options.map((o) => (

0 commit comments

Comments
 (0)