consensys-ui
Version:
Consensys UI component library and design system
104 lines (80 loc) • 3.51 kB
Markdown
# Contributing Guidelines
## Compound Component Pattern
When implementing new components, prefer using the [compound component pattern](https://www.patterns.dev/react/compound-pattern/) for complex UI elements that have multiple related parts.
### Implementation Guidelines
1. **Create a Context**: Define a context to share state between the parent and child components:
```typescript
// components/example/context.ts
export const ExampleContext = createContext<ExampleContextType | null>(null);
export function useExampleContext() {
const context = useContext(ExampleContext);
if (!context) {
throw new Error('useExampleContext must be used within an ExampleProvider');
}
return context;
}
```
2. **Implement Child Components**: Create each part that consumes the shared context:
```typescript
// components/example/Example.shared.tsx
export const ExamplePart = () => {
const context = useExampleContext();
// Use context values to render the part
// ...
};
```
3. **Combine with Object.assign**: Attach child components to the parent component:
```typescript
// components/example/Example.tsx
const ExampleRoot = (props) => {
// Create and provide context
// Render the component
};
const Example = Object.assign(ExampleRoot, {
Part: ExamplePart,
AnotherPart: ExampleAnotherPart,
});
export { Example };
```
4. **Export as Named Export**: Follow our export strategy for the new compound component:
```typescript
// components/example/index.ts
export { Example } from './Example';
export type { ExampleProps } from './types';
```
### Best Practices
- **Context Memoization**: Memoize context values to prevent unnecessary re-renders
- **Forwarding Refs**: Use `forwardRef` for all components to support ref forwarding
- **Part Naming**: Name child components with the parent name as prefix (`Button.Text` not `Text`)
- **Type Safety**: Provide proper TypeScript types for the context and all components
- **Documentation**: Document both simple and compound usage patterns
### Real Component Examples
For reference, check these implementations:
- `Button` and its child components (`Button.Text`, `Button.Icon`, etc.)
- `Input` and its parts
- `Select` and its dropdown components
## Export Strategy
This package uses a consistent export strategy to ensure optimal tree-shaking and clear public API boundaries:
1. **Named Exports Only**: All exports are named exports to enable better tree-shaking.
2. **Component-Level Exports**: Each component directory exports everything it needs internally:
```typescript
// components/button/index.ts
export { Button } from './button';
export { IconButton } from './icon-button';
export { useButtonContext } from './context';
export type { ButtonProps, IconButtonProps } from './types';
// ... other internal exports
```
3. **Public API**: The root `src/index.ts` file serves as the public API, carefully selecting which exports to expose:
```typescript
// src/index.ts
export { Button } from './components/button/button';
export { IconButton } from './components/button/icon-button';
export type { ButtonRootProps as ButtonProps } from './components/button/types';
// ... other public exports
```
This approach provides several benefits:
- Clear separation between internal and public APIs
- Optimal tree-shaking as all exports are named
- Components can use their full internal API while maintaining a clean public interface
- Easy to track and maintain the public API surface