From bd30784b1d29818a738291cfa0dadbf2c2ab1e27 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:48:29 +0800 Subject: [PATCH] chore(web): upgrade @base-ui/react to v1.4.0 (#35048) --- pnpm-lock.yaml | 47 ++++++++++++++----- pnpm-workspace.yaml | 4 +- .../base/ui/select/__tests__/index.spec.tsx | 27 +++++++++++ .../base/ui/slider/__tests__/index.spec.tsx | 17 +++++++ .../base/ui/toast/__tests__/index.spec.tsx | 26 ++++++++++ web/package.json | 2 + 6 files changed, 111 insertions(+), 12 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7985dac7c..7a471c6a64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,14 +16,17 @@ catalogs: specifier: 8.1.1 version: 8.1.1 '@base-ui/react': - specifier: 1.3.0 - version: 1.3.0 + specifier: 1.4.0 + version: 1.4.0 '@chromatic-com/storybook': specifier: 5.1.1 version: 5.1.1 '@cucumber/cucumber': specifier: 12.7.0 version: 12.7.0 + '@date-fns/tz': + specifier: 1.2.0 + version: 1.2.0 '@egoist/tailwindcss-icons': specifier: 1.9.2 version: 1.9.2 @@ -267,6 +270,9 @@ catalogs: cron-parser: specifier: 5.5.0 version: 5.5.0 + date-fns: + specifier: 4.0.0 + version: 4.0.0 dayjs: specifier: 1.11.20 version: 1.11.20 @@ -648,7 +654,10 @@ importers: version: 1.27.6(@amplitude/rrweb@2.0.0-alpha.37)(rollup@4.59.0) '@base-ui/react': specifier: 'catalog:' - version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.4.0(@date-fns/tz@1.2.0)(@types/react@19.2.14)(date-fns@4.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@date-fns/tz': + specifier: 'catalog:' + version: 1.2.0 '@emoji-mart/data': specifier: 'catalog:' version: 1.2.1 @@ -751,6 +760,9 @@ importers: cron-parser: specifier: 'catalog:' version: 5.5.0 + date-fns: + specifier: 'catalog:' + version: 4.0.0 dayjs: specifier: 'catalog:' version: 1.11.20 @@ -1391,19 +1403,21 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@base-ui/react@1.3.0': - resolution: {integrity: sha512-FwpKqZbPz14AITp1CVgf4AjhKPe1OeeVKSBMdgD10zbFlj3QSWelmtCMLi2+/PFZZcIm3l87G7rwtCZJwHyXWA==} + '@base-ui/react@1.4.0': + resolution: {integrity: sha512-QcqdVbr/+ba2/RAKJIV1PV6S02Q5+r6a4Eym8ndBw+ZbBILkkmQAyRxXCg/pArrHnkrGeU8goe26aw0h6eE8pg==} engines: {node: '>=14.0.0'} peerDependencies: + '@date-fns/tz': ^1.2.0 '@types/react': ^17 || ^18 || ^19 + date-fns: ^4.0.0 react: ^17 || ^18 || ^19 react-dom: ^17 || ^18 || ^19 peerDependenciesMeta: '@types/react': optional: true - '@base-ui/utils@0.2.6': - resolution: {integrity: sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw==} + '@base-ui/utils@0.2.7': + resolution: {integrity: sha512-nXYKhiL/0JafyJE8PfcflipGftOftlIwKd72rU15iZ1M5yqgg5J9P8NHU71GReDuXco5MJA/eVQqUT5WRqX9sA==} peerDependencies: '@types/react': ^17 || ^18 || ^19 react: ^17 || ^18 || ^19 @@ -1532,6 +1546,9 @@ packages: '@cucumber/tag-expressions@9.1.0': resolution: {integrity: sha512-bvHjcRFZ+J1TqIa9eFNO1wGHqwx4V9ZKV3hYgkuK/VahHx73uiP4rKV3JVrvWSMrwrFvJG6C8aEwnCWSvbyFdQ==} + '@date-fns/tz@1.2.0': + resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} + '@e18e/eslint-plugin@0.3.0': resolution: {integrity: sha512-hHgfpxsrZ2UYHcicA+tGZnmk19uJTaye9VH79O+XS8R4ona2Hx3xjhXghclNW58uXMk3xXlbYEOMr8thsoBmWg==} peerDependencies: @@ -5325,6 +5342,9 @@ packages: dagre-d3-es@7.0.14: resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + date-fns@4.0.0: + resolution: {integrity: sha512-6K33+I8fQ5otvHgLIvKK1xmMbLAh0pduyrx7dwMXKiGYeoWhmk6M3Zoak9n7bXHMJQlHq1yqmdGy1QxKddJjUA==} + dayjs@1.11.20: resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} @@ -8896,20 +8916,21 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@base-ui/react@1.3.0(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@base-ui/react@1.4.0(@date-fns/tz@1.2.0)(@types/react@19.2.14)(date-fns@4.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.6(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@base-ui/utils': 0.2.7(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@date-fns/tz': 1.2.0 '@floating-ui/react-dom': 2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@floating-ui/utils': 0.2.11 + date-fns: 4.0.0 react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - tabbable: 6.4.0 use-sync-external-store: 1.6.0(react@19.2.5) optionalDependencies: '@types/react': 19.2.14 - '@base-ui/utils@0.2.6(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@base-ui/utils@0.2.7(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@floating-ui/utils': 0.2.11 @@ -9128,6 +9149,8 @@ snapshots: '@cucumber/tag-expressions@9.1.0': {} + '@date-fns/tz@1.2.0': {} + '@e18e/eslint-plugin@0.3.0(eslint@10.2.0(jiti@2.6.1))(oxlint@1.58.0(oxlint-tsgolint@0.20.0))': dependencies: eslint-plugin-depend: 1.5.0(eslint@10.2.0(jiti@2.6.1)) @@ -12793,6 +12816,8 @@ snapshots: d3: 7.9.0 lodash-es: 4.18.0 + date-fns@4.0.0: {} + dayjs@1.11.20: {} debug@4.4.3(supports-color@8.1.1): diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d0f9c4e1be..1e33db0001 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -48,7 +48,8 @@ catalog: "@amplitude/analytics-browser": 2.38.1 "@amplitude/plugin-session-replay-browser": 1.27.6 "@antfu/eslint-config": 8.1.1 - "@base-ui/react": 1.3.0 + "@base-ui/react": 1.4.0 + "@date-fns/tz": 1.2.0 "@chromatic-com/storybook": 5.1.1 "@cucumber/cucumber": 12.7.0 "@egoist/tailwindcss-icons": 1.9.2 @@ -135,6 +136,7 @@ catalog: code-inspector-plugin: 1.5.1 copy-to-clipboard: 3.3.3 cron-parser: 5.5.0 + date-fns: 4.0.0 dayjs: 1.11.20 decimal.js: 10.6.0 dompurify: 3.3.3 diff --git a/web/app/components/base/ui/select/__tests__/index.spec.tsx b/web/app/components/base/ui/select/__tests__/index.spec.tsx index 7168342377..f33b1eb650 100644 --- a/web/app/components/base/ui/select/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/select/__tests__/index.spec.tsx @@ -41,6 +41,33 @@ const renderOpenSelect = ({ } describe('Select wrappers', () => { + describe('Select root integration', () => { + it('should associate the hidden input with an external form and preserve autocomplete hints', () => { + const formId = 'profile-form' + const { container } = render( + <> +
+ + , + ) + + const hiddenInput = container.querySelector('input[name="city"]') + const form = container.querySelector(`#${formId}`) as HTMLFormElement + + expect(hiddenInput).toHaveAttribute('form', formId) + expect(hiddenInput).toHaveAttribute('autocomplete', 'address-level2') + expect(new FormData(form).get('city')).toBe('seattle') + }) + }) + describe('SelectTrigger', () => { it('should render clear button when clearable is true and loading is false', () => { renderOpenSelect({ diff --git a/web/app/components/base/ui/slider/__tests__/index.spec.tsx b/web/app/components/base/ui/slider/__tests__/index.spec.tsx index 4026b7f13b..ab58df844f 100644 --- a/web/app/components/base/ui/slider/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/slider/__tests__/index.spec.tsx @@ -47,6 +47,23 @@ describe('Slider', () => { expect(onValueChange).toHaveBeenLastCalledWith(21, expect.anything()) }) + it('should round floating point keyboard updates to the configured step', async () => { + const user = userEvent.setup() + const onValueChange = vi.fn() + + render() + + const slider = getSliderInput() + + await act(async () => { + slider.focus() + await user.keyboard('{ArrowRight}') + }) + + expect(onValueChange).toHaveBeenCalledTimes(1) + expect(onValueChange).toHaveBeenLastCalledWith(0.3, expect.anything()) + }) + it('should not trigger onValueChange when disabled', async () => { const user = userEvent.setup() const onValueChange = vi.fn() diff --git a/web/app/components/base/ui/toast/__tests__/index.spec.tsx b/web/app/components/base/ui/toast/__tests__/index.spec.tsx index 1c0eb84f9e..d7b1d86077 100644 --- a/web/app/components/base/ui/toast/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/toast/__tests__/index.spec.tsx @@ -251,6 +251,32 @@ describe('base/ui/toast', () => { expect(screen.queryByText('Loading')).not.toBeInTheDocument() }) + // Re-adding the same toast id should upsert in place instead of stacking duplicates. + it('should upsert an existing toast when add is called with the same id', async () => { + render() + + act(() => { + toast('Syncing', { + id: 'sync-job', + description: 'Uploading changes…', + }) + }) + + expect(await screen.findByText('Syncing')).toBeInTheDocument() + + act(() => { + toast.success('Synced', { + id: 'sync-job', + description: 'All changes are uploaded.', + }) + }) + + expect(screen.queryByText('Syncing')).not.toBeInTheDocument() + expect(screen.getByText('Synced')).toBeInTheDocument() + expect(screen.getByText('All changes are uploaded.')).toBeInTheDocument() + expect(screen.getAllByRole('dialog')).toHaveLength(1) + }) + // Action props should pass through to the Base UI action button. it('should render and invoke toast action props', async () => { const onAction = vi.fn() diff --git a/web/package.json b/web/package.json index 8bc31dce31..3ce16d8fb0 100644 --- a/web/package.json +++ b/web/package.json @@ -56,6 +56,7 @@ "@amplitude/analytics-browser": "catalog:", "@amplitude/plugin-session-replay-browser": "catalog:", "@base-ui/react": "catalog:", + "@date-fns/tz": "catalog:", "@emoji-mart/data": "catalog:", "@floating-ui/react": "catalog:", "@formatjs/intl-localematcher": "catalog:", @@ -90,6 +91,7 @@ "cmdk": "catalog:", "copy-to-clipboard": "catalog:", "cron-parser": "catalog:", + "date-fns": "catalog:", "dayjs": "catalog:", "decimal.js": "catalog:", "dompurify": "catalog:",