SiamCafe.net Blog
Cybersecurity

Shadcn UI Automation Script

shadcn ui automation script
Shadcn UI Automation Script | SiamCafe Blog
2025-11-08· อ. บอม — SiamCafe.net· 10,761 คำ

Shadcn UI คืออะไร

Shadcn UI เป็น Component Library แนวใหม่ที่ไม่ได้ติดตั้งเป็น npm package แต่ Copy Source Code ลงในโปรเจคโดยตรง ทำให้ Developer มี Full Control สามารถปรับแต่ง Component ได้ตามต้องการ สร้างบน Radix UI Primitives (Accessible) และ Tailwind CSS (Styling)

การใช้ Automation Scripts ร่วมกับ Shadcn UI ช่วยเพิ่มประสิทธิภาพในการ Setup Project, Generate Components หลายตัวพร้อมกัน, Customize Theme อัตโนมัติ และสร้าง Documentation จาก Components

Automation Script — Setup และ Generate Components

#!/bin/bash
# shadcn-setup.sh — Automation Script สำหรับ Shadcn UI Setup
# ใช้กับ Next.js + TypeScript + Tailwind CSS

set -e

PROJECT_NAME=
echo "=== Setting up Shadcn UI Project: $PROJECT_NAME ==="

# 1. สร้าง Next.js Project
npx create-next-app@latest "$PROJECT_NAME" \
  --typescript --tailwind --eslint --app \
  --src-dir --import-alias "@/*" --no-git

cd "$PROJECT_NAME"

# 2. Initialize Shadcn UI
npx shadcn-ui@latest init -y \
  --style default \
  --base-color slate \
  --css-variables

# 3. ติดตั้ง Essential Components
COMPONENTS=(
  "button"
  "input"
  "label"
  "card"
  "dialog"
  "dropdown-menu"
  "table"
  "tabs"
  "toast"
  "form"
  "select"
  "badge"
  "alert"
  "avatar"
  "separator"
  "skeleton"
  "sheet"
  "command"
  "popover"
  "tooltip"
)

echo "Installing  components..."
for comp in ""; do
  echo "  Adding: $comp"
  npx shadcn-ui@latest add "$comp" -y 2>/dev/null || echo "  Skipped: $comp"
done

# 4. ติดตั้ง Dependencies เพิ่มเติม
npm install lucide-react @tanstack/react-table date-fns zod \
  @hookform/resolvers react-hook-form next-themes

# 5. สร้าง Theme Provider
mkdir -p src/components
cat > src/components/theme-provider.tsx << 'THEME_EOF'
"use client"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return {children}
}
THEME_EOF

# 6. สร้าง Toaster Component
cat > src/components/toaster.tsx << 'TOAST_EOF'
"use client"
import { Toaster as SonnerToaster } from "sonner"

export function Toaster() {
  return 
}
TOAST_EOF

echo ""
echo "=== Setup Complete ==="
echo "Components installed: "
echo "Run: cd $PROJECT_NAME && npm run dev"

Python Script — Component Generator

# shadcn_generator.py — Generate Custom Components จาก Template
import os
import json
import subprocess
from pathlib import Path
from dataclasses import dataclass
from typing import List, Optional

@dataclass
class ComponentSpec:
    name: str
    description: str
    props: dict
    shadcn_deps: List[str]
    template: str = "default"

class ShadcnComponentGenerator:
    """Generate Custom Components บน Shadcn UI"""

    def __init__(self, project_path):
        self.project_path = Path(project_path)
        self.components_dir = self.project_path / "src" / "components"
        self.ui_dir = self.components_dir / "ui"

    def ensure_shadcn_deps(self, deps: List[str]):
        """ติดตั้ง Shadcn Components ที่จำเป็น"""
        for dep in deps:
            dep_file = self.ui_dir / f"{dep}.tsx"
            if not dep_file.exists():
                print(f"  Installing shadcn component: {dep}")
                subprocess.run(
                    ["npx", "shadcn-ui@latest", "add", dep, "-y"],
                    cwd=str(self.project_path),
                    capture_output=True,
                )

    def generate_data_table(self, name="data-table", columns=None):
        """Generate Data Table Component"""
        self.ensure_shadcn_deps(["table", "button", "input",
                                  "dropdown-menu", "badge"])

        component_code = '''
"use client"

import * as React from "react"
import {
  ColumnDef,
  ColumnFiltersState,
  SortingState,
  VisibilityState,
  flexRender,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
} from "@tanstack/react-table"
import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
  Table, TableBody, TableCell, TableHead,
  TableHeader, TableRow,
} from "@/components/ui/table"
import {
  DropdownMenu, DropdownMenuCheckboxItem,
  DropdownMenuContent, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

interface DataTableProps {
  columns: ColumnDef[]
  data: TData[]
  searchKey?: string
  searchPlaceholder?: string
}

export function DataTable({
  columns, data, searchKey, searchPlaceholder = "Search...",
}: DataTableProps) {
  const [sorting, setSorting] = React.useState([])
  const [columnFilters, setColumnFilters] =
    React.useState([])
  const [columnVisibility, setColumnVisibility] =
    React.useState({})
  const [rowSelection, setRowSelection] = React.useState({})

  const table = useReactTable({
    data, columns,
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    onColumnVisibilityChange: setColumnVisibility,
    onRowSelectionChange: setRowSelection,
    state: { sorting, columnFilters, columnVisibility, rowSelection },
  })

  return (
    
{searchKey && ( table.getColumn(searchKey) ?.setFilterValue(e.target.value)} className="max-w-sm" /> )} {table.getAllColumns() .filter((col) => col.getCanHide()) .map((col) => ( col.toggleVisibility(!!v)} > {col.id} ))}
{table.getHeaderGroups().map((hg) => ( {hg.headers.map((h) => ( {h.isPlaceholder ? null : flexRender(h.column.columnDef.header, h.getContext())} ))} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( No results. )}
{table.getFilteredSelectedRowModel().rows.length} of{" "} {table.getFilteredRowModel().rows.length} row(s) selected.
) } ''' output_path = self.components_dir / f"{name}.tsx" output_path.write_text(component_code.strip(), encoding="utf-8") print(f"Generated: {output_path}") def generate_component_index(self): """สร้าง Index File สำหรับ Export ทุก Components""" ui_files = sorted(self.ui_dir.glob("*.tsx")) exports = [] for f in ui_files: name = f.stem exports.append(f'export * from "./ui/{name}"') index_path = self.components_dir / "index.ts" index_path.write_text("\n".join(exports), encoding="utf-8") print(f"Generated index: {index_path} ({len(exports)} exports)") def audit_accessibility(self): """ตรวจสอบ Accessibility ของ Components""" issues = [] for f in self.ui_dir.glob("*.tsx"): content = f.read_text(encoding="utf-8", errors="ignore") # Check for aria labels if "6} bytes") # ตัวอย่าง # gen = ShadcnComponentGenerator("/path/to/project") # gen.list_components() # gen.generate_data_table() # gen.generate_component_index() # gen.audit_accessibility()

CI/CD Script สำหรับ Component Testing

# === GitHub Actions — Shadcn UI CI/CD ===
# .github/workflows/ui-test.yml

name: UI Component Tests
on:
  push:
    branches: [main]
    paths: ["src/components/**"]
  pull_request:
    branches: [main]
    paths: ["src/components/**"]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - run: npm ci

      # Lint
      - name: ESLint
        run: npx eslint src/components/ --ext .tsx,.ts

      # Type Check
      - name: TypeScript
        run: npx tsc --noEmit

      # Unit Tests
      - name: Jest
        run: npx jest --coverage --testPathPattern=components

      # Accessibility
      - name: Axe Accessibility
        run: |
          npx playwright install chromium
          npx playwright test --project=accessibility

      # Storybook Build (ถ้ามี)
      - name: Build Storybook
        run: npx storybook build --quiet
        if: hashFiles('**/.storybook') != ''

      # Visual Regression (ถ้ามี)
      - name: Visual Tests
        run: npx playwright test --project=visual
        if: hashFiles('**/visual.spec.ts') != ''

# === package.json scripts ===
# {
#   "scripts": {
#     "shadcn:add": "npx shadcn-ui@latest add",
#     "shadcn:diff": "npx shadcn-ui@latest diff",
#     "shadcn:audit": "python3 scripts/shadcn_generator.py audit",
#     "test:ui": "jest --testPathPattern=components",
#     "test:a11y": "playwright test --project=accessibility",
#     "lint:ui": "eslint src/components/ --ext .tsx,.ts"
#   }
# }

# === Playwright Accessibility Test ===
# tests/accessibility.spec.ts
# import { test, expect } from '@playwright/test'
# import AxeBuilder from '@axe-core/playwright'
#
# test.describe('Component Accessibility', () => {
#   test('Button should be accessible', async ({ page }) => {
#     await page.goto('/storybook/button')
#     const results = await new AxeBuilder({ page }).analyze()
#     expect(results.violations).toEqual([])
#   })
#
#   test('Dialog should be accessible', async ({ page }) => {
#     await page.goto('/storybook/dialog')
#     const results = await new AxeBuilder({ page }).analyze()
#     expect(results.violations).toEqual([])
#   })
# })

Best Practices

Shadcn UI คืออะไร

Component Library สำหรับ React ที่ Copy Source Code ลงโปรเจคโดยตรง ปรับแต่งได้เต็มที่ สร้างบน Radix UI Primitives และ Tailwind CSS มี CLI สำหรับเพิ่ม Components อัตโนมัติ

ทำไมต้องใช้ Automation Script กับ Shadcn UI

Setup เร็วขึ้น Generate Components หลายตัวพร้อมกัน Customize Theme อัตโนมัติ สร้าง Documentation อัตโนมัติ Accessibility Audit และ Integration Testing ลดเวลาจากหลายชั่วโมงเหลือไม่กี่นาที

วิธีติดตั้ง Shadcn UI ทำอย่างไร

npx shadcn-ui@latest init ตอบ Configuration เช่น Style Base Color CSS Variables เพิ่ม Components ด้วย npx shadcn-ui@latest add button dialog table Components ถูก Copy ลง src/components/ui/

Shadcn UI รองรับ Framework อะไรบ้าง

Next.js, Remix, Vite, Astro, Laravel, Gatsby ใช้กับ React และ Tailwind CSS มี Port สำหรับ Vue (shadcn-vue) Svelte (shadcn-svelte) และ Angular ด้วย

สรุป

Shadcn UI เป็น Component Library ที่ให้ Full Control แก่ Developer Automation Scripts ช่วยเพิ่มประสิทธิภาพในการ Setup, Generate Components, Audit Accessibility และ Testing ใช้ CI/CD Pipeline ทดสอบ Components อัตโนมัติทุก PR สิ่งสำคัญคือ Customize ได้เต็มที่เพราะเป็น Source Code ของคุณเอง

📖 บทความที่เกี่ยวข้อง

Prometheus Alertmanager Automation Scriptอ่านบทความ → Great Expectations Automation Scriptอ่านบทความ → Airbyte ETL Automation Scriptอ่านบทความ → Linkerd Service Mesh Compliance Automationอ่านบทความ → Qwik Resumability Compliance Automationอ่านบทความ →

📚 ดูบทความทั้งหมด →