From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../widgets/moz-message-bar/README.stories.md | 67 +++++++ .../widgets/moz-message-bar/moz-message-bar.css | 211 +++++++++++++++++++++ .../widgets/moz-message-bar/moz-message-bar.mjs | 176 +++++++++++++++++ .../moz-message-bar/moz-message-bar.stories.mjs | 123 ++++++++++++ 4 files changed, 577 insertions(+) create mode 100644 toolkit/content/widgets/moz-message-bar/README.stories.md create mode 100644 toolkit/content/widgets/moz-message-bar/moz-message-bar.css create mode 100644 toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs create mode 100644 toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs (limited to 'toolkit/content/widgets/moz-message-bar') 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 + + +``` + +## 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 + +``` + +And used as follows: + +```html + + +``` + +### 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 + +``` + +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` +
+ +
+ `; + } + return ""; + } + + headingTemplate() { + if (this.heading) { + return html`${this.heading}`; + } + return ""; + } + + closeButtonTemplate() { + if (this.dismissable) { + return html` + + `; + } + return ""; + } + + render() { + return html` + +
+
+
+ ${this.iconTemplate()} +
+ ${this.headingTemplate()} +
+ + ${this.message} + + + + +
+
+
+ + + +
+ ${this.closeButtonTemplate()} +
+ `; + } + + 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` + + ${hasSupportLink + ? html` + + ` + : ""} + ${hasActionButton + ? html` + + ` + : ""} + +`; + +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, +}; -- cgit v1.2.3