diff --git a/.changeset/beige-cameras-lick.md b/.changeset/beige-cameras-lick.md new file mode 100644 index 000000000..416355db1 --- /dev/null +++ b/.changeset/beige-cameras-lick.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Make menu props optional for open method in `useAnchoredMenu`. diff --git a/.changeset/clean-carrots-flash.md b/.changeset/clean-carrots-flash.md new file mode 100644 index 000000000..a100f3138 --- /dev/null +++ b/.changeset/clean-carrots-flash.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Increase search input size for FilterListBox and CommandMenu. diff --git a/.changeset/gentle-timers-know.md b/.changeset/gentle-timers-know.md new file mode 100644 index 000000000..f6f781ce4 --- /dev/null +++ b/.changeset/gentle-timers-know.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Add default menu props to `useAnchoredMenu`. diff --git a/.changeset/happy-cycles-pretend.md b/.changeset/happy-cycles-pretend.md new file mode 100644 index 000000000..af582b0ad --- /dev/null +++ b/.changeset/happy-cycles-pretend.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Add `allValueProps`, `customValueProps` and `newCustomValueProps` to customize the additional options in ListBox, FilterListBox and FilterPicker. diff --git a/.changeset/tricky-carpets-laugh.md b/.changeset/tricky-carpets-laugh.md new file mode 100644 index 000000000..4c53a4dae --- /dev/null +++ b/.changeset/tricky-carpets-laugh.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Sync opening state between FilterPicker instances and other triggers. diff --git a/.changeset/violet-months-obey.md b/.changeset/violet-months-obey.md new file mode 100644 index 000000000..05581b17c --- /dev/null +++ b/.changeset/violet-months-obey.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": minor +--- + +Add itemBase and ItemButton components packed with lots of features. ItemBase is now used as a base for all Item components in Menu, CommandMenu, ListBox, FilterListBox, FilterPicker, Select, ComboBox. diff --git a/.size-limit.cjs b/.size-limit.cjs index 9e9ad2c3c..caae05973 100644 --- a/.size-limit.cjs +++ b/.size-limit.cjs @@ -20,7 +20,7 @@ module.exports = [ }), ); }, - limit: '284kB', + limit: '285kB', }, { name: 'Tree shaking (just a Button)', diff --git a/.storybook/main.js b/.storybook/main.js index 4ca907008..8525ab9af 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,7 +1,6 @@ // @ts-check import remarkGfm from 'remark-gfm'; -/** @type {import('@storybook/core-common').StorybookConfig} */ const config = { staticDirs: ['../public'], @@ -23,8 +22,6 @@ const config = { addons: [ '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-interactions', { name: 'storybook-addon-turbo-build', options: { @@ -44,9 +41,5 @@ const config = { }, }, ], - - docs: { - autodocs: true, - }, }; export default config; diff --git a/.storybook/preview.jsx b/.storybook/preview.jsx index 0f6765d36..eaf7db55a 100644 --- a/.storybook/preview.jsx +++ b/.storybook/preview.jsx @@ -1,7 +1,6 @@ -import { DocsContainer } from '@storybook/addon-docs'; -import { configure } from '@storybook/test'; import isChromatic from 'chromatic/isChromatic'; import { config } from 'react-transition-group'; +import { configure } from 'storybook/test'; import { Root } from '../src'; @@ -37,13 +36,7 @@ export const parameters = { return aTitle.localeCompare(bTitle, undefined, { numeric: true }); }, }, - docs: { - container: ({ children, context }) => ( - - {children} - - ), - }, + docs: {}, backgrounds: { default: 'transparent', values: [ @@ -64,3 +57,4 @@ export const decorators = [ ), ]; +export const tags = ['autodocs']; diff --git a/package.json b/package.json index 3a42ffa78..1418bd3f4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "fix": "npm-run-all -p eslint:fix prettier:fix", "storybook": "STORYBOOK_MODE=stories storybook dev -p 6060", "build-storybook": "STORYBOOK_MODE=stories storybook build", - "build-docs": "STORYBOOK_MODE=docs storybook build --docs -o storybook-docs", + "build-docs": "storybook build -o storybook-docs", "build-static-docs": "npm run build-docs && node scripts/snapshot-docs.js", "size": "size-limit", "prepare": "husky install", @@ -115,16 +115,9 @@ "@size-limit/webpack": "^8.2.4", "@size-limit/webpack-why": "^8.2.4", "@statoscope/cli": "^5.20.1", - "@storybook/addon-actions": "^8.6.12", - "@storybook/addon-docs": "^8.6.12", - "@storybook/addon-essentials": "^8.6.12", - "@storybook/addon-interactions": "^8.6.12", - "@storybook/addon-links": "^8.6.12", - "@storybook/addon-mdx-gfm": "^8.6.12", - "@storybook/blocks": "^8.6.12", - "@storybook/react": "^8.6.12", - "@storybook/react-vite": "^8.6.12", - "@storybook/test": "^8.6.12", + "@storybook/addon-docs": "^9.1.2", + "@storybook/addon-links": "^9.1.2", + "@storybook/react-vite": "^9.1.2", "@swc/core": "^1.3.36", "@swc/jest": "^0.2.36", "@testing-library/dom": "^9.3.4", @@ -153,7 +146,7 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-storybook": "^0.12.0", + "eslint-plugin-storybook": "^9.1.2", "husky": "^6.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -170,7 +163,7 @@ "react-test-renderer": "^18.2.0", "rimraf": "^6.0.1", "size-limit": "^8.2.6", - "storybook": "^8.6.12", + "storybook": "^9.1.2", "storybook-addon-turbo-build": "^2.0.1", "styled-components": "^6.1.8", "swc-loader": "^0.2.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97d3ea9ef..d265675f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,36 +162,15 @@ importers: '@statoscope/cli': specifier: ^5.20.1 version: 5.20.1 - '@storybook/addon-actions': - specifier: ^8.6.12 - version: 8.6.12(storybook@8.6.12(prettier@3.2.5)) '@storybook/addon-docs': - specifier: ^8.6.12 - version: 8.6.12(@types/react@18.2.70)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-essentials': - specifier: ^8.6.12 - version: 8.6.12(@types/react@18.2.70)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-interactions': - specifier: ^8.6.12 - version: 8.6.12(storybook@8.6.12(prettier@3.2.5)) + specifier: ^9.1.2 + version: 9.1.2(@types/react@18.2.70)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))) '@storybook/addon-links': - specifier: ^8.6.12 - version: 8.6.12(react@18.2.0)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-mdx-gfm': - specifier: ^8.6.12 - version: 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/blocks': - specifier: ^8.6.12 - version: 8.6.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/react': - specifier: ^8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.2.5)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5))(typescript@5.6.3) + specifier: ^9.1.2 + version: 9.1.2(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))) '@storybook/react-vite': - specifier: ^8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.2.5)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.40.0)(storybook@8.6.12(prettier@3.2.5))(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) - '@storybook/test': - specifier: ^8.6.12 - version: 8.6.12(storybook@8.6.12(prettier@3.2.5)) + specifier: ^9.1.2 + version: 9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.40.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) '@swc/core': specifier: ^1.3.36 version: 1.3.36 @@ -277,8 +256,8 @@ importers: specifier: ^5.2.0 version: 5.2.0(eslint@9.25.1) eslint-plugin-storybook: - specifier: ^0.12.0 - version: 0.12.0(eslint@9.25.1)(typescript@5.6.3) + specifier: ^9.1.2 + version: 9.1.2(eslint@9.25.1)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(typescript@5.6.3) husky: specifier: ^6.0.0 version: 6.0.0 @@ -328,8 +307,8 @@ importers: specifier: ^8.2.6 version: 8.2.6 storybook: - specifier: ^8.6.12 - version: 8.6.12(prettier@3.2.5) + specifier: ^9.1.2 + version: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) storybook-addon-turbo-build: specifier: ^2.0.1 version: 2.0.1(webpack@5.76.1(@swc/core@1.3.36)(esbuild@0.25.3)) @@ -1716,11 +1695,11 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0': - resolution: {integrity: sha512-qYDdL7fPwLRI+bJNurVcis+tNgJmvWjH4YTBGXTA8xMuxFrnAz6E5o35iyzyKbq5J5Lr8mJGfrR5GXl+WGwhgQ==} + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1': + resolution: {integrity: sha512-J4BaTocTOYFkMHIra1JDWrMWpNmBl4EkplIwHEsV8aeUOtdWjwSnln9U7twjMFTAEB7mptNtSKyVi1Y2W9sDJw==} peerDependencies: typescript: '>= 4.3.x' - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: typescript: optional: true @@ -1743,6 +1722,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -2606,113 +2588,30 @@ packages: '@statoscope/webpack-ui@5.26.2': resolution: {integrity: sha512-01RYHyG2nrif9Y5i717EI6jUMqdypbrOMdqpNUBFlw2rmaEB5t21V35b5Vd0pZEgesKNijE3ULvP7EQ37jEbIg==} - '@storybook/addon-actions@8.6.12': - resolution: {integrity: sha512-B5kfiRvi35oJ0NIo53CGH66H471A3XTzrfaa6SxXEJsgxxSeKScG5YeXcCvLiZfvANRQ7QDsmzPUgg0o3hdMXw==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-backgrounds@8.6.12': - resolution: {integrity: sha512-lmIAma9BiiCTbJ8YfdZkXjpnAIrOUcgboLkt1f6XJ78vNEMnLNzD9gnh7Tssz1qrqvm34v9daDjIb+ggdiKp3Q==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-controls@8.6.12': - resolution: {integrity: sha512-9VSRPJWQVb9wLp21uvpxDGNctYptyUX0gbvxIWOHMH3R2DslSoq41lsC/oQ4l4zSHVdL+nq8sCTkhBxIsjKqdQ==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-docs@8.6.12': - resolution: {integrity: sha512-kEezQjAf/p3SpDzLABgg4fbT48B6dkT2LiZCKTRmCrJVtuReaAr4R9MMM6Jsph6XjbIj/SvOWf3CMeOPXOs9sg==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-essentials@8.6.12': - resolution: {integrity: sha512-Y/7e8KFlttaNfv7q2zoHMPdX6hPXHdsuQMAjYl5NG9HOAJREu4XBy4KZpbcozRe4ApZ78rYsN/MO1EuA+bNMIA==} + '@storybook/addon-docs@9.1.2': + resolution: {integrity: sha512-U3eHJ8lQFfEZ/OcgdKkUBbW2Y2tpAsHfy8lQOBgs5Pgj9biHEJcUmq+drOS/sJhle673eoBcUFmspXulI4KP1w==} peerDependencies: - storybook: ^8.6.12 + storybook: ^9.1.2 - '@storybook/addon-highlight@8.6.12': - resolution: {integrity: sha512-9FITVxdoycZ+eXuAZL9ElWyML/0fPPn9UgnnAkrU7zkMi+Segq/Tx7y+WWanC5zfWZrXAuG6WTOYEXeWQdm//w==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-interactions@8.6.12': - resolution: {integrity: sha512-cTAJlTq6uVZBEbtwdXkXoPQ4jHOAGKQnYSezBT4pfNkdjn/FnEeaQhMBDzf14h2wr5OgBnJa6Lmd8LD9ficz4A==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-links@8.6.12': - resolution: {integrity: sha512-AfKujFHoAxhxq4yu+6NwylltS9lf5MPs1eLLXvOlwo3l7Y/c68OdxJ7j68vLQhs9H173WVYjKyjbjFxJWf/YYg==} + '@storybook/addon-links@9.1.2': + resolution: {integrity: sha512-drAWdhn5cRo5WcaORoCYfJ6tgTAw1m+ZJb1ICyNtTU6i/0nErV8jJjt7AziUcUIyzaGVJAkAMNC3+R4uDPSFDA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.6.12 - peerDependenciesMeta: - react: - optional: true - - '@storybook/addon-mdx-gfm@8.6.12': - resolution: {integrity: sha512-OKI5+O8xyK8axGPFwkl38NGJ6Rjf7kyhiBPxw5NuHOjOnU/FL4Pw3QmY47TT96TVws27vP3gF5+FX8lj3Dd3rQ==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-measure@8.6.12': - resolution: {integrity: sha512-tACmwqqOvutaQSduw8SMb62wICaT1rWaHtMN3vtWXuxgDPSdJQxLP+wdVyRYMAgpxhLyIO7YRf++Hfha9RHgFg==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-outline@8.6.12': - resolution: {integrity: sha512-1ylwm+n1s40S91No0v9T4tCjZORu3GbnjINlyjYTDLLhQHyBQd3nWR1Y1eewU4xH4cW9SnSLcMQFS/82xHqU6A==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-toolbars@8.6.12': - resolution: {integrity: sha512-HEcSzo1DyFtIu5/ikVOmh5h85C1IvK9iFKSzBR6ice33zBOaehVJK+Z5f487MOXxPsZ63uvWUytwPyViGInj+g==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/addon-viewport@8.6.12': - resolution: {integrity: sha512-EXK2LArAnABsPP0leJKy78L/lbMWow+EIJfytEP5fHaW4EhMR6h7Hzaqzre6U0IMMr/jVFa1ci+m0PJ0eQc2bw==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/blocks@8.6.12': - resolution: {integrity: sha512-DohlTq6HM1jDbHYiXL4ZvZ00VkhpUp5uftzj/CZDLY1fYHRjqtaTwWm2/OpceivMA8zDitLcq5atEZN+f+siTg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^8.6.12 + storybook: ^9.1.2 peerDependenciesMeta: react: optional: true - react-dom: - optional: true - '@storybook/builder-vite@8.6.12': - resolution: {integrity: sha512-Gju21ud/3Qw4v2vLNaa5SuJECsI9ICNRr2G0UyCCzRvCHg8jpA9lDReu2NqhLDyFIuDG+ZYT38gcaHEUoNQ8KQ==} + '@storybook/builder-vite@9.1.2': + resolution: {integrity: sha512-5Y7e5wnSzFxCGP63UNRRZVoxHe1znU4dYXazJBobAlEcUPBk7A0sH2716tA6bS4oz92oG9tgvn1g996hRrw4ow==} peerDependencies: - storybook: ^8.6.12 - vite: ^4.0.0 || ^5.0.0 || ^6.0.0 + storybook: ^9.1.2 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/components@8.6.12': - resolution: {integrity: sha512-FiaE8xvCdvKC2arYusgtlDNZ77b8ysr8njAYQZwwaIHjy27TbR2tEpLDCmUwSbANNmivtc/xGEiDDwcNppMWlQ==} + '@storybook/csf-plugin@9.1.2': + resolution: {integrity: sha512-bfMh6r+RieBLPWtqqYN70le2uTE4JzOYPMYSCagHykUti3uM/1vRFaZNkZtUsRy5GwEzE5jLdDXioG1lOEeT2Q==} 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.12': - resolution: {integrity: sha512-t+ZuDzAlsXKa6tLxNZT81gEAt4GNwsKP/Id2wluhmUWD/lwYW0uum1JiPUuanw8xD6TdakCW/7ULZc7aQUBLCQ==} - peerDependencies: - prettier: ^2 || ^3 - peerDependenciesMeta: - prettier: - optional: true - - '@storybook/csf-plugin@8.6.12': - resolution: {integrity: sha512-6s8CnP1aoKPb3XtC0jRLUp8M5vTA8RhGAwQDKUsFpCC7g89JR9CaKs9FY2ZSzsNbjR15uASi7b3K8BzeYumYQg==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/csf@0.1.13': - resolution: {integrity: sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==} + storybook: ^9.1.2 '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} @@ -2724,66 +2623,34 @@ packages: 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/instrumenter@8.6.12': - resolution: {integrity: sha512-VK5fYAF8jMwWP/u3YsmSwKGh+FeSY8WZn78flzRUwirp2Eg1WWjsqPRubAk7yTpcqcC/km9YMF3KbqfzRv2s/A==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/manager-api@8.6.12': - resolution: {integrity: sha512-O0SpISeJLNTQvhSBOsWzzkCgs8vCjOq1578rwqHlC6jWWm4QmtfdyXqnv7rR1Hk08kQ+Dzqh0uhwHx0nfwy4nQ==} - 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.12': - resolution: {integrity: sha512-84FE3Hrs0AYKHqpDZOwx1S/ffOfxBdL65lhCoeI8GoWwCkzwa9zEP3kvXBo/BnEDO7nAfxvMhjASTZXbKRJh5Q==} - peerDependencies: - storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - - '@storybook/react-dom-shim@8.6.12': - resolution: {integrity: sha512-51QvoimkBzYs8s3rCYnY5h0cFqLz/Mh0vRcughwYaXckWzDBV8l67WBO5Xf5nBsukCbWyqBVPpEQLww8s7mrLA==} + '@storybook/react-dom-shim@9.1.2': + resolution: {integrity: sha512-nw7BLAHCJswPZGsuL0Gs2AvFUWriusCTgPBmcHppSw/AqvT4XRFRDE+5q3j04/XKuZBrAA2sC4L+HuC0uzEChQ==} 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.12 + storybook: ^9.1.2 - '@storybook/react-vite@8.6.12': - resolution: {integrity: sha512-UA2Kule99oyFgHdhcuhrRwCKyWu/yMbqbl9U7NwowFHNwWWFjVMMir/AmfShb/H1C1DQ3LqOad6/QwJyPLjP8g==} - engines: {node: '>=18.0.0'} + '@storybook/react-vite@9.1.2': + resolution: {integrity: sha512-dv3CBjOzmMoSyIotMtdmsBRjB25i19OjFP0IZqauLeUoVm6QddILW7JRcZVLrzhATyBEn+sEAdWQ4j79Z11HAg==} + engines: {node: '>=20.0.0'} peerDependencies: - '@storybook/test': 8.6.12 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.12 - vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - peerDependenciesMeta: - '@storybook/test': - optional: true + storybook: ^9.1.2 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - '@storybook/react@8.6.12': - resolution: {integrity: sha512-NzxlHLA5DkDgZM/dMwTYinuzRs6rsUPmlqP+NIv6YaciQ4NGnTYyOC7R/SqI6HHFm8ZZ5eMYvpfiFmhZ9rU+rQ==} - engines: {node: '>=18.0.0'} + '@storybook/react@9.1.2': + resolution: {integrity: sha512-VVXu1HrhDExj/yj+heFYc8cgIzBruXy1UYT3LW0WiJyadgzYz3J41l/Lf/j2FCppyxwlXb19Uv51plb1F1C77w==} + engines: {node: '>=20.0.0'} peerDependencies: - '@storybook/test': 8.6.12 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.12 - typescript: '>= 4.2.x' + storybook: ^9.1.2 + typescript: '>= 4.9.x' peerDependenciesMeta: - '@storybook/test': - optional: true typescript: optional: true - '@storybook/test@8.6.12': - resolution: {integrity: sha512-0BK1Eg+VD0lNMB1BtxqHE3tP9FdkUmohtvWG7cq6lWvMrbCmAmh3VWai3RMCCDOukPFpjabOr8BBRLVvhNpv2w==} - peerDependencies: - storybook: ^8.6.12 - - '@storybook/theming@8.6.12': - resolution: {integrity: sha512-6VjZg8HJ2Op7+KV7ihJpYrDnFtd9D1jrQnUS8LckcpuBXrIEbaut5+34ObY8ssQnSqkk2GwIZBBBQYQBCVvkOw==} - peerDependencies: - storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - '@swc/core-darwin-arm64@1.3.36': resolution: {integrity: sha512-lsP+C8p9cC/Vd9uAbtxpEnM8GoJI/MMnVuXak7OlxOtDH9/oTwmAcAQTfNGNaH19d2FAIRwf+5RbXCPnxa2Zjw==} engines: {node: '>=10'} @@ -2877,10 +2744,6 @@ packages: '@tanstack/virtual-core@3.13.12': resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} - '@testing-library/dom@10.4.0': - resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} - engines: {node: '>=18'} - '@testing-library/dom@9.3.4': resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} engines: {node: '>=14'} @@ -2889,6 +2752,10 @@ packages: resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/jest-dom@6.6.4': + resolution: {integrity: sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/react-hooks@8.0.1': resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==} engines: {node: '>=12'} @@ -2912,12 +2779,6 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - '@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'} @@ -2974,9 +2835,15 @@ packages: '@types/babel__traverse@7.18.5': resolution: {integrity: sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/debug@4.1.8': resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/doctrine@0.0.9': resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} @@ -3091,9 +2958,6 @@ packages: '@types/unist@3.0.2': resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/yargs-parser@21.0.0': resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -3238,23 +3102,28 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 - '@vitest/expect@2.0.5': - resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@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/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@vitest/spy@2.0.5': - resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/utils@2.0.5': - resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/utils@2.1.9': - resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} '@webassemblyjs/ast@1.11.1': resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} @@ -3432,9 +3301,6 @@ packages: aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -3638,9 +3504,6 @@ packages: breakword@1.0.5: resolution: {integrity: sha512-ex5W9DoOQ/LUEU3PMdLs9ua/CYZl1678NUkKOdUSi8Aw5F1idieaiRURCBFJCwVcrD1J8Iy3vfWSloaMwO2qFg==} - browser-assert@1.2.1: - resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} - browserslist@4.24.0: resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -4343,11 +4206,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-storybook@0.12.0: - resolution: {integrity: sha512-Lg5I0+npTgiYgZ4KSvGWGDFZi3eOCNJPaWX0c9rTEEXC5wvooOClsP9ZtbI4hhFKyKgYR877KiJxbRTSJq9gWA==} - engines: {node: '>= 18'} + eslint-plugin-storybook@9.1.2: + resolution: {integrity: sha512-EQa/kChrYrekxv36q3pvW57anqxMlAP4EdPXEDyA/EDrCQJaaTbWEdsMnVZtD744RjPP0M5wzaUjHbMhNooAwQ==} + engines: {node: '>=20.0.0'} peerDependencies: eslint: '>=8' + storybook: ^9.1.2 eslint-plugin-testing-library@5.11.1: resolution: {integrity: sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==} @@ -4510,6 +4374,10 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + find-yarn-workspace-root2@1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} @@ -5214,10 +5082,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsdoc-type-pratt-parser@4.1.0: - resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} - engines: {node: '>=12.0.0'} - jsdom@20.0.3: resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} engines: {node: '>=14'} @@ -5359,6 +5223,10 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -5417,6 +5285,9 @@ packages: loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + loupe@3.2.0: + resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} + lru-cache@10.2.0: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} @@ -5439,9 +5310,8 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.27.0: - resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} - engines: {node: '>=12'} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} @@ -5465,9 +5335,6 @@ packages: resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} engines: {node: '>=8'} - map-or-similar@1.5.0: - resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} - markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} @@ -5511,9 +5378,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - memoizerific@1.11.3: - resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} - memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} @@ -5828,6 +5692,10 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -5836,6 +5704,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -5869,6 +5741,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -5955,10 +5831,6 @@ packages: please-upgrade-node@3.2.0: resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} - polished@4.2.2: - resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} - engines: {node: '>=10'} - possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -6014,10 +5886,6 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -6082,9 +5950,9 @@ packages: peerDependencies: typescript: '>= 4.3.x' - react-docgen@7.0.3: - resolution: {integrity: sha512-i8aF1nyKInZnANZ4uZrH49qn1paRgBZ7wZiCNBMnenlPzEv0mRl+ShpTVEI6wZNl8sSc79xZkivtgLKQArcanQ==} - engines: {node: '>=16.14.0'} + react-docgen@8.0.0: + resolution: {integrity: sha512-kmob/FOTwep7DUWf9KjuenKX0vyvChr3oTdvvPt09V60Iz75FJp+T/0ZeHMbAfJj2WaVWqAPP5Hmm3PYzSPPKg==} + engines: {node: ^20.9.0 || >=22} react-dom@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} @@ -6492,8 +6360,8 @@ packages: storybook-addon-turbo-build@2.0.1: resolution: {integrity: sha512-NP9e42fOmhkRe93okDlmIJ+2m+j4c9HZSa8EQJPJiJBQiAZ6MrjL6v0jzMukcwhIlu91RtHSkjlACm3xbi9jWQ==} - storybook@8.6.12: - resolution: {integrity: sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==} + storybook@9.1.2: + resolution: {integrity: sha512-TYcq7WmgfVCAQge/KueGkVlM/+g33sQcmbATlC3X6y/g2FEeSSLGrb6E6d3iemht8oio+aY6ld3YOdAnMwx45Q==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -6706,12 +6574,12 @@ packages: resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} engines: {node: '>=12.0.0'} - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} tmp@0.0.33: @@ -6826,10 +6694,6 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -6885,6 +6749,10 @@ packages: resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} engines: {node: '>=4'} + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + unified@11.0.4: resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} @@ -6960,13 +6828,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - - uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true - v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -7149,6 +7010,18 @@ packages: utf-8-validate: optional: true + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -7204,6 +7077,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -8875,10 +8752,10 @@ snapshots: '@types/yargs': 17.0.24 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': dependencies: glob: 10.3.10 - magic-string: 0.27.0 + magic-string: 0.30.8 react-docgen-typescript: 2.2.2(typescript@5.6.3) vite: 6.3.2(@types/node@18.19.86)(terser@5.31.1) optionalDependencies: @@ -8901,6 +8778,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -10261,160 +10140,38 @@ snapshots: dependencies: '@statoscope/types': 5.22.0 - '@storybook/addon-actions@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - '@types/uuid': 9.0.8 - dequal: 2.0.3 - polished: 4.2.2 - storybook: 8.6.12(prettier@3.2.5) - uuid: 9.0.0 - - '@storybook/addon-backgrounds@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - - '@storybook/addon-controls@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - dequal: 2.0.3 - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - - '@storybook/addon-docs@8.6.12(@types/react@18.2.70)(storybook@8.6.12(prettier@3.2.5))': + '@storybook/addon-docs@9.1.2(@types/react@18.2.70)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))': dependencies: '@mdx-js/react': 3.0.1(@types/react@18.2.70)(react@18.2.0) - '@storybook/blocks': 8.6.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/csf-plugin': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/react-dom-shim': 8.6.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5)) + '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))) + '@storybook/icons': 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/react-dom-shim': 9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - transitivePeerDependencies: - - '@types/react' - - '@storybook/addon-essentials@8.6.12(@types/react@18.2.70)(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/addon-actions': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-backgrounds': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-controls': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-docs': 8.6.12(@types/react@18.2.70)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-highlight': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-measure': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-outline': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-toolbars': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/addon-viewport': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - storybook: 8.6.12(prettier@3.2.5) + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/addon-interactions@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - polished: 4.2.2 - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - - '@storybook/addon-links@8.6.12(react@18.2.0)(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - optionalDependencies: - react: 18.2.0 - - '@storybook/addon-mdx-gfm@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - remark-gfm: 4.0.1 - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - transitivePeerDependencies: - - supports-color - - '@storybook/addon-measure@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - storybook: 8.6.12(prettier@3.2.5) - tiny-invariant: 1.3.3 - - '@storybook/addon-outline@8.6.12(storybook@8.6.12(prettier@3.2.5))': + '@storybook/addon-links@9.1.2(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 - - '@storybook/addon-toolbars@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/addon-viewport@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - memoizerific: 1.11.3 - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/blocks@8.6.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/icons': 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - storybook: 8.6.12(prettier@3.2.5) - ts-dedent: 2.2.0 + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) optionalDependencies: react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - '@storybook/builder-vite@8.6.12(storybook@8.6.12(prettier@3.2.5))(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': + '@storybook/builder-vite@9.1.2(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': dependencies: - '@storybook/csf-plugin': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - browser-assert: 1.2.1 - storybook: 8.6.12(prettier@3.2.5) + '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))) + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) ts-dedent: 2.2.0 vite: 6.3.2(@types/node@18.19.86)(terser@5.31.1) - '@storybook/components@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/core@8.6.12(prettier@3.2.5)(storybook@8.6.12(prettier@3.2.5))': + '@storybook/csf-plugin@9.1.2(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))': dependencies: - '@storybook/theming': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - better-opn: 3.0.2 - browser-assert: 1.2.1 - esbuild: 0.25.3 - esbuild-register: 3.5.0(esbuild@0.25.3) - jsdoc-type-pratt-parser: 4.1.0 - process: 0.11.10 - recast: 0.23.6 - semver: 7.7.1 - util: 0.12.5 - ws: 8.13.0 - optionalDependencies: - prettier: 3.2.5 - transitivePeerDependencies: - - bufferutil - - storybook - - supports-color - - utf-8-validate - - '@storybook/csf-plugin@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - storybook: 8.6.12(prettier@3.2.5) + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) unplugin: 1.10.0 - '@storybook/csf@0.1.13': - dependencies: - type-fest: 2.19.0 - '@storybook/global@5.0.0': {} '@storybook/icons@1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': @@ -10422,78 +10179,42 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/instrumenter@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - '@vitest/utils': 2.1.9 - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/manager-api@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/preview-api@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - storybook: 8.6.12(prettier@3.2.5) - - '@storybook/react-dom-shim@8.6.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5))': + '@storybook/react-dom-shim@9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))': dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - storybook: 8.6.12(prettier@3.2.5) + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) - '@storybook/react-vite@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.2.5)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.40.0)(storybook@8.6.12(prettier@3.2.5))(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': + '@storybook/react-vite@9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.40.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.6.3)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) '@rollup/pluginutils': 5.1.0(rollup@4.40.0) - '@storybook/builder-vite': 8.6.12(storybook@8.6.12(prettier@3.2.5))(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.2.5)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5))(typescript@5.6.3) - find-up: 5.0.0 + '@storybook/builder-vite': 9.1.2(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) + '@storybook/react': 9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(typescript@5.6.3) + find-up: 7.0.0 magic-string: 0.30.8 react: 18.2.0 - react-docgen: 7.0.3 + react-docgen: 8.0.0 react-dom: 18.2.0(react@18.2.0) resolve: 1.22.8 - storybook: 8.6.12(prettier@3.2.5) + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) tsconfig-paths: 4.2.0 vite: 6.3.2(@types/node@18.19.86)(terser@5.31.1) - optionalDependencies: - '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.2.5)) transitivePeerDependencies: - rollup - supports-color - typescript - '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.2.5)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5))(typescript@5.6.3)': + '@storybook/react@9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(typescript@5.6.3)': dependencies: - '@storybook/components': 8.6.12(storybook@8.6.12(prettier@3.2.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/preview-api': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@storybook/react-dom-shim': 8.6.12(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.2.5)) - '@storybook/theming': 8.6.12(storybook@8.6.12(prettier@3.2.5)) + '@storybook/react-dom-shim': 9.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - storybook: 8.6.12(prettier@3.2.5) + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) optionalDependencies: - '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.2.5)) typescript: 5.6.3 - '@storybook/test@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.12(storybook@8.6.12(prettier@3.2.5)) - '@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.12(prettier@3.2.5) - - '@storybook/theming@8.6.12(storybook@8.6.12(prettier@3.2.5))': - dependencies: - storybook: 8.6.12(prettier@3.2.5) - '@swc/core-darwin-arm64@1.3.36': optional: true @@ -10565,17 +10286,6 @@ snapshots: '@tanstack/virtual-core@3.13.12': {} - '@testing-library/dom@10.4.0': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/runtime': 7.25.7 - '@types/aria-query': 5.0.1 - aria-query: 5.3.0 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - '@testing-library/dom@9.3.4': dependencies: '@babel/code-frame': 7.26.2 @@ -10597,6 +10307,16 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 + '@testing-library/jest-dom@6.6.4': + dependencies: + '@adobe/css-tools': 4.4.2 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + picocolors: 1.1.1 + redent: 3.0.0 + '@testing-library/react-hooks@8.0.1(@types/react@18.2.70)(react-dom@18.2.0(react@18.2.0))(react-test-renderer@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.25.7 @@ -10615,10 +10335,6 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@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@9.3.4)': dependencies: '@testing-library/dom': 9.3.4 @@ -10670,10 +10386,16 @@ snapshots: dependencies: '@babel/types': 7.27.0 + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/debug@4.1.8': dependencies: '@types/ms': 0.7.31 + '@types/deep-eql@4.0.2': {} + '@types/doctrine@0.0.9': {} '@types/eslint-scope@3.7.4': @@ -10787,8 +10509,6 @@ snapshots: '@types/unist@3.0.2': {} - '@types/uuid@9.0.8': {} - '@types/yargs-parser@21.0.0': {} '@types/yargs@17.0.24': @@ -11006,37 +10726,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/expect@2.0.5': + '@vitest/expect@3.2.4': dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 chai: 5.2.0 - tinyrainbow: 1.2.0 + tinyrainbow: 2.0.0 - '@vitest/pretty-format@2.0.5': + '@vitest/mocker@3.2.4(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1))': dependencies: - tinyrainbow: 1.2.0 - - '@vitest/pretty-format@2.1.9': - dependencies: - tinyrainbow: 1.2.0 + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.2(@types/node@18.19.86)(terser@5.31.1) - '@vitest/spy@2.0.5': + '@vitest/pretty-format@3.2.4': dependencies: - tinyspy: 3.0.2 + tinyrainbow: 2.0.0 - '@vitest/utils@2.0.5': + '@vitest/spy@3.2.4': dependencies: - '@vitest/pretty-format': 2.0.5 - estree-walker: 3.0.3 - loupe: 3.1.3 - tinyrainbow: 1.2.0 + tinyspy: 4.0.3 - '@vitest/utils@2.1.9': + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 2.1.9 - loupe: 3.1.3 - tinyrainbow: 1.2.0 + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.0 + tinyrainbow: 2.0.0 '@webassemblyjs/ast@1.11.1': dependencies: @@ -11219,10 +10937,6 @@ snapshots: dependencies: deep-equal: 2.2.1 - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 - aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -11497,8 +11211,6 @@ snapshots: dependencies: wcwidth: 1.0.1 - browser-assert@1.2.1: {} - browserslist@4.24.0: dependencies: caniuse-lite: 1.0.30001726 @@ -12398,12 +12110,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-storybook@0.12.0(eslint@9.25.1)(typescript@5.6.3): + eslint-plugin-storybook@9.1.2(eslint@9.25.1)(storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)))(typescript@5.6.3): dependencies: - '@storybook/csf': 0.1.13 '@typescript-eslint/utils': 8.31.0(eslint@9.25.1)(typescript@5.6.3) eslint: 9.25.1 - ts-dedent: 2.2.0 + storybook: 9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) transitivePeerDependencies: - supports-color - typescript @@ -12607,6 +12318,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + find-yarn-workspace-root2@1.2.16: dependencies: micromatch: 4.0.8 @@ -13493,8 +13210,6 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@4.1.0: {} - jsdom@20.0.3: dependencies: abab: 2.0.6 @@ -13681,6 +13396,10 @@ snapshots: dependencies: p-locate: 5.0.0 + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + lodash.camelcase@4.3.0: {} lodash.debounce@4.0.8: {} @@ -13727,6 +13446,8 @@ snapshots: loupe@3.1.3: {} + loupe@3.2.0: {} + lru-cache@10.2.0: {} lru-cache@11.1.0: {} @@ -13746,9 +13467,9 @@ snapshots: lz-string@1.5.0: {} - magic-string@0.27.0: + magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.5 magic-string@0.30.8: dependencies: @@ -13768,8 +13489,6 @@ snapshots: map-obj@4.3.0: {} - map-or-similar@1.5.0: {} - markdown-table@3.0.3: {} math-intrinsics@1.1.0: {} @@ -13881,10 +13600,6 @@ snapshots: dependencies: '@types/mdast': 4.0.3 - memoizerific@1.11.3: - dependencies: - map-or-similar: 1.5.0 - memorystream@0.3.1: {} meow@6.1.1: @@ -14319,6 +14034,10 @@ snapshots: dependencies: yocto-queue: 0.1.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -14327,6 +14046,10 @@ snapshots: dependencies: p-limit: 3.1.0 + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + p-map@2.1.0: {} p-try@2.2.0: {} @@ -14357,6 +14080,8 @@ snapshots: path-exists@4.0.0: {} + path-exists@5.0.0: {} + path-is-absolute@1.0.1: {} path-is-inside@1.0.2: {} @@ -14417,10 +14142,6 @@ snapshots: dependencies: semver-compare: 1.0.0 - polished@4.2.2: - dependencies: - '@babel/runtime': 7.25.7 - possible-typed-array-names@1.0.0: {} postcss-value-parser@4.2.0: {} @@ -14472,8 +14193,6 @@ snapshots: prismjs@1.30.0: {} - process@0.11.10: {} - prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -14565,7 +14284,7 @@ snapshots: dependencies: typescript: 5.6.3 - react-docgen@7.0.3: + react-docgen@8.0.0: dependencies: '@babel/core': 7.25.8 '@babel/traverse': 7.27.0 @@ -15100,15 +14819,29 @@ snapshots: transitivePeerDependencies: - webpack - storybook@8.6.12(prettier@3.2.5): + storybook@9.1.2(@testing-library/dom@9.3.4)(prettier@3.2.5)(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)): dependencies: - '@storybook/core': 8.6.12(prettier@3.2.5)(storybook@8.6.12(prettier@3.2.5)) + '@storybook/global': 5.0.0 + '@testing-library/jest-dom': 6.6.4 + '@testing-library/user-event': 14.6.1(@testing-library/dom@9.3.4) + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.3.2(@types/node@18.19.86)(terser@5.31.1)) + '@vitest/spy': 3.2.4 + better-opn: 3.0.2 + esbuild: 0.25.3 + esbuild-register: 3.5.0(esbuild@0.25.3) + recast: 0.23.6 + semver: 7.7.1 + ws: 8.18.3 optionalDependencies: prettier: 3.2.5 transitivePeerDependencies: + - '@testing-library/dom' - bufferutil + - msw - supports-color - utf-8-validate + - vite stream-transform@2.1.3: dependencies: @@ -15355,9 +15088,9 @@ snapshots: fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 - tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} - tinyspy@3.0.2: {} + tinyspy@4.0.3: {} tmp@0.0.33: dependencies: @@ -15463,8 +15196,6 @@ snapshots: type-fest@0.8.1: {} - type-fest@2.19.0: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -15534,6 +15265,8 @@ snapshots: unicode-property-aliases-ecmascript@2.1.0: {} + unicorn-magic@0.1.0: {} + unified@11.0.4: dependencies: '@types/unist': 3.0.2 @@ -15615,16 +15348,6 @@ snapshots: util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.1.1 - is-generator-function: 1.0.10 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - - uuid@9.0.0: {} - v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.1.0: @@ -15829,6 +15552,8 @@ snapshots: ws@8.13.0: {} + ws@8.18.3: {} + xml-name-validator@4.0.0: {} xmlchars@2.2.0: {} @@ -15882,4 +15607,6 @@ snapshots: yocto-queue@0.1.0: {} + yocto-queue@1.2.1: {} + zwitch@2.0.4: {} diff --git a/scripts/snapshot-docs.js b/scripts/snapshot-docs.js index 8efee59fc..9bc4ad866 100644 --- a/scripts/snapshot-docs.js +++ b/scripts/snapshot-docs.js @@ -24,15 +24,122 @@ const log = (...a) => console.log('[docs-snapshot]', ...a); /* ───── helper functions ─────────────────────────────────────────────── */ +/** + * Creates a mapping from component names to their story IDs + * e.g., "ItemButton" -> "actions-itembutton--docs" + */ +function createComponentMapping(availableIds) { + const mapping = new Map(); + + // Common component word patterns for splitting compound words + const patterns = [ + { suffix: 'button', replacement: 'Button' }, + { suffix: 'input', replacement: 'Input' }, + { suffix: 'box', replacement: 'Box' }, + { suffix: 'base', replacement: 'Base' }, + { suffix: 'menu', replacement: 'Menu' }, + { suffix: 'picker', replacement: 'Picker' }, + { suffix: 'group', replacement: 'Group' }, + { suffix: 'dialog', replacement: 'Dialog' }, + { suffix: 'trigger', replacement: 'Trigger' }, + { suffix: 'container', replacement: 'Container' }, + { suffix: 'wrapper', replacement: 'Wrapper' }, + ]; + + availableIds.forEach((storyId) => { + // Extract component name from story ID + // e.g., "actions-itembutton--docs" -> "itembutton" + const match = storyId.match(/^[^-]+-(.+)--docs$/); + if (match) { + const componentPart = match[1]; + + // First, handle kebab-case (has dashes) + if (componentPart.includes('-')) { + const pascalCase = componentPart + .split('-') + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(''); + mapping.set(pascalCase, storyId); + } else { + // Handle compound words without dashes + let bestMatch = null; + // let componentName = componentPart; + + // Try to split compound words using common patterns + for (const pattern of patterns) { + if (componentPart.endsWith(pattern.suffix)) { + const prefix = componentPart.slice(0, -pattern.suffix.length); + if (prefix.length > 0) { + const prefixCapitalized = + prefix.charAt(0).toUpperCase() + prefix.slice(1); + bestMatch = prefixCapitalized + pattern.replacement; + break; + } + } + } + + if (bestMatch) { + mapping.set(bestMatch, storyId); + // componentName = bestMatch; + } + + // Also add the simple capitalized version + const simpleCapital = + componentPart.charAt(0).toUpperCase() + componentPart.slice(1); + mapping.set(simpleCapital, storyId); + } + + // Always add the exact lowercase version + mapping.set(componentPart, storyId); + } + }); + + return mapping; +} + +/** + * Fixes /components/ComponentName links in source files to proper /docs/ format + * e.g., "/components/ItemButton" -> "/docs/actions-itembutton--docs" + */ +function fixComponentLinks(content, componentMapping) { + const brokenLinks = new Set(); + + // Pattern to match [text](/components/ComponentName) + const componentLinkPattern = /\[([^\]]*)\]\(\/components\/([^)]+)\)/g; + + const fixedContent = content.replace( + componentLinkPattern, + (match, linkText, componentName) => { + const storyId = componentMapping.get(componentName); + if (storyId) { + return `[${linkText}](/docs/${storyId})`; + } + // If not found, log it and leave the link as is + brokenLinks.add(componentName); + return match; + }, + ); + + // Log broken component links if any were found + if (brokenLinks.size > 0) { + log( + `⚠️ Found ${brokenLinks.size} unresolved component links:`, + Array.from(brokenLinks).join(', '), + ); + } + + return fixedContent; +} + /** * Fixes internal links in HTML content to point to the generated HTML files * Transforms links like: - * - `/docs/pickers-select--docs` -> `pickers-select--docs.html` - * - `?path=/docs/pickers-select--docs` -> `pickers-select--docs.html` - * - `/?path=/docs/pickers-select--docs` -> `pickers-select--docs.html` - * - `./?path=/docs/pickers-select--docs` -> `pickers-select--docs.html` + * - `href="/docs/pickers-select--docs"` -> `href="pickers-select--docs.html"` + * - `href="?path=/docs/pickers-select--docs"` -> `href="pickers-select--docs.html"` + * - `href="/?path=/docs/pickers-select--docs"` -> `href="pickers-select--docs.html"` + * - `href="./?path=/docs/pickers-select--docs"` -> `href="pickers-select--docs.html"` */ -function fixInternalLinks(html, availableIds) { +function fixInternalLinks(content, availableIds) { // Create a set of available IDs for quick lookup const availableIdsSet = new Set(availableIds); const brokenLinks = new Set(); @@ -49,10 +156,10 @@ function fixInternalLinks(html, availableIds) { /href="\.\/\?path=\/docs\/([^"]+--docs)"/g, ]; - let fixedHtml = html; + let fixedContent = content; linkPatterns.forEach((pattern) => { - fixedHtml = fixedHtml.replace(pattern, (match, storyId) => { + fixedContent = fixedContent.replace(pattern, (match, storyId) => { // Check if this story ID exists in our generated files if (availableIdsSet.has(storyId)) { return `href="${storyId}.html"`; @@ -71,7 +178,7 @@ function fixInternalLinks(html, availableIds) { ); } - return fixedHtml; + return fixedContent; } /* ───── 2. start local HTTP server ─────────────────────────────────── */ @@ -296,8 +403,91 @@ const indexHtml = ` await fs.writeFile(path.join(OUT_DIR, 'index.html'), indexHtml); log('✔ Table of contents generated at index.html'); +/* ───── 6. fix component links in source files ─────────────────────── */ +log('Fixing component links in source files...'); +const componentMapping = createComponentMapping(docStoryIds); +let sourceFilesFixed = 0; + +// Find all MDX files in the source directory +const findMdxFiles = async (dir) => { + const files = []; + const items = await fs.readdir(dir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(dir, item.name); + if (item.isDirectory()) { + files.push(...(await findMdxFiles(fullPath))); + } else if (item.isFile() && item.name.endsWith('.mdx')) { + files.push(fullPath); + } + } + + return files; +}; + +// Fix component links in MDX source files +const mdxFiles = await findMdxFiles('src'); + +for (const filePath of mdxFiles) { + try { + const content = await fs.readFile(filePath, 'utf8'); + const fixedContent = fixComponentLinks(content, componentMapping); + + // Only write back if the content actually changed + if (fixedContent !== content) { + await fs.writeFile(filePath, fixedContent); + sourceFilesFixed++; + } + } catch (error) { + log(`⚠️ Failed to fix component links in ${filePath}: ${error.message}`); + } +} + +log(`✔ Fixed component links in ${sourceFilesFixed} source files`); + +/* ───── 7. apply fixed links to original build files ──────────────── */ +log('Applying fixed links to original build files...'); +let originalFilesFixed = 0; + +// Find all HTML files in the build directory +const findHtmlFiles = async (dir) => { + const files = []; + const items = await fs.readdir(dir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(dir, item.name); + if (item.isDirectory()) { + files.push(...(await findHtmlFiles(fullPath))); + } else if (item.isFile() && item.name.endsWith('.html')) { + files.push(fullPath); + } + } + + return files; +}; + +// Fix links in HTML files from build directory only +const htmlFiles = await findHtmlFiles(BUILD_DIR); + +for (const filePath of htmlFiles) { + try { + const content = await fs.readFile(filePath, 'utf8'); + const fixedContent = fixInternalLinks(content, docStoryIds); + + // Only write back if the content actually changed + if (fixedContent !== content) { + await fs.writeFile(filePath, fixedContent); + originalFilesFixed++; + } + } catch (error) { + log(`⚠️ Failed to fix links in ${filePath}: ${error.message}`); + } +} + +log(`✔ Fixed internal links in ${originalFilesFixed} original build files`); + log('Snapshots written to', OUT_DIR); -log(`✔ Fixed internal links in ${succeeded.length} pages`); +log(`✔ Fixed internal links in ${succeeded.length} snapshot pages`); if (failed.length) { log('\n⚠️ Some pages were skipped:'); diff --git a/src/components/Item.tsx b/src/components/Item.tsx new file mode 100644 index 000000000..34d22b775 --- /dev/null +++ b/src/components/Item.tsx @@ -0,0 +1,22 @@ +import { ReactElement, ReactNode } from 'react'; +import { Item, ItemProps } from 'react-stately'; + +import { Styles } from '../tasty'; + +export interface CubeItemProps extends ItemProps { + qa?: string; + description?: ReactNode; + descriptionPlacement?: 'inline' | 'block' | 'auto'; + icon?: ReactNode | 'checkbox'; + prefix?: ReactNode; + suffix?: ReactNode; + rightIcon?: ReactNode; + styles?: Styles; + onAction?: () => void; + wrapper?: (item: ReactElement) => ReactElement; + [key: string]: any; +} + +const _Item = Item as unknown as (props: CubeItemProps) => ReactElement; + +export { _Item as Item }; diff --git a/src/components/actions/Action/Action.stories.tsx b/src/components/actions/Action/Action.stories.tsx index 58cde86bf..1e5f062c1 100644 --- a/src/components/actions/Action/Action.stories.tsx +++ b/src/components/actions/Action/Action.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/actions/Button/Button.docs.mdx b/src/components/actions/Button/Button.docs.mdx index 2d9d83074..57a533500 100644 --- a/src/components/actions/Button/Button.docs.mdx +++ b/src/components/actions/Button/Button.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from '@storybook/blocks'; +import { Meta, Story, Controls } from '@storybook/addon-docs/blocks'; import { Button } from './Button'; import * as ButtonStories from './Button.stories'; @@ -156,5 +156,5 @@ import { IconPlus } from '@tabler/icons-react'; ## Related Components -- [Link](/components/Link) – Semantic navigation styled like a button. -- [IconButton](/components/IconButton) – Dedicated icon-only button with simplified API. +- [Link](/docs/navigation-link--docs) – Semantic navigation styled like a button. +- [ItemButton](/docs/actions-itembutton--docs) – Interactive button built on ItemBase with icon support. diff --git a/src/components/actions/Button/Button.stories.tsx b/src/components/actions/Button/Button.stories.tsx index 9a2381ccb..4a90557cc 100644 --- a/src/components/actions/Button/Button.stories.tsx +++ b/src/components/actions/Button/Button.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { IconCaretDown, IconCoin } from '@tabler/icons-react'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/actions/Button/Button.tsx b/src/components/actions/Button/Button.tsx index 7be90222c..b1768a1ee 100644 --- a/src/components/actions/Button/Button.tsx +++ b/src/components/actions/Button/Button.tsx @@ -1,6 +1,32 @@ import { FocusableRef } from '@react-types/shared'; import { cloneElement, forwardRef, ReactElement, useMemo } from 'react'; +import { + DANGER_CLEAR_STYLES, + DANGER_LINK_STYLES, + DANGER_NEUTRAL_STYLES, + DANGER_OUTLINE_STYLES, + DANGER_PRIMARY_STYLES, + DANGER_SECONDARY_STYLES, + DEFAULT_CLEAR_STYLES, + DEFAULT_LINK_STYLES, + DEFAULT_NEUTRAL_STYLES, + DEFAULT_OUTLINE_STYLES, + DEFAULT_PRIMARY_STYLES, + DEFAULT_SECONDARY_STYLES, + SPECIAL_CLEAR_STYLES, + SPECIAL_LINK_STYLES, + SPECIAL_NEUTRAL_STYLES, + SPECIAL_OUTLINE_STYLES, + SPECIAL_PRIMARY_STYLES, + SPECIAL_SECONDARY_STYLES, + SUCCESS_CLEAR_STYLES, + SUCCESS_LINK_STYLES, + SUCCESS_NEUTRAL_STYLES, + SUCCESS_OUTLINE_STYLES, + SUCCESS_PRIMARY_STYLES, + SUCCESS_SECONDARY_STYLES, +} from '../../../data/item-themes'; import { LoadingIcon } from '../../../icons'; import { CONTAINER_STYLES, @@ -138,449 +164,6 @@ export const DEFAULT_BUTTON_STYLES = { }, } as const; -// ---------- DEFAULT THEME ---------- -export const DEFAULT_PRIMARY_STYLES: Styles = { - outline: { - '': '0 #purple-text.0', - focused: '1bw #purple-text', - }, - border: { - '': '#clear', - pressed: '#purple-text', - focused: '#purple-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#purple', - hovered: '#purple-text', - pressed: '#purple', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#white', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DEFAULT_SECONDARY_STYLES: Styles = { - border: { - '': '#purple.15', - pressed: '#purple.3', - focused: '#purple-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#purple.10', - hovered: '#purple.16', - pressed: '#purple-text.10', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#purple', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DEFAULT_OUTLINE_STYLES: Styles = { - border: { - '': '#dark.12', - focused: '#purple-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#dark.0', - hovered: '#dark.03', - 'pressed | (selected & !hovered)': '#dark.06', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#dark-02', - hovered: '#dark-02', - 'pressed | (selected & !hovered)': '#dark', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DEFAULT_NEUTRAL_STYLES: Styles = { - border: { - '': '#clear', - focused: '#purple-text', - }, - fill: { - '': '#dark.0', - hovered: '#dark.03', - 'pressed | (selected & !hovered)': '#dark.06', - }, - color: { - '': '#dark-02', - hovered: '#dark-02', - pressed: '#dark', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DEFAULT_CLEAR_STYLES: Styles = { - border: { - '': '#clear', - pressed: '#purple-text.10', - focused: '#purple-text', - }, - fill: { - '': '#purple.0', - hovered: '#purple.16', - 'pressed | (selected & !hovered)': '#purple.10', - }, - color: { - '': '#purple-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DEFAULT_LINK_STYLES: Styles = { - outline: { - '': '0 #purple-text.0', - focused: '1bw #purple-text', - }, - border: '0', - fill: { - '': '#clear', - }, - color: { - '': '#purple-text', - 'hovered & !pressed': '#purple', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -// ---------- DANGER THEME ---------- -export const DANGER_PRIMARY_STYLES: Styles = { - outline: { - '': '0 #danger-text.0', - focused: '1bw #danger-text', - }, - border: { - '': '#clear', - 'pressed | focused': '#danger-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#danger', - 'hovered & !pressed': '#danger-text', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#white', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DANGER_SECONDARY_STYLES: Styles = { - border: { - '': '#danger.15', - pressed: '#danger.3', - focused: '#danger-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#danger.05', - 'hovered & !pressed': '#danger.1', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#danger', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DANGER_OUTLINE_STYLES: Styles = { - border: { - '': '#danger.15', - pressed: '#danger.3', - focused: '#danger-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#danger.0', - hovered: '#danger.1', - 'pressed | (selected & !hovered)': '#danger.05', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#danger-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DANGER_NEUTRAL_STYLES: Styles = { - border: { - '': '#clear', - focused: '#danger-text', - }, - fill: { - '': '#dark.0', - hovered: '#dark.04', - 'pressed | (selected & !hovered)': '#dark.05', - }, - color: { - '': '#dark-02', - 'pressed | (selected & !hovered)': '#danger-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DANGER_CLEAR_STYLES: Styles = { - border: { - '': '#clear', - pressed: '#danger.05', - focused: '#danger-text', - }, - fill: { - '': '#danger.0', - hovered: '#danger.1', - 'pressed | (selected & !hovered)': '#danger.05', - }, - color: { - '': '#danger-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const DANGER_LINK_STYLES: Styles = { - outline: { - '': '0 #danger-text.0', - focused: '1bw #danger-text', - }, - border: 0, - fill: { - '': '#clear', - }, - color: { - '': '#danger-text', - 'hovered & !pressed': '#danger', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -// ---------- SUCCESS THEME ---------- -export const SUCCESS_PRIMARY_STYLES: Styles = { - outline: { - '': '0 #success-text.0', - focused: '1bw #success-text', - }, - border: { - '': '#clear', - 'pressed | focused': '#success-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#success', - 'hovered & !pressed': '#success-text', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#white', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const SUCCESS_SECONDARY_STYLES: Styles = { - border: { - '': '#success.15', - pressed: '#success.3', - focused: '#success-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#success.05', - 'hovered & !pressed': '#success.1', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#success', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const SUCCESS_OUTLINE_STYLES: Styles = { - border: { - '': '#success.15', - pressed: '#success.3', - focused: '#success-text', - '[disabled] | disabled': '#border', - }, - fill: { - '': '#success.0', - hovered: '#success.1', - 'pressed | (selected & !hovered)': '#success.05', - '[disabled] | disabled': '#dark.04', - }, - color: { - '': '#success-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const SUCCESS_NEUTRAL_STYLES: Styles = { - border: { - '': '#clear', - focused: '#success-text', - }, - fill: { - '': '#dark.0', - hovered: '#dark.04', - 'pressed | (selected & !hovered)': '#dark.05', - }, - color: { - '': '#dark-02', - 'pressed | (selected & !hovered)': '#success-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const SUCCESS_CLEAR_STYLES: Styles = { - border: { - '': '#clear', - pressed: '#success.05', - focused: '#success-text', - }, - fill: { - '': '#success.0', - hovered: '#success.1', - 'pressed | (selected & !hovered)': '#success.05', - }, - color: { - '': '#success-text', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -export const SUCCESS_LINK_STYLES: Styles = { - outline: { - '': '0 #success-text.0', - focused: '1bw #success-text', - }, - border: '0', - fill: { - '': '#clear', - }, - color: { - '': '#success-text', - 'hovered & !pressed': '#success', - '[disabled] | disabled': '#dark-04', - }, -} as const; - -// ---------- SPECIAL THEME ---------- -export const SPECIAL_PRIMARY_STYLES: Styles = { - outline: { - '': '0 #white.0', - focused: '1bw #white', - }, - border: { - '': '#clear', - pressed: '#purple-03', - '[disabled] | disabled': '#white.3', - }, - fill: { - '': '#purple', - 'hovered & !pressed': '#purple-text', - '[disabled] | disabled': '#white.12', - }, - color: { - '': '#white', - '[disabled] | disabled': '#white.4', - }, -} as const; - -export const SPECIAL_SECONDARY_STYLES: Styles = { - border: { - '': '#white.3', - pressed: '#white.4', - focused: '#white', - }, - fill: { - '': '#white.12', - 'hovered & !pressed': '#white.18', - '[disabled] | disabled': '#white.12', - }, - color: { - '': '#white', - '[disabled] | disabled': '#white.4', - }, -} as const; - -export const SPECIAL_OUTLINE_STYLES: Styles = { - border: { - '': '#white.3', - pressed: '#white.12', - focused: '#white', - }, - fill: { - '': '#white.0', - hovered: '#white.18', - 'pressed | (selected & !hovered)': '#white.12', - '[disabled] | disabled': '#white.12', - }, - color: { - '': '#white', - '[disabled] | disabled': '#white.4', - }, -} as const; - -export const SPECIAL_NEUTRAL_STYLES: Styles = { - border: { - '': 0, - focused: '#white', - }, - fill: { - '': '#white.0', - hovered: '#white.12', - 'pressed | (selected & !hovered)': '#white.18', - }, - color: { - '': '#white', - '[disabled] | disabled': '#white.4', - }, -} as const; - -export const SPECIAL_CLEAR_STYLES: Styles = { - outline: { - focused: '1bw #white', - }, - border: { - '': '#clear', - 'pressed | focused': '#white', - '[disabled] | disabled': '#white.3', - }, - fill: { - '': '#white', - 'hovered & !pressed': '#white.94', - '[disabled] | disabled': '#white.12', - }, - color: { - '': '#purple-text', - hovered: '#purple', - 'pressed & hovered': '#purple-text', - '[disabled] | disabled': '#white.4', - }, -} as const; - -export const SPECIAL_LINK_STYLES: Styles = { - outline: { - '': '0 #white.0', - focused: '1bw #white', - }, - border: '0', - fill: { - '': '#clear', - }, - color: { - '': '#white', - 'hovered & !pressed': '#white.9', - '[disabled] | disabled': '#white.4', - }, -} as const; - const ButtonElement = tasty({ qa: 'Button', styles: DEFAULT_BUTTON_STYLES, diff --git a/src/components/actions/CommandMenu/CommandMenu.docs.mdx b/src/components/actions/CommandMenu/CommandMenu.docs.mdx index ceb6c5f32..6458135bf 100644 --- a/src/components/actions/CommandMenu/CommandMenu.docs.mdx +++ b/src/components/actions/CommandMenu/CommandMenu.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from '@storybook/blocks'; +import { Meta, Story, Controls } from '@storybook/addon-docs/blocks'; import { CommandMenu } from './CommandMenu'; import * as CommandMenuStories from './CommandMenu.stories'; @@ -62,6 +62,73 @@ The CommandMenu component supports the following modifiers: |----------|------|-------------| | loading | `boolean` | Whether the command palette is in loading state | +## Sub-components + +### CommandMenu.Item + +Individual menu items within the CommandMenu. Each item is rendered using [ItemBase](/docs/content-itembase--docs) and supports all ItemBase properties for layout, icons, descriptions, and interactive features. + +For detailed information about all available item properties, see the [Menu.Item documentation](/components/Menu#sub-components) which provides the complete API reference. Key properties include: + +| Property | Type | Description | +|----------|------|-------------| +| key | `Key` | Unique identifier for the item (required) | +| children | `ReactNode` | The main content/label for the menu item | +| icon | `ReactNode` | Icon displayed before the content | +| rightIcon | `ReactNode` | Icon displayed after the content | +| description | `ReactNode` | Secondary text below the main content | +| prefix | `ReactNode` | Content before the main text | +| suffix | `ReactNode` | Content after the main text | +| hotkeys | `string` | Keyboard shortcut that triggers this item | +| tooltip | `string \| boolean \| object` | Tooltip configuration | +| isDisabled | `boolean` | Whether the item is disabled | +| onAction | `() => void` | Callback fired when item is activated | + +#### CommandMenu-specific Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| keywords | `string[]` | - | Additional search terms for better discoverability | +| forceMount | `boolean` | - | Whether to always show this item regardless of search filter | +| textValue | `string` | - | Text value for search filtering (if different from children) | + +#### Example with Enhanced Search + +```jsx + + } + hotkeys="cmd+s" + keywords={["write", "disk", "persist"]} + description="Save the current document" + > + Save Document + + } + forceMount={true} + description="Always visible help option" + > + Help & Support + + +``` + +### CommandMenu.Section + +Groups related items together with an optional heading. Same as Menu.Section - see [Menu documentation](/components/Menu#sub-components) for full details. + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| title | `ReactNode` | - | Optional heading text for the section | +| children | `CommandMenu.Item[]` | - | Collection of CommandMenu.Item components | + +### CommandMenu.Trigger + +Alias for MenuTrigger component. See [Menu.Trigger documentation](/components/Menu#menu-trigger) for full API reference. + ## Accessibility ### Keyboard Navigation diff --git a/src/components/actions/CommandMenu/CommandMenu.stories.tsx b/src/components/actions/CommandMenu/CommandMenu.stories.tsx index d8d1983dd..a29fc6b16 100644 --- a/src/components/actions/CommandMenu/CommandMenu.stories.tsx +++ b/src/components/actions/CommandMenu/CommandMenu.stories.tsx @@ -1,10 +1,3 @@ -import { - expect, - findByRole, - userEvent, - waitFor, - within, -} from '@storybook/test'; import { IconArrowBack, IconArrowForward, @@ -14,6 +7,7 @@ import { IconSelect, } from '@tabler/icons-react'; import React, { useState } from 'react'; +import { expect, findByRole, userEvent, waitFor, within } from 'storybook/test'; import { tasty } from '../../../tasty'; import { Card } from '../../content/Card/Card'; @@ -35,7 +29,7 @@ import { useContextMenu } from '../use-context-menu'; import { CommandMenu, CubeCommandMenuProps } from './CommandMenu'; -import type { StoryFn } from '@storybook/react'; +import type { StoryFn } from '@storybook/react-vite'; // Styled components for header and footer const HeaderTitle = tasty(Text, { @@ -606,7 +600,6 @@ export const MultipleSelection: StoryFn> = (args) => { width="20x 50x" {...args} selectionMode="multiple" - selectionIcon="checkbox" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys} > @@ -1095,19 +1088,28 @@ export const WithContextMenu = () => { onAction={onAction} {...props} > - + 📋}> Copy - + 📄} + > Paste - + ✂️}> Cut - + 🗑️} + > Delete - + ✏️} + > Rename diff --git a/src/components/actions/CommandMenu/CommandMenu.tsx b/src/components/actions/CommandMenu/CommandMenu.tsx index b4e3a96f8..73374aca1 100644 --- a/src/components/actions/CommandMenu/CommandMenu.tsx +++ b/src/components/actions/CommandMenu/CommandMenu.tsx @@ -397,7 +397,6 @@ function CommandMenu( styles={completeProps.sectionStyles} itemStyles={completeProps.itemStyles} headingStyles={completeProps.sectionHeadingStyles} - selectionIcon={completeProps.selectionIcon} size={size} />, ); @@ -412,7 +411,6 @@ function CommandMenu( item={item} state={treeState} styles={completeProps.itemStyles} - selectionIcon={completeProps.selectionIcon} size={size} onAction={item.onAction} /> @@ -452,7 +450,7 @@ function CommandMenu( treeState, completeProps.sectionStyles, completeProps.itemStyles, - completeProps.selectionIcon, + completeProps.sectionHeadingStyles, ]); diff --git a/src/components/actions/CommandMenu/styled.tsx b/src/components/actions/CommandMenu/styled.tsx index 454827f1b..9f075d4ac 100644 --- a/src/components/actions/CommandMenu/styled.tsx +++ b/src/components/actions/CommandMenu/styled.tsx @@ -38,26 +38,33 @@ export const StyledSearchInput = tasty({ display: 'grid', width: '100%', color: '#dark', - fill: '#white', + fill: '#clear', border: '#border bottom', outline: 'none', transition: 'theme', radius: 0, - padding: '.5x 1.5x', textAlign: 'left', reset: 'input', preset: 't3', margin: 0, boxSizing: 'border-box', userSelect: 'auto', - height: { - '': '$size-md $size-md', - '[data-size="large"]': '$size-lg $size-lg', + height: '($size + 1x)', + padding: { + '': '.5x $inline-padding', + prefix: '0 $inline-padding 0 .5x', }, - '&::placeholder': { - color: '#dark-03', + $size: { + '': '$size-md', + '[data-size="small"]': '$size-sm', + '[data-size="medium"]': '$size-md', + '[data-size="large"]': '$size-lg', }, + '$inline-padding': + 'max($min-inline-padding, (($size - 1lh) / 2 + $inline-compensation))', + '$inline-compensation': '1x', + '$min-inline-padding': '1x', }, }); diff --git a/src/components/actions/ItemButton/ItemButton.docs.mdx b/src/components/actions/ItemButton/ItemButton.docs.mdx new file mode 100644 index 000000000..3a7ce91d0 --- /dev/null +++ b/src/components/actions/ItemButton/ItemButton.docs.mdx @@ -0,0 +1,253 @@ +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; +import { ItemButton } from './ItemButton'; +import * as ItemButtonStories from './ItemButton.stories'; + + + +# ItemButton + +An interactive button component built on top of `ItemBase` that provides all the layout and styling capabilities of ItemBase with full button functionality. ItemButton supports actions, links, form submissions, and all the advanced features like hotkeys, tooltips, and selection states. + +## When to Use + +- For buttons that need structured content with icons, descriptions, or prefixes/suffixes +- When you need button functionality with the flexible layout capabilities of ItemBase +- For interactive items in lists, menus, or toolbars that need consistent styling +- When building buttons that support features like keyboard shortcuts or overflow tooltips +- As an alternative to traditional buttons when you need more layout control + +## Component + + + +--- + +### Properties + + + +### Base Properties + +Supports [Base properties](/BaseProperties) + +### ItemBase Properties + +ItemButton inherits all properties from [ItemBase](/docs/content-itembase--docs), including: +- Layout properties: `icon`, `rightIcon`, `prefix`, `suffix`, `description`, `descriptionPlacement` +- Interactive properties: `hotkeys`, `tooltip`, `isSelected` +- Styling properties: `size`, `type`, `theme`, `styles` + +### Action Properties + +#### onPress +- **Type**: `(e: PressEvent) => void` +- **Description**: Callback fired when the button is pressed via mouse, keyboard, or touch + +#### to +- **Type**: `string` +- **Description**: URL for link behavior - when provided, the button acts as a link + +#### buttonType +- **Type**: `'button' | 'submit' | 'reset'` +- **Description**: HTML button type attribute for form integration +- **Default**: `'button'` + +#### isDisabled +- **Type**: `boolean` +- **Description**: Whether the button is disabled and non-interactive + +### Style Properties + +These properties allow direct style application without using the `styles` prop: `width`, `height`, `padding`, `margin`, `color`, `fill`, `opacity`, `display`, `position`, `zIndex`, `gap`, `flow`, `placeItems`, `placeContent`, `alignItems`, `justifyContent`, `border`, `radius`, `shadow`, `overflow`. + +### Modifiers + +Inherits all modifiers from [ItemBase](/docs/content-itembase--docs) plus: + +| Modifier | Type | Description | +|----------|------|-------------| +| disabled | `boolean` | Applied when the button is disabled | +| pressed | `boolean` | Applied during button press interaction | + +## Variants + +### Types + +- `primary` - Primary button with prominent styling for main actions +- `secondary` - Secondary button with moderate emphasis +- `outline` - Button with border outline and transparent background +- `neutral` - Neutral styled button for subtle actions +- `clear` - Transparent button with minimal visual weight +- `link` - Link-styled button that looks like a text link + +### Themes + +- `default` - Standard appearance with default colors +- `danger` - Red theme for destructive actions (delete, remove, etc.) +- `success` - Green theme for positive actions (save, confirm, etc.) +- `special` - Special theme with unique styling for standout actions + +### Sizes + +- `xsmall` - Extra small size for compact interfaces +- `small` - Small size for dense layouts +- `medium` - Default size for most use cases +- `large` - Large size for prominent actions +- `xlarge` - Extra large size for hero actions +- `inline` - Inline size that adapts to content + +## Examples + +### Basic Button + +```jsx + console.log('Clicked')}> + Click me + +``` + +### Button with Icons + +```jsx +} + rightIcon={} + onPress={handleOpen} +> + Open File + +``` + +### Link Button + +```jsx + + Go to Documentation + +``` + +### Button with Hotkey + +```jsx + + Save Document + +``` + +### Form Submit Button + +```jsx +} +> + Submit Form + +``` + +### Button with Selection State + +```jsx + setIsSelected(!isSelected)} +> + Toggle Selection + +``` + +## Accessibility + +### Keyboard Navigation + +- `Tab` - Moves focus to the button +- `Space/Enter` - Activates the button +- Custom hotkeys - Trigger button action when hotkeys are specified +- `Escape` - Removes focus when pressed (standard browser behavior) + +### Screen Reader Support + +- Button announces as "button" to screen readers +- Link buttons announce as "link" when `to` prop is provided +- Button state is announced (pressed, disabled, selected) +- Hotkey information is included in accessible description + +### ARIA Properties + +- `aria-label` - Provides accessible label when text content isn't descriptive enough +- `aria-disabled` - Indicates disabled state +- `aria-pressed` - Indicates toggle state for toggle buttons +- `role` - Automatically set to "button" or "link" based on props + +## Best Practices + +1. **Do**: Use clear, action-oriented text + ```jsx + + Save Changes + + ``` + +2. **Do**: Add hotkeys for frequently used actions + ```jsx + + Save Document + + ``` + +3. **Do**: Use appropriate button types for forms + ```jsx + + Submit Form + + ``` + +4. **Don't**: Use vague or unclear button text + ```jsx + {/* Avoid this - unclear what "OK" does */} + OK + ``` + +5. **Don't**: Overcrowd buttons with too many visual elements + ```jsx + {/* Avoid this - too many competing elements */} + } + rightIcon={} + prefix="Pre" + suffix="Suf" + description="Long description" + > + Overcrowded Button + + ``` + +6. **Accessibility**: Always ensure buttons have clear, descriptive labels and proper keyboard support + +## Integration with Forms + +ItemButton integrates seamlessly with forms when using the `buttonType` prop: + +```jsx +
+ + + Sign In + + + Clear Form + + +``` + +## Related Components + +- [ItemBase](/docs/content-itembase--docs) - The foundational component that ItemButton extends +- [Button](/docs/actions-button--docs) - Traditional button component for simpler use cases +- [Link](/docs/navigation-link--docs) - Text link component for navigation +- [Menu.Item](/docs/actions-menu--docs) - Menu item component that also uses ItemBase \ No newline at end of file diff --git a/src/components/actions/ItemButton/ItemButton.stories.tsx b/src/components/actions/ItemButton/ItemButton.stories.tsx new file mode 100644 index 000000000..34b3d83f1 --- /dev/null +++ b/src/components/actions/ItemButton/ItemButton.stories.tsx @@ -0,0 +1,458 @@ +import { IconExternalLink, IconFile } from '@tabler/icons-react'; + +import { TooltipProvider } from '../../overlays/Tooltip/TooltipProvider'; + +import { ItemButton } from './ItemButton'; + +import type { Meta, StoryObj } from '@storybook/react-vite'; + +const meta: Meta = { + title: 'Actions/ItemButton', + component: ItemButton, + parameters: { + layout: 'padded', + }, + argTypes: { + type: { + control: 'select', + options: ['primary', 'secondary', 'outline', 'neutral', 'clear'], + description: 'Visual type/variant of the button', + }, + theme: { + control: 'select', + options: ['default', 'danger', 'success', 'special'], + description: 'Color theme of the button', + }, + size: { + control: 'select', + options: ['xsmall', 'small', 'medium', 'large', 'xlarge', 'inline'], + description: 'Size of the button', + }, + hotkeys: { + control: 'text', + description: + 'Keyboard shortcut that triggers the button (e.g., "cmd+s", "ctrl+alt+d")', + }, + tooltip: { + control: 'object', + description: + 'Tooltip configuration: string for simple text, true for auto overflow tooltips, or object for advanced config with optional auto property', + }, + isDisabled: { + control: 'boolean', + description: 'Whether the button is disabled', + }, + isSelected: { + control: 'boolean', + description: 'Whether the button shows as selected with checkbox', + }, + to: { + control: 'text', + description: 'URL for link behavior (makes button act as link)', + }, + buttonType: { + control: 'select', + options: ['button', 'submit', 'reset'], + description: 'HTML button type attribute', + }, + description: { + control: 'text', + description: 'Secondary text shown below the main content', + }, + // Icon controls are typically not included in argTypes since they're complex ReactNode objects + // prefix and suffix are also ReactNode, so omitted from controls + onPress: { + action: 'pressed', + description: 'Callback fired when button is pressed', + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Item Button', + }, +}; + +export const WithIcons: Story = { + args: { + children: 'Open', + icon: , + rightIcon: , + }, +}; + +export const AsLink: Story = { + args: { + children: 'Go to Docs', + to: '/docs', + }, +}; + +export const Variants: Story = { + render: (args) => ( +
+ + Primary + + + Secondary + + + Outline + + + Neutral + + + Clear + + + Link + +
+ ), +}; + +export const WithHotkeys: Story = { + render: (args) => ( +
+
+

Buttons with Keyboard Shortcuts

+

+ Try pressing the keyboard shortcuts to trigger the buttons: +

+
+ alert('Save action triggered!')} + > + Save + + } + onPress={() => alert('Open action triggered!')} + > + Open File + + } + onPress={() => alert('Export action triggered!')} + > + Export + + alert('Cancel action triggered!')} + > + Cancel + +
+
+ +
+

With Custom Suffix

+

+ When a custom suffix is provided, hotkeys are still functional but the + shortcut hint is not shown: +

+
+ alert('Download with custom suffix triggered!')} + > + Download + +
+
+ +
+

Different Hotkey Formats

+
+ alert('Multi-modifier triggered!')} + > + Multi-modifier + + alert('Function key triggered!')} + > + Help + + alert('Shift+Enter triggered!')} + > + Submit + +
+
+
+ ), + parameters: { + docs: { + description: { + story: + "Demonstrates the hotkeys functionality in ItemButton. When the `hotkeys` prop is provided, it automatically displays the keyboard shortcut in the suffix area (unless a custom suffix is provided) and registers a global keyboard listener. The hotkeys work from anywhere on the page and will trigger the button's onPress handler.", + }, + }, + }, +}; + +export const WithCheckbox: Story = { + render: (args) => ( +
+
+

Selected Items (Checkbox Visible)

+
+ + Selected item with checkbox + + + Primary selected button + + + Outline selected button + +
+
+ +
+

Non-Selected Items (Checkbox Hidden)

+
+ + Non-selected item with hidden checkbox + + + Primary non-selected button + + + Outline non-selected button + +
+
+ +
+

Mixed Selection States

+
+ + Selected Item 1 + + + Non-selected Item 2 + + + Selected Item 3 + +
+
+ +
+

Comparison: Checkbox vs Regular Icon

+
+ + With checkbox (selected) + + + With checkbox (not selected) + + }> + With regular icon + +
+
+
+ ), + parameters: { + docs: { + description: { + story: + 'Demonstrates the checkbox functionality in ItemButton when `isSelected` prop is provided. When `isSelected` is `true`, the checkbox is visible (opacity 1, hover opacity 0.8). When `isSelected` is `false`, the checkbox is invisible (opacity 0, hover opacity 0.4). The checkbox replaces the `icon` prop when `isSelected` is provided, inherited from the ItemBase component.', + }, + }, + }, +}; + +export const AutoTooltipOnOverflow: Story = { + render: (args) => ( +
+
+

Auto Tooltip with tooltip=true

+
+ } + style={{ width: '200px' }} + > + This text is long enough to overflow and trigger auto tooltip + + } + style={{ width: '200px' }} + > + Short text + +
+
+ +
+

Auto Tooltip with Configuration Object

+
+ } + style={{ width: '180px' }} + > + Long text with custom placement that will show tooltip on top + + } + style={{ width: '180px' }} + > + Text with custom delay and bottom placement + +
+
+ +
+

Auto vs Explicit Tooltip

+
+ } + style={{ width: '150px' }} + > + Explicit title takes priority over auto + + } + style={{ width: '150px' }} + > + Auto tooltip shows this text when overflowed + +
+
+ +
+

No Tooltip When Not Overflowed

+
+ } + style={{ width: '300px' }} + > + Short text (no tooltip) + + } + style={{ width: '400px' }} + > + Normal length text (no tooltip) + +
+
+ +
+

Auto Tooltip with Different Button Types

+
+ } + style={{ width: '140px' }} + > + Primary button with overflow + + } + style={{ width: '140px' }} + > + Outline button with overflow + + } + style={{ width: '140px' }} + > + Clear button with overflow + +
+
+ +
+

Auto Tooltip with Hotkeys

+
+ } + style={{ width: '160px' }} + onPress={() => alert('Save action triggered!')} + > + Save with very long descriptive name + + } + style={{ width: '160px' }} + onPress={() => alert('Open action triggered!')} + > + Open document with long name + +
+
+
+ ), + parameters: { + docs: { + description: { + story: + 'Demonstrates the auto tooltip functionality that shows tooltips only when button text overflows. Use `tooltip={true}` for simple auto tooltips or `tooltip={{ auto: true, ...config }}` for advanced configuration. When text is not overflowed, no tooltip is shown. Explicit `title` in tooltip config takes priority over auto behavior. Auto tooltips work with all button types, hotkeys, and other features.', + }, + }, + }, +}; diff --git a/src/components/actions/ItemButton/ItemButton.tsx b/src/components/actions/ItemButton/ItemButton.tsx new file mode 100644 index 000000000..37ef3ad16 --- /dev/null +++ b/src/components/actions/ItemButton/ItemButton.tsx @@ -0,0 +1,46 @@ +import { FocusableRef } from '@react-types/shared'; +import { forwardRef } from 'react'; + +import { tasty } from '../../../tasty'; +import { mergeProps } from '../../../utils/react'; +import { CubeItemBaseProps, ItemBase } from '../../content/ItemBase'; +import { CubeUseActionProps, useAction } from '../use-action'; + +export interface CubeItemButtonProps + extends Omit, + Omit {} + +const StyledItemBase = tasty(ItemBase, { + as: 'button', + type: 'neutral', + theme: 'default', + styles: { + reset: 'button', + placeContent: 'center stretch', + }, +}); + +export const ItemButton = forwardRef(function ItemButton( + allProps: CubeItemButtonProps, + ref: FocusableRef, +) { + const { mods, to, htmlType, buttonType, as, type, ...rest } = + allProps as CubeItemButtonProps & { + as?: 'a' | 'button' | 'div' | 'span'; + }; + + const { actionProps } = useAction( + { ...(allProps as any), type: buttonType, to, htmlType, as, mods }, + ref, + ); + + return ( + + ); +}); + +export type { CubeItemButtonProps as ItemButtonProps }; diff --git a/src/components/actions/ItemButton/index.ts b/src/components/actions/ItemButton/index.ts new file mode 100644 index 000000000..0bedc167d --- /dev/null +++ b/src/components/actions/ItemButton/index.ts @@ -0,0 +1 @@ +export * from './ItemButton'; diff --git a/src/components/actions/Menu/Menu.docs.mdx b/src/components/actions/Menu/Menu.docs.mdx index 4b2f218c3..1a30347aa 100644 --- a/src/components/actions/Menu/Menu.docs.mdx +++ b/src/components/actions/Menu/Menu.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { Menu } from './Menu'; import * as MenuStories from './Menu.stories'; @@ -117,31 +117,62 @@ The `mods` property accepts the following modifiers you can override: | header | `ReactNode` | - | Optional header content (deprecated) | | footer | `ReactNode` | - | Optional footer content | -## Menu.Item Properties +## Sub-components -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| key | `Key` | - | Unique identifier for the item | -| children | `ReactNode` | - | Item content/label | -| description | `ReactNode` | - | Secondary text shown below label | -| icon | `ReactElement` | - | Icon displayed before the label | -| hotkeys | `string` | - | Keyboard shortcut (e.g., "Ctrl+C") | -| wrapper | `(item: ReactElement) => ReactElement` | - | Function to wrap the item | -| tooltip | `string \| TooltipProviderProps` | - | Tooltip configuration - string for simple tooltip or object for advanced options | +### Menu.Item + +Individual menu items. Each item is rendered using [ItemBase](/docs/content-itembase--docs) and supports all ItemBase properties for layout, icons, descriptions, and interactive features. + +#### Item API -### Menu.Item State Properties +For detailed information about all available item properties, see [ItemBase documentation](/docs/content-itembase--docs). Key properties include: + +| Property | Type | Description | +|----------|------|-------------| +| key | `Key` | Unique identifier for the item (required) | +| children | `ReactNode` | The main content/label for the menu item | +| icon | `ReactNode` | Icon displayed before the content | +| rightIcon | `ReactNode` | Icon displayed after the content | +| description | `ReactNode` | Secondary text below the main content | +| descriptionPlacement | `'inline' \| 'block'` | How the description is positioned | +| prefix | `ReactNode` | Content before the main text | +| suffix | `ReactNode` | Content after the main text | +| hotkeys | `string` | Keyboard shortcut that triggers this item | +| tooltip | `string \| boolean \| object` | Tooltip configuration | +| isDisabled | `boolean` | Whether the item is disabled | +| styles | `Styles` | Custom styling for the item | +| qa | `string` | QA identifier for testing | + +#### Menu-specific Properties | Property | Type | Default | Description | |----------|------|---------|-------------| -| isDisabled | `boolean` | `false` | Whether the item is disabled | | textValue | `string` | - | Text for typeahead search | +| wrapper | `(item: ReactElement) => ReactElement` | - | Function to wrap the item | +| onAction | `() => void` | - | Callback fired when item is activated | -### Menu.Item Button Properties +#### Example with Rich Items -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| postfix | `ReactNode` | - | Content displayed on the right side | -| prefix | `ReactNode` | - | Content displayed on the left side | +```jsx + + } + hotkeys="cmd+s" + description="Save the current document" + > + Save + + } + suffix="PDF" + tooltip="Export as PDF file" + > + Export + + +``` ## Menu.Section Properties @@ -459,6 +490,6 @@ The Menu component is typically used with MenuTrigger for dropdown functionality ## Related Components - [MenuTrigger](/components/MenuTrigger) - Handles menu positioning and trigger interaction -- [Button](/components/Button) - Common trigger element for menus -- [ListBox](/components/ListBox) - Alternative for selection-focused interfaces -- [ComboBox](/components/ComboBox) - Combines input with menu selection \ No newline at end of file +- [Button](/docs/actions-button--docs) - Common trigger element for menus +- [ListBox](/docs/forms-listbox--docs) - Alternative for selection-focused interfaces +- [ComboBox](/docs/forms-combobox--docs) - Combines input with menu selection \ No newline at end of file diff --git a/src/components/actions/Menu/Menu.stories.tsx b/src/components/actions/Menu/Menu.stories.tsx index 4e70b4a84..6028777d1 100644 --- a/src/components/actions/Menu/Menu.stories.tsx +++ b/src/components/actions/Menu/Menu.stories.tsx @@ -1,13 +1,6 @@ // @ts-nocheck // NOTE: Type checking is disabled in this Storybook file to prevent // noisy errors from complex generic typings that do not affect runtime behaviour. -import { - expect, - findByRole, - userEvent, - waitFor, - within, -} from '@storybook/test'; import { IconBook, IconBulb, @@ -17,6 +10,7 @@ import { IconReload, } from '@tabler/icons-react'; import { useState } from 'react'; +import { expect, findByRole, userEvent, waitFor, within } from 'storybook/test'; import { CheckIcon, @@ -115,14 +109,7 @@ export default { type: { summary: 'Iterable' }, }, }, - selectionIcon: { - options: [undefined, 'checkbox', 'radio'], - control: { type: 'radio' }, - description: 'Type of selection indicator to display', - table: { - type: { summary: "'checkbox' | 'radio'" }, - }, - }, + disabledKeys: { control: { type: null }, description: 'Keys of items that should appear disabled', @@ -401,13 +388,13 @@ export const InsideModal = () => { width="220px" selectionMode="multiple" > - + Copy - + Paste - + Cut @@ -494,12 +481,12 @@ export const GitActions = (props) => { Merge to master (hovered) - + Merge to master (disabled) {bulbIcon} {stuffText} @@ -511,7 +498,7 @@ export const GitActions = (props) => { {successIcon} {stuffText} @@ -520,10 +507,10 @@ export const GitActions = (props) => { > Merge to master (pressed) - + Merge to master - {bulbIcon}}> + {bulbIcon}}> Merge to master @@ -574,7 +561,6 @@ export const MenuSelectableCheckboxes = (props) => { return MenuTemplate({ ...props, - selectionIcon: 'checkbox', selectionMode: 'multiple', selectedKeys, onSelectionChange, @@ -589,7 +575,6 @@ export const MenuSelectableRadio = (props) => { return MenuTemplate({ ...props, - selectionIcon: 'radio', selectionMode: 'single', selectedKeys, onSelectionChange, @@ -600,16 +585,16 @@ export const PaymentDetails = (props) => { return (
- + Invoice #16C7B3AE-000113-000113 - + Invoice #16C7B3AE - + Invoice #16C7B3AE manual - + #16C7B3AE @@ -618,32 +603,51 @@ export const PaymentDetails = (props) => { }; export const ItemCustomIcons = (props) => { - const [selectedKeys, setSelectedKeys] = useState(['1']); - const onSelectionChange = (keys) => { - setSelectedKeys(keys); - }; - return (
- - } postfix="March, 2022"> + + } + suffix={ + + March, 2022 + + } + > #16C7B3AE-000113-000113 - } postfix="Jan, 2022"> + } + suffix={ + + Jan, 2022 + + } + > #16C7B3AE - } postfix="Feb, 2022"> + } + suffix={ + + Feb, 2022 + + } + > #16C7B3AE - } postfix="July, 2022"> + } + suffix={ + + July, 2022 + + } + > #16C7B3AE @@ -662,7 +666,6 @@ export const ItemWithTooltip = (props) => { { export const WithTriggerState = ({ ...props }) => { const menu = ( - + Copy - + Paste - + Cut @@ -1424,7 +1427,7 @@ export const MenuSynchronization = () => { {rendered1} + ); }; @@ -928,6 +968,15 @@ export const FilterPicker = forwardRef(function FilterPicker( shouldUpdatePosition={shouldUpdatePosition} shouldFlip={shouldFlip} isDismissable={true} + shouldCloseOnInteractOutside={(el) => { + const menuTriggerEl = el.closest('[data-popover-trigger]'); + // If no menu trigger was clicked, allow closing + if (!menuTriggerEl) return true; + // If the same trigger that opened this popover was clicked, allow closing (toggle) + if (menuTriggerEl === (triggerRef as any)?.current) return true; + // Otherwise, don't close here. Let the event bus handle closing when the other opens. + return false; + }} > {renderTrigger} {(close) => ( @@ -965,6 +1014,9 @@ export const FilterPicker = forwardRef(function FilterPicker( headerStyles={headerStyles} footerStyles={footerStyles} qa={`${props.qa || 'FilterPicker'}ListBox`} + allValueProps={allValueProps} + customValueProps={customValueProps} + newCustomValueProps={newCustomValueProps} onEscape={() => close()} onOptionClick={(key) => { // For FilterPicker, clicking the content area should close the popover @@ -1057,7 +1109,7 @@ export const FilterPicker = forwardRef(function FilterPicker( ); - return wrapWithField, 'children'>>( + return wrapWithField, 'children' | 'tooltip'>>( filterPickerField, ref as any, mergeProps( diff --git a/src/components/fields/ListBox/ListBox.docs.mdx b/src/components/fields/ListBox/ListBox.docs.mdx index d63c619ba..245c52504 100644 --- a/src/components/fields/ListBox/ListBox.docs.mdx +++ b/src/components/fields/ListBox/ListBox.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { ListBox } from './ListBox'; import * as ListBoxStories from './ListBox.stories'; @@ -88,6 +88,62 @@ The `mods` property accepts the following modifiers you can override: | `header` | `boolean` | Applied when header prop is provided | | `footer` | `boolean` | Applied when footer prop is provided | +## Sub-components + +### ListBox.Item + +Individual items within the ListBox. Each item is rendered using [ItemBase](/docs/content-itembase--docs) and supports all ItemBase properties for layout, icons, descriptions, and interactive features. + +#### Item API + +For detailed information about all available item properties, see [ItemBase documentation](/docs/content-itembase--docs). Key properties include: + +| Property | Type | Description | +|----------|------|-------------| +| key | `string \| number` | Unique identifier for the item (required) | +| children | `ReactNode` | The main content/label for the option | +| icon | `ReactNode` | Icon displayed before the content | +| rightIcon | `ReactNode` | Icon displayed after the content | +| description | `ReactNode` | Secondary text below the main content | +| descriptionPlacement | `'inline' \| 'block'` | How the description is positioned | +| prefix | `ReactNode` | Content before the main text | +| suffix | `ReactNode` | Content after the main text | +| tooltip | `string \| boolean \| object` | Tooltip configuration | +| styles | `Styles` | Custom styling for the item | +| qa | `string` | QA identifier for testing | + +#### Example with Rich Items + +```jsx + + } + description="Product Manager" + suffix="Online" + > + Alice Johnson + + } + description="Senior Developer" + rightIcon={} + > + Bob Smith + + +``` + +### ListBox.Section + +Groups related items together with an optional heading. + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| title | `ReactNode` | - | Optional heading text for the section | +| children | `ListBox.Item[]` | - | Collection of ListBox.Item components | + ## Variants ### Selection Modes diff --git a/src/components/fields/ListBox/ListBox.stories.tsx b/src/components/fields/ListBox/ListBox.stories.tsx index 4e33591cb..fc4881cd0 100644 --- a/src/components/fields/ListBox/ListBox.stories.tsx +++ b/src/components/fields/ListBox/ListBox.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn, StoryObj } from '@storybook/react'; +import { StoryObj } from '@storybook/react-vite'; import { useState } from 'react'; import { @@ -23,9 +23,9 @@ import { DialogTrigger } from '../../overlays/Dialog/DialogTrigger'; import { CubeListBoxProps, ListBox } from './ListBox'; -import type { Meta } from '@storybook/react'; +// import type { Meta, StoryObj } from '@storybook/react-vite'; -const meta: Meta = { +const meta: any = { title: 'Forms/ListBox', component: ListBox, parameters: { @@ -189,7 +189,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = any; // Sample data for stories const fruits = [ @@ -231,7 +231,7 @@ const permissions = [ { key: 'share', label: 'Share', description: 'Share content with others' }, ]; -const Template: StoryFn> = (args) => ( +const Template: StoryObj>['render'] = (args) => ( Apple Banana @@ -268,7 +268,11 @@ export const MultipleSelection: Story = { render: (args) => ( {permissions.map((permission) => ( - + {permission.label} ))} @@ -281,7 +285,9 @@ export const MultipleSelection: Story = { }, }; -export const WithDescriptions: StoryFn> = (args) => ( +export const WithDescriptions: StoryObj>['render'] = ( + args, +) => ( > = (args) => ( +export const WithSections: StoryObj>['render'] = ( + args, +) => ( Apple @@ -332,7 +340,9 @@ WithSections.args = { selectionMode: 'single', }; -export const WithHeaderAndFooter: StoryFn> = (args) => ( +export const WithHeaderAndFooter: StoryObj>['render'] = ( + args, +) => ( > = (args) => ( +export const DisabledState: StoryObj>['render'] = ( + args, +) => ( Option 1 Option 2 @@ -561,7 +573,9 @@ DisabledState.args = { selectionMode: 'single', }; -export const ValidationStates: StoryFn> = () => ( +export const ValidationStates: StoryObj< + CubeListBoxProps +>['render'] = () => ( > = () => ( ); -export const ControlledExample: StoryFn> = () => { +export const ControlledExample: StoryObj< + CubeListBoxProps +>['render'] = () => { const [selectedKey, setSelectedKey] = useState('apple'); return ( @@ -628,7 +644,9 @@ export const ControlledExample: StoryFn> = () => { ); }; -export const MultipleControlledExample: StoryFn> = () => { +export const MultipleControlledExample: StoryObj< + CubeListBoxProps +>['render'] = () => { const [selectedKeys, setSelectedKeys] = useState(['read', 'write']); return ( @@ -673,7 +691,7 @@ export const MultipleControlledExample: StoryFn> = () => { ); }; -export const InForm: StoryFn> = () => { +export const InForm: StoryObj>['render'] = () => { const handleSubmit = (data: any) => { console.log('Form submitted:', data); alert(`Selected: ${data.technology || 'None'}`); @@ -735,7 +753,7 @@ export const InForm: StoryFn> = () => { ); }; -export const InPopover: StoryFn> = () => { +export const InPopover: StoryObj>['render'] = () => { const [selectedKey, setSelectedKey] = useState(null); return ( @@ -851,7 +869,9 @@ InPopover.parameters = { }, }; -export const VirtualizedList: StoryFn> = (args) => { +export const VirtualizedList: StoryObj>['render'] = ( + args, +) => { const [selected, setSelected] = useState(null); // Generate a large list of items with varying content to test virtualization @@ -909,31 +929,19 @@ export const WithIcons: Story = { render: (args) => ( - - - - Users - + }> + Users - - - - Permissions - + }> + Permissions - - - - Database - + }> + Database - - - - Settings - + }> + Settings @@ -999,7 +1007,9 @@ export const FocusBehavior: Story = { }, }; -export const EscapeKeyHandling: StoryFn> = () => { +export const EscapeKeyHandling: StoryObj< + CubeListBoxProps +>['render'] = () => { const [selectedKey, setSelectedKey] = useState('apple'); const [escapeCount, setEscapeCount] = useState(0); @@ -1090,3 +1100,289 @@ export const WithSelectAll: Story = { }, }, }; + +export const WithHotkeys: Story = { + render: (args) => ( + + + Try pressing Ctrl+1, Ctrl+2, or{' '} + Ctrl+3 to select options via hotkeys + + + + + + New Project + + + + + + Edit Project + + + + + + Project Settings + + + + + ), + args: { + label: 'Project Actions', + selectionMode: 'single', + }, + parameters: { + docs: { + description: { + story: + 'ListBox options now support hotkeys via the ItemBase integration. Press the specified keyboard shortcuts to select options. The hotkey hint is automatically displayed as a suffix.', + }, + }, + }, +}; + +export const WithSuffixAndRightIcon: Story = { + render: (args) => ( + + 5} + rightIcon={} + > + User Management + + Online} + rightIcon={} + > + Database Status + + 2} + rightIcon={} + > + System Settings + + + ), + args: { + label: 'System Dashboard', + selectionMode: 'single', + size: 'large', + }, + parameters: { + docs: { + description: { + story: + 'ListBox options now support suffix content and right icons via ItemBase integration. This allows for rich option layouts with status indicators, counts, and action icons.', + }, + }, + }, +}; + +export const WithTooltips: Story = { + render: (args) => ( + + } + > + Create Project + + } + > + Import Project + + } + > + Configure + + + ), + args: { + label: 'Project Actions', + selectionMode: 'single', + }, + parameters: { + docs: { + description: { + story: + 'ListBox options now support tooltips via ItemBase integration. Provide either a simple string or a full tooltip configuration object.', + }, + }, + }, +}; + +export const RichContentOptions: Story = { + render: (args) => ( + + Admin} + suffix={3} + rightIcon={} + hotkeys="ctrl+a" + > + System Administrator + + Editor} + suffix={12} + rightIcon={} + hotkeys="ctrl+e" + > + Content Editor + + Viewer} + suffix={45} + rightIcon={} + hotkeys="ctrl+v" + > + Content Viewer + + + ), + args: { + label: 'User Roles', + selectionMode: 'single', + size: 'large', + }, + parameters: { + docs: { + description: { + story: + 'This story demonstrates the full capabilities of ListBox options with ItemBase integration: descriptions, prefix/suffix content, right icons, and hotkeys all working together.', + }, + }, + }, +}; + +export const TooltipsWithActions: Story = { + render: (args) => ( + + + } + suffix={Daily} + > + Backup Database + + } + suffix={Destructive} + > + Restore Database + + } + suffix={Safe} + > + Optimize Database + + + + } + suffix={+} + > + Invite User + + } + suffix={5 users} + > + Manage Permissions + + + + ), + args: { + label: 'System Actions', + selectionMode: 'single', + size: 'large', + }, + parameters: { + docs: { + description: { + story: + 'This story demonstrates tooltips in a practical context with action items. Tooltips provide additional context about what each action does, including warnings for destructive operations.', + }, + }, + }, +}; + +export const AllValuePropsExample: Story = { + render: (args) => ( + + {permissions.map((permission) => ( + + {permission.label} + + ))} + + ), + args: { + label: 'Select permissions with styled "Select All"', + selectionMode: 'multiple', + isCheckable: true, + showSelectAll: true, + selectAllLabel: 'All Permissions', + allValueProps: { + styles: { + preset: 't3m', + color: '#purple-text', + }, + }, + defaultSelectedKeys: ['read'], + }, + parameters: { + docs: { + description: { + story: + 'The `allValueProps` prop allows you to customize the styling and behavior of the "Select All" option. In this example, the "Select All" option uses the `t3m` preset and purple text color.', + }, + }, + }, +}; diff --git a/src/components/fields/ListBox/ListBox.test.tsx b/src/components/fields/ListBox/ListBox.test.tsx index 0ab5dfc31..84103ac98 100644 --- a/src/components/fields/ListBox/ListBox.test.tsx +++ b/src/components/fields/ListBox/ListBox.test.tsx @@ -225,7 +225,7 @@ describe('', () => { }); it('should correctly assign refs', () => { - const listRef = createRef(); + const listRef = createRef(); const { getByRole } = render( diff --git a/src/components/fields/ListBox/ListBox.tsx b/src/components/fields/ListBox/ListBox.tsx index f05ebf983..93987300a 100644 --- a/src/components/fields/ListBox/ListBox.tsx +++ b/src/components/fields/ListBox/ListBox.tsx @@ -21,7 +21,7 @@ import { useListBoxSection, useOption, } from 'react-aria'; -import { Section as BaseSection, Item, useListState } from 'react-stately'; +import { Section as BaseSection, useListState } from 'react-stately'; import { useWarn } from '../../../_internal/hooks/use-warn'; import { CheckIcon } from '../../../icons'; @@ -46,7 +46,9 @@ import { StyledHeader, StyledSectionHeading, } from '../../actions/Menu/styled'; +import { ItemBase } from '../../content/ItemBase/ItemBase'; import { useFieldProps, useFormProps, wrapWithField } from '../../form'; +import { CubeItemProps, Item } from '../../Item'; import type { CollectionBase, Key } from '@react-types/shared'; import type { FieldBaseProps } from '../../../shared'; @@ -106,116 +108,15 @@ const ListBoxScrollElement = tasty({ }, }); -const OptionElement = tasty({ +// Create an extended ItemBase for ListBox options with 'all' modifier support +const ListBoxItemBase = tasty(ItemBase, { as: 'li', styles: { - display: 'flex', - flow: 'row', - placeItems: 'center start', - gap: '.75x', - padding: '.5x 1x', margin: { '': '0 0 1bw 0', ':last-of-type': '0', all: '.5x', }, - height: { - '': 'min $size-md', - '[data-size="large"]': 'min $size-lg', - }, - boxSizing: 'border-box', - radius: '1r', - cursor: { - '': 'default', - disabled: 'not-allowed', - }, - transition: 'theme', - border: 0, - userSelect: 'none', - color: { - '': '#dark-02', - 'selected | pressed': '#dark', - disabled: '#dark-04', - valid: '#success-text', - invalid: '#danger-text', - }, - fill: { - '': '#clear', - 'hovered | focused': '#dark.03', - selected: '#dark.09', - 'selected & (hovered | focused)': '#dark.12', - 'selected & hovered & focused': '#dark.15', - pressed: '#dark.09', - valid: '#success-bg', - invalid: '#danger-bg', - disabled: '#clear', - }, - outline: 0, - backgroundClip: 'padding-box', - - CheckboxWrapper: { - cursor: 'pointer', - padding: '.75x', - margin: '-.75x', - }, - - Checkbox: { - display: 'grid', - placeItems: 'center', - radius: '.5r', - width: '(2x - 2bw)', - height: '(2x - 2bw)', - flexShrink: 0, - transition: 'theme', - opacity: { - '': 0, - 'selected | indeterminate | :hover | focused': 1, - }, - fill: { - '': '#white', - 'selected | indeterminate': '#purple-text', - 'invalid & !(selected | indeterminate)': '#white', - 'invalid & (selected | indeterminate)': '#danger', - disabled: '#dark.12', - }, - color: { - '': '#white', - 'disabled & !selected': '#clear', - }, - border: { - '': '#dark.30', - invalid: '#danger', - 'disabled | ((selected | indeterminate) & !invalid)': '#clear', - }, - }, - - Content: { - display: 'flex', - flow: 'column', - gap: '.25x', - flex: 1, - width: 'max 100%', - }, - - Label: { - preset: 't3', - color: 'inherit', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: 'max 100%', - }, - - Description: { - preset: 't4', - color: { - '': '#dark-03', - }, - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: 'max 100%', - }, }, }); @@ -243,6 +144,51 @@ const SectionListElement = tasty({ }, }); +// Checkbox component for multiple selection options +const ListBoxCheckbox = tasty({ + as: 'div', + styles: { + display: 'grid', + placeItems: 'center', + radius: '.5r', + width: '(2x - 2bw)', + height: '(2x - 2bw)', + flexShrink: 0, + transition: 'theme', + opacity: { + '': 0, + 'selected | indeterminate | :hover | focused': 1, + }, + fill: { + '': '#white', + 'selected | indeterminate': '#purple-text', + 'invalid & !(selected | indeterminate)': '#white', + 'invalid & (selected | indeterminate)': '#danger', + disabled: '#dark.12', + }, + color: { + '': '#white', + 'disabled & !selected': '#clear', + }, + border: { + '': '#dark.30', + invalid: '#danger', + 'disabled | ((selected | indeterminate) & !invalid)': '#clear', + }, + }, +}); + +const ListBoxCheckboxWrapper = tasty({ + as: 'div', + styles: { + display: 'grid', + placeItems: 'center', + placeContent: 'center', + cursor: 'pointer', + placeSelf: 'stretch', + }, +}); + export interface CubeListBoxProps extends AriaListBoxProps, CollectionBase, @@ -271,7 +217,7 @@ export interface CubeListBoxProps /** The selection mode of the ListBox */ selectionMode?: 'single' | 'multiple'; /** Ref for accessing the list DOM element */ - listRef?: RefObject; + listRef?: RefObject; /** Whether to disallow empty selection */ disallowEmptySelection?: boolean; /** Whether to wrap focus when reaching the end of the list */ @@ -336,6 +282,11 @@ export interface CubeListBoxProps * Label for the "Select All" option. Defaults to "Select All". */ selectAllLabel?: ReactNode; + + /** + * Props to apply to the "Select All" option. + */ + allValueProps?: Partial>; } const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES]; @@ -350,6 +301,7 @@ const SelectAllOption = ({ state, lastFocusSourceRef, onClick, + allValueProps = {}, }: { label?: ReactNode; isSelected: boolean; @@ -360,6 +312,7 @@ const SelectAllOption = ({ state: any; lastFocusSourceRef: MutableRefObject<'keyboard' | 'mouse' | 'other'>; onClick: (propagate?: boolean) => void; + allValueProps?: Partial>; }) => { const { hoverProps, isHovered } = useHover({ isDisabled }); @@ -373,6 +326,44 @@ const SelectAllOption = ({ const localRef = useRef(null); + // Create checkbox icon for select all option + const checkboxIcon = useMemo(() => { + if (!isCheckable) { + return null; + } + + return ( + + + {(isIndeterminate || isSelected) && markIcon} + + + ); + }, [ + isCheckable, + isSelected, + isDisabled, + isHovered, + isIndeterminate, + markIcon, + ]); + const handleOptionClick = (e) => { // Mark focus changes from mouse clicks if (lastFocusSourceRef) { @@ -405,35 +396,28 @@ const SelectAllOption = ({ return ( <> - - {isCheckable && ( -
-
- {(isIndeterminate || isSelected) && markIcon} -
-
- )} -
-
{label}
-
-
+ {label} + ); @@ -504,6 +488,7 @@ export const ListBox = forwardRef(function ListBox( onOptionClick, showSelectAll, selectAllLabel, + allValueProps, ...otherProps } = props; @@ -849,6 +834,7 @@ export const ListBox = forwardRef(function ListBox( isDisabled={isDisabled} isCheckable={isCheckable} size={size} + allValueProps={allValueProps} onClick={handleSelectAllClick} /> ) : ( @@ -1025,7 +1011,7 @@ function Option({ const { hoverProps, isHovered } = useHover({ isDisabled }); - const { optionProps, isPressed } = useOption( + const { optionProps, isPressed, labelProps, descriptionProps } = useOption( { key: item.key, isDisabled, @@ -1037,7 +1023,54 @@ function Option({ combinedRef, ); - const description = (item as any)?.props?.description; + // Create checkbox icon for multiple selection mode + const effectiveIcon = useMemo(() => { + if ( + !isCheckable || + state.selectionManager.selectionMode !== 'multiple' || + item.icon + ) { + return ( + (item as any)?.props?.icon ?? + (state.selectionManager.selectionMode !== 'multiple' ? null : undefined) + ); + } + + return ( + + + + + + ); + }, [ + isCheckable, + state.selectionManager.selectionMode, + isSelected, + isDisabled, + isFocused, + isHovered, + validationState, + item.icon, + ]); // Custom click handler for the entire option const handleOptionClick = (e) => { @@ -1092,41 +1125,64 @@ function Option({ } }; + // Filter out React Aria props that shouldn't reach the DOM + const { + onKeyDown, + onKeyUp, + tabIndex, + 'aria-selected': ariaSelected, + 'aria-disabled': ariaDisabled, + role, + ...filteredOptionProps + } = optionProps; + + const theme = + { valid: 'success', invalid: 'danger' }[validationState] || 'default'; + return ( - - {isCheckable && state.selectionManager.selectionMode === 'multiple' && ( -
-
- -
-
- )} -
-
{item.rendered}
- {description ? ( -
{description}
- ) : null} -
-
+ {item.rendered} + ); } @@ -1169,7 +1225,11 @@ function ListBoxSection(props: ListBoxSectionProps) { return ( {heading && ( - + {heading} )} @@ -1202,11 +1262,7 @@ const ListBoxSectionComponent = Object.assign(BaseSection, { displayName: 'Section', }) as SectionComponent; -ListBox.Item = Item as unknown as (props: { - description?: ReactNode; - textValue?: string; - [key: string]: any; -}) => ReactElement; +ListBox.Item = Item; ListBox.Section = ListBoxSectionComponent; diff --git a/src/components/fields/NumberInput/NumberInput.docs.mdx b/src/components/fields/NumberInput/NumberInput.docs.mdx index 5ce4463f3..289e51ee2 100644 --- a/src/components/fields/NumberInput/NumberInput.docs.mdx +++ b/src/components/fields/NumberInput/NumberInput.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { NumberInput } from './NumberInput'; import * as NumberInputStories from './NumberInput.stories'; @@ -172,6 +172,6 @@ This component supports all [Field properties](/docs/forms-field--docs) when use ## Related Components -- [TextInput](/components/TextInput) – Single-line or multiline text input -- [Slider](/components/Slider) – For selecting a value from a range +- [TextInput](/docs/forms-textinput--docs) – Single-line or multiline text input +- [Slider](/docs/forms-slider--docs) – For selecting a value from a range - [RangeInput](/components/RangeInput) – For selecting a numeric range diff --git a/src/components/fields/NumberInput/NumberInput.stories.tsx b/src/components/fields/NumberInput/NumberInput.stories.tsx index 3027ee81f..77811d3ab 100644 --- a/src/components/fields/NumberInput/NumberInput.stories.tsx +++ b/src/components/fields/NumberInput/NumberInput.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { IconCoin } from '@tabler/icons-react'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/PasswordInput/PasswordInput.docs.mdx b/src/components/fields/PasswordInput/PasswordInput.docs.mdx index 065da12d3..c8ec12523 100644 --- a/src/components/fields/PasswordInput/PasswordInput.docs.mdx +++ b/src/components/fields/PasswordInput/PasswordInput.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { PasswordInput } from './PasswordInput'; import * as PasswordInputStories from './PasswordInput.stories'; diff --git a/src/components/fields/PasswordInput/PasswordInput.stories.tsx b/src/components/fields/PasswordInput/PasswordInput.stories.tsx index c8e760ef7..68b8a95c5 100644 --- a/src/components/fields/PasswordInput/PasswordInput.stories.tsx +++ b/src/components/fields/PasswordInput/PasswordInput.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { IconKey } from '@tabler/icons-react'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/RadioGroup/RadioGroup.docs.mdx b/src/components/fields/RadioGroup/RadioGroup.docs.mdx index f82e86b12..fd6eade07 100644 --- a/src/components/fields/RadioGroup/RadioGroup.docs.mdx +++ b/src/components/fields/RadioGroup/RadioGroup.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { RadioGroup } from './RadioGroup'; import * as RadioGroupStories from './RadioGroup.stories.tsx'; diff --git a/src/components/fields/RadioGroup/RadioGroup.stories.tsx b/src/components/fields/RadioGroup/RadioGroup.stories.tsx index 3586c57e3..fd2384425 100644 --- a/src/components/fields/RadioGroup/RadioGroup.stories.tsx +++ b/src/components/fields/RadioGroup/RadioGroup.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { TEXT_VALUE_ARG } from '../../../stories/FormFieldArgs'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/SearchInput/SearchInput.docs.mdx b/src/components/fields/SearchInput/SearchInput.docs.mdx index 6dc357e45..7b5046ece 100644 --- a/src/components/fields/SearchInput/SearchInput.docs.mdx +++ b/src/components/fields/SearchInput/SearchInput.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { SearchInput } from './SearchInput'; import * as SearchInputStories from './SearchInput.stories'; diff --git a/src/components/fields/SearchInput/SearchInput.stories.tsx b/src/components/fields/SearchInput/SearchInput.stories.tsx index 82b100345..7cc571e0e 100644 --- a/src/components/fields/SearchInput/SearchInput.stories.tsx +++ b/src/components/fields/SearchInput/SearchInput.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/Select/Select.docs.mdx b/src/components/fields/Select/Select.docs.mdx index 0d3585ceb..b02f41c29 100644 --- a/src/components/fields/Select/Select.docs.mdx +++ b/src/components/fields/Select/Select.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { Select } from './Select'; import * as SelectStories from './Select.stories'; @@ -74,14 +74,47 @@ The `mods` property accepts the following modifiers you can override: ### Select.Item -Individual option items within the select dropdown. +Individual option items within the select dropdown. Each item is rendered using [ItemBase](/docs/content-itembase--docs) and supports all ItemBase properties for layout, icons, descriptions, and interactive features. -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| key | `string \| number` | - | Unique identifier for the item (required) | -| textValue | `string` | - | Text representation for accessibility and filtering | -| description | `ReactNode` | - | Secondary text displayed below the main label | -| children | `ReactNode` | - | The content to display as the option label | +#### Item API + +For detailed information about all available item properties, see [ItemBase documentation](/docs/content-itembase--docs). Key properties include: + +| Property | Type | Description | +|----------|------|-------------| +| key | `string \| number` | Unique identifier for the item (required) | +| children | `ReactNode` | The main content/label for the option | +| icon | `ReactNode` | Icon displayed before the content | +| rightIcon | `ReactNode` | Icon displayed after the content | +| description | `ReactNode` | Secondary text below the main content | +| descriptionPlacement | `'inline' \| 'block'` | How the description is positioned | +| prefix | `ReactNode` | Content before the main text | +| suffix | `ReactNode` | Content after the main text | +| tooltip | `string \| boolean \| object` | Tooltip configuration | +| styles | `Styles` | Custom styling for the item | +| qa | `string` | QA identifier for testing | + +#### Example with Rich Items + +```jsx + +``` ### Select.Section @@ -283,6 +316,6 @@ This component supports all [Field properties](/docs/forms-field--docs) when use ## Related Components -- [ComboBox](/components/ComboBox) - For searchable selection with large option lists -- [RadioGroup](/components/RadioGroup) - For visible selection options -- [Checkbox](/components/Checkbox) - For multiple selection scenarios +- [ComboBox](/docs/forms-combobox--docs) - For searchable selection with large option lists +- [RadioGroup](/docs/forms-radiogroup--docs) - For visible selection options +- [Checkbox](/docs/forms-checkbox--docs) - For multiple selection scenarios diff --git a/src/components/fields/Select/Select.stories.tsx b/src/components/fields/Select/Select.stories.tsx index 3249e7eb8..bdad89e33 100644 --- a/src/components/fields/Select/Select.stories.tsx +++ b/src/components/fields/Select/Select.stories.tsx @@ -1,6 +1,5 @@ -import { Meta, StoryFn } from '@storybook/react'; -import { userEvent, within } from '@storybook/test'; -import { IconCoin } from '@tabler/icons-react'; +import { IconCoin, IconUser } from '@tabler/icons-react'; +import { userEvent, within } from 'storybook/test'; import { baseProps } from '../../../stories/lists/baseProps'; import { Text } from '../../content/Text'; @@ -8,6 +7,8 @@ import { Space } from '../../layout/Space'; import { CubeSelectProps, Select } from './Select'; +import type { Meta, StoryObj } from '@storybook/react'; + export default { title: 'Forms/Select', component: Select, @@ -69,7 +70,7 @@ export default { /* Presentation */ type: { - options: ['outline', 'clear', 'primary', 'secondary', 'neutral', 'link'], + options: ['outline', 'clear', 'primary', 'secondary', 'neutral'], control: { type: 'radio' }, description: 'Visual style variant of the select', table: { @@ -368,7 +369,7 @@ const options = [ 'very-long-option-value-with-suffix', ]; -const Template: StoryFn> = (args) => ( +const Template: StoryObj>['render'] = (args) => ( { await userEvent.click(button); }; -export const Wide: StoryFn> = (args) => ( +export const Wide: StoryObj>['render'] = (args) => ( > = (args) => ( WithDescription.args = {}; WithDescription.play = WithDisabledOption.play; +export const WithIconsAndDescriptions: StoryObj< + CubeSelectProps +>['render'] = (args) => ( + +); +WithIconsAndDescriptions.args = {}; +WithIconsAndDescriptions.storyName = 'With icons and descriptions'; + // ------------------------------ // Section stories // ------------------------------ -export const SectionsStatic: StoryFn> = (args) => ( +export const SectionsStatic: StoryObj>['render'] = ( + args, +) => ( {options.map((option) => ( @@ -559,3 +605,57 @@ DifferentSizes.parameters = { }, }, }; + +export const WithTooltips: StoryObj>['render'] = ( + args, +) => ( + +); + +WithTooltips.args = {}; +WithTooltips.storyName = 'With tooltips'; +WithTooltips.play = WithDisabledOption.play; + +WithTooltips.parameters = { + docs: { + description: { + story: + 'Select options support tooltips to provide additional context. Use either a simple string or a full tooltip configuration object with title, description, and placement options.', + }, + }, +}; diff --git a/src/components/fields/Select/Select.tsx b/src/components/fields/Select/Select.tsx index 78013c275..00cfa2a7f 100644 --- a/src/components/fields/Select/Select.tsx +++ b/src/components/fields/Select/Select.tsx @@ -23,10 +23,11 @@ import { useOverlayPosition, useSelect, } from 'react-aria'; -import { Section as BaseSection, Item, useSelectState } from 'react-stately'; +import { Section as BaseSection, useSelectState } from 'react-stately'; +import { CubeTooltipProviderProps } from 'src/components/overlays/Tooltip/TooltipProvider'; import styled from 'styled-components'; -import { DirectionIcon, DownIcon, LoadingIcon } from '../../../icons/index'; +import { DirectionIcon, LoadingIcon } from '../../../icons/index'; import { useProviderProps } from '../../../provider'; import { FieldBaseProps } from '../../../shared/index'; import { @@ -47,31 +48,17 @@ import { mergeProps, useCombinedRefs } from '../../../utils/react/index'; import { useFocus } from '../../../utils/react/interactions'; import { useEventBus } from '../../../utils/react/useEventBus'; import { getOverlayTransitionCSS } from '../../../utils/transitions'; -import { - DEFAULT_BUTTON_STYLES, - DEFAULT_CLEAR_STYLES, - DEFAULT_LINK_STYLES, - DEFAULT_NEUTRAL_STYLES, - DEFAULT_OUTLINE_STYLES, - DEFAULT_PRIMARY_STYLES, - DEFAULT_SECONDARY_STYLES, - SPECIAL_CLEAR_STYLES, - SPECIAL_LINK_STYLES, - SPECIAL_NEUTRAL_STYLES, - SPECIAL_OUTLINE_STYLES, - SPECIAL_PRIMARY_STYLES, - SPECIAL_SECONDARY_STYLES, -} from '../../actions/index'; import { StyledDivider as ListDivider, StyledSectionHeading as ListSectionHeading, StyledSection as ListSectionWrapper, } from '../../actions/Menu/styled'; +import { ItemBase } from '../../content/ItemBase'; import { useFieldProps, useFormProps, wrapWithField } from '../../form'; +import { Item } from '../../Item'; import { OverlayWrapper } from '../../overlays/OverlayWrapper'; import { InvalidIcon } from '../../shared/InvalidIcon'; import { ValidIcon } from '../../shared/ValidIcon'; -import { INPUT_WRAPPER_STYLES } from '../index'; const SelectWrapperElement = tasty({ styles: { @@ -92,115 +79,18 @@ const SelectWrapperElement = tasty({ }, }); -type VariantType = - | 'default.primary' - | 'default.secondary' - | 'default.outline' - | 'default.neutral' - | 'default.clear' - | 'default.link' - | 'special.primary' - | 'special.secondary' - | 'special.outline' - | 'special.neutral' - | 'special.clear' - | 'special.link'; - -function WithValidationState(styles: Styles) { - return { - ...styles, - border: { - ...(typeof styles.border === 'object' ? styles.border : {}), - invalid: '#danger-text', - valid: '#success-text', - }, - }; -} - -const SelectElement = tasty({ +const SelectTrigger = tasty(ItemBase, { as: 'button', - qa: 'Button', + qa: 'Trigger', styles: { - ...INPUT_WRAPPER_STYLES, - ...DEFAULT_BUTTON_STYLES, - backgroundClip: 'initial', - gap: 0, - - Prefix: { - display: 'flex', - placeContent: 'center start', - placeItems: 'center', - placeSelf: 'center start', - flexShrink: 0, - }, - - Suffix: { - display: 'flex', - placeContent: 'center start', - placeItems: 'center', - placeSelf: 'center end', - flexShrink: 0, - }, + reset: 'button', - ButtonIcon: { - display: 'grid', - placeItems: 'center', - color: 'inherit', - fontSize: '$icon-size', - }, - - Value: { - display: 'block', - width: 'max 100%', - placeItems: 'center stretch', - preset: { - '': 't3', - '[data-type="primary"]': 't3m', - }, - padding: { - '': 0, - prefix: '.5x left', - suffix: '.5x right', - 'prefix & suffix': '.5x left right', - }, - color: 'inherit', + Label: { opacity: { '': 1, - placeholder: '.6', + placeholder: '$disabled-opacity', }, - textAlign: 'left', - fill: '#clear', - textOverflow: 'ellipsis', - overflow: 'hidden', - flexGrow: 1, - }, - - '& [data-element="Prefix"] [data-element="ButtonIcon"]': { - marginLeft: -4, - placeSelf: 'center start', }, - - '& [data-element="Suffix"] [data-element="ButtonIcon"]': { - marginRight: -4, - placeSelf: 'center end', - }, - }, - variants: { - // Default theme - 'default.primary': DEFAULT_PRIMARY_STYLES, - 'default.secondary': DEFAULT_SECONDARY_STYLES, - 'default.outline': WithValidationState(DEFAULT_OUTLINE_STYLES), - 'default.neutral': WithValidationState(DEFAULT_NEUTRAL_STYLES), - 'default.clear': WithValidationState(DEFAULT_CLEAR_STYLES), - 'default.link': DEFAULT_LINK_STYLES, - - // Special theme - 'special.primary': SPECIAL_PRIMARY_STYLES, - 'special.secondary': SPECIAL_SECONDARY_STYLES, - 'special.outline': WithValidationState(SPECIAL_OUTLINE_STYLES), - 'special.neutral': WithValidationState(SPECIAL_NEUTRAL_STYLES), - 'special.clear': WithValidationState(SPECIAL_CLEAR_STYLES), - 'special.link': SPECIAL_LINK_STYLES, }, }); @@ -235,57 +125,12 @@ export const ListBoxElement = tasty({ }, }); -const OptionElement = tasty({ +// Use ItemBase for options to unify item visuals and reduce custom styling +const OptionItem = tasty(ItemBase, { as: 'li', + qa: 'Option', styles: { - display: 'flex', - placeContent: 'start center', - placeItems: 'start center', - flow: 'column', - gap: '0', - padding: '.5x 1x', - cursor: { - '': 'default', - disabled: 'not-allowed', - }, - radius: true, - boxSizing: 'border-box', - color: { - '': '#dark-02', - selected: '#dark', - disabled: '#dark-04', - }, - fill: { - '': '#clear', - focused: '#dark.03', - selected: '#dark.09', - 'selected & focused': '#dark.12', - pressed: '#dark.09', - disabled: '#clear', - }, - preset: 't3', - transition: 'theme', - width: 'max 100%', - height: { - '': 'min $size-md', - '[data-size="large"]': 'min $size-lg', - }, - - Label: { - preset: 't3', - overflow: 'hidden', - textOverflow: 'ellipsis', - width: 'max 100%', - }, - - Description: { - preset: 't4', - color: '#dark-03', - overflow: 'hidden', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - width: 'max 100%', - }, + '$inline-compensation': '0px', }, }); @@ -307,12 +152,28 @@ export interface CubeSelectBaseProps BaseStyleProps, OuterStyleProps, ColorStyleProps, - FieldBaseProps, + Omit, CollectionBase, Omit, 'errorMessage'> { icon?: ReactElement; + rightIcon?: ReactNode; prefix?: ReactNode; suffix?: ReactNode; + /** Description text for the trigger. Note: Different from field-level description. */ + description?: ReactNode; + descriptionPlacement?: 'inline' | 'block' | 'auto'; + /** Keyboard shortcut that triggers the select when pressed */ + hotkeys?: string; + /** + * Tooltip content and configuration for the trigger: + * - string: simple tooltip text + * - true: auto tooltip on overflow (shows selected value as tooltip when truncated) + * - object: advanced configuration with optional auto property + */ + tooltip?: + | string + | boolean + | (Omit & { auto?: boolean }); triggerRef?: RefObject; isLoading?: boolean; loadingIndicator?: ReactNode; @@ -367,6 +228,7 @@ function Select( label, extra, icon, + rightIcon, labelStyles, isRequired, necessityIndicator, @@ -390,6 +252,8 @@ function Select( suffix, message, description, + descriptionPlacement, + hotkeys, direction = 'bottom', shouldFlip = true, placeholder, @@ -466,21 +330,6 @@ function Select( let triggerWidth = triggerRef?.current?.offsetWidth; - if (icon) { - icon =
{icon}
; - - if (prefix) { - prefix = ( - <> - {icon} - {prefix} - - ); - } else { - prefix = icon; - } - } - const showPlaceholder = !!placeholder?.trim() && !state.selectedItem; const modifiers = useMemo( @@ -506,6 +355,19 @@ function Select( ], ); + suffix = useMemo(() => { + if (!suffix && !validationState) { + return null; + } + + return ( + <> + {suffix} + {validationState ? validation : null} + + ); + }, [suffix, validationState, validation]); + let selectField = ( ( label={props.label} name={props.name} /> - - {prefix ?
{prefix}
: null} - - {state.selectedItem - ? state.selectedItem.rendered - : placeholder || <> } - -
- {suffixPosition === 'before' ? suffix : null} - {validationState && !isLoading ? validation : null} - {isLoading && } - {suffixPosition === 'after' ? suffix : null} -
+ prefix={prefix} + suffix={suffix} + icon={icon} + rightIcon={ + rightIcon !== undefined ? ( + rightIcon + ) : isLoading ? ( + + ) : ( -
-
-
+ ) + } + description={description} + descriptionPlacement={descriptionPlacement} + hotkeys={hotkeys} + tooltip={tooltip} + labelProps={valueProps} + > + {state.selectedItem + ? state.selectedItem.rendered + : placeholder || <> } + { - const menuTriggerEl = el.closest('[data-menu-trigger]'); + const menuTriggerEl = el.closest('[data-popover-trigger]'); // If no menu trigger was clicked, allow closing if (!menuTriggerEl) return true; // If the same trigger that opened this select was clicked, allow closing @@ -674,7 +543,6 @@ export function ListBoxPopup({ item={item} state={state} optionStyles={optionStyles} - headingStyles={{ padding: '.5x 1.5x' }} sectionStyles={undefined} shouldUseVirtualFocus={shouldUseVirtualFocus} size={listItemSize} @@ -714,12 +582,12 @@ export function ListBoxPopup({ } function Option({ item, state, styles, shouldUseVirtualFocus, size }) { - let ref = useRef(null); + let ref = useRef(null); let isDisabled = state.disabledKeys.has(item.key); let isSelected = state.selectionManager.isSelected(item.key); let isVirtualFocused = state.selectionManager.focusedKey === item.key; - let { optionProps, isPressed } = useOption( + let { optionProps, isPressed, labelProps, descriptionProps } = useOption( { key: item.key, isDisabled, @@ -736,25 +604,57 @@ function Option({ item, state, styles, shouldUseVirtualFocus, size }) { // style to the focused option let { isFocused, focusProps } = useFocus({ isDisabled }); - const description = (item as any)?.props?.description; + const { + qa, + description, + icon, + prefix, + suffix, + rightIcon, + descriptionPlacement, + tooltip, + styles: itemStyles, + } = ((item as any)?.props || {}) as { + description?: React.ReactNode; + icon?: React.ReactElement; + prefix?: React.ReactNode; + suffix?: React.ReactNode; + rightIcon?: React.ReactElement; + styles?: Styles; + descriptionPlacement?: 'inline' | 'block' | 'auto'; + tooltip?: + | string + | boolean + | (Omit & { auto?: boolean }); + qa?: string; + }; return ( - -
{item.rendered}
- {description ?
{description}
: null} -
+ {item.rendered} + ); } @@ -789,7 +689,11 @@ function SelectSection(props: SelectSectionProps) { return ( {heading && ( - + {heading} )} @@ -827,10 +731,7 @@ const __Select = Object.assign( Section: typeof SelectSectionComponent; }, { - Item: Item as unknown as (props: { - description?: ReactNode; - [key: string]: any; - }) => ReactElement, + Item, Section: SelectSectionComponent, }, ); diff --git a/src/components/fields/Select/select.test.tsx b/src/components/fields/Select/select.test.tsx index ff39f440f..1a8976d5c 100644 --- a/src/components/fields/Select/select.test.tsx +++ b/src/components/fields/Select/select.test.tsx @@ -68,4 +68,28 @@ describe(' + Blue + Red + Green + , + ); + + const select = getByRole('button'); + + // First click - open the popover + await act(async () => await userEvent.click(select)); + + // Check that the listbox is visible + expect(queryByRole('listbox')).toBeInTheDocument(); + + // Second click - should close the popover + await act(async () => await userEvent.click(select)); + + // Check that the listbox is no longer visible + expect(queryByRole('listbox')).not.toBeInTheDocument(); + }); }); diff --git a/src/components/fields/Slider/RangeSlider.stories.tsx b/src/components/fields/Slider/RangeSlider.stories.tsx index 928c1ecf3..e6a27de97 100644 --- a/src/components/fields/Slider/RangeSlider.stories.tsx +++ b/src/components/fields/Slider/RangeSlider.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/Slider/Slider.docs.mdx b/src/components/fields/Slider/Slider.docs.mdx index e5d31eff4..de08b0e4b 100644 --- a/src/components/fields/Slider/Slider.docs.mdx +++ b/src/components/fields/Slider/Slider.docs.mdx @@ -1,5 +1,5 @@ -import { Meta, Canvas, ArgTypes, Source } from '@storybook/blocks'; -import { Badge } from '@storybook/components'; +import { Meta, Canvas, ArgTypes, Source } from '@storybook/addon-docs/blocks'; +import { Badge } from 'storybook/internal/components'; import * as SliderStories from './Slider.stories'; diff --git a/src/components/fields/Slider/Slider.stories.tsx b/src/components/fields/Slider/Slider.stories.tsx index 55903fbb1..2d4e5f9ae 100644 --- a/src/components/fields/Slider/Slider.stories.tsx +++ b/src/components/fields/Slider/Slider.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/Switch/Switch.docs.mdx b/src/components/fields/Switch/Switch.docs.mdx index 59ee1b0d5..bd74d79a9 100644 --- a/src/components/fields/Switch/Switch.docs.mdx +++ b/src/components/fields/Switch/Switch.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { Switch } from './Switch'; import * as SwitchStories from './Switch.stories.tsx'; @@ -191,6 +191,6 @@ This component supports all [Field properties](/docs/forms-field--docs) when use ## Related Components -- [Checkbox](/components/Checkbox) - For selection that doesn't take immediate effect -- [RadioGroup](/components/RadioGroup) - For selecting one option from multiple choices -- [Button](/components/Button) - For actions that require confirmation +- [Checkbox](/docs/forms-checkbox--docs) - For selection that doesn't take immediate effect +- [RadioGroup](/docs/forms-radiogroup--docs) - For selecting one option from multiple choices +- [Button](/docs/actions-button--docs) - For actions that require confirmation diff --git a/src/components/fields/Switch/Switch.stories.tsx b/src/components/fields/Switch/Switch.stories.tsx index d65946e9a..bee4e5d92 100644 --- a/src/components/fields/Switch/Switch.stories.tsx +++ b/src/components/fields/Switch/Switch.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/TextArea/TextArea.docs.mdx b/src/components/fields/TextArea/TextArea.docs.mdx index 0480bc435..659e6fb51 100644 --- a/src/components/fields/TextArea/TextArea.docs.mdx +++ b/src/components/fields/TextArea/TextArea.docs.mdx @@ -1,5 +1,5 @@ -import { Meta, Canvas, ArgTypes, Source } from '@storybook/blocks'; -import { Badge } from '@storybook/components'; +import { Meta, Canvas, ArgTypes, Source } from '@storybook/addon-docs/blocks'; +import { Badge } from 'storybook/internal/components'; import * as TextAreaStories from './TextArea.stories'; diff --git a/src/components/fields/TextArea/TextArea.stories.tsx b/src/components/fields/TextArea/TextArea.stories.tsx index 6c03a49cd..14c3c46b6 100644 --- a/src/components/fields/TextArea/TextArea.stories.tsx +++ b/src/components/fields/TextArea/TextArea.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { IconCoin } from '@tabler/icons-react'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/TextInput/TextInput.docs.mdx b/src/components/fields/TextInput/TextInput.docs.mdx index 38a7a314c..f8369b839 100644 --- a/src/components/fields/TextInput/TextInput.docs.mdx +++ b/src/components/fields/TextInput/TextInput.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from '@storybook/blocks'; +import { Meta, Story, Controls } from '@storybook/addon-docs/blocks'; import { TextInput } from './TextInput'; import * as TextInputStories from './TextInput.stories'; diff --git a/src/components/fields/TextInput/TextInput.stories.tsx b/src/components/fields/TextInput/TextInput.stories.tsx index 2098bfda7..b8d1e9e6d 100644 --- a/src/components/fields/TextInput/TextInput.stories.tsx +++ b/src/components/fields/TextInput/TextInput.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { IconCoin } from '@tabler/icons-react'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/fields/TextInputMapper/TextInputMapper.docs.mdx b/src/components/fields/TextInputMapper/TextInputMapper.docs.mdx index b0ed3e069..175230d16 100644 --- a/src/components/fields/TextInputMapper/TextInputMapper.docs.mdx +++ b/src/components/fields/TextInputMapper/TextInputMapper.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, ArgTypes, Source } from '@storybook/blocks'; +import { Meta, Canvas, ArgTypes, Source } from '@storybook/addon-docs/blocks'; import * as TextInputMapperStories from './TextInputMapper.stories'; diff --git a/src/components/fields/TextInputMapper/TextInputMapper.stories.tsx b/src/components/fields/TextInputMapper/TextInputMapper.stories.tsx index 3b6842d9e..f4b5c1568 100644 --- a/src/components/fields/TextInputMapper/TextInputMapper.stories.tsx +++ b/src/components/fields/TextInputMapper/TextInputMapper.stories.tsx @@ -1,5 +1,5 @@ -import { StoryFn } from '@storybook/react'; -import { userEvent, within } from '@storybook/test'; +import { StoryFn } from '@storybook/react-vite'; +import { userEvent, within } from 'storybook/test'; import { baseProps } from '../../../stories/lists/baseProps'; import { Form } from '../../form'; diff --git a/src/components/form/FieldWrapper/FieldWrapper.stories.tsx b/src/components/form/FieldWrapper/FieldWrapper.stories.tsx index 936afbfef..b5a63cc78 100644 --- a/src/components/form/FieldWrapper/FieldWrapper.stories.tsx +++ b/src/components/form/FieldWrapper/FieldWrapper.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { IconCoin } from '@tabler/icons-react'; import { Button } from '../../actions/index'; diff --git a/src/components/form/Form/ComplexForm.stories.tsx b/src/components/form/Form/ComplexForm.stories.tsx index 977af0b59..ce05a1674 100644 --- a/src/components/form/Form/ComplexForm.stories.tsx +++ b/src/components/form/Form/ComplexForm.stories.tsx @@ -1,6 +1,6 @@ import { linkTo } from '@storybook/addon-links'; -import { StoryFn } from '@storybook/react'; -import { expect, userEvent, waitFor, within } from '@storybook/test'; +import { StoryFn } from '@storybook/react-vite'; +import { expect, userEvent, waitFor, within } from 'storybook/test'; import { Block, diff --git a/src/components/form/Form/Field.docs.mdx b/src/components/form/Form/Field.docs.mdx index 66e9938a3..fb34263af 100644 --- a/src/components/form/Form/Field.docs.mdx +++ b/src/components/form/Form/Field.docs.mdx @@ -1,5 +1,5 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; -import { Badge } from '@storybook/components'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; +import { Badge } from 'storybook/internal/components'; import * as FieldStories from './Field.stories'; diff --git a/src/components/form/Form/Field.stories.tsx b/src/components/form/Form/Field.stories.tsx index 8f95ec1ba..690a959b1 100644 --- a/src/components/form/Form/Field.stories.tsx +++ b/src/components/form/Form/Field.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; import { Block } from '../../Block'; diff --git a/src/components/form/Form/Form.docs.mdx b/src/components/form/Form/Form.docs.mdx index 252e881a8..fc5a9f40e 100644 --- a/src/components/form/Form/Form.docs.mdx +++ b/src/components/form/Form/Form.docs.mdx @@ -1,5 +1,5 @@ -import { Meta, Canvas, ArgTypes } from '@storybook/blocks'; -import { Badge } from '@storybook/components'; +import { Meta, Canvas, ArgTypes } from '@storybook/addon-docs/blocks'; +import { Badge } from 'storybook/internal/components'; import * as FormStories from './Form.stories'; diff --git a/src/components/form/Form/Form.stories.tsx b/src/components/form/Form/Form.stories.tsx index 2e0fed72a..4279e9994 100644 --- a/src/components/form/Form/Form.stories.tsx +++ b/src/components/form/Form/Form.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryObj } from '@storybook/react'; +import { Meta, StoryObj } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; import { Input } from '../../fields'; diff --git a/src/components/form/Form/FormInstance.docs.mdx b/src/components/form/Form/FormInstance.docs.mdx index 8300fe444..ebd5e7735 100644 --- a/src/components/form/Form/FormInstance.docs.mdx +++ b/src/components/form/Form/FormInstance.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, ArgTypes } from '@storybook/blocks'; +import { Meta, ArgTypes } from '@storybook/addon-docs/blocks'; import { CubeFormInstance } from './use-form'; diff --git a/src/components/form/Form/field.test.tsx b/src/components/form/Form/field.test.tsx index ae372358b..17bf3409d 100644 --- a/src/components/form/Form/field.test.tsx +++ b/src/components/form/Form/field.test.tsx @@ -1,5 +1,4 @@ -import { waitFor } from '@storybook/test'; -import { render } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { useEffect, useState } from 'react'; import { act, renderWithForm, userEvent } from '../../../test/index'; diff --git a/src/components/layout/ResizablePanel.stories.tsx b/src/components/layout/ResizablePanel.stories.tsx index 3ab356a07..48f8f4538 100644 --- a/src/components/layout/ResizablePanel.stories.tsx +++ b/src/components/layout/ResizablePanel.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { useState } from 'react'; import { Panel } from './Panel'; diff --git a/src/components/overlays/AlertDialog/AlertDialog.stories.tsx b/src/components/overlays/AlertDialog/AlertDialog.stories.tsx index 7da80158d..6cfd89684 100644 --- a/src/components/overlays/AlertDialog/AlertDialog.stories.tsx +++ b/src/components/overlays/AlertDialog/AlertDialog.stories.tsx @@ -1,6 +1,6 @@ -import { action } from '@storybook/addon-actions'; -import { Meta, StoryFn } from '@storybook/react'; -import { expect, userEvent, within } from '@storybook/test'; +import { Meta, StoryFn } from '@storybook/react-vite'; +import { action } from 'storybook/actions'; +import { expect, userEvent, within } from 'storybook/test'; import { baseProps } from '../../../stories/lists/baseProps'; import { Button } from '../../actions'; diff --git a/src/components/overlays/Dialog/Dialog.docs.mdx b/src/components/overlays/Dialog/Dialog.docs.mdx index f0014f34a..98acf6f36 100644 --- a/src/components/overlays/Dialog/Dialog.docs.mdx +++ b/src/components/overlays/Dialog/Dialog.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story, Controls } from '@storybook/blocks'; +import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks'; import { Dialog } from './Dialog'; import * as DialogStories from './Dialog.stories'; @@ -197,9 +197,9 @@ Dialog is typically used within `DialogTrigger` or `Modal` components for proper ## Related Components -- [DialogTrigger](/components/DialogTrigger) - Manages dialog open/close state and positioning +- [DialogTrigger](/docs/overlays-dialogtrigger--docs) - Manages dialog open/close state and positioning - [Modal](/components/Modal) - Lower-level modal overlay container -- [AlertDialog](/components/AlertDialog) - Specialized component for alert scenarios +- [AlertDialog](/docs/overlays-alertdialog--docs) - Specialized component for alert scenarios - [Header](/components/Header) - Dialog header content component - [Content](/components/Content) - Dialog body content component - [Footer](/components/Footer) - Dialog footer content component diff --git a/src/components/overlays/Dialog/Dialog.stories.tsx b/src/components/overlays/Dialog/Dialog.stories.tsx index 1df1e7a43..c552d2f7f 100644 --- a/src/components/overlays/Dialog/Dialog.stories.tsx +++ b/src/components/overlays/Dialog/Dialog.stories.tsx @@ -1,7 +1,7 @@ import { FocusableRefValue } from '@react-types/shared'; -import { StoryFn } from '@storybook/react'; -import { expect, userEvent, waitFor, within } from '@storybook/test'; +import { StoryFn } from '@storybook/react-vite'; import { useRef, useState } from 'react'; +import { expect, userEvent, waitFor, within } from 'storybook/test'; import { Button, diff --git a/src/components/overlays/Dialog/DialogContainer.docs.mdx b/src/components/overlays/Dialog/DialogContainer.docs.mdx index 35890bc7e..6076d556a 100644 --- a/src/components/overlays/Dialog/DialogContainer.docs.mdx +++ b/src/components/overlays/Dialog/DialogContainer.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from '@storybook/blocks'; +import { Meta, Story, Controls } from '@storybook/addon-docs/blocks'; import * as DialogContainerStories from './DialogContainer.stories'; @@ -197,7 +197,7 @@ function ConditionalDialog() { ## Related Components -- [DialogTrigger](/components/DialogTrigger) - Use when you have a specific trigger element -- [Dialog](/components/Dialog) - The content component used within DialogContainer +- [DialogTrigger](/docs/overlays-dialogtrigger--docs) - Use when you have a specific trigger element +- [Dialog](/docs/overlays-dialog--docs) - The content component used within DialogContainer - [Modal](/components/Modal) - Lower-level modal overlay component -- [Button](/components/Button) - Common element for dialog actions +- [Button](/docs/actions-button--docs) - Common element for dialog actions diff --git a/src/components/overlays/Dialog/DialogContainer.stories.tsx b/src/components/overlays/Dialog/DialogContainer.stories.tsx index 759c8d027..9ef6d798f 100644 --- a/src/components/overlays/Dialog/DialogContainer.stories.tsx +++ b/src/components/overlays/Dialog/DialogContainer.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { useState } from 'react'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/overlays/Dialog/DialogForm.stories.tsx b/src/components/overlays/Dialog/DialogForm.stories.tsx index 526f29ec0..e34f40508 100644 --- a/src/components/overlays/Dialog/DialogForm.stories.tsx +++ b/src/components/overlays/Dialog/DialogForm.stories.tsx @@ -1,11 +1,11 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; +import { useState } from 'react'; import { expect, userEvent, waitForElementToBeRemoved, within, -} from '@storybook/test'; -import { useState } from 'react'; +} from 'storybook/test'; import { baseProps } from '../../../stories/lists/baseProps'; import { Button } from '../../actions'; diff --git a/src/components/overlays/Dialog/DialogTrigger.docs.mdx b/src/components/overlays/Dialog/DialogTrigger.docs.mdx index b4247c9ea..06f235070 100644 --- a/src/components/overlays/Dialog/DialogTrigger.docs.mdx +++ b/src/components/overlays/Dialog/DialogTrigger.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls } from '@storybook/blocks'; +import { Meta, Controls } from '@storybook/addon-docs/blocks'; import * as DialogTriggerStories from './DialogTrigger.stories'; @@ -174,7 +174,7 @@ function ControlledDialog() { ## Related Components -- [Dialog](/components/Dialog) - The content component used within DialogTrigger +- [Dialog](/docs/overlays-dialog--docs) - The content component used within DialogTrigger - [Modal](/components/Modal) - Lower-level modal overlay component - [Popover](/components/Popover) - Standalone popover component -- [Button](/components/Button) - Common trigger element +- [Button](/docs/actions-button--docs) - Common trigger element diff --git a/src/components/overlays/Dialog/DialogTrigger.stories.tsx b/src/components/overlays/Dialog/DialogTrigger.stories.tsx index ea197f96a..4a37af26b 100644 --- a/src/components/overlays/Dialog/DialogTrigger.stories.tsx +++ b/src/components/overlays/Dialog/DialogTrigger.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; import { Button } from '../../actions/Button/Button'; diff --git a/src/components/overlays/Dialog/UseDialogContainer.docs.mdx b/src/components/overlays/Dialog/UseDialogContainer.docs.mdx index 6df4550a3..5b12af528 100644 --- a/src/components/overlays/Dialog/UseDialogContainer.docs.mdx +++ b/src/components/overlays/Dialog/UseDialogContainer.docs.mdx @@ -1,4 +1,4 @@ -import { Meta } from '@storybook/blocks'; +import { Meta } from '@storybook/addon-docs/blocks'; @@ -563,9 +563,9 @@ To avoid this error, always ensure `dialog.rendered` is included in your JSX bef ## Related Components -- [DialogContainer](/components/DialogContainer) - The underlying container component -- [Dialog](/components/Dialog) - The dialog content component -- [DialogTrigger](/components/DialogTrigger) - Alternative for trigger-based dialogs +- [DialogContainer](/docs/overlays-dialogcontainer--docs) - The underlying container component +- [Dialog](/docs/overlays-dialog--docs) - The dialog content component +- [DialogTrigger](/docs/overlays-dialogtrigger--docs) - Alternative for trigger-based dialogs - [Modal](/components/Modal) - Lower-level modal overlay component ## Migration from DialogContainer diff --git a/src/components/overlays/NewNotifications/Notifications.stories.tsx b/src/components/overlays/NewNotifications/Notifications.stories.tsx index a4c81c4fa..90db3ccb4 100644 --- a/src/components/overlays/NewNotifications/Notifications.stories.tsx +++ b/src/components/overlays/NewNotifications/Notifications.stories.tsx @@ -1,7 +1,7 @@ -import { Meta, StoryFn } from '@storybook/react'; -import { expect, userEvent, within } from '@storybook/test'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { IconBell, IconBellFilled, IconBrandWechat } from '@tabler/icons-react'; import { Key, useRef, useState } from 'react'; +import { expect, userEvent, within } from 'storybook/test'; import { wait } from '../../../test/utils/wait'; import { Button } from '../../actions'; diff --git a/src/components/overlays/Toasts/Toasts.stories.tsx b/src/components/overlays/Toasts/Toasts.stories.tsx index 6737d12c2..ebbb6aa14 100644 --- a/src/components/overlays/Toasts/Toasts.stories.tsx +++ b/src/components/overlays/Toasts/Toasts.stories.tsx @@ -1,6 +1,6 @@ -import { Meta, StoryFn } from '@storybook/react'; -import { expect, userEvent, within } from '@storybook/test'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { IconBell } from '@tabler/icons-react'; +import { expect, userEvent, within } from 'storybook/test'; import { Button } from '../../actions'; diff --git a/src/components/overlays/Tooltip/Tooltip.stories.tsx b/src/components/overlays/Tooltip/Tooltip.stories.tsx index bc099e6c7..ba680c1a7 100644 --- a/src/components/overlays/Tooltip/Tooltip.stories.tsx +++ b/src/components/overlays/Tooltip/Tooltip.stories.tsx @@ -1,5 +1,5 @@ -import { ComponentMeta, Story } from '@storybook/react'; -import { expect, userEvent, waitFor, within } from '@storybook/test'; +import { ComponentMeta, Story } from '@storybook/react-vite'; +import { expect, userEvent, waitFor, within } from 'storybook/test'; import { baseProps } from '../../../stories/lists/baseProps'; import { Button } from '../../actions'; diff --git a/src/components/portal/storybook/portal.stories.tsx b/src/components/portal/storybook/portal.stories.tsx index d69bbec67..f22c2d137 100644 --- a/src/components/portal/storybook/portal.stories.tsx +++ b/src/components/portal/storybook/portal.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { useRef } from 'react'; import { Portal } from '../Portal'; diff --git a/src/components/status/LoadingAnimation/LoadingAnimation.stories.tsx b/src/components/status/LoadingAnimation/LoadingAnimation.stories.tsx index 7b92fdb90..e6222e961 100644 --- a/src/components/status/LoadingAnimation/LoadingAnimation.stories.tsx +++ b/src/components/status/LoadingAnimation/LoadingAnimation.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { baseProps } from '../../../stories/lists/baseProps'; diff --git a/src/components/status/Spin/Spin.stories.tsx b/src/components/status/Spin/Spin.stories.tsx index df641b8db..0aae16f29 100644 --- a/src/components/status/Spin/Spin.stories.tsx +++ b/src/components/status/Spin/Spin.stories.tsx @@ -1,4 +1,4 @@ -import { Meta, StoryFn } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react-vite'; import { Paragraph } from '../../content/Paragraph'; diff --git a/src/data/item-themes.ts b/src/data/item-themes.ts new file mode 100644 index 000000000..8d1e5185e --- /dev/null +++ b/src/data/item-themes.ts @@ -0,0 +1,567 @@ +import { Styles } from 'src/tasty'; + +export const VALIDATION_STYLES: Styles = { + border: { + invalid: '#danger-text', + valid: '#success-text', + } as Record, +} as const; + +// ---------- DEFAULT THEME ---------- +export const DEFAULT_PRIMARY_STYLES: Styles = { + outline: { + '': '0 #purple-text.0', + focused: '1bw #purple-text', + }, + border: { + '': '#clear', + pressed: '#purple-text', + focused: '#purple-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#purple', + hovered: '#purple-text', + pressed: '#purple', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#white', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DEFAULT_SECONDARY_STYLES: Styles = { + border: { + '': '#purple.15', + pressed: '#purple.3', + focused: '#purple-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#purple.10', + hovered: '#purple.16', + pressed: '#purple-text.10', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#purple', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DEFAULT_OUTLINE_STYLES: Styles = { + border: { + '': '#dark.12', + focused: '#purple-text', + '[disabled] | disabled': '#border', + ...(VALIDATION_STYLES.border as Record), + }, + fill: { + '': '#dark.0', + hovered: '#dark.03', + 'pressed | (selected & !hovered)': '#dark.06', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#dark-02', + hovered: '#dark-02', + 'pressed | (selected & !hovered)': '#dark', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DEFAULT_NEUTRAL_STYLES: Styles = { + border: { + '': '#clear', + focused: '#purple-text', + ...(VALIDATION_STYLES.border as Record), + }, + fill: { + '': '#dark.0', + hovered: '#dark.03', + selected: '#dark.09', + 'selected & hovered': '#dark.12', + pressed: '#dark.09', + '[disabled] | disabled': '#clear', + }, + color: { + '': '#dark-02', + hovered: '#dark-02', + pressed: '#dark', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DEFAULT_CLEAR_STYLES: Styles = { + border: { + '': '#clear', + pressed: '#purple-text.10', + focused: '#purple-text', + ...(VALIDATION_STYLES.border as Record), + }, + fill: { + '': '#purple.0', + hovered: '#purple.16', + 'pressed | (selected & !hovered)': '#purple.10', + }, + color: { + '': '#purple-text', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DEFAULT_LINK_STYLES: Styles = { + outline: { + '': '0 #purple-text.0', + focused: '1bw #purple-text', + }, + border: '0', + fill: { + '': '#clear', + }, + color: { + '': '#purple-text', + 'hovered & !pressed': '#purple', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DEFAULT_ITEM_STYLES: Styles = { + border: '#clear', + fill: { + '': '#dark.0', + 'hovered | focused': '#dark.03', + selected: '#dark.09', + 'selected & (hovered | focused)': '#dark.12', + pressed: '#dark.09', + '[disabled] | disabled': '#clear', + }, + color: { + '': '#dark-02', + hovered: '#dark-02', + pressed: '#dark', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +// ---------- DANGER THEME ---------- +export const DANGER_PRIMARY_STYLES: Styles = { + outline: { + '': '0 #danger-text.0', + focused: '1bw #danger-text', + }, + border: { + '': '#clear', + 'pressed | focused': '#danger-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#danger', + 'hovered & !pressed': '#danger-text', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#white', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DANGER_SECONDARY_STYLES: Styles = { + border: { + '': '#danger.15', + pressed: '#danger.3', + focused: '#danger-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#danger.05', + 'hovered & !pressed': '#danger.1', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#danger', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DANGER_OUTLINE_STYLES: Styles = { + border: { + '': '#danger.15', + pressed: '#danger.3', + focused: '#danger-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#danger.0', + hovered: '#danger.1', + 'pressed | (selected & !hovered)': '#danger.05', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#danger-text', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DANGER_NEUTRAL_STYLES: Styles = { + border: { + '': '#clear', + focused: '#danger-text', + }, + fill: { + '': '#dark.0', + hovered: '#dark.04', + 'pressed | (selected & !hovered)': '#dark.05', + }, + color: { + '': '#dark-02', + 'pressed | (selected & !hovered)': '#danger-text', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DANGER_CLEAR_STYLES: Styles = { + border: { + '': '#clear', + pressed: '#danger.05', + focused: '#danger-text', + }, + fill: { + '': '#danger-text.0', + hovered: '#danger-text.03', + selected: '#danger-text.09', + 'selected & hovered': '#danger-text.12', + pressed: '#danger-text.09', + '[disabled] | disabled': '#clear', + }, + color: { + '': '#danger-text', + hovered: '#danger-text', + pressed: '#danger-text', + '[disabled] | disabled': '#danger-text.4', + }, +} as const; + +export const DANGER_LINK_STYLES: Styles = { + outline: { + '': '0 #danger-text.0', + focused: '1bw #danger-text', + }, + border: 0, + fill: { + '': '#clear', + }, + color: { + '': '#danger-text', + 'hovered & !pressed': '#danger', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const DANGER_ITEM_STYLES: Styles = { + border: '#clear', + fill: { + '': '#danger-text.0', + 'hovered | focused': '#danger-text.03', + selected: '#danger-text.09', + 'selected & (hovered | focused)': '#danger-text.12', + pressed: '#danger-text.09', + '[disabled] | disabled': '#clear', + }, + color: { + '': '#danger-text', + hovered: '#danger-text', + pressed: '#danger-text', + '[disabled] | disabled': '#danger-text.4', + }, +} as const; + +// ---------- SUCCESS THEME ---------- +export const SUCCESS_PRIMARY_STYLES: Styles = { + outline: { + '': '0 #success-text.0', + focused: '1bw #success-text', + }, + border: { + '': '#clear', + 'pressed | focused': '#success-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#success', + 'hovered & !pressed': '#success-text', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#white', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const SUCCESS_SECONDARY_STYLES: Styles = { + border: { + '': '#success.15', + pressed: '#success.3', + focused: '#success-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#success.05', + 'hovered & !pressed': '#success.1', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#success', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const SUCCESS_OUTLINE_STYLES: Styles = { + border: { + '': '#success.15', + pressed: '#success.3', + focused: '#success-text', + '[disabled] | disabled': '#border', + }, + fill: { + '': '#success.0', + hovered: '#success.1', + 'pressed | (selected & !hovered)': '#success.05', + '[disabled] | disabled': '#dark.04', + }, + color: { + '': '#success-text', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const SUCCESS_NEUTRAL_STYLES: Styles = { + border: { + '': '#clear', + focused: '#success-text', + }, + fill: { + '': '#dark.0', + hovered: '#dark.04', + 'pressed | (selected & !hovered)': '#dark.05', + }, + color: { + '': '#dark-02', + 'pressed | (selected & !hovered)': '#success-text', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const SUCCESS_CLEAR_STYLES: Styles = { + border: { + '': '#clear', + pressed: '#success.05', + focused: '#success-text', + }, + fill: { + '': '#success-text.0', + hovered: '#success-text.03', + selected: '#success-text.09', + 'selected & hovered': '#success-text.12', + pressed: '#success-text.09', + '[disabled] | disabled': '#clear', + }, + color: { + '': '#success-text', + hovered: '#success-text', + pressed: '#success-text', + '[disabled] | disabled': '#success-text.4', + }, +} as const; + +export const SUCCESS_LINK_STYLES: Styles = { + outline: { + '': '0 #success-text.0', + focused: '1bw #success-text', + }, + border: '0', + fill: { + '': '#clear', + }, + color: { + '': '#success-text', + 'hovered & !pressed': '#success', + '[disabled] | disabled': '#dark-04', + }, +} as const; + +export const SUCCESS_ITEM_STYLES: Styles = { + border: '#clear', + fill: { + '': '#success-text.0', + 'hovered | focused': '#success-text.03', + selected: '#success-text.09', + 'selected & (hovered | focused)': '#success-text.12', + pressed: '#success-text.09', + '[disabled] | disabled': '#clear', + }, + color: { + '': '#success-text', + hovered: '#success-text', + pressed: '#success-text', + '[disabled] | disabled': '#success-text.4', + }, +} as const; + +// ---------- SPECIAL THEME ---------- +export const SPECIAL_PRIMARY_STYLES: Styles = { + outline: { + '': '0 #white.0', + focused: '1bw #white', + }, + border: { + '': '#clear', + pressed: '#purple-03', + '[disabled] | disabled': '#white.3', + }, + fill: { + '': '#purple', + 'hovered & !pressed': '#purple-text', + '[disabled] | disabled': '#white.12', + }, + color: { + '': '#white', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export const SPECIAL_SECONDARY_STYLES: Styles = { + border: { + '': '#white.3', + pressed: '#white.4', + focused: '#white', + }, + fill: { + '': '#white.12', + 'hovered & !pressed': '#white.18', + '[disabled] | disabled': '#white.12', + }, + color: { + '': '#white', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export const SPECIAL_OUTLINE_STYLES: Styles = { + border: { + '': '#white.3', + pressed: '#white.12', + focused: '#white', + ...(VALIDATION_STYLES.border as Record), + }, + fill: { + '': '#white.0', + hovered: '#white.18', + 'pressed | (selected & !hovered)': '#white.12', + '[disabled] | disabled': '#white.12', + }, + color: { + '': '#white', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export const SPECIAL_NEUTRAL_STYLES: Styles = { + border: { + '': '#clear', + focused: '#white', + ...(VALIDATION_STYLES.border as Record), + }, + fill: { + '': '#white.0', + hovered: '#white.12', + 'pressed | (selected & !hovered)': '#white.18', + }, + color: { + '': '#white', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export const SPECIAL_CLEAR_STYLES: Styles = { + outline: { + focused: '1bw #white', + }, + border: { + '': '#clear', + 'pressed | focused': '#white', + '[disabled] | disabled': '#white.3', + ...(VALIDATION_STYLES.border as Record), + }, + fill: { + '': '#white', + 'hovered & !pressed': '#white.94', + '[disabled] | disabled': '#white.12', + }, + color: { + '': '#purple-text', + hovered: '#purple', + 'pressed & hovered': '#purple-text', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export const SPECIAL_LINK_STYLES: Styles = { + outline: { + '': '0 #white.0', + focused: '1bw #white', + }, + border: '0', + fill: { + '': '#clear', + }, + color: { + '': '#white', + 'hovered & !pressed': '#white.9', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export const SPECIAL_ITEM_STYLES: Styles = { + border: '#clear', + fill: { + '': '#white.0', + hovered: '#white.12', + 'pressed | (selected & !hovered)': '#white.18', + }, + color: { + '': '#white', + '[disabled] | disabled': '#white.4', + }, +} as const; + +export type ItemVariant = + | 'default.primary' + | 'default.secondary' + | 'default.outline' + | 'default.neutral' + | 'default.clear' + | 'default.link' + | 'default.item' + | 'danger.primary' + | 'danger.secondary' + | 'danger.outline' + | 'danger.neutral' + | 'danger.clear' + | 'danger.link' + | 'danger.item' + | 'success.primary' + | 'success.secondary' + | 'success.outline' + | 'success.neutral' + | 'success.clear' + | 'success.link' + | 'success.item' + | 'special.primary' + | 'special.secondary' + | 'special.outline' + | 'special.neutral' + | 'special.clear' + | 'special.link' + | 'special.item'; diff --git a/src/data/themes.ts b/src/data/themes.ts index e8e638c19..ccf036bb8 100644 --- a/src/data/themes.ts +++ b/src/data/themes.ts @@ -2,7 +2,7 @@ export default { special: { fill: '#purple', color: '#white', - border: '#purple', + border: '#white', }, success: { fill: '#success-bg', diff --git a/src/icons/Icons.stories.tsx b/src/icons/Icons.stories.tsx index b2bfa268a..5bb52ef6b 100644 --- a/src/icons/Icons.stories.tsx +++ b/src/icons/Icons.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { Text } from '../components/content/Text'; import { Title } from '../components/content/Title'; diff --git a/src/index.ts b/src/index.ts index 66bc126fe..b9a43b6c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,13 +4,16 @@ import { CubeTitleProps, Title } from './components/content/Title'; import './version'; -export { Item, Section } from 'react-stately'; +export { Section } from 'react-stately'; export * from '@internationalized/date'; // generic components +export { Item } from './components/Item'; export { Block } from './components/Block'; export type { CubeBlockProps } from './components/Block'; +export { ItemBase } from './components/content/ItemBase/ItemBase'; +export type { CubeItemBaseProps } from './components/content/ItemBase/ItemBase'; export { ActiveZone } from './components/content/ActiveZone/ActiveZone'; export type { CubeActiveZoneProps } from './components/content/ActiveZone/ActiveZone'; export * from './components/content/CopySnippet'; diff --git a/src/stories/BaseProperties.docs.mdx b/src/stories/BaseProperties.docs.mdx index eb8210cb2..3728e5fe7 100644 --- a/src/stories/BaseProperties.docs.mdx +++ b/src/stories/BaseProperties.docs.mdx @@ -1,4 +1,4 @@ -import { Meta } from '@storybook/blocks'; +import { Meta } from '@storybook/addon-docs/blocks'; diff --git a/src/stories/CreateComponent.docs.mdx b/src/stories/CreateComponent.docs.mdx index 248fb786e..b6404dee1 100644 --- a/src/stories/CreateComponent.docs.mdx +++ b/src/stories/CreateComponent.docs.mdx @@ -1,4 +1,4 @@ -import { Meta } from '@storybook/blocks'; +import { Meta } from '@storybook/addon-docs/blocks'; diff --git a/src/stories/Introduction.docs.mdx b/src/stories/Introduction.docs.mdx index d4f461ff2..37a93921f 100644 --- a/src/stories/Introduction.docs.mdx +++ b/src/stories/Introduction.docs.mdx @@ -1,4 +1,4 @@ -import { Meta } from '@storybook/blocks'; +import { Meta } from '@storybook/addon-docs/blocks'; import Code from './assets/code-brackets.svg'; import Colors from './assets/colors.svg'; import Comments from './assets/comments.svg'; diff --git a/src/stories/Layout.docs.mdx b/src/stories/Layout.docs.mdx index ac1cb5f3a..4ea098ca1 100644 --- a/src/stories/Layout.docs.mdx +++ b/src/stories/Layout.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story } from '@storybook/blocks'; +import { Meta, Canvas, Story } from '@storybook/addon-docs/blocks'; import { Button, Root, diff --git a/src/stories/Result.docs.mdx b/src/stories/Result.docs.mdx index a089ce415..bd51fb2fc 100644 --- a/src/stories/Result.docs.mdx +++ b/src/stories/Result.docs.mdx @@ -1,5 +1,5 @@ -import { Meta } from '@storybook/blocks'; -import { Story, Canvas, Controls } from '@storybook/blocks'; +import { Meta } from '@storybook/addon-docs/blocks'; +import { Story, Canvas, Controls } from '@storybook/addon-docs/blocks'; import * as ResultStories from './Result.stories.tsx'; import { baseProps } from './lists/baseProps'; diff --git a/src/stories/Result.stories.tsx b/src/stories/Result.stories.tsx index 3f39ad732..7b4c8577d 100644 --- a/src/stories/Result.stories.tsx +++ b/src/stories/Result.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { IconBulb, IconLock } from '@tabler/icons-react'; import { diff --git a/src/stories/Styles.stories.tsx b/src/stories/Styles.stories.tsx index 670f734b4..72abf9df0 100644 --- a/src/stories/Styles.stories.tsx +++ b/src/stories/Styles.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { Block, CubeBlockProps } from '../index'; diff --git a/src/stories/Tasty.docs.mdx b/src/stories/Tasty.docs.mdx index 5a2f0309a..5a020ec5f 100644 --- a/src/stories/Tasty.docs.mdx +++ b/src/stories/Tasty.docs.mdx @@ -1,4 +1,4 @@ -import { Meta } from '@storybook/blocks'; +import { Meta } from '@storybook/addon-docs/blocks'; diff --git a/src/stories/Text.stories.tsx b/src/stories/Text.stories.tsx index 06c293ce2..49b9b75b6 100644 --- a/src/stories/Text.stories.tsx +++ b/src/stories/Text.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn } from '@storybook/react'; +import { StoryFn } from '@storybook/react-vite'; import { CubeTextProps, Text } from '../index'; diff --git a/src/stories/Typography.docs.mdx b/src/stories/Typography.docs.mdx index 85da21d79..5a0f18039 100644 --- a/src/stories/Typography.docs.mdx +++ b/src/stories/Typography.docs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Story } from '@storybook/blocks'; +import { Meta, Canvas, Story } from '@storybook/addon-docs/blocks'; import { Paragraph, Title, diff --git a/tsconfig.es.json b/tsconfig.es.json index 002450785..93230cde8 100644 --- a/tsconfig.es.json +++ b/tsconfig.es.json @@ -9,6 +9,7 @@ "src/**/__mocks__/**/*", "src/**/*.stories.tsx", "src/**/*.stories.ts", + "src/**/storybook/**/*", "src/test/**/*", "src/**/*.test.tsx", "src/**/*.test.ts",