@uswds/uswds
Version:
Open source UI components and visual style guide for U.S. government websites
253 lines (233 loc) • 8.05 kB
JavaScript
import { LitElement, html, unsafeCSS } from "lit";
import { unsafeHTML } from "lit/directives/unsafe-html.js"; // eslint-disable-line import/extensions
import { classMap } from "lit/directives/class-map.js"; // eslint-disable-line import/extensions
import bannerStyles from "../_index.scss?inline";
import bannerComponentStyles from "./styles/_usa-banner.component.css?inline";
import usFlagSmall from "./img/us_flag_small.png";
import iconDotGov from "./img/icon-dot-gov.svg";
import iconHttps from "./img/icon-https.svg";
/**
* @summary The usa-banner component.
*
* @attribute {string} lang - The element's language.
* @attribute {string} label - The custom aria label users can override.
* @attribute {string} tld - The top level domain for the site.
*
* @cssprop --theme-banner-text-color - Sets banner text color.
* @cssprop --theme-banner-background-color - Sets banner background color.
* @cssprop --theme-banner-font-family - Sets banner font family.
* @cssprop --theme-banner-link-color - Sets the default link color.
* @cssprop --theme-banner-chevron-color - Sets the default chevron color.
* @cssprop --theme-banner-link-hover-color - Sets the default link color.
*
* @slot banner-text - The text for official government website text.
* @slot banner-action - Action text label "Here's how you know."
* @slot domain-heading - Heading text for the domain section.
* @slot domain-text - Body text for domain section.
* @slot https-heading - Heading for HTTPs section.
* @slot https-text - Body text for HTTPs section.
*
* @tagname usa-banner
*/
export default class UsaBanner extends LitElement {
static properties = {
lang: {
type: String,
reflect: true,
},
data: { attribute: false },
isOpen: { type: Boolean },
classes: {},
label: { type: String },
tld: {
type: String,
reflect: true,
},
};
toggle() {
this.isOpen = !this.isOpen;
this.shadowRoot
.querySelector(".usa-banner__content")
.toggleAttribute("hidden");
}
constructor() {
super();
this.lang = "en";
this.isOpen = false;
this.tld = "gov";
this.data = {
en: {
banner: {
label: "Official website of the United States government",
text: "An official website of the United States government",
action: "Here's how you know",
},
domain: {
heading: "Official websites use",
text1: "A",
text2:
"website belongs to an official government organization in the United States.",
},
https: {
heading1: "Secure",
heading2: "websites use HTTPS",
text1: "A <strong>lock</strong>",
text2:
"or <strong>https://</strong> means you’ve safely connected to the",
text3:
"website. Share sensitive information only on official, secure websites.",
},
},
es: {
banner: {
label: "Un sitio oficial del Gobierno de Estados Unidos",
text: "Un sitio oficial del Gobierno de Estados Unidos",
action: "Así es como usted puede verificarlo",
},
domain: {
heading: "Los sitios web oficiales usan",
text1: "Un sitio web",
text2:
"pertenece a una organización oficial del Gobierno de Estados Unidos.",
},
https: {
heading1: "Los sitios web seguros",
heading2: "usan HTTPS",
text1: "Un <strong>candado</strong>",
text2:
"o <strong>https://</strong> significa que usted se conectó de forma segura a un sitio web",
text3:
"Comparta información sensible sólo en sitios web oficiales y seguros.",
},
},
};
}
// Get English or Spanish strings. Default to English if an unknown `lang` is passed.
// Ex: <usa-banner lang="zy"></usa-banner>
get _bannerText() {
const content = this.data[this.lang] || this.data.en;
return content;
}
// Get the action text and use for both mobile & desktop buttons.
get _actionText() {
const bannerActionText = this.querySelector('[slot="banner-action"]');
return bannerActionText?.textContent;
}
domainTemplate(tld) {
const { domain } = this._bannerText;
return html`
<img
class="usa-banner__icon usa-media-block__img"
src="${iconDotGov}"
role="img"
alt=""
aria-hidden="true"
/>
<div class="usa-media-block__body">
<p>
<strong>
<slot name="domain-heading"> ${domain.heading} .${tld} </slot>
</strong>
<br />
<slot name="domain-text">
${domain.text1} <strong>.${tld}</strong> ${domain.text2}
</slot>
</p>
</div>
`;
}
static lockIcon() {
return html`
<span
class="usa-banner__icon-lock"
role="img"
aria-label="Locked padlock icon"
part="lock-icon"
></span>
`;
}
httpsTemplate(tld) {
const { https } = this._bannerText;
return html`
<img
class="usa-banner__icon usa-media-block__img"
src="${iconHttps}"
role="img"
alt=""
aria-hidden="true"
/>
<div class="usa-media-block__body">
<p>
<strong>
<slot name="https-heading">
${https.heading1} .${tld} ${https.heading2}
</slot> </strong
><br />
<slot name="https-text">
${unsafeHTML(https.text1)} (${UsaBanner.lockIcon()})
${unsafeHTML(https.text2)} .${tld} ${https.text3}
</slot>
</p>
</div>
`;
}
static styles = [unsafeCSS(bannerStyles), unsafeCSS(bannerComponentStyles)];
render() {
const classes = { "usa-banner__header--expanded": this.isOpen };
// ? Is there a better way to fallback to a default value if passed value doesn't match?
// Example: User passes `tld="zzz"` --> uses "gov" domain instead of `zzz`.
const tld = this.tld === "mil" ? "mil" : "gov";
const { banner } = this._bannerText;
return html`
<section class="usa-banner" aria-label=${this.label || banner.label}>
<div class="usa-accordion">
<header class="usa-banner__header ${classMap(classes)}">
<div class="usa-banner__inner">
<div class="grid-col-auto">
<img
aria-hidden="true"
class="usa-banner__header-flag"
src=${usFlagSmall}
alt=""
/>
</div>
<div
class="grid-col-fill tablet:grid-col-auto"
aria-hidden="true"
>
<p class="usa-banner__header-text">
<slot name="banner-text">${banner.text}</slot>
</p>
<p class="usa-banner__header-action">
<slot name="banner-action">${banner.action}</slot>
</p>
</div>
<button
type="button"
class="usa-accordion__button usa-banner__button"
aria-expanded="${this.isOpen}"
aria-controls="gov-banner-default"
@click="${this.toggle}"
>
<span class="usa-banner__button-text">
${this._actionText || banner.action}
</span>
</button>
</div>
</header>
<div class="usa-banner__content usa-accordion__content" hidden>
<div class="grid-row grid-gap-lg">
<div class="usa-banner__guidance tablet:grid-col-6">
${this.domainTemplate(tld)}
</div>
<div class="usa-banner__guidance tablet:grid-col-6">
${this.httpsTemplate(tld)}
</div>
</div>
</div>
</div>
</section>
`;
}
}
customElements.define("usa-banner", UsaBanner);