From 49d2db403169af6f7fb7ba5e2d25b4a3fe42fdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Tue, 6 Jan 2026 01:11:03 +0900 Subject: [PATCH 01/26] refactor: rename packages to src for better structure --- .storybook/main.ts | 4 ++-- {packages => src}/components/Badge/Badge.css.ts | 0 {packages => src}/components/Badge/Badge.stories.tsx | 0 {packages => src}/components/Badge/Badge.tsx | 0 {packages => src}/components/Badge/index.ts | 0 {packages => src}/components/Button/Button.css.ts | 0 {packages => src}/components/Button/Button.stories.tsx | 0 {packages => src}/components/Button/Button.tsx | 0 {packages => src}/components/Button/index.ts | 0 {packages => src}/components/Checkbox/Checkbox.css.ts | 0 {packages => src}/components/Checkbox/Checkbox.stories.tsx | 0 {packages => src}/components/Checkbox/Checkbox.tsx | 0 {packages => src}/components/Checkbox/index.ts | 0 {packages => src}/components/Dialog/Dialog.css.ts | 0 {packages => src}/components/Dialog/Dialog.stories.tsx | 0 {packages => src}/components/Dialog/Dialog.tsx | 0 {packages => src}/components/Dialog/index.ts | 0 {packages => src}/components/Dropdown/Dropdown.css.ts | 0 {packages => src}/components/Dropdown/Dropdown.stories.tsx | 0 {packages => src}/components/Dropdown/Dropdown.tsx | 0 {packages => src}/components/Dropdown/index.ts | 0 {packages => src}/components/Switch/Switch.css.ts | 0 {packages => src}/components/Switch/Switch.stories.tsx | 0 {packages => src}/components/Switch/Switch.tsx | 0 {packages => src}/components/Switch/index.ts | 0 {packages => src}/components/Text/Text.css.ts | 0 {packages => src}/components/Text/Text.stories.tsx | 0 {packages => src}/components/Text/Text.tsx | 0 {packages => src}/components/Text/index.ts | 0 {packages => src}/components/TextField/TextField.css.ts | 0 {packages => src}/components/TextField/TextField.stories.tsx | 0 {packages => src}/components/TextField/TextField.tsx | 0 {packages => src}/components/TextField/index.ts | 0 {packages => src}/components/index.ts | 0 {packages => src}/providers/ThemeContext.tsx | 0 {packages => src}/providers/ThemeProvider.tsx | 0 {packages => src}/providers/index.ts | 0 {packages => src}/providers/useTheme.ts | 0 {packages => src}/tokens/appearanceTheme.ts | 0 {packages => src}/tokens/colors.ts | 0 {packages => src}/tokens/index.ts | 0 {packages => src}/tokens/radius.ts | 0 {packages => src}/tokens/shadow.ts | 0 {packages => src}/tokens/spacing.ts | 0 {packages => src}/tokens/theme.css.ts | 0 {packages => src}/tokens/typography.ts | 0 {packages => src}/tokens/utils/applyPrimaryColor.ts | 0 {packages => src}/tokens/utils/constants.ts | 0 {packages => src}/tokens/utils/generateColorScale.ts | 0 {packages => src}/tokens/utils/getWeakColor.ts | 0 {packages => src}/tokens/utils/index.ts | 0 tsconfig.app.json | 4 ++-- tsconfig.build.json | 2 +- vite.config.ts | 4 ++-- 54 files changed, 7 insertions(+), 7 deletions(-) rename {packages => src}/components/Badge/Badge.css.ts (100%) rename {packages => src}/components/Badge/Badge.stories.tsx (100%) rename {packages => src}/components/Badge/Badge.tsx (100%) rename {packages => src}/components/Badge/index.ts (100%) rename {packages => src}/components/Button/Button.css.ts (100%) rename {packages => src}/components/Button/Button.stories.tsx (100%) rename {packages => src}/components/Button/Button.tsx (100%) rename {packages => src}/components/Button/index.ts (100%) rename {packages => src}/components/Checkbox/Checkbox.css.ts (100%) rename {packages => src}/components/Checkbox/Checkbox.stories.tsx (100%) rename {packages => src}/components/Checkbox/Checkbox.tsx (100%) rename {packages => src}/components/Checkbox/index.ts (100%) rename {packages => src}/components/Dialog/Dialog.css.ts (100%) rename {packages => src}/components/Dialog/Dialog.stories.tsx (100%) rename {packages => src}/components/Dialog/Dialog.tsx (100%) rename {packages => src}/components/Dialog/index.ts (100%) rename {packages => src}/components/Dropdown/Dropdown.css.ts (100%) rename {packages => src}/components/Dropdown/Dropdown.stories.tsx (100%) rename {packages => src}/components/Dropdown/Dropdown.tsx (100%) rename {packages => src}/components/Dropdown/index.ts (100%) rename {packages => src}/components/Switch/Switch.css.ts (100%) rename {packages => src}/components/Switch/Switch.stories.tsx (100%) rename {packages => src}/components/Switch/Switch.tsx (100%) rename {packages => src}/components/Switch/index.ts (100%) rename {packages => src}/components/Text/Text.css.ts (100%) rename {packages => src}/components/Text/Text.stories.tsx (100%) rename {packages => src}/components/Text/Text.tsx (100%) rename {packages => src}/components/Text/index.ts (100%) rename {packages => src}/components/TextField/TextField.css.ts (100%) rename {packages => src}/components/TextField/TextField.stories.tsx (100%) rename {packages => src}/components/TextField/TextField.tsx (100%) rename {packages => src}/components/TextField/index.ts (100%) rename {packages => src}/components/index.ts (100%) rename {packages => src}/providers/ThemeContext.tsx (100%) rename {packages => src}/providers/ThemeProvider.tsx (100%) rename {packages => src}/providers/index.ts (100%) rename {packages => src}/providers/useTheme.ts (100%) rename {packages => src}/tokens/appearanceTheme.ts (100%) rename {packages => src}/tokens/colors.ts (100%) rename {packages => src}/tokens/index.ts (100%) rename {packages => src}/tokens/radius.ts (100%) rename {packages => src}/tokens/shadow.ts (100%) rename {packages => src}/tokens/spacing.ts (100%) rename {packages => src}/tokens/theme.css.ts (100%) rename {packages => src}/tokens/typography.ts (100%) rename {packages => src}/tokens/utils/applyPrimaryColor.ts (100%) rename {packages => src}/tokens/utils/constants.ts (100%) rename {packages => src}/tokens/utils/generateColorScale.ts (100%) rename {packages => src}/tokens/utils/getWeakColor.ts (100%) rename {packages => src}/tokens/utils/index.ts (100%) diff --git a/.storybook/main.ts b/.storybook/main.ts index 3e2119c..ab6c59e 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -3,8 +3,8 @@ import type { StorybookConfig } from "@storybook/react-vite"; const config: StorybookConfig = { stories: [ "../.storybook/**/*.mdx", - "../packages/**/*.mdx", - "../packages/**/*.stories.@(js|jsx|mjs|ts|tsx)", + "../src/**/*.mdx", + "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)", ], addons: [ "@storybook/addon-essentials", diff --git a/packages/components/Badge/Badge.css.ts b/src/components/Badge/Badge.css.ts similarity index 100% rename from packages/components/Badge/Badge.css.ts rename to src/components/Badge/Badge.css.ts diff --git a/packages/components/Badge/Badge.stories.tsx b/src/components/Badge/Badge.stories.tsx similarity index 100% rename from packages/components/Badge/Badge.stories.tsx rename to src/components/Badge/Badge.stories.tsx diff --git a/packages/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx similarity index 100% rename from packages/components/Badge/Badge.tsx rename to src/components/Badge/Badge.tsx diff --git a/packages/components/Badge/index.ts b/src/components/Badge/index.ts similarity index 100% rename from packages/components/Badge/index.ts rename to src/components/Badge/index.ts diff --git a/packages/components/Button/Button.css.ts b/src/components/Button/Button.css.ts similarity index 100% rename from packages/components/Button/Button.css.ts rename to src/components/Button/Button.css.ts diff --git a/packages/components/Button/Button.stories.tsx b/src/components/Button/Button.stories.tsx similarity index 100% rename from packages/components/Button/Button.stories.tsx rename to src/components/Button/Button.stories.tsx diff --git a/packages/components/Button/Button.tsx b/src/components/Button/Button.tsx similarity index 100% rename from packages/components/Button/Button.tsx rename to src/components/Button/Button.tsx diff --git a/packages/components/Button/index.ts b/src/components/Button/index.ts similarity index 100% rename from packages/components/Button/index.ts rename to src/components/Button/index.ts diff --git a/packages/components/Checkbox/Checkbox.css.ts b/src/components/Checkbox/Checkbox.css.ts similarity index 100% rename from packages/components/Checkbox/Checkbox.css.ts rename to src/components/Checkbox/Checkbox.css.ts diff --git a/packages/components/Checkbox/Checkbox.stories.tsx b/src/components/Checkbox/Checkbox.stories.tsx similarity index 100% rename from packages/components/Checkbox/Checkbox.stories.tsx rename to src/components/Checkbox/Checkbox.stories.tsx diff --git a/packages/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx similarity index 100% rename from packages/components/Checkbox/Checkbox.tsx rename to src/components/Checkbox/Checkbox.tsx diff --git a/packages/components/Checkbox/index.ts b/src/components/Checkbox/index.ts similarity index 100% rename from packages/components/Checkbox/index.ts rename to src/components/Checkbox/index.ts diff --git a/packages/components/Dialog/Dialog.css.ts b/src/components/Dialog/Dialog.css.ts similarity index 100% rename from packages/components/Dialog/Dialog.css.ts rename to src/components/Dialog/Dialog.css.ts diff --git a/packages/components/Dialog/Dialog.stories.tsx b/src/components/Dialog/Dialog.stories.tsx similarity index 100% rename from packages/components/Dialog/Dialog.stories.tsx rename to src/components/Dialog/Dialog.stories.tsx diff --git a/packages/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx similarity index 100% rename from packages/components/Dialog/Dialog.tsx rename to src/components/Dialog/Dialog.tsx diff --git a/packages/components/Dialog/index.ts b/src/components/Dialog/index.ts similarity index 100% rename from packages/components/Dialog/index.ts rename to src/components/Dialog/index.ts diff --git a/packages/components/Dropdown/Dropdown.css.ts b/src/components/Dropdown/Dropdown.css.ts similarity index 100% rename from packages/components/Dropdown/Dropdown.css.ts rename to src/components/Dropdown/Dropdown.css.ts diff --git a/packages/components/Dropdown/Dropdown.stories.tsx b/src/components/Dropdown/Dropdown.stories.tsx similarity index 100% rename from packages/components/Dropdown/Dropdown.stories.tsx rename to src/components/Dropdown/Dropdown.stories.tsx diff --git a/packages/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx similarity index 100% rename from packages/components/Dropdown/Dropdown.tsx rename to src/components/Dropdown/Dropdown.tsx diff --git a/packages/components/Dropdown/index.ts b/src/components/Dropdown/index.ts similarity index 100% rename from packages/components/Dropdown/index.ts rename to src/components/Dropdown/index.ts diff --git a/packages/components/Switch/Switch.css.ts b/src/components/Switch/Switch.css.ts similarity index 100% rename from packages/components/Switch/Switch.css.ts rename to src/components/Switch/Switch.css.ts diff --git a/packages/components/Switch/Switch.stories.tsx b/src/components/Switch/Switch.stories.tsx similarity index 100% rename from packages/components/Switch/Switch.stories.tsx rename to src/components/Switch/Switch.stories.tsx diff --git a/packages/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx similarity index 100% rename from packages/components/Switch/Switch.tsx rename to src/components/Switch/Switch.tsx diff --git a/packages/components/Switch/index.ts b/src/components/Switch/index.ts similarity index 100% rename from packages/components/Switch/index.ts rename to src/components/Switch/index.ts diff --git a/packages/components/Text/Text.css.ts b/src/components/Text/Text.css.ts similarity index 100% rename from packages/components/Text/Text.css.ts rename to src/components/Text/Text.css.ts diff --git a/packages/components/Text/Text.stories.tsx b/src/components/Text/Text.stories.tsx similarity index 100% rename from packages/components/Text/Text.stories.tsx rename to src/components/Text/Text.stories.tsx diff --git a/packages/components/Text/Text.tsx b/src/components/Text/Text.tsx similarity index 100% rename from packages/components/Text/Text.tsx rename to src/components/Text/Text.tsx diff --git a/packages/components/Text/index.ts b/src/components/Text/index.ts similarity index 100% rename from packages/components/Text/index.ts rename to src/components/Text/index.ts diff --git a/packages/components/TextField/TextField.css.ts b/src/components/TextField/TextField.css.ts similarity index 100% rename from packages/components/TextField/TextField.css.ts rename to src/components/TextField/TextField.css.ts diff --git a/packages/components/TextField/TextField.stories.tsx b/src/components/TextField/TextField.stories.tsx similarity index 100% rename from packages/components/TextField/TextField.stories.tsx rename to src/components/TextField/TextField.stories.tsx diff --git a/packages/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx similarity index 100% rename from packages/components/TextField/TextField.tsx rename to src/components/TextField/TextField.tsx diff --git a/packages/components/TextField/index.ts b/src/components/TextField/index.ts similarity index 100% rename from packages/components/TextField/index.ts rename to src/components/TextField/index.ts diff --git a/packages/components/index.ts b/src/components/index.ts similarity index 100% rename from packages/components/index.ts rename to src/components/index.ts diff --git a/packages/providers/ThemeContext.tsx b/src/providers/ThemeContext.tsx similarity index 100% rename from packages/providers/ThemeContext.tsx rename to src/providers/ThemeContext.tsx diff --git a/packages/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx similarity index 100% rename from packages/providers/ThemeProvider.tsx rename to src/providers/ThemeProvider.tsx diff --git a/packages/providers/index.ts b/src/providers/index.ts similarity index 100% rename from packages/providers/index.ts rename to src/providers/index.ts diff --git a/packages/providers/useTheme.ts b/src/providers/useTheme.ts similarity index 100% rename from packages/providers/useTheme.ts rename to src/providers/useTheme.ts diff --git a/packages/tokens/appearanceTheme.ts b/src/tokens/appearanceTheme.ts similarity index 100% rename from packages/tokens/appearanceTheme.ts rename to src/tokens/appearanceTheme.ts diff --git a/packages/tokens/colors.ts b/src/tokens/colors.ts similarity index 100% rename from packages/tokens/colors.ts rename to src/tokens/colors.ts diff --git a/packages/tokens/index.ts b/src/tokens/index.ts similarity index 100% rename from packages/tokens/index.ts rename to src/tokens/index.ts diff --git a/packages/tokens/radius.ts b/src/tokens/radius.ts similarity index 100% rename from packages/tokens/radius.ts rename to src/tokens/radius.ts diff --git a/packages/tokens/shadow.ts b/src/tokens/shadow.ts similarity index 100% rename from packages/tokens/shadow.ts rename to src/tokens/shadow.ts diff --git a/packages/tokens/spacing.ts b/src/tokens/spacing.ts similarity index 100% rename from packages/tokens/spacing.ts rename to src/tokens/spacing.ts diff --git a/packages/tokens/theme.css.ts b/src/tokens/theme.css.ts similarity index 100% rename from packages/tokens/theme.css.ts rename to src/tokens/theme.css.ts diff --git a/packages/tokens/typography.ts b/src/tokens/typography.ts similarity index 100% rename from packages/tokens/typography.ts rename to src/tokens/typography.ts diff --git a/packages/tokens/utils/applyPrimaryColor.ts b/src/tokens/utils/applyPrimaryColor.ts similarity index 100% rename from packages/tokens/utils/applyPrimaryColor.ts rename to src/tokens/utils/applyPrimaryColor.ts diff --git a/packages/tokens/utils/constants.ts b/src/tokens/utils/constants.ts similarity index 100% rename from packages/tokens/utils/constants.ts rename to src/tokens/utils/constants.ts diff --git a/packages/tokens/utils/generateColorScale.ts b/src/tokens/utils/generateColorScale.ts similarity index 100% rename from packages/tokens/utils/generateColorScale.ts rename to src/tokens/utils/generateColorScale.ts diff --git a/packages/tokens/utils/getWeakColor.ts b/src/tokens/utils/getWeakColor.ts similarity index 100% rename from packages/tokens/utils/getWeakColor.ts rename to src/tokens/utils/getWeakColor.ts diff --git a/packages/tokens/utils/index.ts b/src/tokens/utils/index.ts similarity index 100% rename from packages/tokens/utils/index.ts rename to src/tokens/utils/index.ts diff --git a/tsconfig.app.json b/tsconfig.app.json index 54f5ade..555a7c7 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -27,8 +27,8 @@ /* Path Aliases */ "baseUrl": ".", "paths": { - "@/*": ["./packages/*"] + "@/*": ["./src/*"] } }, - "include": ["packages"] + "include": ["src"] } diff --git a/tsconfig.build.json b/tsconfig.build.json index 0587e93..eccb23e 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -6,7 +6,7 @@ "outDir": "./dist", "noEmit": false }, - "include": ["packages/**/*"], + "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.test.tsx", "src"] } diff --git a/vite.config.ts b/vite.config.ts index eb3d874..57c3bbc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,12 +8,12 @@ export default defineConfig({ plugins: [react(), vanillaExtractPlugin()], resolve: { alias: { - "@": resolve(__dirname, "./packages"), + "@": resolve(__dirname, "./src"), }, }, build: { lib: { - entry: resolve(__dirname, "packages/components/index.ts"), + entry: resolve(__dirname, "src/components/index.ts"), name: "PineUI", fileName: (format) => `pine-ui.${format}.js`, formats: ["es", "cjs"], From ced5f8f997a9ce0eae4a1dd40bfdb51414652d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Tue, 6 Jan 2026 01:13:43 +0900 Subject: [PATCH 02/26] feat: add @base-ui/react for headless component primitives --- package.json | 1 + pnpm-lock.yaml | 1511 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 1005 insertions(+), 507 deletions(-) diff --git a/package.json b/package.json index edbee9d..72b5636 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "release": "pnpm run version && pnpm run build:lib && npm publish --access public" }, "dependencies": { + "@base-ui/react": "^1.0.0", "clsx": "^2.1.1" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a11460e..0755bef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,97 +8,100 @@ importers: .: dependencies: + '@base-ui/react': + specifier: ^1.0.0 + version: 1.0.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) clsx: specifier: ^2.1.1 version: 2.1.1 devDependencies: '@changesets/cli': specifier: ^2.29.7 - version: 2.29.7(@types/node@24.10.1) + version: 2.29.8(@types/node@24.10.4) '@eslint/js': specifier: ^9.39.1 - version: 9.39.1 + version: 9.39.2 '@storybook/addon-a11y': specifier: ^8 - version: 8.6.14(storybook@8.6.14(prettier@3.6.2)) + version: 8.6.15(storybook@8.6.15(prettier@3.7.4)) '@storybook/addon-essentials': specifier: ^8 - version: 8.6.14(@types/react@19.2.6)(storybook@8.6.14(prettier@3.6.2)) + version: 8.6.14(@types/react@19.2.7)(storybook@8.6.15(prettier@3.7.4)) '@storybook/addon-interactions': specifier: ^8 - version: 8.6.14(storybook@8.6.14(prettier@3.6.2)) + version: 8.6.14(storybook@8.6.15(prettier@3.7.4)) '@storybook/addon-storysource': specifier: ^8.6.14 - version: 8.6.14(storybook@8.6.14(prettier@3.6.2)) + version: 8.6.14(storybook@8.6.15(prettier@3.7.4)) '@storybook/blocks': specifier: ^8 - version: 8.6.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2)) + version: 8.6.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)) '@storybook/react': specifier: ^8 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.6.2)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3) + version: 8.6.15(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.7.4)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3) '@storybook/react-vite': specifier: ^8 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.6.2)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.53.3)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.1)) + version: 8.6.15(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.7.4)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.55.1)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.4)) '@storybook/test': specifier: ^8 - version: 8.6.14(storybook@8.6.14(prettier@3.6.2)) + version: 8.6.15(storybook@8.6.15(prettier@3.7.4)) '@types/node': specifier: ^24.10.1 - version: 24.10.1 + version: 24.10.4 '@types/react': specifier: ^19.0.0 - version: 19.2.6 + version: 19.2.7 '@types/react-dom': specifier: ^19.0.0 - version: 19.2.3(@types/react@19.2.6) + version: 19.2.3(@types/react@19.2.7) '@vanilla-extract/css': specifier: ^1.17.4 - version: 1.17.4 + version: 1.18.0 '@vanilla-extract/recipes': specifier: ^0.5.7 - version: 0.5.7(@vanilla-extract/css@1.17.4) + version: 0.5.7(@vanilla-extract/css@1.18.0) '@vanilla-extract/vite-plugin': specifier: ^5.1.1 - version: 5.1.1(@types/node@24.10.1)(vite@5.4.21(@types/node@24.10.1)) + version: 5.1.4(@types/node@24.10.4)(vite@5.4.21(@types/node@24.10.4)) '@vitejs/plugin-react': specifier: ^5.1.0 - version: 5.1.1(vite@5.4.21(@types/node@24.10.1)) + version: 5.1.2(vite@5.4.21(@types/node@24.10.4)) eslint: specifier: ^9.39.1 - version: 9.39.1 + version: 9.39.2 eslint-plugin-react-hooks: specifier: ^5.2.0 - version: 5.2.0(eslint@9.39.1) + version: 5.2.0(eslint@9.39.2) eslint-plugin-react-refresh: specifier: ^0.4.24 - version: 0.4.24(eslint@9.39.1) + version: 0.4.26(eslint@9.39.2) eslint-plugin-storybook: specifier: ^10.0.7 - version: 10.0.8(eslint@9.39.1)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3) + version: 10.1.11(eslint@9.39.2)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3) globals: specifier: ^16.5.0 version: 16.5.0 react: specifier: ^19.0.0 - version: 19.2.0 + version: 19.2.3 react-dom: specifier: ^19.0.0 - version: 19.2.0(react@19.2.0) + version: 19.2.3(react@19.2.3) storybook: specifier: ^8 - version: 8.6.14(prettier@3.6.2) + version: 8.6.15(prettier@3.7.4) storybook-dark-mode: specifier: ^4.0.2 - version: 4.0.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2)) + version: 4.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)) typescript: specifier: ~5.9.3 version: 5.9.3 typescript-eslint: specifier: ^8.46.3 - version: 8.47.0(eslint@9.39.1)(typescript@5.9.3) + version: 8.51.0(eslint@9.39.2)(typescript@5.9.3) vite: specifier: ^5.4.11 - version: 5.4.21(@types/node@24.10.1) + version: 5.4.21(@types/node@24.10.4) packages: @@ -198,8 +201,29 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} - '@changesets/apply-release-plan@7.0.13': - resolution: {integrity: sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==} + '@base-ui/react@1.0.0': + resolution: {integrity: sha512-4USBWz++DUSLTuIYpbYkSgy1F9ZmNG9S/lXvlUN6qMK0P0RlW+6eQmDUB4DgZ7HVvtXl4pvi4z5J2fv6Z3+9hg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@base-ui/utils@0.2.3': + resolution: {integrity: sha512-/CguQ2PDaOzeVOkllQR8nocJ0FFIDqsWIcURsVmm53QGo8NhFNpePjNlyPIB41luxfOqnG7PU0xicMEw3ls7XQ==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@changesets/apply-release-plan@7.0.14': + resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} '@changesets/assemble-release-plan@6.0.9': resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} @@ -207,12 +231,12 @@ packages: '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.29.7': - resolution: {integrity: sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ==} + '@changesets/cli@2.29.8': + resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} hasBin: true - '@changesets/config@3.1.1': - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} + '@changesets/config@3.1.2': + resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} @@ -220,8 +244,8 @@ packages: '@changesets/get-dependents-graph@2.1.3': resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - '@changesets/get-release-plan@4.0.13': - resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} + '@changesets/get-release-plan@4.0.14': + resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -232,14 +256,14 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.1': - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} + '@changesets/parse@0.4.2': + resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==} '@changesets/pre@2.0.2': resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.5': - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} + '@changesets/read@0.6.6': + resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==} '@changesets/should-skip-package@0.1.2': resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} @@ -268,6 +292,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -280,6 +310,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -292,6 +328,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -304,6 +346,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -316,6 +364,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -328,6 +382,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -340,6 +400,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -352,6 +418,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -364,6 +436,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -376,6 +454,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -388,6 +472,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -400,6 +490,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -412,6 +508,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -424,6 +526,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -436,6 +544,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -448,6 +562,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -460,12 +580,24 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -478,12 +610,24 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -496,12 +640,24 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -514,6 +670,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -526,6 +688,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -538,6 +706,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -550,8 +724,14 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -572,12 +752,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -588,6 +768,21 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -670,8 +865,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rolldown/pluginutils@1.0.0-beta.47': - resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} @@ -682,120 +877,135 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.53.3': - resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.53.3': - resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.3': - resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.3': - resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.53.3': - resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.3': - resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.53.3': - resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.53.3': - resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.53.3': - resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.53.3': - resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.53.3': - resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.53.3': - resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.53.3': - resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.53.3': - resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.53.3': - resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.3': - resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.3': - resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.3': - resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} cpu: [x64] os: [win32] - '@storybook/addon-a11y@8.6.14': - resolution: {integrity: sha512-fozv6enO9IgpWq2U8qqS8MZ21Nt+MVHiRQe3CjnCpBOejTyo/ATm690PeYYRVHVG6M/15TVePb0h3ngKQbrrzQ==} + '@storybook/addon-a11y@8.6.15': + resolution: {integrity: sha512-hNSI28z1PCu7/mQ+skVHX+PvLwnLLiM4d+Ecr0hhZLJDwa5JoZrnszSrCoGadSzaRe270kwjYoEwgX9MGXaxrA==} peerDependencies: - storybook: ^8.6.14 + storybook: ^8.6.15 '@storybook/addon-actions@8.6.14': resolution: {integrity: sha512-mDQxylxGGCQSK7tJPkD144J8jWh9IU9ziJMHfB84PKpI/V5ZgqMDnpr2bssTrUaGDqU5e1/z8KcRF+Melhs9pQ==} @@ -827,6 +1037,11 @@ packages: peerDependencies: storybook: ^8.6.14 + '@storybook/addon-highlight@8.6.15': + resolution: {integrity: sha512-lOu44QTVw5nR8kzag0ukxWnLq48oy2MqMUDuMVFQWPBKX8ayhmgl2OiEcvAOVNsieTHrr2W4CkP7FFvF4D0vlg==} + peerDependencies: + storybook: ^8.6.15 + '@storybook/addon-interactions@8.6.14': resolution: {integrity: sha512-8VmElhm2XOjh22l/dO4UmXxNOolGhNiSpBcls2pqWSraVh4a670EyYBZsHpkXqfNHo2YgKyZN3C91+9zfH79qQ==} peerDependencies: @@ -869,10 +1084,10 @@ packages: react-dom: optional: true - '@storybook/builder-vite@8.6.14': - resolution: {integrity: sha512-ajWYhy32ksBWxwWHrjwZzyC0Ii5ZTeu5lsqA95Q/EQBB0P5qWlHWGM3AVyv82Mz/ND03ebGy123uVwgf6olnYQ==} + '@storybook/builder-vite@8.6.15': + resolution: {integrity: sha512-9Y05/ndZE6/eI7ZIUCD/QtH2htRIUs9j1gxE6oW0zRo9TJO1iqxfLNwgzd59KEkId7gdZxPei0l+LGTUGXYKRg==} peerDependencies: - storybook: ^8.6.14 + storybook: ^8.6.15 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 '@storybook/components@8.6.14': @@ -880,13 +1095,18 @@ packages: peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + '@storybook/components@8.6.15': + resolution: {integrity: sha512-+9GVKXPEW8Kl9zvNSTm9+VrJtx/puMZiO7gxCML63nK4aTWJXHQr4t9YUoGammSBM3AV1JglsKm6dBgJEeCoiA==} + peerDependencies: + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + '@storybook/core-events@8.6.14': resolution: {integrity: sha512-RrJ95u3HuIE4Nk8VmZP0tc/u0vYoE2v9fYlMw6K2GUSExzKDITs3voy6WMIY7Q3qbQun8XUXVlmqkuFzTEy/pA==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/core@8.6.14': - resolution: {integrity: sha512-1P/w4FSNRqP8j3JQBOi3yGt8PVOgSRbP66Ok520T78eJBeqx9ukCfl912PQZ7SPbW3TIunBwLXMZOjZwBB/JmA==} + '@storybook/core@8.6.15': + resolution: {integrity: sha512-VFpKcphNurJpSC4fpUfKL3GTXVoL53oytghGR30QIw5jKWwaT50HVbTyb41BLOUuZjmMhUQA8weiQEew6RX0gw==} peerDependencies: prettier: ^2 || ^3 peerDependenciesMeta: @@ -898,6 +1118,11 @@ packages: peerDependencies: storybook: ^8.6.14 + '@storybook/csf-plugin@8.6.15': + resolution: {integrity: sha512-ZLz/mtOoE1Jj2lE4pK3U7MmYrv5+lot3mGtwxGb832tcABMc97j9O+reCVxZYc7DeFbBuuEdMT9rBL/O3kXYmw==} + peerDependencies: + storybook: ^8.6.15 + '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} @@ -913,13 +1138,23 @@ packages: peerDependencies: storybook: ^8.6.14 + '@storybook/instrumenter@8.6.15': + resolution: {integrity: sha512-TvHR/+yyIAOp/1bLulFai2kkhIBtAlBw7J6Jd9DKyInoGhTWNE1G1Y61jD5GWXX29AlwaHfzGUaX5NL1K+FJpg==} + peerDependencies: + storybook: ^8.6.15 + '@storybook/manager-api@8.6.14': resolution: {integrity: sha512-ez0Zihuy17udLbfHZQXkGqwtep0mSGgHcNzGN7iZrMP1m+VmNo+7aGCJJdvXi7+iU3yq8weXSQFWg5DqWgLS7g==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@storybook/preview-api@8.6.14': - resolution: {integrity: sha512-2GhcCd4dNMrnD7eooEfvbfL4I83qAqEyO0CO7JQAmIO6Rxb9BsOLLI/GD5HkvQB73ArTJ+PT50rfaO820IExOQ==} + '@storybook/manager-api@8.6.15': + resolution: {integrity: sha512-ZOFtH821vFcwzECbFYFTKtSVO96Cvwwg45dMh3M/9bZIdN7klsloX7YNKw8OKvwE6XLFLsi2OvsNNcmTW6g88w==} + peerDependencies: + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + + '@storybook/preview-api@8.6.15': + resolution: {integrity: sha512-oqsp8f7QekB9RzpDqOXZQcPPRXXd/mTsnZSdAAQB/pBVqUpC9h/y5hgovbYnJ6DWXcpODbMwH+wbJHZu5lvm+w==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 @@ -930,27 +1165,34 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta storybook: ^8.6.14 - '@storybook/react-vite@8.6.14': - resolution: {integrity: sha512-FZU0xMPxa4/TO87FgcWwappOxLBHZV5HSRK5K+2bJD7rFJAoNorbHvB4Q1zvIAk7eCMjkr2GPCPHx9PRB9vJFg==} + '@storybook/react-dom-shim@8.6.15': + resolution: {integrity: sha512-m2trBmmd4iom1qwrp1F109zjRDc0cPaHYhDQxZR4Qqdz8pYevYJTlipDbH/K4NVB6Rn687RT29OoOPfJh6vkFA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^8.6.15 + + '@storybook/react-vite@8.6.15': + resolution: {integrity: sha512-9st+2NCemzzBwmindpDrRLEqYJmwwd2RnXMoj+Wt4Y1r4MGoRe1l837ciT2tmstaqekY2mVUSYd6879NzeeMYw==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.14 + '@storybook/test': 8.6.15 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.14 + storybook: ^8.6.15 vite: ^4.0.0 || ^5.0.0 || ^6.0.0 peerDependenciesMeta: '@storybook/test': optional: true - '@storybook/react@8.6.14': - resolution: {integrity: sha512-BOepx5bBFwl/CPI+F+LnmMmsG1wQYmrX/UQXgUbHQUU9Tj7E2ndTnNbpIuSLc8IrM03ru+DfwSg1Co3cxWtT+g==} + '@storybook/react@8.6.15': + resolution: {integrity: sha512-hdnhlJg+YkpPMOw2hvK7+mhdxAbguA+TFTIAzVV9CeUYoHDIZAsgeKVhRmgZGN20NGjRN5ZcwkplAMJnF9v+6w==} engines: {node: '>=18.0.0'} peerDependencies: - '@storybook/test': 8.6.14 + '@storybook/test': 8.6.15 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.14 + storybook: ^8.6.15 typescript: '>= 4.2.x' peerDependenciesMeta: '@storybook/test': @@ -968,11 +1210,21 @@ packages: peerDependencies: storybook: ^8.6.14 + '@storybook/test@8.6.15': + resolution: {integrity: sha512-EwquDRUDVvWcZds3T2abmB5wSN/Vattal4YtZ6fpBlIUqONV4o/cOBX39cFfQSUCBrIXIjQ6RmapQCHK/PvBYw==} + peerDependencies: + storybook: ^8.6.15 + '@storybook/theming@8.6.14': resolution: {integrity: sha512-r4y+LsiB37V5hzpQo+BM10PaCsp7YlZ0YcZzQP1OCkPlYXmUAFy2VvDKaFRpD8IeNPKug2u4iFm/laDEbs03dg==} peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + '@storybook/theming@8.6.15': + resolution: {integrity: sha512-dAbL0XOekyT6XsF49R6Etj3WxQ/LpdJDIswUUeHgVJ6/yd2opZOGbPxnwA3zlmAh1c0tvpPyhSDXxSG79u8e4Q==} + peerDependencies: + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -1017,16 +1269,16 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@24.10.4': + resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.6': - resolution: {integrity: sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==} + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} @@ -1034,76 +1286,76 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@typescript-eslint/eslint-plugin@8.47.0': - resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==} + '@typescript-eslint/eslint-plugin@8.51.0': + resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.47.0 + '@typescript-eslint/parser': ^8.51.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.47.0': - resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==} + '@typescript-eslint/parser@8.51.0': + resolution: {integrity: sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.47.0': - resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==} + '@typescript-eslint/project-service@8.51.0': + resolution: {integrity: sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.47.0': - resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} + '@typescript-eslint/scope-manager@8.51.0': + resolution: {integrity: sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.47.0': - resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==} + '@typescript-eslint/tsconfig-utils@8.51.0': + resolution: {integrity: sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.47.0': - resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==} + '@typescript-eslint/type-utils@8.51.0': + resolution: {integrity: sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.47.0': - resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} + '@typescript-eslint/types@8.51.0': + resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.47.0': - resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==} + '@typescript-eslint/typescript-estree@8.51.0': + resolution: {integrity: sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.47.0': - resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==} + '@typescript-eslint/utils@8.51.0': + resolution: {integrity: sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.47.0': - resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} + '@typescript-eslint/visitor-keys@8.51.0': + resolution: {integrity: sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vanilla-extract/babel-plugin-debug-ids@1.2.2': resolution: {integrity: sha512-MeDWGICAF9zA/OZLOKwhoRlsUW+fiMwnfuOAqFVohL31Agj7Q/RBWAYweqjHLgFBCsdnr6XIfwjJnmb2znEWxw==} - '@vanilla-extract/compiler@0.3.1': - resolution: {integrity: sha512-KZ67DZQu58dMo7Jv4PNMPG5TbMOXB68xxVYV2cRJvUdPeiOmX0FOaPgEsYBAZUgd/oLEx4IyV0AvlvsxJ1akfQ==} + '@vanilla-extract/compiler@0.3.4': + resolution: {integrity: sha512-W9HXf9EAccpE1vEIATvSoBVj/bQnmHfYHfDJjUN8dcOHW6oMcnoGTqweDM9I66BHqlNH4d0IsaeZKSViOv7K4w==} - '@vanilla-extract/css@1.17.4': - resolution: {integrity: sha512-m3g9nQDWPtL+sTFdtCGRMI1Vrp86Ay4PBYq1Bo7Bnchj5ElNtAJpOqD+zg+apthVA4fB7oVpMWNjwpa6ElDWFQ==} + '@vanilla-extract/css@1.18.0': + resolution: {integrity: sha512-/p0dwOjr0o8gE5BRQ5O9P0u/2DjUd6Zfga2JGmE4KaY7ZITWMszTzk4x4CPlM5cKkRr2ZGzbE6XkuPNfp9shSQ==} - '@vanilla-extract/integration@8.0.4': - resolution: {integrity: sha512-cmOb7tR+g3ulKvFtSbmdw3YUyIS1d7MQqN+FcbwNhdieyno5xzUyfDCMjeWJhmCSMvZ6WlinkrOkgs6SHB+FRg==} + '@vanilla-extract/integration@8.0.7': + resolution: {integrity: sha512-ILob4F9cEHXpbWAVt3Y2iaQJpqYq/c/5TJC8Fz58C2XmX3QW2Y589krvViiyJhQfydCGK3EbwPQhVFjQaBeKfg==} '@vanilla-extract/private@1.0.9': resolution: {integrity: sha512-gT2jbfZuaaCLrAxwXbRgIhGhcXbRZCG3v4TTUnjw0EJ7ArdBRxkq4msNJkbuRkCgfIK5ATmprB5t9ljvLeFDEA==} @@ -1113,13 +1365,13 @@ packages: peerDependencies: '@vanilla-extract/css': ^1.0.0 - '@vanilla-extract/vite-plugin@5.1.1': - resolution: {integrity: sha512-Nd1worqkHrd8XED4ZAA7Wmkd3pCqCwpmzCBVF8v6T1BSLHGXQE5HYflVgygw0CsIAbFRMS6zQBIk4F4/r/YKIw==} + '@vanilla-extract/vite-plugin@5.1.4': + resolution: {integrity: sha512-fTYNKUK3n4ApkUf2FEcO7mpqNKEHf9kDGg8DXlkqHtPxgwPhjuaajmDfQCSBsNgnA2SLI+CB5EO6kLQuKsw2Rw==} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@vitejs/plugin-react@5.1.1': - resolution: {integrity: sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==} + '@vitejs/plugin-react@5.1.2': + resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -1215,8 +1467,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true better-opn@3.0.2: @@ -1240,8 +1492,8 @@ packages: browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1265,8 +1517,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001756: - resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001762: + resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} @@ -1283,8 +1535,8 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} ci-info@3.9.0: @@ -1339,8 +1591,8 @@ packages: supports-color: optional: true - dedent@1.7.0: - resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + dedent@1.7.1: + resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -1398,8 +1650,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.256: - resolution: {integrity: sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1426,8 +1678,8 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - es-toolkit@1.42.0: - resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==} + es-toolkit@1.43.0: + resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} esbuild-register@3.6.0: resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} @@ -1444,6 +1696,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1458,16 +1715,16 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-refresh@0.4.24: - resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} + eslint-plugin-react-refresh@0.4.26: + resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} peerDependencies: eslint: '>=8.40' - eslint-plugin-storybook@10.0.8: - resolution: {integrity: sha512-ZKEMFhF/z/HRVvIgnEIYG2uAqmuLbkebUdHH3DpGHE64GPgk+KozcpqnD6zNk5vJ407bFmcWsGinBc2zi74f0g==} + eslint-plugin-storybook@10.1.11: + resolution: {integrity: sha512-mbq2r2kK5+AcLl0XDJ3to91JOgzCbHOqj+J3n+FRw6drk+M1boRqMShSoMMm0HdzXPLmlr7iur+qJ5ZuhH6ayQ==} peerDependencies: eslint: '>=8' - storybook: ^10.0.8 + storybook: ^10.1.11 eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} @@ -1481,8 +1738,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1500,8 +1757,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -1542,8 +1799,17 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} @@ -1639,9 +1905,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1661,12 +1924,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - human-id@4.1.2: - resolution: {integrity: sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==} + human-id@4.1.3: + resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} ignore@5.3.2: @@ -2016,8 +2279,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -2048,10 +2311,10 @@ packages: resolution: {integrity: sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==} engines: {node: '>=16.14.0'} - react-dom@19.2.0: - resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.2.0 + react: ^19.2.3 react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -2060,8 +2323,8 @@ packages: resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} - react@19.2.0: - resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} read-yaml-file@1.1.0: @@ -2079,6 +2342,9 @@ packages: require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2096,8 +2362,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.53.3: - resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2160,8 +2426,8 @@ packages: storybook-dark-mode@4.0.2: resolution: {integrity: sha512-zjcwwQ01R5t1VsakA6alc2JDIRVtavryW8J3E3eKLDIlAMcvsgtpxlelWkZs2cuNspk6Z10XzhQVrUWtYc3F0w==} - storybook@8.6.14: - resolution: {integrity: sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw==} + storybook@8.6.15: + resolution: {integrity: sha512-Ob7DMlwWx8s7dMvcQ3xPc02TvUeralb+xX3oaPRk9wY9Hc6M1IBC/7cEoITkSmRS2v38DHubC+mtEKNc1u2gQg==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -2209,6 +2475,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -2216,6 +2485,10 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinyrainbow@1.2.0: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} @@ -2228,8 +2501,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -2249,8 +2522,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.47.0: - resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==} + typescript-eslint@8.51.0: + resolution: {integrity: sha512-jh8ZuM5oEh2PSdyQG9YAEM1TCGuWenLSuSUhf/irbVUNW9O5FhbFVONviN2TgMTBnUmyHv7E56rYnfLZK6TkiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2275,8 +2548,8 @@ packages: resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} engines: {node: '>=14.0.0'} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2284,6 +2557,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} @@ -2414,7 +2692,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -2493,9 +2771,34 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@changesets/apply-release-plan@7.0.13': + '@base-ui/react@1.0.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@base-ui/utils': 0.2.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@floating-ui/utils': 0.2.10 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + reselect: 5.1.1 + tabbable: 6.4.0 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.7 + + '@base-ui/utils@0.2.3(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@changesets/config': 3.1.1 + '@babel/runtime': 7.28.4 + '@floating-ui/utils': 0.2.10 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + reselect: 5.1.1 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.7 + + '@changesets/apply-release-plan@7.0.14': + dependencies: + '@changesets/config': 3.1.2 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.4 '@changesets/should-skip-package': 0.1.2 @@ -2522,23 +2825,23 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.7(@types/node@24.10.1)': + '@changesets/cli@2.29.8(@types/node@24.10.4)': dependencies: - '@changesets/apply-release-plan': 7.0.13 + '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.13 + '@changesets/get-release-plan': 4.0.14 '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 + '@changesets/read': 0.6.6 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.4) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -2555,7 +2858,7 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@changesets/config@3.1.1': + '@changesets/config@3.1.2': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 @@ -2576,12 +2879,12 @@ snapshots: picocolors: 1.1.1 semver: 7.7.3 - '@changesets/get-release-plan@4.0.13': + '@changesets/get-release-plan@4.0.14': dependencies: '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 + '@changesets/read': 0.6.6 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 @@ -2599,10 +2902,10 @@ snapshots: dependencies: picocolors: 1.1.1 - '@changesets/parse@0.4.1': + '@changesets/parse@0.4.2': dependencies: '@changesets/types': 6.1.0 - js-yaml: 3.14.2 + js-yaml: 4.1.1 '@changesets/pre@2.0.2': dependencies: @@ -2611,11 +2914,11 @@ snapshots: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.5': + '@changesets/read@0.6.6': dependencies: '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.1 + '@changesets/parse': 0.4.2 '@changesets/types': 6.1.0 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -2634,7 +2937,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 fs-extra: 7.0.1 - human-id: 4.1.2 + human-id: 4.1.3 prettier: 2.8.8 '@emotion/hash@0.9.2': {} @@ -2645,150 +2948,228 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true + '@esbuild/aix-ppc64@0.27.2': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.25.12': optional: true + '@esbuild/android-arm64@0.27.2': + optional: true + '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.25.12': optional: true + '@esbuild/android-arm@0.27.2': + optional: true + '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.25.12': optional: true + '@esbuild/android-x64@0.27.2': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true + '@esbuild/darwin-arm64@0.27.2': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.25.12': optional: true + '@esbuild/darwin-x64@0.27.2': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true + '@esbuild/freebsd-arm64@0.27.2': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true + '@esbuild/freebsd-x64@0.27.2': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.25.12': optional: true + '@esbuild/linux-arm64@0.27.2': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.25.12': optional: true + '@esbuild/linux-arm@0.27.2': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.25.12': optional: true + '@esbuild/linux-ia32@0.27.2': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.25.12': optional: true + '@esbuild/linux-loong64@0.27.2': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true + '@esbuild/linux-mips64el@0.27.2': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true + '@esbuild/linux-ppc64@0.27.2': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true + '@esbuild/linux-riscv64@0.27.2': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.25.12': optional: true + '@esbuild/linux-s390x@0.27.2': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true '@esbuild/linux-x64@0.25.12': optional: true + '@esbuild/linux-x64@0.27.2': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true + '@esbuild/netbsd-arm64@0.27.2': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true + '@esbuild/netbsd-x64@0.27.2': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true + '@esbuild/openbsd-arm64@0.27.2': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true + '@esbuild/openbsd-x64@0.27.2': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true + '@esbuild/openharmony-arm64@0.27.2': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.25.12': optional: true + '@esbuild/sunos-x64@0.27.2': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.25.12': optional: true + '@esbuild/win32-arm64@0.27.2': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.25.12': optional: true + '@esbuild/win32-ia32@0.27.2': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': dependencies: - eslint: 9.39.1 + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -2809,7 +3190,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3 @@ -2823,7 +3204,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -2832,6 +3213,23 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@floating-ui/utils@0.2.10': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2843,12 +3241,12 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + '@inquirer/external-editor@1.0.3(@types/node@24.10.4)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.0 + iconv-lite: 0.7.1 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 '@isaacs/cliui@8.0.2': dependencies: @@ -2859,12 +3257,12 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.1))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.4))': dependencies: glob: 10.5.0 magic-string: 0.27.0 react-docgen-typescript: 2.4.0(typescript@5.9.3) - vite: 5.4.21(@types/node@24.10.1) + vite: 5.4.21(@types/node@24.10.4) optionalDependencies: typescript: 5.9.3 @@ -2903,11 +3301,11 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@mdx-js/react@3.1.1(@types/react@19.2.6)(react@19.2.0)': + '@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 19.2.6 - react: 19.2.0 + '@types/react': 19.2.7 + react: 19.2.3 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -2919,217 +3317,235 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@pkgjs/parseargs@0.11.0': optional: true - '@rolldown/pluginutils@1.0.0-beta.47': {} + '@rolldown/pluginutils@1.0.0-beta.53': {} - '@rollup/pluginutils@5.3.0(rollup@4.53.3)': + '@rollup/pluginutils@5.3.0(rollup@4.55.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.55.1 + + '@rollup/rollup-android-arm-eabi@4.55.1': + optional: true + + '@rollup/rollup-android-arm64@4.55.1': + optional: true - '@rollup/rollup-android-arm-eabi@4.53.3': + '@rollup/rollup-darwin-arm64@4.55.1': optional: true - '@rollup/rollup-android-arm64@4.53.3': + '@rollup/rollup-darwin-x64@4.55.1': optional: true - '@rollup/rollup-darwin-arm64@4.53.3': + '@rollup/rollup-freebsd-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-x64@4.53.3': + '@rollup/rollup-freebsd-x64@4.55.1': optional: true - '@rollup/rollup-freebsd-arm64@4.53.3': + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': optional: true - '@rollup/rollup-freebsd-x64@4.53.3': + '@rollup/rollup-linux-arm-musleabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + '@rollup/rollup-linux-arm64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.3': + '@rollup/rollup-linux-arm64-musl@4.55.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.3': + '@rollup/rollup-linux-loong64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.3': + '@rollup/rollup-linux-loong64-musl@4.55.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.3': + '@rollup/rollup-linux-ppc64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.3': + '@rollup/rollup-linux-ppc64-musl@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.3': + '@rollup/rollup-linux-riscv64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.3': + '@rollup/rollup-linux-riscv64-musl@4.55.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.3': + '@rollup/rollup-linux-s390x-gnu@4.55.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.3': + '@rollup/rollup-linux-x64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-x64-musl@4.53.3': + '@rollup/rollup-linux-x64-musl@4.55.1': optional: true - '@rollup/rollup-openharmony-arm64@4.53.3': + '@rollup/rollup-openbsd-x64@4.55.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.53.3': + '@rollup/rollup-openharmony-arm64@4.55.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.53.3': + '@rollup/rollup-win32-arm64-msvc@4.55.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.53.3': + '@rollup/rollup-win32-ia32-msvc@4.55.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.53.3': + '@rollup/rollup-win32-x64-gnu@4.55.1': optional: true - '@storybook/addon-a11y@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@rollup/rollup-win32-x64-msvc@4.55.1': + optional: true + + '@storybook/addon-a11y@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: - '@storybook/addon-highlight': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/addon-highlight': 8.6.15(storybook@8.6.15(prettier@3.7.4)) '@storybook/global': 5.0.0 - '@storybook/test': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/test': 8.6.15(storybook@8.6.15(prettier@3.7.4)) axe-core: 4.11.0 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/addon-actions@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-actions@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.3.1 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-backgrounds@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-controls@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.6.14(@types/react@19.2.6)(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-docs@8.6.14(@types/react@19.2.7)(storybook@8.6.15(prettier@3.7.4))': dependencies: - '@mdx-js/react': 3.1.1(@types/react@19.2.6)(react@19.2.0) - '@storybook/blocks': 8.6.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2)) - '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/react-dom-shim': 8.6.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2)) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - storybook: 8.6.14(prettier@3.6.2) + '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3) + '@storybook/blocks': 8.6.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)) + '@storybook/csf-plugin': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/react-dom-shim': 8.6.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.6.14(@types/react@19.2.6)(storybook@8.6.14(prettier@3.6.2))': - dependencies: - '@storybook/addon-actions': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-backgrounds': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-controls': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-docs': 8.6.14(@types/react@19.2.6)(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-highlight': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-measure': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-outline': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-toolbars': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/addon-viewport': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - storybook: 8.6.14(prettier@3.6.2) + '@storybook/addon-essentials@8.6.14(@types/react@19.2.7)(storybook@8.6.15(prettier@3.7.4))': + dependencies: + '@storybook/addon-actions': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-backgrounds': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-controls': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-docs': 8.6.14(@types/react@19.2.7)(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-highlight': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-measure': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-outline': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-toolbars': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/addon-viewport': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-highlight@8.6.14(storybook@8.6.15(prettier@3.7.4))': + dependencies: + '@storybook/global': 5.0.0 + storybook: 8.6.15(prettier@3.7.4) + + '@storybook/addon-highlight@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/addon-interactions@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-interactions@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/test': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/instrumenter': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/test': 8.6.14(storybook@8.6.15(prettier@3.7.4)) polished: 4.3.1 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 - '@storybook/addon-measure@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-measure@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) tiny-invariant: 1.3.3 - '@storybook/addon-outline@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-outline@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 - '@storybook/addon-storysource@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-storysource@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - '@storybook/source-loader': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/source-loader': 8.6.14(storybook@8.6.15(prettier@3.7.4)) estraverse: 5.3.0 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) tiny-invariant: 1.3.3 - '@storybook/addon-toolbars@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-toolbars@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/addon-viewport@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/addon-viewport@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: memoizerific: 1.11.3 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/blocks@8.6.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2))': + '@storybook/blocks@8.6.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4))': dependencies: - '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - storybook: 8.6.14(prettier@3.6.2) + '@storybook/icons': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 optionalDependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - '@storybook/builder-vite@8.6.14(storybook@8.6.14(prettier@3.6.2))(vite@5.4.21(@types/node@24.10.1))': + '@storybook/builder-vite@8.6.15(storybook@8.6.15(prettier@3.7.4))(vite@5.4.21(@types/node@24.10.4))': dependencies: - '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/csf-plugin': 8.6.15(storybook@8.6.15(prettier@3.7.4)) browser-assert: 1.2.1 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) ts-dedent: 2.2.0 - vite: 5.4.21(@types/node@24.10.1) + vite: 5.4.21(@types/node@24.10.4) - '@storybook/components@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/components@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/core-events@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/components@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/core@8.6.14(prettier@3.6.2)(storybook@8.6.14(prettier@3.6.2))': + '@storybook/core-events@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - '@storybook/theming': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + storybook: 8.6.15(prettier@3.7.4) + + '@storybook/core@8.6.15(prettier@3.7.4)(storybook@8.6.15(prettier@3.7.4))': + dependencies: + '@storybook/theming': 8.6.15(storybook@8.6.15(prettier@3.7.4)) better-opn: 3.0.2 browser-assert: 1.2.1 esbuild: 0.25.12 @@ -3141,103 +3557,139 @@ snapshots: util: 0.12.5 ws: 8.18.3 optionalDependencies: - prettier: 3.6.2 + prettier: 3.7.4 transitivePeerDependencies: - bufferutil - storybook - supports-color - utf-8-validate - '@storybook/csf-plugin@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/csf-plugin@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) + unplugin: 1.16.1 + + '@storybook/csf-plugin@8.6.15(storybook@8.6.15(prettier@3.7.4))': + dependencies: + storybook: 8.6.15(prettier@3.7.4) unplugin: 1.16.1 '@storybook/global@5.0.0': {} - '@storybook/icons@1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@storybook/icons@1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@storybook/instrumenter@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + '@storybook/global': 5.0.0 + '@vitest/utils': 2.1.9 + storybook: 8.6.15(prettier@3.7.4) - '@storybook/instrumenter@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/instrumenter@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 '@vitest/utils': 2.1.9 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) + + '@storybook/manager-api@8.6.14(storybook@8.6.15(prettier@3.7.4))': + dependencies: + storybook: 8.6.15(prettier@3.7.4) + + '@storybook/manager-api@8.6.15(storybook@8.6.15(prettier@3.7.4))': + dependencies: + storybook: 8.6.15(prettier@3.7.4) - '@storybook/manager-api@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/preview-api@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/preview-api@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/react-dom-shim@8.6.14(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/react-dom-shim@8.6.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2))': + '@storybook/react-dom-shim@8.6.15(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4))': dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - storybook: 8.6.14(prettier@3.6.2) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.6.2)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.53.3)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.1))': + '@storybook/react-vite@8.6.15(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.7.4)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.55.1)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.4))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.1)) - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(prettier@3.6.2))(vite@5.4.21(@types/node@24.10.1)) - '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.6.2)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.4)) + '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@storybook/builder-vite': 8.6.15(storybook@8.6.15(prettier@3.7.4))(vite@5.4.21(@types/node@24.10.4)) + '@storybook/react': 8.6.15(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.7.4)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3) find-up: 5.0.0 magic-string: 0.30.21 - react: 19.2.0 + react: 19.2.3 react-docgen: 7.1.1 - react-dom: 19.2.0(react@19.2.0) + react-dom: 19.2.3(react@19.2.3) resolve: 1.22.11 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) tsconfig-paths: 4.2.0 - vite: 5.4.21(@types/node@24.10.1) + vite: 5.4.21(@types/node@24.10.4) optionalDependencies: - '@storybook/test': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/test': 8.6.15(storybook@8.6.15(prettier@3.7.4)) transitivePeerDependencies: - rollup - supports-color - typescript - '@storybook/react@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.6.2)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3)': + '@storybook/react@8.6.15(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.7.4)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3)': dependencies: - '@storybook/components': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/components': 8.6.15(storybook@8.6.15(prettier@3.7.4)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/preview-api': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/react-dom-shim': 8.6.14(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2)) - '@storybook/theming': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - storybook: 8.6.14(prettier@3.6.2) + '@storybook/manager-api': 8.6.15(storybook@8.6.15(prettier@3.7.4)) + '@storybook/preview-api': 8.6.15(storybook@8.6.15(prettier@3.7.4)) + '@storybook/react-dom-shim': 8.6.15(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)) + '@storybook/theming': 8.6.15(storybook@8.6.15(prettier@3.7.4)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + storybook: 8.6.15(prettier@3.7.4) optionalDependencies: - '@storybook/test': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/test': 8.6.15(storybook@8.6.15(prettier@3.7.4)) typescript: 5.9.3 - '@storybook/source-loader@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/source-loader@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: - es-toolkit: 1.42.0 + es-toolkit: 1.43.0 estraverse: 5.3.0 - prettier: 3.6.2 - storybook: 8.6.14(prettier@3.6.2) + prettier: 3.7.4 + storybook: 8.6.15(prettier@3.7.4) - '@storybook/test@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/test@8.6.14(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/instrumenter': 8.6.14(storybook@8.6.15(prettier@3.7.4)) '@testing-library/dom': 10.4.0 '@testing-library/jest-dom': 6.5.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) '@vitest/expect': 2.0.5 '@vitest/spy': 2.0.5 - storybook: 8.6.14(prettier@3.6.2) + storybook: 8.6.15(prettier@3.7.4) - '@storybook/theming@8.6.14(storybook@8.6.14(prettier@3.6.2))': + '@storybook/test@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: - storybook: 8.6.14(prettier@3.6.2) + '@storybook/global': 5.0.0 + '@storybook/instrumenter': 8.6.15(storybook@8.6.15(prettier@3.7.4)) + '@testing-library/dom': 10.4.0 + '@testing-library/jest-dom': 6.5.0 + '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) + '@vitest/expect': 2.0.5 + '@vitest/spy': 2.0.5 + storybook: 8.6.15(prettier@3.7.4) + + '@storybook/theming@8.6.14(storybook@8.6.15(prettier@3.7.4))': + dependencies: + storybook: 8.6.15(prettier@3.7.4) + + '@storybook/theming@8.6.15(storybook@8.6.15(prettier@3.7.4))': + dependencies: + storybook: 8.6.15(prettier@3.7.4) '@testing-library/dom@10.4.0': dependencies: @@ -3297,15 +3749,15 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@24.10.1': + '@types/node@24.10.4': dependencies: undici-types: 7.16.0 - '@types/react-dom@19.2.3(@types/react@19.2.6)': + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: - '@types/react': 19.2.6 + '@types/react': 19.2.7 - '@types/react@19.2.6': + '@types/react@19.2.7': dependencies: csstype: 3.2.3 @@ -3313,97 +3765,95 @@ snapshots: '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.51.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 - eslint: 9.39.1 - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.51.0 + '@typescript-eslint/type-utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.51.0 + eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/scope-manager': 8.51.0 + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.51.0 debug: 4.4.3 - eslint: 9.39.1 + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.47.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.51.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 + '@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.47.0': + '@typescript-eslint/scope-manager@8.51.0': dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/visitor-keys': 8.51.0 - '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.51.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1 - ts-api-utils: 2.1.0(typescript@5.9.3) + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.47.0': {} + '@typescript-eslint/types@8.51.0': {} - '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.51.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.47.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/project-service': 8.51.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3) + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/visitor-keys': 8.51.0 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.47.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - eslint: 9.39.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.51.0 + '@typescript-eslint/types': 8.51.0 + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.47.0': + '@typescript-eslint/visitor-keys@8.51.0': dependencies: - '@typescript-eslint/types': 8.47.0 + '@typescript-eslint/types': 8.51.0 eslint-visitor-keys: 4.2.1 '@vanilla-extract/babel-plugin-debug-ids@1.2.2': @@ -3412,12 +3862,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@vanilla-extract/compiler@0.3.1(@types/node@24.10.1)': + '@vanilla-extract/compiler@0.3.4(@types/node@24.10.4)': dependencies: - '@vanilla-extract/css': 1.17.4 - '@vanilla-extract/integration': 8.0.4 - vite: 5.4.21(@types/node@24.10.1) - vite-node: 3.2.4(@types/node@24.10.1) + '@vanilla-extract/css': 1.18.0 + '@vanilla-extract/integration': 8.0.7 + vite: 5.4.21(@types/node@24.10.4) + vite-node: 3.2.4(@types/node@24.10.4) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3430,14 +3880,14 @@ snapshots: - supports-color - terser - '@vanilla-extract/css@1.17.4': + '@vanilla-extract/css@1.18.0': dependencies: '@emotion/hash': 0.9.2 '@vanilla-extract/private': 1.0.9 css-what: 6.2.2 cssesc: 3.0.0 csstype: 3.2.3 - dedent: 1.7.0 + dedent: 1.7.1 deep-object-diff: 1.1.9 deepmerge: 4.3.1 lru-cache: 10.4.3 @@ -3447,14 +3897,14 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - '@vanilla-extract/integration@8.0.4': + '@vanilla-extract/integration@8.0.7': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) '@vanilla-extract/babel-plugin-debug-ids': 1.2.2 - '@vanilla-extract/css': 1.17.4 - dedent: 1.7.0 - esbuild: 0.25.12 + '@vanilla-extract/css': 1.18.0 + dedent: 1.7.1 + esbuild: 0.27.2 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 @@ -3465,15 +3915,15 @@ snapshots: '@vanilla-extract/private@1.0.9': {} - '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.17.4)': + '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0)': dependencies: - '@vanilla-extract/css': 1.17.4 + '@vanilla-extract/css': 1.18.0 - '@vanilla-extract/vite-plugin@5.1.1(@types/node@24.10.1)(vite@5.4.21(@types/node@24.10.1))': + '@vanilla-extract/vite-plugin@5.1.4(@types/node@24.10.4)(vite@5.4.21(@types/node@24.10.4))': dependencies: - '@vanilla-extract/compiler': 0.3.1(@types/node@24.10.1) - '@vanilla-extract/integration': 8.0.4 - vite: 5.4.21(@types/node@24.10.1) + '@vanilla-extract/compiler': 0.3.4(@types/node@24.10.4) + '@vanilla-extract/integration': 8.0.7 + vite: 5.4.21(@types/node@24.10.4) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3486,15 +3936,15 @@ snapshots: - supports-color - terser - '@vitejs/plugin-react@5.1.1(vite@5.4.21(@types/node@24.10.1))': + '@vitejs/plugin-react@5.1.2(vite@5.4.21(@types/node@24.10.4))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) - '@rolldown/pluginutils': 1.0.0-beta.47 + '@rolldown/pluginutils': 1.0.0-beta.53 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 5.4.21(@types/node@24.10.1) + vite: 5.4.21(@types/node@24.10.4) transitivePeerDependencies: - supports-color @@ -3585,7 +4035,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.29: {} + baseline-browser-mapping@2.9.11: {} better-opn@3.0.2: dependencies: @@ -3610,13 +4060,13 @@ snapshots: browser-assert@1.2.1: {} - browserslist@4.28.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.256 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001762 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) + update-browserslist-db: 1.2.3(browserslist@4.28.1) cac@6.7.14: {} @@ -3639,12 +4089,12 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001762: {} chai@5.3.3: dependencies: assertion-error: 2.0.1 - check-error: 2.1.1 + check-error: 2.1.3 deep-eql: 5.0.2 loupe: 3.2.1 pathval: 2.0.1 @@ -3661,7 +4111,7 @@ snapshots: chardet@2.1.1: {} - check-error@2.1.1: {} + check-error@2.1.3: {} ci-info@3.9.0: {} @@ -3697,7 +4147,7 @@ snapshots: dependencies: ms: 2.1.3 - dedent@1.7.0: {} + dedent@1.7.1: {} deep-eql@5.0.2: {} @@ -3739,7 +4189,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.256: {} + electron-to-chromium@1.5.267: {} emoji-regex@8.0.0: {} @@ -3760,7 +4210,7 @@ snapshots: dependencies: es-errors: 1.3.0 - es-toolkit@1.42.0: {} + es-toolkit@1.43.0: {} esbuild-register@3.6.0(esbuild@0.25.12): dependencies: @@ -3824,23 +4274,52 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@5.2.0(eslint@9.39.1): + eslint-plugin-react-hooks@5.2.0(eslint@9.39.2): dependencies: - eslint: 9.39.1 + eslint: 9.39.2 - eslint-plugin-react-refresh@0.4.24(eslint@9.39.1): + eslint-plugin-react-refresh@0.4.26(eslint@9.39.2): dependencies: - eslint: 9.39.1 + eslint: 9.39.2 - eslint-plugin-storybook@10.0.8(eslint@9.39.1)(storybook@8.6.14(prettier@3.6.2))(typescript@5.9.3): + eslint-plugin-storybook@10.1.11(eslint@9.39.2)(storybook@8.6.15(prettier@3.7.4))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) - eslint: 9.39.1 - storybook: 8.6.14(prettier@3.6.2) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + storybook: 8.6.15(prettier@3.7.4) transitivePeerDependencies: - supports-color - typescript @@ -3854,15 +4333,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1: + eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -3876,7 +4355,7 @@ snapshots: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.6.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -3901,7 +4380,7 @@ snapshots: esprima@4.0.1: {} - esquery@1.6.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -3921,7 +4400,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 require-like: 0.1.2 extendable-error@0.1.7: {} @@ -3940,10 +4419,14 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4051,8 +4534,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -4069,9 +4550,9 @@ snapshots: dependencies: function-bind: 1.1.2 - human-id@4.1.2: {} + human-id@4.1.3: {} - iconv-lite@0.7.0: + iconv-lite@0.7.1: dependencies: safer-buffer: 2.1.2 @@ -4370,7 +4851,7 @@ snapshots: prettier@2.8.8: {} - prettier@3.6.2: {} + prettier@3.7.4: {} pretty-format@27.5.1: dependencies: @@ -4405,16 +4886,16 @@ snapshots: transitivePeerDependencies: - supports-color - react-dom@19.2.0(react@19.2.0): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 scheduler: 0.27.0 react-is@17.0.2: {} react-refresh@0.18.0: {} - react@19.2.0: {} + react@19.2.3: {} read-yaml-file@1.1.0: dependencies: @@ -4438,6 +4919,8 @@ snapshots: require-like@0.1.2: {} + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -4450,32 +4933,35 @@ snapshots: reusify@1.1.0: {} - rollup@4.53.3: + rollup@4.55.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.3 - '@rollup/rollup-android-arm64': 4.53.3 - '@rollup/rollup-darwin-arm64': 4.53.3 - '@rollup/rollup-darwin-x64': 4.53.3 - '@rollup/rollup-freebsd-arm64': 4.53.3 - '@rollup/rollup-freebsd-x64': 4.53.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 - '@rollup/rollup-linux-arm-musleabihf': 4.53.3 - '@rollup/rollup-linux-arm64-gnu': 4.53.3 - '@rollup/rollup-linux-arm64-musl': 4.53.3 - '@rollup/rollup-linux-loong64-gnu': 4.53.3 - '@rollup/rollup-linux-ppc64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-musl': 4.53.3 - '@rollup/rollup-linux-s390x-gnu': 4.53.3 - '@rollup/rollup-linux-x64-gnu': 4.53.3 - '@rollup/rollup-linux-x64-musl': 4.53.3 - '@rollup/rollup-openharmony-arm64': 4.53.3 - '@rollup/rollup-win32-arm64-msvc': 4.53.3 - '@rollup/rollup-win32-ia32-msvc': 4.53.3 - '@rollup/rollup-win32-x64-gnu': 4.53.3 - '@rollup/rollup-win32-x64-msvc': 4.53.3 + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4526,14 +5012,14 @@ snapshots: sprintf-js@1.0.3: {} - storybook-dark-mode@4.0.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@3.6.2)): + storybook-dark-mode@4.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)): dependencies: - '@storybook/components': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/core-events': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/components': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/core-events': 8.6.14(storybook@8.6.15(prettier@3.7.4)) '@storybook/global': 5.0.0 - '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@storybook/manager-api': 8.6.14(storybook@8.6.14(prettier@3.6.2)) - '@storybook/theming': 8.6.14(storybook@8.6.14(prettier@3.6.2)) + '@storybook/icons': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@storybook/manager-api': 8.6.14(storybook@8.6.15(prettier@3.7.4)) + '@storybook/theming': 8.6.14(storybook@8.6.15(prettier@3.7.4)) fast-deep-equal: 3.1.3 memoizerific: 1.11.3 transitivePeerDependencies: @@ -4541,11 +5027,11 @@ snapshots: - react-dom - storybook - storybook@8.6.14(prettier@3.6.2): + storybook@8.6.15(prettier@3.7.4): dependencies: - '@storybook/core': 8.6.14(prettier@3.6.2)(storybook@8.6.14(prettier@3.6.2)) + '@storybook/core': 8.6.15(prettier@3.7.4)(storybook@8.6.15(prettier@3.7.4)) optionalDependencies: - prettier: 3.6.2 + prettier: 3.7.4 transitivePeerDependencies: - bufferutil - supports-color @@ -4587,10 +5073,17 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tabbable@6.4.0: {} + term-size@2.2.1: {} tiny-invariant@1.3.3: {} + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + tinyrainbow@1.2.0: {} tinyspy@3.0.2: {} @@ -4599,7 +5092,7 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@2.1.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -4617,13 +5110,13 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.47.0(eslint@9.39.1)(typescript@5.9.3): + typescript-eslint@8.51.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) - eslint: 9.39.1 + '@typescript-eslint/eslint-plugin': 8.51.0(@typescript-eslint/parser@8.51.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4641,9 +5134,9 @@ snapshots: acorn: 8.15.0 webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.1.4(browserslist@4.28.0): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -4651,6 +5144,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.6.0(react@19.2.3): + dependencies: + react: 19.2.3 + util@0.12.5: dependencies: inherits: 2.0.4 @@ -4661,13 +5158,13 @@ snapshots: uuid@9.0.1: {} - vite-node@3.2.4(@types/node@24.10.1): + vite-node@3.2.4(@types/node@24.10.4): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 5.4.21(@types/node@24.10.1) + vite: 5.4.21(@types/node@24.10.4) transitivePeerDependencies: - '@types/node' - less @@ -4679,13 +5176,13 @@ snapshots: - supports-color - terser - vite@5.4.21(@types/node@24.10.1): + vite@5.4.21(@types/node@24.10.4): dependencies: esbuild: 0.21.5 postcss: 8.5.6 - rollup: 4.53.3 + rollup: 4.55.1 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.4 fsevents: 2.3.3 webpack-virtual-modules@0.6.2: {} From f622a582a9a0dd257dbfaa43182ab6ae212b0ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Tue, 6 Jan 2026 01:15:50 +0900 Subject: [PATCH 03/26] feat(Button): migrate to @base-ui/react for better accessibility --- src/components/Button/Button.tsx | 17 ++++++++++++++--- tsconfig.build.json | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 58efcf3..6b20a8a 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { Button as BaseButton } from "@base-ui/react/button"; import clsx from "clsx"; import * as styles from "./Button.css"; import { lightTheme, type ColorIntent } from "@/tokens"; @@ -10,7 +11,7 @@ export type ButtonIntent = ColorIntent; export type ButtonRounded = "small" | "medium" | "large"; export interface ButtonProps - extends React.ButtonHTMLAttributes { + extends Omit, "color"> { variant?: ButtonVariant; size?: ButtonSize; intent?: ButtonIntent; @@ -19,6 +20,16 @@ export interface ButtonProps disabled?: boolean; } +/** + * Button component built on Base UI + * + * @example + * ```tsx + * + * ``` + */ export const Button = ({ variant = "solid", size = "medium", @@ -34,7 +45,7 @@ export const Button = ({ const themeClass = themeContext?.themeClass ?? lightTheme; return ( - + ); }; diff --git a/tsconfig.build.json b/tsconfig.build.json index eccb23e..a212602 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -7,6 +7,6 @@ "noEmit": false }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.test.tsx", "src"] + "exclude": ["node_modules", "dist", "**/*.stories.tsx", "**/*.test.tsx"] } From f941e1b44cf0d12efd56cb8fe18f77e5f4b53555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Tue, 6 Jan 2026 01:17:06 +0900 Subject: [PATCH 04/26] test(Button): add comprehensive test suite with vitest --- package.json | 11 +- pnpm-lock.yaml | 714 ++++++++++++++++++++++++++ src/components/Button/Button.spec.tsx | 183 +++++++ src/components/Button/Button.tsx | 2 +- vitest.config.ts | 20 + vitest.setup.ts | 2 + 6 files changed, 930 insertions(+), 2 deletions(-) create mode 100644 src/components/Button/Button.spec.tsx create mode 100644 vitest.config.ts create mode 100644 vitest.setup.ts diff --git a/package.json b/package.json index 72b5636..fe5755a 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,9 @@ "build": "vite build && tsc --project tsconfig.build.json", "build:lib": "pnpm run build", "lint": "eslint .", + "test": "vitest", + "test:ui": "vitest --ui", + "test:run": "vitest run", "storybook": "storybook dev -p 6006", "build-storybook": "pnpm run sync-readme && storybook build", "sync-readme": "node scripts/sync-readme.js", @@ -69,6 +72,9 @@ "@storybook/react": "^8", "@storybook/react-vite": "^8", "@storybook/test": "^8", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.1", + "@testing-library/user-event": "^14.6.1", "@types/node": "^24.10.1", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", @@ -76,18 +82,21 @@ "@vanilla-extract/recipes": "^0.5.7", "@vanilla-extract/vite-plugin": "^5.1.1", "@vitejs/plugin-react": "^5.1.0", + "@vitest/ui": "^4.0.16", "eslint": "^9.39.1", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-storybook": "^10.0.7", "globals": "^16.5.0", + "jsdom": "^27.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", "storybook": "^8", "storybook-dark-mode": "^4.0.2", "typescript": "~5.9.3", "typescript-eslint": "^8.46.3", - "vite": "^5.4.11" + "vite": "^5.4.11", + "vitest": "^4.0.16" }, "eslintConfig": { "extends": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0755bef..076315f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,15 @@ importers: '@storybook/test': specifier: ^8 version: 8.6.15(storybook@8.6.15(prettier@3.7.4)) + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.1 + version: 16.3.1(@testing-library/dom@10.4.0)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.0) '@types/node': specifier: ^24.10.1 version: 24.10.4 @@ -66,6 +75,9 @@ importers: '@vitejs/plugin-react': specifier: ^5.1.0 version: 5.1.2(vite@5.4.21(@types/node@24.10.4)) + '@vitest/ui': + specifier: ^4.0.16 + version: 4.0.16(vitest@4.0.16) eslint: specifier: ^9.39.1 version: 9.39.2 @@ -81,6 +93,9 @@ importers: globals: specifier: ^16.5.0 version: 16.5.0 + jsdom: + specifier: ^27.4.0 + version: 27.4.0 react: specifier: ^19.0.0 version: 19.2.3 @@ -102,12 +117,27 @@ importers: vite: specifier: ^5.4.11 version: 5.4.21(@types/node@24.10.4) + vitest: + specifier: ^4.0.16 + version: 4.0.16(@types/node@24.10.4)(@vitest/ui@4.0.16)(jsdom@27.4.0) packages: + '@acemir/cssom@0.9.30': + resolution: {integrity: sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==} + '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@asamuzakjp/css-color@4.1.1': + resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} + + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -277,6 +307,38 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.22': + resolution: {integrity: sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==} + engines: {node: '>=18'} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} @@ -768,6 +830,15 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.8.0': + resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@exodus/crypto': ^1.0.0-rc.4 + peerDependenciesMeta: + '@exodus/crypto': + optional: true + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -865,6 +936,9 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@rolldown/pluginutils@1.0.0-beta.53': resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} @@ -1002,6 +1076,9 @@ packages: cpu: [x64] os: [win32] + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@storybook/addon-a11y@8.6.15': resolution: {integrity: sha512-hNSI28z1PCu7/mQ+skVHX+PvLwnLLiM4d+Ecr0hhZLJDwa5JoZrnszSrCoGadSzaRe270kwjYoEwgX9MGXaxrA==} peerDependencies: @@ -1233,12 +1310,37 @@ packages: resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.1': + resolution: {integrity: sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@testing-library/user-event@14.5.2': resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -1254,6 +1356,12 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/doctrine@0.0.9': resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} @@ -1379,21 +1487,55 @@ packages: '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + '@vitest/expect@4.0.16': + resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==} + + '@vitest/mocker@4.0.16': + resolution: {integrity: sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@2.0.5': resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} '@vitest/pretty-format@2.1.9': resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + '@vitest/pretty-format@4.0.16': + resolution: {integrity: sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==} + + '@vitest/runner@4.0.16': + resolution: {integrity: sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==} + + '@vitest/snapshot@4.0.16': + resolution: {integrity: sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==} + '@vitest/spy@2.0.5': resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/spy@4.0.16': + resolution: {integrity: sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==} + + '@vitest/ui@4.0.16': + resolution: {integrity: sha512-rkoPH+RqWopVxDnCBE/ysIdfQ2A7j1eDmW8tCxxrR9nnFBa9jKf86VgsSAzxBd1x+ny0GC4JgiD3SNfRHv3pOg==} + peerDependencies: + vitest: 4.0.16 + '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} '@vitest/utils@2.1.9': resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@vitest/utils@4.0.16': + resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1404,6 +1546,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1479,6 +1625,9 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -1524,6 +1673,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -1567,6 +1720,10 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} @@ -1579,9 +1736,17 @@ packages: engines: {node: '>=4'} hasBin: true + cssstyle@5.3.6: + resolution: {integrity: sha512-legscpSpgSAeGEe0TNcai97DKt9Vd9AsAdOL7Uoetb52Ar/8eJm3LIa39qpv8wWzLFlNG4vVvppQM+teaMPj3A==} + engines: {node: '>=20'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-urls@6.0.0: + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} + engines: {node: '>=20'} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1591,6 +1756,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + dedent@1.7.1: resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} peerDependencies: @@ -1663,6 +1831,10 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1783,6 +1955,10 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -1811,6 +1987,9 @@ packages: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1924,6 +2103,18 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true @@ -1992,6 +2183,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2036,6 +2230,15 @@ packages: resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} engines: {node: '>=12.0.0'} + jsdom@27.4.0: + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -2088,6 +2291,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -2109,6 +2316,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + media-query-parser@2.0.2: resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} @@ -2151,6 +2361,10 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2165,6 +2379,9 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -2214,6 +2431,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2339,6 +2559,10 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} @@ -2377,6 +2601,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -2401,10 +2629,17 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -2423,6 +2658,12 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + storybook-dark-mode@4.0.2: resolution: {integrity: sha512-zjcwwQ01R5t1VsakA6alc2JDIRVtavryW8J3E3eKLDIlAMcvsgtpxlelWkZs2cuNspk6Z10XzhQVrUWtYc3F0w==} @@ -2475,6 +2716,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tabbable@6.4.0: resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} @@ -2485,6 +2729,13 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -2493,14 +2744,37 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} + + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -2605,9 +2879,99 @@ packages: terser: optional: true + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.16: + resolution: {integrity: sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.16 + '@vitest/browser-preview': 4.0.16 + '@vitest/browser-webdriverio': 4.0.16 + '@vitest/ui': 4.0.16 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} + which-typed-array@1.1.19: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} @@ -2617,6 +2981,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2641,6 +3010,13 @@ packages: utf-8-validate: optional: true + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -2650,8 +3026,28 @@ packages: snapshots: + '@acemir/cssom@0.9.30': {} + '@adobe/css-tools@4.4.4': {} + '@asamuzakjp/css-color@4.1.1': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 11.2.4 + + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.4 + + '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -2940,6 +3336,28 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.22': {} + + '@csstools/css-tokenizer@3.0.4': {} + '@emotion/hash@0.9.2': {} '@esbuild/aix-ppc64@0.21.5': @@ -3213,6 +3631,8 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.8.0': {} + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -3322,6 +3742,8 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@polka/url@1.0.0-next.29': {} + '@rolldown/pluginutils@1.0.0-beta.53': {} '@rollup/pluginutils@5.3.0(rollup@4.55.1)': @@ -3407,6 +3829,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.55.1': optional: true + '@standard-schema/spec@1.1.0': {} + '@storybook/addon-a11y@8.6.15(storybook@8.6.15(prettier@3.7.4))': dependencies: '@storybook/addon-highlight': 8.6.15(storybook@8.6.15(prettier@3.7.4)) @@ -3712,10 +4136,33 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.1(@testing-library/dom@10.4.0)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@testing-library/dom': 10.4.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': dependencies: '@testing-library/dom': 10.4.0 + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': + dependencies: + '@testing-library/dom': 10.4.0 + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -3739,6 +4186,13 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/doctrine@0.0.9': {} '@types/estree@1.0.8': {} @@ -3955,6 +4409,23 @@ snapshots: chai: 5.3.3 tinyrainbow: 1.2.0 + '@vitest/expect@4.0.16': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + chai: 6.2.2 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.16(vite@7.3.0(@types/node@24.10.4))': + dependencies: + '@vitest/spy': 4.0.16 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.0(@types/node@24.10.4) + '@vitest/pretty-format@2.0.5': dependencies: tinyrainbow: 1.2.0 @@ -3963,10 +4434,38 @@ snapshots: dependencies: tinyrainbow: 1.2.0 + '@vitest/pretty-format@4.0.16': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.16': + dependencies: + '@vitest/utils': 4.0.16 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.16': + dependencies: + '@vitest/pretty-format': 4.0.16 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@2.0.5': dependencies: tinyspy: 3.0.2 + '@vitest/spy@4.0.16': {} + + '@vitest/ui@4.0.16(vitest@4.0.16)': + dependencies: + '@vitest/utils': 4.0.16 + fflate: 0.8.2 + flatted: 3.3.3 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vitest: 4.0.16(@types/node@24.10.4)(@vitest/ui@4.0.16)(jsdom@27.4.0) + '@vitest/utils@2.0.5': dependencies: '@vitest/pretty-format': 2.0.5 @@ -3980,12 +4479,19 @@ snapshots: loupe: 3.2.1 tinyrainbow: 1.2.0 + '@vitest/utils@4.0.16': + dependencies: + '@vitest/pretty-format': 4.0.16 + tinyrainbow: 3.0.3 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + agent-base@7.1.4: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -4045,6 +4551,10 @@ snapshots: dependencies: is-windows: 1.0.2 + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -4099,6 +4609,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.2: {} + chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -4135,18 +4647,37 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + css-what@6.2.2: {} css.escape@1.5.1: {} cssesc@3.0.0: {} + cssstyle@5.3.6: + dependencies: + '@asamuzakjp/css-color': 4.1.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.22 + css-tree: 3.1.0 + lru-cache: 11.2.4 + csstype@3.2.3: {} + data-urls@6.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + debug@4.4.3: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + dedent@1.7.1: {} deep-eql@5.0.2: {} @@ -4200,6 +4731,8 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + entities@6.0.1: {} + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -4403,6 +4936,8 @@ snapshots: '@types/node': 24.10.4 require-like: 0.1.2 + expect-type@1.3.0: {} + extendable-error@0.1.7: {} fast-deep-equal@3.1.3: {} @@ -4427,6 +4962,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.8.2: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4550,6 +5087,26 @@ snapshots: dependencies: function-bind: 1.1.2 + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.8.0 + transitivePeerDependencies: + - '@exodus/crypto' + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-id@4.1.3: {} iconv-lite@0.7.1: @@ -4602,6 +5159,8 @@ snapshots: is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -4646,6 +5205,34 @@ snapshots: jsdoc-type-pratt-parser@4.8.0: {} + jsdom@27.4.0: + dependencies: + '@acemir/cssom': 0.9.30 + '@asamuzakjp/dom-selector': 6.7.6 + '@exodus/bytes': 1.8.0 + cssstyle: 5.3.6 + data-urls: 6.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@exodus/crypto' + - bufferutil + - supports-color + - utf-8-validate + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -4687,6 +5274,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.4: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -4705,6 +5294,8 @@ snapshots: math-intrinsics@1.1.0: {} + mdn-data@2.12.2: {} + media-query-parser@2.0.2: dependencies: '@babel/runtime': 7.28.4 @@ -4745,6 +5336,8 @@ snapshots: mri@1.2.0: {} + mrmime@2.0.1: {} + ms@2.1.3: {} nanoid@3.3.11: {} @@ -4753,6 +5346,8 @@ snapshots: node-releases@2.0.27: {} + obug@2.1.1: {} + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -4804,6 +5399,10 @@ snapshots: dependencies: callsites: 3.1.0 + parse5@8.0.0: + dependencies: + entities: 6.0.1 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4917,6 +5516,8 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + require-from-string@2.0.2: {} + require-like@0.1.2: {} reselect@5.1.1: {} @@ -4976,6 +5577,10 @@ snapshots: safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.27.0: {} semver@6.3.1: {} @@ -4997,8 +5602,16 @@ snapshots: shebang-regex@3.0.0: {} + siginfo@2.0.0: {} + signal-exit@4.1.0: {} + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + slash@3.0.0: {} source-map-js@1.2.1: {} @@ -5012,6 +5625,10 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + storybook-dark-mode@4.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@8.6.15(prettier@3.7.4)): dependencies: '@storybook/components': 8.6.14(storybook@8.6.15(prettier@3.7.4)) @@ -5073,12 +5690,18 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + tabbable@6.4.0: {} term-size@2.2.1: {} tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + + tinyexec@1.0.2: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -5086,12 +5709,30 @@ snapshots: tinyrainbow@1.2.0: {} + tinyrainbow@3.0.3: {} + tinyspy@3.0.2: {} + tldts-core@7.0.19: {} + + tldts@7.0.19: + dependencies: + tldts-core: 7.0.19 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + totalist@3.0.1: {} + + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.19 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -5185,8 +5826,72 @@ snapshots: '@types/node': 24.10.4 fsevents: 2.3.3 + vite@7.3.0(@types/node@24.10.4): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.55.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.4 + fsevents: 2.3.3 + + vitest@4.0.16(@types/node@24.10.4)(@vitest/ui@4.0.16)(jsdom@27.4.0): + dependencies: + '@vitest/expect': 4.0.16 + '@vitest/mocker': 4.0.16(vite@7.3.0(@types/node@24.10.4)) + '@vitest/pretty-format': 4.0.16 + '@vitest/runner': 4.0.16 + '@vitest/snapshot': 4.0.16 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.0(@types/node@24.10.4) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.10.4 + '@vitest/ui': 4.0.16(vitest@4.0.16) + jsdom: 27.4.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + webpack-virtual-modules@0.6.2: {} + whatwg-mimetype@4.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.1 + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -5201,6 +5906,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: @@ -5217,6 +5927,10 @@ snapshots: ws@8.18.3: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + yallist@3.1.1: {} yocto-queue@0.1.0: {} diff --git a/src/components/Button/Button.spec.tsx b/src/components/Button/Button.spec.tsx new file mode 100644 index 0000000..e0c17df --- /dev/null +++ b/src/components/Button/Button.spec.tsx @@ -0,0 +1,183 @@ +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Button } from "./Button"; + +describe("Button", () => { + describe("Rendering", () => { + it("renders children correctly", () => { + render(); + expect(screen.getByRole("button")).toHaveTextContent("Click me"); + }); + + it("renders with default props", () => { + render(); + const button = screen.getByRole("button"); + expect(button).toBeInTheDocument(); + }); + + it("applies custom className", () => { + render(); + const button = screen.getByRole("button"); + expect(button).toHaveClass("custom-class"); + }); + }); + + describe("Variants", () => { + it("renders solid variant", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("renders outline variant", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("renders ghost variant", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("renders weak variant", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + }); + + describe("Sizes", () => { + it("renders small size", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("renders medium size", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("renders large size", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("renders xlarge size", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + }); + + describe("Intents", () => { + const intents = [ + "primary", + "secondary", + "success", + "warning", + "danger", + "neutral", + ] as const; + + intents.forEach((intent) => { + it(`renders ${intent} intent`, () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + }); + }); + + describe("States", () => { + it("renders disabled button", () => { + render(); + const button = screen.getByRole("button"); + expect(button).toBeDisabled(); + }); + + it("renders fullWidth button", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + }); + + describe("Interactions", () => { + it("calls onClick handler when clicked", async () => { + const handleClick = vi.fn(); + const user = userEvent.setup(); + + render(); + const button = screen.getByRole("button"); + + await user.click(button); + expect(handleClick).toHaveBeenCalledTimes(1); + }); + + it("does not call onClick when disabled", async () => { + const handleClick = vi.fn(); + const user = userEvent.setup(); + + render( + + ); + const button = screen.getByRole("button"); + + await user.click(button); + expect(handleClick).not.toHaveBeenCalled(); + }); + + it("handles multiple clicks", async () => { + const handleClick = vi.fn(); + const user = userEvent.setup(); + + render(); + const button = screen.getByRole("button"); + + await user.tripleClick(button); + expect(handleClick).toHaveBeenCalledTimes(3); + }); + }); + + describe("Accessibility", () => { + it("has button role", () => { + render(); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + it("accepts aria-label", () => { + render(); + expect(screen.getByLabelText("Custom label")).toBeInTheDocument(); + }); + + it("accepts aria-disabled", () => { + render(); + const button = screen.getByRole("button"); + expect(button).toHaveAttribute("aria-disabled", "true"); + }); + + it("can be focused with keyboard", () => { + render(); + const button = screen.getByRole("button"); + button.focus(); + expect(button).toHaveFocus(); + }); + }); + + describe("HTML Attributes", () => { + it("accepts type attribute", () => { + render(); + expect(screen.getByRole("button")).toHaveAttribute("type", "submit"); + }); + + it("accepts data attributes", () => { + render(); + expect(screen.getByTestId("custom-button")).toBeInTheDocument(); + }); + + it("forwards ref", () => { + const ref = vi.fn(); + render(); + expect(ref).toHaveBeenCalled(); + }); + }); +}); + diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 6b20a8a..438609e 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -22,7 +22,7 @@ export interface ButtonProps /** * Button component built on Base UI - * + * * @example * ```tsx * + )} )} @@ -200,9 +152,7 @@ export const Dialog = ({ : footer} )} - - + + ); - - return createPortal(dialogContent, document.body); }; From 392727ef9a7a311a0aad96fd589bbda86377b189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:13:42 +0900 Subject: [PATCH 11/26] test(Dialog): add comprehensive test suite --- src/components/Dialog/Dialog.spec.tsx | 419 ++++++++++++++++++++++++++ src/components/Dialog/Dialog.tsx | 24 +- 2 files changed, 432 insertions(+), 11 deletions(-) create mode 100644 src/components/Dialog/Dialog.spec.tsx diff --git a/src/components/Dialog/Dialog.spec.tsx b/src/components/Dialog/Dialog.spec.tsx new file mode 100644 index 0000000..6d47225 --- /dev/null +++ b/src/components/Dialog/Dialog.spec.tsx @@ -0,0 +1,419 @@ +import { describe, it, expect, vi } from "vitest"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Dialog } from "./Dialog"; +import React from "react"; + +describe("Dialog", () => { + describe("Rendering", () => { + it("renders when open", () => { + render( + {}}> +

Dialog content

+
+ ); + expect(screen.getByRole("dialog")).toBeInTheDocument(); + expect(screen.getByText("Dialog content")).toBeInTheDocument(); + }); + + it("does not render when closed", () => { + render( + {}}> +

Dialog content

+
+ ); + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + }); + + it("renders with title", () => { + render( + {}} title="Test Title"> +

Content

+
+ ); + expect(screen.getByText("Test Title")).toBeInTheDocument(); + }); + + it("renders with description", () => { + render( + {}} + title="Title" + description="Test Description" + > +

Content

+
+ ); + expect(screen.getByText("Test Description")).toBeInTheDocument(); + }); + + it("renders with close button by default", () => { + render( + {}}> +

Content

+
+ ); + // Dialog has a header with close functionality by default + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + + it("hides close button when showCloseButton is false", () => { + const { container } = render( + {}} showCloseButton={false}> +

Content

+
+ ); + const closeButton = container.querySelector('.Dialog_closeButton__8n7xxog'); + expect(closeButton).not.toBeInTheDocument(); + }); + + it("applies custom className", () => { + render( + {}} + className="custom-dialog" + > +

Content

+
+ ); + const dialog = screen.getByRole("dialog"); + expect(dialog).toHaveClass("custom-dialog"); + }); + }); + + describe("Sizes", () => { + const sizes = ["small", "medium", "large", "xlarge", "full"] as const; + + sizes.forEach((size) => { + it(`renders ${size} size`, () => { + render( + {}} size={size}> +

Content

+
+ ); + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + }); + }); + + describe("Rounded Variants", () => { + const rounded = ["small", "medium", "large"] as const; + + rounded.forEach((variant) => { + it(`renders ${variant} rounded`, () => { + render( + {}} rounded={variant}> +

Content

+
+ ); + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + }); + }); + + describe("Actions", () => { + it("renders action buttons", () => { + const handleAction1 = vi.fn(); + const handleAction2 = vi.fn(); + + render( + {}} + actions={[ + { label: "Cancel", onClick: handleAction1, variant: "outline" }, + { label: "Confirm", onClick: handleAction2 }, + ]} + > +

Content

+
+ ); + + expect(screen.getByText("Cancel")).toBeInTheDocument(); + expect(screen.getByText("Confirm")).toBeInTheDocument(); + }); + + it("calls action onClick handlers", async () => { + const handleAction = vi.fn(); + const user = userEvent.setup(); + + render( + {}} + actions={[{ label: "Action", onClick: handleAction }]} + > +

Content

+
+ ); + + await user.click(screen.getByText("Action")); + expect(handleAction).toHaveBeenCalledTimes(1); + }); + + it("disables action buttons when specified", () => { + render( + {}} + actions={[{ label: "Disabled", onClick: () => {}, disabled: true }]} + > +

Content

+
+ ); + + const button = screen.getByText("Disabled"); + expect(button).toBeDisabled(); + }); + + it("renders custom footer instead of actions", () => { + render( + {}} + footer={
Custom Footer
} + > +

Content

+
+ ); + + expect(screen.getByText("Custom Footer")).toBeInTheDocument(); + }); + }); + + describe("Interactions", () => { + it("calls onOpenChange when close button is clicked", async () => { + const handleOpenChange = vi.fn(); + const user = userEvent.setup(); + + const { container } = render( + +

Content

+
+ ); + + const closeButton = container.querySelector('.Dialog_closeButton__8n7xxog') as HTMLElement; + await user.click(closeButton); + + await waitFor(() => { + expect(handleOpenChange).toHaveBeenCalledWith(false); + }); + }); + + it("calls onClose when dialog is closed", async () => { + const handleClose = vi.fn(); + const handleOpenChange = vi.fn(); + const user = userEvent.setup(); + + const { container } = render( + +

Content

+
+ ); + + const closeButton = container.querySelector('.Dialog_closeButton__8n7xxog') as HTMLElement; + await user.click(closeButton); + + await waitFor(() => { + expect(handleClose).toHaveBeenCalled(); + }); + }); + + it("closes on Escape key when closeOnEscape is true", async () => { + const handleOpenChange = vi.fn(); + const user = userEvent.setup(); + + render( + +

Content

+
+ ); + + await user.keyboard("{Escape}"); + + await waitFor(() => { + expect(handleOpenChange).toHaveBeenCalledWith(false); + }); + }); + + it("remains open when closeOnEscape is false", () => { + const handleOpenChange = vi.fn(); + + render( + +

Content

+
+ ); + + // Dialog should be open + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + }); + + describe("Controlled Behavior", () => { + it("works in controlled mode", async () => { + const TestComponent = () => { + const [open, setOpen] = React.useState(false); + return ( + <> + + +

Dialog content

+
+ + ); + }; + + const user = userEvent.setup(); + render(); + + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + + await user.click(screen.getByText("Open Dialog")); + + await waitFor(() => { + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + }); + }); + + describe("Accessibility", () => { + it("has dialog role", () => { + render( + {}}> +

Content

+
+ ); + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + + it("has aria-labelledby when title is provided", () => { + render( + {}} title="Dialog Title"> +

Content

+
+ ); + const dialog = screen.getByRole("dialog"); + expect(dialog).toHaveAttribute("aria-labelledby"); + }); + + it("has aria-describedby when description is provided", () => { + render( + {}} + title="Title" + description="Description" + > +

Content

+
+ ); + const dialog = screen.getByRole("dialog"); + expect(dialog).toHaveAttribute("aria-describedby"); + }); + + it("supports dialog interactions", () => { + render( + {}}> +

Content

+
+ ); + const dialog = screen.getByRole("dialog"); + expect(dialog).toBeInTheDocument(); + expect(dialog).toBeVisible(); + }); + + it("traps focus within dialog when open", async () => { + const user = userEvent.setup(); + + render( + {}} + title="Dialog" + actions={[ + { label: "Cancel", onClick: () => {} }, + { label: "Confirm", onClick: () => {} }, + ]} + > +

Content

+
+ ); + + const cancelButton = screen.getByText("Cancel"); + const confirmButton = screen.getByText("Confirm"); + + // Tab through dialog elements + await user.tab(); + // One of the buttons should have focus + const hasButtonFocus = cancelButton.matches(':focus') || confirmButton.matches(':focus'); + expect(hasButtonFocus || document.activeElement === cancelButton || document.activeElement === confirmButton).toBe(true); + }); + }); + + describe("Portal", () => { + it("renders dialog outside of parent DOM hierarchy", () => { + const { container } = render( +
+ {}}> +

Content

+
+
+ ); + + const dialog = screen.getByRole("dialog"); + const parent = container.querySelector('[data-testid="parent"]'); + + // Dialog should not be a descendant of parent + expect(parent).not.toContainElement(dialog); + }); + }); + + describe("Multiple Actions", () => { + it("renders multiple actions with different variants and intents", () => { + render( + {}} + actions={[ + { + label: "Delete", + onClick: () => {}, + variant: "solid", + intent: "danger", + }, + { + label: "Cancel", + onClick: () => {}, + variant: "outline", + intent: "neutral", + }, + { + label: "Save", + onClick: () => {}, + variant: "solid", + intent: "primary", + }, + ]} + > +

Content

+
+ ); + + expect(screen.getByText("Delete")).toBeInTheDocument(); + expect(screen.getByText("Cancel")).toBeInTheDocument(); + expect(screen.getByText("Save")).toBeInTheDocument(); + }); + }); +}); + diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 5ce2e63..689ce63 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -86,16 +86,17 @@ export const Dialog = ({ disablePointerDismissal={!closeOnOverlayClick} modal={closeOnEscape} > - - + + + {(title || showCloseButton) && (
@@ -152,7 +153,8 @@ export const Dialog = ({ : footer}
)} - + + ); }; From 9562939dd8a43d6b5636970b798a47dd360a8b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:15:51 +0900 Subject: [PATCH 12/26] feat(Dropdown): migrate to @base-ui/react for better accessibility --- src/components/Dropdown/Dropdown.tsx | 274 +++++++++------------------ 1 file changed, 90 insertions(+), 184 deletions(-) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index b186949..3904a1a 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState, useCallback } from "react"; +import { Select as BaseSelect } from "@base-ui/react/select"; import clsx from "clsx"; import * as styles from "./Dropdown.css"; import { lightTheme, type ColorIntent } from "@/tokens"; @@ -17,7 +17,8 @@ export interface DropdownOption { export interface DropdownProps { options: DropdownOption[]; value?: string; - onChange?: (value: string) => void; + defaultValue?: string; + onValueChange?: (value: string | null) => void; placeholder?: string; size?: DropdownSize; rounded?: DropdownRounded; @@ -25,12 +26,31 @@ export interface DropdownProps { fullWidth?: boolean; disabled?: boolean; className?: string; + name?: string; + required?: boolean; } +/** + * Dropdown component built on Base UI Select + * + * @example + * ```tsx + * + * ``` + */ export const Dropdown = ({ options, value, - onChange, + defaultValue, + onValueChange, placeholder = "Select an option", size = "medium", rounded = "medium", @@ -38,197 +58,83 @@ export const Dropdown = ({ fullWidth = false, disabled = false, className, + name, + required, }: DropdownProps) => { - const [isOpen, setIsOpen] = useState(false); - const [isClosing, setIsClosing] = useState(false); - const [focusedIndex, setFocusedIndex] = useState(-1); - const containerRef = useRef(null); - const menuRef = useRef(null); const themeContext = useTheme(); const themeClass = themeContext?.themeClass ?? lightTheme; - const selectedOption = options.find((opt) => opt.value === value); - - const handleClose = useCallback(() => { - setIsClosing(true); - setTimeout(() => { - setIsClosing(false); - setIsOpen(false); - setFocusedIndex(-1); - }, 150); - }, []); - - const handleToggle = useCallback(() => { - if (disabled) return; - if (isOpen) { - handleClose(); - } else { - setIsOpen(true); - setFocusedIndex(-1); - } - }, [isOpen, disabled, handleClose]); - - const handleSelect = useCallback( - (optionValue: string) => { - onChange?.(optionValue); - handleClose(); - }, - [onChange, handleClose] - ); - - useEffect(() => { - if (!isOpen) return; - - const handleClickOutside = (event: MouseEvent) => { - if ( - containerRef.current && - !containerRef.current.contains(event.target as Node) - ) { - handleClose(); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); - }, [isOpen, handleClose]); - - useEffect(() => { - if (!isOpen) return; - - const enabledOptions = options.filter((opt) => !opt.disabled); - - const handleKeyDown = (event: KeyboardEvent) => { - switch (event.key) { - case "Escape": - event.preventDefault(); - handleClose(); - break; - - case "ArrowDown": - event.preventDefault(); - setFocusedIndex((prev) => { - const nextIndex = prev + 1; - return nextIndex >= enabledOptions.length ? 0 : nextIndex; - }); - break; - - case "ArrowUp": - event.preventDefault(); - setFocusedIndex((prev) => { - const nextIndex = prev - 1; - return nextIndex < 0 ? enabledOptions.length - 1 : nextIndex; - }); - break; - - case "Enter": - event.preventDefault(); - if (focusedIndex >= 0 && focusedIndex < enabledOptions.length) { - handleSelect(enabledOptions[focusedIndex].value); - } - break; - - case "Home": - event.preventDefault(); - setFocusedIndex(0); - break; - - case "End": - event.preventDefault(); - setFocusedIndex(enabledOptions.length - 1); - break; - } - }; - - document.addEventListener("keydown", handleKeyDown); - return () => document.removeEventListener("keydown", handleKeyDown); - }, [isOpen, focusedIndex, options, handleSelect, handleClose]); - - useEffect(() => { - if (!isOpen || focusedIndex < 0 || !menuRef.current) return; - - const focusedElement = menuRef.current.children[ - focusedIndex - ] as HTMLElement | null; - if (focusedElement?.scrollIntoView) { - focusedElement.scrollIntoView({ - block: "nearest", - }); - } - }, [focusedIndex, isOpen]); - return ( -
onValueChange?.(newValue)} + disabled={disabled} + name={name} + required={required} > - - - {(isOpen || isClosing) && ( -
- {options.map((option, index) => ( - - ))} -
- )} -
+ + + + + + + + + + {options.map((option) => ( + + {option.label} + + ))} + + + + +
+ ); }; From bf97d86692008007b4623daa0103030eacacde6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:17:53 +0900 Subject: [PATCH 13/26] test(Dropdown): add comprehensive test suite --- src/components/Dropdown/Dropdown.spec.tsx | 374 ++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 src/components/Dropdown/Dropdown.spec.tsx diff --git a/src/components/Dropdown/Dropdown.spec.tsx b/src/components/Dropdown/Dropdown.spec.tsx new file mode 100644 index 0000000..c15775b --- /dev/null +++ b/src/components/Dropdown/Dropdown.spec.tsx @@ -0,0 +1,374 @@ +import { describe, it, expect, vi } from "vitest"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Dropdown, type DropdownOption } from "./Dropdown"; +import React from "react"; + +const mockOptions: DropdownOption[] = [ + { value: "1", label: "Option 1" }, + { value: "2", label: "Option 2" }, + { value: "3", label: "Option 3" }, + { value: "4", label: "Option 4", disabled: true }, +]; + +describe("Dropdown", () => { + describe("Rendering", () => { + it("renders with placeholder", () => { + render( + + ); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + expect(screen.getByText("Select an option")).toBeInTheDocument(); + }); + + it("renders with selected value", () => { + render( + + ); + expect(screen.getByText("Option 2")).toBeInTheDocument(); + }); + + it("applies custom className", () => { + const { container } = render( + + ); + expect(container.querySelector(".custom-class")).toBeInTheDocument(); + }); + + it("renders disabled dropdown", () => { + render(); + const trigger = screen.getByRole("combobox"); + expect(trigger).toHaveAttribute("data-disabled"); + }); + }); + + describe("Sizes", () => { + const sizes = ["small", "medium", "large"] as const; + + sizes.forEach((size) => { + it(`renders ${size} size`, () => { + render(); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + }); + }); + + describe("Rounded Variants", () => { + const rounded = ["small", "medium", "large"] as const; + + rounded.forEach((variant) => { + it(`renders ${variant} rounded`, () => { + render(); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + }); + }); + + describe("Intents", () => { + const intents = [ + "primary", + "secondary", + "success", + "warning", + "danger", + "neutral", + ] as const; + + intents.forEach((intent) => { + it(`renders ${intent} intent`, () => { + render(); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + }); + }); + + describe("Interactions", () => { + it("opens dropdown when clicked", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + await user.click(trigger); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + }); + + it("selects option when clicked", async () => { + const handleChange = vi.fn(); + const user = userEvent.setup(); + + render( + + ); + + const trigger = screen.getByRole("combobox"); + await user.click(trigger); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + + const option2 = await screen.findByRole("option", { name: "Option 2" }); + await user.click(option2); + + await waitFor(() => { + expect(handleChange).toHaveBeenCalledWith("2"); + }); + }); + + it("does not open when disabled", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + await user.click(trigger); + + // Should not open + expect(trigger).toHaveAttribute("aria-expanded", "false"); + }); + + it("does not select disabled option", async () => { + const handleChange = vi.fn(); + const user = userEvent.setup(); + + render( + + ); + + const trigger = screen.getByRole("combobox"); + await user.click(trigger); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + + const option4 = await screen.findByRole("option", { name: "Option 4" }); + expect(option4).toHaveAttribute("data-disabled"); + }); + }); + + describe("Keyboard Navigation", () => { + it("opens dropdown with Enter key", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + trigger.focus(); + await user.keyboard("{Enter}"); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + }); + + it("opens dropdown with Space key", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + trigger.focus(); + await user.keyboard(" "); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + }); + + it("closes dropdown with Escape key", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + await user.click(trigger); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + + await user.keyboard("{Escape}"); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "false"); + }); + }); + }); + + describe("Controlled Mode", () => { + it("works in controlled mode", async () => { + const TestComponent = () => { + const [value, setValue] = React.useState(null); + return ( + + ); + }; + + const user = userEvent.setup(); + render(); + + const trigger = screen.getByRole("combobox"); + expect(trigger).toBeInTheDocument(); + + // Trigger should initially show placeholder + await user.click(trigger); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + + const option1 = await screen.findByRole("option", { name: "Option 1" }); + await user.click(option1); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "false"); + }); + }); + + it("supports defaultValue for uncontrolled mode", () => { + render( + + ); + expect(screen.getByText("Option 2")).toBeInTheDocument(); + }); + }); + + describe("Accessibility", () => { + it("has combobox role", () => { + render(); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + + it("has correct aria-expanded attribute", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + expect(trigger).toHaveAttribute("aria-expanded", "false"); + + await user.click(trigger); + + await waitFor(() => { + expect(trigger).toHaveAttribute("aria-expanded", "true"); + }); + }); + + it("has correct aria-disabled attribute when disabled", () => { + render(); + const trigger = screen.getByRole("combobox"); + expect(trigger).toHaveAttribute("data-disabled"); + }); + + it("options have option role", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + await user.click(trigger); + + await waitFor(async () => { + const options = await screen.findAllByRole("option"); + expect(options).toHaveLength(4); + }); + }); + + it("selected option has correct aria-selected attribute", async () => { + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + await user.click(trigger); + + await waitFor(async () => { + const option2 = await screen.findByRole("option", { name: "Option 2" }); + expect(option2).toHaveAttribute("data-selected"); + }); + }); + }); + + describe("Form Integration", () => { + it("accepts name attribute", () => { + const { container } = render( + + ); + const hiddenInput = container.querySelector( + 'input[name="dropdown-field"]' + ); + expect(hiddenInput).toBeInTheDocument(); + }); + + it("accepts required attribute", () => { + render(); + const trigger = screen.getByRole("combobox"); + // Required attribute is passed to the component + expect(trigger).toBeInTheDocument(); + }); + }); + + describe("Width", () => { + it("renders with fullWidth", () => { + render(); + const trigger = screen.getByRole("combobox"); + expect(trigger).toBeInTheDocument(); + }); + + it("renders with default width", () => { + render(); + const trigger = screen.getByRole("combobox"); + expect(trigger).toBeInTheDocument(); + }); + }); + + describe("Empty Options", () => { + it("renders with empty options array", () => { + render(); + expect(screen.getByText("No options")).toBeInTheDocument(); + }); + }); + + describe("Multiple Options", () => { + it("renders many options", async () => { + const manyOptions: DropdownOption[] = Array.from({ length: 20 }, (_, i) => ({ + value: `${i + 1}`, + label: `Option ${i + 1}`, + })); + + const user = userEvent.setup(); + + render(); + const trigger = screen.getByRole("combobox"); + + await user.click(trigger); + + await waitFor(async () => { + const options = await screen.findAllByRole("option"); + expect(options.length).toBe(20); + }); + }); + }); +}); + From d17b66bd2e9e974225064da3eb3955cf67118a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:27:41 +0900 Subject: [PATCH 14/26] feat(TextField): migrate to @base-ui/react for better form handling --- src/components/TextField/TextField.css.ts | 76 ++++++++++++++++ src/components/TextField/TextField.tsx | 106 +++++++++++----------- 2 files changed, 131 insertions(+), 51 deletions(-) diff --git a/src/components/TextField/TextField.css.ts b/src/components/TextField/TextField.css.ts index ae822a2..589a4a2 100644 --- a/src/components/TextField/TextField.css.ts +++ b/src/components/TextField/TextField.css.ts @@ -2,6 +2,82 @@ import { style, styleVariants } from "@vanilla-extract/css"; import { recipe } from "@vanilla-extract/recipes"; import { themeContract } from "@/tokens"; +export const label = recipe({ + base: { + display: "block", + marginBottom: "4px", + fontFamily: themeContract.typography.fontFamily.sans, + fontWeight: themeContract.typography.fontWeight.medium, + color: themeContract.color.surface.text, + }, + variants: { + size: { + small: { + fontSize: themeContract.typography.fontSize.xsmall, + lineHeight: themeContract.typography.lineHeight.xsmall, + }, + medium: { + fontSize: themeContract.typography.fontSize.small, + lineHeight: themeContract.typography.lineHeight.small, + }, + large: { + fontSize: themeContract.typography.fontSize.medium, + lineHeight: themeContract.typography.lineHeight.medium, + }, + xlarge: { + fontSize: themeContract.typography.fontSize.medium, + lineHeight: themeContract.typography.lineHeight.medium, + }, + }, + }, + defaultVariants: { + size: "medium", + }, +}); + +export const helperText = recipe({ + base: { + display: "block", + marginTop: "4px", + fontFamily: themeContract.typography.fontFamily.sans, + }, + variants: { + size: { + small: { + fontSize: themeContract.typography.fontSize.xsmall, + lineHeight: themeContract.typography.lineHeight.xsmall, + }, + medium: { + fontSize: themeContract.typography.fontSize.xsmall, + lineHeight: themeContract.typography.lineHeight.xsmall, + }, + large: { + fontSize: themeContract.typography.fontSize.small, + lineHeight: themeContract.typography.lineHeight.small, + }, + xlarge: { + fontSize: themeContract.typography.fontSize.small, + lineHeight: themeContract.typography.lineHeight.small, + }, + }, + status: { + default: { + color: themeContract.color.surface.textMuted, + }, + error: { + color: themeContract.color.danger.surface, + }, + success: { + color: themeContract.color.success.surface, + }, + }, + }, + defaultVariants: { + size: "medium", + status: "default", + }, +}); + export const container = style({ display: "flex", flexDirection: "column", diff --git a/src/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx index 32c4ea0..a8861e7 100644 --- a/src/components/TextField/TextField.tsx +++ b/src/components/TextField/TextField.tsx @@ -1,9 +1,9 @@ -import React from "react"; +import { Field } from "@base-ui/react/field"; +import { Input } from "@base-ui/react/input"; import clsx from "clsx"; -import { container, containerVariants, input } from "./TextField.css"; +import { container, containerVariants, input, label, helperText } from "./TextField.css"; import { lightTheme } from "@/tokens"; import { useTheme } from "@/providers"; -import { Text } from "../Text/Text"; export type TextFieldSize = "small" | "medium" | "large" | "xlarge"; export type TextFieldRounded = "small" | "medium" | "large"; @@ -19,80 +19,84 @@ export interface TextFieldProps label?: string; helperText?: string; fullWidth?: boolean; + name?: string; + required?: boolean; + validate?: ( + value: unknown, + formValues: Record + ) => string | string[] | Promise | null; } +/** + * TextField component built on Base UI + * + * @example + * ```tsx + * + * ``` + */ export const TextField = ({ size = "medium", rounded = "medium", variant = "outline", status = "default", - label, - helperText, + label: labelText, + helperText: helperTextContent, fullWidth = false, disabled = false, className, + name, + required, + validate, ...props }: TextFieldProps) => { const themeContext = useTheme(); const themeClass = themeContext?.themeClass ?? lightTheme; - const getHelperTextIntent = (): "inherit" | "danger" | "success" => { - if (status === "error") return "danger"; - if (status === "success") return "success"; - return "inherit"; - }; - - const getLabelSize = (): "xsmall" | "small" | "medium" => { - if (size === "small") return "xsmall"; - if (size === "medium") return "small"; - return "medium"; - }; - - const getHelperTextSize = (): "xsmall" | "small" => { - if (size === "small" || size === "medium") return "xsmall"; - return "small"; - }; - return ( -
- {label && ( - - {label} - + {labelText && ( + + {labelText} + {required && " *"} + )} - ( + + )} {...props} /> - {helperText && ( - - {helperText} - + {helperTextContent && status !== "error" && ( + + {helperTextContent} + )} -
+ + ); }; From ec0dc809147512c753e3eb7fc51e81fbf51eea6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:31:54 +0900 Subject: [PATCH 15/26] test(TextField): add comprehensive test suite --- src/components/TextField/TextField.spec.tsx | 376 ++++++++++++++++++++ src/components/TextField/TextField.tsx | 21 +- 2 files changed, 384 insertions(+), 13 deletions(-) create mode 100644 src/components/TextField/TextField.spec.tsx diff --git a/src/components/TextField/TextField.spec.tsx b/src/components/TextField/TextField.spec.tsx new file mode 100644 index 0000000..6492073 --- /dev/null +++ b/src/components/TextField/TextField.spec.tsx @@ -0,0 +1,376 @@ +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { TextField } from "./TextField"; + +describe("TextField", () => { + describe("Rendering", () => { + it("renders with default props", () => { + render(); + const input = screen.getByPlaceholderText("Enter text"); + expect(input).toBeInTheDocument(); + }); + + it("renders with label", () => { + render(); + expect(screen.getByText("Username")).toBeInTheDocument(); + }); + + it("renders with helperText", () => { + render( + + ); + expect(screen.getByText("This is a helper text")).toBeInTheDocument(); + }); + + it("renders required indicator when required is true", () => { + render(); + expect(screen.getByText(/Email \*/)).toBeInTheDocument(); + }); + + it("renders with custom className", () => { + const { container } = render( + + ); + const fieldRoot = container.querySelector(".custom-class"); + expect(fieldRoot).toBeInTheDocument(); + }); + }); + + describe("Sizes", () => { + it("renders with small size", () => { + render(); + const input = screen.getByPlaceholderText("Small input"); + expect(input).toBeInTheDocument(); + }); + + it("renders with medium size (default)", () => { + render(); + const input = screen.getByPlaceholderText("Medium input"); + expect(input).toBeInTheDocument(); + }); + + it("renders with large size", () => { + render(); + const input = screen.getByPlaceholderText("Large input"); + expect(input).toBeInTheDocument(); + }); + + it("renders with xlarge size", () => { + render(); + const input = screen.getByPlaceholderText("XLarge input"); + expect(input).toBeInTheDocument(); + }); + }); + + describe("Rounded Variants", () => { + it("renders with small rounded", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + + it("renders with medium rounded (default)", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + + it("renders with large rounded", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + }); + + describe("Variants", () => { + it("renders with outline variant (default)", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + + it("renders with filled variant", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + }); + + describe("Status", () => { + it("renders with default status", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + + it("renders with error status", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + + it("renders with success status", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + + it("shows helper text for default status", () => { + render( + + ); + expect(screen.getByText("Default helper text")).toBeInTheDocument(); + }); + + it("shows helper text for success status", () => { + render( + + ); + expect(screen.getByText("Success helper text")).toBeInTheDocument(); + }); + }); + + describe("States", () => { + it("handles disabled state", () => { + render(); + const input = screen.getByPlaceholderText("Disabled input"); + expect(input).toBeDisabled(); + }); + + it("handles fullWidth", () => { + const { container } = render(); + const fieldRoot = container.querySelector("div"); + expect(fieldRoot).toBeInTheDocument(); + }); + }); + + describe("Interactions", () => { + it("handles onChange event", async () => { + const handleChange = vi.fn(); + const user = userEvent.setup(); + + render(); + const input = screen.getByPlaceholderText("test"); + + await user.type(input, "hello"); + expect(handleChange).toHaveBeenCalled(); + }); + + it("handles onFocus event", async () => { + const handleFocus = vi.fn(); + const user = userEvent.setup(); + + render(); + const input = screen.getByPlaceholderText("test"); + + await user.click(input); + expect(handleFocus).toHaveBeenCalledTimes(1); + }); + + it("handles onBlur event", async () => { + const handleBlur = vi.fn(); + const user = userEvent.setup(); + + render(); + const input = screen.getByPlaceholderText("test"); + + await user.click(input); + await user.tab(); + expect(handleBlur).toHaveBeenCalledTimes(1); + }); + + it("accepts user input", async () => { + const user = userEvent.setup(); + render(); + const input = screen.getByPlaceholderText("test") as HTMLInputElement; + + await user.type(input, "test value"); + expect(input.value).toBe("test value"); + }); + }); + + describe("Controlled Component", () => { + it("works as controlled component", async () => { + const handleChange = vi.fn(); + const user = userEvent.setup(); + + const { rerender } = render( + + ); + const input = screen.getByPlaceholderText("test") as HTMLInputElement; + expect(input.value).toBe("initial"); + + await user.type(input, "a"); + expect(handleChange).toHaveBeenCalled(); + + rerender( + + ); + expect(input.value).toBe("updated"); + }); + }); + + describe("Types", () => { + it("renders as input element", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + expect(input.tagName).toBe("INPUT"); + }); + + it("renders with type='email'", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("type", "email"); + }); + + it("renders with type='password'", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("type", "password"); + }); + + it("renders with type='number'", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("type", "number"); + }); + }); + + describe("HTML Attributes", () => { + it("accepts placeholder attribute", () => { + render(); + const input = screen.getByPlaceholderText("Enter your name"); + expect(input).toHaveAttribute("placeholder", "Enter your name"); + }); + + it("accepts name attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("name", "username"); + }); + + it("accepts id attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("id", "custom-id"); + }); + + it("accepts required attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeRequired(); + }); + + it("accepts maxLength attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("maxLength", "10"); + }); + + it("accepts minLength attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("minLength", "3"); + }); + + it("accepts pattern attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("pattern", "[0-9]+"); + }); + + it("accepts autoComplete attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("autoComplete", "email"); + }); + + it("accepts autoFocus attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveFocus(); + }); + + it("accepts readOnly attribute", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toHaveAttribute("readOnly"); + }); + }); + + describe("Accessibility", () => { + it("associates label with input", () => { + render(); + const label = screen.getByText("Email Address"); + const input = screen.getByPlaceholderText("test"); + expect(label).toBeInTheDocument(); + expect(input).toBeInTheDocument(); + }); + + it("has proper ARIA attributes when disabled", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeDisabled(); + }); + + it("has proper ARIA attributes when required", () => { + render(); + const input = screen.getByPlaceholderText("test"); + expect(input).toBeRequired(); + }); + }); + + describe("Form Integration", () => { + it("works within a form", async () => { + const handleSubmit = vi.fn((e) => e.preventDefault()); + const user = userEvent.setup(); + + render( +
+ + + + ); + + const input = screen.getByPlaceholderText("Username"); + await user.type(input, "testuser"); + + const submitButton = screen.getByRole("button", { name: /submit/i }); + await user.click(submitButton); + + expect(handleSubmit).toHaveBeenCalledTimes(1); + }); + }); + + describe("Validation", () => { + it("accepts validate prop", () => { + const validate = (value: unknown) => { + if (typeof value === "string" && value.length < 3) { + return "Must be at least 3 characters"; + } + return null; + }; + + render( + + ); + + const input = screen.getByPlaceholderText("test"); + expect(input).toBeInTheDocument(); + }); + }); +}); + diff --git a/src/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx index a8861e7..ce7f413 100644 --- a/src/components/TextField/TextField.tsx +++ b/src/components/TextField/TextField.tsx @@ -76,19 +76,14 @@ export const TextField = ({ {required && " *"} )} - ( - - )} + {helperTextContent && status !== "error" && ( From 5ba781f4714e05066473235eb9c805a230913f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:38:27 +0900 Subject: [PATCH 16/26] test(Text): add comprehensive test suite --- src/components/Text/Text.spec.tsx | 383 ++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 src/components/Text/Text.spec.tsx diff --git a/src/components/Text/Text.spec.tsx b/src/components/Text/Text.spec.tsx new file mode 100644 index 0000000..71630d6 --- /dev/null +++ b/src/components/Text/Text.spec.tsx @@ -0,0 +1,383 @@ +import { describe, it, expect } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { Text } from "./Text"; + +describe("Text", () => { + describe("Rendering", () => { + it("renders with default props", () => { + render(Hello World); + expect(screen.getByText("Hello World")).toBeInTheDocument(); + }); + + it("renders children correctly", () => { + render(Test Content); + expect(screen.getByText("Test Content")).toBeInTheDocument(); + }); + + it("renders with custom className", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveClass("custom-class"); + }); + }); + + describe("Semantic Elements (as prop)", () => { + it("renders as p by default", () => { + render(Paragraph); + const element = screen.getByText("Paragraph"); + expect(element.tagName).toBe("P"); + }); + + it("renders as h1", () => { + render(Heading 1); + const element = screen.getByText("Heading 1"); + expect(element.tagName).toBe("H1"); + }); + + it("renders as h2", () => { + render(Heading 2); + const element = screen.getByText("Heading 2"); + expect(element.tagName).toBe("H2"); + }); + + it("renders as h3", () => { + render(Heading 3); + const element = screen.getByText("Heading 3"); + expect(element.tagName).toBe("H3"); + }); + + it("renders as h4", () => { + render(Heading 4); + const element = screen.getByText("Heading 4"); + expect(element.tagName).toBe("H4"); + }); + + it("renders as h5", () => { + render(Heading 5); + const element = screen.getByText("Heading 5"); + expect(element.tagName).toBe("H5"); + }); + + it("renders as h6", () => { + render(Heading 6); + const element = screen.getByText("Heading 6"); + expect(element.tagName).toBe("H6"); + }); + + it("renders as span", () => { + render(Span text); + const element = screen.getByText("Span text"); + expect(element.tagName).toBe("SPAN"); + }); + + it("renders as div", () => { + render(Div text); + const element = screen.getByText("Div text"); + expect(element.tagName).toBe("DIV"); + }); + + it("renders as label", () => { + render(Label text); + const element = screen.getByText("Label text"); + expect(element.tagName).toBe("LABEL"); + }); + }); + + describe("Size Variants", () => { + it("renders with xsmall size", () => { + render(XSmall text); + expect(screen.getByText("XSmall text")).toBeInTheDocument(); + }); + + it("renders with small size", () => { + render(Small text); + expect(screen.getByText("Small text")).toBeInTheDocument(); + }); + + it("renders with medium size (default)", () => { + render(Medium text); + expect(screen.getByText("Medium text")).toBeInTheDocument(); + }); + + it("renders with large size", () => { + render(Large text); + expect(screen.getByText("Large text")).toBeInTheDocument(); + }); + + it("renders with xlarge size", () => { + render(XLarge text); + expect(screen.getByText("XLarge text")).toBeInTheDocument(); + }); + + it("renders with display-small size", () => { + render(Display Small); + expect(screen.getByText("Display Small")).toBeInTheDocument(); + }); + + it("renders with display-medium size", () => { + render(Display Medium); + expect(screen.getByText("Display Medium")).toBeInTheDocument(); + }); + + it("renders with display-large size", () => { + render(Display Large); + expect(screen.getByText("Display Large")).toBeInTheDocument(); + }); + }); + + describe("Weight Variants", () => { + it("renders with regular weight (default)", () => { + render(Regular weight); + expect(screen.getByText("Regular weight")).toBeInTheDocument(); + }); + + it("renders with medium weight", () => { + render(Medium weight); + expect(screen.getByText("Medium weight")).toBeInTheDocument(); + }); + + it("renders with semibold weight", () => { + render(Semibold weight); + expect(screen.getByText("Semibold weight")).toBeInTheDocument(); + }); + + it("renders with bold weight", () => { + render(Bold weight); + expect(screen.getByText("Bold weight")).toBeInTheDocument(); + }); + }); + + describe("Intent/Color Variants", () => { + it("renders with inherit intent (default)", () => { + render(Inherit intent); + expect(screen.getByText("Inherit intent")).toBeInTheDocument(); + }); + + it("renders with primary intent", () => { + render(Primary intent); + expect(screen.getByText("Primary intent")).toBeInTheDocument(); + }); + + it("renders with secondary intent", () => { + render(Secondary intent); + expect(screen.getByText("Secondary intent")).toBeInTheDocument(); + }); + + it("renders with success intent", () => { + render(Success intent); + expect(screen.getByText("Success intent")).toBeInTheDocument(); + }); + + it("renders with warning intent", () => { + render(Warning intent); + expect(screen.getByText("Warning intent")).toBeInTheDocument(); + }); + + it("renders with danger intent", () => { + render(Danger intent); + expect(screen.getByText("Danger intent")).toBeInTheDocument(); + }); + + it("renders with neutral intent", () => { + render(Neutral intent); + expect(screen.getByText("Neutral intent")).toBeInTheDocument(); + }); + }); + + describe("Align Variants", () => { + it("renders with left align (default)", () => { + render(Left aligned); + expect(screen.getByText("Left aligned")).toBeInTheDocument(); + }); + + it("renders with center align", () => { + render(Center aligned); + expect(screen.getByText("Center aligned")).toBeInTheDocument(); + }); + + it("renders with right align", () => { + render(Right aligned); + expect(screen.getByText("Right aligned")).toBeInTheDocument(); + }); + }); + + describe("Truncate", () => { + it("renders without truncate by default", () => { + render(Not truncated text); + expect(screen.getByText("Not truncated text")).toBeInTheDocument(); + }); + + it("renders with truncate enabled", () => { + render(This is a very long text that should be truncated); + expect( + screen.getByText("This is a very long text that should be truncated") + ).toBeInTheDocument(); + }); + }); + + describe("Combined Props", () => { + it("renders with multiple props combined", () => { + render( + + Combined props + + ); + const element = screen.getByText("Combined props"); + expect(element.tagName).toBe("H2"); + expect(element).toBeInTheDocument(); + }); + + it("renders with all props combined including truncate", () => { + render( + + All props combined + + ); + const element = screen.getByText("All props combined"); + expect(element.tagName).toBe("SPAN"); + expect(element).toBeInTheDocument(); + }); + }); + + describe("HTML Attributes", () => { + it("accepts id attribute", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveAttribute("id", "custom-id"); + }); + + it("accepts data attributes", () => { + render(Test); + const element = screen.getByTestId("custom-test-id"); + expect(element).toBeInTheDocument(); + }); + + it("accepts aria attributes", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveAttribute("aria-label", "Custom label"); + }); + + it("accepts style attribute", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveStyle({ marginTop: "10px" }); + }); + + it("accepts title attribute", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveAttribute("title", "Tooltip text"); + }); + }); + + describe("Accessibility", () => { + it("renders semantic heading elements for better accessibility", () => { + render(Main Heading); + const heading = screen.getByRole("heading", { level: 1 }); + expect(heading).toHaveTextContent("Main Heading"); + }); + + it("renders with proper semantic structure", () => { + render( +
+ Title + Description +
+ ); + expect(screen.getByRole("heading")).toHaveTextContent("Title"); + }); + + it("supports aria-label for screen readers", () => { + render(Visible text); + const element = screen.getByLabelText("Description text"); + expect(element).toBeInTheDocument(); + }); + }); + + describe("Complex Content", () => { + it("renders nested elements", () => { + render( + + Hello World + + ); + expect(screen.getByText(/Hello/)).toBeInTheDocument(); + expect(screen.getByText("World")).toBeInTheDocument(); + }); + + it("renders multiple text nodes", () => { + render( + + First line +
+ Second line +
+ ); + expect(screen.getByText(/First line/)).toBeInTheDocument(); + expect(screen.getByText(/Second line/)).toBeInTheDocument(); + }); + + it("renders with React fragments", () => { + render( + + <> + Part 1 + Part 2 + + + ); + expect(screen.getByText("Part 1")).toBeInTheDocument(); + expect(screen.getByText("Part 2")).toBeInTheDocument(); + }); + }); + + describe("Edge Cases", () => { + it("renders empty content", () => { + const { container } = render(); + const element = container.querySelector("p"); + expect(element).toBeInTheDocument(); + expect(element).toBeEmptyDOMElement(); + }); + + it("renders with zero as content", () => { + render({0}); + expect(screen.getByText("0")).toBeInTheDocument(); + }); + + it("renders with false as content (should render nothing)", () => { + const { container } = render({false}); + const element = container.querySelector("p"); + expect(element).toBeInTheDocument(); + expect(element).toBeEmptyDOMElement(); + }); + + it("renders with null as content (should render nothing)", () => { + const { container } = render({null}); + const element = container.querySelector("p"); + expect(element).toBeInTheDocument(); + expect(element).toBeEmptyDOMElement(); + }); + + it("renders with undefined as content (should render nothing)", () => { + const { container } = render({undefined}); + const element = container.querySelector("p"); + expect(element).toBeInTheDocument(); + expect(element).toBeEmptyDOMElement(); + }); + }); +}); + From 8012d2409915b7a703e56b47dc86f7908d7a8ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Wed, 7 Jan 2026 00:56:55 +0900 Subject: [PATCH 17/26] test(Badge): add comprehensive test suite --- src/components/Badge/Badge.spec.tsx | 417 ++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 src/components/Badge/Badge.spec.tsx diff --git a/src/components/Badge/Badge.spec.tsx b/src/components/Badge/Badge.spec.tsx new file mode 100644 index 0000000..c9c2b56 --- /dev/null +++ b/src/components/Badge/Badge.spec.tsx @@ -0,0 +1,417 @@ +import { describe, it, expect } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { Badge } from "./Badge"; + +describe("Badge", () => { + describe("Rendering", () => { + it("renders with default props", () => { + render(Badge); + expect(screen.getByText("Badge")).toBeInTheDocument(); + }); + + it("renders children correctly", () => { + render(Test Content); + expect(screen.getByText("Test Content")).toBeInTheDocument(); + }); + + it("renders with custom className", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveClass("custom-class"); + }); + + it("renders as span element", () => { + render(Badge); + const element = screen.getByText("Badge"); + expect(element.tagName).toBe("SPAN"); + }); + }); + + describe("Size Variants", () => { + it("renders with small size", () => { + render(Small); + expect(screen.getByText("Small")).toBeInTheDocument(); + }); + + it("renders with medium size (default)", () => { + render(Medium); + expect(screen.getByText("Medium")).toBeInTheDocument(); + }); + + it("renders with large size", () => { + render(Large); + expect(screen.getByText("Large")).toBeInTheDocument(); + }); + + it("renders with xlarge size", () => { + render(XLarge); + expect(screen.getByText("XLarge")).toBeInTheDocument(); + }); + }); + + describe("Variant Styles", () => { + it("renders with solid variant (default)", () => { + render(Solid); + expect(screen.getByText("Solid")).toBeInTheDocument(); + }); + + it("renders with outline variant", () => { + render(Outline); + expect(screen.getByText("Outline")).toBeInTheDocument(); + }); + + it("renders with subtle variant", () => { + render(Subtle); + expect(screen.getByText("Subtle")).toBeInTheDocument(); + }); + + it("renders with weak variant", () => { + render(Weak); + expect(screen.getByText("Weak")).toBeInTheDocument(); + }); + }); + + describe("Intent/Color Variants", () => { + it("renders with primary intent (default)", () => { + render(Primary); + expect(screen.getByText("Primary")).toBeInTheDocument(); + }); + + it("renders with secondary intent", () => { + render(Secondary); + expect(screen.getByText("Secondary")).toBeInTheDocument(); + }); + + it("renders with success intent", () => { + render(Success); + expect(screen.getByText("Success")).toBeInTheDocument(); + }); + + it("renders with warning intent", () => { + render(Warning); + expect(screen.getByText("Warning")).toBeInTheDocument(); + }); + + it("renders with danger intent", () => { + render(Danger); + expect(screen.getByText("Danger")).toBeInTheDocument(); + }); + + it("renders with neutral intent", () => { + render(Neutral); + expect(screen.getByText("Neutral")).toBeInTheDocument(); + }); + }); + + describe("Rounded Variants", () => { + it("renders with small rounded", () => { + render(Small Rounded); + expect(screen.getByText("Small Rounded")).toBeInTheDocument(); + }); + + it("renders with medium rounded (default)", () => { + render(Medium Rounded); + expect(screen.getByText("Medium Rounded")).toBeInTheDocument(); + }); + + it("renders with large rounded", () => { + render(Large Rounded); + expect(screen.getByText("Large Rounded")).toBeInTheDocument(); + }); + }); + + describe("Dot Indicator", () => { + it("renders without dot by default", () => { + const { container } = render(No Dot); + const dots = container.querySelectorAll("span > span"); + expect(dots).toHaveLength(0); + }); + + it("renders with dot when showDot is true", () => { + const { container } = render(With Dot); + const badge = screen.getByText("With Dot"); + const dot = badge.querySelector("span"); + expect(dot).toBeInTheDocument(); + }); + }); + + describe("Combined Props", () => { + it("renders with all props combined", () => { + render( + + Combined + + ); + expect(screen.getByText("Combined")).toBeInTheDocument(); + }); + + it("renders solid + primary combination", () => { + render( + + Solid Primary + + ); + expect(screen.getByText("Solid Primary")).toBeInTheDocument(); + }); + + it("renders outline + secondary combination", () => { + render( + + Outline Secondary + + ); + expect(screen.getByText("Outline Secondary")).toBeInTheDocument(); + }); + + it("renders subtle + success combination", () => { + render( + + Subtle Success + + ); + expect(screen.getByText("Subtle Success")).toBeInTheDocument(); + }); + + it("renders weak + danger combination", () => { + render( + + Weak Danger + + ); + expect(screen.getByText("Weak Danger")).toBeInTheDocument(); + }); + }); + + describe("HTML Attributes", () => { + it("accepts id attribute", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveAttribute("id", "custom-id"); + }); + + it("accepts data attributes", () => { + render(Test); + const element = screen.getByTestId("custom-test-id"); + expect(element).toBeInTheDocument(); + }); + + it("accepts aria attributes", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveAttribute("aria-label", "Custom label"); + }); + + it("accepts style attribute", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveStyle({ marginLeft: "10px" }); + }); + + it("accepts title attribute", () => { + render(Test); + const element = screen.getByText("Test"); + expect(element).toHaveAttribute("title", "Tooltip text"); + }); + + it("accepts onClick handler", () => { + const handleClick = vitest.fn(); + render(Clickable); + const element = screen.getByText("Clickable"); + element.click(); + expect(handleClick).toHaveBeenCalledTimes(1); + }); + }); + + describe("Content Types", () => { + it("renders text content", () => { + render(Text Badge); + expect(screen.getByText("Text Badge")).toBeInTheDocument(); + }); + + it("renders number content", () => { + render(42); + expect(screen.getByText("42")).toBeInTheDocument(); + }); + + it("renders with zero", () => { + render({0}); + expect(screen.getByText("0")).toBeInTheDocument(); + }); + + it("renders nested elements", () => { + render( + + Bold Text + + ); + expect(screen.getByText("Bold")).toBeInTheDocument(); + expect(screen.getByText(/Text/)).toBeInTheDocument(); + }); + + it("renders with icon", () => { + render( + + βœ“ Success + + ); + expect(screen.getByText("βœ“")).toBeInTheDocument(); + expect(screen.getByText(/Success/)).toBeInTheDocument(); + }); + }); + + describe("Accessibility", () => { + it("has proper role when used as status indicator", () => { + render(Active); + const element = screen.getByText("Active"); + expect(element).toHaveAttribute("role", "status"); + }); + + it("supports aria-label for screen readers", () => { + render(3); + const element = screen.getByLabelText("3 unread messages"); + expect(element).toBeInTheDocument(); + }); + + it("supports aria-describedby", () => { + render( + <> + New + This item is new + + ); + const badge = screen.getByText("New"); + expect(badge).toHaveAttribute("aria-describedby", "badge-desc"); + }); + }); + + describe("Visual Combinations", () => { + it("renders all size + variant combinations", () => { + const sizes: Array<"small" | "medium" | "large" | "xlarge"> = [ + "small", + "medium", + "large", + "xlarge", + ]; + const variants: Array<"solid" | "outline" | "subtle" | "weak"> = [ + "solid", + "outline", + "subtle", + "weak", + ]; + + sizes.forEach((size) => { + variants.forEach((variant) => { + const { container } = render( + + {size}-{variant} + + ); + expect( + screen.getByText(`${size}-${variant}`) + ).toBeInTheDocument(); + container.remove(); + }); + }); + }); + + it("renders all intent colors with solid variant", () => { + const intents: Array< + "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" + > = ["primary", "secondary", "success", "warning", "danger", "neutral"]; + + intents.forEach((intent) => { + const { container } = render( + + {intent} + + ); + expect(screen.getByText(intent)).toBeInTheDocument(); + container.remove(); + }); + }); + + it("renders all intent colors with outline variant", () => { + const intents: Array< + "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" + > = ["primary", "secondary", "success", "warning", "danger", "neutral"]; + + intents.forEach((intent) => { + const { container } = render( + + {intent} + + ); + expect(screen.getByText(intent)).toBeInTheDocument(); + container.remove(); + }); + }); + }); + + describe("Use Cases", () => { + it("renders as status badge", () => { + render( + + Active + + ); + expect(screen.getByText("Active")).toBeInTheDocument(); + }); + + it("renders as notification count", () => { + render( + + 99+ + + ); + expect(screen.getByText("99+")).toBeInTheDocument(); + }); + + it("renders as category tag", () => { + render( + + Technology + + ); + expect(screen.getByText("Technology")).toBeInTheDocument(); + }); + + it("renders as feature badge", () => { + render( + + NEW + + ); + expect(screen.getByText("NEW")).toBeInTheDocument(); + }); + }); + + describe("Edge Cases", () => { + it("renders with very long text", () => { + const longText = "This is a very long badge text that might overflow"; + render({longText}); + expect(screen.getByText(longText)).toBeInTheDocument(); + }); + + it("renders with single character", () => { + render(A); + expect(screen.getByText("A")).toBeInTheDocument(); + }); + + it("renders with special characters", () => { + render(β˜… Special β˜…); + expect(screen.getByText("β˜… Special β˜…")).toBeInTheDocument(); + }); + + it("renders with emoji", () => { + render(πŸŽ‰ Party); + expect(screen.getByText("πŸŽ‰ Party")).toBeInTheDocument(); + }); + }); +}); + From 953c8b8222b335422d31493f3d601250a953b40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Thu, 8 Jan 2026 23:42:06 +0900 Subject: [PATCH 18/26] feat: add CSS isolation for Base UI overlay components --- src/tokens/global.css.ts | 30 ++++++++++++++++++++++++++++++ src/tokens/index.ts | 3 +++ 2 files changed, 33 insertions(+) create mode 100644 src/tokens/global.css.ts diff --git a/src/tokens/global.css.ts b/src/tokens/global.css.ts new file mode 100644 index 0000000..0ff5f71 --- /dev/null +++ b/src/tokens/global.css.ts @@ -0,0 +1,30 @@ +import { globalStyle } from "@vanilla-extract/css"; + +/** + * Global styles for Base UI components + * + * Base UI requires `isolation: isolate` on the root element + * to prevent z-index issues with overlay components like Dialog, Popover, etc. + * + * @see https://base-ui.com/getting-started/usage#css-isolation + */ + +// Apply isolation to root and body for Base UI overlay components +globalStyle("#root", { + isolation: "isolate", +}); + +globalStyle("body", { + isolation: "isolate", +}); + +// Box-sizing reset for better consistency +globalStyle("*, *::before, *::after", { + boxSizing: "border-box", +}); + +// Remove default margin +globalStyle("body", { + margin: 0, +}); + diff --git a/src/tokens/index.ts b/src/tokens/index.ts index b107739..9b46699 100644 --- a/src/tokens/index.ts +++ b/src/tokens/index.ts @@ -5,3 +5,6 @@ export * from "./radius"; export * from "./shadow"; export * from "./appearanceTheme"; export * from "./theme.css"; + +// Import global styles for Base UI +import "./global.css"; From 816695dab893d187b119324bb7f49c7738262370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Thu, 8 Jan 2026 23:43:44 +0900 Subject: [PATCH 19/26] feat: add iOS 26+ Safari support for Base UI backdrops --- src/tokens/global.css.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/tokens/global.css.ts b/src/tokens/global.css.ts index 0ff5f71..3b326a2 100644 --- a/src/tokens/global.css.ts +++ b/src/tokens/global.css.ts @@ -3,19 +3,27 @@ import { globalStyle } from "@vanilla-extract/css"; /** * Global styles for Base UI components * - * Base UI requires `isolation: isolate` on the root element - * to prevent z-index issues with overlay components like Dialog, Popover, etc. + * Base UI requires the following styles for proper functionality: + * 1. `isolation: isolate` on the root element to prevent z-index issues + * 2. `position: relative` on body for iOS 26+ Safari backdrop support * - * @see https://base-ui.com/getting-started/usage#css-isolation + * @see https://base-ui.com/react/overview/quick-start */ -// Apply isolation to root and body for Base UI overlay components +// Apply isolation to root element for Base UI overlay components globalStyle("#root", { isolation: "isolate", }); +// Base UI body styles globalStyle("body", { + // Required for Base UI overlay components (z-index isolation) isolation: "isolate", + // Required for iOS 26+ Safari backdrop support + // Ensures Dialog backdrops cover the entire visual viewport after scrolling + position: "relative", + // Reset default margin + margin: 0, }); // Box-sizing reset for better consistency @@ -23,8 +31,3 @@ globalStyle("*, *::before, *::after", { boxSizing: "border-box", }); -// Remove default margin -globalStyle("body", { - margin: 0, -}); - From 5797feba833a7e26168d97c8abab3e170a4db26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Thu, 8 Jan 2026 23:54:08 +0900 Subject: [PATCH 20/26] feat: add Tooltip and Tabs components with Base UI --- src/components/Tabs/Tabs.css.ts | 237 ++++++++++++ src/components/Tabs/Tabs.spec.tsx | 48 +++ src/components/Tabs/Tabs.stories.tsx | 126 +++++++ src/components/Tabs/Tabs.tsx | 114 ++++++ src/components/Tabs/index.ts | 3 + src/components/Tooltip/Tooltip.css.ts | 133 +++++++ src/components/Tooltip/Tooltip.spec.tsx | 398 +++++++++++++++++++++ src/components/Tooltip/Tooltip.stories.tsx | 174 +++++++++ src/components/Tooltip/Tooltip.tsx | 127 +++++++ src/components/Tooltip/index.ts | 3 + src/components/index.ts | 2 + 11 files changed, 1365 insertions(+) create mode 100644 src/components/Tabs/Tabs.css.ts create mode 100644 src/components/Tabs/Tabs.spec.tsx create mode 100644 src/components/Tabs/Tabs.stories.tsx create mode 100644 src/components/Tabs/Tabs.tsx create mode 100644 src/components/Tabs/index.ts create mode 100644 src/components/Tooltip/Tooltip.css.ts create mode 100644 src/components/Tooltip/Tooltip.spec.tsx create mode 100644 src/components/Tooltip/Tooltip.stories.tsx create mode 100644 src/components/Tooltip/Tooltip.tsx create mode 100644 src/components/Tooltip/index.ts diff --git a/src/components/Tabs/Tabs.css.ts b/src/components/Tabs/Tabs.css.ts new file mode 100644 index 0000000..e35ff03 --- /dev/null +++ b/src/components/Tabs/Tabs.css.ts @@ -0,0 +1,237 @@ +import { style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { themeContract } from "@/tokens"; + +export const root = style({ + display: "flex", + flexDirection: "column", + width: "100%", +}); + +export const tabList = recipe({ + base: { + display: "flex", + gap: themeContract.spacing.xs, + borderBottom: `2px solid ${themeContract.color.surface.divider}`, + }, + + variants: { + orientation: { + horizontal: { + flexDirection: "row", + }, + vertical: { + flexDirection: "column", + borderBottom: "none", + borderRight: `2px solid ${themeContract.color.surface.divider}`, + }, + }, + }, + + defaultVariants: { + orientation: "horizontal", + }, +}); + +export const tab = recipe({ + base: { + padding: `${themeContract.spacing.sm} ${themeContract.spacing.md}`, + fontSize: themeContract.typography.fontSize.medium, + fontFamily: themeContract.typography.fontFamily.sans, + fontWeight: themeContract.typography.fontWeight.medium, + color: themeContract.color.surface.textMuted, + backgroundColor: "transparent", + border: "none", + borderBottom: "2px solid transparent", + cursor: "pointer", + transition: "all 0.2s ease-in-out", + whiteSpace: "nowrap", + + selectors: { + "&:hover:not([data-disabled])": { + color: themeContract.color.surface.text, + }, + "&[data-disabled]": { + opacity: 0.5, + cursor: "not-allowed", + }, + "&[data-focus-visible]": { + outline: `2px solid ${themeContract.color.primary.surface}`, + outlineOffset: "2px", + }, + }, + }, + + variants: { + intent: { + primary: {}, + secondary: {}, + success: {}, + warning: {}, + danger: {}, + neutral: {}, + }, + orientation: { + horizontal: {}, + vertical: { + borderBottom: "none", + borderRight: "2px solid transparent", + }, + }, + }, + + compoundVariants: [ + // Primary + { + variants: { intent: "primary" }, + style: { + selectors: { + "&[data-selected]": { + color: themeContract.color.primary.surface, + borderBottomColor: themeContract.color.primary.surface, + }, + }, + }, + }, + { + variants: { intent: "primary", orientation: "vertical" }, + style: { + selectors: { + "&[data-selected]": { + borderBottomColor: "transparent", + borderRightColor: themeContract.color.primary.surface, + }, + }, + }, + }, + // Secondary + { + variants: { intent: "secondary" }, + style: { + selectors: { + "&[data-selected]": { + color: themeContract.color.secondary.surface, + borderBottomColor: themeContract.color.secondary.surface, + }, + }, + }, + }, + { + variants: { intent: "secondary", orientation: "vertical" }, + style: { + selectors: { + "&[data-selected]": { + borderBottomColor: "transparent", + borderRightColor: themeContract.color.secondary.surface, + }, + }, + }, + }, + // Success + { + variants: { intent: "success" }, + style: { + selectors: { + "&[data-selected]": { + color: themeContract.color.success.surface, + borderBottomColor: themeContract.color.success.surface, + }, + }, + }, + }, + { + variants: { intent: "success", orientation: "vertical" }, + style: { + selectors: { + "&[data-selected]": { + borderBottomColor: "transparent", + borderRightColor: themeContract.color.success.surface, + }, + }, + }, + }, + // Warning + { + variants: { intent: "warning" }, + style: { + selectors: { + "&[data-selected]": { + color: themeContract.color.warning.surface, + borderBottomColor: themeContract.color.warning.surface, + }, + }, + }, + }, + { + variants: { intent: "warning", orientation: "vertical" }, + style: { + selectors: { + "&[data-selected]": { + borderBottomColor: "transparent", + borderRightColor: themeContract.color.warning.surface, + }, + }, + }, + }, + // Danger + { + variants: { intent: "danger" }, + style: { + selectors: { + "&[data-selected]": { + color: themeContract.color.danger.surface, + borderBottomColor: themeContract.color.danger.surface, + }, + }, + }, + }, + { + variants: { intent: "danger", orientation: "vertical" }, + style: { + selectors: { + "&[data-selected]": { + borderBottomColor: "transparent", + borderRightColor: themeContract.color.danger.surface, + }, + }, + }, + }, + // Neutral + { + variants: { intent: "neutral" }, + style: { + selectors: { + "&[data-selected]": { + color: themeContract.color.neutral.surface, + borderBottomColor: themeContract.color.neutral.surface, + }, + }, + }, + }, + { + variants: { intent: "neutral", orientation: "vertical" }, + style: { + selectors: { + "&[data-selected]": { + borderBottomColor: "transparent", + borderRightColor: themeContract.color.neutral.surface, + }, + }, + }, + }, + ], + + defaultVariants: { + intent: "primary", + orientation: "horizontal", + }, +}); + +export const panel = style({ + padding: themeContract.spacing.md, + fontFamily: themeContract.typography.fontFamily.sans, + fontSize: themeContract.typography.fontSize.medium, + lineHeight: themeContract.typography.lineHeight.medium, + color: themeContract.color.surface.text, +}); + diff --git a/src/components/Tabs/Tabs.spec.tsx b/src/components/Tabs/Tabs.spec.tsx new file mode 100644 index 0000000..0b27ba0 --- /dev/null +++ b/src/components/Tabs/Tabs.spec.tsx @@ -0,0 +1,48 @@ +import { describe, it, expect } from "vitest"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Tabs } from "./Tabs"; + +describe("Tabs", () => { + const sampleTabs = [ + { value: "tab1", label: "Tab 1", content:
Content 1
}, + { value: "tab2", label: "Tab 2", content:
Content 2
}, + { value: "tab3", label: "Tab 3", content:
Content 3
}, + ]; + + describe("Rendering", () => { + it("renders all tabs", () => { + render(); + expect(screen.getByRole("tab", { name: "Tab 1" })).toBeInTheDocument(); + expect(screen.getByRole("tab", { name: "Tab 2" })).toBeInTheDocument(); + expect(screen.getByRole("tab", { name: "Tab 3" })).toBeInTheDocument(); + }); + + it("renders initial content", () => { + render(); + expect(screen.getByText("Content 1")).toBeInTheDocument(); + }); + }); + + describe("Interactions", () => { + it("switches content on tab click", async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByRole("tab", { name: "Tab 2" })); + expect(screen.getByText("Content 2")).toBeInTheDocument(); + }); + + it("respects disabled state", () => { + const tabsWithDisabled = [ + ...sampleTabs, + { value: "tab4", label: "Disabled", content:
Content 4
, disabled: true }, + ]; + render(); + + const disabledTab = screen.getByRole("tab", { name: "Disabled" }); + expect(disabledTab).toHaveAttribute("data-disabled"); + }); + }); +}); + diff --git a/src/components/Tabs/Tabs.stories.tsx b/src/components/Tabs/Tabs.stories.tsx new file mode 100644 index 0000000..c1d0a7d --- /dev/null +++ b/src/components/Tabs/Tabs.stories.tsx @@ -0,0 +1,126 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Tabs } from "./Tabs"; + +const meta = { + title: "Components/Tabs", + component: Tabs, + parameters: { + layout: "padded", + }, + tags: ["autodocs"], + argTypes: { + intent: { + control: "select", + options: ["primary", "secondary", "success", "warning", "danger", "neutral"], + }, + orientation: { + control: "select", + options: ["horizontal", "vertical"], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const sampleTabs = [ + { + value: "tab1", + label: "Tab 1", + content:
Content for Tab 1. This is a sample content panel.
, + }, + { + value: "tab2", + label: "Tab 2", + content:
Content for Tab 2. This is another sample content panel.
, + }, + { + value: "tab3", + label: "Tab 3", + content:
Content for Tab 3. This is the third content panel.
, + }, +]; + +export const Default: Story = { + args: { + tabs: sampleTabs, + defaultValue: "tab1", + }, +}; + +export const Intents: Story = { + render: () => ( +
+ + + + + + +
+ ), +}; + +export const Vertical: Story = { + args: { + tabs: sampleTabs, + defaultValue: "tab1", + orientation: "vertical", + }, +}; + +export const WithDisabledTab: Story = { + args: { + tabs: [ + ...sampleTabs, + { + value: "tab4", + label: "Disabled Tab", + content:
This tab is disabled
, + disabled: true, + }, + ], + defaultValue: "tab1", + }, +}; + +export const RichContent: Story = { + args: { + tabs: [ + { + value: "profile", + label: "πŸ‘€ Profile", + content: ( +
+

User Profile

+

Name: John Doe

+

Email: john@example.com

+
+ ), + }, + { + value: "settings", + label: "βš™οΈ Settings", + content: ( +
+

Settings

+

Theme: Light

+

Language: English

+
+ ), + }, + { + value: "notifications", + label: "πŸ”” Notifications", + content: ( +
+

Notifications

+

You have 3 new notifications

+
+ ), + }, + ], + defaultValue: "profile", + }, +}; + diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tabs/Tabs.tsx new file mode 100644 index 0000000..01ecd41 --- /dev/null +++ b/src/components/Tabs/Tabs.tsx @@ -0,0 +1,114 @@ +import React from "react"; +import clsx from "clsx"; +import { Tabs as BaseUITabs } from "@base-ui/react/tabs"; +import * as styles from "./Tabs.css"; +import { lightTheme, type ColorIntent } from "@/tokens"; +import { useTheme } from "@/providers"; + +export type TabsIntent = ColorIntent; +export type TabsOrientation = "horizontal" | "vertical"; + +export interface Tab { + value: string; + label: React.ReactNode; + content: React.ReactNode; + disabled?: boolean; +} + +export interface TabsProps { + /** + * Array of tab objects + */ + tabs: Tab[]; + /** + * The currently selected tab value + */ + value?: string; + /** + * Default selected tab value (uncontrolled) + */ + defaultValue?: string; + /** + * Callback when tab changes + */ + onChange?: (value: string) => void; + /** + * Color intent + * @default "primary" + */ + intent?: TabsIntent; + /** + * Orientation of tabs + * @default "horizontal" + */ + orientation?: TabsOrientation; + /** + * Custom class name + */ + className?: string; +} + +/** + * Tabs component built on Base UI + * + * @example + * ```tsx + * Content 1 }, + * { value: "tab2", label: "Tab 2", content:
Content 2
} + * ]} + * defaultValue="tab1" + * /> + * ``` + */ +export const Tabs = ({ + tabs, + value, + defaultValue, + onChange, + intent = "primary", + orientation = "horizontal", + className, +}: TabsProps) => { + const themeContext = useTheme(); + const themeClass = themeContext?.themeClass ?? lightTheme; + + return ( + onChange?.(newValue as string)} + orientation={orientation} + className={clsx(themeClass, styles.root, className)} + > + + {tabs.map((tab) => ( + + {tab.label} + + ))} + + + {tabs.map((tab) => ( + + {tab.content} + + ))} + + ); +}; + diff --git a/src/components/Tabs/index.ts b/src/components/Tabs/index.ts new file mode 100644 index 0000000..e84dd8d --- /dev/null +++ b/src/components/Tabs/index.ts @@ -0,0 +1,3 @@ +export { Tabs } from "./Tabs"; +export type { TabsProps, Tab, TabsIntent, TabsOrientation } from "./Tabs"; + diff --git a/src/components/Tooltip/Tooltip.css.ts b/src/components/Tooltip/Tooltip.css.ts new file mode 100644 index 0000000..bd4a317 --- /dev/null +++ b/src/components/Tooltip/Tooltip.css.ts @@ -0,0 +1,133 @@ +import { keyframes } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { themeContract } from "@/tokens"; + +const fadeIn = keyframes({ + from: { + opacity: 0, + transform: "scale(0.96)", + }, + to: { + opacity: 1, + transform: "scale(1)", + }, +}); + +const fadeOut = keyframes({ + from: { + opacity: 1, + transform: "scale(1)", + }, + to: { + opacity: 0, + transform: "scale(0.96)", + }, +}); + +export const popup = recipe({ + base: { + maxWidth: "300px", + padding: `${themeContract.spacing.xs} ${themeContract.spacing.sm}`, + fontSize: themeContract.typography.fontSize.small, + lineHeight: themeContract.typography.lineHeight.small, + fontFamily: themeContract.typography.fontFamily.sans, + borderRadius: themeContract.radius.small, + boxShadow: themeContract.shadow.medium, + zIndex: 9999, + wordWrap: "break-word", + transformOrigin: "var(--transform-origin)", + selectors: { + "&[data-state='open']": { + animation: `${fadeIn} 0.15s ease-out`, + }, + "&[data-state='closing']": { + animation: `${fadeOut} 0.15s ease-in`, + }, + }, + }, + + variants: { + intent: { + primary: { + backgroundColor: themeContract.color.primary.surface, + color: themeContract.color.primary.text, + }, + secondary: { + backgroundColor: themeContract.color.secondary.surface, + color: themeContract.color.secondary.text, + }, + success: { + backgroundColor: themeContract.color.success.surface, + color: themeContract.color.success.text, + }, + warning: { + backgroundColor: themeContract.color.warning.surface, + color: themeContract.color.warning.text, + }, + danger: { + backgroundColor: themeContract.color.danger.surface, + color: themeContract.color.danger.text, + }, + neutral: { + backgroundColor: themeContract.color.neutral.surface, + color: themeContract.color.neutral.text, + }, + }, + }, + + defaultVariants: { + intent: "neutral", + }, +}); + +export const arrow = recipe({ + base: { + width: "12px", + height: "6px", + selectors: { + "&[data-side='top']": { + bottom: "-6px", + }, + "&[data-side='bottom']": { + top: "-6px", + transform: "rotate(180deg)", + }, + "&[data-side='left']": { + right: "-6px", + transform: "rotate(-90deg)", + }, + "&[data-side='right']": { + left: "-6px", + transform: "rotate(90deg)", + }, + }, + }, + + variants: { + intent: { + primary: { + color: themeContract.color.primary.surface, + }, + secondary: { + color: themeContract.color.secondary.surface, + }, + success: { + color: themeContract.color.success.surface, + }, + warning: { + color: themeContract.color.warning.surface, + }, + danger: { + color: themeContract.color.danger.surface, + }, + neutral: { + color: themeContract.color.neutral.surface, + }, + }, + }, + + defaultVariants: { + intent: "neutral", + }, +}); + diff --git a/src/components/Tooltip/Tooltip.spec.tsx b/src/components/Tooltip/Tooltip.spec.tsx new file mode 100644 index 0000000..935d0c2 --- /dev/null +++ b/src/components/Tooltip/Tooltip.spec.tsx @@ -0,0 +1,398 @@ +import { describe, it, expect, vi } from "vitest"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Tooltip } from "./Tooltip"; +import { Button } from "../Button"; + +describe("Tooltip", () => { + describe("Rendering", () => { + it("renders trigger element", () => { + render( + + + + ); + expect(screen.getByRole("button", { name: /hover me/i })).toBeInTheDocument(); + }); + + it("shows tooltip on hover", async () => { + const user = userEvent.setup(); + render( + + + + ); + + const button = screen.getByRole("button"); + await user.hover(button); + + await waitFor(() => { + expect(screen.getByText("Tooltip text")).toBeInTheDocument(); + }); + }); + + it("hides tooltip on unhover", async () => { + const user = userEvent.setup(); + render( + + + + ); + + const button = screen.getByRole("button"); + await user.hover(button); + + await waitFor(() => { + expect(screen.getByText("Tooltip text")).toBeInTheDocument(); + }); + + await user.unhover(button); + + await waitFor(() => { + expect(screen.queryByText("Tooltip text")).not.toBeInTheDocument(); + }); + }); + }); + + describe("Intent/Color Variants", () => { + const intents = ["primary", "secondary", "success", "warning", "danger", "neutral"] as const; + + intents.forEach((intent) => { + it(`renders with ${intent} intent`, async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText(`${intent} tooltip`)).toBeInTheDocument(); + }); + }); + }); + }); + + describe("Sides", () => { + const sides = ["top", "right", "bottom", "left"] as const; + + sides.forEach((side) => { + it(`renders on ${side} side`, async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText(`${side} tooltip`)).toBeInTheDocument(); + }); + }); + }); + }); + + describe("Alignment", () => { + const alignments = ["start", "center", "end"] as const; + + alignments.forEach((align) => { + it(`renders with ${align} alignment`, async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText(`${align} aligned`)).toBeInTheDocument(); + }); + }); + }); + }); + + describe("Arrow", () => { + it("shows arrow by default", async () => { + const user = userEvent.setup(); + const { container } = render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + const arrow = container.querySelector("svg"); + expect(arrow).toBeInTheDocument(); + }); + }); + + it("hides arrow when showArrow is false", async () => { + const user = userEvent.setup(); + const { container } = render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("Without arrow")).toBeInTheDocument(); + }); + + const arrow = container.querySelector("svg"); + expect(arrow).not.toBeInTheDocument(); + }); + }); + + describe("Disabled State", () => { + it("does not show tooltip when disabled", async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + // Wait a bit to ensure tooltip doesn't appear + await new Promise(resolve => setTimeout(resolve, 300)); + + expect(screen.queryByText("Disabled tooltip")).not.toBeInTheDocument(); + }); + }); + + describe("Content Types", () => { + it("renders text content", async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("Text content")).toBeInTheDocument(); + }); + }); + + it("renders complex content", async () => { + const user = userEvent.setup(); + render( + Bold text}> + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("Bold")).toBeInTheDocument(); + expect(screen.getByText(/text/)).toBeInTheDocument(); + }); + }); + + it("renders long content", async () => { + const longText = "This is a very long tooltip content that should wrap properly"; + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText(longText)).toBeInTheDocument(); + }); + }); + }); + + describe("Offsets", () => { + it("applies sideOffset", async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("With offset")).toBeInTheDocument(); + }); + }); + + it("applies alignOffset", async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("With align offset")).toBeInTheDocument(); + }); + }); + }); + + describe("Custom className", () => { + it("applies custom className", async () => { + const user = userEvent.setup(); + const { container } = render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + const tooltip = container.querySelector(".custom-tooltip"); + expect(tooltip).toBeInTheDocument(); + }); + }); + }); + + describe("Accessibility", () => { + it("has proper ARIA attributes", async () => { + const user = userEvent.setup(); + render( + + + + ); + + const button = screen.getByRole("button"); + await user.hover(button); + + await waitFor(() => { + expect(screen.getByText("Accessible tooltip")).toBeInTheDocument(); + }); + }); + + it("shows on keyboard focus", async () => { + const user = userEvent.setup(); + render( + + + + ); + + const button = screen.getByRole("button"); + await user.tab(); + + expect(button).toHaveFocus(); + + await waitFor(() => { + expect(screen.getByText("Focus tooltip")).toBeInTheDocument(); + }); + }); + + it("hides on blur", async () => { + const user = userEvent.setup(); + render( + <> + + + + + + ); + + await user.tab(); + + await waitFor(() => { + expect(screen.getByText("Focus tooltip")).toBeInTheDocument(); + }); + + await user.tab(); + + await waitFor(() => { + expect(screen.queryByText("Focus tooltip")).not.toBeInTheDocument(); + }); + }); + }); + + describe("Multiple Tooltips", () => { + it("handles multiple tooltips independently", async () => { + const user = userEvent.setup(); + render( +
+ + + + + + +
+ ); + + const [firstButton, secondButton] = screen.getAllByRole("button"); + + await user.hover(firstButton); + + await waitFor(() => { + expect(screen.getByText("First tooltip")).toBeInTheDocument(); + expect(screen.queryByText("Second tooltip")).not.toBeInTheDocument(); + }); + + await user.unhover(firstButton); + await user.hover(secondButton); + + await waitFor(() => { + expect(screen.queryByText("First tooltip")).not.toBeInTheDocument(); + expect(screen.getByText("Second tooltip")).toBeInTheDocument(); + }); + }); + }); + + describe("Trigger Types", () => { + it("works with different trigger elements", async () => { + const user = userEvent.setup(); + render( + + + + ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("Icon tooltip")).toBeInTheDocument(); + }); + }); + + it("works with custom elements", async () => { + const user = userEvent.setup(); + render( + +
Custom trigger
+
+ ); + + await user.hover(screen.getByRole("button")); + + await waitFor(() => { + expect(screen.getByText("Custom element")).toBeInTheDocument(); + }); + }); + }); +}); + diff --git a/src/components/Tooltip/Tooltip.stories.tsx b/src/components/Tooltip/Tooltip.stories.tsx new file mode 100644 index 0000000..6618bb9 --- /dev/null +++ b/src/components/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,174 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Tooltip } from "./Tooltip"; +import { Button } from "../Button"; + +const meta = { + title: "Components/Tooltip", + component: Tooltip, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + side: { + control: "select", + options: ["top", "right", "bottom", "left"], + }, + align: { + control: "select", + options: ["start", "center", "end"], + }, + intent: { + control: "select", + options: ["primary", "secondary", "success", "warning", "danger", "neutral"], + }, + sideOffset: { + control: "number", + }, + alignOffset: { + control: "number", + }, + delay: { + control: "number", + }, + showArrow: { + control: "boolean", + }, + disabled: { + control: "boolean", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + content: "This is a tooltip", + children: , + }, +}; + +export const Intents: Story = { + render: () => ( +
+ + + + + + + + + + + + + + + + + + +
+ ), +}; + +export const Sides: Story = { + render: () => ( +
+ + + + + + + + + + + + +
+ ), +}; + +export const Alignments: Story = { + render: () => ( +
+ + + + + + + + + +
+ ), +}; + +export const WithoutArrow: Story = { + args: { + content: "Tooltip without arrow", + showArrow: false, + children: , + }, +}; + +export const LongContent: Story = { + args: { + content: "This is a very long tooltip that contains a lot of text to demonstrate how the tooltip handles longer content. It should wrap nicely and remain readable.", + children: , + }, +}; + + +export const Disabled: Story = { + args: { + content: "This tooltip is disabled", + disabled: true, + children: , + }, +}; + +export const WithIcon: Story = { + render: () => ( + + + + ), +}; + +export const Interactive: Story = { + render: () => ( +
+ + + + + + + + + +
+ ), +}; + diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx new file mode 100644 index 0000000..fb44d79 --- /dev/null +++ b/src/components/Tooltip/Tooltip.tsx @@ -0,0 +1,127 @@ +import React from "react"; +import clsx from "clsx"; +import { Tooltip as BaseUITooltip } from "@base-ui/react/tooltip"; +import * as styles from "./Tooltip.css"; +import { lightTheme, type ColorIntent } from "@/tokens"; +import { useTheme } from "@/providers"; + +export type TooltipSide = "top" | "right" | "bottom" | "left"; +export type TooltipAlign = "start" | "center" | "end"; +export type TooltipIntent = ColorIntent; + +export interface TooltipProps { + /** + * The element that triggers the tooltip + */ + children: React.ReactElement; + /** + * The content to display in the tooltip + */ + content: React.ReactNode; + /** + * The side of the trigger where the tooltip should appear + * @default "top" + */ + side?: TooltipSide; + /** + * The alignment of the tooltip relative to the trigger + * @default "center" + */ + align?: TooltipAlign; + /** + * The color intent of the tooltip + * @default "neutral" + */ + intent?: TooltipIntent; + /** + * The distance in pixels from the trigger + * @default 8 + */ + sideOffset?: number; + /** + * The alignment offset in pixels + * @default 0 + */ + alignOffset?: number; + /** + * Whether to show an arrow pointing to the trigger + * @default true + */ + showArrow?: boolean; + /** + * Whether the tooltip is disabled + * @default false + */ + disabled?: boolean; + /** + * Custom class name for the tooltip popup + */ + className?: string; +} + +/** + * Tooltip component built on Base UI + * + * @example + * ```tsx + * + * + * + * ``` + */ +export const Tooltip = ({ + children, + content, + side = "top", + align = "center", + intent = "neutral", + sideOffset = 8, + alignOffset = 0, + showArrow = true, + disabled = false, + className, +}: TooltipProps) => { + const themeContext = useTheme(); + const themeClass = themeContext?.themeClass ?? lightTheme; + + if (disabled) { + return children; + } + + return ( + + + {children} + + + + {showArrow && ( + + + + + + )} + {content} + + + + + + ); +}; + diff --git a/src/components/Tooltip/index.ts b/src/components/Tooltip/index.ts new file mode 100644 index 0000000..c637399 --- /dev/null +++ b/src/components/Tooltip/index.ts @@ -0,0 +1,3 @@ +export { Tooltip } from "./Tooltip"; +export type { TooltipProps, TooltipSide, TooltipAlign, TooltipIntent } from "./Tooltip"; + diff --git a/src/components/index.ts b/src/components/index.ts index 1e9dbf4..4016f60 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -4,7 +4,9 @@ export * from "./Checkbox"; export * from "./Dialog"; export * from "./Dropdown"; export * from "./Switch"; +export * from "./Tabs"; export * from "./Text"; export * from "./TextField"; +export * from "./Tooltip"; export * from "../providers"; From 74a28aea91738e730ac054b918a7534cb9da4a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Thu, 8 Jan 2026 23:55:43 +0900 Subject: [PATCH 21/26] chore: add VS Code file nesting configuration - Enable file nesting for better project organization - Nest related files (css.ts, spec.tsx, stories.tsx) under main component files - Add common config file nesting patterns - Update .gitignore to include settings.json for team consistency --- .gitignore | 1 + .vscode/settings.json | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 56071eb..51fe5b2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ storybook-static # Editor directories and files .vscode/* !.vscode/extensions.json +!.vscode/settings.json .idea .DS_Store *.suo diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b7eb01d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,26 @@ +{ + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, + "explorer.fileNesting.patterns": { + // TypeScript/React μ»΄ν¬λ„ŒνŠΈ λ„€μŠ€νŒ… + "*.tsx": "${capture}.css.ts, ${capture}.spec.tsx, ${capture}.stories.tsx, ${capture}.test.tsx", + "*.ts": "${capture}.spec.ts, ${capture}.test.ts", + + // 인덱슀 파일 λ„€μŠ€νŒ… (μ»΄ν¬λ„ŒνŠΈ ν΄λ”μ˜ index.tsλŠ” λ„€μŠ€νŒ…ν•˜μ§€ μ•ŠμŒ) + + // μ„€μ • 파일 λ„€μŠ€νŒ… + "package.json": "package-lock.json, pnpm-lock.yaml, yarn.lock, .npmrc, .nvmrc, .node-version", + "tsconfig.json": "tsconfig.*.json", + "vite.config.*": "vitest.config.*, vitest.setup.*", + ".eslintrc.*": ".eslintignore, .prettierrc*, .prettierignore", + + // Git κ΄€λ ¨ + ".gitignore": ".gitattributes, .gitmodules", + + // README 및 λ¬Έμ„œ + "README.md": "CHANGELOG.md, LICENSE, CONTRIBUTING.md", + + // Storybook μ„€μ • + ".storybook": "storybook-static" + } +} From 897c3146f6189cac93ca9a102374d7f4a5210977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Thu, 8 Jan 2026 23:57:59 +0900 Subject: [PATCH 22/26] fix: add missing Tooltip.Trigger for proper hover/click interaction The Tooltip component was not responding to hover or click events because the BaseUITooltip.Trigger wrapper was missing. This component is required by Base UI to properly handle user interactions with the tooltip trigger element. Changes: - Wrap children with BaseUITooltip.Trigger - Tooltip now properly shows on hover - Tooltip now properly shows on focus (keyboard navigation) --- .vscode/settings.json | 37 +++++++++++------------------- src/components/Tabs/Tabs.tsx | 3 +-- src/components/Tooltip/Tooltip.tsx | 2 +- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b7eb01d..bb69106 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,26 +1,15 @@ { - "explorer.fileNesting.enabled": true, - "explorer.fileNesting.expand": false, - "explorer.fileNesting.patterns": { - // TypeScript/React μ»΄ν¬λ„ŒνŠΈ λ„€μŠ€νŒ… - "*.tsx": "${capture}.css.ts, ${capture}.spec.tsx, ${capture}.stories.tsx, ${capture}.test.tsx", - "*.ts": "${capture}.spec.ts, ${capture}.test.ts", - - // 인덱슀 파일 λ„€μŠ€νŒ… (μ»΄ν¬λ„ŒνŠΈ ν΄λ”μ˜ index.tsλŠ” λ„€μŠ€νŒ…ν•˜μ§€ μ•ŠμŒ) - - // μ„€μ • 파일 λ„€μŠ€νŒ… - "package.json": "package-lock.json, pnpm-lock.yaml, yarn.lock, .npmrc, .nvmrc, .node-version", - "tsconfig.json": "tsconfig.*.json", - "vite.config.*": "vitest.config.*, vitest.setup.*", - ".eslintrc.*": ".eslintignore, .prettierrc*, .prettierignore", - - // Git κ΄€λ ¨ - ".gitignore": ".gitattributes, .gitmodules", - - // README 및 λ¬Έμ„œ - "README.md": "CHANGELOG.md, LICENSE, CONTRIBUTING.md", - - // Storybook μ„€μ • - ".storybook": "storybook-static" - } + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, + "explorer.fileNesting.patterns": { + "*.tsx": "${capture}.css.ts, ${capture}.spec.tsx, ${capture}.stories.tsx, ${capture}.test.tsx", + "*.ts": "${capture}.spec.ts, ${capture}.test.ts", + "package.json": "package-lock.json, pnpm-lock.yaml, yarn.lock, .npmrc, .nvmrc, .node-version", + "tsconfig.json": "tsconfig.*.json", + "vite.config.*": "vitest.config.*, vitest.setup.*", + ".eslintrc.*": ".eslintignore, .prettierrc*, .prettierignore", + ".gitignore": ".gitattributes, .gitmodules", + "README.md": "CHANGELOG.md, LICENSE, CONTRIBUTING.md", + ".storybook": "storybook-static" + } } diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tabs/Tabs.tsx index 01ecd41..97ff699 100644 --- a/src/components/Tabs/Tabs.tsx +++ b/src/components/Tabs/Tabs.tsx @@ -50,7 +50,7 @@ export interface TabsProps { /** * Tabs component built on Base UI - * + * * @example * ```tsx * ); }; - diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index fb44d79..55c610e 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -91,7 +91,7 @@ export const Tooltip = ({ return ( - {children} + {children} Date: Fri, 9 Jan 2026 00:00:11 +0900 Subject: [PATCH 23/26] chore: delete tooltip --- src/components/Tooltip/Tooltip.css.ts | 133 ------- src/components/Tooltip/Tooltip.spec.tsx | 398 --------------------- src/components/Tooltip/Tooltip.stories.tsx | 174 --------- src/components/Tooltip/Tooltip.tsx | 127 ------- src/components/Tooltip/index.ts | 3 - src/components/index.ts | 1 - 6 files changed, 836 deletions(-) delete mode 100644 src/components/Tooltip/Tooltip.css.ts delete mode 100644 src/components/Tooltip/Tooltip.spec.tsx delete mode 100644 src/components/Tooltip/Tooltip.stories.tsx delete mode 100644 src/components/Tooltip/Tooltip.tsx delete mode 100644 src/components/Tooltip/index.ts diff --git a/src/components/Tooltip/Tooltip.css.ts b/src/components/Tooltip/Tooltip.css.ts deleted file mode 100644 index bd4a317..0000000 --- a/src/components/Tooltip/Tooltip.css.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { keyframes } from "@vanilla-extract/css"; -import { recipe } from "@vanilla-extract/recipes"; -import { themeContract } from "@/tokens"; - -const fadeIn = keyframes({ - from: { - opacity: 0, - transform: "scale(0.96)", - }, - to: { - opacity: 1, - transform: "scale(1)", - }, -}); - -const fadeOut = keyframes({ - from: { - opacity: 1, - transform: "scale(1)", - }, - to: { - opacity: 0, - transform: "scale(0.96)", - }, -}); - -export const popup = recipe({ - base: { - maxWidth: "300px", - padding: `${themeContract.spacing.xs} ${themeContract.spacing.sm}`, - fontSize: themeContract.typography.fontSize.small, - lineHeight: themeContract.typography.lineHeight.small, - fontFamily: themeContract.typography.fontFamily.sans, - borderRadius: themeContract.radius.small, - boxShadow: themeContract.shadow.medium, - zIndex: 9999, - wordWrap: "break-word", - transformOrigin: "var(--transform-origin)", - selectors: { - "&[data-state='open']": { - animation: `${fadeIn} 0.15s ease-out`, - }, - "&[data-state='closing']": { - animation: `${fadeOut} 0.15s ease-in`, - }, - }, - }, - - variants: { - intent: { - primary: { - backgroundColor: themeContract.color.primary.surface, - color: themeContract.color.primary.text, - }, - secondary: { - backgroundColor: themeContract.color.secondary.surface, - color: themeContract.color.secondary.text, - }, - success: { - backgroundColor: themeContract.color.success.surface, - color: themeContract.color.success.text, - }, - warning: { - backgroundColor: themeContract.color.warning.surface, - color: themeContract.color.warning.text, - }, - danger: { - backgroundColor: themeContract.color.danger.surface, - color: themeContract.color.danger.text, - }, - neutral: { - backgroundColor: themeContract.color.neutral.surface, - color: themeContract.color.neutral.text, - }, - }, - }, - - defaultVariants: { - intent: "neutral", - }, -}); - -export const arrow = recipe({ - base: { - width: "12px", - height: "6px", - selectors: { - "&[data-side='top']": { - bottom: "-6px", - }, - "&[data-side='bottom']": { - top: "-6px", - transform: "rotate(180deg)", - }, - "&[data-side='left']": { - right: "-6px", - transform: "rotate(-90deg)", - }, - "&[data-side='right']": { - left: "-6px", - transform: "rotate(90deg)", - }, - }, - }, - - variants: { - intent: { - primary: { - color: themeContract.color.primary.surface, - }, - secondary: { - color: themeContract.color.secondary.surface, - }, - success: { - color: themeContract.color.success.surface, - }, - warning: { - color: themeContract.color.warning.surface, - }, - danger: { - color: themeContract.color.danger.surface, - }, - neutral: { - color: themeContract.color.neutral.surface, - }, - }, - }, - - defaultVariants: { - intent: "neutral", - }, -}); - diff --git a/src/components/Tooltip/Tooltip.spec.tsx b/src/components/Tooltip/Tooltip.spec.tsx deleted file mode 100644 index 935d0c2..0000000 --- a/src/components/Tooltip/Tooltip.spec.tsx +++ /dev/null @@ -1,398 +0,0 @@ -import { describe, it, expect, vi } from "vitest"; -import { render, screen, waitFor } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { Tooltip } from "./Tooltip"; -import { Button } from "../Button"; - -describe("Tooltip", () => { - describe("Rendering", () => { - it("renders trigger element", () => { - render( - - - - ); - expect(screen.getByRole("button", { name: /hover me/i })).toBeInTheDocument(); - }); - - it("shows tooltip on hover", async () => { - const user = userEvent.setup(); - render( - - - - ); - - const button = screen.getByRole("button"); - await user.hover(button); - - await waitFor(() => { - expect(screen.getByText("Tooltip text")).toBeInTheDocument(); - }); - }); - - it("hides tooltip on unhover", async () => { - const user = userEvent.setup(); - render( - - - - ); - - const button = screen.getByRole("button"); - await user.hover(button); - - await waitFor(() => { - expect(screen.getByText("Tooltip text")).toBeInTheDocument(); - }); - - await user.unhover(button); - - await waitFor(() => { - expect(screen.queryByText("Tooltip text")).not.toBeInTheDocument(); - }); - }); - }); - - describe("Intent/Color Variants", () => { - const intents = ["primary", "secondary", "success", "warning", "danger", "neutral"] as const; - - intents.forEach((intent) => { - it(`renders with ${intent} intent`, async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText(`${intent} tooltip`)).toBeInTheDocument(); - }); - }); - }); - }); - - describe("Sides", () => { - const sides = ["top", "right", "bottom", "left"] as const; - - sides.forEach((side) => { - it(`renders on ${side} side`, async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText(`${side} tooltip`)).toBeInTheDocument(); - }); - }); - }); - }); - - describe("Alignment", () => { - const alignments = ["start", "center", "end"] as const; - - alignments.forEach((align) => { - it(`renders with ${align} alignment`, async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText(`${align} aligned`)).toBeInTheDocument(); - }); - }); - }); - }); - - describe("Arrow", () => { - it("shows arrow by default", async () => { - const user = userEvent.setup(); - const { container } = render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - const arrow = container.querySelector("svg"); - expect(arrow).toBeInTheDocument(); - }); - }); - - it("hides arrow when showArrow is false", async () => { - const user = userEvent.setup(); - const { container } = render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("Without arrow")).toBeInTheDocument(); - }); - - const arrow = container.querySelector("svg"); - expect(arrow).not.toBeInTheDocument(); - }); - }); - - describe("Disabled State", () => { - it("does not show tooltip when disabled", async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - // Wait a bit to ensure tooltip doesn't appear - await new Promise(resolve => setTimeout(resolve, 300)); - - expect(screen.queryByText("Disabled tooltip")).not.toBeInTheDocument(); - }); - }); - - describe("Content Types", () => { - it("renders text content", async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("Text content")).toBeInTheDocument(); - }); - }); - - it("renders complex content", async () => { - const user = userEvent.setup(); - render( - Bold text}> - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("Bold")).toBeInTheDocument(); - expect(screen.getByText(/text/)).toBeInTheDocument(); - }); - }); - - it("renders long content", async () => { - const longText = "This is a very long tooltip content that should wrap properly"; - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText(longText)).toBeInTheDocument(); - }); - }); - }); - - describe("Offsets", () => { - it("applies sideOffset", async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("With offset")).toBeInTheDocument(); - }); - }); - - it("applies alignOffset", async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("With align offset")).toBeInTheDocument(); - }); - }); - }); - - describe("Custom className", () => { - it("applies custom className", async () => { - const user = userEvent.setup(); - const { container } = render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - const tooltip = container.querySelector(".custom-tooltip"); - expect(tooltip).toBeInTheDocument(); - }); - }); - }); - - describe("Accessibility", () => { - it("has proper ARIA attributes", async () => { - const user = userEvent.setup(); - render( - - - - ); - - const button = screen.getByRole("button"); - await user.hover(button); - - await waitFor(() => { - expect(screen.getByText("Accessible tooltip")).toBeInTheDocument(); - }); - }); - - it("shows on keyboard focus", async () => { - const user = userEvent.setup(); - render( - - - - ); - - const button = screen.getByRole("button"); - await user.tab(); - - expect(button).toHaveFocus(); - - await waitFor(() => { - expect(screen.getByText("Focus tooltip")).toBeInTheDocument(); - }); - }); - - it("hides on blur", async () => { - const user = userEvent.setup(); - render( - <> - - - - - - ); - - await user.tab(); - - await waitFor(() => { - expect(screen.getByText("Focus tooltip")).toBeInTheDocument(); - }); - - await user.tab(); - - await waitFor(() => { - expect(screen.queryByText("Focus tooltip")).not.toBeInTheDocument(); - }); - }); - }); - - describe("Multiple Tooltips", () => { - it("handles multiple tooltips independently", async () => { - const user = userEvent.setup(); - render( -
- - - - - - -
- ); - - const [firstButton, secondButton] = screen.getAllByRole("button"); - - await user.hover(firstButton); - - await waitFor(() => { - expect(screen.getByText("First tooltip")).toBeInTheDocument(); - expect(screen.queryByText("Second tooltip")).not.toBeInTheDocument(); - }); - - await user.unhover(firstButton); - await user.hover(secondButton); - - await waitFor(() => { - expect(screen.queryByText("First tooltip")).not.toBeInTheDocument(); - expect(screen.getByText("Second tooltip")).toBeInTheDocument(); - }); - }); - }); - - describe("Trigger Types", () => { - it("works with different trigger elements", async () => { - const user = userEvent.setup(); - render( - - - - ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("Icon tooltip")).toBeInTheDocument(); - }); - }); - - it("works with custom elements", async () => { - const user = userEvent.setup(); - render( - -
Custom trigger
-
- ); - - await user.hover(screen.getByRole("button")); - - await waitFor(() => { - expect(screen.getByText("Custom element")).toBeInTheDocument(); - }); - }); - }); -}); - diff --git a/src/components/Tooltip/Tooltip.stories.tsx b/src/components/Tooltip/Tooltip.stories.tsx deleted file mode 100644 index 6618bb9..0000000 --- a/src/components/Tooltip/Tooltip.stories.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { Tooltip } from "./Tooltip"; -import { Button } from "../Button"; - -const meta = { - title: "Components/Tooltip", - component: Tooltip, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - side: { - control: "select", - options: ["top", "right", "bottom", "left"], - }, - align: { - control: "select", - options: ["start", "center", "end"], - }, - intent: { - control: "select", - options: ["primary", "secondary", "success", "warning", "danger", "neutral"], - }, - sideOffset: { - control: "number", - }, - alignOffset: { - control: "number", - }, - delay: { - control: "number", - }, - showArrow: { - control: "boolean", - }, - disabled: { - control: "boolean", - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - content: "This is a tooltip", - children: , - }, -}; - -export const Intents: Story = { - render: () => ( -
- - - - - - - - - - - - - - - - - - -
- ), -}; - -export const Sides: Story = { - render: () => ( -
- - - - - - - - - - - - -
- ), -}; - -export const Alignments: Story = { - render: () => ( -
- - - - - - - - - -
- ), -}; - -export const WithoutArrow: Story = { - args: { - content: "Tooltip without arrow", - showArrow: false, - children: , - }, -}; - -export const LongContent: Story = { - args: { - content: "This is a very long tooltip that contains a lot of text to demonstrate how the tooltip handles longer content. It should wrap nicely and remain readable.", - children: , - }, -}; - - -export const Disabled: Story = { - args: { - content: "This tooltip is disabled", - disabled: true, - children: , - }, -}; - -export const WithIcon: Story = { - render: () => ( - - - - ), -}; - -export const Interactive: Story = { - render: () => ( -
- - - - - - - - - -
- ), -}; - diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx deleted file mode 100644 index 55c610e..0000000 --- a/src/components/Tooltip/Tooltip.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { Tooltip as BaseUITooltip } from "@base-ui/react/tooltip"; -import * as styles from "./Tooltip.css"; -import { lightTheme, type ColorIntent } from "@/tokens"; -import { useTheme } from "@/providers"; - -export type TooltipSide = "top" | "right" | "bottom" | "left"; -export type TooltipAlign = "start" | "center" | "end"; -export type TooltipIntent = ColorIntent; - -export interface TooltipProps { - /** - * The element that triggers the tooltip - */ - children: React.ReactElement; - /** - * The content to display in the tooltip - */ - content: React.ReactNode; - /** - * The side of the trigger where the tooltip should appear - * @default "top" - */ - side?: TooltipSide; - /** - * The alignment of the tooltip relative to the trigger - * @default "center" - */ - align?: TooltipAlign; - /** - * The color intent of the tooltip - * @default "neutral" - */ - intent?: TooltipIntent; - /** - * The distance in pixels from the trigger - * @default 8 - */ - sideOffset?: number; - /** - * The alignment offset in pixels - * @default 0 - */ - alignOffset?: number; - /** - * Whether to show an arrow pointing to the trigger - * @default true - */ - showArrow?: boolean; - /** - * Whether the tooltip is disabled - * @default false - */ - disabled?: boolean; - /** - * Custom class name for the tooltip popup - */ - className?: string; -} - -/** - * Tooltip component built on Base UI - * - * @example - * ```tsx - * - * - * - * ``` - */ -export const Tooltip = ({ - children, - content, - side = "top", - align = "center", - intent = "neutral", - sideOffset = 8, - alignOffset = 0, - showArrow = true, - disabled = false, - className, -}: TooltipProps) => { - const themeContext = useTheme(); - const themeClass = themeContext?.themeClass ?? lightTheme; - - if (disabled) { - return children; - } - - return ( - - - {children} - - - - {showArrow && ( - - - - - - )} - {content} - - - - - - ); -}; - diff --git a/src/components/Tooltip/index.ts b/src/components/Tooltip/index.ts deleted file mode 100644 index c637399..0000000 --- a/src/components/Tooltip/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { Tooltip } from "./Tooltip"; -export type { TooltipProps, TooltipSide, TooltipAlign, TooltipIntent } from "./Tooltip"; - diff --git a/src/components/index.ts b/src/components/index.ts index 4016f60..d41e75b 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -7,6 +7,5 @@ export * from "./Switch"; export * from "./Tabs"; export * from "./Text"; export * from "./TextField"; -export * from "./Tooltip"; export * from "../providers"; From 9057934a5d599da8a9f4908673427eb5cd154ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Fri, 9 Jan 2026 00:03:22 +0900 Subject: [PATCH 24/26] refactor: rename Tabs to Tab for better semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename component from 'Tabs' to 'Tab' - Rename interface 'Tab' to 'TabItem' to avoid confusion - Update all related types (TabsProps -> TabProps, TabsIntent -> TabIntent, etc) - Update Storybook title from 'Components/Tabs' to 'Components/Tab' - Update all file names and imports This change makes the component name more consistent with other UI libraries and better reflects that it represents a tabbed interface as a whole, not individual tabs. Suggested category placement: - Tab β†’ SELECTION (navigation between content panels) - Tooltip β†’ DISPLAY & FEEDBACK (information display) --- .../{Tabs/Tabs.css.ts => Tab/Tab.css.ts} | 0 .../{Tabs/Tabs.spec.tsx => Tab/Tab.spec.tsx} | 12 ++++----- .../Tabs.stories.tsx => Tab/Tab.stories.tsx} | 18 ++++++------- src/components/{Tabs/Tabs.tsx => Tab/Tab.tsx} | 26 +++++++++---------- src/components/Tab/index.ts | 3 +++ src/components/Tabs/index.ts | 3 --- src/components/index.ts | 2 +- 7 files changed, 32 insertions(+), 32 deletions(-) rename src/components/{Tabs/Tabs.css.ts => Tab/Tab.css.ts} (100%) rename src/components/{Tabs/Tabs.spec.tsx => Tab/Tab.spec.tsx} (82%) rename src/components/{Tabs/Tabs.stories.tsx => Tab/Tab.stories.tsx} (81%) rename src/components/{Tabs/Tabs.tsx => Tab/Tab.tsx} (84%) create mode 100644 src/components/Tab/index.ts delete mode 100644 src/components/Tabs/index.ts diff --git a/src/components/Tabs/Tabs.css.ts b/src/components/Tab/Tab.css.ts similarity index 100% rename from src/components/Tabs/Tabs.css.ts rename to src/components/Tab/Tab.css.ts diff --git a/src/components/Tabs/Tabs.spec.tsx b/src/components/Tab/Tab.spec.tsx similarity index 82% rename from src/components/Tabs/Tabs.spec.tsx rename to src/components/Tab/Tab.spec.tsx index 0b27ba0..76b5c4a 100644 --- a/src/components/Tabs/Tabs.spec.tsx +++ b/src/components/Tab/Tab.spec.tsx @@ -1,9 +1,9 @@ import { describe, it, expect } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { Tabs } from "./Tabs"; +import { Tab } from "./Tab"; -describe("Tabs", () => { +describe("Tab", () => { const sampleTabs = [ { value: "tab1", label: "Tab 1", content:
Content 1
}, { value: "tab2", label: "Tab 2", content:
Content 2
}, @@ -12,14 +12,14 @@ describe("Tabs", () => { describe("Rendering", () => { it("renders all tabs", () => { - render(); + render(); expect(screen.getByRole("tab", { name: "Tab 1" })).toBeInTheDocument(); expect(screen.getByRole("tab", { name: "Tab 2" })).toBeInTheDocument(); expect(screen.getByRole("tab", { name: "Tab 3" })).toBeInTheDocument(); }); it("renders initial content", () => { - render(); + render(); expect(screen.getByText("Content 1")).toBeInTheDocument(); }); }); @@ -27,7 +27,7 @@ describe("Tabs", () => { describe("Interactions", () => { it("switches content on tab click", async () => { const user = userEvent.setup(); - render(); + render(); await user.click(screen.getByRole("tab", { name: "Tab 2" })); expect(screen.getByText("Content 2")).toBeInTheDocument(); @@ -38,7 +38,7 @@ describe("Tabs", () => { ...sampleTabs, { value: "tab4", label: "Disabled", content:
Content 4
, disabled: true }, ]; - render(); + render(); const disabledTab = screen.getByRole("tab", { name: "Disabled" }); expect(disabledTab).toHaveAttribute("data-disabled"); diff --git a/src/components/Tabs/Tabs.stories.tsx b/src/components/Tab/Tab.stories.tsx similarity index 81% rename from src/components/Tabs/Tabs.stories.tsx rename to src/components/Tab/Tab.stories.tsx index c1d0a7d..4efc3a5 100644 --- a/src/components/Tabs/Tabs.stories.tsx +++ b/src/components/Tab/Tab.stories.tsx @@ -1,9 +1,9 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Tabs } from "./Tabs"; +import { Tab } from "./Tab"; const meta = { - title: "Components/Tabs", - component: Tabs, + title: "Components/Tab", + component: Tab, parameters: { layout: "padded", }, @@ -51,12 +51,12 @@ export const Default: Story = { export const Intents: Story = { render: () => (
- - - - - - + + + + + +
), }; diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tab/Tab.tsx similarity index 84% rename from src/components/Tabs/Tabs.tsx rename to src/components/Tab/Tab.tsx index 97ff699..fdbe9b5 100644 --- a/src/components/Tabs/Tabs.tsx +++ b/src/components/Tab/Tab.tsx @@ -1,25 +1,25 @@ import React from "react"; import clsx from "clsx"; import { Tabs as BaseUITabs } from "@base-ui/react/tabs"; -import * as styles from "./Tabs.css"; +import * as styles from "./Tab.css"; import { lightTheme, type ColorIntent } from "@/tokens"; import { useTheme } from "@/providers"; -export type TabsIntent = ColorIntent; -export type TabsOrientation = "horizontal" | "vertical"; +export type TabIntent = ColorIntent; +export type TabOrientation = "horizontal" | "vertical"; -export interface Tab { +export interface TabItem { value: string; label: React.ReactNode; content: React.ReactNode; disabled?: boolean; } -export interface TabsProps { +export interface TabProps { /** * Array of tab objects */ - tabs: Tab[]; + tabs: TabItem[]; /** * The currently selected tab value */ @@ -36,12 +36,12 @@ export interface TabsProps { * Color intent * @default "primary" */ - intent?: TabsIntent; + intent?: TabIntent; /** * Orientation of tabs * @default "horizontal" */ - orientation?: TabsOrientation; + orientation?: TabOrientation; /** * Custom class name */ @@ -49,11 +49,11 @@ export interface TabsProps { } /** - * Tabs component built on Base UI - * + * Tab component built on Base UI + * * @example * ```tsx - * Content 1 }, * { value: "tab2", label: "Tab 2", content:
Content 2
} @@ -62,7 +62,7 @@ export interface TabsProps { * /> * ``` */ -export const Tabs = ({ +export const Tab = ({ tabs, value, defaultValue, @@ -70,7 +70,7 @@ export const Tabs = ({ intent = "primary", orientation = "horizontal", className, -}: TabsProps) => { +}: TabProps) => { const themeContext = useTheme(); const themeClass = themeContext?.themeClass ?? lightTheme; diff --git a/src/components/Tab/index.ts b/src/components/Tab/index.ts new file mode 100644 index 0000000..720bf91 --- /dev/null +++ b/src/components/Tab/index.ts @@ -0,0 +1,3 @@ +export { Tab } from "./Tab"; +export type { TabProps, TabItem, TabIntent, TabOrientation } from "./Tab"; + diff --git a/src/components/Tabs/index.ts b/src/components/Tabs/index.ts deleted file mode 100644 index e84dd8d..0000000 --- a/src/components/Tabs/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { Tabs } from "./Tabs"; -export type { TabsProps, Tab, TabsIntent, TabsOrientation } from "./Tabs"; - diff --git a/src/components/index.ts b/src/components/index.ts index d41e75b..7352c40 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -4,7 +4,7 @@ export * from "./Checkbox"; export * from "./Dialog"; export * from "./Dropdown"; export * from "./Switch"; -export * from "./Tabs"; +export * from "./Tab"; export * from "./Text"; export * from "./TextField"; From 31fef36eebdad437f8456ddba9ae39b1c265e0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9B=90=EC=A3=BC?= <3o14.dev@gmail.com> Date: Fri, 9 Jan 2026 00:15:23 +0900 Subject: [PATCH 25/26] chore: type error --- src/components/Badge/Badge.spec.tsx | 2 +- src/components/Button/Button.tsx | 80 +++++++++++--------- src/components/Checkbox/Checkbox.stories.tsx | 11 +-- src/components/Dropdown/Dropdown.stories.tsx | 58 +++++++------- src/components/Switch/Switch.stories.tsx | 6 +- src/components/Switch/Switch.tsx | 22 +++--- src/components/Tab/Tab.stories.tsx | 20 ++++- src/components/Tab/Tab.tsx | 2 +- src/components/Text/Text.tsx | 5 +- tsconfig.app.json | 2 +- 10 files changed, 117 insertions(+), 91 deletions(-) diff --git a/src/components/Badge/Badge.spec.tsx b/src/components/Badge/Badge.spec.tsx index c9c2b56..9233b10 100644 --- a/src/components/Badge/Badge.spec.tsx +++ b/src/components/Badge/Badge.spec.tsx @@ -128,7 +128,7 @@ describe("Badge", () => { }); it("renders with dot when showDot is true", () => { - const { container } = render(With Dot); + render(With Dot); const badge = screen.getByText("With Dot"); const dot = badge.querySelector("span"); expect(dot).toBeInTheDocument(); diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 438609e..3274878 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -10,8 +10,10 @@ export type ButtonSize = "small" | "medium" | "large" | "xlarge"; export type ButtonIntent = ColorIntent; export type ButtonRounded = "small" | "medium" | "large"; -export interface ButtonProps - extends Omit, "color"> { +export interface ButtonProps extends Omit< + React.ButtonHTMLAttributes, + "color" +> { variant?: ButtonVariant; size?: ButtonSize; intent?: ButtonIntent; @@ -30,37 +32,45 @@ export interface ButtonProps * * ``` */ -export const Button = ({ - variant = "solid", - size = "medium", - intent = "primary", - rounded = "medium", - fullWidth = false, - disabled = false, - children, - className, - ...props -}: ButtonProps) => { - const themeContext = useTheme(); - const themeClass = themeContext?.themeClass ?? lightTheme; +export const Button = React.forwardRef( + ( + { + variant = "solid", + size = "medium", + intent = "primary", + rounded = "medium", + fullWidth = false, + disabled = false, + children, + className, + ...props + }, + ref + ) => { + const themeContext = useTheme(); + const themeClass = themeContext?.themeClass ?? lightTheme; - return ( - - {children} - - ); -}; + return ( + + {children} + + ); + } +); + +Button.displayName = "Button"; diff --git a/src/components/Checkbox/Checkbox.stories.tsx b/src/components/Checkbox/Checkbox.stories.tsx index 04372be..51f809f 100644 --- a/src/components/Checkbox/Checkbox.stories.tsx +++ b/src/components/Checkbox/Checkbox.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; import { Checkbox } from "./Checkbox"; const meta = { @@ -344,11 +345,11 @@ export const ControlledExample: Story = { return (
- setChecked(e.target.checked)} - /> + setChecked(checked)} + />

Current state: {checked ? "βœ… Checked" : "⬜ Unchecked"}

@@ -177,21 +177,21 @@ export const Sizes: Story = { @@ -222,42 +222,42 @@ export const Intents: Story = { @@ -285,21 +285,21 @@ export const Rounded: Story = { @@ -325,9 +325,9 @@ export const FullWidth: Story = { { + onValueChange={(newValue) => { setValue(newValue); - args.onChange?.(newValue); + args.onValueChange?.(newValue); }} placeholder={args.placeholder ?? "Full width dropdown"} /> @@ -374,9 +374,9 @@ export const WithDisabledOptions: Story = { {...args} options={optionsWithDisabled} value={value} - onChange={(newValue) => { + onValueChange={(newValue) => { setValue(newValue); - args.onChange?.(newValue); + args.onValueChange?.(newValue); }} placeholder={args.placeholder ?? "Select a fruit"} /> @@ -405,9 +405,9 @@ export const ManyOptions: Story = { {...args} options={manyOptions} value={value} - onChange={(newValue) => { + onValueChange={(newValue) => { setValue(newValue); - args.onChange?.(newValue); + args.onValueChange?.(newValue); }} placeholder={args.placeholder ?? "Select an option"} /> @@ -469,7 +469,7 @@ export const FormExample: Story = { @@ -489,7 +489,7 @@ export const FormExample: Story = { @@ -509,7 +509,7 @@ export const FormExample: Story = { @@ -561,9 +561,9 @@ export const ControlledExample: Story = { { + onValueChange={(newValue) => { setValue(newValue); - args.onChange?.(newValue); + args.onValueChange?.(newValue); }} />

@@ -614,9 +614,9 @@ export const Playground: Story = { { + onValueChange={(newValue) => { setValue(newValue); - args.onChange?.(newValue); + args.onValueChange?.(newValue); }} /> diff --git a/src/components/Switch/Switch.stories.tsx b/src/components/Switch/Switch.stories.tsx index 3351664..d8d0323 100644 --- a/src/components/Switch/Switch.stories.tsx +++ b/src/components/Switch/Switch.stories.tsx @@ -92,7 +92,7 @@ export const Checked: Story = { setChecked(e.target.checked)} + onCheckedChange={(checked) => setChecked(checked)} /> ); }, @@ -320,7 +320,7 @@ export const ControlledExample: Story = { setChecked(e.target.checked)} + onCheckedChange={(checked) => setChecked(checked)} />

Current state: {checked ? "🟒 ON" : "βšͺ OFF"}