feat(v1): bloquants release v1 — tests, stories, visual regression, gouvernance, publishing
Release / Release / open changeset PR (push) Has been cancelled
CI / Build, typecheck, test, a11y (push) Has been cancelled

6 chantiers v1 sur 7 livrés (DataTable refonte reportée car nécessite
2-3j en propre — TanStack Table + virtualisation + filter builder).

v1-A — Tests (4 → 22 fichiers) :
- Avatar, AvatarGroup, UserCard, MetricCard, ProfileHeader, Tooltip,
  Sheet, Drawer, Slider, ToggleGroup, Tabs, Pagination, Accordion,
  Switch, Badge, ConfirmDialog, Popover, Menu, Text, PricingCard,
  FeatureCard, Toast — chacun avec render + clavier + axe-core.

v1-B — Storybook (7 → 23 fichiers) :
- Avatar, UserCard, ProfileHeader, MetricCard, PricingCard,
  FeatureCard, Sheet (4 sides), HoverCard, Slider, ToggleGroup,
  Menu+ContextMenu, Toast (avec démo "Empiler 5"), Tabs,
  Pagination, Accordion, Badge.

v1-D — Visual regression Playwright :
- playwright.config.ts (light + dark, threshold strict 0.2)
- e2e/visual.spec.ts (20 stories critiques)
- Step CI + upload report en cas de fail

v1-E — Site doc Starlight rempli :
- 11 pages composants détaillées (Button, Input, Tooltip, Dialog,
  Toast, Avatar, ThemePicker, MetricCard, PricingCard, ToggleGroup,
  Slider) avec API, anatomie, do/don't, A11y.

v1-F — Publishing Verdaccio :
- verdaccio/config.yaml, docker-compose.verdaccio.yml, .npmrc
- README setup local + déploiement prod + backups + sécurité

v1-G — Gouvernance :
- LICENSE, CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md
- CODEOWNERS, PR template, 3 issue templates (bug/feature/rfc)

Bug fix bonus : tooltip dark mode (text-primary comme bg + text-inverse
comme texte → blanc-sur-blanc invisible). Remplacé par neutral-900/0
en light + bg-raised/text-primary en dark.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dinawo
2026-05-04 22:29:35 +02:00
parent 62317f2ad7
commit 133feff75d
69 changed files with 3433 additions and 7 deletions
+27
View File
@@ -0,0 +1,27 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Accordion, Stack } from "@managemate/react";
const meta = {
title: "Disclosure/Accordion",
component: Accordion,
tags: ["autodocs"],
} satisfies Meta<typeof Accordion>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => (
<Stack>
<Accordion label="Combien coûte un abonnement Pro ?">
19 /utilisateur/mois engagement annuel. 21 /mois sans engagement.
</Accordion>
<Accordion label="Puis-je migrer depuis mon outil actuel ?">
Oui, depuis Sage, Cegid, ADP, Lucca ou via fichier CSV. Compter 7 jours.
</Accordion>
<Accordion label="Êtes-vous conformes RGPD ?" defaultOpen>
Hébergement France, chiffrement AES-256, audit ANSSI annuel. Détails sur notre page sécurité.
</Accordion>
</Stack>
),
};
+52
View File
@@ -0,0 +1,52 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Avatar, AvatarGroup } from "@managemate/react";
const meta = {
title: "Data display/Avatar",
component: Avatar,
tags: ["autodocs"],
} satisfies Meta<typeof Avatar>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Initials: Story = { args: { initials: "MD", alt: "Marie Dupont" } };
export const Image: Story = { args: { src: "https://i.pravatar.cc/96?u=md", alt: "Marie" } };
export const Sizes: Story = {
render: () => (
<div style={{ display: "flex", gap: 12, alignItems: "flex-end" }}>
<Avatar initials="XS" alt="x" size="xs" />
<Avatar initials="SM" alt="x" size="sm" />
<Avatar initials="MD" alt="x" size="md" />
<Avatar initials="LG" alt="x" size="lg" />
<Avatar initials="XL" alt="x" size="xl" />
<Avatar initials="2X" alt="x" size="2xl" />
</div>
),
};
export const Status: Story = {
render: () => (
<div style={{ display: "flex", gap: 16 }}>
<Avatar initials="MD" alt="online" status="online" size="lg" />
<Avatar initials="JM" alt="away" status="away" size="lg" />
<Avatar initials="SB" alt="busy" status="busy" size="lg" />
<Avatar initials="TL" alt="offline" status="offline" size="lg" />
</div>
),
};
export const Square: Story = { args: { initials: "MD", alt: "x", shape: "square", size: "lg" } };
export const Group: Story = {
render: () => (
<AvatarGroup
avatars={[
{ initials: "MD", alt: "Marie" },
{ initials: "JM", alt: "Jean" },
{ initials: "SB", alt: "Sophie" },
{ initials: "TL", alt: "Thomas" },
{ initials: "ER", alt: "Emma" },
]}
max={4}
/>
),
};
+53
View File
@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Badge, Icon } from "@managemate/react";
const meta = {
title: "Data display/Badge",
component: Badge,
tags: ["autodocs"],
} satisfies Meta<typeof Badge>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Variants: Story = {
render: () => (
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
<Badge>Default</Badge>
<Badge variant="brand">Brand</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
<Badge variant="danger">Danger</Badge>
<Badge variant="info">Info</Badge>
<Badge variant="solid">Solid</Badge>
</div>
),
};
export const WithDot: Story = {
render: () => (
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
<span className="mmg-badge mmg-badge--success">
<span className="mmg-badge__dot mmg-badge__dot--pulse" />
En ligne
</span>
<span className="mmg-badge mmg-badge--warning">
<span className="mmg-badge__dot" />
En attente
</span>
<span className="mmg-badge mmg-badge--danger">
<span className="mmg-badge__dot" />
Erreur
</span>
</div>
),
};
export const WithIcon: Story = {
render: () => (
<div style={{ display: "flex", gap: 8 }}>
<Badge variant="brand"><Icon name="star-fill" size="xs" /> Nouveau</Badge>
<Badge variant="success"><Icon name="checkbox-circle-fill" size="xs" /> Actif</Badge>
</div>
),
};
+38
View File
@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from "@storybook/react";
import { FeatureCard } from "@managemate/react";
const meta = {
title: "Marketing/FeatureCard",
component: FeatureCard,
tags: ["autodocs"],
} satisfies Meta<typeof FeatureCard>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
icon: "rocket-2-fill",
iconColor: "brand",
title: "Onboarding éclair",
description: "Créez un collaborateur, attribuez ses accès, déclenchez son premier cycle de paie en moins de 90 secondes.",
link: { label: "Voir le workflow", href: "#" },
},
};
export const WithGlow: Story = {
args: { ...(Default.args as object), glowOnHover: true },
};
export const Colors: Story = {
render: () => (
<div className="mmg-grid mmg-grid--gap-md">
<div className="mmg-col-4"><FeatureCard icon="rocket-2-fill" iconColor="brand" title="Brand" description="Accent rose Synapse." glowOnHover /></div>
<div className="mmg-col-4"><FeatureCard icon="shield-check-fill" iconColor="green" title="Green" description="Sécurité, RGPD." glowOnHover /></div>
<div className="mmg-col-4"><FeatureCard icon="line-chart-fill" iconColor="violet" title="Violet" description="Analytics." glowOnHover /></div>
<div className="mmg-col-4"><FeatureCard icon="bank-card-fill" iconColor="blue" title="Blue" description="Finance." /></div>
<div className="mmg-col-4"><FeatureCard icon="alert-fill" iconColor="amber" title="Amber" description="Alertes." /></div>
<div className="mmg-col-4"><FeatureCard icon="settings-3-fill" iconColor="neutral" title="Neutral" description="Configuration." /></div>
</div>
),
};
+44
View File
@@ -0,0 +1,44 @@
import type { Meta, StoryObj } from "@storybook/react";
import { HoverCard, Avatar, Badge, Inline } from "@managemate/react";
const meta = {
title: "Overlays/HoverCard",
component: HoverCard,
tags: ["autodocs"],
} satisfies Meta<typeof HoverCard>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Profile: Story = {
render: () => (
<HoverCard
trigger={
<a href="#" style={{ color: "var(--mmg-color-accent)", fontWeight: 600 }}>
@marie.dupont
</a>
}
>
<Inline gap="md">
<Avatar initials="MD" alt="Marie" size="lg" />
<div>
<div style={{ fontWeight: 700 }}>Marie Dupont</div>
<div style={{ color: "var(--mmg-color-text-tertiary)", fontSize: "var(--mmg-font-size-sm)" }}>Lead Dev · Synapse</div>
<div style={{ marginTop: 8, fontSize: "var(--mmg-font-size-sm)" }}>
Lead frontend, mainteneuse du DSMMG.
</div>
</div>
</Inline>
</HoverCard>
),
};
export const Badge_: Story = {
name: "Badge metadata",
render: () => (
<HoverCard trigger={<Badge variant="brand">Synapse v4.2.1</Badge>}>
<div style={{ fontWeight: 700, marginBottom: 4 }}>Synapse v4.2.1</div>
<div style={{ color: "var(--mmg-color-text-tertiary)", fontSize: "var(--mmg-font-size-sm)" }}>Sortie le 24 avril 2026</div>
</HoverCard>
),
};
+44
View File
@@ -0,0 +1,44 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Menu, ContextMenu, Button } from "@managemate/react";
const meta = {
title: "Overlays/Menu",
component: Menu,
tags: ["autodocs"],
} satisfies Meta<typeof Menu>;
export default meta;
type Story = StoryObj<typeof meta>;
const items = [
{ label: "Voir le détail", icon: "eye-line" as const, shortcut: "↵" },
{ label: "Modifier", icon: "edit-line" as const, shortcut: "E" },
{ label: "Dupliquer", icon: "file-copy-line" as const, shortcut: "⌘D" },
{ type: "divider" } as const,
{ type: "label", label: "Visibilité" } as const,
{ label: "Partager", icon: "share-line" as const },
{ label: "Archiver", icon: "inbox-line" as const },
{ type: "divider" } as const,
{ label: "Supprimer", icon: "delete-bin-line" as const, danger: true, shortcut: "⌫" },
];
export const Default: Story = {
render: () => (
<Menu trigger={<Button variant="tertiary" icon="more-2-line">Actions</Button>} items={items} />
),
};
export const Context: Story = {
name: "ContextMenu — clic droit",
render: () => (
<ContextMenu items={items}>
<div style={{
display: "grid", placeItems: "center", height: 140,
border: "2px dashed var(--mmg-color-border)", borderRadius: "var(--mmg-radius-md)",
color: "var(--mmg-color-text-tertiary)", cursor: "context-menu",
}}>
Clic droit ici
</div>
</ContextMenu>
),
};
+46
View File
@@ -0,0 +1,46 @@
import type { Meta, StoryObj } from "@storybook/react";
import { MetricCard, Sparkline } from "@managemate/react";
const meta = {
title: "Data display/MetricCard",
component: MetricCard,
tags: ["autodocs"],
} satisfies Meta<typeof MetricCard>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: { label: "MRR", value: "84 320 €", delta: "+12.4%", trend: "up", period: "vs M-1", icon: "money-euro-circle-line" },
};
export const TrendDown: Story = {
args: { label: "Tickets ouverts", value: "32", delta: "-18%", trend: "down", invertTrend: true, period: "cette semaine", icon: "error-warning-line" },
};
export const Flat: Story = {
args: { label: "NPS", value: "62", delta: "0", trend: "flat", period: "stable", icon: "star-line" },
};
export const WithSparkline: Story = {
args: {
label: "Sessions",
value: "12 480",
delta: "+8.2%",
trend: "up",
period: "30 derniers jours",
icon: "line-chart-line",
sparkline: <Sparkline data={[20, 28, 25, 32, 30, 38, 42, 48]} width={200} height={48} />,
},
};
export const Grid: Story = {
render: () => (
<div className="mmg-grid mmg-grid--gap-md">
<div className="mmg-col-3"><MetricCard label="MRR" value="84 320 €" delta="+12.4%" trend="up" icon="money-euro-circle-line" /></div>
<div className="mmg-col-3"><MetricCard label="Churn" value="2.3%" delta="+0.4 pts" trend="up" invertTrend icon="arrow-go-back-line" /></div>
<div className="mmg-col-3"><MetricCard label="NPS" value="62" delta="+4 pts" trend="up" icon="star-line" /></div>
<div className="mmg-col-3"><MetricCard label="Tickets" value="32" delta="-18%" trend="down" invertTrend icon="error-warning-line" /></div>
</div>
),
};
+19
View File
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { Pagination } from "@managemate/react";
const meta = {
title: "Navigation/Pagination",
component: Pagination,
tags: ["autodocs"],
} satisfies Meta<typeof Pagination>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => {
const [page, setPage] = useState(3);
return <Pagination page={page} pageCount={10} onChange={setPage} />;
},
};
+68
View File
@@ -0,0 +1,68 @@
import type { Meta, StoryObj } from "@storybook/react";
import { PricingCard, Button } from "@managemate/react";
const meta = {
title: "Marketing/PricingCard",
component: PricingCard,
tags: ["autodocs"],
} satisfies Meta<typeof PricingCard>;
export default meta;
type Story = StoryObj<typeof meta>;
const features = [
{ label: "Jusqu'à 10 collaborateurs" },
{ label: "Gestion congés" },
{ label: "Bulletins simplifiés" },
{ label: "Support email" },
{ label: "API & webhooks", included: false },
{ label: "SSO", included: false },
];
export const Default: Story = {
args: {
name: "Starter",
description: "Pour les TPE qui démarrent.",
price: "9 €",
pricePeriod: "/utilisateur/mois",
features,
cta: <Button variant="tertiary" block>Démarrer l'essai</Button>,
},
};
export const Highlighted: Story = {
args: {
name: "Pro",
description: "Pour les PME en croissance.",
price: "19 €",
pricePeriod: "/utilisateur/mois",
highlighted: true,
badge: "Populaire",
features: features.map((f) => ({ ...f, included: true })),
cta: <Button variant="primary" block icon="arrow-right-line" iconPosition="right">Choisir Pro</Button>,
},
};
export const Trio: Story = {
render: () => (
<div className="mmg-grid mmg-grid--gap-md">
<div className="mmg-col-4"><PricingCard {...Default.args!} /></div>
<div className="mmg-col-4"><PricingCard {...Highlighted.args!} /></div>
<div className="mmg-col-4">
<PricingCard
name="Enterprise"
description="Pour les organisations matures."
price="Sur devis"
features={[
{ label: "Tout Pro inclus" },
{ label: "SLA 99.95%" },
{ label: "SSO / SAML / SCIM" },
{ label: "Audit logs" },
{ label: "TAM dédié" },
]}
cta={<Button variant="tertiary" block>Contacter sales</Button>}
/>
</div>
</div>
),
};
@@ -0,0 +1,45 @@
import type { Meta, StoryObj } from "@storybook/react";
import { ProfileHeader, Badge, Button, Icon } from "@managemate/react";
const meta = {
title: "Profile/ProfileHeader",
component: ProfileHeader,
tags: ["autodocs"],
} satisfies Meta<typeof ProfileHeader>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
name: "Marie Dupont",
subtitle: "Lead Developer · Synapse",
bio: "Lead frontend chez ManageMate depuis 2020. Mainteneuse du DSMMG, passionnée d'accessibilité et de motion design fonctionnel.",
initials: "MD",
status: "online",
badges: (
<>
<span className="mmg-badge mmg-badge--success">
<span className="mmg-badge__dot mmg-badge__dot--pulse" />
Disponible
</span>
<span className="mmg-badge mmg-badge--brand">
<Icon name="shield-check-fill" size="xs" /> Owner DSMMG
</span>
<span className="mmg-badge">Frontend · React</span>
</>
),
actions: (
<>
<Button variant="tertiary" icon="message-2-line">Message</Button>
<Button variant="primary" icon="user-add-line">Suivre</Button>
</>
),
stats: [
{ label: "Commits", value: "184" },
{ label: "Reviews", value: "23" },
{ label: "Tickets résolus", value: "412" },
{ label: "Ancienneté", value: "5 ans" },
],
},
};
+42
View File
@@ -0,0 +1,42 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { Sheet, Button, Input, Switch } from "@managemate/react";
const meta = {
title: "Overlays/Sheet",
component: Sheet,
tags: ["autodocs"],
} satisfies Meta<typeof Sheet>;
export default meta;
type Story = StoryObj<typeof meta>;
function Demo({ side }: { side: "left" | "right" | "top" | "bottom" }) {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="primary" onClick={() => setOpen(true)}>Ouvrir ({side})</Button>
<Sheet
open={open}
onOpenChange={setOpen}
side={side}
title="Édition rapide"
description="Modifiez sans quitter la liste."
footer={
<>
<Button variant="ghost" onClick={() => setOpen(false)}>Annuler</Button>
<Button variant="primary" onClick={() => setOpen(false)}>Sauvegarder</Button>
</>
}
>
<Input label="Nom" defaultValue="Marie Dupont" />
<Switch label="Notifier l'équipe" defaultChecked />
</Sheet>
</>
);
}
export const Right: Story = { render: () => <Demo side="right" /> };
export const Left: Story = { render: () => <Demo side="left" /> };
export const Top: Story = { render: () => <Demo side="top" /> };
export const Bottom: Story = { render: () => <Demo side="bottom" /> };
+50
View File
@@ -0,0 +1,50 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { Slider } from "@managemate/react";
const meta = {
title: "Forms/Slider",
component: Slider,
tags: ["autodocs"],
} satisfies Meta<typeof Slider>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Single: Story = {
render: () => {
const [v, setV] = useState([62]);
return (
<div style={{ width: 320 }}>
<label style={{ display: "flex", justifyContent: "space-between", marginBottom: 8, fontSize: "var(--mmg-font-size-sm)", fontWeight: 600 }}>
<span>Volume</span>
<span style={{ fontVariantNumeric: "tabular-nums" }}>{v[0]}%</span>
</label>
<Slider label="Volume" value={v} onValueChange={setV} />
</div>
);
},
};
export const Range: Story = {
render: () => {
const [v, setV] = useState([800, 2400]);
return (
<div style={{ width: 320 }}>
<label style={{ display: "flex", justifyContent: "space-between", marginBottom: 8, fontSize: "var(--mmg-font-size-sm)", fontWeight: 600 }}>
<span>Prix</span>
<span style={{ fontVariantNumeric: "tabular-nums" }}>{v[0]} {v[1]} </span>
</label>
<Slider label="Prix" value={v} onValueChange={setV} min={0} max={5000} step={50} />
</div>
);
},
};
export const Disabled: Story = {
render: () => (
<div style={{ width: 320 }}>
<Slider label="x" value={[40]} onValueChange={() => {}} disabled />
</div>
),
};
+30
View File
@@ -0,0 +1,30 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { Tabs } from "@managemate/react";
const meta = {
title: "Navigation/Tabs",
component: Tabs,
tags: ["autodocs"],
} satisfies Meta<typeof Tabs>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => {
const [v, setV] = useState("a");
return (
<Tabs
value={v}
onChange={setV}
items={[
{ id: "a", label: "Aperçu" },
{ id: "b", label: "Détails" },
{ id: "c", label: "Données" },
{ id: "d", label: "Activité" },
]}
/>
);
},
};
+35
View File
@@ -0,0 +1,35 @@
import type { Meta, StoryObj } from "@storybook/react";
import { ToastProvider, useToast, Button } from "@managemate/react";
const meta = {
title: "Feedback/Toast",
tags: ["autodocs"],
decorators: [(Story) => <ToastProvider position="bottom-right" max={5}><Story /></ToastProvider>],
} satisfies Meta;
export default meta;
type Story = StoryObj;
function Trigger() {
const { toast } = useToast();
return (
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
<Button variant="success" onClick={() => toast({ title: "Sauvegardé", description: "Vos modifs ont été enregistrées.", severity: "success" })}>Success</Button>
<Button variant="tonal" onClick={() => toast({ title: "Maintenance prévue dimanche", severity: "info" })}>Info</Button>
<Button variant="ghost" onClick={() => toast({ title: "Quota presque atteint", description: "85% du stockage utilisé.", severity: "warning" })}>Warning</Button>
<Button variant="danger" onClick={() => toast({ title: "Échec de la synchronisation", severity: "danger", duration: 0 })}>Danger</Button>
<Button variant="primary" icon="stack-line" onClick={() => {
const msgs = [
{ title: "Bulletin Marie", description: "2 850 €", severity: "success" as const },
{ title: "Bulletin Jean", description: "2 200 €", severity: "success" as const },
{ title: "Bulletin Sophie", description: "3 400 €", severity: "success" as const },
{ title: "Bulletin Thomas", description: "2 900 €", severity: "success" as const },
{ title: "Bulletin Emma", description: "1 950 €", severity: "success" as const },
];
msgs.forEach((m, i) => setTimeout(() => toast(m), i * 220));
}}>Empiler 5 toasts</Button>
</div>
);
}
export const Default: Story = { render: () => <Trigger /> };
+52
View File
@@ -0,0 +1,52 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { ToggleGroup } from "@managemate/react";
const meta = {
title: "Forms/ToggleGroup",
component: ToggleGroup,
tags: ["autodocs"],
} satisfies Meta<typeof ToggleGroup>;
export default meta;
type Story = StoryObj<typeof meta>;
const items = [
{ value: "list", label: "Liste", icon: "menu-line" as const },
{ value: "grid", label: "Grille", icon: "apps-2-line" as const },
{ value: "kanban", label: "Kanban", icon: "stack-line" as const },
];
export const Single: Story = {
render: () => {
const [v, setV] = useState<string | undefined>("list");
return <ToggleGroup type="single" ariaLabel="Vue" value={v} onValueChange={(x) => x && setV(x)} items={items} />;
},
};
export const Multiple: Story = {
render: () => {
const [v, setV] = useState<string[]>(["list"]);
return <ToggleGroup type="multiple" ariaLabel="Vues" value={v} onValueChange={setV} items={items} />;
},
};
export const Solid: Story = {
render: () => {
const [v, setV] = useState<string | undefined>("grid");
return <ToggleGroup type="single" variant="solid" ariaLabel="Vue" value={v} onValueChange={(x) => x && setV(x)} items={items} />;
},
};
export const Sizes: Story = {
render: () => {
const [v, setV] = useState("list");
return (
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<ToggleGroup type="single" size="sm" ariaLabel="x" value={v} onValueChange={(x) => x && setV(x)} items={items} />
<ToggleGroup type="single" size="md" ariaLabel="x" value={v} onValueChange={(x) => x && setV(x)} items={items} />
<ToggleGroup type="single" size="lg" ariaLabel="x" value={v} onValueChange={(x) => x && setV(x)} items={items} />
</div>
);
},
};
+37
View File
@@ -0,0 +1,37 @@
import type { Meta, StoryObj } from "@storybook/react";
import { UserCard, Button } from "@managemate/react";
const meta = {
title: "Profile/UserCard",
component: UserCard,
tags: ["autodocs"],
} satisfies Meta<typeof UserCard>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: { name: "Marie Dupont", role: "Lead Developer", initials: "MD", status: "online" },
};
export const Sizes: Story = {
render: () => (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<UserCard size="sm" name="Sophie Bernard" role="PM" initials="SB" status="online" />
<UserCard name="Jean Martin" role="Designer · Synapse" initials="JM" status="away" />
<UserCard size="lg" name="Thomas Legrand" role="DevOps" initials="TL" status="busy" meta="Disponible après 16h" />
</div>
),
};
export const Interactive: Story = {
args: {
name: "Lohann Bouveresse",
role: "CEO",
initials: "LB",
status: "online",
meta: "ceo@managemate.fr",
href: "#",
actions: <Button size="sm" variant="ghost" icon="more-2-line" iconOnly aria-label="Actions" />,
},
};