mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 04:29:10 +00:00
feat: add storybook and new badge component with stories
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import {
|
||||
BanIcon,
|
||||
BoxesIcon,
|
||||
BoxIcon,
|
||||
CheckIcon,
|
||||
ChevronsUpDownIcon,
|
||||
CircleXIcon,
|
||||
ExternalLinkIcon,
|
||||
GlobeIcon,
|
||||
InfoIcon,
|
||||
RadarIcon,
|
||||
TriangleAlertIcon
|
||||
} from "lucide-react";
|
||||
|
||||
import { Badge } from "./Badge";
|
||||
|
||||
/**
|
||||
* Badges act as an indicator that can optionally be made interactable.
|
||||
* You can place text and icons inside a badge.
|
||||
* Badges are often used for the indication of a status, state or scope.
|
||||
*/
|
||||
const meta = {
|
||||
title: "Generic/Badge",
|
||||
component: Badge,
|
||||
parameters: {
|
||||
layout: "centered"
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: "select",
|
||||
options: ["neutral", "success", "info", "warning", "danger", "project", "org", "sub-org"]
|
||||
},
|
||||
isTruncatable: {
|
||||
table: {
|
||||
disable: true
|
||||
}
|
||||
},
|
||||
asChild: {
|
||||
table: {
|
||||
disable: true
|
||||
}
|
||||
},
|
||||
children: {
|
||||
table: {
|
||||
disable: true
|
||||
}
|
||||
}
|
||||
},
|
||||
args: { children: "Badge", isTruncatable: false }
|
||||
} satisfies Meta<typeof Badge>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Neutral: Story = {
|
||||
name: "Variant: Neutral",
|
||||
args: {
|
||||
variant: "neutral",
|
||||
children: (
|
||||
<>
|
||||
<BanIcon />
|
||||
Disabled
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating neutral or disabled states."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Success: Story = {
|
||||
name: "Variant: Success",
|
||||
args: {
|
||||
variant: "success",
|
||||
children: (
|
||||
<>
|
||||
<CheckIcon />
|
||||
Success
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating successful or healthy states."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Info: Story = {
|
||||
name: "Variant: Info",
|
||||
args: {
|
||||
variant: "info",
|
||||
children: (
|
||||
<>
|
||||
<InfoIcon />
|
||||
Info
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use this variant when indicating informational states or linking to external references."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Warning: Story = {
|
||||
name: "Variant: Warning",
|
||||
args: {
|
||||
variant: "warning",
|
||||
children: (
|
||||
<>
|
||||
<TriangleAlertIcon />
|
||||
Warning
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating activity or attention warranting states."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
name: "Variant: Danger",
|
||||
args: {
|
||||
variant: "danger",
|
||||
children: (
|
||||
<>
|
||||
<CircleXIcon />
|
||||
Danger
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating destructive or error states."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Organization: Story = {
|
||||
name: "Variant: Organization",
|
||||
args: {
|
||||
variant: "org",
|
||||
children: (
|
||||
<>
|
||||
<GlobeIcon />
|
||||
Organization
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating organization scope or links."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const SubOrganization: Story = {
|
||||
name: "Variant: Sub-Organization",
|
||||
args: {
|
||||
variant: "sub-org",
|
||||
children: (
|
||||
<>
|
||||
<BoxesIcon />
|
||||
Sub-Organization
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating sub-organization scope or links."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Project: Story = {
|
||||
name: "Variant: Project",
|
||||
args: {
|
||||
variant: "project",
|
||||
children: (
|
||||
<>
|
||||
<BoxIcon />
|
||||
Project
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating project scope or links."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AsExternalLink: Story = {
|
||||
name: "Example: As External Link",
|
||||
args: {
|
||||
variant: "info",
|
||||
asChild: true,
|
||||
children: (
|
||||
<a href="https://infisical.com/">
|
||||
Link <ExternalLinkIcon />
|
||||
</a>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use the `asChild` prop with an `a` tag to use a badge as an external link."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AsRouterLink: Story = {
|
||||
name: "Example: As Router Link",
|
||||
args: {
|
||||
variant: "project",
|
||||
asChild: true,
|
||||
children: (
|
||||
<Link to=".">
|
||||
<RadarIcon />
|
||||
Secret Scanning
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use the `asChild` prop with a `Link` component to use a badge as an internal link."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AsButton: Story = {
|
||||
name: "Example: As Button",
|
||||
args: {
|
||||
variant: "org",
|
||||
asChild: true,
|
||||
children: (
|
||||
<button type="button" onClick={() => alert("button clicked")}>
|
||||
<GlobeIcon />
|
||||
Organization
|
||||
<ChevronsUpDownIcon />
|
||||
</button>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use the `asChild` prop with a `button` tag to use a badge as a button. Do not use a styled `Button` component."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const IsTruncatable: Story = {
|
||||
name: "Example: isTruncatable",
|
||||
args: {
|
||||
isTruncatable: true,
|
||||
children: (
|
||||
<>
|
||||
<GlobeIcon />
|
||||
<span>Infisical Infrastructure</span>
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use the `isTruncatable` prop with a `span` tag wrapping the text content to support truncation."
|
||||
}
|
||||
}
|
||||
},
|
||||
decorators: (Story) => (
|
||||
<div className="flex w-32">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import { forwardRef } from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "cva";
|
||||
|
||||
import { cn } from "@app/components/v3/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
[
|
||||
"select-none items-center rounded-sm px-1.5 py-0.5 text-xs",
|
||||
"gap-x-1 [a&,button&]:cursor-pointer inline-flex",
|
||||
"[&>svg]:pointer-events-none [&>svg]:shrink-0 [&>svg]:stroke-[2.25] [&>svg]:size-3",
|
||||
"transition duration-200 ease-in-out"
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
isTruncatable: {
|
||||
true: "[&>span,&>p]:truncate min-w-0",
|
||||
false: "w-fit shrink-0 whitespace-nowrap overflow-hidden"
|
||||
},
|
||||
variant: {
|
||||
neutral: [
|
||||
"border-neutral/75 bg-neutral/30 text-neutral",
|
||||
"[a&,button&]:hover:bg-neutral/40 [a&,button&]:hover:border-neutral"
|
||||
],
|
||||
success: [
|
||||
"border-success/75 bg-success/30 text-success",
|
||||
"[a&,button&]:hover:bg-success/40 [a&,button&]:hover:border-success"
|
||||
],
|
||||
info: [
|
||||
"border-info/75 bg-info/30 text-info",
|
||||
"[a&,button&]:hover:bg-info/40 [a&,button&]:hover:border-info"
|
||||
],
|
||||
warning: [
|
||||
"border-warning/75 bg-warning/30 text-warning",
|
||||
"[a&,button&]:hover:bg-warning/40 [a&,button&]:hover:border-warning"
|
||||
],
|
||||
danger: [
|
||||
"border-danger/75 bg-danger/30 text-danger",
|
||||
"[a&,button&]:hover:bg-danger/40 [a&,button&]:hover:border-danger"
|
||||
],
|
||||
project: [
|
||||
"border-project/75 bg-project/30 text-project",
|
||||
"[a&,button&]:hover:bg-project/40 [a&,button&]:hover:border-project"
|
||||
],
|
||||
org: [
|
||||
"border-org/75 bg-org/30 text-org",
|
||||
"[a&,button&]:hover:bg-org/40 [a&,button&]:hover:border-org"
|
||||
],
|
||||
"sub-org": [
|
||||
"border-sub-org/75 bg-sub-org/30 text-sub-org",
|
||||
"[a&,button&]:hover:bg-sub-org/40 [a&,button&]:hover:border-sub-org"
|
||||
]
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "success"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
type TBadgeProps = VariantProps<typeof badgeVariants> &
|
||||
React.ComponentProps<"span"> & {
|
||||
asChild?: boolean;
|
||||
};
|
||||
|
||||
const Badge = forwardRef<HTMLButtonElement | HTMLSpanElement | HTMLAnchorElement, TBadgeProps>(
|
||||
({ className, variant, asChild = false, isTruncatable = false, ...props }, ref): JSX.Element => {
|
||||
const Comp = asChild ? Slot : "span";
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant, isTruncatable }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Badge.displayName = "Badge";
|
||||
|
||||
export { Badge, badgeVariants, type TBadgeProps };
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./Badge";
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./Badge";
|
||||
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
+16
-2
@@ -37,12 +37,26 @@
|
||||
}
|
||||
|
||||
@theme {
|
||||
/*legacy color schema */
|
||||
/* Fonts */
|
||||
--font-inter: "Inter", sans-serif;
|
||||
--max-width-8xl: 88rem; /* 1408px */
|
||||
|
||||
/* Colors v2 */
|
||||
--color-background: #19191c;
|
||||
--color-foreground: white;
|
||||
--color-success: #2ecc71;
|
||||
--color-info: #34c2db;
|
||||
--color-warning: #f1c40f;
|
||||
--color-danger: #e74c3c;
|
||||
--color-org: #30B3FF;
|
||||
--color-sub-org: #96ff59;
|
||||
--color-project: #e0ed34;
|
||||
--color-neutral: #adaeb0;
|
||||
|
||||
/*legacy color schema */
|
||||
--color-org-v1: #30B3FF;
|
||||
--color-namespace-v1: #96ff59;
|
||||
--max-width-8xl: 88rem; /* 1408px */
|
||||
|
||||
/* Primary */
|
||||
--color-primary-50: #fffff5;
|
||||
--color-primary-100: #fcfce8;
|
||||
|
||||
Reference in New Issue
Block a user