Skip to content

Commit

Permalink
feat: make alerts dismissable
Browse files Browse the repository at this point in the history
  • Loading branch information
eitjuh committed Dec 30, 2024
1 parent 3822c4f commit e7e1ae8
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@inkonchain/ink-kit",
"version": "0.4.3",
"version": "0.4.4",
"description": "",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand Down
13 changes: 13 additions & 0 deletions src/components/Alert/Alert.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Alert, AlertProps } from "./Alert";
import { InkIcon } from "../..";
import { fn } from "@storybook/test";

const meta: Meta<AlertProps> = {
title: "Components/Alert",
Expand Down Expand Up @@ -53,3 +54,15 @@ export const TitleOnly: Story = {
description: undefined,
},
};

export const Dismissable: Story = {
args: {
variant: "info",
title: "This alert can be dismissed",
description:
"Click the X to dismiss. The state will persist across refreshes.",
dismissable: true,
id: "example-alert",
onDismiss: fn(),
},
};
49 changes: 47 additions & 2 deletions src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { classNames, variantClassNames } from "../../util/classes";
import { InkIcon } from "../..";

Expand All @@ -8,6 +8,18 @@ export interface AlertProps {
variant?: "success" | "error" | "warning" | "info";
icon?: React.ReactNode;
className?: string;
/**
* Unique identifier for the alert. Required if dismissable is true.
*/
id?: string;
/**
* Whether the alert can be dismissed. If true, id is required.
*/
dismissable?: boolean;
/**
* Callback fired when the alert is dismissed
*/
onDismiss?: () => void;
}

export const Alert: React.FC<AlertProps> = ({
Expand All @@ -16,14 +28,38 @@ export const Alert: React.FC<AlertProps> = ({
variant = "info",
icon,
className,
id,
dismissable,
onDismiss,
}) => {
const [isDismissed, setIsDismissed] = useState(false);

useEffect(() => {
if (dismissable && id) {
const isDismissedStored = localStorage.getItem(`ink-alert-${id}`);
setIsDismissed(isDismissedStored === "true");
}
}, [dismissable, id]);

if (isDismissed) {
return null;
}

const defaultIcon = {
success: <InkIcon.Check />,
error: <InkIcon.Error />,
warning: <InkIcon.Error />,
info: <InkIcon.Settings />,
}[variant];

const handleDismiss = () => {
if (dismissable && id) {
localStorage.setItem(`ink-alert-${id}`, "true");
setIsDismissed(true);
onDismiss?.();
}
};

return (
<div
className={classNames(
Expand All @@ -38,12 +74,21 @@ export const Alert: React.FC<AlertProps> = ({
)}
>
<div className="ink:size-4 ink:shrink-0">{icon || defaultIcon}</div>
<div className="ink:flex ink:flex-col ink:gap-1">
<div className="ink:flex ink:flex-col ink:gap-1 ink:flex-1">
<div className="ink:text-body-2-bold">{title}</div>
{description && (
<div className="ink:text-body-2-regular">{description}</div>
)}
</div>
{dismissable && (
<button
onClick={handleDismiss}
className="ink:size-4 ink:shrink-0 ink:opacity-60 hover:ink:opacity-100 ink:cursor-pointer"
aria-label="Dismiss alert"
>
<InkIcon.Close />
</button>
)}
</div>
);
};

0 comments on commit e7e1ae8

Please sign in to comment.