feat(docs): add analytics and pattern showcase stories
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
import {
|
||||
StatCard,
|
||||
StatCardDelta,
|
||||
StatCardDescription,
|
||||
StatCardEyebrow,
|
||||
StatCardHeader,
|
||||
StatCardLabel,
|
||||
StatCardMetric,
|
||||
StatCardValue
|
||||
} from "@ai-ui/ui";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
function RevenueStatCard({
|
||||
interactive = true,
|
||||
tone = "default"
|
||||
}: {
|
||||
interactive?: boolean;
|
||||
tone?: "default" | "subtle" | "accent";
|
||||
}) {
|
||||
return (
|
||||
<StatCard className="w-[360px]" interactive={interactive} tone={tone}>
|
||||
<StatCardHeader>
|
||||
<StatCardEyebrow>Revenue pulse</StatCardEyebrow>
|
||||
<StatCardLabel>Monthly recurring revenue</StatCardLabel>
|
||||
</StatCardHeader>
|
||||
<StatCardMetric>
|
||||
<StatCardValue>$101,820</StatCardValue>
|
||||
<StatCardDelta tone="success">+8.4%</StatCardDelta>
|
||||
</StatCardMetric>
|
||||
<StatCardDescription>
|
||||
Assisted follow-up and steadier route timing are lifting close quality this month.
|
||||
</StatCardDescription>
|
||||
</StatCard>
|
||||
);
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: "Components/StatCard",
|
||||
component: RevenueStatCard,
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"StatCard is the compact KPI surface for one headline metric, one supporting delta, and one short line of context. Use it when a dashboard needs a readable value slab without the extra regions that belong to a richer analytics panel. The default treatment stays lightly hover-ready so high-value summaries feel like lit objects instead of flat admin tiles."
|
||||
}
|
||||
},
|
||||
layout: "centered"
|
||||
},
|
||||
tags: ["autodocs"]
|
||||
} satisfies Meta<typeof RevenueStatCard>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
|
||||
export const Motion: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Hover the interactive cards directly in the canvas. StatCard motion should stay compact: the slab lifts as one object, the value sharpens slightly, and the delta chip follows with a softer secondary response instead of turning the panel into a busy dashboard tile."
|
||||
}
|
||||
}
|
||||
},
|
||||
render: () => (
|
||||
<div className="grid w-[840px] gap-4 rounded-[var(--radius-xl)] border border-[color-mix(in_oklch,var(--color-border)_72%,transparent)] bg-[linear-gradient(180deg,color-mix(in_oklch,var(--color-surface)_78%,white_22%),color-mix(in_oklch,var(--color-surface-container-low)_86%,white_14%))] p-6 shadow-[var(--shadow-sm)]">
|
||||
<div className="grid gap-2">
|
||||
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-primary)]">
|
||||
Motion review
|
||||
</p>
|
||||
<h3 className="text-lg font-semibold tracking-[var(--tracking-tight)] text-[var(--color-foreground)]">
|
||||
Keep the KPI slab buoyant, not theatrical.
|
||||
</h3>
|
||||
<p className="max-w-[40rem] text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
The card should read like one lifted object. The headline value and delta chip can echo
|
||||
the lift, but the motion should still feel quieter than the richer choreography used by
|
||||
MetricCard.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<RevenueStatCard />
|
||||
<StatCard className="w-full" interactive tone="accent">
|
||||
<StatCardHeader>
|
||||
<StatCardEyebrow>Pipeline pulse</StatCardEyebrow>
|
||||
<StatCardLabel>Qualified expansion forecast</StatCardLabel>
|
||||
</StatCardHeader>
|
||||
<StatCardMetric>
|
||||
<StatCardValue>31%</StatCardValue>
|
||||
<StatCardDelta tone="primary">+9.2%</StatCardDelta>
|
||||
</StatCardMetric>
|
||||
<StatCardDescription>
|
||||
The uplift signal is strong enough to justify a wider follow-up wave this week.
|
||||
</StatCardDescription>
|
||||
</StatCard>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const States: Story = {
|
||||
render: () => (
|
||||
<div className="grid w-[820px] gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<RevenueStatCard />
|
||||
<StatCard className="w-full" interactive={false} tone="subtle">
|
||||
<StatCardHeader>
|
||||
<StatCardEyebrow>Risk watch</StatCardEyebrow>
|
||||
<StatCardLabel>Qualified pipeline</StatCardLabel>
|
||||
</StatCardHeader>
|
||||
<StatCardMetric>
|
||||
<StatCardValue>$82,450</StatCardValue>
|
||||
<StatCardDelta tone="warning">-3.1%</StatCardDelta>
|
||||
</StatCardMetric>
|
||||
<StatCardDescription>
|
||||
Mid-market deal spread is still wider than the board would like.
|
||||
</StatCardDescription>
|
||||
</StatCard>
|
||||
<StatCard className="w-full" interactive tone="accent">
|
||||
<StatCardHeader>
|
||||
<StatCardEyebrow>AI influence</StatCardEyebrow>
|
||||
<StatCardLabel>Forecast confidence</StatCardLabel>
|
||||
</StatCardHeader>
|
||||
<StatCardMetric>
|
||||
<StatCardValue>31%</StatCardValue>
|
||||
<StatCardDelta tone="primary">+9.2%</StatCardDelta>
|
||||
</StatCardMetric>
|
||||
<StatCardDescription>
|
||||
Commercial planning is stable enough to expand the next follow-up wave.
|
||||
</StatCardDescription>
|
||||
</StatCard>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const Anatomy: Story = {
|
||||
render: () => (
|
||||
<div className="w-[760px] rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
||||
<div className="grid gap-5">
|
||||
<div className="grid gap-2">
|
||||
<p className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
||||
Stat card anatomy
|
||||
</p>
|
||||
<p className="text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
The contract stays intentionally small: framing copy, one metric group, and one line of
|
||||
context. If the panel needs media, actions, or footer detail, move up to
|
||||
<code className="ml-1 text-[var(--color-foreground)]">MetricCard</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<RevenueStatCard />
|
||||
|
||||
<div className="grid gap-3 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
<p>
|
||||
<code className="text-[var(--color-foreground)]">data-slot="header"</code> groups
|
||||
the eyebrow and label.
|
||||
</p>
|
||||
<p>
|
||||
<code className="text-[var(--color-foreground)]">data-slot="metric"</code> holds
|
||||
the headline value and its delta badge.
|
||||
</p>
|
||||
<p>
|
||||
<code className="text-[var(--color-foreground)]">data-slot="value"</code>,
|
||||
<code className="ml-1 text-[var(--color-foreground)]">data-slot="delta"</code>, and
|
||||
<code className="ml-1 text-[var(--color-foreground)]">data-slot="description"</code>
|
||||
keep KPI styling hooks stable for docs and consumers.
|
||||
</p>
|
||||
<p>
|
||||
<code className="text-[var(--color-foreground)]">data-interactive</code> keeps the
|
||||
hover-ready treatment explicit when the stat should feel more lifted than surrounding
|
||||
utility panels.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const Accessibility: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Keep stat-card labels concrete, keep delta color from being the only signal, and reserve the description for the reason the number matters right now rather than repeating the value."
|
||||
}
|
||||
}
|
||||
},
|
||||
render: () => (
|
||||
<div className="grid w-[840px] gap-4 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
|
||||
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
||||
<h3 className="text-lg font-semibold tracking-[var(--tracking-tight)] text-[var(--color-foreground)]">
|
||||
Review guidance
|
||||
</h3>
|
||||
<div className="mt-4 grid gap-3 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
||||
<p>Use a label that names the metric, not just a vague business area.</p>
|
||||
<p>Delta should still be readable as text such as <code>+8.4%</code> or <code>-3.1%</code>.</p>
|
||||
<p>Use the chip tone and shape as reinforcement, not as the only signal that the number moved.</p>
|
||||
<p>The description should explain the current signal, not restate the number.</p>
|
||||
</div>
|
||||
</article>
|
||||
<div className="flex items-center justify-center rounded-[var(--radius-lg)] border border-dashed border-[var(--color-border-strong)] bg-[var(--color-background)] p-6">
|
||||
<RevenueStatCard />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
Reference in New Issue
Block a user