289 lines
6.3 KiB
Markdown
Executable File
289 lines
6.3 KiB
Markdown
Executable File
# 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>
|
|
```
|
|
|
|
|