Interactive Components
Interactive components focus on user interaction and state management. They handle user input, provide feedback, and facilitate complex interactions throughout the BuildAppsWith platform.
Core Interactive Components
Interactive components are organized into several categories based on their functionality:
Form components are used to capture user input and manage form state:
| Component | Purpose | Variants | Status |
|---|
| Input | Text entry | Text, Number, Email, Password | Completed |
| Select | Option selection | Dropdown, Multi-select, Searchable | Completed |
| Checkbox | Boolean selection | Standard, Toggle, Card | Completed |
| Radio | Single selection | Standard, Card, Button Group | Completed |
| DatePicker | Date selection | Single, Range, Calendar | In Progress |
| FileUpload | File attachment | Drop Zone, Button, Preview | In Progress |
| Form | Complete form with validation | Standard, Multi-step, Wizard | Completed |
All form components are integrated with React Hook Form and Zod for validation.
// Example Form with validation
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Form, Input, Button } from '@/components/ui/form';
// Define schema with Zod
const schema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
function LoginForm() {
const form = useForm({
resolver: zodResolver(schema)
});
return (
<Form form={form} onSubmit={values => console.log(values)}>
<Input name="email" label="Email" />
<Input name="password" label="Password" type="password" />
<Button type="submit">Log In</Button>
</Form>
);
}
Navigation Components
Navigation components help users move between different sections of the application:
| Component | Purpose | Variants | Status |
|---|
| Tabs | Section navigation | Horizontal, Vertical, Underlined | Completed |
| Menu | Option selection | Dropdown, Hamburger, Sidebar | Completed |
| Pagination | Page navigation | Numbers, Prev/Next, Infinite | Completed |
| Breadcrumbs | Location tracking | Standard, Collapsed | Completed |
| Stepper | Multi-step process | Horizontal, Vertical, Numbered | In Progress |
| Nav Link | Navigation link | Standard, Active, Icon | Completed |
// Example Tabs Component
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
function ProfileTabs() {
return (
<Tabs defaultValue="portfolio">
<TabsList>
<TabsTrigger value="portfolio">Portfolio</TabsTrigger>
<TabsTrigger value="reviews">Reviews</TabsTrigger>
<TabsTrigger value="availability">Availability</TabsTrigger>
</TabsList>
<TabsContent value="portfolio">
{/* Portfolio content */}
</TabsContent>
<TabsContent value="reviews">
{/* Reviews content */}
</TabsContent>
<TabsContent value="availability">
{/* Availability content */}
</TabsContent>
</Tabs>
);
}
Overlay Components
Overlay components display content above the main interface:
| Component | Purpose | Variants | Status |
|---|
| Modal | Focused user interaction | Standard, Large, Confirmation | Completed |
| Dialog | Confirmation or info | Alert, Confirmation, Form | Completed |
| Drawer | Side panel content | Left, Right, Bottom | Completed |
| Popover | Contextual information | Tooltip, Menu, Info | Completed |
| Toast | Temporary notifications | Success, Error, Info, Warning | Completed |
| CommandPalette | Keyboard command access | Standard, Search | In Progress |
Overlay components should be used sparingly and should not interrupt the user’s flow unnecessarily.
// Example Modal Component
import { Modal, ModalContent, ModalHeader, ModalFooter, Button } from '@/components/ui/modal';
function DeleteConfirmation() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="destructive" onClick={() => setOpen(true)}>
Delete Account
</Button>
<Modal open={open} onOpenChange={setOpen}>
<ModalHeader>Confirm Account Deletion</ModalHeader>
<ModalContent>
Are you sure you want to delete your account? This action cannot be undone.
</ModalContent>
<ModalFooter>
<Button variant="ghost" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button variant="destructive" onClick={handleDelete}>
Delete
</Button>
</ModalFooter>
</Modal>
</>
);
}
Data Display Components
Components that display and interact with data:
| Component | Purpose | Variants | Status |
|---|
| Table | Structured data display | Sortable, Filterable, Paginated | Completed |
| List | Sequential data display | Ordered, Unordered, Action | Completed |
| Calendar | Date-based data display | Month, Week, Day, Agenda | In Progress |
| Chart | Data visualization | Bar, Line, Pie, Area | Planned |
| DataGrid | Complex data manipulation | Editable, Sortable, Filterable | Planned |
| TreeView | Hierarchical data | Expandable, Selectable | In Progress |
State Management
Interactive components follow consistent state management patterns:
Component State
Local component state is managed using React’s useState hook:
// Local component state
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<Button onClick={() => setCount(count + 1)}>Increment</Button>
</div>
);
}
Form state is managed using React Hook Form:
// Form state management
function ProfileForm() {
const { control, handleSubmit, formState: { errors } } = useForm({
defaultValues: {
name: '',
email: '',
bio: ''
}
});
const onSubmit = (data) => {
// Handle form submission
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* Form fields */}
</form>
);
}
Shared State
Shared state across components is managed using React Context:
// Context provider
export const FilterContext = createContext();
export function FilterProvider({ children }) {
const [filters, setFilters] = useState({});
return (
<FilterContext.Provider value={{ filters, setFilters }}>
{children}
</FilterContext.Provider>
);
}
// Context consumer
function FilterPanel() {
const { filters, setFilters } = useContext(FilterContext);
// Use shared filter state
}
Interaction Patterns
Loading States
Interactive components handle loading states:
// Loading state example
function SubmitButton({ isLoading }) {
return (
<Button disabled={isLoading}>
{isLoading ? (
<>
<Spinner className="mr-2 h-4 w-4" />
<span>Loading...</span>
</>
) : (
<span>Submit</span>
)}
</Button>
);
}
Error Handling
Components handle errors gracefully:
// Error handling example
function SearchForm() {
const [error, setError] = useState(null);
const handleSearch = async (query) => {
try {
const results = await fetchSearchResults(query);
// Handle results
} catch (err) {
setError('Search failed. Please try again.');
}
};
return (
<div>
{error && (
<Alert variant="destructive" className="mb-4">
<AlertTitle>Error</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Search form */}
</div>
);
}
Validation Feedback
Components provide immediate validation feedback:
// Validation feedback example
function EmailInput() {
const [value, setValue] = useState('');
const [error, setError] = useState('');
const validateEmail = (email) => {
if (!email) {
setError('Email is required');
return false;
}
if (!/\S+@\S+\.\S+/.test(email)) {
setError('Invalid email format');
return false;
}
setError('');
return true;
};
return (
<div>
<Label htmlFor="email">Email</Label>
<Input
id="email"
value={value}
onChange={(e) => {
setValue(e.target.value);
validateEmail(e.target.value);
}}
onBlur={() => validateEmail(value)}
aria-invalid={Boolean(error)}
aria-describedby={error ? "email-error" : undefined}
/>
{error && (
<p id="email-error" className="text-red-500 text-sm mt-1">
{error}
</p>
)}
</div>
);
}
Accessibility
Interactive components have enhanced accessibility requirements:
- Keyboard Navigation: Fully keyboard accessible with logical tab order
- ARIA Attributes: Proper ARIA roles, states, and properties
- Focus Management: Proper focus trapping in modals and dialogs
- Live Regions: Announcements for dynamic content changes
- Error Identification: Clear error messages with proper associations
// Accessibility in dialogs example
function AccessibleDialog({ title, isOpen, onClose, children }) {
// Return focus to trigger element when dialog closes
const previousFocus = React.useRef(null);
React.useEffect(() => {
if (isOpen) {
previousFocus.current = document.activeElement;
} else if (previousFocus.current) {
previousFocus.current.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
className="fixed inset-0 z-50 flex items-center justify-center"
>
<div className="fixed inset-0 bg-black bg-opacity-50" onClick={onClose} />
<div className="bg-white rounded-lg p-6 z-10 max-w-md w-full">
<h2 id="dialog-title" className="text-xl font-semibold mb-4">
{title}
</h2>
<div>{children}</div>
<button
className="mt-4 px-4 py-2 bg-primary text-white rounded"
onClick={onClose}
>
Close
</button>
</div>
</div>
);
}
Testing
Interactive components require thorough testing:
// Component test example
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click Me</Button>);
fireEvent.click(screen.getByText('Click Me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Disabled</Button>);
expect(screen.getByText('Disabled')).toBeDisabled();
});
});
See Also