Skip to main content

TextDAO Frontend UI Component Development Guide

This guide outlines the process and best practices for developing UI components in the TextDAO frontend application. It covers component creation, styling, testing, and documentation.

Component Creation Process

  1. Plan: Determine the purpose and requirements of the component.
  2. Design: Create a basic design or use existing design specifications.
  3. Implement: Write the component code.
  4. Test: Write and run unit tests for the component.
  5. Document: Add inline documentation and update any relevant documentation files.
  6. Review: Submit the component for code review.
  7. Iterate: Make any necessary changes based on feedback.

Component Structure

Use the following structure for new components:

import React from 'react';
import { cn } from '@/lib/utils';

export interface MyComponentProps {
// Define props here
}

export const MyComponent: React.FC<MyComponentProps> = ({
// Destructure props here
}) => {
// Component logic here

return (
// JSX here
);
};

Styling

Use Tailwind CSS for styling components. Apply classes directly in the JSX:

<div className="flex items-center justify-between p-4 bg-white rounded-lg shadow">
{/* Component content */}
</div>

For conditional styling, use the cn utility function:

<button
className={cn(
"px-4 py-2 rounded-md font-medium",
isActive ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-800"
)}
>
{children}
</button>

State Management

  • Use React hooks for local state management.
  • For more complex state, consider using React Context or moving the state to a parent component.

Example:

const [isOpen, setIsOpen] = useState(false);

const toggleOpen = () => setIsOpen(!isOpen);

Props and TypeScript

  • Define prop types using TypeScript interfaces.
  • Use descriptive names for props and provide default values where appropriate.

Example:

export interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
onClick?: () => void;
children: React.ReactNode;
}

export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'medium',
onClick,
children,
}) => {
// Component implementation
};

Accessibility

  • Ensure components are keyboard accessible.
  • Use semantic HTML elements where possible.
  • Add appropriate ARIA attributes when necessary.
  • Maintain sufficient color contrast for text and important UI elements.

Example:

<button
aria-label="Close dialog"
onClick={onClose}
className="p-2 text-gray-500 hover:text-gray-700"
>
<span className="sr-only">Close</span>
<XIcon className="w-5 h-5" />
</button>

Testing

Use React Testing Library for component testing. Write tests that:

  1. Render the component with various props.
  2. Simulate user interactions.
  3. Check for expected output or behavior.

Example test file (MyComponent.test.tsx):

import { render, screen, fireEvent } from '@testing-library/react';
import { MyComponent } from './MyComponent';

describe('MyComponent', () => {
it('renders correctly with default props', () => {
render(<MyComponent />);
// Add assertions here
});

it('handles user interaction correctly', () => {
const handleClick = jest.fn();
render(<MyComponent onClick={handleClick} />);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});

Documentation

Use JSDoc comments to document your components:

/**
* MyComponent displays...
*
* @param {Object} props - The component props
* @param {string} props.title - The title to display
* @param {() => void} [props.onClick] - Optional click handler
*
* @example
* <MyComponent title="Hello World" onClick={() => console.log('Clicked')} />
*/
export const MyComponent: React.FC<MyComponentProps> = ({ title, onClick }) => {
// Component implementation
};

Performance Optimization

  • Use React.memo for components that render often but rarely change.
  • Avoid unnecessary re-renders by carefully managing component props and state.
  • Use the React DevTools Profiler to identify and resolve performance bottlenecks.

Example of using React.memo:

export const MyComponent = React.memo(({ title, onClick }) => {
// Component implementation
});

Reusability and Composition

  • Design components to be reusable across different parts of the application.
  • Use composition to build complex components from simpler ones.
  • Implement the "children" prop to make components more flexible.

Example:

export const Card: React.FC<CardProps> = ({ title, children }) => (
<div className="p-4 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-2">{title}</h2>
{children}
</div>
);

// Usage
<Card title="User Profile">
<UserAvatar />
<UserDetails />
</Card>

Error Handling

  • Implement error boundaries to catch and handle errors in components.
  • Provide meaningful error messages and fallback UI when errors occur.

Example error boundary:

class ErrorBoundary extends React.Component<{ fallback: React.ReactNode }> {
state = { hasError: false };

static getDerivedStateFromError() {
return { hasError: true };
}

render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}

// Usage
<ErrorBoundary fallback={<ErrorMessage />}>
<MyComponent />
</ErrorBoundary>

Code Review Checklist

When submitting a component for review, ensure:

  1. The component follows the structure and naming conventions outlined in this guide.
  2. Props are properly typed and documented.
  3. The component is accessible and follows WCAG guidelines.
  4. Unit tests cover the main functionality and edge cases.
  5. The component is optimized for performance where necessary.
  6. Documentation is clear and up-to-date.

By following these guidelines, we can ensure that our UI components are consistent, maintainable, and of high quality. Remember to stay updated with the latest React best practices and continuously refine our development process.