From b79884b5a4a97fe2e4ab7d34f24fa384b8482bdc Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 10 Aug 2025 19:34:57 +0300 Subject: [PATCH 1/9] adapt the tooling table to shad cn with new table element --- components/ui/table.tsx | 114 +++++++++++++++++ pages/tools/components/ToolingTable.tsx | 157 ++++++++++++------------ 2 files changed, 191 insertions(+), 80 deletions(-) create mode 100644 components/ui/table.tsx diff --git a/components/ui/table.tsx b/components/ui/table.tsx new file mode 100644 index 000000000..5513a5cdb --- /dev/null +++ b/components/ui/table.tsx @@ -0,0 +1,114 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Table({ className, ...props }: React.ComponentProps<"table">) { + return ( +
+ + + ) +} + +function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { + return ( + + ) +} + +function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { + return ( + + ) +} + +function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { + return ( + tr]:last:border-b-0", + className + )} + {...props} + /> + ) +} + +function TableRow({ className, ...props }: React.ComponentProps<"tr">) { + return ( + + ) +} + +function TableHead({ className, ...props }: React.ComponentProps<"th">) { + return ( +
[role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> + ) +} + +function TableCell({ className, ...props }: React.ComponentProps<"td">) { + return ( + [role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> + ) +} + +function TableCaption({ + className, + ...props +}: React.ComponentProps<"caption">) { + return ( +
+ ) +} + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/pages/tools/components/ToolingTable.tsx b/pages/tools/components/ToolingTable.tsx index 95bdc6ec7..2ae95f7d3 100644 --- a/pages/tools/components/ToolingTable.tsx +++ b/pages/tools/components/ToolingTable.tsx @@ -10,6 +10,14 @@ import { Headline2 } from '~/components/Headlines'; import InfoIcon from '~/public/icons/icons8-info.svg'; import OutLinkIcon from '~/public/icons/outlink.svg'; import { Button } from '~/components/ui/button'; +import { + Table, + TableBody, + TableCell as ShadcnTableCell, + TableHead, + TableHeader, + TableRow, +} from '~/components/ui/table'; import toTitleCase from '../lib/toTitleCase'; import type { GroupedTools, Transform } from '../hooks/useToolsTransform'; @@ -117,17 +125,18 @@ const ToolingTable = ({ {toTitleCase(group, '-')} )} -
+
{/* Desktop Table */} - - - +
+
+ + Name @@ -135,7 +144,7 @@ const ToolingTable = ({ {transform.groupBy !== 'toolingTypes' && ( Tooling Type @@ -143,15 +152,14 @@ const ToolingTable = ({ )} {transform.groupBy !== 'languages' && ( Languages )} Dialects @@ -160,7 +168,7 @@ const ToolingTable = ({ sortBy='license' transform={transform} setTransform={setTransform} - attributes={{ style: { flexBasis: '15%' } }} + attributes={{ className: 'w-1/6' }} > License @@ -169,83 +177,69 @@ const ToolingTable = ({ transform={transform} setTransform={setTransform} attributes={{ - className: 'text-center !px-0', - style: { flexBasis: '70px', flexShrink: 0, flexGrow: 0 }, + className: 'text-center px-1 w-20', }} > Bowtie - - - + + + {toolsByGroup[group].map((tool: JSONSchemaTool, index) => { const bowtieData = getBowtieData(tool); if (bowtieData) { tool.bowtie = bowtieData; } return ( - openModal(tool)} > segment.length > 25) ? 'break-all' : ''} gap-x-2 gap-y-1`, - style: { - flexBasis: '240px', - flexShrink: 1, - flexGrow: 0, - }, + className: 'break-words', title: 'See details', }} > - {tool.name} - {tool.status === 'obsolete' && ( - {tool.status} - )} +
+ segment.length > 25) ? 'break-all' : 'break-words'}> + {tool.name} + + {tool.status === 'obsolete' && ( + {tool.status} + )} +
{transform.groupBy !== 'toolingTypes' && ( - + {tool.toolingTypes ?.map((type) => toTitleCase(type, '-')) .join(', ')} )} {transform.groupBy !== 'languages' && ( - + {tool.languages?.join(', ')} )} - {tool.supportedDialects?.draft?.map((draft) => { - return {draft}; - })} +
+ {tool.supportedDialects?.draft?.map((draft) => { + return {draft}; + })} +
- + {tool.license} {bowtieReport && ( @@ -266,27 +260,29 @@ const ToolingTable = ({ )} -
+ ); })} - -
+ +
+
{/* Mobile Table */} - - - {toolsByGroup[group].map((tool: JSONSchemaTool, index) => { - const bowtieData = getBowtieData(tool); - if (bowtieData) { - tool.bowtie = bowtieData; - } - return ( - openModal(tool)} - > -
+
+ + + {toolsByGroup[group].map((tool: JSONSchemaTool, index) => { + const bowtieData = getBowtieData(tool); + if (bowtieData) { + tool.bowtie = bowtieData; + } + return ( + openModal(tool)} + > + {bowtieData && (
Bowtie: @@ -303,7 +299,7 @@ const ToolingTable = ({ )}
-
+
{tool.name} {tool.status === 'obsolete' && ( {tool.status} @@ -321,15 +317,16 @@ const ToolingTable = ({ {draft} ))}
-
+
License: {tool.license}
- - - ); - })} - -
+ + + ); + })} + +
+ ))} @@ -347,15 +344,15 @@ const TableColumnHeader = ({ attributes?: Record; }) => { return ( - {children} - + ); }; @@ -436,16 +433,16 @@ const TableCell = ({ attributes?: Record; }) => { return ( - {children} - + ); }; From 7a69703a0456c136f57c8d75bb46031a39ab0539 Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 10 Aug 2025 19:44:40 +0300 Subject: [PATCH 2/9] adapting toolingdetailed modal and istalling dialog element --- components/ui/dialog.tsx | 141 +++++++++++ package.json | 1 + pages/tools/components/ToolingDetailModal.tsx | 46 ++-- yarn.lock | 237 ++++++++++++++++++ 4 files changed, 396 insertions(+), 29 deletions(-) create mode 100644 components/ui/dialog.tsx diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx new file mode 100644 index 000000000..6cb123b38 --- /dev/null +++ b/components/ui/dialog.tsx @@ -0,0 +1,141 @@ +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { XIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Dialog({ + ...props +}: React.ComponentProps) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/package.json b/package.json index 8e6358e44..188cc57dc 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@docsearch/react": "3.9.0", "@radix-ui/react-checkbox": "^1.3.1", "@radix-ui/react-collapsible": "^1.1.11", + "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-radio-group": "^1.3.7", "@radix-ui/react-separator": "^1.1.7", diff --git a/pages/tools/components/ToolingDetailModal.tsx b/pages/tools/components/ToolingDetailModal.tsx index 8c91a50e1..44d2a1fed 100644 --- a/pages/tools/components/ToolingDetailModal.tsx +++ b/pages/tools/components/ToolingDetailModal.tsx @@ -2,6 +2,12 @@ import React, { useEffect, useState } from 'react'; import CancelIcon from '~/public/icons/cancel.svg'; import { Button } from '~/components/ui/button'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '~/components/ui/dialog'; import Badge from './ui/Badge'; import type { JSONSchemaTool } from '../JSONSchemaTool'; @@ -18,34 +24,16 @@ export default function ToolingDetailModal({ tool: JSONSchemaTool; onClose: () => void; }) { - useEffect(() => { - document.body.classList.add('no-scroll'); - return () => { - document.body.classList.remove('no-scroll'); - }; - }, []); - - useEffect(() => { - const clickEsc = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - onClose(); - } - }; - document.addEventListener('keydown', clickEsc); - return () => { - document.removeEventListener('keydown', clickEsc); - }; - }, [onClose]); - return ( -
-
-
onClose()}> +
-
-
+ + ); } diff --git a/yarn.lock b/yarn.lock index b3c9bc0ff..deabab9bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2436,6 +2436,38 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-dialog@npm:^1.1.14": + version: 1.1.14 + resolution: "@radix-ui/react-dialog@npm:1.1.14" + dependencies: + "@radix-ui/primitive": "npm:1.1.2" + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-context": "npm:1.1.2" + "@radix-ui/react-dismissable-layer": "npm:1.1.10" + "@radix-ui/react-focus-guards": "npm:1.1.2" + "@radix-ui/react-focus-scope": "npm:1.1.7" + "@radix-ui/react-id": "npm:1.1.1" + "@radix-ui/react-portal": "npm:1.1.9" + "@radix-ui/react-presence": "npm:1.1.4" + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-slot": "npm:1.2.3" + "@radix-ui/react-use-controllable-state": "npm:1.2.2" + aria-hidden: "npm:^1.2.4" + react-remove-scroll: "npm:^2.6.3" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/ab7bc783510ed8fccfe91020b214f4a571d5a1d46d398faa33f4c151bc9f586c47483b307e72b67687b06694c194b3aa80dd1de728460fa765db9f3057690ba3 + languageName: node + linkType: hard + "@radix-ui/react-direction@npm:1.1.1": version: 1.1.1 resolution: "@radix-ui/react-direction@npm:1.1.1" @@ -2449,6 +2481,63 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-dismissable-layer@npm:1.1.10": + version: 1.1.10 + resolution: "@radix-ui/react-dismissable-layer@npm:1.1.10" + dependencies: + "@radix-ui/primitive": "npm:1.1.2" + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-use-callback-ref": "npm:1.1.1" + "@radix-ui/react-use-escape-keydown": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/21a2d03689f5e06586135b6a735937ef14f2571fdf6044a3019bc3f9fa368a9400b5a9b631f43e8ad3682693449e369ffa7cc8642764246ce18ebe7359a45faf + languageName: node + linkType: hard + +"@radix-ui/react-focus-guards@npm:1.1.2": + version: 1.1.2 + resolution: "@radix-ui/react-focus-guards@npm:1.1.2" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/8d6fa55752b9b6e55d1eebb643178e38a824e8ba418eb29031b2979077a12c4e3922892de9f984dd326f77071a14960cd81e99a960beea07598b8c80da618dc5 + languageName: node + linkType: hard + +"@radix-ui/react-focus-scope@npm:1.1.7": + version: 1.1.7 + resolution: "@radix-ui/react-focus-scope@npm:1.1.7" + dependencies: + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-use-callback-ref": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/8a6071331bdeeb79b223463de75caf759b8ad19339cab838e537b8dbb2db236891a1f4df252445c854d375d43d9d315dfcce0a6b01553a2984ec372bb8f1300e + languageName: node + linkType: hard + "@radix-ui/react-id@npm:1.1.1": version: 1.1.1 resolution: "@radix-ui/react-id@npm:1.1.1" @@ -2483,6 +2572,26 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-portal@npm:1.1.9": + version: 1.1.9 + resolution: "@radix-ui/react-portal@npm:1.1.9" + dependencies: + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-use-layout-effect": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/45b432497c722720c72c493a29ef6085bc84b50eafe79d48b45c553121b63e94f9cdb77a3a74b9c49126f8feb3feee009fe400d48b7759d3552396356b192cd7 + languageName: node + linkType: hard + "@radix-ui/react-presence@npm:1.1.4": version: 1.1.4 resolution: "@radix-ui/react-presence@npm:1.1.4" @@ -2689,6 +2798,21 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-use-escape-keydown@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.1.1" + dependencies: + "@radix-ui/react-use-callback-ref": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/bff53be99e940fef1d3c4df7d560e1d9133182e5a98336255d3063327d1d3dd4ec54a95dc5afe15cca4fb6c184f0a956c70de2815578c318cf995a7f9beabaa1 + languageName: node + linkType: hard + "@radix-ui/react-use-layout-effect@npm:1.1.1": version: 1.1.1 resolution: "@radix-ui/react-use-layout-effect@npm:1.1.1" @@ -3725,6 +3849,15 @@ __metadata: languageName: node linkType: hard +"aria-hidden@npm:^1.2.4": + version: 1.2.6 + resolution: "aria-hidden@npm:1.2.6" + dependencies: + tslib: "npm:^2.0.0" + checksum: 10c0/7720cb539497a9f760f68f98a4b30f22c6767aa0e72fa7d58279f7c164e258fc38b2699828f8de881aab0fc8e9c56d1313a3f1a965046fc0381a554dbc72b54a + languageName: node + linkType: hard + "aria-query@npm:^5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" @@ -5085,6 +5218,13 @@ __metadata: languageName: node linkType: hard +"detect-node-es@npm:^1.1.0": + version: 1.1.0 + resolution: "detect-node-es@npm:1.1.0" + checksum: 10c0/e562f00de23f10c27d7119e1af0e7388407eb4b06596a25f6d79a360094a109ff285de317f02b090faae093d314cf6e73ac3214f8a5bb3a0def5bece94557fbe + languageName: node + linkType: hard + "didyoumean@npm:^1.2.2": version: 1.2.2 resolution: "didyoumean@npm:1.2.2" @@ -6544,6 +6684,13 @@ __metadata: languageName: node linkType: hard +"get-nonce@npm:^1.0.0": + version: 1.0.1 + resolution: "get-nonce@npm:1.0.1" + checksum: 10c0/2d7df55279060bf0568549e1ffc9b84bc32a32b7541675ca092dce56317cdd1a59a98dcc4072c9f6a980779440139a3221d7486f52c488e69dc0fd27b1efb162 + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -7789,6 +7936,7 @@ __metadata: "@next/eslint-plugin-next": "npm:^14.0.1" "@radix-ui/react-checkbox": "npm:^1.3.1" "@radix-ui/react-collapsible": "npm:^1.1.11" + "@radix-ui/react-dialog": "npm:^1.1.14" "@radix-ui/react-label": "npm:^2.1.7" "@radix-ui/react-radio-group": "npm:^1.3.7" "@radix-ui/react-separator": "npm:^1.1.7" @@ -9529,6 +9677,57 @@ __metadata: languageName: node linkType: hard +"react-remove-scroll-bar@npm:^2.3.7": + version: 2.3.8 + resolution: "react-remove-scroll-bar@npm:2.3.8" + dependencies: + react-style-singleton: "npm:^2.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/9a0675c66cbb52c325bdbfaed80987a829c4504cefd8ff2dd3b6b3afc9a1500b8ec57b212e92c1fb654396d07bbe18830a8146fe77677d2a29ce40b5e1f78654 + languageName: node + linkType: hard + +"react-remove-scroll@npm:^2.6.3": + version: 2.7.1 + resolution: "react-remove-scroll@npm:2.7.1" + dependencies: + react-remove-scroll-bar: "npm:^2.3.7" + react-style-singleton: "npm:^2.2.3" + tslib: "npm:^2.1.0" + use-callback-ref: "npm:^1.3.3" + use-sidecar: "npm:^1.1.3" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/7ad8f6ffd3e2aedf9b3d79f0c9088a9a3d7c5332d80c923427a6d97fe0626fb4cb33a6d9174d19fad57d860be69c96f68497a0619c3a8af0e8a5332e49bdde31 + languageName: node + linkType: hard + +"react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": + version: 2.2.3 + resolution: "react-style-singleton@npm:2.2.3" + dependencies: + get-nonce: "npm:^1.0.0" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/841938ff16d16a6b76895f4cb2e1fea957e5fe3b30febbf03a54892dae1c9153f2383e231dea0b3ba41192ad2f2849448fa859caccd288943bce32639e971bee + languageName: node + linkType: hard + "react-syntax-highlighter@npm:^15.6.1": version: 15.6.1 resolution: "react-syntax-highlighter@npm:15.6.1" @@ -10955,6 +11154,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.0.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + "tslib@npm:^2.0.3, tslib@npm:^2.1.0": version: 2.6.3 resolution: "tslib@npm:2.6.3" @@ -11337,6 +11543,37 @@ __metadata: languageName: node linkType: hard +"use-callback-ref@npm:^1.3.3": + version: 1.3.3 + resolution: "use-callback-ref@npm:1.3.3" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/f887488c6e6075cdad4962979da1714b217bcb1ee009a9e57ce9a844bcfc4c3a99e93983dfc2e5af9e0913824d24e730090ff255e902c516dcb58d2d3837e01c + languageName: node + linkType: hard + +"use-sidecar@npm:^1.1.3": + version: 1.1.3 + resolution: "use-sidecar@npm:1.1.3" + dependencies: + detect-node-es: "npm:^1.1.0" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": "*" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/161599bf921cfaa41c85d2b01c871975ee99260f3e874c2d41c05890d41170297bdcf314bc5185e7a700de2034ac5b888e3efc8e9f35724f4918f53538d717c9 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" From f46e3aab43cb0fb81eb3d8f2347863dda57c7343 Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 10 Aug 2025 20:06:57 +0300 Subject: [PATCH 3/9] format adjustments --- components/ui/dialog.tsx | 74 +++-- components/ui/table.tsx | 84 ++--- pages/tools/components/ToolingDetailModal.tsx | 13 +- pages/tools/components/ToolingTable.tsx | 309 +++++++++--------- 4 files changed, 241 insertions(+), 239 deletions(-) diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 6cb123b38..bf20e0e83 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -1,31 +1,33 @@ -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { XIcon } from "lucide-react" +/* eslint-disable linebreak-style */ +/* eslint-disable react/prop-types */ +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { XIcon } from 'lucide-react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; function Dialog({ ...props }: React.ComponentProps) { - return + return ; } function DialogTrigger({ ...props }: React.ComponentProps) { - return + return ; } function DialogPortal({ ...props }: React.ComponentProps) { - return + return ; } function DialogClose({ ...props }: React.ComponentProps) { - return + return ; } function DialogOverlay({ @@ -34,14 +36,14 @@ function DialogOverlay({ }: React.ComponentProps) { return ( - ) + ); } function DialogContent({ @@ -50,55 +52,55 @@ function DialogContent({ showCloseButton = true, ...props }: React.ComponentProps & { - showCloseButton?: boolean + showCloseButton?: boolean; }) { return ( - + {children} {showCloseButton && ( - Close + Close )} - ) + ); } -function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { +function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } -function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { +function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
- ) + ); } function DialogTitle({ @@ -107,11 +109,11 @@ function DialogTitle({ }: React.ComponentProps) { return ( - ) + ); } function DialogDescription({ @@ -120,11 +122,11 @@ function DialogDescription({ }: React.ComponentProps) { return ( - ) + ); } export { @@ -138,4 +140,4 @@ export { DialogPortal, DialogTitle, DialogTrigger, -} +}; diff --git a/components/ui/table.tsx b/components/ui/table.tsx index 5513a5cdb..df422c53a 100644 --- a/components/ui/table.tsx +++ b/components/ui/table.tsx @@ -1,105 +1,107 @@ -import * as React from "react" +/* eslint-disable linebreak-style */ +/* eslint-disable react/prop-types */ +import * as React from 'react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -function Table({ className, ...props }: React.ComponentProps<"table">) { +function Table({ className, ...props }: React.ComponentProps<'table'>) { return (
- ) + ); } -function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { +function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) { return ( - ) + ); } -function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { +function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) { return ( - ) + ); } -function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { +function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) { return ( tr]:last:border-b-0", - className + 'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', + className, )} {...props} /> - ) + ); } -function TableRow({ className, ...props }: React.ComponentProps<"tr">) { +function TableRow({ className, ...props }: React.ComponentProps<'tr'>) { return ( - ) + ); } -function TableHead({ className, ...props }: React.ComponentProps<"th">) { +function TableHead({ className, ...props }: React.ComponentProps<'th'>) { return (
[role=checkbox]]:translate-y-[2px]", - className + 'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', + className, )} {...props} /> - ) + ); } -function TableCell({ className, ...props }: React.ComponentProps<"td">) { +function TableCell({ className, ...props }: React.ComponentProps<'td'>) { return ( [role=checkbox]]:translate-y-[2px]", - className + 'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', + className, )} {...props} /> - ) + ); } function TableCaption({ className, ...props -}: React.ComponentProps<"caption">) { +}: React.ComponentProps<'caption'>) { return (
- ) + ); } export { @@ -111,4 +113,4 @@ export { TableRow, TableCell, TableCaption, -} +}; diff --git a/pages/tools/components/ToolingDetailModal.tsx b/pages/tools/components/ToolingDetailModal.tsx index 44d2a1fed..6746ac98e 100644 --- a/pages/tools/components/ToolingDetailModal.tsx +++ b/pages/tools/components/ToolingDetailModal.tsx @@ -2,12 +2,7 @@ import React, { useEffect, useState } from 'react'; import CancelIcon from '~/public/icons/cancel.svg'; import { Button } from '~/components/ui/button'; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, -} from '~/components/ui/dialog'; +import { Dialog, DialogContent } from '~/components/ui/dialog'; import Badge from './ui/Badge'; import type { JSONSchemaTool } from '../JSONSchemaTool'; @@ -26,12 +21,12 @@ export default function ToolingDetailModal({ }) { return ( onClose()}> - diff --git a/pages/tools/components/ToolingTable.tsx b/pages/tools/components/ToolingTable.tsx index 2ae95f7d3..13e4addfa 100644 --- a/pages/tools/components/ToolingTable.tsx +++ b/pages/tools/components/ToolingTable.tsx @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ import React, { Dispatch, ReactNode, @@ -131,138 +132,140 @@ const ToolingTable = ({ - - Name - - {transform.groupBy !== 'toolingTypes' && ( + + Name + + {transform.groupBy !== 'toolingTypes' && ( + + Tooling Type + + )} + {transform.groupBy !== 'languages' && ( + + Languages + + )} - Tooling Type + Dialects - )} - {transform.groupBy !== 'languages' && ( - - Languages - - )} - - Dialects - - - License - - - Bowtie - + License + + + Bowtie + - {toolsByGroup[group].map((tool: JSONSchemaTool, index) => { - const bowtieData = getBowtieData(tool); - if (bowtieData) { - tool.bowtie = bowtieData; - } - return ( - openModal(tool)} - > - -
- segment.length > 25) ? 'break-all' : 'break-words'}> - {tool.name} - - {tool.status === 'obsolete' && ( - {tool.status} - )} -
-
- {transform.groupBy !== 'toolingTypes' && ( - - {tool.toolingTypes - ?.map((type) => toTitleCase(type, '-')) - .join(', ')} - - )} - {transform.groupBy !== 'languages' && ( - - {tool.languages?.join(', ')} - - )} - -
- {tool.supportedDialects?.draft?.map((draft) => { - return {draft}; - })} -
-
- - {tool.license} - - { + const bowtieData = getBowtieData(tool); + if (bowtieData) { + tool.bowtie = bowtieData; + } + return ( + openModal(tool)} > - {bowtieReport && ( -
- {bowtieData ? ( - event.stopPropagation()} - title='See at Bowtie' - > - - - ) : ( - + +
+ segment.length > 25) + ? 'break-all' + : 'break-words' + } + > + {tool.name} + + {tool.status === 'obsolete' && ( + {tool.status} )}
+
+ {transform.groupBy !== 'toolingTypes' && ( + + {tool.toolingTypes + ?.map((type) => toTitleCase(type, '-')) + .join(', ')} + + )} + {transform.groupBy !== 'languages' && ( + {tool.languages?.join(', ')} )} - - - ); - })} + +
+ {tool.supportedDialects?.draft?.map((draft) => { + return {draft}; + })} +
+
+ {tool.license} + + {bowtieReport && ( + + )} + + + ); + })}
@@ -283,43 +286,43 @@ const ToolingTable = ({ onClick={() => openModal(tool)} > - {bowtieData && ( - - )} + {bowtieData && ( + + )} -
-
- {tool.name} - {tool.status === 'obsolete' && ( - {tool.status} - )} +
+
+ {tool.name} + {tool.status === 'obsolete' && ( + {tool.status} + )} +
+
+
+ Languages: {tool.languages?.join(', ')} +
+
+ Supported Dialects: +
+
+ {tool.supportedDialects?.draft?.map((draft) => ( + {draft} + ))} +
+
+ License: {tool.license}
-
-
- Languages: {tool.languages?.join(', ')} -
-
- Supported Dialects: -
-
- {tool.supportedDialects?.draft?.map((draft) => ( - {draft} - ))} -
-
- License: {tool.license} -
); From 1583a1f7940242977957d3590e94efa8b96daa84 Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 17 Aug 2025 16:33:22 +0300 Subject: [PATCH 4/9] adding tests for ToolingTable --- cypress/components/ToolingTable.cy.tsx | 139 +++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 cypress/components/ToolingTable.cy.tsx diff --git a/cypress/components/ToolingTable.cy.tsx b/cypress/components/ToolingTable.cy.tsx new file mode 100644 index 000000000..2e07d07e7 --- /dev/null +++ b/cypress/components/ToolingTable.cy.tsx @@ -0,0 +1,139 @@ +/* eslint-disable linebreak-style */ +import React from 'react'; +import ToolingTable from '~/pages/tools/components/ToolingTable'; +import type { GroupedTools, Transform } from '~/pages/tools/hooks/useToolsTransform'; +import type { JSONSchemaTool } from '~/pages/tools/JSONSchemaTool'; +import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; + +const mockTools: JSONSchemaTool[] = [ + { + name: 'Test Tool 1', + toolingTypes: ['validator'], + languages: ['JavaScript'], + license: 'MIT', + source: 'https://github.com/test/tool1', + supportedDialects: { draft: ['2020-12'] }, + status: 'obsolete', + }, + { + name: 'Test Tool 2', + toolingTypes: ['editor'], + languages: ['Python'], + license: 'Apache-2.0', + source: 'https://github.com/test/tool2', + supportedDialects: { draft: ['2019-09'] }, + }, +]; + +const mockToolsByGroup: GroupedTools = { + 'validator': [mockTools[0]], + 'editor': [mockTools[1]], +}; + +const mockTransform: Transform = { + query: '', + sortBy: 'name', + sortOrder: 'ascending', + groupBy: 'toolingTypes', + licenses: [], + languages: [], + drafts: [], + toolingTypes: [], + environments: [], + showObsolete: 'false', + supportsBowtie: 'false', +}; + +describe('ToolingTable Component', () => { + let setTransformStub: Cypress.SinonStub; + let mockRouter: MockRouter; + + beforeEach(() => { + mockRouter = mockNextRouter(); + cy.stub(global, 'fetch').resolves({ + json: () => Promise.resolve({}), + } as Response); + setTransformStub = cy.stub().as('setTransform'); + }); + + it('renders grouped tools correctly', () => { + cy.mount( + + ); + + cy.contains('Validator').should('exist'); + cy.contains('Editor').should('exist'); + cy.contains('Test Tool 1').should('exist'); + cy.contains('Test Tool 2').should('exist'); + }); + + it('shows obsolete status and tool details', () => { + cy.mount( + + ); + + cy.contains('obsolete').should('exist'); + cy.contains('JavaScript').should('exist'); + cy.contains('MIT').should('exist'); + cy.contains('2020-12').should('exist'); + }); + + it('handles sorting and modal interactions', () => { + cy.viewport(1200, 800); // Set large viewport to show desktop table + + cy.mount( + + ); + + cy.get('.hidden.lg\\:block').first().within(() => { + cy.contains('Name').click(); + cy.get('@setTransform').should('have.been.called'); + + cy.get('tbody tr').first().click(); + }); + + cy.get('[role="dialog"]').should('exist'); + }); + + it('displays empty state when no tools', () => { + cy.mount( + + ); + + cy.contains('No Tools Found :(').should('exist'); + }); + + it('renders both desktop and mobile tables', () => { + cy.mount( + + ); + + cy.get('.hidden.lg\\:block').should('exist'); + cy.get('.lg\\:hidden').should('exist'); + }); +}); From e315f6a2d5e408b3f0d3b89d2def91b2c7815a4e Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 17 Aug 2025 16:51:28 +0300 Subject: [PATCH 5/9] format fixes --- cypress/components/ToolingTable.cy.tsx | 38 +++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/cypress/components/ToolingTable.cy.tsx b/cypress/components/ToolingTable.cy.tsx index 2e07d07e7..3d12eb800 100644 --- a/cypress/components/ToolingTable.cy.tsx +++ b/cypress/components/ToolingTable.cy.tsx @@ -1,7 +1,11 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable linebreak-style */ import React from 'react'; import ToolingTable from '~/pages/tools/components/ToolingTable'; -import type { GroupedTools, Transform } from '~/pages/tools/hooks/useToolsTransform'; +import type { + GroupedTools, + Transform, +} from '~/pages/tools/hooks/useToolsTransform'; import type { JSONSchemaTool } from '~/pages/tools/JSONSchemaTool'; import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; @@ -26,8 +30,8 @@ const mockTools: JSONSchemaTool[] = [ ]; const mockToolsByGroup: GroupedTools = { - 'validator': [mockTools[0]], - 'editor': [mockTools[1]], + validator: [mockTools[0]], + editor: [mockTools[1]], }; const mockTransform: Transform = { @@ -45,7 +49,7 @@ const mockTransform: Transform = { }; describe('ToolingTable Component', () => { - let setTransformStub: Cypress.SinonStub; + let setTransformStub: any; let mockRouter: MockRouter; beforeEach(() => { @@ -63,7 +67,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={2} - /> + />, ); cy.contains('Validator').should('exist'); @@ -79,7 +83,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={2} - /> + />, ); cy.contains('obsolete').should('exist'); @@ -90,22 +94,24 @@ describe('ToolingTable Component', () => { it('handles sorting and modal interactions', () => { cy.viewport(1200, 800); // Set large viewport to show desktop table - + cy.mount( + />, ); - cy.get('.hidden.lg\\:block').first().within(() => { - cy.contains('Name').click(); - cy.get('@setTransform').should('have.been.called'); - - cy.get('tbody tr').first().click(); - }); + cy.get('.hidden.lg\\:block') + .first() + .within(() => { + cy.contains('Name').click(); + cy.get('@setTransform').should('have.been.called'); + + cy.get('tbody tr').first().click(); + }); cy.get('[role="dialog"]').should('exist'); }); @@ -117,7 +123,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={0} - /> + />, ); cy.contains('No Tools Found :(').should('exist'); @@ -130,7 +136,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={2} - /> + />, ); cy.get('.hidden.lg\\:block').should('exist'); From bda26c1e85ac5da266c69dab80c52533ab4c6da5 Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 17 Aug 2025 18:51:43 +0300 Subject: [PATCH 6/9] Revert "format fixes" This reverts commit e315f6a2d5e408b3f0d3b89d2def91b2c7815a4e. --- cypress/components/ToolingTable.cy.tsx | 38 +++++++++++--------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/cypress/components/ToolingTable.cy.tsx b/cypress/components/ToolingTable.cy.tsx index 3d12eb800..2e07d07e7 100644 --- a/cypress/components/ToolingTable.cy.tsx +++ b/cypress/components/ToolingTable.cy.tsx @@ -1,11 +1,7 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable linebreak-style */ import React from 'react'; import ToolingTable from '~/pages/tools/components/ToolingTable'; -import type { - GroupedTools, - Transform, -} from '~/pages/tools/hooks/useToolsTransform'; +import type { GroupedTools, Transform } from '~/pages/tools/hooks/useToolsTransform'; import type { JSONSchemaTool } from '~/pages/tools/JSONSchemaTool'; import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; @@ -30,8 +26,8 @@ const mockTools: JSONSchemaTool[] = [ ]; const mockToolsByGroup: GroupedTools = { - validator: [mockTools[0]], - editor: [mockTools[1]], + 'validator': [mockTools[0]], + 'editor': [mockTools[1]], }; const mockTransform: Transform = { @@ -49,7 +45,7 @@ const mockTransform: Transform = { }; describe('ToolingTable Component', () => { - let setTransformStub: any; + let setTransformStub: Cypress.SinonStub; let mockRouter: MockRouter; beforeEach(() => { @@ -67,7 +63,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={2} - />, + /> ); cy.contains('Validator').should('exist'); @@ -83,7 +79,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={2} - />, + /> ); cy.contains('obsolete').should('exist'); @@ -94,24 +90,22 @@ describe('ToolingTable Component', () => { it('handles sorting and modal interactions', () => { cy.viewport(1200, 800); // Set large viewport to show desktop table - + cy.mount( , + /> ); - cy.get('.hidden.lg\\:block') - .first() - .within(() => { - cy.contains('Name').click(); - cy.get('@setTransform').should('have.been.called'); - - cy.get('tbody tr').first().click(); - }); + cy.get('.hidden.lg\\:block').first().within(() => { + cy.contains('Name').click(); + cy.get('@setTransform').should('have.been.called'); + + cy.get('tbody tr').first().click(); + }); cy.get('[role="dialog"]').should('exist'); }); @@ -123,7 +117,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={0} - />, + /> ); cy.contains('No Tools Found :(').should('exist'); @@ -136,7 +130,7 @@ describe('ToolingTable Component', () => { transform={mockTransform} setTransform={setTransformStub} numberOfTools={2} - />, + /> ); cy.get('.hidden.lg\\:block').should('exist'); From 90c5393beb5532d944819980457db956ae30919f Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Sun, 17 Aug 2025 18:51:50 +0300 Subject: [PATCH 7/9] Revert "adding tests for ToolingTable" This reverts commit 1583a1f7940242977957d3590e94efa8b96daa84. --- cypress/components/ToolingTable.cy.tsx | 139 ------------------------- 1 file changed, 139 deletions(-) delete mode 100644 cypress/components/ToolingTable.cy.tsx diff --git a/cypress/components/ToolingTable.cy.tsx b/cypress/components/ToolingTable.cy.tsx deleted file mode 100644 index 2e07d07e7..000000000 --- a/cypress/components/ToolingTable.cy.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* eslint-disable linebreak-style */ -import React from 'react'; -import ToolingTable from '~/pages/tools/components/ToolingTable'; -import type { GroupedTools, Transform } from '~/pages/tools/hooks/useToolsTransform'; -import type { JSONSchemaTool } from '~/pages/tools/JSONSchemaTool'; -import mockNextRouter, { MockRouter } from '../plugins/mockNextRouterUtils'; - -const mockTools: JSONSchemaTool[] = [ - { - name: 'Test Tool 1', - toolingTypes: ['validator'], - languages: ['JavaScript'], - license: 'MIT', - source: 'https://github.com/test/tool1', - supportedDialects: { draft: ['2020-12'] }, - status: 'obsolete', - }, - { - name: 'Test Tool 2', - toolingTypes: ['editor'], - languages: ['Python'], - license: 'Apache-2.0', - source: 'https://github.com/test/tool2', - supportedDialects: { draft: ['2019-09'] }, - }, -]; - -const mockToolsByGroup: GroupedTools = { - 'validator': [mockTools[0]], - 'editor': [mockTools[1]], -}; - -const mockTransform: Transform = { - query: '', - sortBy: 'name', - sortOrder: 'ascending', - groupBy: 'toolingTypes', - licenses: [], - languages: [], - drafts: [], - toolingTypes: [], - environments: [], - showObsolete: 'false', - supportsBowtie: 'false', -}; - -describe('ToolingTable Component', () => { - let setTransformStub: Cypress.SinonStub; - let mockRouter: MockRouter; - - beforeEach(() => { - mockRouter = mockNextRouter(); - cy.stub(global, 'fetch').resolves({ - json: () => Promise.resolve({}), - } as Response); - setTransformStub = cy.stub().as('setTransform'); - }); - - it('renders grouped tools correctly', () => { - cy.mount( - - ); - - cy.contains('Validator').should('exist'); - cy.contains('Editor').should('exist'); - cy.contains('Test Tool 1').should('exist'); - cy.contains('Test Tool 2').should('exist'); - }); - - it('shows obsolete status and tool details', () => { - cy.mount( - - ); - - cy.contains('obsolete').should('exist'); - cy.contains('JavaScript').should('exist'); - cy.contains('MIT').should('exist'); - cy.contains('2020-12').should('exist'); - }); - - it('handles sorting and modal interactions', () => { - cy.viewport(1200, 800); // Set large viewport to show desktop table - - cy.mount( - - ); - - cy.get('.hidden.lg\\:block').first().within(() => { - cy.contains('Name').click(); - cy.get('@setTransform').should('have.been.called'); - - cy.get('tbody tr').first().click(); - }); - - cy.get('[role="dialog"]').should('exist'); - }); - - it('displays empty state when no tools', () => { - cy.mount( - - ); - - cy.contains('No Tools Found :(').should('exist'); - }); - - it('renders both desktop and mobile tables', () => { - cy.mount( - - ); - - cy.get('.hidden.lg\\:block').should('exist'); - cy.get('.lg\\:hidden').should('exist'); - }); -}); From 68d294e8ff6fe79d563d59bbf9bda849314ec3b6 Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Mon, 18 Aug 2025 13:41:43 +0300 Subject: [PATCH 8/9] adding tests --- cypress/components/ui/dialog.cy.tsx | 326 ++++++++++++++++++++ cypress/components/ui/table.cy.tsx | 445 ++++++++++++++++++++++++++++ 2 files changed, 771 insertions(+) create mode 100644 cypress/components/ui/dialog.cy.tsx create mode 100644 cypress/components/ui/table.cy.tsx diff --git a/cypress/components/ui/dialog.cy.tsx b/cypress/components/ui/dialog.cy.tsx new file mode 100644 index 000000000..296224844 --- /dev/null +++ b/cypress/components/ui/dialog.cy.tsx @@ -0,0 +1,326 @@ +import React from 'react'; +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} from '@/components/ui/dialog'; + +describe('Dialog Component', () => { + it('renders Dialog with all subcomponents correctly', () => { + cy.mount( + + + + Test Dialog Title + + This is a test dialog description + + +
Dialog content goes here
+ + + + +
+
, + ); + + // Dialog root doesn't render visible DOM elements, so we test the content instead + cy.get('[data-slot="dialog-content"]').should('exist'); + cy.get('[data-slot="dialog-header"]').should('exist'); + cy.get('[data-slot="dialog-title"]').should('exist'); + cy.get('[data-slot="dialog-description"]').should('exist'); + cy.get('[data-slot="dialog-footer"]').should('exist'); + }); + + it('renders Dialog with custom className correctly', () => { + cy.mount( + + + Custom Dialog +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-content"]').should( + 'have.class', + 'custom-dialog-class', + ); + }); + + it('renders Dialog with showCloseButton=false', () => { + cy.mount( + + + No Close Button +
Content without close button
+
+
, + ); + + cy.get('[data-slot="dialog-close"]').should('not.exist'); + }); + + it('renders Dialog with showCloseButton=true (default)', () => { + cy.mount( + + + With Close Button +
Content with close button
+
+
, + ); + + cy.get('[data-slot="dialog-close"]').should('exist'); + cy.get('[data-slot="dialog-close"]').should('contain.text', 'Close'); + }); + + it('renders DialogTitle with correct styling', () => { + cy.mount( + + + Test Title +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-title"]') + .should('exist') + .and('contain.text', 'Test Title') + .and('have.class', 'text-lg') + .and('have.class', 'font-semibold'); + }); + + it('renders DialogDescription with correct styling', () => { + cy.mount( + + + Title + Test Description +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-description"]') + .should('exist') + .and('contain.text', 'Test Description') + .and('have.class', 'text-sm') + .and('have.class', 'text-muted-foreground'); + }); + + it('renders DialogHeader with correct styling', () => { + cy.mount( + + + + Header Test + Header description + +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-header"]') + .should('exist') + .and('have.class', 'custom-header-class') + .and('have.class', 'flex') + .and('have.class', 'flex-col') + .and('have.class', 'gap-2'); + }); + + it('renders DialogFooter with correct styling', () => { + cy.mount( + + + Footer Test +
Content
+ + + + +
+
, + ); + + cy.get('[data-slot="dialog-footer"]') + .should('exist') + .and('have.class', 'custom-footer-class') + .and('have.class', 'flex') + .and('have.class', 'flex-col-reverse') + .and('have.class', 'gap-2'); + }); + + it('renders DialogOverlay with correct styling', () => { + cy.mount( + + + Overlay Test +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-overlay"]') + .should('exist') + .and('have.class', 'fixed') + .and('have.class', 'inset-0') + .and('have.class', 'z-50') + .and('have.class', 'bg-black/50'); + }); + + it('renders DialogContent with correct positioning and styling', () => { + cy.mount( + + + Content Test +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-content"]') + .should('exist') + .and('have.class', 'fixed') + .and('have.class', 'top-[50%]') + .and('have.class', 'left-[50%]') + .and('have.class', 'z-50') + .and('have.class', 'rounded-lg') + .and('have.class', 'border') + .and('have.class', 'shadow-lg'); + }); + + it('renders DialogTrigger correctly', () => { + cy.mount( + + Open Dialog + + Triggered Dialog +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-trigger"]') + .should('exist') + .and('contain.text', 'Open Dialog'); + }); + + it('renders DialogClose button with correct styling and accessibility', () => { + cy.mount( + + + Close Button Test +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-close"]') + .should('exist') + .and('have.class', 'absolute') + .and('have.class', 'top-4') + .and('have.class', 'right-4') + .and('have.class', 'rounded-xs'); + + // Check for screen reader text + cy.get('[data-slot="dialog-close"]').should('contain.text', 'Close'); + }); + + it('applies custom className to DialogContent', () => { + cy.mount( + + + Custom Class Test +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-content"]').should( + 'have.class', + 'my-custom-dialog', + ); + }); + + it('applies custom className to DialogHeader', () => { + cy.mount( + + + + Custom Header Class + +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-header"]').should( + 'have.class', + 'my-custom-header', + ); + }); + + it('applies custom className to DialogFooter', () => { + cy.mount( + + + Custom Footer Class +
Content
+ + + +
+
, + ); + + cy.get('[data-slot="dialog-footer"]').should( + 'have.class', + 'my-custom-footer', + ); + }); + + it('applies custom className to DialogTitle', () => { + cy.mount( + + + + Custom Title Class + +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-title"]').should( + 'have.class', + 'my-custom-title', + ); + }); + + it('applies custom className to DialogDescription', () => { + cy.mount( + + + Title + + Custom Description Class + +
Content
+
+
, + ); + + cy.get('[data-slot="dialog-description"]').should( + 'have.class', + 'my-custom-description', + ); + }); +}); diff --git a/cypress/components/ui/table.cy.tsx b/cypress/components/ui/table.cy.tsx new file mode 100644 index 000000000..58062f6be --- /dev/null +++ b/cypress/components/ui/table.cy.tsx @@ -0,0 +1,445 @@ +import React from 'react'; +import { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} from '@/components/ui/table'; + +describe('Table Component', () => { + it('renders complete Table with all subcomponents correctly', () => { + cy.mount( + + Test Table Caption + + + Name + Age + Email + + + + + John Doe + 30 + john@example.com + + + Jane Smith + 25 + jane@example.com + + + + + Total + 2 + - + + +
, + ); + + cy.get('[data-slot="table-container"]').should('exist'); + cy.get('[data-slot="table"]').should('exist'); + cy.get('[data-slot="table-caption"]').should('exist'); + cy.get('[data-slot="table-header"]').should('exist'); + cy.get('[data-slot="table-body"]').should('exist'); + cy.get('[data-slot="table-footer"]').should('exist'); + }); + + it('renders Table with custom className correctly', () => { + cy.mount( + + + + Content + + +
, + ); + + cy.get('[data-slot="table"]').should('have.class', 'custom-table-class'); + }); + + it('renders TableContainer with correct styling', () => { + cy.mount( + + + + Content + + +
, + ); + + cy.get('[data-slot="table-container"]') + .should('exist') + .and('have.class', 'relative') + .and('have.class', 'w-full') + .and('have.class', 'overflow-x-auto'); + }); + + it('renders Table with correct base styling', () => { + cy.mount( + + + + Content + + +
, + ); + + cy.get('[data-slot="table"]') + .should('exist') + .and('have.class', 'w-full') + .and('have.class', 'caption-bottom') + .and('have.class', 'text-sm'); + }); + + it('renders TableHeader with correct styling', () => { + cy.mount( + + + + Header + + + + + Content + + +
, + ); + + cy.get('[data-slot="table-header"]') + .should('exist') + .and('have.class', 'custom-header-class') + .and('have.class', '[&_tr]:border-b'); + }); + + it('renders TableBody with correct styling', () => { + cy.mount( + + + + Content + + +
, + ); + + cy.get('[data-slot="table-body"]') + .should('exist') + .and('have.class', 'custom-body-class') + .and('have.class', '[&_tr:last-child]:border-0'); + }); + + it('renders TableFooter with correct styling', () => { + cy.mount( + + + + Content + + + + + Footer + + +
, + ); + + cy.get('[data-slot="table-footer"]') + .should('exist') + .and('have.class', 'custom-footer-class') + .and('have.class', 'bg-muted/50') + .and('have.class', 'border-t') + .and('have.class', 'font-medium'); + }); + + it('renders TableRow with correct styling and hover effects', () => { + cy.mount( + + + + Content + + +
, + ); + + cy.get('[data-slot="table-row"]') + .should('exist') + .and('have.class', 'custom-row-class') + .and('have.class', 'hover:bg-muted/50') + .and('have.class', 'border-b') + .and('have.class', 'transition-colors'); + + // Check data-state attribute + cy.get('[data-slot="table-row"]').should( + 'have.attr', + 'data-state', + 'selected', + ); + }); + + it('renders TableHead with correct styling', () => { + cy.mount( + + + + Header Cell + + + + + Content + + +
, + ); + + cy.get('[data-slot="table-head"]') + .should('exist') + .and('contain.text', 'Header Cell') + .and('have.class', 'custom-head-class') + .and('have.class', 'h-10') + .and('have.class', 'px-2') + .and('have.class', 'text-left') + .and('have.class', 'align-middle') + .and('have.class', 'font-medium') + .and('have.class', 'whitespace-nowrap'); + }); + + it('renders TableCell with correct styling', () => { + cy.mount( + + + + Cell Content + + +
, + ); + + cy.get('[data-slot="table-cell"]') + .should('exist') + .and('contain.text', 'Cell Content') + .and('have.class', 'custom-cell-class') + .and('have.class', 'p-2') + .and('have.class', 'align-middle') + .and('have.class', 'whitespace-nowrap'); + }); + + it('renders TableCaption with correct styling', () => { + cy.mount( + + + Test Caption + + + + Content + + +
, + ); + + cy.get('[data-slot="table-caption"]') + .should('exist') + .and('contain.text', 'Test Caption') + .and('have.class', 'custom-caption-class') + .and('have.class', 'mt-4') + .and('have.class', 'text-sm') + .and('have.class', 'text-muted-foreground'); + }); + + it('renders complex table structure correctly', () => { + const data = [ + { name: 'Alice', role: 'Developer', department: 'Engineering' }, + { name: 'Bob', role: 'Designer', department: 'Design' }, + { name: 'Charlie', role: 'Manager', department: 'Management' }, + ]; + + cy.mount( + + Employee Directory + + + Name + Role + Department + + + + {data.map((employee, index) => ( + + {employee.name} + {employee.role} + {employee.department} + + ))} + + + + Total Employees + {data.length} + - + + +
, + ); + + // Check caption + cy.get('[data-slot="table-caption"]').should( + 'contain.text', + 'Employee Directory', + ); + + // Check header + cy.get('[data-slot="table-header"]').should('exist'); + cy.get('[data-slot="table-head"]').should('have.length', 3); + cy.get('[data-slot="table-head"]').eq(0).should('contain.text', 'Name'); + cy.get('[data-slot="table-head"]').eq(1).should('contain.text', 'Role'); + cy.get('[data-slot="table-head"]') + .eq(2) + .should('contain.text', 'Department'); + + // Check body rows + cy.get('[data-slot="table-body"] [data-slot="table-row"]').should( + 'have.length', + 3, + ); + cy.get('[data-slot="table-body"] [data-slot="table-cell"]').should( + 'have.length', + 9, + ); // 3 rows × 3 cells in body only + + // Check specific data + cy.get('[data-slot="table-cell"]').eq(0).should('contain.text', 'Alice'); + cy.get('[data-slot="table-cell"]') + .eq(1) + .should('contain.text', 'Developer'); + cy.get('[data-slot="table-cell"]') + .eq(2) + .should('contain.text', 'Engineering'); + + // Check footer + cy.get('[data-slot="table-footer"]').should('exist'); + cy.get('[data-slot="table-footer"] [data-slot="table-cell"]') + .eq(1) + .should('contain.text', '3'); + }); + + it('applies custom className to all table components', () => { + cy.mount( + + Caption + + + Header + + + + + Content + + + + + Footer + + +
, + ); + + cy.get('[data-slot="table"]').should('have.class', 'custom-table'); + cy.get('[data-slot="table-caption"]').should( + 'have.class', + 'custom-caption', + ); + cy.get('[data-slot="table-header"]').should('have.class', 'custom-header'); + cy.get('[data-slot="table-header"] [data-slot="table-row"]').should( + 'have.class', + 'custom-header-row', + ); + cy.get('[data-slot="table-head"]').should('have.class', 'custom-head'); + cy.get('[data-slot="table-body"]').should('have.class', 'custom-body'); + cy.get('[data-slot="table-body"] [data-slot="table-row"]').should( + 'have.class', + 'custom-body-row', + ); + cy.get('[data-slot="table-cell"]').should('have.class', 'custom-cell'); + cy.get('[data-slot="table-footer"]').should('have.class', 'custom-footer'); + cy.get('[data-slot="table-footer"] [data-slot="table-row"]').should( + 'have.class', + 'custom-footer-row', + ); + cy.get('[data-slot="table-footer"] [data-slot="table-cell"]').should( + 'have.class', + 'custom-footer-cell', + ); + }); + + it('renders table with accessibility attributes correctly', () => { + cy.mount( + + Accessible Table + + + Name + Age + + + + + John + 30 + + +
, + ); + + // Check that table elements are properly structured + cy.get('[data-slot="table"]').should('exist'); + cy.get('[data-slot="table-header"]').should('exist'); + cy.get('[data-slot="table-body"]').should('exist'); + + // Check for proper table structure + cy.get('[data-slot="table"]').should('be.visible'); + cy.get('[data-slot="table-caption"]').should( + 'contain.text', + 'Accessible Table', + ); + }); + + it('renders empty table structure correctly', () => { + cy.mount( + + + + Column 1 + Column 2 + + + {/* Empty body */} +
, + ); + + cy.get('[data-slot="table"]').should('exist'); + cy.get('[data-slot="table-header"]').should('exist'); + cy.get('[data-slot="table-body"]').should('exist'); + cy.get('[data-slot="table-body"] [data-slot="table-row"]').should( + 'have.length', + 0, + ); + }); +}); From 0935f94d9998ce60540f4eac2de9cec4bf66a5fe Mon Sep 17 00:00:00 2001 From: Idan Levi <29idan29@gmail.com> Date: Mon, 18 Aug 2025 15:24:44 +0300 Subject: [PATCH 9/9] small adjustments --- components/ui/dialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index bf20e0e83..64c8db5c4 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -1,5 +1,6 @@ /* eslint-disable linebreak-style */ /* eslint-disable react/prop-types */ +/* istanbul ignore file */ import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { XIcon } from 'lucide-react';