feat(toast): custom toast component (#9898)

This commit is contained in:
Mislav Lukach 2025-07-25 14:24:17 +02:00 committed by GitHub
parent 06ad5e30c9
commit 9fcf900a23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 171 additions and 86 deletions

View File

@ -11,9 +11,7 @@
"react-bootstrap-icons": "^1.11.6",
"react-select": "^5.10.2",
"sonner": "^2.0.6",
"tailwind-merge": "^3.3.1",
"tailwind-scrollbar": "^4.0.2",
"tailwindcss": "^4.1.10",
},
"devDependencies": {
"@chromatic-com/storybook": "^4.0.0",
@ -35,8 +33,10 @@
"vitest": "^3.2.4",
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0",
"react": ">=19.1.0",
"react-dom": ">=19.1.0",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.10",
},
},
},
@ -69,7 +69,7 @@
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
"@babel/helpers": ["@babel/helpers@7.27.6", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.6" } }, "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug=="],
"@babel/helpers": ["@babel/helpers@7.28.2", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.2" } }, "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw=="],
"@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="],
@ -77,13 +77,13 @@
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
"@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="],
"@babel/runtime": ["@babel/runtime@7.28.2", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="],
"@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
"@babel/traverse": ["@babel/traverse@7.28.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/types": "^7.28.0", "debug": "^4.3.1" } }, "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg=="],
"@babel/types": ["@babel/types@7.28.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ=="],
"@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
@ -191,9 +191,9 @@
"@mdx-js/react": ["@mdx-js/react@3.1.0", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ=="],
"@microsoft/api-extractor": ["@microsoft/api-extractor@7.52.8", "", { "dependencies": { "@microsoft/api-extractor-model": "7.30.6", "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.1", "@rushstack/rig-package": "0.5.3", "@rushstack/terminal": "0.15.3", "@rushstack/ts-command-line": "5.0.1", "lodash": "~4.17.15", "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-cszYIcjiNscDoMB1CIKZ3My61+JOhpERGlGr54i6bocvGLrcL/wo9o+RNXMBrb7XgLtKaizZWUpqRduQuHQLdg=="],
"@microsoft/api-extractor": ["@microsoft/api-extractor@7.52.9", "", { "dependencies": { "@microsoft/api-extractor-model": "7.30.7", "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.14.0", "@rushstack/rig-package": "0.5.3", "@rushstack/terminal": "0.15.4", "@rushstack/ts-command-line": "5.0.2", "lodash": "~4.17.15", "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-313nyhc6DSSMVKD43jZK6Yp5XbliGw5vjN7QOw1FHzR1V6DQ67k4dzkd3BSxMtWcm+cEs1Ux8rmDqots6EABFA=="],
"@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.30.6", "", { "dependencies": { "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.1" } }, "sha512-znmFn69wf/AIrwHya3fxX6uB5etSIn6vg4Q4RB/tb5VDDs1rqREc+AvMC/p19MUN13CZ7+V/8pkYPTj7q8tftg=="],
"@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.30.7", "", { "dependencies": { "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.14.0" } }, "sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ=="],
"@microsoft/tsdoc": ["@microsoft/tsdoc@0.15.1", "", {}, "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw=="],
@ -249,13 +249,13 @@
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.45.1", "", { "os": "win32", "cpu": "x64" }, "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA=="],
"@rushstack/node-core-library": ["@rushstack/node-core-library@5.13.1", "", { "dependencies": { "ajv": "~8.13.0", "ajv-draft-04": "~1.0.0", "ajv-formats": "~3.0.1", "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-5yXhzPFGEkVc9Fu92wsNJ9jlvdwz4RNb2bMso+/+TH0nMm1jDDDsOIf4l8GAkPxGuwPw5DH24RliWVfSPhlW/Q=="],
"@rushstack/node-core-library": ["@rushstack/node-core-library@5.14.0", "", { "dependencies": { "ajv": "~8.13.0", "ajv-draft-04": "~1.0.0", "ajv-formats": "~3.0.1", "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg=="],
"@rushstack/rig-package": ["@rushstack/rig-package@0.5.3", "", { "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow=="],
"@rushstack/terminal": ["@rushstack/terminal@0.15.3", "", { "dependencies": { "@rushstack/node-core-library": "5.13.1", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-DGJ0B2Vm69468kZCJkPj3AH5nN+nR9SPmC0rFHtzsS4lBQ7/dgOwtwVxYP7W9JPDMuRBkJ4KHmWKr036eJsj9g=="],
"@rushstack/terminal": ["@rushstack/terminal@0.15.4", "", { "dependencies": { "@rushstack/node-core-library": "5.14.0", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-OQSThV0itlwVNHV6thoXiAYZlQh4Fgvie2CzxFABsbO2MWQsI4zOh3LRNigYSTrmS+ba2j0B3EObakPzf/x6Zg=="],
"@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.0.1", "", { "dependencies": { "@rushstack/terminal": "0.15.3", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-bsbUucn41UXrQK7wgM8CNM/jagBytEyJqXw/umtI8d68vFm1Jwxh1OtLrlW7uGZgjCWiiPH6ooUNa1aVsuVr3Q=="],
"@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.0.2", "", { "dependencies": { "@rushstack/terminal": "0.15.4", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ=="],
"@storybook/addon-a11y": ["@storybook/addon-a11y@9.0.18", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^9.0.18" } }, "sha512-msbsTI9TmePQ5ElVclLi7ns5WaAntouJFaj9ElNugFWME21k68RiyXnioDjDfEoi/+y8tthQNNqjsHoX/Ev0Og=="],
@ -505,7 +505,7 @@
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.190", "", {}, "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw=="],
"electron-to-chromium": ["electron-to-chromium@1.5.191", "", {}, "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
@ -661,7 +661,7 @@
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
"jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
"jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="],
@ -713,7 +713,7 @@
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="],
"loupe": ["loupe@3.2.0", "", {}, "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw=="],
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
@ -973,7 +973,7 @@
"use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA=="],
"vite": ["vite@7.0.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw=="],
"vite": ["vite@7.0.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg=="],
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
@ -1041,8 +1041,6 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@testing-library/jest-dom/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
"@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="],

View File

@ -40,7 +40,8 @@ export const Button = ({
"flex flex-row items-center gap-x-8",
hasIcons ? " justify-between" : " justify-center",
"group enabled:cursor-pointer focus:outline-0",
buttonClassNames.button
buttonClassNames.button,
className
)}
>
{cloneIcon(start, {

View File

@ -16,7 +16,7 @@ export default meta;
type Story = StoryObj<typeof meta>;
const CheckboxComponent = (props: CheckboxProps) => {
const [checked, setChecked] = useState(false);
const [checked, setChecked] = useState(props.checked);
return (
<Checkbox
{...props}
@ -28,6 +28,7 @@ const CheckboxComponent = (props: CheckboxProps) => {
export const Enabled: Story = {
args: {
checked: false,
label:
"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
},
@ -37,6 +38,7 @@ export const Enabled: Story = {
export const Disabled: Story = {
args: {
disabled: true,
checked: false,
label:
"Lorem Ipsum is simply dummy text of the printing and typesetting industry",
},

View File

@ -1,13 +1,17 @@
import { useId } from "react";
import type { HTMLProps } from "../../shared/types";
import type { BaseProps, HTMLProps } from "../../shared/types";
import { cn } from "../../shared/utils/cn";
import { Typography } from "../typography/Typography";
import { Icon } from "../icon/Icon";
export type CheckboxProps = HTMLProps<"input"> & {
export type CheckboxProps = Omit<
HTMLProps<"input">,
"checked" | "defaultChecked"
> & {
label: React.ReactNode;
labelClassName?: string;
};
checked: boolean;
} & BaseProps;
export const Checkbox = ({
className,
@ -17,6 +21,7 @@ export const Checkbox = ({
disabled,
checked,
onChange,
testId,
...props
}: CheckboxProps) => {
const generatedId = useId();
@ -26,7 +31,8 @@ export const Checkbox = ({
htmlFor={id}
className={cn(
"flex items-center gap-2 cursor-pointer",
disabled && "cursor-not-allowed"
disabled && "cursor-not-allowed",
className
)}
>
<input
@ -36,6 +42,7 @@ export const Checkbox = ({
onChange={onChange}
disabled={disabled}
className="sr-only peer"
data-testid={testId}
{...props}
/>
<div

View File

@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import { Button } from "../button/Button";
import { toasterMessages } from "./Toast";
import { ToastManager } from "./ToastManager";
import { Typography } from "../typography/Typography";
const meta = {
title: "Components/Toast",
@ -21,6 +22,7 @@ const toastComponents: Record<ToastType, (text?: string) => void> = {
success: toasterMessages.success,
info: toasterMessages.info,
warning: toasterMessages.warning,
custom: (text) => toasterMessages.custom(() => <div>{text}</div>),
};
const ToastComponent = () => {
@ -39,12 +41,36 @@ const ToastComponent = () => {
<Button onClick={() => toastComponents["warning"]("Lorem Ipsum")}>
Show warning toast
</Button>
<Button onClick={() => toastComponents["custom"]("Lorem Ipsum")}>
Show custom toast
</Button>
</div>
</ToastManager>
);
};
const CustomToastComponent = () => {
const notify = () => {
toasterMessages.custom((props) => (
<Typography.Text fontSize="xs" className="text-white">
Custom toast !
</Typography.Text>
));
};
return (
<>
<ToastManager>
<Button onClick={notify}>Notify</Button>
</ToastManager>
</>
);
};
export const Main: Story = {
args: {},
render: () => <ToastComponent />,
};
export const Custom: Story = {
args: {},
render: CustomToastComponent,
};

View File

@ -1,16 +1,39 @@
import { toast as sonnerToast } from "sonner";
import { toast as sonnerToast, type ExternalToast } from "sonner";
import { Icon, type IconProps } from "../icon/Icon";
import { cn } from "../../shared/utils/cn";
import { Typography } from "../typography/Typography";
import { toastStyles } from "./utils";
import type { JSX } from "react";
import { invariant } from "../../shared/utils/invariant";
type IBaseToastProps = {
id: string | number;
type RenderContentProps = {
onDismiss: () => void;
};
type WithRenderContent = {
renderContent: (props: RenderContentProps) => JSX.Element;
text?: never;
icon?: never;
};
type WithTextAndIcon = {
text: string;
icon: IconProps["icon"];
iconClassName: string;
renderContent?: never;
};
type IBaseToastProps = (WithRenderContent | WithTextAndIcon) & {
id: string | number;
};
const BaseToast = (props: IBaseToastProps) => {
invariant(
!!props.renderContent || !!props.text,
"Either define renderContent or text. Both cannot be defined."
);
const onDismiss = () => sonnerToast.dismiss(props.id);
return (
<div
className={cn(
@ -20,66 +43,90 @@ const BaseToast = (props: IBaseToastProps) => {
"flex flex-row items-center justify-between gap-x-4"
)}
>
<Icon
icon={props.icon}
className={cn("w-6 h-6 flex-shrink-0", props.iconClassName)}
/>
<Typography.Text fontSize="xs" className="text-white">
{props.text}
</Typography.Text>
<button
onClick={() => sonnerToast.dismiss(props.id)}
className="cursor-pointer"
>
<Icon icon="X" className={cn("w-6 h-6 flex-shrink-0 text-white")} />
</button>
{props.renderContent ? (
props.renderContent({ onDismiss })
) : (
<>
<Icon
icon={props.icon}
className={cn("w-6 h-6 flex-shrink-0", props.iconClassName)}
/>
<Typography.Text fontSize="xs" className="text-white">
{props.text}
</Typography.Text>
<button onClick={onDismiss} className="cursor-pointer">
<Icon icon="X" className={cn("w-6 h-6 flex-shrink-0 text-white")} />
</button>
</>
)}
</div>
);
};
export const toasterMessages = {
error: (text?: string) => {
error: (text?: string, props?: ExternalToast) => {
const styles = toastStyles["error"];
sonnerToast.custom((id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
));
sonnerToast.custom(
(id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
),
props
);
},
success: (text?: string) => {
success: (text?: string, props?: ExternalToast) => {
const styles = toastStyles["success"];
sonnerToast.custom((id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
));
sonnerToast.custom(
(id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
),
props
);
},
info: (text?: string) => {
info: (text?: string, props?: ExternalToast) => {
const styles = toastStyles["info"];
sonnerToast.custom((id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
));
sonnerToast.custom(
(id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
),
props
);
},
warning: (text?: string) => {
warning: (text?: string, props?: ExternalToast) => {
const styles = toastStyles["warning"];
sonnerToast.custom((id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
));
sonnerToast.custom(
(id) => (
<BaseToast
id={id}
icon={styles.icon}
iconClassName={cn(styles.iconColor)}
text={text!}
/>
),
props
);
},
custom: (
renderContent: WithRenderContent["renderContent"],
props?: ExternalToast
) => {
sonnerToast.custom(
(id) => <BaseToast id={id} renderContent={renderContent} />,
props
);
},
};

View File

@ -1,5 +1,5 @@
import { useId } from "react";
import type { HTMLProps } from "../../shared/types";
import type { BaseProps, HTMLProps } from "../../shared/types";
import { cn } from "../../shared/utils/cn";
import { Typography } from "../typography/Typography";
import { invariant } from "../../shared/utils/invariant";
@ -11,7 +11,8 @@ type ToggleTextProps =
export type ToggleProps = HTMLProps<"input"> & {
label?: React.ReactNode;
labelClassName?: string;
} & ToggleTextProps;
} & ToggleTextProps &
BaseProps;
export const Toggle = ({
className,
@ -23,6 +24,7 @@ export const Toggle = ({
onChange,
onText,
offText,
testId,
...props
}: ToggleProps) => {
invariant(
@ -49,6 +51,7 @@ export const Toggle = ({
onChange={onChange}
disabled={disabled}
className="sr-only peer"
data-testid={testId}
{...props}
/>
<div
@ -95,7 +98,7 @@ export const Toggle = ({
<Typography.Text
fontSize="xxs"
fontWeight={500}
className={cn("mr-5", disabled && "opacity-50")}
className={cn("mr-3", disabled && "opacity-50")}
>
{checked ? onText : offText}
</Typography.Text>
@ -104,7 +107,7 @@ export const Toggle = ({
<Typography.Text
fontSize="xxs"
fontWeight={500}
className={cn(labelClassName, disabled && "opacity-50")}
className={cn("ml-2", disabled && "opacity-50", labelClassName)}
>
{label}
</Typography.Text>

View File

@ -14,7 +14,7 @@
"email": "stephan@all-hands.dev"
}
],
"version": "1.0.0-beta.6",
"version": "1.0.0-beta.7",
"description": "OpenHands UI Components",
"keywords": [
"openhands",
@ -71,8 +71,10 @@
"vitest": "^3.2.4"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
"react": ">=19.1.0",
"react-dom": ">=19.1.0",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.10"
},
"dependencies": {
"@floating-ui/react": "^0.27.12",
@ -82,9 +84,7 @@
"react-bootstrap-icons": "^1.11.6",
"react-select": "^5.10.2",
"sonner": "^2.0.6",
"tailwind-merge": "^3.3.1",
"tailwind-scrollbar": "^4.0.2",
"tailwindcss": "^4.1.10"
"tailwind-scrollbar": "^4.0.2"
},
"scripts": {
"dev": "storybook dev -p 6006",

View File

@ -1,6 +1,7 @@
export type BaseProps = {
className?: string;
style?: React.CSSProperties;
testId?: string;
};
export type HTMLProps<T extends React.ElementType> = Omit<