diff --git a/config/vite.config.ts b/config/vite.config.ts index 8317686b..1719657a 100644 --- a/config/vite.config.ts +++ b/config/vite.config.ts @@ -22,6 +22,11 @@ const entries: Array = [ path: "src/components/usa-banner/index.ts", sizeLimit: "10 kB", }, + { + name: "components/usa-alert", + path: "src/components/usa-alert/index.ts", + sizeLimit: "3 kB", + }, { name: "components/usa-link", path: "src/components/usa-link/index.js", diff --git a/custom-elements.json b/custom-elements.json index c84f0585..98356469 100644 --- a/custom-elements.json +++ b/custom-elements.json @@ -7,6 +7,14 @@ "path": "src/components/index.ts", "declarations": [], "exports": [ + { + "kind": "js", + "name": "UsaAlert", + "declaration": { + "name": "UsaAlert", + "module": "src/components/index.ts" + } + }, { "kind": "js", "name": "UsaLink", @@ -25,6 +33,228 @@ } ] }, + { + "kind": "javascript-module", + "path": "src/components/usa-alert/index.ts", + "declarations": [ + { + "kind": "class", + "description": "", + "name": "UsaAlert", + "cssProperties": [ + { + "description": "Override background color.", + "name": "--usa-alert-background-color" + }, + { + "description": "Override left border color.", + "name": "--usa-alert-border-color" + }, + { + "description": "Override icon color.", + "name": "--usa-alert-icon-color" + }, + { + "description": "Override icon size.", + "name": "--usa-alert-icon-size" + }, + { + "description": "Override horizontal padding.", + "name": "--usa-alert-padding-x" + }, + { + "description": "Override vertical padding.", + "name": "--usa-alert-padding-y" + }, + { + "description": "Override font family.", + "name": "--usa-alert-font-family" + }, + { + "description": "Override text color.", + "name": "--usa-alert-text-color" + } + ], + "slots": [ + { + "description": "The alert heading (use an h-element: h2, h3, etc.).", + "name": "headline" + }, + { + "description": "Default slot for alert body content.", + "name": "" + } + ], + "members": [ + { + "kind": "field", + "name": "status", + "privacy": "public", + "type": { + "text": "AlertStatus" + }, + "default": "\"info\"", + "attribute": "status", + "reflects": true + }, + { + "kind": "field", + "name": "slim", + "privacy": "public", + "type": { + "text": "boolean" + }, + "default": "false", + "attribute": "slim", + "reflects": true + }, + { + "kind": "field", + "name": "noIcon", + "privacy": "public", + "type": { + "text": "boolean" + }, + "default": "false", + "attribute": "no-icon", + "reflects": true + }, + { + "kind": "field", + "name": "closeable", + "privacy": "public", + "type": { + "text": "boolean" + }, + "default": "false", + "attribute": "closeable", + "reflects": true + }, + { + "kind": "field", + "name": "closeLabel", + "privacy": "public", + "type": { + "text": "string" + }, + "default": "\"Close alert\"", + "attribute": "close-label" + }, + { + "kind": "field", + "name": "_visible", + "privacy": "public", + "type": { + "text": "boolean" + }, + "default": "true", + "attribute": "_visible" + }, + { + "kind": "field", + "name": "#role", + "privacy": "private", + "type": { + "text": "string" + }, + "readonly": true + }, + { + "kind": "method", + "name": "_handleClose", + "privacy": "private" + } + ], + "events": [ + { + "name": "close", + "type": { + "text": "CustomEvent" + }, + "description": "Dispatched when the close button is clicked." + } + ], + "attributes": [ + { + "type": { + "text": "AlertStatus" + }, + "description": "Determines the icon and border/background color.", + "name": "status", + "default": "\"info\"", + "fieldName": "status", + "propName": "status" + }, + { + "type": { + "text": "boolean" + }, + "description": "Displays the slim variation (smaller icon, no heading).", + "name": "slim", + "default": "false", + "fieldName": "slim", + "propName": "slim" + }, + { + "type": { + "text": "boolean" + }, + "description": "Hides the status icon.", + "name": "no-icon", + "default": "false", + "fieldName": "noIcon", + "propName": "noIcon" + }, + { + "type": { + "text": "boolean" + }, + "description": "Shows a close button.", + "name": "closeable", + "default": "false", + "fieldName": "closeable", + "propName": "closeable" + }, + { + "type": { + "text": "string" + }, + "description": "Aria-label for the close button. Defaults to \"Close alert\".", + "name": "close-label", + "default": "\"Close alert\"", + "fieldName": "closeLabel", + "propName": "closeLabel" + }, + { + "name": "_visible", + "type": { + "text": "boolean" + }, + "default": "true", + "fieldName": "_visible", + "propName": "_visible" + } + ], + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "tagName": "usa-alert", + "customElement": true, + "summary": "Displays important messages to the user with contextual status styling." + } + ], + "exports": [ + { + "kind": "js", + "name": "UsaAlert", + "declaration": { + "name": "UsaAlert", + "module": "src/components/usa-alert/index.ts" + } + } + ] + }, { "kind": "javascript-module", "path": "src/components/usa-banner/index.ts", diff --git a/src/components/index.ts b/src/components/index.ts index a42c8674..17a270e8 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,4 +1,5 @@ +import { UsaAlert } from "./usa-alert"; import { UsaLink } from "./usa-link"; import { UsaBanner } from "./usa-banner"; -export { UsaLink, UsaBanner }; +export { UsaAlert, UsaLink, UsaBanner }; diff --git a/src/components/usa-alert/index.ts b/src/components/usa-alert/index.ts new file mode 100644 index 00000000..8f6c3268 --- /dev/null +++ b/src/components/usa-alert/index.ts @@ -0,0 +1,136 @@ +import { LitElement, html, css, unsafeCSS, nothing } from "lit"; +import { classMap } from "lit/directives/class-map.js"; + +import styles from "./usa-alert.css"; +import iconClose from "../../shared/icons/close.svg"; +import { defineCustomElement } from "../../utils"; + +const VALID_STATUSES = [ + "info", + "warning", + "error", + "success", + "emergency", +] as const; + +type AlertStatus = (typeof VALID_STATUSES)[number]; + +/** + * @summary Displays important messages to the user with contextual status styling. + * + * @attribute {"info" | "warning" | "error" | "success" | "emergency"} status - Determines the icon and border/background color. + * @attribute {boolean} slim - Displays the slim variation (smaller icon, no heading). + * @attribute {boolean} no-icon - Hides the status icon. + * @attribute {boolean} closeable - Shows a close button. + * @attribute {string} close-label - Aria-label for the close button. Defaults to "Close alert". + * + * @cssprop --usa-alert-background-color - Override background color. + * @cssprop --usa-alert-border-color - Override left border color. + * @cssprop --usa-alert-icon-color - Override icon color. + * @cssprop --usa-alert-icon-size - Override icon size. + * @cssprop --usa-alert-padding-x - Override horizontal padding. + * @cssprop --usa-alert-padding-y - Override vertical padding. + * @cssprop --usa-alert-font-family - Override font family. + * @cssprop --usa-alert-text-color - Override text color. + * + * @slot headline - The alert heading (use an h-element: h2, h3, etc.). + * @slot - Default slot for alert body content. + * + * @fires usa-alert-close - Dispatched when the close button is clicked. + * + * @element usa-alert + */ +export class UsaAlert extends LitElement { + static properties = { + status: { type: String, reflect: true }, + slim: { type: Boolean, reflect: true }, + noIcon: { type: Boolean, attribute: "no-icon", reflect: true }, + closeable: { type: Boolean, reflect: true }, + closeLabel: { type: String, attribute: "close-label" }, + _visible: { state: true }, + }; + + status!: AlertStatus; + slim!: boolean; + noIcon!: boolean; + closeable!: boolean; + closeLabel!: string; + _visible!: boolean; + + constructor() { + super(); + this.status = "info"; + this.slim = false; + this.noIcon = false; + this.closeable = false; + this.closeLabel = "Close alert"; + this._visible = true; + } + + get #role(): string { + if ( + this.status === "error" || + this.status === "emergency" || + this.status === "warning" + ) { + return "alert"; + } + return "status"; + } + + private _handleClose() { + this.dispatchEvent( + new CustomEvent("usa-alert-close", { bubbles: true, composed: true }), + ); + this._visible = false; + } + + static styles = [ + css` + :host { + --usa-icon-close: url("${unsafeCSS(iconClose)}"); + } + `, + styles, + ]; + + render() { + if (!this._visible) { + return nothing; + } + + const status = VALID_STATUSES.includes(this.status) ? this.status : "info"; + + const classes = { + "usa-alert": true, + [`usa-alert--${status}`]: true, + "usa-alert--slim": this.slim, + "usa-alert--no-icon": this.noIcon, + }; + + return html` +
+
+ ${!this.slim ? html`` : null} +
+ +
+
+ ${this.closeable + ? html` + + ` + : null} +
+ `; + } +} + +defineCustomElement("usa-alert", UsaAlert); diff --git a/src/components/usa-alert/usa-alert.css b/src/components/usa-alert/usa-alert.css new file mode 100644 index 00000000..95a2fea0 --- /dev/null +++ b/src/components/usa-alert/usa-alert.css @@ -0,0 +1,232 @@ +:host { + --usa-alert-font-family: + system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, + Arial, sans-serif; + --usa-alert-text-color: var(--usa-color-base-darkest, #1b1b1b); + --usa-alert-padding-x: 1.25rem; + --usa-alert-padding-y: 1rem; + --usa-alert-icon-size: 2rem; + --usa-alert-border-width: 0.25rem; + + /** Status colors — info */ + --usa-alert-info-background: var(--usa-color-info-lighter, #e7f6f8); + --usa-alert-info-border: var(--usa-color-info, #00bde3); + --usa-alert-info-icon: var(--usa-color-info, #00bde3); + + /** Status colors — warning */ + --usa-alert-warning-background: var(--usa-color-warning-lighter, #faf3d1); + --usa-alert-warning-border: var(--usa-color-warning, #ffbe2e); + --usa-alert-warning-icon: var(--usa-color-warning, #ffbe2e); + + /** Status colors — error */ + --usa-alert-error-background: var(--usa-color-error-lighter, #f4e3db); + --usa-alert-error-border: var(--usa-color-error, #d54309); + --usa-alert-error-icon: var(--usa-color-error, #d54309); + + /** Status colors — success */ + --usa-alert-success-background: var(--usa-color-success-lighter, #ecf3ec); + --usa-alert-success-border: var(--usa-color-success, #00a91c); + --usa-alert-success-icon: var(--usa-color-success, #00a91c); + + /** Status colors — emergency */ + --usa-alert-emergency-background: var(--usa-color-emergency, #9c3d10); + --usa-alert-emergency-border: var(--usa-color-emergency-dark, #332d29); + --usa-alert-emergency-icon: #fff; + --usa-alert-emergency-text-color: #fff; + + display: block; +} + +* { + box-sizing: border-box; +} + +.usa-alert { + background-color: var( + --usa-alert-background-color, + var(--usa-alert-info-background) + ); + border-left: var(--usa-alert-border-width) solid + var(--usa-alert-border-color, var(--usa-alert-info-border)); + color: var(--usa-alert-text-color); + font-family: var(--usa-alert-font-family); + position: relative; +} + +/** Status variants */ +.usa-alert--info { + --usa-alert-background-color: var(--usa-alert-info-background); + --usa-alert-border-color: var(--usa-alert-info-border); + --usa-alert-icon-color: var(--usa-alert-info-icon); +} + +.usa-alert--warning { + --usa-alert-background-color: var(--usa-alert-warning-background); + --usa-alert-border-color: var(--usa-alert-warning-border); + --usa-alert-icon-color: var(--usa-alert-warning-icon); +} + +.usa-alert--error { + --usa-alert-background-color: var(--usa-alert-error-background); + --usa-alert-border-color: var(--usa-alert-error-border); + --usa-alert-icon-color: var(--usa-alert-error-icon); +} + +.usa-alert--success { + --usa-alert-background-color: var(--usa-alert-success-background); + --usa-alert-border-color: var(--usa-alert-success-border); + --usa-alert-icon-color: var(--usa-alert-success-icon); +} + +.usa-alert--emergency { + --usa-alert-background-color: var(--usa-alert-emergency-background); + --usa-alert-border-color: var(--usa-alert-emergency-border); + --usa-alert-icon-color: var(--usa-alert-emergency-icon); + + & .usa-alert__body { + color: var(--usa-alert-emergency-text-color); + } +} + +/** Alert body */ +.usa-alert__body { + padding: var(--usa-alert-padding-y) var(--usa-alert-padding-x); + padding-left: calc( + var(--usa-alert-padding-x) + var(--usa-alert-icon-size) + 0.75rem + ); + position: relative; + + &::before { + background-color: var(--usa-alert-icon-color); + content: ""; + display: block; + height: var(--usa-alert-icon-size); + left: var(--usa-alert-padding-x); + mask-position: center; + mask-repeat: no-repeat; + mask-size: var(--usa-alert-icon-size); + position: absolute; + top: var(--usa-alert-padding-y); + width: var(--usa-alert-icon-size); + } +} + +/** Status icons via CSS mask */ +.usa-alert--info .usa-alert__body::before { + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z'/%3E%3C/svg%3E"); +} + +.usa-alert--warning .usa-alert__body::before { + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z'/%3E%3C/svg%3E"); +} + +.usa-alert--error .usa-alert__body::before, +.usa-alert--emergency .usa-alert__body::before { + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z'/%3E%3C/svg%3E"); +} + +.usa-alert--success .usa-alert__body::before { + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E"); +} + +/** No icon */ +.usa-alert--no-icon .usa-alert__body { + padding-left: var(--usa-alert-padding-x); + + &::before { + display: none; + } +} + +/** Slim */ +.usa-alert--slim .usa-alert__body { + padding: 0.5rem var(--usa-alert-padding-x); + padding-left: calc(var(--usa-alert-padding-x) + 2rem); + + &::before { + height: 1.5rem; + mask-size: 1.5rem; + top: 0.5rem; + width: 1.5rem; + } +} + +.usa-alert--slim.usa-alert--no-icon .usa-alert__body { + padding-left: var(--usa-alert-padding-x); +} + +/** Headline slot */ +::slotted([slot="headline"]) { + font-size: 1.33rem; + font-weight: 700; + line-height: 1.2; + margin: 0 0 0.5rem; +} + +.usa-alert--emergency ::slotted([slot="headline"]) { + color: var(--usa-alert-emergency-text-color); +} + +/** Alert text */ +.usa-alert__text { + font-size: 1rem; + line-height: 1.5; + margin: 0; +} + +.usa-alert__text ::slotted(*) { + margin: 0; +} + +.usa-alert__text ::slotted(:not(:first-child)) { + margin-top: 0.5rem; +} + +/** Close button */ +.usa-alert__close { + appearance: none; + background: transparent; + border: 0; + cursor: pointer; + display: block; + padding: 0.5rem; + position: absolute; + right: 0.5rem; + top: 0.5rem; +} + +.usa-alert__close:focus-visible { + outline: 2px solid var(--usa-color-blue-vivid-40, #2491ff); + outline-offset: 2px; +} + +.usa-alert__close-icon { + background-color: var(--usa-alert-text-color); + display: block; + height: 1.5rem; + mask-image: var(--usa-icon-close); + mask-position: center; + mask-repeat: no-repeat; + mask-size: 1.5rem; + width: 1.5rem; +} + +.usa-alert--emergency .usa-alert__close-icon { + background-color: var(--usa-alert-emergency-text-color); +} + +/** High contrast mode */ +@media (forced-colors: active) { + .usa-alert { + border: 1px solid ButtonText; + border-left-width: var(--usa-alert-border-width); + } + + .usa-alert__body::before { + background-color: ButtonText; + } + + .usa-alert__close-icon { + background-color: ButtonText; + } +} diff --git a/src/components/usa-alert/usa-alert.spec.ts b/src/components/usa-alert/usa-alert.spec.ts new file mode 100644 index 00000000..1614e462 --- /dev/null +++ b/src/components/usa-alert/usa-alert.spec.ts @@ -0,0 +1,191 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { LitElement } from "lit"; + +import "./index.js"; + +function getAlert(): HTMLElement & LitElement { + const alert = document.body.querySelector("usa-alert"); + if (!alert) throw new Error("usa-alert element not found"); + return alert as HTMLElement & LitElement; +} + +function getShadow(): ShadowRoot { + const shadow = getAlert().shadowRoot; + if (!shadow) throw new Error("usa-alert shadowRoot not found"); + return shadow; +} + +async function updateComplete(): Promise { + await getAlert().updateComplete; +} + +describe("usa-alert component", () => { + beforeEach(() => { + document.body.innerHTML = ` + +

Alert heading

+

Alert body content.

+
+ `; + }); + + it("defaults to info status with role=status", () => { + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.classList.contains("usa-alert--info")).toBe(true); + expect(alertDiv?.getAttribute("role")).toBe("status"); + }); + + it("renders headline slot", () => { + const slot = getShadow().querySelector( + 'slot[name="headline"]', + ) as HTMLSlotElement; + expect(slot).not.toBeNull(); + const assigned = slot?.assignedElements(); + expect(assigned?.[0]?.textContent).toBe("Alert heading"); + }); + + it("renders default slot content", () => { + const slot = getShadow().querySelector( + "slot:not([name])", + ) as HTMLSlotElement; + expect(slot).not.toBeNull(); + const assigned = slot?.assignedElements(); + expect(assigned?.[0]?.textContent).toBe("Alert body content."); + }); +}); + +describe("status variants", () => { + const statuses = ["info", "warning", "error", "success", "emergency"]; + + statuses.forEach((status) => { + it(`renders ${status} variant`, async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.classList.contains(`usa-alert--${status}`)).toBe(true); + }); + }); + + it("falls back to info for invalid status", async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.classList.contains("usa-alert--info")).toBe(true); + }); +}); + +describe("ARIA roles", () => { + it("uses role=status for success", async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.getAttribute("role")).toBe("status"); + }); + + it("uses role=alert for error", async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.getAttribute("role")).toBe("alert"); + }); + + it("uses role=alert for warning", async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.getAttribute("role")).toBe("alert"); + }); + + it("uses role=alert for emergency", async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.getAttribute("role")).toBe("alert"); + }); +}); + +describe("slim variant", () => { + beforeEach(() => { + document.body.innerHTML = `

Slim alert

`; + }); + + it("applies slim class", () => { + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.classList.contains("usa-alert--slim")).toBe(true); + }); + + it("does not render headline slot when slim", () => { + const slot = getShadow().querySelector('slot[name="headline"]'); + expect(slot).toBeNull(); + }); +}); + +describe("no-icon variant", () => { + beforeEach(() => { + document.body.innerHTML = `

No icon

`; + }); + + it("applies no-icon class", () => { + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv?.classList.contains("usa-alert--no-icon")).toBe(true); + }); +}); + +describe("closeable", () => { + beforeEach(() => { + document.body.innerHTML = `

Closeable alert

`; + }); + + it("renders close button", () => { + const btn = getShadow().querySelector(".usa-alert__close"); + expect(btn).not.toBeNull(); + }); + + it("has correct default aria-label", () => { + const btn = getShadow().querySelector(".usa-alert__close"); + expect(btn?.getAttribute("aria-label")).toBe("Close alert"); + }); + + it("dispatches usa-alert-close event on click", async () => { + const handler = vi.fn(); + getAlert().addEventListener("usa-alert-close", handler); + + const btn = getShadow().querySelector( + ".usa-alert__close", + ) as HTMLButtonElement; + btn.click(); + + expect(handler).toHaveBeenCalledOnce(); + }); + + it("hides alert after close", async () => { + const btn = getShadow().querySelector( + ".usa-alert__close", + ) as HTMLButtonElement; + btn.click(); + + // Wait for Lit reactive update + await updateComplete(); + + const alertDiv = getShadow().querySelector(".usa-alert"); + expect(alertDiv).toBeNull(); + }); + + it("supports custom close-label", async () => { + document.body.innerHTML = `

Test

`; + await updateComplete(); + const btn = getShadow().querySelector(".usa-alert__close"); + expect(btn?.getAttribute("aria-label")).toBe("Dismiss warning"); + }); +}); + +describe("does not render close button when not closeable", () => { + beforeEach(() => { + document.body.innerHTML = `

Not closeable

`; + }); + + it("has no close button", () => { + const btn = getShadow().querySelector(".usa-alert__close"); + expect(btn).toBeNull(); + }); +}); diff --git a/src/components/usa-alert/usa-alert.stories.ts b/src/components/usa-alert/usa-alert.stories.ts new file mode 100644 index 00000000..79ded184 --- /dev/null +++ b/src/components/usa-alert/usa-alert.stories.ts @@ -0,0 +1,128 @@ +import type { Meta, StoryObj } from "@storybook/web-components-vite"; +import { html } from "lit"; +import "./index.js"; + +const meta: Meta = { + title: "Components/Alert", + component: "usa-alert", + tags: ["autodocs", "alpha"], + argTypes: { + status: { + control: "select", + options: ["info", "warning", "error", "success", "emergency"], + }, + slim: { control: "boolean" }, + "no-icon": { control: "boolean" }, + closeable: { control: "boolean" }, + "close-label": { control: "text" }, + }, +}; +export default meta; + +type Story = StoryObj; + +export const Info: Story = { + render: () => html` + +

Informative status

+

An alert with contextual information for the user.

+
+ `, +}; + +export const Warning: Story = { + render: () => html` + +

Warning status

+

An alert that warns the user about something important.

+
+ `, +}; + +export const Error: Story = { + render: () => html` + +

Error status

+

An alert indicating an error has occurred.

+
+ `, +}; + +export const Success: Story = { + render: () => html` + +

Success status

+

An alert indicating a successful action.

+
+ `, +}; + +export const Emergency: Story = { + render: () => html` + +

Emergency status

+

An alert for emergency communications.

+
+ `, +}; + +export const Slim: Story = { + render: () => html` + +

A slim alert without a heading.

+
+ `, +}; + +export const SlimWarning: Story = { + render: () => html` + +

A slim warning alert.

+
+ `, +}; + +export const NoIcon: Story = { + render: () => html` + +

No icon

+

An alert without the status icon.

+
+ `, +}; + +export const Closeable: Story = { + render: () => html` + +

Closeable alert

+

This alert can be dismissed by clicking the close button.

+
+ `, +}; + +export const AllVariants: Story = { + render: () => html` +
+ +

Informative status

+

An informational alert.

+
+ +

Warning status

+

A warning alert.

+
+ +

Error status

+

An error alert.

+
+ +

Success status

+

A success alert.

+
+ +

Emergency status

+

An emergency alert.

+
+
+ `, +};