mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: add storybook and new badge component with stories
This commit is contained in:
3
frontend/.gitignore
vendored
3
frontend/.gitignore
vendored
@@ -22,3 +22,6 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*storybook.log
|
||||
storybook-static
|
||||
|
||||
17
frontend/.storybook/decorators/RouterDecorator.tsx
Normal file
17
frontend/.storybook/decorators/RouterDecorator.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { Decorator } from "@storybook/react-vite";
|
||||
import { createRootRoute, createRouter, RouterProvider } from "@tanstack/react-router";
|
||||
|
||||
export const RouterDecorator: Decorator = (Story) => {
|
||||
const rootRoute = createRootRoute({
|
||||
component: Story
|
||||
});
|
||||
|
||||
const routeTree = rootRoute;
|
||||
|
||||
const router = createRouter({
|
||||
routeTree
|
||||
});
|
||||
|
||||
// @ts-expect-error just to make Links happy :)
|
||||
return <RouterProvider router={router} />;
|
||||
};
|
||||
1
frontend/.storybook/decorators/index.ts
Normal file
1
frontend/.storybook/decorators/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./RouterDecorator";
|
||||
14
frontend/.storybook/main.ts
Normal file
14
frontend/.storybook/main.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
"../src/components/v3/**/*.mdx",
|
||||
"../src/components/v3/**/*.stories.@(js|jsx|mjs|ts|tsx)"
|
||||
],
|
||||
addons: ["@storybook/addon-docs", "@storybook/addon-a11y"],
|
||||
framework: {
|
||||
name: "@storybook/react-vite",
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
45
frontend/.storybook/preview.tsx
Normal file
45
frontend/.storybook/preview.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { Preview } from "@storybook/react-vite";
|
||||
|
||||
import { RouterDecorator } from "./decorators";
|
||||
|
||||
import "../src/index.css";
|
||||
|
||||
const preview: Preview = {
|
||||
decorators: [
|
||||
(Story) => {
|
||||
const root = document.getElementsByTagName("html")[0];
|
||||
|
||||
root.setAttribute("class", "overflow-visible");
|
||||
|
||||
return <Story />;
|
||||
},
|
||||
RouterDecorator
|
||||
],
|
||||
parameters: {
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i
|
||||
}
|
||||
},
|
||||
docs: {
|
||||
backgroundColor: "var(--background)"
|
||||
},
|
||||
a11y: {
|
||||
test: "todo"
|
||||
},
|
||||
backgrounds: {
|
||||
default: "dark",
|
||||
options: {
|
||||
dark: { name: "Dark", value: "var(--background)" }
|
||||
}
|
||||
}
|
||||
},
|
||||
initialGlobals: {
|
||||
backgrounds: {
|
||||
value: "dark"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default preview;
|
||||
@@ -1,3 +1,6 @@
|
||||
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
|
||||
import storybook from "eslint-plugin-storybook";
|
||||
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import reactHooks from "eslint-plugin-react-hooks";
|
||||
@@ -133,5 +136,6 @@ export default tseslint.config(
|
||||
rules: Object.fromEntries(
|
||||
Object.keys(stylisticPlugin.configs["all-flat"].rules ?? {}).map((key) => [key, "off"])
|
||||
)
|
||||
}
|
||||
},
|
||||
storybook.configs["flat/recommended"]
|
||||
);
|
||||
|
||||
2089
frontend/package-lock.json
generated
2089
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,9 @@
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint ./src",
|
||||
"lint:fix": "eslint --fix ./src",
|
||||
"type:check": "tsc --noEmit --project ./tsconfig.app.json"
|
||||
"type:check": "tsc --noEmit --project ./tsconfig.app.json",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
},
|
||||
"overrides": {
|
||||
"sha.js": "2.4.12"
|
||||
@@ -47,7 +49,9 @@
|
||||
"@radix-ui/react-popper": "^1.2.1",
|
||||
"@radix-ui/react-progress": "^1.1.1",
|
||||
"@radix-ui/react-radio-group": "^1.2.2",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.1.3",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.2",
|
||||
"@radix-ui/react-toast": "^1.2.3",
|
||||
@@ -64,6 +68,7 @@
|
||||
"argon2-browser": "^1.18.0",
|
||||
"axios": "^1.12.0",
|
||||
"classnames": "^2.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cva": "npm:class-variance-authority@^0.7.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"dompurify": "^3.2.4",
|
||||
@@ -76,6 +81,7 @@
|
||||
"jsrp": "^0.2.4",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lexical": "^0.29.0",
|
||||
"lucide-react": "^0.544.0",
|
||||
"ms": "^2.1.3",
|
||||
"nprogress": "^0.2.0",
|
||||
"picomatch": "^4.0.2",
|
||||
@@ -105,6 +111,9 @@
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.15.0",
|
||||
"@kesills/eslint-config-airbnb-typescript": "^20.0.0",
|
||||
"@storybook/addon-a11y": "^9.1.9",
|
||||
"@storybook/addon-docs": "^9.1.9",
|
||||
"@storybook/react-vite": "^9.1.9",
|
||||
"@stylistic/eslint-plugin": "^2.12.1",
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
@@ -130,6 +139,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.14",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-storybook": "^9.1.9",
|
||||
"globals": "^15.12.0",
|
||||
"postcss": "^8.4.49",
|
||||
"prettier": "3.4.2",
|
||||
|
||||
BIN
frontend/public/fonts/AllianceNo2-Regular.otf
Normal file
BIN
frontend/public/fonts/AllianceNo2-Regular.otf
Normal file
Binary file not shown.
BIN
frontend/public/images/logotransparent_trimmed.png
Normal file
BIN
frontend/public/images/logotransparent_trimmed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
1
frontend/public/lotties/infisical_loading_white.json
Normal file
1
frontend/public/lotties/infisical_loading_white.json
Normal file
File diff suppressed because one or more lines are too long
307
frontend/src/components/v3/generic/Badge/Badge.stories.tsx
Normal file
307
frontend/src/components/v3/generic/Badge/Badge.stories.tsx
Normal file
@@ -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>
|
||||
)
|
||||
};
|
||||
82
frontend/src/components/v3/generic/Badge/Badge.tsx
Normal file
82
frontend/src/components/v3/generic/Badge/Badge.tsx
Normal file
@@ -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 };
|
||||
1
frontend/src/components/v3/generic/Badge/index.ts
Normal file
1
frontend/src/components/v3/generic/Badge/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./Badge";
|
||||
1
frontend/src/components/v3/generic/index.ts
Normal file
1
frontend/src/components/v3/generic/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./Badge";
|
||||
0
frontend/src/components/v3/index.ts
Normal file
0
frontend/src/components/v3/index.ts
Normal file
6
frontend/src/components/v3/utils/index.ts
Normal file
6
frontend/src/components/v3/utils/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -31,5 +31,7 @@
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx",
|
||||
"./.storybook/**/*.ts",
|
||||
"./.storybook/**/*.tsx",
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user