summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/moz-message-bar
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/content/widgets/moz-message-bar
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/content/widgets/moz-message-bar')
-rw-r--r--toolkit/content/widgets/moz-message-bar/README.stories.md67
-rw-r--r--toolkit/content/widgets/moz-message-bar/moz-message-bar.css211
-rw-r--r--toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs176
-rw-r--r--toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs123
4 files changed, 577 insertions, 0 deletions
diff --git a/toolkit/content/widgets/moz-message-bar/README.stories.md b/toolkit/content/widgets/moz-message-bar/README.stories.md
new file mode 100644
index 0000000000..c3fc5a88eb
--- /dev/null
+++ b/toolkit/content/widgets/moz-message-bar/README.stories.md
@@ -0,0 +1,67 @@
+# MozMessageBar
+
+`moz-message-bar` is a versatile user interface element designed to display messages or notifications.
+These messages and notifications are nonmodal, and keep users informed without blocking access to the base page.
+It supports various types of messages - info, warning, success, and error - each with distinct visual styling
+to convey the message's urgency or importance. You can customize `moz-message-bar` by adding a message, message heading,
+`moz-support-link`, actions buttons, or by making the message bar dismissable.
+
+```html story
+<moz-message-bar dismissable
+ heading="Heading of the message bar"
+ message="Message for the user">
+</moz-message-bar>
+```
+
+## When to use
+
+* Use the message bar to display important announcements or notifications to the user.
+* Use it to attract the user's attention without interrupting the user's task.
+
+## When not to use
+
+* Do not use the message bar for displaying critical alerts or warnings that require immediate and focused attention.
+
+## Code
+
+The source for `moz-message-bar` can be found under
+[toolkit/content/widgets/moz-message-bar](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs).
+You can find an examples of `moz-message-bar` in use in the Firefox codebase in
+[about:addons](https://searchfox.org/mozilla-central/source/toolkit/mozapps/extensions/content/aboutaddons.html),
+[unified extensions panel](https://searchfox.org/mozilla-central/source/browser/base/content/browser-addons.js) and
+[shopping components](https://searchfox.org/mozilla-central/source/browser/components/shopping/content/shopping-message-bar.mjs).
+
+`moz-message-bar` can be imported into `.html`/`.xhtml` files:
+
+```html
+<script type="module" src="chrome://global/content/elements/moz-message-bar.mjs"></script>
+```
+
+And used as follows:
+
+```html
+<moz-message-bar dismissable
+ heading="Heading of the message bar"
+ message="Message for the user">
+</moz-message-bar>
+```
+
+### Fluent usage
+
+Generally the `heading` and `message` properties of
+`moz-message-bar` will be provided via [Fluent attributes](https://mozilla-l10n.github.io/localizer-documentation/tools/fluent/basic_syntax.html#attributes).
+To get this working you will need to specify a `data-l10n-id` as well as
+`data-l10n-attrs` if you're providing a heading and a message:
+
+```html
+<moz-message-bar data-l10n-id="with-heading-and-message"
+ data-l10n-attrs="heading, message"></moz-message-bar>
+```
+
+In which case your Fluent messages will look something like this:
+
+```
+with-heading-and-message =
+ .heading = Heading text goes here
+ .message = Message text goes here
+```
diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.css b/toolkit/content/widgets/moz-message-bar/moz-message-bar.css
new file mode 100644
index 0000000000..6d35009982
--- /dev/null
+++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.css
@@ -0,0 +1,211 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+:host {
+ /* Icon */
+ --message-bar-icon-color: var(--icon-color-information);
+ --message-bar-icon-size: var(--size-item-small);
+ --message-bar-icon-close-color: var(--icon-color);
+ --message-bar-icon-close-url: url("chrome://global/skin/icons/close-12.svg");
+
+ /* Button */
+ --message-bar-button-size-ghost: var(--button-min-height);
+ --message-bar-button-border-radius-ghost: var(--button-border-radius);
+ --message-bar-button-background-color-ghost-hover: var(--button-background-color-hover);
+ --message-bar-button-background-color-ghost-active: var(--button-background-color-active);
+
+ /* Container */
+ --message-bar-container-min-height: var(--size-item-large);
+
+ /* Border */
+ --message-bar-border-color: color-mix(in srgb, currentColor 9%, transparent);
+ --message-bar-border-radius: var(--border-radius-small);
+ --message-bar-border-width: var(--border-width);
+
+ /* Text */
+ --message-bar-text-color: var(--text-color);
+ --message-bar-text-line-height: 1.5em;
+
+ /* Background */
+ --message-bar-background-color: var(--color-background-information);
+
+ background-color: var(--message-bar-background-color);
+ border: var(--message-bar-border-width) solid var(--message-bar-border-color);
+ border-radius: var(--message-bar-border-radius);
+ color: var(--message-bar-text-color);
+}
+
+@media (prefers-contrast) {
+ :host {
+ --message-bar-border-color: var(--border-color);
+ }
+}
+
+/* Make the host to behave as a block by default, but allow hidden to hide it. */
+:host(:not([hidden])) {
+ display: block;
+}
+
+/* MozMessageBar layout */
+
+.container {
+ display: flex;
+ gap: 8px;
+ min-height: var(--message-bar-container-min-height);
+ padding-inline: 16px 8px;
+ padding-block: 8px;
+}
+
+.content {
+ display: flex;
+ flex-grow: 1;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 8px 12px;
+ margin-inline-start: 24px;
+}
+
+.text-container {
+ display: flex;
+ gap: 4px 8px;
+ padding-block: calc((var(--message-bar-container-min-height) - var(--message-bar-text-line-height)) / 2);
+}
+
+.text-content {
+ display: inline-flex;
+ gap: 4px 8px;
+ flex-wrap: wrap;
+ word-break: break-word;
+ line-height: var(--message-bar-text-line-height);
+}
+
+/* MozMessageBar icon style */
+
+.icon-container {
+ height: var(--message-bar-text-line-height);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-inline-start: -24px;
+}
+
+.icon {
+ width: var(--message-bar-icon-size);
+ height: var(--message-bar-icon-size);
+ flex-shrink: 0;
+ appearance: none;
+ -moz-context-properties: fill, stroke;
+ fill: currentColor;
+ stroke: currentColor;
+ color: var(--message-bar-icon-color);
+}
+
+/* MozMessageBar heading style */
+
+.heading {
+ font-weight: 600;
+}
+
+/* MozMessageBar message style */
+
+.message {
+ margin-inline-end: 4px;
+}
+
+/* MozMessageBar link style */
+
+.link {
+ display: inline-block;
+}
+
+.link ::slotted(a) {
+ margin-inline-end: 4px;
+}
+
+/* MozMessageBar actions style */
+
+.actions {
+ display: none;
+}
+
+.actions.active {
+ display: inline-flex;
+ gap: 8px;
+}
+
+.actions ::slotted(button) {
+ /* Enforce micro-button width. */
+ min-width: fit-content !important;
+
+ margin: 0 !important;
+ padding: 4px 16px !important;
+}
+
+/* Close icon styles */
+
+.close {
+ background-image: var(--message-bar-icon-close-url);
+ background-repeat: no-repeat;
+ background-position: center center;
+ -moz-context-properties: fill;
+ fill: currentColor;
+ min-width: auto;
+ min-height: auto;
+ width: var(--message-bar-button-size-ghost);
+ height: var(--message-bar-button-size-ghost);
+ margin: 0;
+ padding: 0;
+ flex-shrink: 0;
+}
+
+.ghost-button {
+ border-radius: var(--message-bar-button-border-radius-ghost);
+}
+
+.ghost-button:enabled:hover {
+ background-color: var(--message-bar-button-background-color-ghost-hover);
+}
+
+.ghost-button:enabled:hover:active {
+ background-color: var(--message-bar-button-background-color-ghost-active);
+}
+
+@media not (prefers-contrast) {
+ /* MozMessageBar colors by message type */
+ /* Colors from: https://www.figma.com/file/zd3B9UyknB2XNZNdrYLm2W/Outreachy?type=design&node-id=59-1921&mode=design&t=ZYS4e6pAbAlXGvun-4 */
+
+ :host([type=warning]) {
+ --message-bar-background-color: var(--color-background-warning);
+
+ .icon {
+ --message-bar-icon-color: var(--icon-color-warning);
+ }
+ }
+
+ :host([type=success]) {
+ --message-bar-background-color: var(--color-background-success);
+
+ .icon {
+ --message-bar-icon-color: var(--icon-color-success);
+ }
+ }
+
+ :host([type=error]),
+ :host([type=critical]) {
+ --message-bar-background-color: var(--color-background-critical);
+
+ .icon {
+ --message-bar-icon-color: var(--icon-color-critical);
+ }
+ }
+
+ .close {
+ fill: var(--message-bar-icon-close-color);
+ }
+
+ .ghost-button {
+ border: none;
+ background-color: transparent;
+ }
+}
diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs
new file mode 100644
index 0000000000..58f41c28e4
--- /dev/null
+++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs
@@ -0,0 +1,176 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { html, ifDefined } from "../vendor/lit.all.mjs";
+import { MozLitElement } from "../lit-utils.mjs";
+
+const messageTypeToIconData = {
+ info: {
+ iconSrc: "chrome://global/skin/icons/info-filled.svg",
+ l10nId: "moz-message-bar-icon-info",
+ },
+ warning: {
+ iconSrc: "chrome://global/skin/icons/warning.svg",
+ l10nId: "moz-message-bar-icon-warning",
+ },
+ success: {
+ iconSrc: "chrome://global/skin/icons/check-filled.svg",
+ l10nId: "moz-message-bar-icon-success",
+ },
+ error: {
+ iconSrc: "chrome://global/skin/icons/error.svg",
+ l10nId: "moz-message-bar-icon-error",
+ },
+ critical: {
+ iconSrc: "chrome://global/skin/icons/error.svg",
+ l10nId: "moz-message-bar-icon-error",
+ },
+};
+
+/**
+ * A simple message bar element that can be used to display
+ * important information to users.
+ *
+ * @tagname moz-message-bar
+ * @property {string} type - The type of the displayed message.
+ * @property {string} heading - The heading of the message.
+ * @property {string} message - The message text.
+ * @property {boolean} dismissable - Whether or not the element is dismissable.
+ * @property {string} messageL10nId - l10n ID for the message.
+ * @property {string} messageL10nArgs - Any args needed for the message l10n ID.
+ * @fires message-bar:close
+ * Custom event indicating that message bar was closed.
+ * @fires message-bar:user-dismissed
+ * Custom event indicating that message bar was dismissed by the user.
+ */
+
+export default class MozMessageBar extends MozLitElement {
+ static queries = {
+ actionsSlotEl: "slot[name=actions]",
+ actionsEl: ".actions",
+ closeButtonEl: "button.close",
+ supportLinkSlotEl: "slot[name=support-link]",
+ };
+
+ static properties = {
+ type: { type: String },
+ heading: { type: String },
+ message: { type: String },
+ dismissable: { type: Boolean },
+ messageL10nId: { type: String },
+ messageL10nArgs: { type: String },
+ };
+
+ constructor() {
+ super();
+ window.MozXULElement?.insertFTLIfNeeded("toolkit/global/mozMessageBar.ftl");
+ this.type = "info";
+ this.dismissable = false;
+ }
+
+ onSlotchange(e) {
+ let actions = this.actionsSlotEl.assignedNodes();
+ this.actionsEl.classList.toggle("active", actions.length);
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.setAttribute("role", "status");
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.dispatchEvent(new CustomEvent("message-bar:close"));
+ }
+
+ get supportLinkEls() {
+ return this.supportLinkSlotEl.assignedElements();
+ }
+
+ iconTemplate() {
+ let iconData = messageTypeToIconData[this.type];
+ if (iconData) {
+ let { iconSrc, l10nId } = iconData;
+ return html`
+ <div class="icon-container">
+ <img
+ class="icon"
+ src=${iconSrc}
+ data-l10n-id=${l10nId}
+ data-l10n-attrs="alt"
+ />
+ </div>
+ `;
+ }
+ return "";
+ }
+
+ headingTemplate() {
+ if (this.heading) {
+ return html`<strong class="heading">${this.heading}</strong>`;
+ }
+ return "";
+ }
+
+ closeButtonTemplate() {
+ if (this.dismissable) {
+ return html`
+ <button
+ class="close ghost-button"
+ data-l10n-id="moz-message-bar-close-button"
+ @click=${this.dismiss}
+ ></button>
+ `;
+ }
+ return "";
+ }
+
+ render() {
+ return html`
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/elements/moz-message-bar.css"
+ />
+ <div class="container">
+ <div class="content">
+ <div class="text-container">
+ ${this.iconTemplate()}
+ <div class="text-content">
+ ${this.headingTemplate()}
+ <div>
+ <span
+ class="message"
+ data-l10n-id=${ifDefined(this.messageL10nId)}
+ data-l10n-args=${ifDefined(
+ JSON.stringify(this.messageL10nArgs)
+ )}
+ >
+ ${this.message}
+ </span>
+ <span class="link">
+ <slot name="support-link"></slot>
+ </span>
+ </div>
+ </div>
+ </div>
+ <span class="actions">
+ <slot name="actions" @slotchange=${this.onSlotchange}></slot>
+ </span>
+ </div>
+ ${this.closeButtonTemplate()}
+ </div>
+ `;
+ }
+
+ dismiss() {
+ this.dispatchEvent(new CustomEvent("message-bar:user-dismissed"));
+ this.close();
+ }
+
+ close() {
+ this.remove();
+ }
+}
+
+customElements.define("moz-message-bar", MozMessageBar);
diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs b/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs
new file mode 100644
index 0000000000..65803eed9f
--- /dev/null
+++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint-disable import/no-unassigned-import */
+
+import { html, ifDefined } from "../vendor/lit.all.mjs";
+import "./moz-message-bar.mjs";
+import "../moz-support-link/moz-support-link.mjs";
+
+const fluentStrings = [
+ "moz-message-bar-message",
+ "moz-message-bar-message-heading",
+ "moz-message-bar-message-heading-long",
+];
+
+export default {
+ title: "UI Widgets/Message Bar",
+ component: "moz-message-bar",
+ argTypes: {
+ type: {
+ options: ["info", "warning", "success", "error"],
+ control: { type: "select" },
+ },
+ l10nId: {
+ options: fluentStrings,
+ control: { type: "select" },
+ },
+ heading: {
+ table: {
+ disable: true,
+ },
+ },
+ message: {
+ table: {
+ disable: true,
+ },
+ },
+ },
+ parameters: {
+ status: "stable",
+ fluent: `
+moz-message-bar-message =
+ .message = For your information message
+moz-message-bar-message-heading =
+ .heading = Heading
+ .message = For your information message
+moz-message-bar-message-heading-long =
+ .heading = A longer heading to check text wrapping in the message bar
+ .message = Some message that we use to check text wrapping. Some message that we use to check text wrapping.
+moz-message-bar-button = Click me!
+ `,
+ },
+};
+
+const Template = ({
+ type,
+ heading,
+ message,
+ l10nId,
+ dismissable,
+ hasSupportLink,
+ hasActionButton,
+}) => html`
+ <moz-message-bar
+ type=${type}
+ heading=${ifDefined(heading)}
+ message=${ifDefined(message)}
+ data-l10n-id=${ifDefined(l10nId)}
+ data-l10n-attrs="heading, message"
+ ?dismissable=${dismissable}
+ >
+ ${hasSupportLink
+ ? html`
+ <a
+ is="moz-support-link"
+ support-page="addons"
+ slot="support-link"
+ ></a>
+ `
+ : ""}
+ ${hasActionButton
+ ? html`
+ <button data-l10n-id="moz-message-bar-button" slot="actions"></button>
+ `
+ : ""}
+ </moz-message-bar>
+`;
+
+export const Default = Template.bind({});
+Default.args = {
+ type: "info",
+ l10nId: "moz-message-bar-message",
+ dismissable: false,
+ hasSupportLink: false,
+ hasActionButton: false,
+};
+
+export const Dismissable = Template.bind({});
+Dismissable.args = {
+ type: "info",
+ l10nId: "moz-message-bar-message",
+ dismissable: true,
+ hasSupportLink: false,
+ hasActionButton: false,
+};
+
+export const WithActionButton = Template.bind({});
+WithActionButton.args = {
+ type: "info",
+ l10nId: "moz-message-bar-message",
+ dismissable: false,
+ hasSupportLink: false,
+ hasActionButton: true,
+};
+
+export const WithSupportLink = Template.bind({});
+WithSupportLink.args = {
+ type: "info",
+ l10nId: "moz-message-bar-message",
+ dismissable: false,
+ hasSupportLink: true,
+ hasActionButton: false,
+};