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