From 71fac7ffa28b627dfe4aa54e92df39577ab17120 Mon Sep 17 00:00:00 2001 From: Ian Cameron Lyles Date: Thu, 27 Feb 2025 17:07:33 -0800 Subject: [PATCH] chore(ui): Full type documentation (#10) * Adds typedoc for docs consolidation * chore: add UI types * fix: remove latent docs --- .github/workflows/docs.yml | 34 ++ .gitignore | 6 +- DOCUMENTATION.md | 165 +++++++++ README.md | 16 + docs-readme.md | 114 ++++++ package.json | 16 +- packages/backend/src/utils/tsdoc-example.ts | 237 +++++++++++++ packages/frontend/package.json | 2 +- .../frontend/src/components/SearchBar.tsx | 4 +- .../shared/DatePicker/DatePicker.tsx | 4 +- .../src/components/ui/ComboboxDemo.tsx | 113 ++++++ .../frontend/src/components/ui/accordion.tsx | 82 ++++- .../src/components/ui/alert-dialog.tsx | 139 +++++++- packages/frontend/src/components/ui/alert.tsx | 76 +++- .../src/components/ui/aspect-ratio.tsx | 40 ++- .../frontend/src/components/ui/avatar.tsx | 75 +++- packages/frontend/src/components/ui/badge.tsx | 75 ++-- .../frontend/src/components/ui/breadcrumb.tsx | 102 +++++- .../frontend/src/components/ui/button.tsx | 43 +++ .../frontend/src/components/ui/calendar.tsx | 49 ++- packages/frontend/src/components/ui/card.tsx | 65 +++- packages/frontend/src/components/ui/chart.tsx | 129 ++++++- .../frontend/src/components/ui/checkbox.tsx | 24 +- .../src/components/ui/collapsible.tsx | 61 +++- .../frontend/src/components/ui/combobox.tsx | 273 ++++++++++++++ .../frontend/src/components/ui/command.tsx | 167 ++++++++- .../src/components/ui/context-menu.tsx | 120 ++++++- .../src/components/ui/date-picker.tsx | 55 +++ .../frontend/src/components/ui/dialog.tsx | 61 +++- .../frontend/src/components/ui/drawer.tsx | 65 +++- .../src/components/ui/dropdown-menu.tsx | 241 ++++++++++++- .../components/ui/extended/button-w-icons.tsx | 33 +- .../components/ui/extended/input-w-icons.tsx | 44 +-- packages/frontend/src/components/ui/form.tsx | 88 ++++- .../frontend/src/components/ui/hover-card.tsx | 60 +++- packages/frontend/src/components/ui/index.ts | 22 ++ .../frontend/src/components/ui/input-otp.tsx | 71 +++- packages/frontend/src/components/ui/input.tsx | 70 ++-- packages/frontend/src/components/ui/label.tsx | 24 +- .../frontend/src/components/ui/menubar.tsx | 40 ++- .../src/components/ui/navigation-menu.tsx | 90 ++++- .../frontend/src/components/ui/pagination.tsx | 128 ++++++- .../frontend/src/components/ui/popover.tsx | 51 ++- .../frontend/src/components/ui/progress.tsx | 36 +- .../src/components/ui/radio-group.tsx | 50 ++- .../frontend/src/components/ui/resizable.tsx | 8 +- .../src/components/ui/scroll-area.tsx | 37 +- .../frontend/src/components/ui/select.tsx | 158 ++++++++- .../frontend/src/components/ui/separator.tsx | 26 +- packages/frontend/src/components/ui/sheet.tsx | 55 ++- .../frontend/src/components/ui/sidebar.tsx | 91 +++-- .../frontend/src/components/ui/skeleton.tsx | 38 +- .../frontend/src/components/ui/slider.tsx | 21 +- .../frontend/src/components/ui/sonner.tsx | 26 ++ .../frontend/src/components/ui/switch.tsx | 38 +- packages/frontend/src/components/ui/table.tsx | 43 ++- packages/frontend/src/components/ui/tabs.tsx | 87 ++++- .../frontend/src/components/ui/textarea.tsx | 18 +- packages/frontend/src/components/ui/toast.tsx | 29 +- .../frontend/src/components/ui/toaster.tsx | 24 +- .../src/components/ui/toggle-group.tsx | 44 ++- .../frontend/src/components/ui/toggle.tsx | 45 ++- .../frontend/src/components/ui/tooltip.tsx | 52 ++- packages/frontend/src/index.css | 11 + packages/frontend/src/utils/tsdoc-example.ts | 194 ++++++++++ scripts/generate-docs.js | 82 +++++ tsconfig.doc.json | 23 ++ typedoc.json | 25 ++ yarn.lock | 335 +++++++++--------- 69 files changed, 4336 insertions(+), 634 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 DOCUMENTATION.md create mode 100644 docs-readme.md create mode 100644 packages/backend/src/utils/tsdoc-example.ts create mode 100644 packages/frontend/src/components/ui/ComboboxDemo.tsx create mode 100644 packages/frontend/src/components/ui/combobox.tsx create mode 100644 packages/frontend/src/components/ui/date-picker.tsx create mode 100644 packages/frontend/src/utils/tsdoc-example.ts create mode 100755 scripts/generate-docs.js create mode 100644 tsconfig.doc.json create mode 100644 typedoc.json diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..44f71c17 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,34 @@ +name: Generate and Deploy Documentation + +on: + push: + branches: [ main, master, develop, develop-qwrk ] # Trigger on push to these branches + workflow_dispatch: # Allow manual triggering + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 # Fetch all history for proper versioning + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '16' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Generate documentation + run: node scripts/generate-docs.js + + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages # The branch the action should deploy to + folder: docs # The folder the action should deploy + clean: true # Automatically remove deleted files from the deploy branch \ No newline at end of file diff --git a/.gitignore b/.gitignore index 76344a89..1d4ed0c8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ yarn-error.log .yarnrc.yml .yarn/ .yarnrc +.cursor packages/backend/environments/local.toml packages/backend/dev/ @@ -10,4 +11,7 @@ packages/frontend/dist/ # ignore all .DS_Store files **/.DS_Store -.vscode \ No newline at end of file +.vscode + +# TypeDoc generated documentation +/docs diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 00000000..cd9978a1 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,165 @@ +# Documentation Guide for Snowball Tools + +This guide explains how to write and generate documentation for the Snowball Tools project. + +## TSDoc and TypeDoc + +We use [TSDoc](https://tsdoc.org/) for documenting our TypeScript code and [TypeDoc](https://typedoc.org/) for generating API documentation from those comments. + +## Writing Documentation + +### Basic Comment Structure + +TSDoc comments start with `/**` and end with `*/`. Each line within the comment block typically starts with a `*`. + +```typescript +/** + * This is a TSDoc comment. + */ +``` + +### Documenting Functions + +```typescript +/** + * Calculates the sum of two numbers. + * + * @param a - The first number + * @param b - The second number + * @returns The sum of a and b + * + * @example + * ```ts + * const result = add(1, 2); + * console.log(result); // 3 + * ``` + */ +function add(a: number, b: number): number { + return a + b; +} +``` + +### Documenting Classes + +```typescript +/** + * Represents a user in the system. + * + * @remarks + * This class is used throughout the application to represent user data. + * + * @example + * ```ts + * const user = new User('John', 'Doe'); + * console.log(user.fullName); // "John Doe" + * ``` + */ +class User { + /** + * Creates a new User instance. + * + * @param firstName - The user's first name + * @param lastName - The user's last name + */ + constructor( + /** The user's first name */ + public firstName: string, + /** The user's last name */ + public lastName: string + ) {} + + /** + * Gets the user's full name. + */ + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } +} +``` + +### Documenting Interfaces + +```typescript +/** + * Configuration options for the application. + * + * @public + */ +interface AppConfig { + /** + * The port number the server should listen on. + * @default 3000 + */ + port: number; + + /** + * The host the server should bind to. + * @default "localhost" + */ + host: string; + + /** + * Whether to enable debug mode. + * @default false + */ + debug?: boolean; +} +``` + +## Common TSDoc Tags + +| Tag | Description | +|-----|-------------| +| `@param` | Documents a function parameter | +| `@returns` | Documents the return value | +| `@throws` | Documents exceptions that might be thrown | +| `@example` | Provides an example of usage | +| `@remarks` | Adds additional information | +| `@deprecated` | Marks an item as deprecated | +| `@see` | Refers to related documentation | +| `@default` | Documents the default value | +| `@public`, `@protected`, `@private` | Visibility modifiers | +| `@internal` | Marks an item as internal (not part of the public API) | +| `@beta` | Marks an item as in beta stage | +| `@alpha` | Marks an item as in alpha stage | +| `@experimental` | Marks an item as experimental | + +## Generating Documentation + +To generate documentation for the project, run: + +```bash +yarn docs +``` + +This will create a `docs` directory with the generated documentation. + +To watch for changes and regenerate documentation automatically: + +```bash +yarn docs:watch +``` + +## Best Practices + +1. **Document Public APIs**: Always document public APIs thoroughly. +2. **Include Examples**: Provide examples for complex functions or classes. +3. **Be Concise**: Keep documentation clear and to the point. +4. **Use Proper Grammar**: Use proper grammar and punctuation. +5. **Update Documentation**: Keep documentation in sync with code changes. +6. **Document Parameters**: Document all parameters, including their types and purpose. +7. **Document Return Values**: Document what a function returns. +8. **Document Exceptions**: Document any exceptions that might be thrown. + +## Example Files + +For reference, check out these example files that demonstrate proper TSDoc usage: + +- `packages/backend/src/utils/tsdoc-example.ts` +- `packages/frontend/src/utils/tsdoc-example.ts` + +## Resources + +- [TSDoc Official Documentation](https://tsdoc.org/) +- [TypeDoc Official Documentation](https://typedoc.org/) +- [TypeScript Documentation](https://www.typescriptlang.org/docs/) \ No newline at end of file diff --git a/README.md b/README.md index 7a28e9c7..fda8b5e8 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,19 @@ yarn build --ignore frontend ### Environment variables, running the development server, and deployment Follow the instructions in the README.md files of the [backend](packages/backend/README.md) and [frontend](packages/frontend/README.md) packages. + +## Documentation + +This project uses TSDoc for code documentation and TypeDoc for generating API documentation. + +### Generating Documentation + +To generate the API documentation, run: + +```zsh +yarn docs +``` + +This will create a `docs` directory with the generated documentation. + +For more information about writing documentation, see [DOCUMENTATION.md](DOCUMENTATION.md). diff --git a/docs-readme.md b/docs-readme.md new file mode 100644 index 00000000..82ab9104 --- /dev/null +++ b/docs-readme.md @@ -0,0 +1,114 @@ +# Snowball Tools API Documentation + +This documentation is automatically generated using [TypeDoc](https://typedoc.org/) from TSDoc comments in the codebase. + +## Packages + +The monorepo contains the following packages: + +- **frontend**: The frontend web application +- **backend**: The backend API server +- **gql-client**: GraphQL client library +- **deployer**: Deployment utilities + +## How to Use This Documentation + +The documentation is organized by packages and modules. You can navigate through the sidebar to explore the different parts of the codebase. + +## Contributing to Documentation + +To contribute to the documentation: + +1. Add TSDoc comments to your code using the JSDoc syntax +2. Run `yarn docs` to generate the documentation +3. Preview the documentation in the `docs` directory + +## TSDoc Comment Examples + +### Function Documentation + +```typescript +/** + * Calculates the sum of two numbers. + * + * @param a - The first number + * @param b - The second number + * @returns The sum of a and b + * + * @example + * ```ts + * const result = add(1, 2); + * console.log(result); // 3 + * ``` + */ +function add(a: number, b: number): number { + return a + b; +} +``` + +### Class Documentation + +```typescript +/** + * Represents a user in the system. + * + * @remarks + * This class is used throughout the application to represent user data. + * + * @example + * ```ts + * const user = new User('John', 'Doe'); + * console.log(user.fullName); // "John Doe" + * ``` + */ +class User { + /** + * Creates a new User instance. + * + * @param firstName - The user's first name + * @param lastName - The user's last name + */ + constructor( + /** The user's first name */ + public firstName: string, + /** The user's last name */ + public lastName: string + ) {} + + /** + * Gets the user's full name. + */ + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } +} +``` + +### Interface Documentation + +```typescript +/** + * Configuration options for the application. + * + * @public + */ +interface AppConfig { + /** + * The port number the server should listen on. + * @default 3000 + */ + port: number; + + /** + * The host the server should bind to. + * @default "localhost" + */ + host: string; + + /** + * Whether to enable debug mode. + * @default false + */ + debug?: boolean; +} +``` \ No newline at end of file diff --git a/package.json b/package.json index 6e88014c..b35dce45 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,15 @@ "packages/*" ], "devDependencies": { + "chalk": "^4.1.2", + "concurrently": "^8.2.0", "depcheck": "^1.4.2", "husky": "^8.0.3", "lerna": "^8.0.0", "patch-package": "^8.0.0", - "chalk": "^4.1.2", - "concurrently": "^8.2.0" + "rimraf": "^6.0.1", + "typedoc": "^0.27.9", + "typedoc-plugin-markdown": "^4.4.2" }, "scripts": { "prepare": "husky install", @@ -19,8 +22,11 @@ "start": "yarn kill:ports && yarn dev", "dev": "yarn && yarn build --ignore frontend && concurrently --names \"BACKEND,FRONTEND\" --prefix-colors \"blue.bold,green.bold\" --prefix \"[{name}]\" \"yarn start:backend\" \"yarn start:frontend\"", "start:backend": "yarn workspace backend start", - "start:frontend": "yarn workspace frontend dev", - "kill:ports": "node scripts/kill-ports.js" + "start:frontend": "yarn workspace frontend dev", + "kill:ports": "node scripts/kill-ports.js", + "docs": "node scripts/generate-docs.js", + "docs:clean": "node scripts/generate-docs.js --clean", + "docs:watch": "typedoc --watch" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" -} \ No newline at end of file +} diff --git a/packages/backend/src/utils/tsdoc-example.ts b/packages/backend/src/utils/tsdoc-example.ts new file mode 100644 index 00000000..2d454d61 --- /dev/null +++ b/packages/backend/src/utils/tsdoc-example.ts @@ -0,0 +1,237 @@ +/** + * This file demonstrates proper TSDoc usage for the Snowball Tools project. + * + * @packageDocumentation + * @module Utils + */ + +/** + * Configuration options for the application. + * + * @public + */ +export interface AppConfig { + /** + * The port number the server should listen on. + * @default 3000 + */ + port: number; + + /** + * The host the server should bind to. + * @default "localhost" + */ + host: string; + + /** + * Whether to enable debug mode. + * @default false + */ + debug?: boolean; +} + +/** + * Represents a user in the system. + * + * @remarks + * This class is used throughout the application to represent user data. + * + * @example + * ```ts + * const user = new User('John', 'Doe'); + * console.log(user.fullName); // "John Doe" + * ``` + */ +export class User { + /** + * Creates a new User instance. + * + * @param firstName - The user's first name + * @param lastName - The user's last name + */ + constructor( + /** The user's first name */ + public firstName: string, + /** The user's last name */ + public lastName: string + ) {} + + /** + * Gets the user's full name. + * + * @returns The user's full name as a string + */ + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } + + /** + * Updates the user's name. + * + * @param firstName - The new first name + * @param lastName - The new last name + * @returns The updated user instance + * + * @throws {Error} If either name is empty + */ + updateName(firstName: string, lastName: string): User { + if (!firstName || !lastName) { + throw new Error('First name and last name cannot be empty'); + } + + this.firstName = firstName; + this.lastName = lastName; + return this; + } +} + +/** + * Calculates the sum of two numbers. + * + * @param a - The first number + * @param b - The second number + * @returns The sum of a and b + * + * @example + * ```ts + * const result = add(1, 2); + * console.log(result); // 3 + * ``` + */ +export function add(a: number, b: number): number { + return a + b; +} + +/** + * Formats a date according to the specified format. + * + * @param date - The date to format + * @param format - The format string (default: 'YYYY-MM-DD') + * @returns The formatted date string + * + * @remarks + * This function uses a simple format string where: + * - YYYY: 4-digit year + * - MM: 2-digit month + * - DD: 2-digit day + * + * @example + * ```ts + * const date = new Date('2023-01-15'); + * const formatted = formatDate(date); + * console.log(formatted); // "2023-01-15" + * ``` + */ +export function formatDate(date: Date, format: string = 'YYYY-MM-DD'): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + + return format + .replace('YYYY', year.toString()) + .replace('MM', month) + .replace('DD', day); +} + +/** + * Type of log level for the application. + */ +export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; + +/** + * A simple logger utility. + * + * @remarks + * This logger provides methods for different log levels and can be configured + * to filter logs based on minimum level. + */ +export class Logger { + /** + * Creates a new Logger instance. + * + * @param name - The name of the logger + * @param minLevel - The minimum log level to display (default: 'info') + */ + constructor( + private name: string, + private minLevel: LogLevel = 'info' + ) {} + + /** + * Logs a debug message. + * + * @param message - The message to log + * @param data - Optional data to include + */ + debug(message: string, data?: unknown): void { + this.log('debug', message, data); + } + + /** + * Logs an info message. + * + * @param message - The message to log + * @param data - Optional data to include + */ + info(message: string, data?: unknown): void { + this.log('info', message, data); + } + + /** + * Logs a warning message. + * + * @param message - The message to log + * @param data - Optional data to include + */ + warn(message: string, data?: unknown): void { + this.log('warn', message, data); + } + + /** + * Logs an error message. + * + * @param message - The message to log + * @param error - Optional error to include + */ + error(message: string, error?: Error): void { + this.log('error', message, error); + } + + /** + * Internal method to handle logging. + * + * @param level - The log level + * @param message - The message to log + * @param data - Optional data to include + * + * @internal + */ + private log(level: LogLevel, message: string, data?: unknown): void { + const levels: Record = { + debug: 0, + info: 1, + warn: 2, + error: 3 + }; + + if (levels[level] >= levels[this.minLevel]) { + const timestamp = new Date().toISOString(); + const formattedMessage = `[${timestamp}] [${level.toUpperCase()}] [${this.name}] ${message}`; + + switch (level) { + case 'debug': + console.debug(formattedMessage, data || ''); + break; + case 'info': + console.info(formattedMessage, data || ''); + break; + case 'warn': + console.warn(formattedMessage, data || ''); + break; + case 'error': + console.error(formattedMessage, data || ''); + break; + } + } + } +} \ No newline at end of file diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 9dfb2aed..471a75d6 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -65,7 +65,7 @@ "axios": "^1.6.7", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "1.0.0", + "cmdk": "^1.0.4", "date-fns": "^4.1.0", "downshift": "^8.3.2", "embla-carousel-react": "^8.5.2", diff --git a/packages/frontend/src/components/SearchBar.tsx b/packages/frontend/src/components/SearchBar.tsx index 2ba5c2e4..a334ad3a 100644 --- a/packages/frontend/src/components/SearchBar.tsx +++ b/packages/frontend/src/components/SearchBar.tsx @@ -1,11 +1,11 @@ import React, { forwardRef, RefAttributes } from 'react'; -import { IconInput, InputProps } from '@/components/ui'; +import { IconInput, type IconInputProps } from '@/components/ui/extended/input-w-icons'; import { Search } from 'lucide-react'; const SearchBar: React.ForwardRefRenderFunction< HTMLInputElement, - InputProps & RefAttributes + IconInputProps & RefAttributes > = ({ value, onChange, placeholder = 'Search', ...props }, ref) => { return (
diff --git a/packages/frontend/src/components/shared/DatePicker/DatePicker.tsx b/packages/frontend/src/components/shared/DatePicker/DatePicker.tsx index 5fde91a5..05dac88c 100644 --- a/packages/frontend/src/components/shared/DatePicker/DatePicker.tsx +++ b/packages/frontend/src/components/shared/DatePicker/DatePicker.tsx @@ -2,7 +2,7 @@ import { Calendar as CalendarComponent, CalendarProps, } from '@/components/shared/Calendar'; -import { IconInput, InputProps } from '@/components/ui'; +import { IconInput, type IconInputProps } from '@/components/ui/extended/input-w-icons'; import { Value } from '@/types/vendor'; import * as Popover from '@radix-ui/react-popover'; import { format } from 'date-fns'; @@ -11,7 +11,7 @@ import { useCallback, useMemo, useState } from 'react'; import { datePickerTheme } from './DatePicker.theme'; export interface DatePickerProps - extends Omit { + extends Omit { /** * The props for the calendar component. */ diff --git a/packages/frontend/src/components/ui/ComboboxDemo.tsx b/packages/frontend/src/components/ui/ComboboxDemo.tsx new file mode 100644 index 00000000..811e3ecd --- /dev/null +++ b/packages/frontend/src/components/ui/ComboboxDemo.tsx @@ -0,0 +1,113 @@ +"use client" + +import { Check, ChevronsUpDown } from "lucide-react" +import * as React from "react" + +import { Button } from "@/components/ui/button" +import { + Combobox, + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + Popover, + PopoverContent, + PopoverTrigger +} from "@/components/ui/combobox" +import { cn } from "@/lib/utils" + +const frameworks = [ + { + value: "next.js", + label: "Next.js", + }, + { + value: "sveltekit", + label: "SvelteKit", + }, + { + value: "nuxt.js", + label: "Nuxt.js", + }, + { + value: "remix", + label: "Remix", + }, + { + value: "astro", + label: "Astro", + }, +] + +export function ComboboxDemo() { + const [open, setOpen] = React.useState(false) + const [value, setValue] = React.useState("") + + return ( +
+ {/* Method 1: Using the individual components */} +
+

Method 1: Using individual components

+ + + + + + + + + No framework found. + + {frameworks.map((framework) => ( + { + setValue(currentValue === value ? "" : currentValue) + setOpen(false) + }} + > + {framework.label} + + + ))} + + + + + +
+ + {/* Method 2: Using the Combobox component */} +
+

Method 2: Using the Combobox component

+ +
+
+ ) +} \ No newline at end of file diff --git a/packages/frontend/src/components/ui/accordion.tsx b/packages/frontend/src/components/ui/accordion.tsx index e1797c93..588dca61 100644 --- a/packages/frontend/src/components/ui/accordion.tsx +++ b/packages/frontend/src/components/ui/accordion.tsx @@ -1,11 +1,51 @@ -import * as React from "react" import * as AccordionPrimitive from "@radix-ui/react-accordion" import { ChevronDown } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" +/** + * A vertically stacked set of interactive headings that each reveal associated content. + * Composes with Radix UI's Accordion primitives. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {'single' | 'multiple'} props.type - Whether single or multiple items can be open + * @param {boolean} [props.collapsible] - Whether an open item can be collapsed + * @param {React.ComponentPropsWithoutRef} props... - All Radix AccordionRoot props + * + * @example + * + * + * Is it accessible? + * Yes. Adheres to WAI-ARIA patterns. + * + * + * + * @see {@link https://ui.shadcn.com/docs/components/accordion | Shadcn Accordion Documentation} + * @see {@link https://www.radix-ui.com/primitives/docs/components/accordion | Radix Accordion Primitive} + */ const Accordion = AccordionPrimitive.Root +/** + * An individual accordion item that contains the trigger and content. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} props.value - Unique value to identify the item + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix Item props + * + * @example + * + * Section 1 + * Content + * + */ const AccordionItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -18,6 +58,23 @@ const AccordionItem = React.forwardRef< )) AccordionItem.displayName = "AccordionItem" +/** + * The interactive button that toggles the accordion content visibility. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {boolean} [props.disabled] - Whether the trigger is disabled + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix Trigger props + * + * @example + * + * Section Title + * + * + */ const AccordionTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -26,30 +83,43 @@ const AccordionTrigger = React.forwardRef< svg]:rotate-180", + "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180", className )} {...props} > {children} - + )) AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName +/** + * The collapsible content section of the accordion item. + * + * @component + * @public + * @example + * + * Collapsible content section + * + */ const AccordionContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( -
{children}
+
{children}
)) AccordionContent.displayName = AccordionPrimitive.Content.displayName -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } diff --git a/packages/frontend/src/components/ui/alert-dialog.tsx b/packages/frontend/src/components/ui/alert-dialog.tsx index fa2b4429..70ace86d 100644 --- a/packages/frontend/src/components/ui/alert-dialog.tsx +++ b/packages/frontend/src/components/ui/alert-dialog.tsx @@ -1,15 +1,63 @@ -import * as React from "react" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" +import * as React from "react" -import { cn } from "@/lib/utils" import { buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils" +/** + * A modal dialog that interrupts the user with important content and expects a response. + * + * @component + * @public + * + * @example + * ```tsx + * + * Open + * + * + * Are you sure? + * + * This action cannot be undone. + * + * + * + * Cancel + * Continue + * + * + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/alert-dialog | Shadcn AlertDialog Documentation} + * @see {@link https://www.radix-ui.com/primitives/docs/components/alert-dialog | Radix AlertDialog Primitive} + */ const AlertDialog = AlertDialogPrimitive.Root +/** + * Button that triggers the alert dialog to open. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {boolean} [props.asChild] - Whether to merge props onto child + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogTrigger props + */ const AlertDialogTrigger = AlertDialogPrimitive.Trigger const AlertDialogPortal = AlertDialogPrimitive.Portal +/** + * A translucent overlay that covers the inert portion of the view when the dialog is open. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogOverlay props + */ const AlertDialogOverlay = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -25,6 +73,16 @@ const AlertDialogOverlay = React.forwardRef< )) AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName +/** + * Contains the content to display in the alert dialog. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogContent props + */ const AlertDialogContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -43,6 +101,16 @@ const AlertDialogContent = React.forwardRef< )) AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName +/** + * Container for alert dialog header content. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.HTMLAttributes} props... - All div element props + */ const AlertDialogHeader = ({ className, ...props @@ -57,6 +125,16 @@ const AlertDialogHeader = ({ ) AlertDialogHeader.displayName = "AlertDialogHeader" +/** + * Container for alert dialog footer content. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.HTMLAttributes} props... - All div element props + */ const AlertDialogFooter = ({ className, ...props @@ -71,6 +149,16 @@ const AlertDialogFooter = ({ ) AlertDialogFooter.displayName = "AlertDialogFooter" +/** + * Title of the alert dialog. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogTitle props + */ const AlertDialogTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -83,6 +171,16 @@ const AlertDialogTitle = React.forwardRef< )) AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName +/** + * Description text for the alert dialog. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogDescription props + */ const AlertDialogDescription = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -93,9 +191,18 @@ const AlertDialogDescription = React.forwardRef< {...props} /> )) -AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName +/** + * Button that confirms the alert dialog action. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogAction props + */ const AlertDialogAction = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -108,6 +215,16 @@ const AlertDialogAction = React.forwardRef< )) AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName +/** + * Button that cancels the alert dialog action. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All Radix AlertDialogCancel props + */ const AlertDialogCancel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -125,15 +242,7 @@ const AlertDialogCancel = React.forwardRef< AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName export { - AlertDialog, - AlertDialogPortal, - AlertDialogOverlay, - AlertDialogTrigger, - AlertDialogContent, - AlertDialogHeader, - AlertDialogFooter, - AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, + AlertDialog, AlertDialogAction, + AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger } + diff --git a/packages/frontend/src/components/ui/alert.tsx b/packages/frontend/src/components/ui/alert.tsx index 5afd41d1..0fd98542 100644 --- a/packages/frontend/src/components/ui/alert.tsx +++ b/packages/frontend/src/components/ui/alert.tsx @@ -1,5 +1,5 @@ +import { cva } from "class-variance-authority" import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" @@ -19,21 +19,65 @@ const alertVariants = cva( } ) +/** + * A contextual notification component for important messages. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {'default' | 'destructive'} [props.variant='default'] - Visual style variant + * @param {string} [props.className] - Additional custom classes + * @param {React.HTMLAttributes} props... - All div element props + * + * @example + * ```tsx + * + * Error + * Something went wrong + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/alert | Shadcn Alert Documentation} + */ const Alert = React.forwardRef< HTMLDivElement, - React.HTMLAttributes & VariantProps ->(({ className, variant, ...props }, ref) => ( + React.HTMLAttributes & { + variant?: 'default' | 'destructive' + } +>(({ className, variant = 'default', ...props }, ref) => (
svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4", + variant === 'destructive' && "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + className + )} {...props} /> )) Alert.displayName = "Alert" +/** + * Title element for Alert components. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.HTMLAttributes} props... - All heading element props + * + * @example + * ```tsx + * Success + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/alert | Shadcn AlertTitle Documentation} + */ const AlertTitle = React.forwardRef< - HTMLParagraphElement, + HTMLHeadingElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
} props... - All paragraph element props + * + * @example + * ```tsx + * + * Your action was completed successfully. + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/alert | Shadcn AlertDescription Documentation} + */ const AlertDescription = React.forwardRef< HTMLParagraphElement, React.HTMLAttributes @@ -56,4 +119,5 @@ const AlertDescription = React.forwardRef< )) AlertDescription.displayName = "AlertDescription" -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertDescription, AlertTitle } + diff --git a/packages/frontend/src/components/ui/aspect-ratio.tsx b/packages/frontend/src/components/ui/aspect-ratio.tsx index c4abbf37..c3b3cc55 100644 --- a/packages/frontend/src/components/ui/aspect-ratio.tsx +++ b/packages/frontend/src/components/ui/aspect-ratio.tsx @@ -1,5 +1,41 @@ -import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" +import * as React from "react" -const AspectRatio = AspectRatioPrimitive.Root +import { cn } from "@/lib/utils" + +/** + * A component to maintain consistent aspect ratios for content. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {number} [props.ratio=16/9] - Aspect ratio (width/height) + * @param {string} [props.className] - Additional custom classes + * @param {React.HTMLAttributes} props... - All div element props + * + * @example + * + * Example + * + * + * @see {@link https://ui.shadcn.com/docs/components/aspect-ratio | Shadcn AspectRatio Documentation} + */ +const AspectRatio = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & { ratio?: number } +>(({ className, ratio = 16 / 9, style, ...props }, ref) => ( +
+)) +AspectRatio.displayName = "AspectRatio" export { AspectRatio } diff --git a/packages/frontend/src/components/ui/avatar.tsx b/packages/frontend/src/components/ui/avatar.tsx index 51e507ba..df85fcb0 100644 --- a/packages/frontend/src/components/ui/avatar.tsx +++ b/packages/frontend/src/components/ui/avatar.tsx @@ -1,10 +1,38 @@ "use client" -import * as React from "react" import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from "react" import { cn } from "@/lib/utils" +/** + * Displays a user's avatar with fallback support. + * Composes Radix UI's Avatar primitives with shadcn styling. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All other Radix AvatarRoot props + * + * @example + * ```tsx + * + * + * CN + * + * + * // With custom size + * + * + * UR + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/avatar | Shadcn Avatar Documentation} + * @see {@link https://www.radix-ui.com/primitives/docs/components/avatar | Radix Avatar Primitive} + */ const Avatar = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -18,8 +46,23 @@ const Avatar = React.forwardRef< {...props} /> )) -Avatar.displayName = AvatarPrimitive.Root.displayName +Avatar.displayName = "Avatar" +/** + * The image element of the avatar. Should be used inside an Avatar component. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} props.src - Source URL for the avatar image + * @param {string} props.alt - Accessible alt text description + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All other Radix Image props + * + * @see {@link https://ui.shadcn.com/docs/components/avatar | Shadcn AvatarImage Documentation} + * @see {@link https://www.radix-ui.com/primitives/docs/components/avatar#image | Radix AvatarImage Primitive} + */ const AvatarImage = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -30,8 +73,29 @@ const AvatarImage = React.forwardRef< {...props} /> )) -AvatarImage.displayName = AvatarPrimitive.Image.displayName +AvatarImage.displayName = "AvatarImage" +/** + * Fallback component that shows when AvatarImage fails to load. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {string} [props.className] - Additional custom classes + * @param {React.ComponentPropsWithoutRef} props... - All other Radix Fallback props + * @param {React.ReactNode} props.children - Fallback content to display + * + * @example + * ```tsx + * + * JD + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/avatar | Shadcn AvatarFallback Documentation} + * @see {@link https://www.radix-ui.com/primitives/docs/components/avatar#fallback | Radix AvatarFallback Primitive} + */ const AvatarFallback = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -45,6 +109,7 @@ const AvatarFallback = React.forwardRef< {...props} /> )) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName +AvatarFallback.displayName = "AvatarFallback" + +export { Avatar, AvatarFallback, AvatarImage } -export { Avatar, AvatarImage, AvatarFallback } diff --git a/packages/frontend/src/components/ui/badge.tsx b/packages/frontend/src/components/ui/badge.tsx index e87d62bf..3aecf122 100644 --- a/packages/frontend/src/components/ui/badge.tsx +++ b/packages/frontend/src/components/ui/badge.tsx @@ -1,36 +1,47 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" - import { cn } from "@/lib/utils" +import * as React from "react" -const badgeVariants = cva( - "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", - { - variants: { - variant: { - default: - "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", - secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: - "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", - outline: "text-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, +/** + * Small status descriptor component for tagging and labeling. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {'default' | 'secondary' | 'destructive' | 'outline'} [props.variant='default'] - Visual style variant + * @param {string} [props.className] - Additional custom classes + * @param {React.HTMLAttributes} props... - All div element props + * + * @example + * ```tsx + * + * New Feature + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/badge | Shadcn Badge Documentation} + */ +const Badge = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & { + variant?: 'default' | 'secondary' | 'destructive' | 'outline' } -) +>(({ className, variant = 'default', ...props }, ref) => ( +
+)) +Badge.displayName = "Badge" -export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} - -function Badge({ className, variant, ...props }: BadgeProps) { - return ( -
- ) -} - -export { Badge, badgeVariants } +export { Badge } diff --git a/packages/frontend/src/components/ui/breadcrumb.tsx b/packages/frontend/src/components/ui/breadcrumb.tsx index 60e6c96f..4745a191 100644 --- a/packages/frontend/src/components/ui/breadcrumb.tsx +++ b/packages/frontend/src/components/ui/breadcrumb.tsx @@ -1,9 +1,36 @@ -import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" +/** + * A navigation component that helps users keep track of their location within a website. + * + * @component + * @public + * + * @param {Object} props - Component props + * @param {React.ReactNode} [props.separator] - Custom separator between items + * @param {React.ComponentPropsWithoutRef<"nav">} props... - All nav element props + * + * @example + * ```tsx + * + * + * + * Home + * + * + * + * Settings + * + * + * + * ``` + * + * @see {@link https://ui.shadcn.com/docs/components/breadcrumb | Shadcn Breadcrumb Documentation} + */ const Breadcrumb = React.forwardRef< HTMLElement, React.ComponentPropsWithoutRef<"nav"> & { @@ -12,6 +39,16 @@ const Breadcrumb = React.forwardRef< >(({ ...props }, ref) =>