chore(design-system): generate and ignore registry output (#49290)

## What kind of change does this PR introduce?

Chore. Stops committing generated design-system registry output
([DEPR-647](https://linear.app/supabase/issue/DEPR-647/generate-and-ignore-design-system-registry-output)).

## What is the current behavior?

`apps/design-system/__registry__` is build output from `registry/`, but
the chart snapshots and index are committed. That makes reviews noisy,
and a forgotten `build:registry` leaves `master` out of date until
someone else regenerates it.

## What is the new behavior?

`pnpm dev` and `pnpm typecheck` generate `__registry__` automatically.
The directory is gitignored, and the previously tracked snapshots are
removed. `pnpm build` still generates it as before.

## To test

- From the repo root, run `pnpm --filter=design-system
generate:registry` and confirm
`apps/design-system/__registry__/index.tsx` is created locally and is
untracked.
- Run `pnpm dev:design-system`, open
[http://localhost:3003](http://localhost:3003), and open any component
docs page with a live preview (for example Charts). Previews and source
panels should still load.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Clarified when the component registry is generated and how to
regenerate it.
* Expanded component documentation guidance, including content sources
and generated-file editing restrictions.

* **Chores**
* Improved registry generation across development, type checking,
builds, and cleanup.
* Generated registry files are now excluded from version control and
linting.
* Improved reliability when creating and refreshing generated registry
files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Danny White authored and GitHub committed 2026-08-20 18:34:46 +10:00
1 parent 9ae6e54dd5
commit facc2df09e
13 files changed
+39 -4636

No files matched your search

+4 -1
View File
@@ -35,4 +35,7 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
.contentlayer
.contentlayer
# Generated by `pnpm generate:registry` (`scripts/build-registry.mts`)
/__registry__
+20 -24
View File
@@ -12,13 +12,15 @@ pnpm i
pnpm dev
```
The `dev` command runs both the Next.js development server and Contentlayer concurrently, which is recommended for most development workflows.
The `dev` command generates `__registry__`, then runs the Next.js development server and Contentlayer together. That is the recommended workflow.
### Alternative commands
You can also run the development server and content watcher separately:
You can also run the development server and content watcher separately. Generate the registry first, because `dev:next` and `dev:content` do not:
```bash
pnpm generate:registry
# Run only the Next.js development server
pnpm dev:next
@@ -26,19 +28,11 @@ pnpm dev:next
pnpm dev:content
```
Or run the development server from the root directory:
From the repo root, `pnpm dev:design-system` runs the same `dev` script, so it also generates `__registry__`. If you split the watchers from the root, generate first:
```bash
pnpm dev:design-system
```
To run both the development server and content watcher from the root directory, you can use:
```bash
# Run the development server
pnpm dev:design-system
# Run the content watcher (in a separate terminal shell)
pnpm --filter=design-system generate:registry
pnpm --filter=design-system dev:next
pnpm --filter=design-system dev:content
```
@@ -46,27 +40,29 @@ Open [http://localhost:3003](http://localhost:3003) in your browser to see the r
### Watching for MDX changes
The `dev` command automatically watches for changes to MDX files with hot reload. If you're running the `pnpm dev:next` separately, you'll need to run `pnpm dev:content` in a separate terminal shell to watch for content changes.
The `dev` command watches MDX files and hot-reloads them. If you are running `pnpm dev:next` on its own, also run `pnpm dev:content` in another terminal.
### Adding components
The design system _references_ components rather than housing them. That’s an important distinction to make, as everything that follows here is about the documentation of components. You can add or edit components in one of these two places:
The design system _references_ components rather than housing them. That distinction matters: everything below is about documenting components, not implementing them. Add or edit the components themselves in one of these two places:
- [`packages/ui`](https://github.com/supabase/supabase/tree/master/packages/ui): basic UI components
- [`packages/ui-patterns`](https://github.com/supabase/supabase/tree/master/packages/ui-patterns): components which are built using NPM libraries or amalgamations of components from `patterns/ui`
- [`packages/ui-patterns`](https://github.com/supabase/supabase/tree/master/packages/ui-patterns): components built from libraries or from `packages/ui`
There are several parts of this design system that need to be manually updated after components have been added or removed (from documentation). These include:
After you add or remove documented components, update these source files:
- `config/docs.ts`: list of components in the sidebar
- `content/docs`: the actual component documentation
- `registry/examples.ts`: list of example components
- `registry/fragments.ts`: list of fragment components
- `registry/charts.ts`: list of chart components
- `registry/default/example/*`: the actual example components
- `content/docs`: the component documentation
- `registry/examples.ts`: example components
- `registry/fragments.ts`: fragment components
- `registry/charts.ts`: chart components
- `registry/copy-writing.ts`: copywriting examples
- `registry/default/example/*`: the example component implementations
- `registry/default/block/*`: chart block implementations, when you add a chart
You will need to rebuild the design system’s registry after making new additions:
Do not edit `__registry__`. `pnpm dev`, `pnpm typecheck`, and `pnpm build` generate it from the files above, and it is gitignored. If you add registry entries while the app is already running, regenerate it:
```bash
cd apps/design-system
pnpm build:registry
pnpm generate:registry
```
@@ -1,211 +0,0 @@
'use client'
import * as React from 'react'
import { Bar, BarChart, CartesianGrid, XAxis } from 'recharts'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from 'ui'
export const description = 'An interactive bar chart'
const chartData = [
{ date: '2024-04-01', desktop: 222, mobile: 150 },
{ date: '2024-04-02', desktop: 97, mobile: 180 },
{ date: '2024-04-03', desktop: 167, mobile: 120 },
{ date: '2024-04-04', desktop: 242, mobile: 260 },
{ date: '2024-04-05', desktop: 373, mobile: 290 },
{ date: '2024-04-06', desktop: 301, mobile: 340 },
{ date: '2024-04-07', desktop: 245, mobile: 180 },
{ date: '2024-04-08', desktop: 409, mobile: 320 },
{ date: '2024-04-09', desktop: 59, mobile: 110 },
{ date: '2024-04-10', desktop: 261, mobile: 190 },
{ date: '2024-04-11', desktop: 327, mobile: 350 },
{ date: '2024-04-12', desktop: 292, mobile: 210 },
{ date: '2024-04-13', desktop: 342, mobile: 380 },
{ date: '2024-04-14', desktop: 137, mobile: 220 },
{ date: '2024-04-15', desktop: 120, mobile: 170 },
{ date: '2024-04-16', desktop: 138, mobile: 190 },
{ date: '2024-04-17', desktop: 446, mobile: 360 },
{ date: '2024-04-18', desktop: 364, mobile: 410 },
{ date: '2024-04-19', desktop: 243, mobile: 180 },
{ date: '2024-04-20', desktop: 89, mobile: 150 },
{ date: '2024-04-21', desktop: 137, mobile: 200 },
{ date: '2024-04-22', desktop: 224, mobile: 170 },
{ date: '2024-04-23', desktop: 138, mobile: 230 },
{ date: '2024-04-24', desktop: 387, mobile: 290 },
{ date: '2024-04-25', desktop: 215, mobile: 250 },
{ date: '2024-04-26', desktop: 75, mobile: 130 },
{ date: '2024-04-27', desktop: 383, mobile: 420 },
{ date: '2024-04-28', desktop: 122, mobile: 180 },
{ date: '2024-04-29', desktop: 315, mobile: 240 },
{ date: '2024-04-30', desktop: 454, mobile: 380 },
{ date: '2024-05-01', desktop: 165, mobile: 220 },
{ date: '2024-05-02', desktop: 293, mobile: 310 },
{ date: '2024-05-03', desktop: 247, mobile: 190 },
{ date: '2024-05-04', desktop: 385, mobile: 420 },
{ date: '2024-05-05', desktop: 481, mobile: 390 },
{ date: '2024-05-06', desktop: 498, mobile: 520 },
{ date: '2024-05-07', desktop: 388, mobile: 300 },
{ date: '2024-05-08', desktop: 149, mobile: 210 },
{ date: '2024-05-09', desktop: 227, mobile: 180 },
{ date: '2024-05-10', desktop: 293, mobile: 330 },
{ date: '2024-05-11', desktop: 335, mobile: 270 },
{ date: '2024-05-12', desktop: 197, mobile: 240 },
{ date: '2024-05-13', desktop: 197, mobile: 160 },
{ date: '2024-05-14', desktop: 448, mobile: 490 },
{ date: '2024-05-15', desktop: 473, mobile: 380 },
{ date: '2024-05-16', desktop: 338, mobile: 400 },
{ date: '2024-05-17', desktop: 499, mobile: 420 },
{ date: '2024-05-18', desktop: 315, mobile: 350 },
{ date: '2024-05-19', desktop: 235, mobile: 180 },
{ date: '2024-05-20', desktop: 177, mobile: 230 },
{ date: '2024-05-21', desktop: 82, mobile: 140 },
{ date: '2024-05-22', desktop: 81, mobile: 120 },
{ date: '2024-05-23', desktop: 252, mobile: 290 },
{ date: '2024-05-24', desktop: 294, mobile: 220 },
{ date: '2024-05-25', desktop: 201, mobile: 250 },
{ date: '2024-05-26', desktop: 213, mobile: 170 },
{ date: '2024-05-27', desktop: 420, mobile: 460 },
{ date: '2024-05-28', desktop: 233, mobile: 190 },
{ date: '2024-05-29', desktop: 78, mobile: 130 },
{ date: '2024-05-30', desktop: 340, mobile: 280 },
{ date: '2024-05-31', desktop: 178, mobile: 230 },
{ date: '2024-06-01', desktop: 178, mobile: 200 },
{ date: '2024-06-02', desktop: 470, mobile: 410 },
{ date: '2024-06-03', desktop: 103, mobile: 160 },
{ date: '2024-06-04', desktop: 439, mobile: 380 },
{ date: '2024-06-05', desktop: 88, mobile: 140 },
{ date: '2024-06-06', desktop: 294, mobile: 250 },
{ date: '2024-06-07', desktop: 323, mobile: 370 },
{ date: '2024-06-08', desktop: 385, mobile: 320 },
{ date: '2024-06-09', desktop: 438, mobile: 480 },
{ date: '2024-06-10', desktop: 155, mobile: 200 },
{ date: '2024-06-11', desktop: 92, mobile: 150 },
{ date: '2024-06-12', desktop: 492, mobile: 420 },
{ date: '2024-06-13', desktop: 81, mobile: 130 },
{ date: '2024-06-14', desktop: 426, mobile: 380 },
{ date: '2024-06-15', desktop: 307, mobile: 350 },
{ date: '2024-06-16', desktop: 371, mobile: 310 },
{ date: '2024-06-17', desktop: 475, mobile: 520 },
{ date: '2024-06-18', desktop: 107, mobile: 170 },
{ date: '2024-06-19', desktop: 341, mobile: 290 },
{ date: '2024-06-20', desktop: 408, mobile: 450 },
{ date: '2024-06-21', desktop: 169, mobile: 210 },
{ date: '2024-06-22', desktop: 317, mobile: 270 },
{ date: '2024-06-23', desktop: 480, mobile: 530 },
{ date: '2024-06-24', desktop: 132, mobile: 180 },
{ date: '2024-06-25', desktop: 141, mobile: 190 },
{ date: '2024-06-26', desktop: 434, mobile: 380 },
{ date: '2024-06-27', desktop: 448, mobile: 490 },
{ date: '2024-06-28', desktop: 149, mobile: 200 },
{ date: '2024-06-29', desktop: 103, mobile: 160 },
{ date: '2024-06-30', desktop: 446, mobile: 400 },
]
const chartConfig = {
views: {
label: 'Page Views',
},
desktop: {
label: 'Desktop',
color: 'hsl(var(--chart-1))',
},
mobile: {
label: 'Mobile',
color: 'hsl(var(--chart-2))',
},
} satisfies ChartConfig
export default function Component() {
const [activeChart, setActiveChart] = React.useState<keyof typeof chartConfig>('desktop')
const total = React.useMemo(
() => ({
desktop: chartData.reduce((acc, curr) => acc + curr.desktop, 0),
mobile: chartData.reduce((acc, curr) => acc + curr.mobile, 0),
}),
[]
)
return (
<Card>
<CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
<CardTitle>Bar Chart - Interactive</CardTitle>
<CardDescription>Showing total visitors for the last 3 months</CardDescription>
</div>
<div className="flex">
{['desktop', 'mobile'].map((key) => {
const chart = key as keyof typeof chartConfig
return (
<button
tabIndex={0}
key={chart}
data-active={activeChart === chart}
className="relative z-30 flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l data-[active=true]:bg-surface-100 sm:border-l sm:border-t-0 sm:px-8 sm:py-6"
onClick={() => setActiveChart(chart)}
>
<span className="text-xs text-foreground-lighter">{chartConfig[chart].label}</span>
<span className="text-lg font-bold leading-none sm:text-3xl">
{total[key as keyof typeof total].toLocaleString()}
</span>
</button>
)
})}
</div>
</CardHeader>
<CardContent className="px-2 sm:p-6">
<ChartContainer config={chartConfig} className="aspect-auto h-[250px] w-full">
<BarChart
accessibilityLayer
data={chartData}
margin={{
left: 12,
right: 12,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="date"
tickLine={false}
axisLine={false}
tickMargin={8}
minTickGap={32}
tickFormatter={(value) => {
const date = new Date(value)
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
})
}}
/>
<ChartTooltip
content={
<ChartTooltipContent
className="w-[150px]"
nameKey="views"
labelFormatter={(value) => {
return new Date(value).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})
}}
/>
}
/>
<Bar dataKey={activeChart} fill={`var(--color-${activeChart})`} />
</BarChart>
</ChartContainer>
</CardContent>
</Card>
)
}
@@ -1,106 +0,0 @@
'use client'
import { BarChart2, ExternalLink, LineChart } from 'lucide-react'
import { useEffect, useState } from 'react'
import {
Chart,
ChartActions,
ChartBar,
ChartCard,
ChartContent,
ChartEmptyState,
ChartHeader,
ChartLine,
ChartLoadingState,
ChartTitle,
} from 'ui-patterns/Chart'
export default function ChartComposedActions() {
const [isLoading, setIsLoading] = useState(true)
const [chartType, setChartType] = useState<'bar' | 'line'>('bar')
const actions = [
{
label: 'Open in Logs Explorer',
onClick: () => {
alert('Opening in Logs Explorer...')
},
icon: <ExternalLink size={12} />,
},
{
label: 'Toggle Chart Type',
onClick: () => {
setChartType(chartType === 'bar' ? 'line' : 'bar')
},
icon: chartType === 'bar' ? <LineChart size={12} /> : <BarChart2 size={12} />,
},
]
const data = Array.from({ length: 46 }, (_, i) => {
const date = new Date()
date.setMinutes(date.getMinutes() - i * 5) // Each point 5 minutes apart
return {
timestamp: date.toISOString(),
requests: Math.floor(Math.random() * 100),
}
}).reverse()
useEffect(() => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
}, [])
return (
<div className="flex flex-col gap-6 w-8/12">
<Chart isLoading={isLoading}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Total Requests</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
isEmpty={data.length === 0}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={16} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
<div className="h-40">
{chartType === 'bar' ? (
<ChartBar
data={data}
dataKey="requests"
showGrid={false}
showYAxis={true}
YAxisProps={{
tickFormatter: (value) => `${value}k`,
width: 80,
}}
isFullHeight={true}
/>
) : (
<ChartLine
data={data}
dataKey="requests"
showGrid={true}
showYAxis={true}
YAxisProps={{
tickFormatter: (value) => `${value}k`,
width: 80,
}}
isFullHeight={true}
/>
)}
</div>
</ChartContent>
</ChartCard>
</Chart>
</div>
)
}
@@ -1,142 +0,0 @@
'use client'
import { BarChart2, ExternalLink } from 'lucide-react'
import { useEffect, useState } from 'react'
import {
Chart,
ChartActions,
ChartBar,
ChartCard,
ChartContent,
ChartEmptyState,
ChartHeader,
ChartLine,
ChartLoadingState,
ChartTitle,
} from 'ui-patterns/Chart'
export default function ComposedChartBasic() {
const [isLoading, setIsLoading] = useState(true)
const actions = [
{
label: 'Open in Logs Explorer',
onClick: () => {
alert('Ta da! You clicked me! 🎉')
},
icon: <ExternalLink size={12} />,
},
]
const data = Array.from({ length: 40 }, (_, i) => {
const date = new Date()
date.setMinutes(date.getMinutes() - i * 3) // Each point 3 minutes apart
const progress = i / 40
const standard_score = Math.floor(55 + progress * 55 + (Math.random() - 0.5) * 12)
const performance = Math.floor(35 + progress * 35 + (Math.random() - 0.5) * 10)
const efficiency = Math.floor(25 + progress * 25 + (Math.random() - 0.5) * 12)
return {
timestamp: date.toISOString(),
standard_score: Math.max(0, Math.min(100, standard_score)),
performance: Math.max(0, Math.min(100, performance)),
efficiency: Math.max(0, Math.min(100, efficiency)),
}
}).reverse()
const chartConfig = {
standard_score: {
label: 'Standard Score',
color: 'hsl(var(--brand-default))',
},
performance: {
label: 'Performance',
color: 'hsl(var(--chart-2))',
},
efficiency: {
label: 'Efficiency',
color: 'hsl(var(--chart-5))',
},
}
useEffect(() => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
}, [])
return (
<div className="flex flex-col gap-6 w-8/12">
<Chart isLoading={isLoading}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Standard Bar Chart</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
isEmpty={data.length === 0}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={16} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
<div className="h-40">
<ChartBar
data={data}
dataKey="standard_score"
showGrid={false}
showYAxis={true}
YAxisProps={{
tickFormatter: (value) => `${value}k`,
width: 80,
}}
isFullHeight={true}
/>
</div>
</ChartContent>
</ChartCard>
</Chart>
<Chart isLoading={isLoading}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Standard Line Chart</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
isEmpty={data.length === 0}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={16} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
<div className="h-40">
<ChartLine
data={data}
dataKey="standard_score"
dataKeys={['standard_score', 'performance', 'efficiency']}
config={chartConfig}
showGrid={true}
showYAxis={true}
YAxisProps={{
tickFormatter: (value) => `${value}k`,
width: 80,
}}
isFullHeight={true}
/>
</div>
</ChartContent>
</ChartCard>
</Chart>
</div>
)
}
@@ -1,73 +0,0 @@
'use client'
import { BarChart2 } from 'lucide-react'
import { useEffect, useState } from 'react'
import {
Chart,
ChartCard,
ChartContent,
ChartEmptyState,
ChartHeader,
ChartLoadingState,
ChartMetric,
} from 'ui-patterns/Chart'
import { LogsBarChart } from 'ui-patterns/LogsBarChart'
export default function ComposedChartDemo() {
const [isLoading, setIsLoading] = useState(true)
const data = Array.from({ length: 60 }, (_, i) => {
const date = new Date()
date.setMinutes(date.getMinutes() - i * 5) // Each point 5 minutes apart
return {
timestamp: date.toISOString(),
ok_count: Math.floor(Math.random() * 100), // Random value 0-99
error_count: Math.floor(Math.random() * 50), // Random value 0-50
warning_count: Math.floor(Math.random() * 50), // Random value 0-50
}
}).reverse()
const totalUsersValue = 4663
useEffect(() => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
}, [])
return (
<div className="w-8/12">
<Chart isLoading={isLoading}>
<ChartCard>
<ChartHeader>
<ChartMetric
label="Total Users"
value={totalUsersValue.toLocaleString('en-US')}
tooltip="This is a tooltip"
/>
<div className="flex items-center gap-6">
<ChartMetric label="Warn" value="1.2k" status="warning" align="end" />
<ChartMetric label="Err" value="736" status="negative" align="end" />
</div>
</ChartHeader>
<ChartContent
isEmpty={data.length === 0}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={16} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
<div className="h-40">
<LogsBarChart data={data} isFullHeight={true} />
</div>
</ChartContent>
</ChartCard>
</Chart>
</div>
)
}
@@ -1,100 +0,0 @@
'use client'
import { ExternalLink } from 'lucide-react'
import { useEffect, useState } from 'react'
import { Skeleton } from 'ui'
import {
Chart,
ChartActions,
ChartCard,
ChartContent,
ChartHeader,
ChartMetric,
ChartSparkline,
} from 'ui-patterns/Chart'
export default function ChartComposedMetrics() {
const [data, setData] = useState<Array<{ value: number; timestamp: string }>>([])
const actions = [
{
label: 'Open in Logs Explorer',
onClick: () => {
alert('Ta da! You clicked me! 🎉')
},
icon: <ExternalLink size={12} />,
},
]
useEffect(() => {
const now = new Date()
setData(
Array.from({ length: 12 }, (_, i) => ({
value: Math.floor(4000 + i * 100 + (Math.random() * 2000 - 800)),
timestamp: new Date(now.getTime() - (11 - i) * 60 * 60 * 1000).toISOString(),
}))
)
}, [])
const averageValue = data.reduce((acc, curr) => acc + curr.value, 0) / data.length
const diff = data[data.length - 1]?.value - data[0]?.value || 0
const diffPercentage = (diff / averageValue) * 100
return (
<div className="grid grid-cols-2 gap-6 w-full">
<Chart isLoading={!data.length}>
<ChartCard>
<ChartHeader className="pb-4" align="start">
<ChartMetric label="Slow Queries" value={0} />
<ChartActions actions={actions} />
</ChartHeader>
</ChartCard>
</Chart>
<Chart isLoading={!data.length}>
<ChartCard>
<ChartHeader className="pb-4" align="start">
<ChartMetric label="Sign Ups" value={127} />
<ChartActions actions={actions} />
</ChartHeader>
</ChartCard>
</Chart>
<Chart isLoading={!data.length}>
<ChartCard>
<ChartHeader align="start">
<ChartMetric
label="Active Users"
value={averageValue.toLocaleString(undefined, { maximumFractionDigits: 0 })}
diffValue={diffPercentage.toFixed(2) + '%'}
/>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
className="p-0"
loadingState={<Skeleton className="w-full h-24 rounded-none mt-4" />}
>
<ChartSparkline data={data} dataKey="value" />
</ChartContent>
</ChartCard>
</Chart>
<Chart isLoading={!data.length}>
<ChartCard>
<ChartHeader align="start">
<ChartMetric
label="Active Users"
value={averageValue.toLocaleString(undefined, { maximumFractionDigits: 0 })}
diffValue={diffPercentage.toFixed(2) + '%'}
/>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
className="p-0"
loadingState={<Skeleton className="w-full h-24 rounded-none mt-4" />}
>
<ChartSparkline data={data} dataKey="value" />
</ChartContent>
</ChartCard>
</Chart>
</div>
)
}
@@ -1,121 +0,0 @@
'use client'
import { BarChart2, ExternalLink } from 'lucide-react'
import { Badge, CriticalIcon } from 'ui'
import {
Chart,
ChartActions,
ChartCard,
ChartContent,
ChartDisabledState,
ChartEmptyState,
ChartHeader,
ChartLoadingState,
ChartTitle,
} from 'ui-patterns/Chart'
export default function ChartComposedStates() {
const actions = [
{
label: 'Open in Logs Explorer',
onClick: () => {
alert('Ta da! You clicked me! 🎉')
},
icon: <ExternalLink size={12} />,
},
]
const disabledActions = [
{
label: 'Upgrade to Pro',
href: '#',
},
]
return (
<div className="flex flex-col gap-6 w-8/12">
<Chart isLoading={true}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Response Errors</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent loadingState={<ChartLoadingState />}>My chart here...</ChartContent>
</ChartCard>
</Chart>
<Chart isErrored={true}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Response Errors</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
isEmpty={true}
errorState={
<ChartEmptyState
icon={<CriticalIcon />}
title="Some error happened"
description="Error happened why trying to fetch data. Please try again later."
/>
}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={20} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
My chart here...
</ChartContent>
</ChartCard>
</Chart>
<Chart>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Response Errors</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
isEmpty={true}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={20} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
My chart here...
</ChartContent>
</ChartCard>
</Chart>
<Chart isDisabled={true}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Response Errors</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
loadingState={<ChartLoadingState />}
disabledState={
<ChartDisabledState
icon={<Badge variant="success">Pro</Badge>}
label="API Processing Time"
description="This chart is available on the Pro plan and above"
actions={disabledActions}
/>
}
>
My chart here...
</ChartContent>
</ChartCard>
</Chart>
</div>
)
}
@@ -1,106 +0,0 @@
'use client'
import { format } from 'date-fns'
import { BarChart2, ExternalLink } from 'lucide-react'
import { useEffect, useState } from 'react'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
import {
Chart,
ChartActions,
ChartBar,
ChartCard,
ChartContent,
ChartEmptyState,
ChartFooter,
ChartHeader,
ChartLoadingState,
ChartTitle,
} from 'ui-patterns/Chart'
export default function ChartComposedTable() {
const [isLoading, setIsLoading] = useState(true)
const actions = [
{
label: 'Open in Logs Explorer',
onClick: () => {
alert('Opening in Logs Explorer...')
},
icon: <ExternalLink size={12} />,
},
]
const data = Array.from({ length: 46 }, (_, i) => {
const date = new Date()
date.setMinutes(date.getMinutes() - i * 5) // Each point 5 minutes apart
return {
timestamp: date.toISOString(),
standard_score: Math.floor(Math.random() * 100),
}
}).reverse()
useEffect(() => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
}, [])
return (
<div className="flex flex-col gap-6 w-8/12">
<Chart isLoading={isLoading}>
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Standard Bar Chart</ChartTitle>
<ChartActions actions={actions} />
</ChartHeader>
<ChartContent
isEmpty={data.length === 0}
emptyState={
<ChartEmptyState
icon={<BarChart2 size={16} />}
title="No data to show"
description="It may take up to 24 hours for data to refresh"
/>
}
loadingState={<ChartLoadingState />}
>
<div className="h-40">
<ChartBar
data={data}
dataKey="standard_score"
showGrid={false}
showYAxis={true}
YAxisProps={{
tickFormatter: (value) => `${value}k`,
width: 80,
}}
isFullHeight={true}
/>
</div>
</ChartContent>
<ChartFooter>
<Table>
<TableHeader>
<TableRow>
<TableHead>Timestamp</TableHead>
<TableHead className="text-right">Standard Score</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.slice(0, 3).map((item) => (
<TableRow key={item.timestamp}>
<TableCell className="text-foreground-light">
{format(new Date(item.timestamp), 'MMM d, yyyy h:mm a')}
</TableCell>
<TableCell className="text-right tabular-nums">{item.standard_score}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</ChartFooter>
</ChartCard>
</Chart>
</div>
)
}
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -3,8 +3,9 @@ const supabaseConfig = require('eslint-config-supabase/next')
module.exports = defineConfig([
supabaseConfig,
{ ignores: ['__registry__'] },
{
files: ['registry/**/*.tsx', '__registry__/**/*.tsx', 'app/**/*.tsx'],
files: ['registry/**/*.tsx', 'app/**/*.tsx'],
rules: {
'no-restricted-exports': 'off',
},
+6 -5
View File
@@ -5,16 +5,17 @@
"type": "module",
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "run-p --race dev:*",
"dev": "pnpm run generate:registry && run-p --race dev:*",
"dev:next": "next dev --turbopack --port 3003",
"dev:content": "contentlayer2 dev",
"build": "pnpm run content:build && pnpm run build:registry && next build --turbopack",
"build:registry": "tsx ./scripts/build-registry.mts && prettier --log-level silent --write \"registry/**/*.{ts,tsx,mdx}\" --cache",
"build": "pnpm run build:registry && pnpm run content:build && next build --turbopack",
"generate:registry": "tsx ./scripts/build-registry.mts",
"build:registry": "pnpm run generate:registry && prettier --log-level silent --write \"registry/**/*.{ts,tsx,mdx}\" --cache",
"start": "next start",
"lint": "eslint .",
"content:build": "contentlayer2 build",
"clean": "rimraf .next .turbo tsconfig.tsbuildinfo",
"typecheck": "contentlayer2 build && tsc --noEmit -p tsconfig.json"
"clean": "rimraf .next .turbo tsconfig.tsbuildinfo __registry__",
"typecheck": "pnpm run generate:registry && contentlayer2 build && tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@hookform/resolvers": "^3.1.1",
@@ -1,5 +1,5 @@
// @sts-nocheck
import { existsSync, promises as fs } from 'fs'
import { promises as fs } from 'fs'
import { tmpdir } from 'os'
import path from 'path'
import { cwd } from 'process'
@@ -191,13 +191,10 @@ export const Index: Record<string, any> = {
})
)
// // Write the source file for blocks only.
// Write the source file for blocks only.
sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx`
const sourcePath = path.join(process.cwd(), sourceFilename)
if (!existsSync(sourcePath)) {
await fs.mkdir(sourcePath, { recursive: true })
}
await fs.mkdir(path.dirname(sourcePath), { recursive: true })
rimraf.sync(sourcePath)
await fs.writeFile(sourcePath, sourceFile.getText())
}
@@ -273,8 +270,10 @@ export const Index: Record<string, any> = {
// await fs.writeFile(path.join(REGISTRY_PATH, 'index.json'), registryJson, 'utf8')
// Write style index.
rimraf.sync(path.join(process.cwd(), '__registry__/index.tsx'))
await fs.writeFile(path.join(process.cwd(), '__registry__/index.tsx'), index)
const indexPath = path.join(process.cwd(), '__registry__/index.tsx')
await fs.mkdir(path.dirname(indexPath), { recursive: true })
rimraf.sync(indexPath)
await fs.writeFile(indexPath, index)
}
// ----------------------------------------------------------------------------