Wip on homepage.
Add documentation how to optimize code size in production.
This commit is contained in:
288
docs/creating-shadcn-react-components.md
Normal file
288
docs/creating-shadcn-react-components.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# Creating New Shadcn and React Components
|
||||
|
||||
This guide explains how to create new Shadcn (UI) components and React components in this Rails application with React frontend.
|
||||
|
||||
## Overview
|
||||
|
||||
This project uses:
|
||||
- **Shadcn/ui** for UI components (built on Radix UI and Tailwind CSS)
|
||||
- **React** for frontend components
|
||||
- **Rails** as the backend framework
|
||||
- **esbuild** for JavaScript bundling
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
app/
|
||||
├── javascript/
|
||||
│ ├── components/
|
||||
│ │ └── ui/ # Shadcn components
|
||||
│ └── controllers/ # React controllers
|
||||
├── views/
|
||||
│ └── components/ # Rails view components
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Creating Shadcn Components
|
||||
|
||||
### 1. Using the Shadcn CLI
|
||||
|
||||
The easiest way to add new Shadcn components is using the CLI:
|
||||
|
||||
```bash
|
||||
# Navigate to the project root
|
||||
cd /home/acid/Documents/aperonight
|
||||
|
||||
# Add a new component (example: adding a card)
|
||||
npx shadcn-ui@latest add card
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install the component to `app/javascript/components/ui/`
|
||||
- Update the components.json configuration
|
||||
- Create the necessary TypeScript/JavaScript files
|
||||
|
||||
### 2. Manual Component Creation
|
||||
|
||||
If the CLI is not available, create components manually:
|
||||
|
||||
#### Create the component file
|
||||
```bash
|
||||
# Create a new component (example: button.jsx)
|
||||
touch app/javascript/components/ui/button.jsx
|
||||
```
|
||||
|
||||
#### Basic component structure
|
||||
```javascript
|
||||
// app/javascript/components/ui/button.jsx
|
||||
import * as React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Button = React.forwardRef(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Button.displayName = "Button"
|
||||
|
||||
export { Button }
|
||||
```
|
||||
|
||||
## Creating React Components
|
||||
|
||||
### 1. Controller-Based Components
|
||||
|
||||
For components that need Rails integration:
|
||||
|
||||
#### Create controller file
|
||||
```bash
|
||||
# Create a new controller
|
||||
touch app/javascript/controllers/my_component_controller.js
|
||||
```
|
||||
|
||||
#### Basic controller structure
|
||||
```javascript
|
||||
// app/javascript/controllers/my_component_controller.js
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["container"]
|
||||
|
||||
connect() {
|
||||
const root = ReactDOM.createRoot(this.containerTarget)
|
||||
root.render(<MyComponent />)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Standalone React Components
|
||||
|
||||
For reusable React components:
|
||||
|
||||
#### Create component file
|
||||
```bash
|
||||
# Create a new React component
|
||||
touch app/javascript/components/MyNewComponent.jsx
|
||||
```
|
||||
|
||||
#### Component structure
|
||||
```javascript
|
||||
// app/javascript/components/MyNewComponent.jsx
|
||||
import React from "react"
|
||||
|
||||
const MyNewComponent = ({ title, description }) => {
|
||||
return (
|
||||
<div className="p-4 border rounded-lg">
|
||||
<h2 className="text-lg font-semibold">{title}</h2>
|
||||
<p className="text-gray-600">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyNewComponent
|
||||
```
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### 1. Using in Rails Views
|
||||
|
||||
To use components in Rails views:
|
||||
|
||||
#### Create partial
|
||||
```erb
|
||||
<!-- app/views/components/_my_component.html.erb -->
|
||||
<div data-controller="my-component">
|
||||
<div data-my-component-target="container"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Include in page
|
||||
```erb
|
||||
<!-- app/views/pages/home.html.erb -->
|
||||
<%= render "components/my_component" %>
|
||||
```
|
||||
|
||||
### 2. Direct React Rendering
|
||||
|
||||
For pages that are primarily React:
|
||||
|
||||
#### Create page component
|
||||
```javascript
|
||||
// app/javascript/components/pages/HomePage.jsx
|
||||
import React from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
const HomePage = () => {
|
||||
return (
|
||||
<div className="container mx-auto">
|
||||
<h1>Welcome</h1>
|
||||
<Button>Get Started</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomePage
|
||||
```
|
||||
|
||||
## Configuration Updates
|
||||
|
||||
### 1. Update components.json
|
||||
```json
|
||||
{
|
||||
"style": "default",
|
||||
"rsc": false,
|
||||
"tsx": false,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "app/assets/stylesheets/application.postcss.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true
|
||||
},
|
||||
"aliases": {
|
||||
"components": "app/javascript/components",
|
||||
"utils": "app/javascript/lib/utils"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Update JavaScript entry point
|
||||
```javascript
|
||||
// app/javascript/application.js
|
||||
import "./components"
|
||||
import "./controllers"
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Shadcn Components
|
||||
- Use kebab-case for filenames: `button.jsx`, `card.jsx`
|
||||
- Use PascalCase for exports: `export { Button }`
|
||||
- Follow Radix UI naming patterns
|
||||
|
||||
### React Components
|
||||
- Use PascalCase for filenames: `MyComponent.jsx`
|
||||
- Use PascalCase for components: `const MyComponent = () => {}`
|
||||
- Use camelCase for props: `myProp`, `onClick`
|
||||
|
||||
## Testing Components
|
||||
|
||||
### 1. Create test file
|
||||
```bash
|
||||
# Create test file
|
||||
touch test/components/my_component_test.rb
|
||||
```
|
||||
|
||||
### 2. Write component test
|
||||
```javascript
|
||||
// test/components/my_component_test.jsx
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import MyComponent from "../../app/javascript/components/MyComponent"
|
||||
|
||||
test("renders component", () => {
|
||||
render(<MyComponent title="Test" />)
|
||||
expect(screen.getByText("Test")).toBeInTheDocument()
|
||||
})
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### 1. Props Pattern
|
||||
```javascript
|
||||
// Pass Rails data as props
|
||||
const MyComponent = ({ user, config }) => {
|
||||
return <div>{user.name}</div>
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Event Handling
|
||||
```javascript
|
||||
// Handle events from Rails
|
||||
const MyComponent = ({ onAction }) => {
|
||||
return <button onClick={onAction}>Click me</button>
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Styling Integration
|
||||
```javascript
|
||||
// Use Tailwind classes
|
||||
const MyComponent = () => {
|
||||
return <div className="bg-white dark:bg-gray-800">Content</div>
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Component not rendering**: Check controller connection
|
||||
2. **Styling issues**: Verify Tailwind classes
|
||||
3. **Props not passing**: Check data-controller attributes
|
||||
4. **Import errors**: Verify alias paths in components.json
|
||||
|
||||
### Debug Steps
|
||||
|
||||
1. Check browser console for errors
|
||||
2. Verify component file exists in correct location
|
||||
3. Check import paths in application.js
|
||||
4. Verify Rails view includes correct data attributes
|
||||
|
||||
|
||||
## Example created for testing purpose
|
||||
|
||||
```html
|
||||
<!-- Shadcn Button Test -->
|
||||
<div data-controller="shadcn-test" class="mt-4">
|
||||
<div data-shadcn-test-target="container"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user