From cfe8020831e929089806f8eaf0439b7a8b2a1d19 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 30 Jul 2026 23:47:34 -0700 Subject: [PATCH] Fix multi-select menus and selected label rendering --- patches/@umami__react-zen@0.247.0.patch | 74 +++++++++++++++++++ pnpm-lock.yaml | 7 +- pnpm-workspace.yaml | 2 + src/app/(main)/teams/TeamMemberAddForm.tsx | 2 +- .../(reports)/attribution/AttributionPage.tsx | 6 +- src/components/common/FilterRecord.tsx | 6 +- src/components/common/LinkButton.tsx | 2 +- src/components/common/MultiSelect.tsx | 66 +++++++++++------ .../metrics/MetricsExpandedTable.tsx | 9 ++- .../property-data/PropertyFilterRecord.tsx | 6 +- 10 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 patches/@umami__react-zen@0.247.0.patch diff --git a/patches/@umami__react-zen@0.247.0.patch b/patches/@umami__react-zen@0.247.0.patch new file mode 100644 index 000000000..2caf21ae2 --- /dev/null +++ b/patches/@umami__react-zen@0.247.0.patch @@ -0,0 +1,74 @@ +diff --git a/dist/index.js b/dist/index.js +index 2dd4992d4f033c22be13a8846637d48ef235c57f..cf5264a8ea0b6803b6fb9a950d35e7bd1ed0f959 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -6600,6 +6600,23 @@ function SearchField({ + ] }) + ] }); + } ++function getSelectItemLabel(children, selectedValue) { ++ let label; ++ react.Children.forEach(children, (child) => { ++ if (label !== void 0 || !react.isValidElement(child)) { ++ return; ++ } ++ if (child.type === ListItem) { ++ const itemValue = child.props.value ?? child.props.id ?? (typeof child.props.children === "string" ? child.props.children : ""); ++ if (itemValue === selectedValue) { ++ label = child.props.children; ++ } ++ return; ++ } ++ label = getSelectItemLabel(child.props.children, selectedValue); ++ }); ++ return label; ++} + function Select({ + value, + defaultValue, +@@ -6666,7 +6683,7 @@ function Select({ + ), + children: [ + /* @__PURE__ */ jsxRuntime.jsx(select.Select.Value, { placeholder, children: (selected) => { +- const defaultChildren = selected ?? placeholder; ++ const defaultChildren = selected == null ? placeholder : getSelectItemLabel(collection, selected) ?? selected; + return typeof renderValue === "function" ? renderValue({ + defaultChildren, + isPlaceholder: selected == null +diff --git a/dist/index.mjs b/dist/index.mjs +index 448a673d0ef00587ba6a233d5e5f8e99efc8215e..f0c0f4e7a5a9f0e2066225e1598f944fbc033c6e 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -6575,6 +6575,23 @@ function SearchField({ + ] }) + ] }); + } ++function getSelectItemLabel(children, selectedValue) { ++ let label; ++ Children.forEach(children, (child) => { ++ if (label !== void 0 || !isValidElement(child)) { ++ return; ++ } ++ if (child.type === ListItem) { ++ const itemValue = child.props.value ?? child.props.id ?? (typeof child.props.children === "string" ? child.props.children : ""); ++ if (itemValue === selectedValue) { ++ label = child.props.children; ++ } ++ return; ++ } ++ label = getSelectItemLabel(child.props.children, selectedValue); ++ }); ++ return label; ++} + function Select({ + value, + defaultValue, +@@ -6641,7 +6658,7 @@ function Select({ + ), + children: [ + /* @__PURE__ */ jsx(Select$1.Value, { placeholder, children: (selected) => { +- const defaultChildren = selected ?? placeholder; ++ const defaultChildren = selected == null ? placeholder : getSelectItemLabel(collection, selected) ?? selected; + return typeof renderValue === "function" ? renderValue({ + defaultChildren, + isPlaceholder: selected == null diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9b584b9d..b61bf9059 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + '@umami/react-zen@0.247.0': 7a13e402d0a59d203f92fa4c64c8511aa38c5cd914092f5232c6271c606c9126 + importers: .: @@ -37,7 +40,7 @@ importers: version: 5.101.4(react@19.2.8) '@umami/react-zen': specifier: ^0.247.0 - version: 0.247.0(@date-fns/tz@1.5.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(date-fns@4.4.0)(immer@11.1.15)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.8)) + version: 0.247.0(patch_hash=7a13e402d0a59d203f92fa4c64c8511aa38c5cd914092f5232c6271c606c9126)(@date-fns/tz@1.5.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(date-fns@4.4.0)(immer@11.1.15)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.8)) bcryptjs: specifier: ^3.0.2 version: 3.0.3 @@ -8265,7 +8268,7 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@umami/react-zen@0.247.0(@date-fns/tz@1.5.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(date-fns@4.4.0)(immer@11.1.15)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.8))': + '@umami/react-zen@0.247.0(patch_hash=7a13e402d0a59d203f92fa4c64c8511aa38c5cd914092f5232c6271c606c9126)(@date-fns/tz@1.5.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(date-fns@4.4.0)(immer@11.1.15)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.1.18)(use-sync-external-store@1.6.0(react@19.2.8))': dependencies: '@base-ui/react': 1.6.0(@date-fns/tz@1.5.0)(@types/react@19.2.17)(date-fns@4.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@react-spring/web': 9.7.5(react-dom@19.2.8(react@19.2.8))(react@19.2.8) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ec9aa42bb..e4eb84858 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,3 +12,5 @@ allowBuilds: sharp: false minimumReleaseAgeExclude: - '@umami/react-zen@0.247.0' +patchedDependencies: + '@umami/react-zen@0.247.0': patches/@umami__react-zen@0.247.0.patch diff --git a/src/app/(main)/teams/TeamMemberAddForm.tsx b/src/app/(main)/teams/TeamMemberAddForm.tsx index 4ab314752..7345d2626 100644 --- a/src/app/(main)/teams/TeamMemberAddForm.tsx +++ b/src/app/(main)/teams/TeamMemberAddForm.tsx @@ -51,7 +51,7 @@ export function TeamMemberAddForm({ - {roles.map(value => ( {renderRole(value)} diff --git a/src/app/(main)/websites/[websiteId]/(reports)/attribution/AttributionPage.tsx b/src/app/(main)/websites/[websiteId]/(reports)/attribution/AttributionPage.tsx index 0917759a7..fb61610d7 100644 --- a/src/app/(main)/websites/[websiteId]/(reports)/attribution/AttributionPage.tsx +++ b/src/app/(main)/websites/[websiteId]/(reports)/attribution/AttributionPage.tsx @@ -3,7 +3,7 @@ import { Column, Grid, Label, ListItem, Loading, Select } from '@umami/react-zen import { useState } from 'react'; import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; import { Empty } from '@/components/common/Empty'; -import { MultiSelect } from '@/components/common/MultiSelect'; +import { MultiSelect, MultiSelectItem } from '@/components/common/MultiSelect'; import { useDateRange, useMessages, useWebsiteValuesQuery } from '@/components/hooks'; import { Attribution } from './Attribution'; @@ -72,9 +72,9 @@ export function AttributionPage({ websiteId }: { websiteId: string }) { allowSearch > {items.map(({ value }) => ( - + {value} - + ))} diff --git a/src/components/common/FilterRecord.tsx b/src/components/common/FilterRecord.tsx index 251d29380..d9b52f4bd 100644 --- a/src/components/common/FilterRecord.tsx +++ b/src/components/common/FilterRecord.tsx @@ -11,7 +11,7 @@ import { } from '@umami/react-zen'; import { useState } from 'react'; import { Empty } from '@/components/common/Empty'; -import { MultiSelect } from '@/components/common/MultiSelect'; +import { MultiSelect, MultiSelectItem } from '@/components/common/MultiSelect'; import { useFilters, useFormat, useWebsiteValuesQuery } from '@/components/hooks'; import { X } from '@/components/icons'; import { isSearchOperator } from '@/lib/params'; @@ -108,9 +108,9 @@ export function FilterRecord({ allowSearch > {items.map(({ value }) => ( - + {formatValue(value, type)} - + ))} )} diff --git a/src/components/common/LinkButton.tsx b/src/components/common/LinkButton.tsx index 8486d6cf6..d5824b43f 100644 --- a/src/components/common/LinkButton.tsx +++ b/src/components/common/LinkButton.tsx @@ -35,5 +35,5 @@ export function LinkButton({ ); - return - - - {allowSearch && } - - {children} - - - + + onChange?.(keys === 'all' ? [] : Array.from(keys, key => String(key))) + } + className="flex flex-col gap-2" + style={{ + width: 'var(--anchor-width)', + maxWidth: 'var(--available-width)', + }} + > + {allowSearch && ( + { + if (event.key !== 'Escape' && event.key !== 'Tab') { + event.stopPropagation(); + } + }} + /> + )} +
+ {children} + {Children.count(children) === 0 && renderEmptyState?.()} +
+
); } diff --git a/src/components/metrics/MetricsExpandedTable.tsx b/src/components/metrics/MetricsExpandedTable.tsx index c4c49e75a..96facd6d8 100644 --- a/src/components/metrics/MetricsExpandedTable.tsx +++ b/src/components/metrics/MetricsExpandedTable.tsx @@ -48,7 +48,14 @@ export function MetricsExpandedTable({ return ( <> - {allowSearch && } + {allowSearch && ( + + )} {children} {allowDownload && } diff --git a/src/components/property-data/PropertyFilterRecord.tsx b/src/components/property-data/PropertyFilterRecord.tsx index 1631ed7ab..2dd387eb4 100644 --- a/src/components/property-data/PropertyFilterRecord.tsx +++ b/src/components/property-data/PropertyFilterRecord.tsx @@ -20,7 +20,7 @@ import { useState } from 'react'; import { ComboBox } from '@/components/common/ComboBox'; import { DateDisplay } from '@/components/common/DateDisplay'; import { Empty } from '@/components/common/Empty'; -import { MultiSelect } from '@/components/common/MultiSelect'; +import { MultiSelect, MultiSelectItem } from '@/components/common/MultiSelect'; import { useMessages, usePropertyValuesQuery } from '@/components/hooks'; import type { PropertyDataSource } from '@/components/hooks/queries/usePropertyFieldsQuery'; import { X } from '@/components/icons'; @@ -222,9 +222,9 @@ export function PropertyFilterRecord({ } > {filteredValues.map(v => ( - + {v} - + ))} )}