diff --git a/package-lock.json b/package-lock.json index b80852de4a..da216eb591 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "smarttrader", "version": "0.0.0", "dependencies": { + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "lint-staged": "^15.0.1", @@ -2102,7 +2105,6 @@ "version": "7.23.1", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -3572,7 +3574,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" } @@ -3632,7 +3633,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -3650,7 +3650,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -3664,6 +3663,117 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", @@ -3714,7 +3824,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -3754,11 +3863,18 @@ } } }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, "node_modules/@radix-ui/react-id": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-layout-effect": "1.0.1" @@ -3830,11 +3946,34 @@ } } }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-primitive": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.2" @@ -3958,7 +4097,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" @@ -4063,7 +4201,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -4081,7 +4218,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" @@ -4100,7 +4236,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" @@ -4119,7 +4254,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -6516,7 +6650,7 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "devOptional": true }, "node_modules/@types/qs": { "version": "6.9.8", @@ -6534,7 +6668,7 @@ "version": "18.2.21", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -6545,7 +6679,7 @@ "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -6554,7 +6688,7 @@ "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "devOptional": true }, "node_modules/@types/semver": { "version": "7.5.3", @@ -7417,7 +7551,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", - "dev": true, "dependencies": { "tslib": "^2.0.0" }, @@ -8839,7 +8972,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "devOptional": true }, "node_modules/dargs": { "version": "7.0.0", @@ -9113,8 +9246,7 @@ "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "dev": true + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/detect-package-manager": { "version": "2.0.1", @@ -11803,7 +11935,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "dev": true, "engines": { "node": ">=6" } @@ -12435,7 +12566,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "dependencies": { "loose-envify": "^1.0.0" } @@ -16436,7 +16566,6 @@ "version": "2.5.5", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dev": true, "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", @@ -16461,7 +16590,6 @@ "version": "2.3.4", "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", - "dev": true, "dependencies": { "react-style-singleton": "^2.2.1", "tslib": "^2.0.0" @@ -16483,7 +16611,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "dev": true, "dependencies": { "get-nonce": "^1.0.0", "invariant": "^2.2.4", @@ -16720,8 +16847,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -18447,8 +18573,7 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -18831,7 +18956,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", - "dev": true, "dependencies": { "tslib": "^2.0.0" }, @@ -18865,7 +18989,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dev": true, "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" diff --git a/package.json b/package.json index 5e8aaccef7..478358ec78 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "lint-staged": "^15.0.1", diff --git a/src/components/ui/button/__test__/button.test.tsx b/src/components/ui/button/__test__/button.test.tsx new file mode 100644 index 0000000000..ecf0e86889 --- /dev/null +++ b/src/components/ui/button/__test__/button.test.tsx @@ -0,0 +1,144 @@ +import { describe, expect, it, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Button } from '..'; + +describe('Button component', () => { + it('should render the button with children', async () => { + render(); + const btnElement = screen.getByText('Button'); + expect(btnElement).toBeInTheDocument(); + }); + + it('should call the onClick handler when button is clicked', async () => { + const onClickMock = vi.fn(); + render(); + const btnElement = screen.getByText('Button'); + await userEvent.click(btnElement); + expect(onClickMock).toHaveBeenCalled(); + }); + + it('should apply the default styling when no props are passed', () => { + render(); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('bg-primary', 'text-white', 'text-base'); + }); + + it("should apply contained secondary styling when color prop is 'secondary' and variant prop is 'contained'", () => { + render( + + ); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('bg-secondary'); + }); + + it("should apply outlined primary styling when color prop is 'primary' and variant prop is 'outlined'", () => { + render( + + ); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('bg-transparent', 'border-primary'); + }); + + it("should apply outlined secondary styling when color prop is 'secondary' and variant prop is 'outlined'", () => { + render( + + ); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('bg-transparent', 'border-secondary'); + }); + + it("should apply outlined secondary styling when color prop is 'secondary' and variant prop is 'outlined'", () => { + render( + + ); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('bg-transparent', 'border-secondary'); + }); + + it("should apply small size style when size prop is 'sm'", () => { + render(); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('px-3'); + }); + + it("should apply medium size style when size prop is 'md'", () => { + render(); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('text-[0.875rem]'); + }); + + it("should have few same classes for size 'default' and 'md'", () => { + render( +
+ + +
+ ); + const defaultBtnElement = screen.getByText('Default Button'); + const mediumBtnElement = screen.getByText('Medium Button'); + + const commonClass = 'min-h-[2rem]'; + expect(defaultBtnElement).toHaveClass(commonClass); + expect(mediumBtnElement).toHaveClass(commonClass); + }); + + it('should have same padding block classe for all size', () => { + render( +
+ + + +
+ ); + const defaultBtnElement = screen.getByText('Default Button'); + const mediumBtnElement = screen.getByText('Medium Button'); + const smallBtnElement = screen.getByText('Small Button'); + + const commonClass = 'py-1'; + expect(defaultBtnElement).toHaveClass(commonClass); + expect(mediumBtnElement).toHaveClass(commonClass); + expect(smallBtnElement).toHaveClass(commonClass); + }); + + it('should apply the fullwidth styling when fullwidth prop is true', () => { + render(); + const btnElement = screen.getByText('Full Width Button'); + expect(btnElement).toHaveClass('w-full'); + }); + + it('should apply custom class and styles correctly', () => { + render( + + ); + const btnElement = screen.getByText('Button'); + expect(btnElement).toHaveClass('custom-class'); + expect(btnElement).toHaveStyle({ fontSize: '20px' }); + }); + + it('should render a disabled button when disabled prop is true', () => { + render(); + const btnElement = screen.getByText('Click me'); + expect(btnElement).toBeDisabled(); + }); + + it('should render the correct tag when asChild prop is true', () => { + render( + + ); + const btnElement = screen.getByRole('link'); + expect(btnElement).toBeInTheDocument(); + }); +}); diff --git a/src/components/ui/button/index.tsx b/src/components/ui/button/index.tsx new file mode 100644 index 0000000000..d1e34e2b1e --- /dev/null +++ b/src/components/ui/button/index.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { type VariantProps } from 'class-variance-authority'; +import { cn } from 'Utils/cn'; +import { buttonVariants } from './variants'; + +interface ButtonProps + extends Omit, 'color'>, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, color, fullwidth, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : 'button'; + return ( + + ); + } +); +Button.displayName = 'Button'; + +export { Button }; diff --git a/src/components/ui/button/variants.ts b/src/components/ui/button/variants.ts new file mode 100644 index 0000000000..18f66f1124 --- /dev/null +++ b/src/components/ui/button/variants.ts @@ -0,0 +1,62 @@ +import { cva } from 'class-variance-authority'; + +export const buttonVariants = cva( + 'inline-flex items-center justify-center rounded font-bold border border-solid transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50', + { + variants: { + variant: { + contained: 'text-white', + outlined: 'bg-transparent text-prominent', + }, + color: { + primary: 'border-primary hover:border-primary-hover focus:border-primary-hover', + secondary: 'border-secondary', + success: 'border-success', + }, + size: { + default: 'text-base', + sm: 'text-[0.75rem] px-3', + md: 'text-[0.875rem]', + }, + fullwidth: { + true: 'flex w-full', + }, + }, + compoundVariants: [ + { + variant: 'contained', + color: 'primary', + className: 'bg-primary hover:bg-primary-hover focus:bg-primary-hover', + }, + { + variant: 'contained', + color: 'secondary', + className: 'bg-secondary', + }, + { + variant: 'outlined', + color: 'primary', + className: 'hover:bg-primary-hover focus:bg-primary-hover', + }, + { + variant: 'outlined', + color: 'secondary', + className: 'hover:bg-secondary/[0.08] focus:bg-secondary/[0.08]', + }, + { + size: ['default', 'md'], + className: 'px-5 min-h-[2rem] min-w-[3.5rem]', + }, + { + size: ['default', 'md', 'sm'], + className: 'py-1', + }, + ], + defaultVariants: { + variant: 'contained', + color: 'primary', + size: 'default', + fullwidth: false, + }, + } +);