diff options
Diffstat (limited to 'browser/components/storybook/docs')
6 files changed, 997 insertions, 0 deletions
diff --git a/browser/components/storybook/docs/README.lit-guide.stories.md b/browser/components/storybook/docs/README.lit-guide.stories.md new file mode 100644 index 0000000000..577b17f416 --- /dev/null +++ b/browser/components/storybook/docs/README.lit-guide.stories.md @@ -0,0 +1,157 @@ +# Lit + +## Background + +[Lit](https://lit.dev) is a small library for creating web components that is maintained by Google. It aims to improve the experience of authoring web components by eliminating boilerplate and providing more declarative syntax and re-rendering optimizations, and should feel familiar to developers who have experience working with popular component-based front end frameworks. + +Mozilla developers began experimenting with using Lit to build a handful of new web components in 2021. The developer experience and productivity benefits were noticeable enough that the team tasked with building out a library of new [reusable widgets](./README.reusable-widgets.stories.md) vendored Lit to make it available in `mozilla-central` in late 2022. Lit can now be used for creating new web components anywhere in the codebase. + +## Using Lit + +Lit has comprehensive documentation on their website that should be consulted alongside this document when building new Lit-based custom elements: [https://lit.dev/docs/](https://lit.dev/docs/) + +While Lit was initially introduced to assist with the work of the Reusable Components team it can also be used for creating both reusable and domain-specific UI widgets throughout `mozilla-central`. Some examples of custom elements that have been created using Lit so far include [moz-toggle](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/moz-toggle/moz-toggle.mjs), [moz-button-group](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/moz-button-group/moz-button-group.mjs), and the Credential Management team's [login-timeline](https://searchfox.org/mozilla-central/source/browser/components/aboutlogins/content/components/login-timeline.mjs) component. + +### When to use Lit + +Lit may be a particularly good choice if you're building a highly reactive element that needs to respond efficiently to state changes. Lit's declarative [templates](https://lit.dev/docs/templates/overview/) and [reactive properties](https://lit.dev/docs/components/properties/) can take care of a lot of the work of figuring out which parts of the UI should update in response to specific changes. + +Because Lit components are ultimately just web components, you may also want to use it just because of some of the syntax it provides, like allowing you to write your template code next to your JavaScript code, providing for binding event listeners and properties in your templates, and automatically creating an open `shadowRoot`. + +### When not to use Lit + +Lit cannot be used in cases where you want to [extend a built-in element](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#customized_built-in_elements). Lit can only be used for creating [autonomous custom elements](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#autonomous_custom_elements), i.e. elements that extend `HTMLElement`. + +## Writing components with Lit + +All of the standard features of the Lit library - with the exception of decorators - are available for use in `mozilla-central`, but there are some special considerations and specific files you should be aware of when using Lit for Firefox code. + +### Using external stylesheets + +Using external stylesheets is the preferred way to style your Lit-based components in `mozilla-central`, despite the fact that the the Lit documentation [explicitly recommends against](https://lit.dev/docs/components/styles/#external-stylesheet) this approach. The caveats they list are not particularly relevant to our use cases, and we have implemented platform level workarounds to ensure external styles will not cause a flash-of-unstyled-content. Using external stylesheets makes it so that CSS changes can be detected by our automated linting and review tools, and helps provide greater visibility to Mozilla's `desktop-theme-reviewers` group. + +### The `lit.all.mjs` vendor file + +A somewhat customized, vendored version of Lit is available at [toolkit/content/widgets/vendor/lit.all.mjs](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/vendor/lit.all.mjs). The version of Lit in `mozilla-central` has a number of patches applied to disable minification, source maps, and certain warning messages, as well as patches to replace usage of `innerHTML` with `DOMParser` and to slightly modify the behavior of the `styleMap` directive. More specifics on these patches, as well as information on how to update `lit.all.mjs`, can be found [here](https://searchfox.org/mozilla-central/source/toolkit/content/vendor/lit). + +Because our vendored version of Lit bundles the contents of a few different Lit source files into a single file, imports that would normally come from different files are pulled directly from `lit.all.mjs`. For example, imports that look like this when using the Lit npm package: + +```js +// Standard npm package. +import { LitElement } from "lit"; +import { classMap } from "lit/directives/class-map.js"; +import { ifDefined } from "lit/directives/if-defined.js"; +``` + +Would look like this in `mozilla-central`: + +```js +// All imports come from a single file (relative path also works). +import { LitElement, classMap, ifDefined } from "chrome://global/content/vendor/lit.all.mjs"; +``` + +### `MozLitElement` and `lit-utils.mjs` + +[MozLitElement](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/lit-utils.mjs#84) is an extension of the `LitElement` class that has added functionality to make it more tailored to Mozilla developers' needs. In almost all cases `MozLitElement` should be used as the base class for your new Lit-based custom elements in place of `LitElement`. + +It can be imported from `lit-utils.js` and used as follows: + +```js +import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; + +class MyCustomElement extends MozLitElement { + ... +} +``` + +`MozLitElement` differs from `LitElement` in a few important ways: + +#### 1. It provides automatic Fluent support for the shadow DOM + +When working with Fluent in the shadow DOM an element's `shadowRoot` must be connected before Fluent can be used. `MozLitElement` handles this by extending `LitElement`'s `connectedCallback` to [call](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/lit-utils.mjs#84) `document.l10n.connectRoot` if needed. `MozLitElement` also automatically calls `document.l10n.translateFragment` on the renderRoot anytime an element updates. The net result of these modifications is that you can use Fluent in your Lit based components just like you would in any other markup in `mozilla-central`. + +#### 2. It implements support for Lit's `@query` and `@queryAll` decorators + +The Lit library includes `@query` and `@queryAll` [decorators](https://lit.dev/docs/components/shadow-dom/#@query-@queryall-and-@queryasync-decorators) that provide an easy way of finding elements within the internal component DOM. These do not work in `mozilla-central` as we do not have support for JavaScript decorators. Instead, `MozLitElement` provides equivalent [DOM querying functionality](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/lit-utils.mjs#87-99) via defining a static `queries` property on the subclass. For example the following Lit code that queries the component's DOM for certain selectors and assigns the results to different class properties: + +```ts +import { LitElement, html } from "lit"; +import { query } from "lit/decorators/query.js"; + +class MyCustomElement extends LitElement { + @query("#title"); + _title; + + @queryAll("p"); + _paragraphs; + + render() { + return html` + <p id="title">The title</p> + <p>Some other paragraph.</p> + `; + } +} +``` + +Is equivalent to this in `mozilla-central`: + +```js +import { html } from "chrome://global/content/vendor/lit.all.mjs"; +import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; + +class MyCustomElement extends MozLitElement { + static queries = { + _title: "#title", // equivalent to @query + _paragraphs: { all: "p" }, // equivalent to @queryAll + }; + + render() { + return html` + <p id="title">The title</p> + <p>Some other paragraph.</p> + `; + } +} +``` + +#### 3. It adds a `dispatchOnUpdateComplete` method + +The `dispatchOnUpdateComplete` method provides an easy way to communicate to test code or other element consumers that a reactive property change has taken effect. It leverages Lit's [updateComplete](https://lit.dev/docs/components/lifecycle/#updatecomplete) promise to emit an event after all updates have been applied and the component's DOM is ready to be queried. It has the potential to be particularly useful when you need to query the DOM in test code, for example: + +```js +// my-custom-element.mjs +class MyCustomElement extends MozLitElement { + static properties = { + clicked: { type: Boolean }, + }; + + async handleClick() { + if (!this.clicked) { + this.clicked = true; + } + this.dispatchOnUpdateComplete(new CustomEvent("button-clicked")); + } + + render() { + return html` + <p>The button was ${this.clicked ? "clicked" : "not clicked"}</p> + <button @click=${this.handleClick}>Click me!</button> + `; + } +} +``` + +```js +// test_my_custom_element.mjs +add_task(async function testButtonClicked() { + let { button, message } = this.convenientHelperToGetElements(); + is(message.textContent.trim(), "The button was not clicked"); + + let clicked = BrowserTestUtils.waitForEvent(button, "button-clicked"); + synthesizeMouseAtCenter(button, {}); + await clicked; + + is(message.textContent.trim(), "The button was clicked"); +}); +``` diff --git a/browser/components/storybook/docs/README.other-widgets.stories.md b/browser/components/storybook/docs/README.other-widgets.stories.md new file mode 100644 index 0000000000..b2614d88b6 --- /dev/null +++ b/browser/components/storybook/docs/README.other-widgets.stories.md @@ -0,0 +1,84 @@ +# Other types of UI Widgets + +In addition to the [new reusable UI widgets](https://firefox-source-docs.mozilla.org/browser/components/storybook/docs/README.reusable-widgets.stories.html) there are existing elements that serve a similar role. +These older elements are broken down into two groups: Mozilla Custom Elements and User Agent (UA) Widgets. +Additionally, we also have domain-specific widgets that are similar to the reusable widgets but are created to serve a specific need and may or may not adhere to the Design Systems specifications. + +## Older custom elements in `toolkit/widgets` + +There are existing UI widgets in `toolkit/content/widgets/` that belong to one of two groups: Mozilla Custom Elements or User Agent (UA) Widgets. +These [existing custom elements](https://searchfox.org/mozilla-central/rev/cde3d4a8d228491e8b7f1bd94c63bbe039850696/toolkit/content/customElements.js#792-809,847-866) are loaded into all privileged main process documents automatically. +You can determine if a custom element belongs to the existing UI widgets category by either [viewing the array](https://searchfox.org/mozilla-central/rev/cde3d4a8d228491e8b7f1bd94c63bbe039850696/toolkit/content/customElements.js#792-809,847-866) or by viewing the [files in toolkit/content/widgets](https://searchfox.org/mozilla-central/source/toolkit/content/widgets). +Additionally, these older custom elements are a mix of XUL and HTML elements. + + +### Mozilla Custom Elements + +Unlike newer reusable UI widgets, the older Mozilla Custom Elements do not have a dedicated directory. +For example `arrowscrollbox.js` is an older single file custom element versus `moz-button-group/moz-button-group.mjs` which exemplifies the structure followed by newer custom elements. + +### User Agent (UA) widgets + +User agent (UA) widgets are like custom elements but run in per-origin UA widget scope instead of the chrome or content scope. +There are a much smaller number of these widgets compared to the Mozilla Custom Elements: +- [datetimebox.js](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/datetimebox.js) +- [marquee.js](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/marquee.js) +- [textrecognition.js](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/textrecognition.js) +- [videocontrols.js](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/videocontrols.js) + +Please refer to the existing [UA widgets documentation](https://firefox-source-docs.mozilla.org/toolkit/content/toolkit_widgets/ua_widget.html) for more details. + +### How to use existing Mozilla Custom Elements + +The existing Mozilla Custom Elements are automatically imported into all chrome privileged documents. +These existing elements do not need to be imported individually via `<script>` tag or by using `window.ensureCustomElements()` when in a privileged main process document. +As long as you are working in a chrome privileged document, you will have access to the existing Mozilla Custom Elements. +You can dynamically create one of the existing custom elements by using `document.createDocument("customElement)` or `document.createXULElement("customElement")` in the relevant JS file, or by using the custom element tag in the relevant XHTML document. +For example, `document.createXULElement("checkbox")` creates an instance of [widgets/checkbox.js](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/checkbox.js) while using `<checkbox>` declares an instance in the XUL document. +You can see the checkbox widget in use in about:preferences. + +### How to use existing UA Widgets + +Please refer to the [existing UA widgets documentation](https://firefox-source-docs.mozilla.org/toolkit/content/toolkit_widgets/ua_widget.html) for more details on how to use these widgets. + +### How these widgets behave in chrome versus in-content + +The UA widgets are intended for web content, they may or may not work in-content or in the chrome. +However since these widgets run in a specific UA widget scope, their DOM cannot be accessed while in a content process. + +The existing Mozilla Custom Elements can only be used in privileged content processes or in the chrome. +Since these elements cannot be used in regular web content, there is no difference in how they behave in the chrome versus in-content. + +## Domain-specific Widgets + +Not all reusable components should be used throughout the entirety of Firefox. +While we do have global UI patterns and components such as a button and modals, there are also specific features within the browser that include their own unique reusable patterns. +These patterns and components are known as "domain-specific widgets". +For example, the [login-timeline widget](https://searchfox.org/mozilla-central/source/browser/components/aboutlogins/content/components/login-timeline.mjs) is a domain-specific widget. + +### Adding new domain-specific widgets + +While we do have the `./mach addwidget` command, as noted in the [adding new design system components document](#adding-new-design-system-components), this command does not currently support the domain-specific widget case. +[See Bug 1828181 for more details on supporting this case](https://bugzilla.mozilla.org/show_bug.cgi?id=1828181). +Instead, you will need to use `./mach addstory your-widget "your-project-or-team-name"` to create a new story in `browser/components/storybook/stories`. +You can also use `./mach addstory your-widget "your-project-or-team-name" --path <your-widget-path>/<widget-source>` so that your widget's source is imported correctly. + +If you specified a path and have Storybook running, a blank story entry for your widget will appear under the Domain-Specific UI Widgets/your-project section. +Otherwise, if you have not added a path, Storybook will throw an error saying that it "Can't resolve 'None'" in the newly added story. +This is expected and serves as a reminder that you need to update the newly created story with the widget source import. + +Since Storybook is unaware of how the actual code is added or built, we won't go into detail about adding your new widget to the codebase. +It's recommended to have a `.html` test for your new widget since this lets you unit test the component directly rather than using integration tests with the domain. +To see what kind of files you may need for your widget, please refer back to [the output of the `./mach addwidget` command](#adding-new-design-system-components). +Just like with the UI widgets, [the `browser_all_files_referenced.js` will fail unless you use your component immediately outside of Storybook.](#known-browser_all_files_referencedjs-issue) + +### Using new domain-specific widgets + +This is effectively the same as [using new design system components](#using-new-design-system-components). +You will need to import your widget into the relevant `html`/`xhtml` files via a `script` tag with `type="module"`: + +```html +<script type="module" src="chrome://browser/content/<domain-directory>/<your-widget>.mjs"></script> +``` + +Or use `window.ensureCustomElements("<your-widget>")` as previously stated in [the using new design system components section.](#using-new-design-system-components) diff --git a/browser/components/storybook/docs/README.reusable-widgets.stories.md b/browser/components/storybook/docs/README.reusable-widgets.stories.md new file mode 100644 index 0000000000..f26c18a2b0 --- /dev/null +++ b/browser/components/storybook/docs/README.reusable-widgets.stories.md @@ -0,0 +1,152 @@ +# Reusable UI widgets + +## Background + +Different Firefox surfaces make use of similar UI elements such as cards, menus, +toggles, and message bars. A group of designers and developers have started +working together to create standardized versions of these elements in the form +of new web components. The intention is for these components to encapsulate our +design system, ensure accessibility and usability across the application, and +reduce the maintenance burden associated with supporting multiple different +implementations of the same UI patterns. + +Many of these components are being built using the [Lit +library](https://lit.dev/) to take advantage of its templating syntax and +re-rendering logic. All new components are being documented in Storybook in an +effort to create a catalog that engineers and designers can use to see which +components can be easily lifted off the shelf for use throughout Firefox. + +## Designing new reusable widgets + +Widgets that live at the global level, "UI Widgets", should be created in collaboration with the Design System team. +This ensures consistency with the rest of the elements in the Design System and the existing UI elements. +Otherwise, you should consult with your team and the appropriate designer to create domain-specific UI widgets. +Ideally, these domain widgets should be consistent with the rest of the UI patterns established in Firefox. + +### Does an existing widget cover the use case you need? + +Before creating a new reusable widget, make sure there isn't a widget you could use already. +When designing a new reusable widget, ensure it is designed for all users. +Here are some questions you can use to help include all users: how will people perceive, operate, and understand this widget? Will the widget use standards proven technology. +[Please refer to the "General Considerations" section of the Mozilla Accessibility Release Guidelines document](https://wiki.mozilla.org/Accessibility/Guidelines#General_Considerations) for more details to ensure your widget adheres to accessibility standards. + +### Supporting widget use in different processes + +A newly designed widget may need to work in the parent process, the content process, or both depending on your use case. +[See the Process Model document for more information about these different processes](https://firefox-source-docs.mozilla.org/dom/ipc/process_model.html). +You will likely be using your widget in a privileged process (such as the parent or privileged content) with access to `Services`, `XPCOMUtils`, and other globals. +Storybook and other web content do not have access to these privileged globals, so you will need to write workarounds for `Services`, `XPCOMUtils`, chrome URIs for CSS files and assets, etc. +[Check out moz-support-link.mjs and moz-support-link.stories.mjs for an example of a widget being used in the parent/chrome and needing to handle `XPCOMUtils` in Storybook](https://searchfox.org/mozilla-central/search?q=moz-support-link&path=&case=false®exp=false). +[See moz-toggle.mjs for handling chrome URIs for CSS in Storybook](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/moz-toggle/moz-toggle.mjs). +[See moz-label.mjs for an example of handling `Services` in Storybook](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/moz-label/moz-label.mjs). + +### Autonomous or Customized built-in Custom Elements + +There are two types of custom elements, autonomous elements that extend `HTMLElement` and customized built-in elements that extend basic HTML elements. +If you use autonomous elements, you can use Shadow DOM and/or the Lit library. +[Lit does not support customized built-in custom elements](https://github.com/lit/lit-element/issues/879). + +In some cases, you may want to provide some functionality on top of a built-in HTML element, [like how `moz-support-link` prepares the `href` value for anchor elements](https://searchfox.org/mozilla-central/rev/3563da061ca2b32f7f77f5f68088dbf9b5332a9f/toolkit/content/widgets/moz-support-link/moz-support-link.mjs#83-89). +In other cases, you may want to focus on creating markup and reacting to changes on the element. +This is where Lit can be useful for declaritively defining the markup and reacting to changes when attributes are updated. + +### How will developers use your widget? + +What does the interface to your widget look like? +Do you expect developers to use reactive attributes or [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots#adding_flexibility_with_slots)? +If there are many ways to accomplish the same end result, this could result in future confusion and increase the maintainance cost. + +You should write stories for your widget to demonstrate how it can be used. +These stories can be used as guides for new use cases that may appear in the future. +This can also help draw the line for the responsibilities of your widget. + +## Adding new design system components + +We have a `./mach addwidget` scaffold command to make it easier to create new +reusable components and hook them up to Storybook. Currently this command can +only be used to add a new Lit based web component to `toolkit/content/widgets`. +In the future we may expand it to support options for creating components +without using Lit and for adding components to different directories. +See [Bug 1803677](https://bugzilla.mozilla.org/show_bug.cgi?id=1803677) for more details on these future use cases. + +To create a new component, you run: + +```sh +# Component names should be in kebab-case and contain at least 1 -. +./mach addwidget component-name +``` + +The scaffold command will generate the following files: + +```sh +└── toolkit + └── content + ├── tests + │ └── widgets + │ └── test_component_name.html # chrome test + └── widgets + └── component-name # new folder for component code + ├── component-name.css # component specific CSS + ├── component-name.mjs # Lit based component + └── component-name.stories.mjs # component stories +``` + +It will also make modifications to `toolkit/content/jar.mn` to add `chrome://` +URLs for the new files, and to `toolkit/content/tests/widgets/chrome.ini` to +enable running the newly added test. + +After running the scaffold command you can start Storybook and you will see +placeholder content that has been generated for your component. You can then +start altering the generated files and see your changes reflected in Storybook. + +### Known `browser_all_files_referenced.js` issue + +Unfortunately for now [the +browser_all_files_referenced.js test](https://searchfox.org/mozilla-central/source/browser/base/content/test/static/browser_all_files_referenced.js) +will fail unless your new component is immediately used somewhere outside +of Storybook. We have plans to fix this issue, [see Bug 1806002 for more details](https://bugzilla.mozilla.org/show_bug.cgi?id=1806002), but for now you can get around it +by updating [this array](https://searchfox.org/mozilla-central/rev/5c922d8b93b43c18bf65539bfc72a30f84989003/browser/base/content/test/static/browser_all_files_referenced.js#113) to include your new chrome filepath. + +### Using new design system components + +Once you've added a new component to `toolkit/content/widgets` and created +`chrome://` URLs via `toolkit/content/jar.mn` you should be able to start using it +throughout Firefox. You can import the component into `html`/`xhtml` files via a +`script` tag with `type="module"`: + +```html +<script type="module" src="chrome://global/content/elements/your-component-name.mjs"></script> +``` + +If you are unable to import the new component in html, you can use [`ensureCustomElements()` in customElements.js](https://searchfox.org/mozilla-central/rev/31f5847a4494b3646edabbdd7ea39cb88509afe2/toolkit/content/customElements.js#865) in the relevant JS file. +For example, [we use `window.ensureCustomElements("moz-button-group")` in `browser-siteProtections.js`](https://searchfox.org/mozilla-central/rev/31f5847a4494b3646edabbdd7ea39cb88509afe2/browser/base/content/browser-siteProtections.js#1749). +**Note** you will need to add your new widget to [the switch in importCustomElementFromESModule](https://searchfox.org/mozilla-central/rev/85b4f7363292b272eb9b606e00de2c37a6be73f0/toolkit/content/customElements.js#845-859) for `ensureCustomElements()` to work as expected. +Once [Bug 1803810](https://bugzilla.mozilla.org/show_bug.cgi?id=1803810) lands, this process will be simplified: you won't need to use `ensureCustomElements()` and you will [add your widget to the appropriate array in customElements.js instead of the switch statement](https://searchfox.org/mozilla-central/rev/85b4f7363292b272eb9b606e00de2c37a6be73f0/toolkit/content/customElements.js#818-841). + +## Common pitfalls + +If you're trying to use a reusable widget but nothing is appearing on the +page it may be due to one of the following issues: + +- Omitting the `type="module"` in your `script` tag. +- Wrong file path for the `src` of your imported module. +- Widget is not declared or incorrectly declared in the correct `jar.mn` file. +- Not specifying the `html:` namespace when using a custom HTML element in an + `xhtml` file. For example the tag should look something like this: + + ```html + <html:your-component-name></html:your-component-name> + ``` +- Adding a `script` tag to an `inc.xhtml` file. For example when using a new + component in the privacy section of `about:preferences` the `script` tag needs + to be added to `preferences.xhtml` rather than to `privacy.inc.xhtml`. +- Trying to extend a built-in HTML element in Lit. [Because Webkit never + implemented support for customized built-ins, Lit doesn't support it either.](https://github.com/lit/lit-element/issues/879#issuecomment-1061892879) + That means if you want to do something like: + + ```js + customElements.define("cool-button", CoolButton, { extends: "button" }); + ``` + + you will need to make a vanilla custom element, you cannot use Lit. + [For an example of extending an HTML element, see `moz-support-link`](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/moz-support-link/moz-support-link.mjs). diff --git a/browser/components/storybook/docs/README.storybook.stories.md b/browser/components/storybook/docs/README.storybook.stories.md new file mode 100644 index 0000000000..bb0fcdd1a2 --- /dev/null +++ b/browser/components/storybook/docs/README.storybook.stories.md @@ -0,0 +1,148 @@ +# Storybook for Firefox + +[Storybook](https://storybook.js.org/) is an interactive tool that creates a +playground for UI components. We use Storybook to document our design system, +reusable components, and any specific components you might want to test with +dummy data. [Take a look at our Storybook +instance!](https://firefoxux.github.io/firefox-desktop-components/?path=/story/docs-reusable-widgets--page) + +## Background + +Storybook lists components that can be reused, and helps document +what common elements we have. It can also list implementation specific +components, but they should be added to the "Domain-Specific UI Widgets" section. + +Changes to files directly referenced from Storybook (so basically non-chrome:// +paths) should automatically reflect changes in the opened browser. If you make a +change to a `chrome://` referenced file then you'll need to do a hard refresh +(Cmd+Shift+R/Ctrl+Shift+R) to notice the changes. If you're on Windows you may +need to `./mach build faster` to have the `chrome://` URL show the latest version. + +## Running Storybook + +Installing the npm dependencies and running the `storybook` npm script should be +enough to get Storybook running. This can be done via `./mach storybook` +commands, or with your personal npm/node that happens to be compatible. + +### Running with mach commands + +This is the recommended approach for installing dependencies and running +Storybook locally. + +To install dependencies, start the Storybook server, and launch the Storybook +site in a local build of Firefox, just run: + +```sh +# This uses npm ci under the hood to install the package-lock.json exactly. +./mach storybook +``` + +This single command will first install any missing dependencies then start the +local Storybook server. It will also start your local browser and point it to +`http://localhost:5703` while enabling certain preferences to ensure components +display as expected (specifically `svg.context-properties.content.enabled` and +`layout.css.light-dark.enabled`). + +It's necessary to use your local build to test in Storybook since `chrome://` +URLs are currently being pulled from the running browser, so any changes to +common-shared.css for example will come from your build. + +The Storybook server will continue running and will watch for component file +changes. + +#### Alternative mach commands + +Although running `./mach storybook` is the most convenient way to interact with +Storybook locally it is also possible to run separate commands to start the +Storybook server and run your local build with the necessary prefs. + +If you only want to start the Storybook server - for example in cases where you +already have a local build running - you can pass a `--no-open` flag to `./mach +storybook`: + +```sh +# Start the storybook server without launching a local Firefox build. +./mach storybook --no-open +``` + +If you just want to spin up a local build of Firefox with the required prefs +enabled you can use the `launch` subcommand: + +```sh +# In another terminal: +./mach storybook launch +``` + +This will run your local browser and point it at `http://localhost:5703`. + +Alternatively, you can simply navigate to `http://localhost:5703/` or run: + +```sh +# In another terminal: +./mach run http://localhost:5703/ +``` + +although with this option certain prefs won't be enabled, so what's displayed in +Storybook may not exactly reflect how components will look when used in Firefox. + +### Personal npm + +You can use your own `npm` to install and run Storybook. Compatibility is up +to you to sort out. + +```sh +cd browser/components/storybook +npm ci # Install the package-lock.json exactly so lockfileVersion won't change. +npm run storybook +``` + +## Updating Storybook dependencies + +On occasion you may need to update or add a npm dependency for Storybook. +This can be done using the version of `npm` packaged with `mach`: + +```sh +# Install a dev dependency from within the storybook directory. +cd browser/components/storybook && ../../../mach npm i -D your-package +``` + +## Adding new stories + +Storybook is currently configured to search for story files (any file with a +`.stories.(js|mjs|md)` extension) in `toolkit/content/widgets` and +`browser/components/storybook/stories`. + +Stories in `toolkit/content/widgets` are used to document design system +components, also known as UI widgets. +As long as you used `./mach addwidget` correctly, there is no additional setup needed to view your newly created story in Storybook. + +Stories in `browser/components/storybook/stories` are used for non-design system components, also called domain-specific UI widgets. +The easiest way to use Storybook for non-design system element is to use `./mach addstory new-component "Your Project"`. +You can also use `./mach addstory new-component "Your Project" --path browser/components/new-component.mjs` where `--path` is the path to your new components' source. +[See the Credential Management/Timeline widget for an example.](https://searchfox.org/mozilla-central/rev/2c11f18f89056a806c299a9d06bfa808718c2e84/browser/components/storybook/stories/login-timeline.stories.mjs#11) + +If you want to colocate your story with the code it is documenting you will need +to add to the `stories` array in the `.storybook/main.js` [configuration +file](https://searchfox.org/mozilla-central/source/browser/components/storybook/.storybook/main.js) +so that Storybook knows where to look for your files. + +The Storybook docs site has a [good +overview](https://storybook.js.org/docs/web-components/get-started/whats-a-story) +of what's involved in writing a new story. For convenience you can use the [Lit +library](https://lit.dev/) to define the template code for your story, but this +is not a requirement. + +### UI Widgets versus Domain-Specific UI Widgets + +Widgets that are part of [our design system](https://acorn.firefox.com/latest/acorn.html) and intended to be used across the Mozilla suite of products live under the "UI Widgets" category in Storybook and under `toolkit/content/widgets/` in Firefox. +These global widgets are denoted in code by the `moz-` prefix in their name. +For example, the name `moz-support-link` informs us that this widget is design system compliant and can be used anywhere in Firefox. + +Storybook can also be used to help document and prototype widgets that are specific to a part of the codebase and not intended for more global use. +Stories for these types of widgets live under the "Domain-Specific UI Widgets" category, while the code can live in any appropriate folder in `mozilla-central`. +[See the Credential Management folder as an example of a domain specific folder](https://firefoxux.github.io/firefox-desktop-components/?path=/docs/domain-specific-ui-widgets-credential-management-timeline--empty-timeline) and [see the login-timeline.stories.mjs for how to make a domain specific folder in Storybook](https://searchfox.org/mozilla-central/source/browser/components/storybook/stories/login-timeline.stories.mjs). +[To add a non-team specific widget to the "Domain-specific UI Widgets" section, see the migration-wizard.stories.mjs file](https://searchfox.org/mozilla-central/source/browser/components/storybook/stories/migration-wizard.stories.mjs). + +Creating and documenting domain specific UI widgets allows other teams to be aware of and take inspiration from existing UI patterns. +With these widgets, **there is no guarantee that the element will work for your domain.** +If you need to use a domain-specific widget outside of its intended domain, it may be worth discussing how to convert this domain specific widget into a global UI widget. diff --git a/browser/components/storybook/docs/README.typography.stories.md b/browser/components/storybook/docs/README.typography.stories.md new file mode 100644 index 0000000000..4b85d59ef8 --- /dev/null +++ b/browser/components/storybook/docs/README.typography.stories.md @@ -0,0 +1,389 @@ +# Typography +## Scale +[In-content pages and the browser chrome](https://acorn.firefox.com/latest/resources/browser-anatomy/desktop-ZaxCgqkt) follow different type scales due to the chrome relying on operating systems' font sizing, while in-content pages follow the type scale set by the design system. + +We set `font: message-box` at the root of `common-shared.css` and `global.css` stylesheets so that both in-content and the chrome can have access to operating system font families. + +We also don't specify line height units and rely on the default. + +### In-content +<table class="sb-preview-design-tokens"> + <thead> + <tr> + <th>Name</th> + <th>HTML class/tag or CSS token</th> + <th>Preview</th> + <th>Font size</th> + <th>Font weight</th> + </tr> + </thead> + <tbody> + <tr> + <th>Heading XLarge</th> + <td><code>h1,<br/>.heading-xlarge</code></td> + <td> + ```html story + <h1 class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</h1> + ``` + </td> + <td> + <code>1.6rem</code> (<code>24px</code>) + </td> + <td> + <code>600</code> + </td> + </tr> + <tr> + <th>Heading Large</th> + <td><code>h2,<br/>.heading-large</code></td> + <td> + ```html story + <h2 class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</h2> + ``` + </td> + <td> + <code>1.467rem</code> (<code>22px</code>) + </td> + <td> + <code>600</code> + </td> + </tr> + <tr> + <th>Heading Medium</th> + <td><code>h3,<br/>.heading-medium</code></td> + <td > + ```html story + <h3 class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</h3> + ``` + </td> + <td> + <code>1.133rem</code> (<code>17px</code>) + </td> + <td> + <code>600</code> + </td> + </tr> + <tr> + <th>Root (body)</th> + <td><code>--font-size-root</code> set at the <code>:root</code> of <code>common-shared.css</code></td> + <td> + ```html story + <p class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</p> + ``` + </td> + <td> + <code>15px</code> (<code>1rem</code>) + </td> + <td> + <code>normal</code> + </td> + </tr> + <tr> + <th>Body Small</th> + <td><code>--font-size-small</code></td> + <td> + ```html story + <p class="text-truncated-ellipsis sb-preview-font-size-small">The quick brown fox jumps over the lazy dog</p> + ``` + </td> + <td> + <code>0.867rem</code> (<code>13px</code>) + </td> + <td> + <code>normal</code> + </td> + </tr> + </tbody> +</table> + +### Chrome + +The chrome solely relies on `font` declarations (it also relies on `font: menu` for panels) so that it can inherit the operating system font family **and** sizing in order for it to feel like it is part of the user's operating system. Keep in mind that font sizes and families vary between macOS, Windows, and Linux. Moreover, you will only see a difference between `font: message-box` and `font: menu` font sizes on macOS. + +Note that there currently isn't a hierarchy of multiple headings on the chrome since every panel and modal that opens from it relies only on an `h1` for its title; so today, we just bold the existing fonts in order to create headings. + +<table class="sb-preview-design-tokens"> + <thead> + <tr> + <th>Name</th> + <th>Class</th> + <th>Preview</th> + <th>Font keyword</th> + <th>Font weight</th> + </tr> + </thead> + <tbody> + <tr> + <th>Menu Heading</th> + <td><code>h1</code></td> + <td class="sb-preview-chrome-typescale sb-preview-chrome-menu"> + ```html story + <h1 class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</h1> + ``` + </td> + <td> + <code>menu</code> + </td> + <td> + <code>600</code> + </td> + </tr> + <tr> + <th>Menu</th> + <td>Applied directly to panel classes in <code>panel.css</code> and <code>panelUI-shared.css</code></td> + <td class="sb-preview-chrome-typescale sb-preview-chrome-menu"> + ```html story + <p class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</p> + ``` + </td> + <td> + <code>menu</code> + </td> + <td> + <code>normal</code> + </td> + </tr> + <tr> + <th>Heading</th> + <td><code>h1</code></td> + <td class="sb-preview-chrome-typescale"> + ```html story + <h1 class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</h1> + ``` + </td> + <td> + <code>message-box</code> + </td> + <td> + <code>600</code> + </td> + </tr> + <tr> + <th>Root (body)</th> + <td><code>message-box</code> set at the <code>:root</code> of <code>global.css</code></td> + <td class="sb-preview-chrome-typescale"> + ```html story + <p class="text-truncated-ellipsis">The quick brown fox jumps over the lazy dog</p> + ``` + </td> + <td> + <code>message-box</code> + </td> + <td> + <code>normal</code> + </td> + </tr> + </tbody> +</table> + +## Design tokens +Type setting relies on design tokens for font size and font weight. + +#### Font size +<table class="sb-preview-design-tokens"> + <thead> + <tr> + <th>Base token</th> + <th>In-content value</th> + <th>Chrome value</th> + </tr> + </thead> + <tbody> + <tr> + <th> + <code>--font-size-xxlarge</code> + </th> + <td> + <code>1.6rem</code> + </td> + <td> + <code>unset</code> + </td> + </tr> + <tr> + <th> + <code>--font-size-xlarge</code> + </th> + <td> + <code>1.467rem</code> + </td> + <td> + <code>unset</code> + </td> + </tr> + <tr> + <th> + <code>--font-size-large</code> + </th> + <td> + <code>1.133rem</code> + </td> + <td> + <code>unset</code> + </td> + </tr> + <tr> + <th> + <code>--font-size-root</code> + </th> + <td> + <code>15px</code> + </td> + <td> + <code>unset</code> + </td> + </tr> + <tr> + <th> + <code>--font-size-small</code> + </th> + <td> + <code>0.867rem</code> + </td> + <td> + <code>unset</code> + </td> + </tr> + </tbody> +</table> + + +#### Font weight +<table class="sb-preview-design-tokens"> + <thead> + <tr> + <th>Base token</th> + <th>In-content value</th> + <th>Chrome value</th> + </tr> + </thead> + <tbody> + <tr> + <th> + <code>--font-weight-default</code> + </th> + <td> + <code>normal</code> + </td> + <td> + <code>normal</code> + </td> + </tr> + <tr> + <th> + <code>--font-weight-bold</code> + </th> + <td> + <code>600</code> + </td> + <td> + <code>600</code> + </td> + </tr> + </tbody> +</table> + +## Helpers +### text-and-typography.css + +The text and typography stylesheet found in `toolkit/themes/shared/design-system/text-and-typography.css` contains type setting declarations, and text and typography helper classes: + +- It applies the design system's type scale by default, therefore it styles the `root` and headings automatically. +- It comes with helper classes for contexts where designers may visually prefer an `h1` to start at the "medium" heading size instead of "large" (e.g. Shopping sidebar). It also contains text related helpers for truncating and deemphasizing text. + +You should rely on typography helper classes and the defaults set by the design system. + +This file is imported into `common-shared.css` and `global-shared.css` so that both in-content pages and the chrome receive their respective typography scale treatments, and have access to helper classes. + +#### Heading +##### XLarge (h1) +###### In-content +```html story + <h1>Firefox View</h1> +``` + +###### Chrome +```html story + <h1 class="sb-preview-chrome-typescale">Close window and quit Firefox?</h1> +``` + +###### Chrome menus +```html story + <h1 class="sb-preview-chrome-typescale sb-preview-chrome-menu">Edit bookmark</h1> +``` + +```css story +h1, +.heading-xlarge { + font-weight: var(--font-weight-bold); + font-size: var(--font-size-xxlarge); +} +``` + +*Reminder: There's no hierarchy of headings on the chrome. So here's just in-content's preview:* + +##### Large (h2) +```html story + <h2>Recent browsing</h2> +``` + +```css story +h2, +.heading-large { + font-weight: var(--font-weight-bold); + font-size: var(--font-size-xlarge); +} +``` + +##### Medium (h3) +```html story + <h3>Tabs from other devices</h3> +``` + +```css story +h3, +.heading-medium { + font-weight: var(--font-weight-bold); + font-size: var(--font-size-large); +} +``` + +#### Text +##### De-emphasized + +```html story + <span class="text-deemphasized">Get your passwords on your other devices.</span> +``` + +```css story +.text-deemphasized { + font-size: var(--font-size-small); + color: var(--text-color-deemphasized); +} +``` + +##### Truncated ellipsis + +```html story + <div class="text-truncated-ellipsis">A really long piece of text a really long piece of text a really long piece of text a really long piece of text a really long piece of text a really long piece of text.</div> +``` + +```css story +.text-truncated-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +``` + +`.text-truncated-ellipsis` can be applied to `display: block` or `display: inline-block` elements. + +For `display: flex` or `display: grid` elements, you'll need to wrap its contents with an element with the `.text-truncated-ellipsis` class instead. + +Example: + +```html +<div class="my-flex-element"> + <span class="text-truncated-ellipsis">A really long string of text that needs truncation.</span> +</div> +``` diff --git a/browser/components/storybook/docs/README.xul-and-html.stories.md b/browser/components/storybook/docs/README.xul-and-html.stories.md new file mode 100644 index 0000000000..8d2d4cb7bf --- /dev/null +++ b/browser/components/storybook/docs/README.xul-and-html.stories.md @@ -0,0 +1,67 @@ +# XUL and HTML + +This document gives a quick overview of XUL and HTML, especially as it pertains to desktop front-end developers. +As we migrate away from XUL elements to HTML elements where possible, it is important to note the differences between these two technologies. +Additionally it is helpful to know how to use both where needed, as some elements will still need to use XUL. + +## What is XUL + +XUL is an XML-based language for building cross-platform user interfaces and applications, so all the features of XML are available in XUL as well. +This is in contrast to HTML which is intended for developing web pages. +Because of this XUL is oriented towards application artifacts such as windows, scrollbars, and menus instead of pages, headings, links, etc. +These XUL elements are provided to an HTML page without the page having any control over them. + +XUL was and is used to create various UI elements, for example: +- Input controls such as textboxes and checkboxes +- Toolbars with buttons or other content +- Menus on a menu bar or pop up menus +- Tabbed dialogs +- Trees for hierarchical or tabular information + +XUL is a Mozilla-specific technology with many similarities but also many differences to HTML. +These include a different box model (although it is now synthesized in the HTML box model) and the ability to be backed by C++ code. + +## What requires XUL + +While many of the existing XUL elements that make up the browser can be migrated to HTML, there are some elements that require XUL. +These elements tend to be fundamental to the browser such as windows, popups, panels, etc. +Elements that need to emulate OS-specific styling also tend to be XUL elements. +While there are parts of these elements that must be XUL, that does not mean that the component must be entirely implmented in XUL. +For example, we require that a `panel` can be drawn outside of a window's bounds, but then we can have HTML inside of that `panel` element. + +The following is not an exhaustive list of elements that require XUL: +- Browser Window + - https://searchfox.org/mozilla-central/source/xpfe/appshell/nsIXULBrowserWindow.idl +- Popups + - https://searchfox.org/mozilla-central/source/dom/webidl/XULPopupElement.webidl + - https://searchfox.org/mozilla-central/source/layout/xul/nsMenuPopupFrame.cpp + - https://searchfox.org/mozilla-central/source/toolkit/content/widgets/autocomplete-popup.js + - https://searchfox.org/mozilla-central/source/toolkit/content/widgets/menupopup.js +- Panels + - https://searchfox.org/mozilla-central/source/toolkit/content/widgets/panel.js + +## When to use HTML or XUL + +Now that HTML is powerful enough for us to create almost an entire application with it, the need for the features of XUL has diminished. +We now prefer to write HTML components over XUL components since that model is better understood by the web and front-end community. +This also allows us to gain new features of the web in the UI that we write without backporting them to XUL. + +There are some cases where XUL may still be required for non-standard functionality. +Some XUL elements have more functions over similar HTML elements, such as the XUL `<browser>` element compared to the HTML `<iframe>` element. +The XUL `<panel>` can exceed the bounds of its parent document and is useful for dropdowns, menus, and similar situations. +In these situations, it would be appropriate to use XUL elements. +Otherwise, we want to [start replacing XUL elements with HTML elements](https://bugzilla.mozilla.org/show_bug.cgi?id=1563415). +Because of this, new code **should use HTML whenever possible**. + +## Mixing HTML and XUL + +There are a few things you must do in order to use HTML in a XUL file. +First, you must add the following attribute to the `window` tag of the XUL file, or to the outermost HTML element. +`xmlns:html="http://www.w3.org/1999/xhtml`. +Using this allows Firefox (or other applications using Gecko) to distinguish the HTML tags from the XUL ones. +Second, any HTML element used must be prefixed with `html:` otherwise the element will be parsed as a XUL element. +For example, to use a HTML anchor, you would declare it as `<html:a>`. +Third, any tags or attributes you use have to be in lowercase as XML is case-sensitive. + +Please note [you cannot declare XUL in HTML markup](https://searchfox.org/mozilla-central/rev/7a4c08f2c3a895c9dc064734ada320f920250c1f/toolkit/content/tests/widgets/test_panel_list_in_xul_panel.html#35-36). +You can use `document.createXULElement()` to programmatically create a XUL element in chrome privileged documents. |