Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions docs/ui-components/Button.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Button Component

The Button component is a versatile and reusable UI element that follows our design system guidelines. It supports multiple variants, sizes, and states while maintaining accessibility standards.

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | - | The content to be displayed inside the button |
| variant | 'primary' \| 'secondary' \| 'outline' | 'primary' | The variant style of the button |
| onClick | () => void | - | Optional click handler |
| disabled | boolean | false | Whether the button is disabled |
| size | 'small' \| 'medium' \| 'large' | 'medium' | The size of the button |
| className | string | '' | Additional CSS classes |

## Usage Examples

```tsx
import Button from '@/components/Button';

// Primary Button (Default)
<Button onClick={() => console.log('Clicked!')}>
Click me
</Button>

// Secondary Button
<Button
variant="secondary"
size="large"
onClick={() => alert('Secondary action')}
>
Secondary Action
</Button>

// Outline Button (Disabled)
<Button
variant="outline"
disabled
>
Disabled Button
</Button>

// Small Primary Button with Custom Class
<Button
size="small"
className="my-4"
>
Small Button
</Button>
```

## Style Guide

The Button component uses Tailwind CSS for styling and follows these design principles:

### Colors
- Primary: Green-based theme (matches PlantPal's nature-focused brand)
- Secondary: Gray-scale for less prominent actions
- Outline: Bordered version for tertiary actions

### Variants

1. **Primary** (Default)
- High emphasis
- Used for main actions
- Filled background with white text
- Example: "Save Changes", "Submit", "Next"

2. **Secondary**
- Medium emphasis
- Used for secondary actions
- Light background with dark text
- Example: "Cancel", "Back", "See More"

3. **Outline**
- Low emphasis
- Used for tertiary actions
- Bordered with no fill
- Example: "Learn More", "Skip"

### Sizes

1. **Small** (`small`)
- Compact size: 24px height
- Used in tight spaces or dense UIs
- Font size: 14px (sm)

2. **Medium** (`medium`) - Default
- Standard size: 32px height
- Used for most button instances
- Font size: 16px (base)

3. **Large** (`large`)
- Prominent size: 40px height
- Used for main call-to-action buttons
- Font size: 18px (lg)

## Theming

The Button component uses these Tailwind CSS classes which can be customized in your `tailwind.config.js`:

```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
green: {
50: '#f0fdf4',
// ... other shades
600: '#16a34a',
700: '#15803d',
},
gray: {
200: '#e5e7eb',
300: '#d1d5db',
800: '#1f2937',
}
}
}
}
}
```

## Accessibility

The Button component follows WCAG 2.1 guidelines:

### Keyboard Navigation
- Fully focusable with keyboard (Tab key)
- Activatable with Enter or Space key
- Visual focus indicator with ring outline

### Screen Readers
- Uses semantic `<button>` element
- Includes `aria-disabled` attribute
- Maintains proper focus management

### Color Contrast
- All color combinations meet WCAG AA standards
- Focus states are clearly visible
- Disabled states are visually distinct

## Testing Guide

Here's how to test the Button component:

```tsx
import { render, fireEvent } from '@testing-library/react';
import Button from '@/components/Button';

describe('Button', () => {
// Render Test
it('renders children correctly', () => {
const { getByText } = render(<Button>Click me</Button>);
expect(getByText('Click me')).toBeInTheDocument();
});

// Click Handler Test
it('handles click events', () => {
const handleClick = jest.fn();
const { getByRole } = render(
<Button onClick={handleClick}>Click me</Button>
);
fireEvent.click(getByRole('button'));
expect(handleClick).toHaveBeenCalled();
});

// Disabled State Test
it('respects disabled state', () => {
const handleClick = jest.fn();
const { getByRole } = render(
<Button disabled onClick={handleClick}>
Click me
</Button>
);
const button = getByRole('button');
fireEvent.click(button);
expect(handleClick).not.toHaveBeenCalled();
expect(button).toBeDisabled();
});

// Style Tests
it('applies variant styles correctly', () => {
const { container } = render(
<Button variant="primary">Primary Button</Button>
);
expect(container.firstChild).toHaveClass('bg-green-600');
});

it('applies size styles correctly', () => {
const { container } = render(
<Button size="large">Large Button</Button>
);
expect(container.firstChild).toHaveClass('px-6 py-3 text-lg');
});
});
```
108 changes: 108 additions & 0 deletions docs/ui-components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# PlantPal UI Component Documentation

Welcome to PlantPal's UI Component Documentation. This guide provides comprehensive information about our component library, including usage examples, styling guidelines, and best practices.

## Table of Contents

1. [Components](#components)
2. [Design System](#design-system)
3. [Accessibility](#accessibility)
4. [Testing](#testing)
5. [Contributing](#contributing)

## Components

Our component library includes the following components:

- [Button](./Button.md) - A versatile button component with multiple variants

## Design System

Our components follow these core principles:

### Colors
- **Primary**: Green-based theme reflecting our nature-focused brand
- **Secondary**: Neutral grays for supporting elements
- **Accent**: Strategic use of complementary colors

### Typography
- **Font Family**: System-native fonts for optimal performance
- **Scale**: Consistent sizing using Tailwind's scale
- **Weights**: Regular (400), Medium (500), Bold (700)

### Spacing
- Based on a 4px grid system
- Consistent spacing using Tailwind's scale
- Responsive spacing utilities

### Elevation
- Subtle shadows for interactive elements
- Clear visual hierarchy
- Consistent z-index scale

## Accessibility

All components are built with accessibility in mind:

### Standards
- WCAG 2.1 AA compliance
- Semantic HTML
- ARIA attributes where necessary
- Keyboard navigation support

### Testing
- Regular accessibility audits
- Screen reader testing
- Keyboard navigation testing
- Color contrast verification

## Testing

Our testing strategy includes:

### Unit Tests
- Component rendering
- Props validation
- Event handling
- State management

### Integration Tests
- Component interactions
- Form submissions
- API interactions

### E2E Tests
- User flows
- Critical paths
- Edge cases

## Contributing

### Adding New Components

1. Create component in `src/frontend/components`
2. Add documentation in `docs/ui-components`
3. Include:
- Props documentation
- Usage examples
- Style guidelines
- Accessibility considerations
- Test cases

### Documentation Structure

Each component's documentation should include:

1. Overview
2. Props API
3. Usage Examples
4. Style Guide
5. Accessibility Notes
6. Testing Guide

### Review Process

1. Code review
2. Documentation review
3. Accessibility review
4. Performance review
72 changes: 72 additions & 0 deletions src/frontend/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';

interface ButtonProps {
/**
* The content to be displayed inside the button
*/
children: React.ReactNode;

/**
* The variant style of the button
*/
variant?: 'primary' | 'secondary' | 'outline';

/**
* Optional click handler
*/
onClick?: () => void;

/**
* Whether the button is disabled
*/
disabled?: boolean;

/**
* The size of the button
*/
size?: 'small' | 'medium' | 'large';

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

/**
* Primary UI component for user interaction
*/
export const Button: React.FC<ButtonProps> = ({
children,
variant = 'primary',
onClick,
disabled = false,
size = 'medium',
className = '',
}) => {
const baseStyles = 'rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';

const variantStyles = {
primary: 'bg-green-600 text-white hover:bg-green-700 focus:ring-green-500',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300 focus:ring-gray-500',
outline: 'border-2 border-green-600 text-green-600 hover:bg-green-50 focus:ring-green-500'
};

const sizeStyles = {
small: 'px-3 py-1.5 text-sm',
medium: 'px-4 py-2 text-base',
large: 'px-6 py-3 text-lg'
};

return (
<button
className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${className}`}
onClick={onClick}
disabled={disabled}
aria-disabled={disabled}
>
{children}
</button>
);
};

export default Button;