Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ const entries: Array<Entry> = [
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",
Expand Down
230 changes: 230 additions & 0 deletions custom-elements.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
"type": {
"text": "string"
},
"privacy": "private",
"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",
Expand Down
3 changes: 2 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
@@ -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 };
136 changes: 136 additions & 0 deletions src/components/usa-alert/index.ts
Original file line number Diff line number Diff line change
@@ -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 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 },
Comment thread
ethangardner marked this conversation as resolved.
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";
Comment thread
ethangardner marked this conversation as resolved.
this._visible = true;
}

private get #role(): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that we don't have a build check running in CI, but when I built this locally with npm run build I got the error below.

src/components/usa-alert/index.ts:70:3 - error TS18010: An accessibility modifier cannot be used with a private identifier.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, I'll be sure to check for this in the future.

if (
this.status === "error" ||
this.status === "emergency" ||
this.status === "warning"
) {
return "alert";
}
return "status";
}

private _handleClose() {
this.dispatchEvent(
new CustomEvent("close", { bubbles: true, composed: true }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first instance of a custom event in this project, and we don't have an established convention for naming them yet. I'll write an ADR for this in a follow up PR, but researching other design systems, the safest option in terms of avoiding a collision with other libraries is to prefix the event with the tag name i.e. new CustomEvent("usa-alert-close", { bubbles: true, composed: true }).

That way, if someone uses document.addEventListener("close", () => {}) we will avoid any unwanted side effects. The native <dialog> tag fires a close event for example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

);
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`
<div class="${classMap(classes)}" role="${this.#role}">
<div class="usa-alert__body">
${!this.slim ? html`<slot name="headline"></slot>` : null}
<div class="usa-alert__text">
<slot></slot>
</div>
</div>
${this.closeable
? html`
<button
class="usa-alert__close"
type="button"
aria-label="${this.closeLabel}"
@click="${this._handleClose}"
>
<span class="usa-alert__close-icon"></span>
</button>
`
: null}
</div>
`;
}
}

defineCustomElement("usa-alert", UsaAlert);
Loading