diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/components/storybook/stories | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
9 files changed, 1236 insertions, 0 deletions
diff --git a/browser/components/storybook/stories/button.stories.mjs b/browser/components/storybook/stories/button.stories.mjs new file mode 100644 index 0000000000..5a6ec2cac5 --- /dev/null +++ b/browser/components/storybook/stories/button.stories.mjs @@ -0,0 +1,79 @@ +/* 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 { classMap, html } from "lit.all.mjs"; + +export default { + title: "UI Widgets/Button", + component: "button", + parameters: { + status: "stable", + fluent: ` +button-regular = Regular +button-primary = Primary +button-disabled = Disabled +button-danger = Danger + `, + }, +}; + +const Template = ({ + disabled, + primary, + l10nId, + ghostButton, + icon, + dangerButton, +}) => + html` + <style> + .icon-button { + background-image: url("${icon}"); + } + </style> + <button + ?disabled=${disabled} + class=${classMap({ + primary, + "ghost-button": ghostButton, + "icon-button": icon, + "danger-button": dangerButton, + })} + data-l10n-id=${l10nId} + ></button> + `; + +export const RegularButton = Template.bind({}); +RegularButton.args = { + l10nId: "button-regular", + primary: false, + disabled: false, +}; +export const PrimaryButton = Template.bind({}); +PrimaryButton.args = { + l10nId: "button-primary", + primary: true, + disabled: false, +}; +export const DisabledButton = Template.bind({}); +DisabledButton.args = { + l10nId: "button-disabled", + primary: false, + disabled: true, +}; + +export const DangerButton = Template.bind({}); +DangerButton.args = { + l10nId: "button-danger", + primary: true, + disabled: false, + dangerButton: true, +}; + +export const GhostIconButton = Template.bind({}); +GhostIconButton.args = { + icon: "chrome://browser/skin/login.svg", + disabled: false, + ghostButton: true, +}; diff --git a/browser/components/storybook/stories/credential-management.stories.mjs b/browser/components/storybook/stories/credential-management.stories.mjs new file mode 100644 index 0000000000..4632da5d02 --- /dev/null +++ b/browser/components/storybook/stories/credential-management.stories.mjs @@ -0,0 +1,45 @@ +/* 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 } from "lit.all.mjs"; +// Imported for side-effects. +// eslint-disable-next-line import/no-unassigned-import +import "../../aboutlogins/content/components/login-timeline.mjs"; + +export default { + title: "Domain-specific UI Widgets/Credential Management/Timeline", + component: "login-timeline", +}; + +window.MozXULElement.insertFTLIfNeeded("browser/aboutLogins.ftl"); + +const Template = ({ historyItems }) => + html` <login-timeline .history=${historyItems}></login-timeline> `; + +const ACTION_ID_CREATED = "login-item-timeline-action-created"; +const ACTION_ID_UPDATED = "login-item-timeline-action-updated"; +const ACTION_ID_USED = "login-item-timeline-action-used"; + +export const EmptyTimeline = Template.bind({}); +EmptyTimeline.args = { + historyItems: [], +}; + +export const TypicalTimeline = Template.bind({}); +TypicalTimeline.args = { + historyItems: [ + { actionId: ACTION_ID_CREATED, time: 1463526500267 }, + { actionId: ACTION_ID_UPDATED, time: 1653621219569 }, + { actionId: ACTION_ID_USED, time: 1561813190300 }, + ], +}; + +export const AllSameDayTimeline = Template.bind({}); +AllSameDayTimeline.args = { + historyItems: [ + { actionId: ACTION_ID_CREATED, time: 1463526500267 }, + { actionId: ACTION_ID_UPDATED, time: 1463526500267 }, + { actionId: ACTION_ID_USED, time: 1463526500267 }, + ], +}; diff --git a/browser/components/storybook/stories/fxview-category-navigation.stories.mjs b/browser/components/storybook/stories/fxview-category-navigation.stories.mjs new file mode 100644 index 0000000000..74a379a5a8 --- /dev/null +++ b/browser/components/storybook/stories/fxview-category-navigation.stories.mjs @@ -0,0 +1,113 @@ +/* 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 } from "lit.all.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "browser/components/firefoxview/fxview-category-navigation.mjs"; + +export default { + title: "Domain-specific UI Widgets/Firefox View/Category Navigation", + component: "fxview-category-navigation", + parameters: { + status: "in-development", + actions: { + handles: ["change-category"], + }, + fluent: ` +fxview-category-button-one = Category 1 + .title = Category 1 +fxview-category-button-two = Category 2 + .title = Category 2 +fxview-category-button-three = Category 3 + .title = Category 3 +fxview-category-footer-button = Settings + .title = Settings + `, + }, +}; + +const Template = () => html` + <style> + #page { + height: 100%; + display: grid; + grid-template-columns: var(--in-content-sidebar-width) 1fr; + } + fxview-category-navigation { + margin-inline-start: 10px; + } + fxview-category-button[name="category-one"]::part(icon) { + background-image: url("chrome://browser/skin/preferences/category-general.svg"); + } + fxview-category-button[name="category-two"]::part(icon) { + background-image: url("chrome://browser/skin/preferences/category-general.svg"); + } + fxview-category-button[name="category-three"]::part(icon) { + background-image: url("chrome://browser/skin/preferences/category-general.svg"); + } + .footer-button { + display: flex; + gap: 12px; + font-weight: normal; + min-width: unset; + padding: 8px; + margin: 0; + } + .footer-button .cat-icon { + background-image: url("chrome://browser/skin/preferences/category-general.svg"); + background-color: initial; + background-size: 20px; + background-repeat: no-repeat; + background-position: center; + height: 20px; + width: 20px; + display: inline-block; + -moz-context-properties: fill; + fill: currentColor; + } + @media (max-width: 52rem) { + #page { + grid-template-columns: 82px 1fr; + } + .cat-name { + display: none; + } + } + </style> + <div id="page"> + <fxview-category-navigation> + <h2 slot="category-nav-header">Header</h2> + <fxview-category-button + slot="category-button" + name="category-one" + data-l10n-id="fxview-category-button-one" + > + </fxview-category-button> + <fxview-category-button + slot="category-button" + name="category-two" + data-l10n-id="fxview-category-button-two" + > + </fxview-category-button> + <fxview-category-button + slot="category-button" + name="category-three" + data-l10n-id="fxview-category-button-three" + > + </fxview-category-button> + <div slot="category-nav-footer" class="category-nav-footer"> + <button class="footer-button ghost-button"> + <span class="cat-icon"></span> + <span + class="cat-name" + data-l10n-id="fxview-category-footer-button" + ></span> + </button> + </div> + </fxview-category-navigation> + </div> +`; + +export const Default = Template.bind({}); +Default.args = {}; diff --git a/browser/components/storybook/stories/fxview-tab-list.stories.md b/browser/components/storybook/stories/fxview-tab-list.stories.md new file mode 100644 index 0000000000..f8317f5605 --- /dev/null +++ b/browser/components/storybook/stories/fxview-tab-list.stories.md @@ -0,0 +1,99 @@ +# FxviewTabList + +`fxview-tab-list` is a list of `fxview-tab-row` elements that display tab info such as: +* A link containing: + * Favicon + * Title + * Domain + * Time when tab was last accessed (can be formatted as `relative`, `date and time`, `date only`, and `time only`) +* Secondary action button + +## When to use + +* Use `fxview-tab-list` anywhere you want to display a list of tabs with the above info displayed. + +## Code + +The source for `fxview-tab-list` can be found under +[browser/components/firefoxview/fxview-tab-list](https://searchfox.org/mozilla-central/source/browser/components/firefoxview/fxview-tab-list.mjs). + +`fxview-tab-list` can be imported into `.html`/`.xhtml` files: + +```html +<script type="module" src="chrome://content/browser/firefoxview/fxview-tab-list.mjs"></script> +``` + +And used as follows: + +With context menu: +```html +<fxview-tab-list + class="with-context-menu" + .dateTimeFormat=${"relative"} + .hasPopup=${"menu"} + .maxTabsLength=${this.maxTabsLength} + .secondaryL10nId=${"fxviewtabrow-open-menu"} + .tabItems=${this.tabItems} + @fxview-tab-list-secondary-action=${this.onSecondaryAction} + @fxview-tab-list-primary-action=${this.onPrimaryAction} +> + <panel-list slot="menu" @hide=${this.menuClosed}> + <panel-item + @click=${...} + data-l10n-id="fxviewtabrow-delete" + ></panel-item> + <panel-item + @click=${...} + data-l10n-id="fxviewtabrow-forget-about-this-site" + ></panel-item> + ... + </panel-list> +</fxview-tab-list> +``` +With dismiss button: +```html +<fxview-tab-list + class="with-dismiss-button" + .dateTimeFormat=${"relative"} + .maxTabsLength=${this.maxTabsLength} + .secondaryL10nId=${"fxviewtabrow-dismiss-tab-button"} + .tabItems=${this.tabItems} + @fxview-tab-list-secondary-action=${this.onSecondaryAction} + @fxview-tab-list-primary-action=${this.onPrimaryAction} +></fxview-tab-list> +``` + +### Notes + +* You'll need to defines function for `this.onPrimaryAction` and `this.onSecondaryAction` in order to add functionality to the primary element and the secondary button +* You can also supply a `class` attribute to the instance on `fxview-tab-list` in order to apply styles to things like the icon for the secondary action button: +For example: +```css + fxview-tab-list.with-context-menu::part(secondary-button) { + background-image: url("chrome://global/skin/icons/more.svg"); + } +``` + +### Properties + +You'll need to pass along some of the following properties: +* `dateTimeFormat`: A string to indicate the expected format/options for the date and/or time displayed on each tab item in the list. The default for this if not given is `"relative"`. + * Options include: + * `relative` (Ex: "Just now", "2m ago", etc.) + * `date` (Ex: "4/1/23", "01/04/23", etc. - Will be formatted based on locale) + * `time` (Ex: "4:00 PM", "16:00", etc - Will be formatted based on locale) + * `dateTime` (Ex: "4/1/23 4:00PM", "01/04/23 16:00", etc. - Will be formatted based on locale) +* `hasPopup`: The optional aria-haspopup attribute for the secondary action, if required +* `maxTabsLength`: The max number of tabs you want to display in the tabs list. The default value will be `25` if no max value is given. +* `secondaryL10nId`: The l10n ID of the secondary action button +* `tabItems`: An array of tab data such as History nodes, Bookmark nodes, Synced Tabs, etc. + * The component is expecting to receive the following properties within each `tabItems` object (you may need to do some normalizing for this): + * `icon` - The location string for the favicon. Will fallback to default favicon if none is found. + * `primaryL10nId` - The l10n id to be used for the primary action element + * `primaryL10nArgs` - The l10n args you can optionally pass for the primary action element + * `secondaryL10nId` - The l10n id to be used for the secondary action button + * `secondaryL10nArgs` - The l10n args you can optionally pass for the secondary action button + * `tabid` - Optional property expected for Recently Closed tab data + * `time` - The time in milliseconds for expected last interaction with the tab (Ex: `lastUsed` for SyncedTabs tabs, `closedAt` for RecentlyClosed tabs, etc.) + * `title` - The title for the tab + * `url` - The full URL for the tab diff --git a/browser/components/storybook/stories/fxview-tab-list.stories.mjs b/browser/components/storybook/stories/fxview-tab-list.stories.mjs new file mode 100644 index 0000000000..692577d378 --- /dev/null +++ b/browser/components/storybook/stories/fxview-tab-list.stories.mjs @@ -0,0 +1,210 @@ +/* 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 } from "lit.all.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "browser/components/firefoxview/fxview-tab-list.mjs"; + +const DATE_TIME_FORMATS = { + relative: "relative", + dateTime: "dateTime", + date: "date", + time: "time", +}; + +export default { + title: "Domain-specific UI Widgets/Firefox View/Tab List", + component: "fxview-tab-list", + argTypes: { + dateTimeFormat: { + options: Object.keys(DATE_TIME_FORMATS), + mapping: DATE_TIME_FORMATS, + control: { type: "select" }, + }, + }, +}; + +const Template = ({ + listClass, + dateTimeFormat, + hasPopup, + maxTabsLength, + primaryAction, + secondaryAction, + tabItems, +}) => html` + <style> + main { + max-width: 750px; + } + fxview-tab-list.menu::part(secondary-button) { + background-image: url("chrome://global/skin/icons/more.svg"); + } + fxview-tab-list.dismiss::part(secondary-button) { + background-image: url("chrome://global/skin/icons/close.svg"); + } + :host panel-item::part(button) { + padding-inline-start: 12px; + cursor: pointer; + } + </style> + <main> + <fxview-tab-list + class=${listClass} + .hasPopup=${hasPopup} + .dateTimeFormat=${dateTimeFormat} + .maxTabsLength=${maxTabsLength} + .tabItems=${tabItems} + @fxview-tab-list-secondary-action=${secondaryAction} + @fxview-tab-list-primary-action=${primaryAction} + > + <panel-list slot="menu"> + <panel-item data-l10n-id="fxviewtabrow-delete"></panel-item> + <panel-item + data-l10n-id="fxviewtabrow-forget-about-this-site" + ></panel-item> + <hr /> + <panel-item data-l10n-id="fxviewtabrow-open-in-window"></panel-item> + <panel-item + data-l10n-id="fxviewtabrow-open-in-private-window" + ></panel-item> + <hr /> + <panel-item data-l10n-id="fxviewtabrow-add-bookmark"></panel-item> + <panel-item data-l10n-id="fxviewtabrow-save-to-pocket"></panel-item> + <panel-item data-l10n-id="fxviewtabrow-copy-link"></panel-item> + </panel-list> + </fxview-tab-list> + </main> +`; + +const MAX_TABS_LENGTH = 25; + +let secondaryAction = e => { + e.target.querySelector("panel-list").toggle(e.detail.originalEvent); +}; + +let primaryAction = e => { + // Open in new tab +}; + +const tabItems = [ + { + icon: "chrome://global/skin/icons/defaultFavicon.svg", + title: "Example Domain", + url: "example.net", + time: 1678141738136, + primaryL10nId: "fxviewtabrow-tabs-list-tab", + primaryL10nArgs: JSON.stringify({ targetURI: "example.net" }), + secondaryL10nId: "fxviewtabrow-open-menu-button", + }, + { + icon: "chrome://global/skin/icons/defaultFavicon.svg", + title: "Example Domain", + url: "example.org", + time: 1678141738136, + primaryL10nId: "fxviewtabrow-tabs-list-tab", + primaryL10nArgs: JSON.stringify({ targetURI: "example.org" }), + secondaryL10nId: "fxviewtabrow-open-menu-button", + }, + { + icon: "chrome://global/skin/icons/defaultFavicon.svg", + title: "Example Domain", + url: "example.com", + time: 1678141738136, + primaryL10nId: "fxviewtabrow-tabs-list-tab", + primaryL10nArgs: JSON.stringify({ targetURI: "example.com" }), + secondaryL10nId: "fxviewtabrow-open-menu-button", + }, +]; +const recentlyClosedItems = [ + { + icon: "chrome://global/skin/icons/defaultFavicon.svg", + title: "Example Domain", + url: "example.net", + tabid: 1, + time: 1678141738136, + primaryL10nId: "fxviewtabrow-tabs-list-tab", + primaryL10nArgs: JSON.stringify({ targetURI: "example.net" }), + secondaryL10nId: "fxviewtabrow-dismiss-tab-button", + secondaryL10nArgs: JSON.stringify({ + tabTitle: "Example Domain", + }), + }, + { + icon: "chrome://global/skin/icons/defaultFavicon.svg", + title: "Example Domain", + url: "example.org", + tabid: 2, + time: 1678141738136, + primaryL10nId: "fxviewtabrow-tabs-list-tab", + primaryL10nArgs: JSON.stringify({ targetURI: "example.net" }), + secondaryL10nId: "fxviewtabrow-dismiss-tab-button", + secondaryL10nArgs: JSON.stringify({ + tabTitle: "Example Domain", + }), + }, + { + icon: "chrome://global/skin/icons/defaultFavicon.svg", + title: "Example Domain", + url: "example.com", + tabid: 3, + time: 1678141738136, + primaryL10nId: "fxviewtabrow-tabs-list-tab", + primaryL10nArgs: JSON.stringify({ targetURI: "example.net" }), + secondaryL10nId: "fxviewtabrow-dismiss-tab-button", + secondaryL10nArgs: JSON.stringify({ + tabTitle: "Example Domain", + }), + }, +]; + +export const RelativeTime = Template.bind({}); +RelativeTime.args = { + listClass: "menu", + dateTimeFormat: "relative", + hasPopup: "menu", + maxTabsLength: MAX_TABS_LENGTH, + primaryAction, + secondaryAction, + tabItems, +}; +export const DateAndTime = Template.bind({}); +DateAndTime.args = { + listClass: "menu", + dateTimeFormat: "dateTime", + maxTabsLength: MAX_TABS_LENGTH, + primaryAction, + secondaryAction, + tabItems, +}; +export const DateOnly = Template.bind({}); +DateOnly.args = { + listClass: "menu", + dateTimeFormat: "date", + hasPopup: "menu", + maxTabsLength: MAX_TABS_LENGTH, + primaryAction, + secondaryAction, + tabItems, +}; +export const TimeOnly = Template.bind({}); +TimeOnly.args = { + listClass: "menu", + dateTimeFormat: "time", + hasPopup: "menu", + maxTabsLength: MAX_TABS_LENGTH, + primaryAction, + secondaryAction, + tabItems, +}; +export const RecentlyClosed = Template.bind({}); +RecentlyClosed.args = { + listClass: "dismiss", + dateTimeFormat: "relative", + hasPopup: null, + maxTabsLength: MAX_TABS_LENGTH, + primaryAction, + secondaryAction: () => {}, + tabItems: recentlyClosedItems, +}; diff --git a/browser/components/storybook/stories/message-bar.stories.mjs b/browser/components/storybook/stories/message-bar.stories.mjs new file mode 100644 index 0000000000..a8a7b7ddc7 --- /dev/null +++ b/browser/components/storybook/stories/message-bar.stories.mjs @@ -0,0 +1,48 @@ +/* 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 } from "lit.all.mjs"; +// Imported for side-effects. +// eslint-disable-next-line import/no-unassigned-import +import "toolkit-widgets/message-bar.js"; + +const MESSAGE_TYPES = { + default: "", + success: "success", + error: "error", + warning: "warning", +}; + +export default { + title: "UI Widgets/Message Bar", + component: "message-bar", + argTypes: { + type: { + options: Object.keys(MESSAGE_TYPES), + mapping: MESSAGE_TYPES, + control: { type: "select" }, + }, + }, + parameters: { + status: "stable", + fluent: ` +message-bar-text = A very expressive and slightly whimsical message goes here. +message-bar-button = Click me, please! + `, + }, +}; + +const Template = ({ dismissable, type }) => + html` + <message-bar type=${type} ?dismissable=${dismissable}> + <span data-l10n-id="message-bar-text"></span> + <button data-l10n-id="message-bar-button"></button> + </message-bar> + `; + +export const Basic = Template.bind({}); +Basic.args = { type: "", dismissable: false }; + +export const Dismissable = Template.bind({}); +Dismissable.args = { type: "", dismissable: true }; diff --git a/browser/components/storybook/stories/migration-wizard.stories.mjs b/browser/components/storybook/stories/migration-wizard.stories.mjs new file mode 100644 index 0000000000..f525c44a33 --- /dev/null +++ b/browser/components/storybook/stories/migration-wizard.stories.mjs @@ -0,0 +1,344 @@ +/* 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/. */ + +// Imported for side-effects. +import { html } from "lit.all.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "toolkit-widgets/panel-list.js"; +// eslint-disable-next-line import/no-unassigned-import +import "browser/components/migration/content/migration-wizard.mjs"; +import { MigrationWizardConstants } from "chrome://browser/content/migration/migration-wizard-constants.mjs"; + +// Imported for side-effects. +// eslint-disable-next-line import/no-unassigned-import +import "toolkit-widgets/named-deck.js"; + +export default { + title: "Domain-specific UI Widgets/Migration Wizard", + component: "migration-wizard", +}; + +const FAKE_MIGRATOR_LIST = [ + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "chrome", + displayName: "Chrome", + resourceTypes: [ + "HISTORY", + "FORMDATA", + "PASSWORDS", + "BOOKMARKS", + "PAYMENT_METHODS", + ], + profile: { id: "Default", name: "Default" }, + brandImage: "chrome://browser/content/migration/brands/chrome.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "chrome", + displayName: "Chrome", + resourceTypes: [ + "HISTORY", + "FORMDATA", + "PASSWORDS", + "BOOKMARKS", + "PAYMENT_METHODS", + ], + profile: { id: "person-2", name: "Person 2" }, + brandImage: "chrome://browser/content/migration/brands/chrome.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "ie", + displayName: "Microsoft Internet Explorer", + resourceTypes: ["HISTORY", "BOOKMARKS"], + profile: null, + brandImage: "chrome://browser/content/migration/brands/ie.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "edge", + displayName: "Microsoft Edge Legacy", + resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"], + profile: null, + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "chromium-edge", + displayName: "Microsoft Edge", + resourceTypes: [ + "HISTORY", + "FORMDATA", + "PASSWORDS", + "BOOKMARKS", + "PAYMENT_METHODS", + ], + profile: { id: "Default", name: "Default" }, + brandImage: "chrome://browser/content/migration/brands/edge.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "brave", + displayName: "Brave", + resourceTypes: [ + "HISTORY", + "FORMDATA", + "PASSWORDS", + "BOOKMARKS", + "PAYMENT_METHODS", + ], + profile: { id: "Default", name: "Default" }, + brandImage: "chrome://browser/content/migration/brands/brave.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "internal-testing", + displayName: "Internal Testing Migrator", + resourceTypes: ["HISTORY", "PASSWORDS", "BOOKMARKS"], + profile: null, + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "safari", + displayName: "Safari", + resourceTypes: ["HISTORY", "PASSWORDS", "BOOKMARKS"], + profile: null, + brandImage: "chrome://browser/content/migration/brands/safari.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "opera", + displayName: "Opera", + resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"], + profile: { id: "Default", name: "Default" }, + brandImage: "chrome://browser/content/migration/brands/opera.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "opera-gx", + displayName: "Opera GX", + resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"], + profile: { id: "Default", name: "Default" }, + brandImage: "chrome://browser/content/migration/brands/operagx.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "vivaldi", + displayName: "Vivaldi", + resourceTypes: ["HISTORY"], + profile: { id: "Default", name: "Default" }, + brandImage: "chrome://browser/content/migration/brands/vivaldi.png", + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER, + key: "no-resources-browser", + displayName: "Browser with no resources", + resourceTypes: [], + profile: { id: "Default", name: "Default" }, + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.FILE, + key: "file-password-csv", + displayName: "Passwords from CSV file", + brandImage: "chrome://branding/content/document.ico", + resourceTypes: [], + }, + { + type: MigrationWizardConstants.MIGRATOR_TYPES.FILE, + key: "file-bookmarks", + displayName: "Bookmarks from file", + brandImage: "chrome://branding/content/document.ico", + resourceTypes: [], + }, +]; + +const Template = ({ state, dialogMode }) => html` + <style> + @media (prefers-reduced-motion: no-preference) { + migration-wizard::part(progress-spinner) { + mask: url(./migration/progress-mask.svg); + } + } + </style> + + <div class="card card-no-hover" style="width: fit-content"> + <migration-wizard ?dialog-mode=${dialogMode} .state=${state}> + <panel-list></panel-list> + </migration-wizard> + </div> +`; + +export const LoadingSkeleton = Template.bind({}); +LoadingSkeleton.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.LOADING, + }, +}; + +export const MainSelectorVariant1 = Template.bind({}); +MainSelectorVariant1.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.SELECTION, + migrators: FAKE_MIGRATOR_LIST, + showImportAll: false, + }, +}; + +export const MainSelectorVariant2 = Template.bind({}); +MainSelectorVariant2.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.SELECTION, + migrators: FAKE_MIGRATOR_LIST, + showImportAll: true, + }, +}; + +export const Progress = Template.bind({}); +Progress.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.PROGRESS, + key: "chrome", + progress: { + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS]: { + inProgress: true, + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS]: { + inProgress: true, + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.HISTORY]: { + inProgress: true, + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.FORMDATA]: { + inProgress: true, + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PAYMENT_METHODS]: { + inProgress: true, + }, + }, + }, +}; + +export const PartialProgress = Template.bind({}); +PartialProgress.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.PROGRESS, + key: "chrome", + progress: { + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS]: { + inProgress: true, + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS]: { + inProgress: false, + message: "14 logins and passwords", + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.HISTORY]: { + inProgress: true, + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.FORMDATA]: { + inProgress: false, + message: "Addresses, credit cards, form history", + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PAYMENT_METHODS]: { + inProgress: false, + message: "6 payment methods", + }, + }, + }, +}; + +export const Success = Template.bind({}); +Success.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.PROGRESS, + key: "chrome", + progress: { + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS]: { + inProgress: false, + message: "14 bookmarks", + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS]: { + inProgress: false, + message: "14 logins and passwords", + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.HISTORY]: { + inProgress: false, + message: "From the last 180 days", + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.FORMDATA]: { + inProgress: false, + message: "Addresses, credit cards, form history", + }, + [MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PAYMENT_METHODS]: { + inProgress: false, + message: "6 payment methods", + }, + }, + }, +}; + +export const FileImportProgress = Template.bind({}); +FileImportProgress.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.FILE_IMPORT_PROGRESS, + title: "Importing Passwords", + progress: { + [MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES + .PASSWORDS_FROM_FILE]: { + inProgress: true, + }, + }, + }, +}; + +export const FileImportSuccess = Template.bind({}); +FileImportSuccess.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.FILE_IMPORT_PROGRESS, + title: "Passwords Imported Successfully", + progress: { + [MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES.PASSWORDS_NEW]: { + inProgress: false, + message: "2 added", + }, + [MigrationWizardConstants.DISPLAYED_FILE_RESOURCE_TYPES + .PASSWORDS_UPDATED]: { + inProgress: false, + message: "14 updated", + }, + }, + }, +}; + +export const SafariPermissions = Template.bind({}); +SafariPermissions.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.SAFARI_PERMISSION, + }, +}; + +export const SafariPasswordPermissions = Template.bind({}); +SafariPasswordPermissions.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.SAFARI_PASSWORD_PERMISSION, + }, +}; + +export const NoBrowsersFound = Template.bind({}); +NoBrowsersFound.args = { + dialogMode: true, + state: { + page: MigrationWizardConstants.PAGES.NO_BROWSERS_FOUND, + hasFileMigrators: true, + }, +}; diff --git a/browser/components/storybook/stories/named-deck.stories.mjs b/browser/components/storybook/stories/named-deck.stories.mjs new file mode 100644 index 0000000000..21c9d62e3e --- /dev/null +++ b/browser/components/storybook/stories/named-deck.stories.mjs @@ -0,0 +1,165 @@ +/* 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 } from "lit.all.mjs"; +// Imported for side-effects. +// eslint-disable-next-line import/no-unassigned-import +import "toolkit-widgets/named-deck.js"; + +export default { + title: "UI Widgets/Named Deck", + component: "named-deck", + parameters: { + status: "stable", + fluent: ` +named-deck-tab-one = Tab 1 +named-deck-tab-two = Tab 2 +named-deck-tab-three = Tab 3 +named-deck-content-one = This is tab 1 +named-deck-content-two = This is tab 2 +named-deck-content-three = This is tab 3 +button-group-one = One +button-group-two = Two +button-group-three = Three +button-group-four = Four + `, + }, +}; + +export const Tabs = () => html` + <style> + button[selected] { + outline: 2px dashed var(--in-content-primary-button-background); + } + </style> + <button-group> + <button is="named-deck-button" deck="tabs-deck" name="tab-1" data-l10n-id="named-deck-tab-one"></button> + <button is="named-deck-button" deck="tabs-deck" name="tab-2" data-l10n-id="named-deck-tab-two"></button> + <button is="named-deck-button" deck="tabs-deck" name="tab-3" data-l10n-id="named-deck-tab-three"></button> + </button-group> + <named-deck id="tabs-deck" is-tabbed> + <p name="tab-1" data-l10n-id="named-deck-content-one"></p> + <p name="tab-2" data-l10n-id="named-deck-content-two"></p> + <p name="tab-3" data-l10n-id="named-deck-content-three"></p> + </named-deck> + + <hr> + + <p> + The dashed outline is added for emphasis here. It applies to the button with + the <code>selected</code> attribute, but matches the deck's + <code>selected-view</code> name. + </p> + + <p> + These tabs are a combination of <code>button-group</code>, + <code>named-deck-button</code>, and <code>named-deck</code>. + <ul> + <li> + <code>button-group</code> makes the tabs a single focusable group, + using left/right to switch between focused buttons. + </li> + <li> + <code>named-deck-button</code>s are <code>button</code> subclasses + that are used to control the <code>named-deck</code>. + </li> + <li> + <code>named-deck</code> show only one selected child at a time. + </li> + </ul> + </p> +`; + +export const ListMenu = () => html` + <style> + .icon-button { + background-image: url("chrome://global/skin/icons/arrow-left.svg"); + } + + .vertical-group { + display: flex; + flex-direction: column; + width: 200px; + } + </style> + <named-deck id="list-deck" is-tabbed> + <section name="list"> + <button-group orientation="vertical" class="vertical-group"> + <button is="named-deck-button" deck="list-deck" name="tab-1"> + Tab 1 + </button> + <button is="named-deck-button" deck="list-deck" name="tab-2"> + Tab 2 + </button> + <button is="named-deck-button" deck="list-deck" name="tab-3"> + Tab 3 + </button> + </button-group> + </section> + <section name="tab-1"> + <button + class="icon-button ghost-button" + is="named-deck-button" + deck="list-deck" + name="list" + ></button> + <p>This is tab 1</p> + </section> + <section name="tab-2"> + <button + class="icon-button ghost-button" + is="named-deck-button" + deck="list-deck" + name="list" + ></button> + <p>This is tab 2</p> + </section> + <section name="tab-3"> + <button + class="icon-button ghost-button" + is="named-deck-button" + deck="list-deck" + name="list" + ></button> + <p>This is tab 3</p> + </section> + </named-deck> + + <hr /> + + <p> + This is an alternate layout for creating a menu navigation. In this case, + the first view in the <code>named-deck</code> is the list view which + contains the <code>named-deck-button</code>s to link to the other views. + Each view then includes a back <code>named-deck-button</code> which is used + to navigate back to the first view. + </p> +`; + +const FocusGroupTemplate = ({ orientation }) => html` + <button-group orientation=${orientation}> + <button data-l10n-id="button-group-one"></button> + <button data-l10n-id="button-group-two"></button> + <button data-l10n-id="button-group-three"></button> + <button data-l10n-id="button-group-four"></button> + </button-group> + + <p> + The <code>button-group</code> element will group focus to the buttons, + requiring left/right or up/down to switch focus between its child elements. + It accepts an <code>orientation</code> property, which determines if + left/right or up/down are used to change the focused button. + </p> +`; + +export const FocusGroup = FocusGroupTemplate.bind({}); +FocusGroup.args = { + orientation: "horizontal", +}; +FocusGroup.argTypes = { + orientation: { + options: ["horizontal", "vertical"], + control: { type: "radio" }, + }, +}; diff --git a/browser/components/storybook/stories/panel-list.stories.mjs b/browser/components/storybook/stories/panel-list.stories.mjs new file mode 100644 index 0000000000..af1678b1e7 --- /dev/null +++ b/browser/components/storybook/stories/panel-list.stories.mjs @@ -0,0 +1,133 @@ +/* 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-next-line import/no-unassigned-import +import "toolkit-widgets/panel-list.js"; +import { html, ifDefined } from "lit.all.mjs"; + +export default { + title: "UI Widgets/Panel Menu", + component: "panel-list", + parameters: { + status: "stable", + actions: { + handles: ["click"], + }, + fluent: ` +panel-list-item-one = Item One +panel-list-item-two = Item Two (accesskey w) +panel-list-item-three = Item Three +panel-list-checked = Checked +panel-list-badged = Badged, look at me +panel-list-passwords = Passwords +panel-list-settings = Settings + `, + }, +}; + +const openMenu = e => { + e.target.getRootNode().querySelector("panel-list").toggle(e); +}; + +const Template = ({ isOpen, items, wideAnchor }) => + html` + <style> + panel-item[icon="passwords"]::part(button) { + background-image: url("chrome://browser/skin/login.svg"); + } + panel-item[icon="settings"]::part(button) { + background-image: url("chrome://global/skin/icons/settings.svg"); + } + button { + position: absolute; + background-image: url("chrome://global/skin/icons/more.svg"); + } + button[wide] { + width: 400px !important; + } + .end { + inset-inline-end: 30px; + } + + .bottom { + inset-block-end: 30px; + } + </style> + <button + class="ghost-button icon-button" + @click=${openMenu} + ?wide="${wideAnchor}" + ></button> + <button + class="ghost-button icon-button end" + @click=${openMenu} + ?wide="${wideAnchor}" + ></button> + <button + class="ghost-button icon-button bottom" + @click=${openMenu} + ?wide="${wideAnchor}" + ></button> + <button + class="ghost-button icon-button bottom end" + @click=${openMenu} + ?wide="${wideAnchor}" + ></button> + <panel-list + ?stay-open=${isOpen} + ?open=${isOpen} + ?min-width-from-anchor=${wideAnchor} + > + ${items.map(i => + i == "<hr>" + ? html` <hr /> ` + : html` + <panel-item + icon=${i.icon ?? ""} + ?checked=${i.checked} + ?badged=${i.badged} + accesskey=${ifDefined(i.accesskey)} + data-l10n-id=${i.l10nId ?? i} + ></panel-item> + ` + )} + </panel-list> + `; + +export const Simple = Template.bind({}); +Simple.args = { + isOpen: false, + wideAnchor: false, + items: [ + "panel-list-item-one", + { l10nId: "panel-list-item-two", accesskey: "w" }, + "panel-list-item-three", + "<hr>", + { l10nId: "panel-list-checked", checked: true }, + { l10nId: "panel-list-badged", badged: true, icon: "settings" }, + ], +}; + +export const Icons = Template.bind({}); +Icons.args = { + isOpen: false, + wideAnchor: false, + items: [ + { l10nId: "panel-list-passwords", icon: "passwords" }, + { l10nId: "panel-list-settings", icon: "settings" }, + ], +}; + +export const Open = Template.bind({}); +Open.args = { + ...Simple.args, + wideAnchor: false, + isOpen: true, +}; + +export const Wide = Template.bind({}); +Wide.args = { + ...Simple.args, + wideAnchor: true, +}; |