# @theorchard/suite-components

A shared React component library for The Orchard suite applications. This package provides 70+ reusable UI components.

## Project Overview

-   **Purpose**: Shared component library for Orchard suite applications
-   **Tech Stack**: React, TypeScript, SCSS, Vitest
-   **Testing**: Vitest with @testing-library/react
-   **Monorepo**: Part of the `orchard-suite` workspace

## Architecture & Structure

```
src/
├── components/        # 70+ React components (see full list below)
├── constants/        # Shared constants and Figma tokens
├── i18n/            # Internationalization setup
├── styles/          # Global SCSS styles and variables
├── utils/           # Utility functions (countries, rendering, etc.)
└── types.ts         # Shared TypeScript types
```

### Key Components

The library includes extensive UI components organized by function:

-   **Forms**: Button, Checkbox, Radio, Select, MultiSelect, Switch, NumberInput, SearchInput
-   **Data Display**: Table, ListView, Metadata, MetadataList, Card, Label, Tag, Badge
-   **Navigation**: Pagination, Sidebar, Sidecar, PageHeader, Dropdown
-   **Overlays**: Modal, FullscreenModal, Tooltip, Popover, Toast
-   **Feedback**: LoadingIndicator, LoadingSpinner, SkeletonLoader, ErrorMessage, InfoMessage
-   **Media**: Image, CoverArt, UserThumb
-   **Specialized**: DatePicker, DateRangePicker, CountrySelect, MarketSelector, TimezoneSelector

## Development Guidelines

### Component Patterns

**CRITICAL: Follow existing component patterns strictly**

All components should follow these established patterns:

1. **File Structure (Simple Component)**:

    ```
    componentName/
    ├── componentName.tsx           # Main component file
    ├── componentName.scss          # Component styles
    ├── index.ts                    # Export file
    ├── i18n/                       # Component-specific translations (if needed)
    └── __tests__/
        └── componentName.spec.tsx  # Test file
    ```

2. **File Structure (Complex Component with Sub-components)**:

    ```
    componentName/
    ├── componentName.tsx           # Main component file
    ├── componentName.scss          # Component styles
    ├── index.ts                    # Export file
    ├── types.ts                    # Shared types for component and sub-components
    ├── components/                 # Sub-components folder
    │   ├── index.ts                # Export all sub-components
    │   ├── subComponent1.tsx
    │   ├── subComponent2.tsx
    │   └── __tests__/              # Tests for sub-components
    │       ├── subComponent1.spec.tsx
    │       └── subComponent2.spec.tsx
    ├── hooks/                      # Component-specific hooks (if needed)
    ├── utils/                      # Component-specific utilities (if needed)
    ├── i18n/                       # Component-specific translations (if needed)
    └── __tests__/
        └── componentName.spec.tsx  # Tests for main component
    ```

    **Example**: See `listView` component which has 30+ sub-components in its `components/` folder (e.g., `listViewHeader.tsx`, `listViewFooter.tsx`, `listViewOption.tsx`)

3. **Component Structure Pattern**:

    ```typescript
    import type { FC } from 'react';
    import React from 'react';
    import cx from 'classnames';

    const CLASSNAME = 'ComponentName';

    export interface ComponentNameProps {
        /** Prop description */
        className?: string;
        testId?: string;
        children?: React.ReactNode;
        // ... other props with JSDoc
    }

    /**
     * Brief component description.
     *
     * @type atom|molecule|organism
     * @status live|beta|deprecated
     * @tags category1, category2
     */
    export const ComponentName: FC<ComponentNameProps> = ({
        className,
        testId = CLASSNAME,
        children,
        // ... other props
    }) => {
        return (
            <div
                className={cx(CLASSNAME, className)}
                data-testid={testId}
            >
                {children}
            </div>
        );
    };
    ```

4. **Key Conventions**:
    - **Uppercase Constants**: Always use uppercase constant for the base class name (e.g., `const CLASSNAME = 'ComponentName'`)
    - **ClassNames**: Use `classnames` (imported as `cx`) for conditional class composition
    - **TestId**: Always include a `testId` prop with a default value matching the component name
    - **Sub-components**: If a component has sub-components, place them in a `components/` folder with their own files
    - **Avoid Bootstrap**: New components should be built without Bootstrap dependencies. Legacy components may use `react-bootstrap`, but new components should not.

### Testing Requirements

**Integration tests with user interactions** and **unit tests for utility functions** are the priority.

**CRITICAL: Use `screen` and React Testing Library queries - avoid direct DOM access**

1. **Test Structure**:

    ```typescript
    import React from 'react';
    import { render, screen, fireEvent, within } from '@testing-library/react';
    import { testComponent } from 'lib/test-utils/common';
    import { ComponentName, ComponentNameProps } from '../componentName';

    describe('<ComponentName>', () => {
        const defaultProps: ComponentNameProps = {
            // ... required props
        };

        const renderComponent = (props: Partial<ComponentNameProps> = {}) =>
            render(<ComponentName {...defaultProps} {...props} />);

        testComponent(ComponentName); // Standard component tests

        test('has className', () => {
            const className = 'my-class';
            renderComponent({ className });
            const root = screen.getByTestId('ComponentName');
            expect(root).toHaveClass(className);
        });

        test('has testId', () => {
            renderComponent();
            expect(screen.getByTestId('ComponentName')).toBeVisible();
        });

        test('handles user interaction', async () => {
            const onClick = vi.fn();
            renderComponent({ onClick });

            // PREFERRED: Use semantic queries
            fireEvent.click(screen.getByRole('button'));
            expect(onClick).toHaveBeenCalled();
        });

        test('shows content after async action', async () => {
            renderComponent({ loadData: true });

            // PREFERRED: Use findBy* for async queries instead of waitFor
            const content = await screen.findByText('Loaded content');
            expect(content).toBeVisible();
        });

        test('displays correct text', () => {
            const text = 'Click me';
            renderComponent({ label: text });

            // Query by text for user-visible content
            expect(screen.getByText(text)).toBeVisible();
        });

        test('supports keyboard navigation', () => {
            renderComponent();
            const button = screen.getByRole('button');

            button.focus();
            expect(button).toHaveFocus();
        });
    });
    ```

2. **Testing Best Practices**:

    - **Always use `screen`**: `screen.getByRole()`, `screen.getByText()`, etc.
    - **Prefer semantic queries** (in order of preference):
        1. `getByRole` - Best for accessibility (e.g., `getByRole('button')`, `getByRole('textbox')`)
        2. `getByLabelText` - For form fields with labels
        3. `getByPlaceholderText` - For inputs with placeholders
        4. `getByText` - For non-interactive elements with visible text
        5. `getByTestId` - Last resort when semantic queries won't work
    - **Use `findBy*` for async queries**: `await screen.findByText('async content')` is preferred over `waitFor`
    - **Use `within`** for scoped queries: `within(container).getByRole('button')`
    - **Simulate real user behavior**: Use `fireEvent` for actual user interactions
    - **Direct DOM access is a last resort**: Only use `container.querySelector()` when absolutely necessary

3. **Async Testing Pattern**:

    ```typescript
    // PREFERRED: Use findBy*
    const element = await screen.findByText('Loaded content');
    expect(element).toBeVisible();

    // NOT PREFERRED: Don't use waitFor unless necessary
    // await waitFor(() => expect(screen.getByText('content')).toBeVisible());
    ```

4. **Test Coverage Focus**:
    - User interactions (clicks, hovers, inputs, keyboard navigation)
    - Conditional rendering logic
    - Accessibility (ARIA attributes, keyboard support, focus management)
    - Edge cases and error states
    - Integration with other components

### Internationalization (i18n)

**CRITICAL: All user-facing text must use i18n**

For components with translations:

```typescript
// Create i18n/index.ts in component folder
import { createFormatter } from '@theorchard/suite-i18n';
export const t = createFormatter('componentName');

// In component:
import { t } from './i18n';

// Usage:
<div>{t('key.path')}</div>
```

Translation files are located in `/locale` directory with format `<component>.<locale>.json`

### Documentation Requirements

**CRITICAL: All components must have proper JSDoc with tags**

Every exported component MUST include JSDoc for frontend-solfege documentation:

```typescript
/**
 * Clear description of what the component does and when to use it.
 *
 * @type atom|molecule|organism
 * @status live|beta|deprecated
 * @tags category1, category2, category3
 * @variantOf baseComponent (optional - if this is a variant)
 */
export const ComponentName: FC<ComponentNameProps> = ({ ... }) => {
```

**JSDoc Tags**:

-   `@type`: Component complexity (`atom`, `molecule`, `organism`)
-   `@status`: Lifecycle state (`live`, `beta`, `deprecated`)
-   `@tags`: Categorization (e.g., `forms`, `overlays`, `utilities`, `visuals`)
-   `@variantOf`: Reference to base component if this is a specialized variant

**All props must have JSDoc descriptions**:

```typescript
export interface ComponentProps {
    /**
     * Description of what this prop does
     */
    propName: string;
}
```

### Styling Conventions

1. **SCSS Modules**: Each component has its own SCSS file
2. **Import Path**: Component styles are in `src/styles/` and exported via `./styles` entry point
3. **Class Naming**: Use the component name as the base class (e.g., `.HelpTooltip`)
4. **Variables**: Global SCSS variables available via `./vars` import

## Common Tasks

### Running Tests

```bash
pnpm test               # Run linting and tests
pnpm test:unit          # Run all tests(or watch mode)
pnpm vitest <test path> # Run individual tests
pnpm lint               # Run CSS and JS linting
```

### Building

```bash
pnpm build          # Build icons and outputs
pnpm clean          # Remove build artifacts
```

### Code Quality

```bash
pnpm format         # Format code with Prettier
pnpm format:check   # Check formatting
pnpm lint:css       # Lint SCSS files
pnpm lint:js        # Lint TS/TSX files
```

## TypeScript Configuration

-   **Strict Mode**: Enabled for type safety
-   **Test Types**: Includes Vitest globals and jest-dom matchers
-   **Path Aliases**: `lib/*` mapped to `./lib/*`

## Important Notes

1. **Consistency is Key**: Always follow existing patterns - look at similar components for reference
2. **Breaking Changes**: Be very careful with changes to public APIs (props, exports)
3. **Accessibility First**: Consider accessibility in all component designs (ARIA labels, keyboard navigation, focus management)
4. **Performance**: Use React best practices (proper memoization, avoid unnecessary re-renders)
5. **Test Like a User**: Write tests that simulate real user behavior, not implementation details
6. **No New Bootstrap Dependencies**: While many legacy components use react-bootstrap/bootstrap, new components should be built without these dependencies

## Example: Complete Component

Here's a reference example following all patterns:

```typescript
// badge.tsx
import type { FC } from 'react';
import React from 'react';
import cx from 'classnames';

const CLASSNAME = 'Badge';

export interface BadgeProps {
    /**
     * The badge content
     */
    children: React.ReactNode;

    /**
     * Visual variant of the badge
     */
    variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger';

    /**
     * Additional CSS classes
     */
    className?: string;

    /**
     * Test identifier
     */
    testId?: string;
}

/**
 * Badges are small status indicators used to highlight important information or counts.
 *
 * @type atom
 * @status live
 * @tags visuals, utilities
 */
export const Badge: FC<BadgeProps> = ({
    children,
    variant = 'primary',
    className,
    testId = CLASSNAME,
}) => {
    return (
        <span
            data-testid={testId}
            className={cx(CLASSNAME, `${CLASSNAME}--${variant}`, className)}
        >
            {children}
        </span>
    );
};
```

```typescript
// badge.spec.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { Badge, BadgeProps } from '../badge';

describe('<Badge>', () => {
    const defaultProps: BadgeProps = {
        children: 'New',
    };

    const renderComponent = (props: Partial<BadgeProps> = {}) =>
        render(<Badge {...defaultProps} {...props} />);

    testComponent(Badge);

    test('renders children', () => {
        renderComponent({ children: 'New Feature' });
        expect(screen.getByText('New Feature')).toBeVisible();
    });

    test('applies variant className', () => {
        renderComponent({ variant: 'success' });
        const badge = screen.getByTestId('Badge');
        expect(badge).toHaveClass('Badge--success');
    });

    test('applies custom className', () => {
        const className = 'custom-class';
        renderComponent({ className });
        const badge = screen.getByTestId('Badge');
        expect(badge).toHaveClass('Badge', className);
    });

    test('uses custom testId', () => {
        renderComponent({ testId: 'custom-badge' });
        expect(screen.getByTestId('custom-badge')).toBeVisible();
    });
});
```

## References

-   [Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/)
-   [Testing Library Queries](https://testing-library.com/docs/queries/about)
-   [Testing Library - Async Utilities](https://testing-library.com/docs/dom-testing-library/api-async)
-   [Vitest Docs](https://vitest.dev/)
