Upgrade react-zen and migrate attribution and menu controls to updated components

This commit is contained in:
Mike Cao committed 2026-07-31 22:14:31 -07:00
1 parent cfe8020831
commit df302595c0
7 files changed
+48 -130

No files matched your search

+1 -1
View File
@@ -65,7 +65,7 @@
"@prisma/extension-read-replicas": "^0.5.0",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.101.4",
"@umami/react-zen": "^0.247.0",
"@umami/react-zen": "^0.248.0",
"bcryptjs": "^3.0.2",
"chalk": "^5.6.2",
"chart.js": "^4.5.1",
-74
View File
@@ -1,74 +0,0 @@
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
+5 -8
View File
@@ -4,9 +4,6 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
patchedDependencies:
'@umami/react-zen@0.247.0': 7a13e402d0a59d203f92fa4c64c8511aa38c5cd914092f5232c6271c606c9126
importers:
.:
@@ -39,8 +36,8 @@ importers:
specifier: ^5.101.4
version: 5.101.4(react@19.2.8)
'@umami/react-zen':
specifier: ^0.247.0
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))
specifier: ^0.248.0
version: 0.248.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))
bcryptjs:
specifier: ^3.0.2
version: 3.0.3
@@ -2930,8 +2927,8 @@ packages:
'@types/yargs@17.0.35':
resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
'@umami/react-zen@0.247.0':
resolution: {integrity: sha512-3I8kCsSX3DEDX2D0ft+2g9fUdmXm5+BWR6mMkbOxYy9bColbL5gTF7UOcl+wuN4ETOh7bo9Lxk5CM6hDyZX6uA==}
'@umami/react-zen@0.248.0':
resolution: {integrity: sha512-sCEA+nVko+7cETmnmF+HvKyP54Bn/9S0GPexyA7QCDoYITu5X4FS2TT422kKM2UX6eVmuiq8slMdeZfAlUEynw==}
peerDependencies:
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
@@ -8268,7 +8265,7 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
'@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))':
'@umami/react-zen@0.248.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))':
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)
+1 -3
View File
@@ -11,6 +11,4 @@ allowBuilds:
prisma: true
sharp: false
minimumReleaseAgeExclude:
- '@umami/react-zen@0.247.0'
patchedDependencies:
'@umami/react-zen@0.247.0': patches/@umami__react-zen@0.247.0.patch
- '@umami/react-zen'
@@ -1,12 +1,13 @@
'use client';
import { Column, Grid, Label, ListItem, Loading, Select } from '@umami/react-zen';
import { Column, Grid, ListItem, 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, MultiSelectItem } from '@/components/common/MultiSelect';
import { useDateRange, useMessages, useWebsiteValuesQuery } from '@/components/hooks';
import { Attribution } from './Attribution';
const stepPlaceholder = 'Select an item';
export function AttributionPage({ websiteId }: { websiteId: string }) {
const [model, setModel] = useState('first-click');
const [type, setType] = useState('path');
@@ -31,10 +32,6 @@ export function AttributionPage({ websiteId }: { websiteId: string }) {
setSearch('');
};
const handleStepChange = (values: string[]) => {
setStep(values.filter(value => value !== step).pop() ?? '');
};
return (
<Column gap="6">
<WebsiteControls websiteId={websiteId} />
@@ -62,21 +59,24 @@ export function AttributionPage({ websiteId }: { websiteId: string }) {
</Select>
</Column>
<Column>
<Label>{t(labels.conversionStep)}</Label>
<MultiSelect
value={step ? [step] : []}
onChange={handleStepChange}
<Select
label={t(labels.conversionStep)}
value={step}
onChange={value => setStep(value?.toString() ?? '')}
placeholder={stepPlaceholder}
renderValue={({ defaultChildren }) => (step ? defaultChildren : stepPlaceholder)}
isLoading={isLoading}
searchValue={search}
onSearch={setSearch}
renderEmptyState={() => (isLoading ? <Loading icon="dots" /> : <Empty />)}
maxHeight={320}
allowSearch
>
{items.map(({ value }) => (
<MultiSelectItem key={value} value={value}>
<ListItem key={value} id={value}>
{value}
</MultiSelectItem>
</ListItem>
))}
</MultiSelect>
</Select>
</Column>
</Grid>
{step ? (
+1 -1
View File
@@ -5,7 +5,7 @@ import type { ReactElement } from 'react';
export function ControlledDialog({ children }: { children: ReactElement }) {
return (
<DialogTrigger>
<span hidden />
<button type="button" hidden />
{children}
</DialogTrigger>
);
+26 -29
View File
@@ -1,6 +1,5 @@
import {
Button,
Column,
Icon,
Menu,
MenuItem,
@@ -72,36 +71,34 @@ export function TeamsButton() {
</Icon>
</Button>
<Popover side="bottom" align="start">
<Column minWidth="300px">
<Menu selectionMode="single" selectedKeys={selectedKeys}>
<MenuSection title={t(labels.myAccount)}>
<MenuItem id="user" onAction={handleAction}>
<IconLabel icon={<User />} label={user.username} />
<Menu className="min-w-[300px]" selectionMode="single" selectedKeys={selectedKeys}>
<MenuSection title={t(labels.myAccount)}>
<MenuItem id="user" onAction={handleAction}>
<IconLabel icon={<User />} label={user.username} />
</MenuItem>
</MenuSection>
<MenuSeparator />
<MenuSection title={t(labels.teams)}>
{user?.teams?.map(({ id, name }) => (
<MenuItem key={id} id={id} onAction={() => handleNavigate(getUrl(`/teams/${id}`))}>
<IconLabel icon={<Users />}>
<Text wrap="nowrap">{name}</Text>
</IconLabel>
</MenuItem>
</MenuSection>
))}
<MenuSeparator />
<MenuSection title={t(labels.teams)}>
{user?.teams?.map(({ id, name }) => (
<MenuItem key={id} id={id} onAction={() => handleNavigate(getUrl(`/teams/${id}`))}>
<IconLabel icon={<Users />}>
<Text wrap="nowrap">{name}</Text>
</IconLabel>
</MenuItem>
))}
<MenuSeparator />
<MenuItem id="manage-teams">
<a href="/settings/teams" style={{ width: '100%' }}>
<Row alignItems="center" justifyContent="space-between" gap>
<Text align="center">Manage teams</Text>
<Icon>
<ArrowRight />
</Icon>
</Row>
</a>
</MenuItem>
</MenuSection>
</Menu>
</Column>
<MenuItem id="manage-teams">
<a href="/settings/teams" style={{ width: '100%' }}>
<Row alignItems="center" justifyContent="space-between" gap>
<Text align="center">Manage teams</Text>
<Icon>
<ArrowRight />
</Icon>
</Row>
</a>
</MenuItem>
</MenuSection>
</Menu>
</Popover>
</MenuTrigger>
);