mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-16 05:45:33 +00:00
Bug 1880481 - Create a story for panel-list submenu r=reusable-components-reviewers,desktop-theme-reviewers,fxview-reviewers,emilio,sclements
This patch adds a story for `panel-list` submenus to document how to create them. It also amends the README with a small code example, and moves the submenu slot creation out of the constructor to the connectedCallback, since we can't reliably read attribute values in custom element constructors. Differential Revision: https://phabricator.services.mozilla.com/D202012
This commit is contained in:
parent
b71caf6061
commit
7d9d21f181
@ -148,10 +148,6 @@ panel-item::part(button):hover:active {
|
||||
background-color: var(--fxview-element-background-active);
|
||||
}
|
||||
|
||||
panel-list {
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
fxview-empty-state:not([isSelectedTab]) button[slot="primary-action"] {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ children and optional `hr` elements as separators. The `panel-list` will anchor
|
||||
itself to the target of the initiating event when opened with
|
||||
`panelList.toggle(event)`.
|
||||
|
||||
Note: Nested menus are not currently supported. XUL is currently required to
|
||||
Note: XUL is currently required to
|
||||
support accesskey underlining (although using `moz-label` could change that).
|
||||
Shortcuts are not displayed automatically in the `panel-item`.
|
||||
|
||||
@ -229,3 +229,23 @@ grow larger than its containing window if needed.
|
||||
</html:panel-list>
|
||||
</panel>
|
||||
```
|
||||
|
||||
### Submenus
|
||||
|
||||
`panel-list` supports nested submenus. Submenus can be created by nesting a second `panel-list` in a `panel-item`'s `submenu` slot and specifying a `submenu` attribute on that `panel-item` that points to the nested list's ID. For example:
|
||||
|
||||
```html
|
||||
<panel-list>
|
||||
<panel-item>No submenu</panel-item>
|
||||
<panel-item>No submenu</panel-item>
|
||||
<panel-item submenu="example-submenu">
|
||||
Has a submenu
|
||||
<panel-list slot="submenu" id="example-submenu">
|
||||
<panel-item>I'm a submenu item!</panel-item>
|
||||
<panel-item>I'm also a submenu item!</panel-item>
|
||||
</panel-list>
|
||||
</panel-item>
|
||||
</panel-list>
|
||||
```
|
||||
|
||||
As of February 2024 submenus are only in use in Firefox View and support for nesting beyond one submenu may be limited.
|
||||
|
@ -26,6 +26,10 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:host([has-submenu]) {
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
:host(:not([slot=submenu])) {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
@ -308,7 +308,7 @@
|
||||
}
|
||||
|
||||
addHideListeners() {
|
||||
if (this.hasAttribute("stay-open") && !this.lastAnchorNode.hasSubmenu) {
|
||||
if (this.hasAttribute("stay-open") && !this.lastAnchorNode?.hasSubmenu) {
|
||||
// This is intended for inspection in Storybook.
|
||||
return;
|
||||
}
|
||||
@ -631,31 +631,12 @@
|
||||
this.#defaultSlot = document.createElement("slot");
|
||||
this.#defaultSlot.style.display = "none";
|
||||
|
||||
if (this.hasSubmenu) {
|
||||
this.icon = document.createElement("div");
|
||||
this.icon.setAttribute("class", "submenu-icon");
|
||||
this.label.setAttribute("class", "submenu-label");
|
||||
|
||||
this.button.setAttribute("class", "submenu-container");
|
||||
this.button.appendChild(this.icon);
|
||||
|
||||
this.submenuSlot = document.createElement("slot");
|
||||
this.submenuSlot.name = "submenu";
|
||||
|
||||
this.shadowRoot.append(
|
||||
style,
|
||||
this.button,
|
||||
this.#defaultSlot,
|
||||
this.submenuSlot
|
||||
);
|
||||
} else {
|
||||
this.shadowRoot.append(
|
||||
style,
|
||||
this.button,
|
||||
supportLinkSlot,
|
||||
this.#defaultSlot
|
||||
);
|
||||
}
|
||||
this.shadowRoot.append(
|
||||
style,
|
||||
this.button,
|
||||
supportLinkSlot,
|
||||
this.#defaultSlot
|
||||
);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -664,6 +645,10 @@
|
||||
this._l10nRootConnected = true;
|
||||
}
|
||||
|
||||
this.panel =
|
||||
this.getRootNode()?.host?.closest("panel-list") ||
|
||||
this.closest("panel-list");
|
||||
|
||||
if (!this.#initialized) {
|
||||
this.#initialized = true;
|
||||
// When click listeners are added to the panel-item it creates a node in
|
||||
@ -683,18 +668,28 @@
|
||||
});
|
||||
|
||||
if (this.hasSubmenu) {
|
||||
this.panel.setAttribute("has-submenu", "");
|
||||
this.icon = document.createElement("div");
|
||||
this.icon.setAttribute("class", "submenu-icon");
|
||||
this.label.setAttribute("class", "submenu-label");
|
||||
|
||||
this.button.setAttribute("class", "submenu-container");
|
||||
this.button.appendChild(this.icon);
|
||||
|
||||
this.submenuSlot = document.createElement("slot");
|
||||
this.submenuSlot.name = "submenu";
|
||||
|
||||
this.shadowRoot.append(this.submenuSlot);
|
||||
|
||||
this.setSubmenuContents();
|
||||
}
|
||||
}
|
||||
|
||||
this.panel =
|
||||
this.getRootNode()?.host?.closest("panel-list") ||
|
||||
this.closest("panel-list");
|
||||
|
||||
if (this.panel) {
|
||||
this.panel.addEventListener("hidden", this);
|
||||
this.panel.addEventListener("shown", this);
|
||||
}
|
||||
|
||||
if (this.hasSubmenu) {
|
||||
this.addEventListener("mouseenter", this);
|
||||
this.addEventListener("mouseleave", this);
|
||||
@ -762,7 +757,9 @@
|
||||
|
||||
setSubmenuContents() {
|
||||
this.submenuPanel = this.submenuSlot.assignedNodes()[0];
|
||||
this.shadowRoot.append(this.submenuPanel);
|
||||
if (this.submenuPanel) {
|
||||
this.shadowRoot.append(this.submenuPanel);
|
||||
}
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
|
@ -22,6 +22,9 @@ panel-list-checked = Checked
|
||||
panel-list-badged = Badged, look at me
|
||||
panel-list-passwords = Passwords
|
||||
panel-list-settings = Settings
|
||||
submenu-item-one = Submenu Item One
|
||||
submenu-item-two = Submenu Item Two
|
||||
submenu-item-three = Submenu Item Three
|
||||
`,
|
||||
},
|
||||
};
|
||||
@ -36,7 +39,7 @@ function openMenu(event) {
|
||||
}
|
||||
}
|
||||
|
||||
const Template = ({ isOpen, items, wideAnchor }) =>
|
||||
const Template = ({ isOpen, items, wideAnchor, hasSubMenu }) =>
|
||||
html`
|
||||
<style>
|
||||
panel-item[icon="passwords"]::part(button) {
|
||||
@ -93,22 +96,36 @@ const Template = ({ isOpen, items, wideAnchor }) =>
|
||||
?open=${isOpen}
|
||||
?min-width-from-anchor=${wideAnchor}
|
||||
>
|
||||
${items.map(i =>
|
||||
i == "<hr>"
|
||||
${items.map((item, index) => {
|
||||
// Always showing submenu on the first item for simplicity.
|
||||
let showSubMenu = hasSubMenu && index == 0;
|
||||
let subMenuId = showSubMenu ? "example-sub-menu" : undefined;
|
||||
return item == "<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>
|
||||
`
|
||||
)}
|
||||
icon=${item.icon ?? ""}
|
||||
?checked=${item.checked}
|
||||
?badged=${item.badged}
|
||||
accesskey=${ifDefined(item.accesskey)}
|
||||
data-l10n-id=${item.l10nId ?? item}
|
||||
submenu=${ifDefined(subMenuId)}
|
||||
>
|
||||
${showSubMenu ? subMenuTemplate() : ""}
|
||||
</panel-item>
|
||||
`;
|
||||
})}
|
||||
</panel-list>
|
||||
`;
|
||||
|
||||
const subMenuTemplate = () => html`
|
||||
<panel-list slot="submenu" id="example-sub-menu">
|
||||
<panel-item data-l10n-id="submenu-item-one"></panel-item>
|
||||
<panel-item data-l10n-id="submenu-item-two"></panel-item>
|
||||
<panel-item data-l10n-id="submenu-item-three"></panel-item>
|
||||
</panel-list>
|
||||
`;
|
||||
|
||||
export const Simple = Template.bind({});
|
||||
Simple.args = {
|
||||
isOpen: false,
|
||||
@ -145,3 +162,9 @@ Wide.args = {
|
||||
...Simple.args,
|
||||
wideAnchor: true,
|
||||
};
|
||||
|
||||
export const SubMenu = Template.bind({});
|
||||
SubMenu.args = {
|
||||
...Simple.args,
|
||||
hasSubMenu: true,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user