feat(ui): alert component
This commit is contained in:
39
pkgs/clan-app/ui/src/components/v2/Alert/Alert.css
Normal file
39
pkgs/clan-app/ui/src/components/v2/Alert/Alert.css
Normal file
@@ -0,0 +1,39 @@
|
||||
div.alert {
|
||||
@apply flex gap-2.5 px-6 py-4 size-full rounded-md items-start;
|
||||
|
||||
&.has-icon {
|
||||
@apply pl-4;
|
||||
|
||||
svg.icon {
|
||||
@apply relative top-0.5;
|
||||
}
|
||||
}
|
||||
|
||||
&.has-dismiss {
|
||||
@apply pr-4;
|
||||
}
|
||||
|
||||
& > div.content {
|
||||
@apply flex flex-col gap-2 size-full;
|
||||
}
|
||||
|
||||
&.info {
|
||||
@apply bg-semantic-info-1 border border-semantic-info-3 fg-semantic-info-3;
|
||||
}
|
||||
|
||||
&.error {
|
||||
@apply bg-semantic-error-2 border border-semantic-error-3 fg-semantic-error-3;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
@apply bg-semantic-warning-2 border border-semantic-warning-3 fg-semantic-warning-3;
|
||||
}
|
||||
|
||||
&.success {
|
||||
@apply bg-semantic-success-1 border border-semantic-success-3 fg-semantic-success-3;
|
||||
}
|
||||
|
||||
& > button.dismiss-trigger {
|
||||
@apply relative top-0.5;
|
||||
}
|
||||
}
|
||||
138
pkgs/clan-app/ui/src/components/v2/Alert/Alert.stories.tsx
Normal file
138
pkgs/clan-app/ui/src/components/v2/Alert/Alert.stories.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
import { Alert, AlertProps } from "@/src/components/v2/Alert/Alert";
|
||||
import { expect, fn } from "storybook/test";
|
||||
import { StoryContext } from "@kachurun/storybook-solid-vite";
|
||||
|
||||
const meta: Meta<AlertProps> = {
|
||||
title: "Components/Alert",
|
||||
component: Alert,
|
||||
decorators: [
|
||||
(Story: StoryObj) => (
|
||||
<div class="w-72">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<AlertProps>;
|
||||
|
||||
export const Info: Story = {
|
||||
args: {
|
||||
type: "info",
|
||||
title: "Headline",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
|
||||
},
|
||||
};
|
||||
|
||||
export const Error: Story = {
|
||||
args: {
|
||||
...Info.args,
|
||||
type: "error",
|
||||
},
|
||||
};
|
||||
|
||||
export const Warning: Story = {
|
||||
args: {
|
||||
...Info.args,
|
||||
type: "warning",
|
||||
},
|
||||
};
|
||||
|
||||
export const Success: Story = {
|
||||
args: {
|
||||
...Info.args,
|
||||
type: "success",
|
||||
},
|
||||
};
|
||||
|
||||
export const InfoIcon: Story = {
|
||||
args: {
|
||||
...Info.args,
|
||||
icon: "Info",
|
||||
},
|
||||
};
|
||||
|
||||
export const ErrorIcon: Story = {
|
||||
args: {
|
||||
...Error.args,
|
||||
icon: "WarningFilled",
|
||||
},
|
||||
};
|
||||
|
||||
export const WarningIcon: Story = {
|
||||
args: {
|
||||
...Warning.args,
|
||||
icon: "WarningFilled",
|
||||
},
|
||||
};
|
||||
|
||||
export const SuccessIcon: Story = {
|
||||
args: {
|
||||
...Success.args,
|
||||
icon: "Checkmark",
|
||||
},
|
||||
};
|
||||
|
||||
export const InfoDismiss: Story = {
|
||||
args: {
|
||||
...Info.args,
|
||||
onDismiss: fn(),
|
||||
play: async ({ canvas, step, userEvent, args }: StoryContext) => {
|
||||
await userEvent.click(canvas.getByRole("button"));
|
||||
await expect(args.onDismiss).toHaveBeenCalled();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ErrorDismiss: Story = {
|
||||
args: {
|
||||
...InfoDismiss.args,
|
||||
type: "error",
|
||||
},
|
||||
};
|
||||
|
||||
export const WarningDismiss: Story = {
|
||||
args: {
|
||||
...InfoDismiss.args,
|
||||
type: "warning",
|
||||
},
|
||||
};
|
||||
|
||||
export const SuccessDismiss: Story = {
|
||||
args: {
|
||||
...InfoDismiss.args,
|
||||
type: "success",
|
||||
},
|
||||
};
|
||||
|
||||
export const InfoIconDismiss: Story = {
|
||||
args: {
|
||||
...InfoDismiss.args,
|
||||
icon: "Info",
|
||||
},
|
||||
};
|
||||
|
||||
export const ErrorIconDismiss: Story = {
|
||||
args: {
|
||||
...ErrorDismiss.args,
|
||||
icon: "WarningFilled",
|
||||
},
|
||||
};
|
||||
|
||||
export const WarningIconDismiss: Story = {
|
||||
args: {
|
||||
...WarningDismiss.args,
|
||||
icon: "WarningFilled",
|
||||
},
|
||||
};
|
||||
|
||||
export const SuccessIconDismiss: Story = {
|
||||
args: {
|
||||
...SuccessDismiss.args,
|
||||
icon: "Checkmark",
|
||||
},
|
||||
};
|
||||
43
pkgs/clan-app/ui/src/components/v2/Alert/Alert.tsx
Normal file
43
pkgs/clan-app/ui/src/components/v2/Alert/Alert.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import "./Alert.css";
|
||||
import cx from "classnames";
|
||||
import Icon, { IconVariant } from "@/src/components/v2/Icon/Icon";
|
||||
import { Typography } from "@/src/components/v2/Typography/Typography";
|
||||
import { Button } from "@kobalte/core/button";
|
||||
import { Alert as KAlert } from "@kobalte/core/alert";
|
||||
|
||||
export interface AlertProps {
|
||||
type: "success" | "error" | "warning" | "info";
|
||||
title: string;
|
||||
description: string;
|
||||
icon?: IconVariant;
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
export const Alert = (props: AlertProps) => (
|
||||
<KAlert
|
||||
class={cx("alert", props.type, {
|
||||
"has-icon": props.icon,
|
||||
"has-dismiss": props.onDismiss,
|
||||
})}
|
||||
>
|
||||
{props.icon && <Icon icon={props.icon} color="inherit" size="1rem" />}
|
||||
<div class="content">
|
||||
<Typography hierarchy="body" size="default" weight="bold" color="inherit">
|
||||
{props.title}
|
||||
</Typography>
|
||||
<Typography hierarchy="body" size="xs" color="inherit">
|
||||
{props.description}
|
||||
</Typography>
|
||||
</div>
|
||||
{props.onDismiss && (
|
||||
<Button
|
||||
name="dismiss-alert"
|
||||
class="dismiss-trigger"
|
||||
onClick={props.onDismiss}
|
||||
aria-label={`Dismiss ${props.type} alert`}
|
||||
>
|
||||
<Icon icon="Close" color="primary" size="0.75rem" />
|
||||
</Button>
|
||||
)}
|
||||
</KAlert>
|
||||
);
|
||||
Reference in New Issue
Block a user