summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/panel-list/README.stories.md
blob: b8800e2b5f7aa6c19465f3e62f87815fd10ce30d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# Panel Menu

The `panel-list` and `panel-item` components work together to create a menu for
in-content contexts. The basic structure is a `panel-list` with `panel-item`
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
support accesskey underlining (although using `moz-label` could change that).
Shortcuts are not displayed automatically in the `panel-item`.

```html story
<panel-list stay-open open>
    <panel-item action="new" accesskey="N">New</panel-item>
    <panel-item accesskey="O">Open</panel-item>
    <hr />
    <panel-item action="save" accesskey="S">Save</panel-item>
    <hr />
    <panel-item accesskey="Q">Quit</panel-item>
</panel-list>
```

## Status

Current status is listed as in-development since this is only intended for use
within in-content contexts. XUL is still required for accesskey underlining, but
could be migrated to use the `moz-label` component. This is a useful but
historical element that could likely use some attention at the API level and to
be brought up to our design systems standards.

## When to use

* When there are multiple options for something that would take too
  much space with individual buttons.
* When the actions are not frequently needed.
* When you are within an in-content context.

## When not to use

* When there is only one action.
* When the actions are frequently needed.
* In the browser chrome, you probably want to use
  [menupopup](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/menupopup.js)
  or
  [panel](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/panel.js)
  instead.

## Basic usage

The source for `panel-list` can be found under
[toolkit/content/widgets/panel-list.js](https://searchfox.org/mozilla-central/source/toolkit/content/widgets/panel-list.js).
You can find an examples of `panel-list` in use in the Firefox codebase in both
[about:addons](https://searchfox.org/mozilla-central/source/toolkit/mozapps/extensions/content/aboutaddons.html#87,102,114)
and the
[migration-wizard](https://searchfox.org/mozilla-central/source/browser/components/migration/content/migration-dialog-window.html#18).

`panel-list` will automatically be imported in chrome documents, both through
markup and through JS with `document.createElement("panel-list")` or by cloning
a template.

```html
<!-- This will import `panel-list` if needed in most cases. -->
<panel-list></panel-list>
```

In non-chrome documents it can be imported into `.html`/`.xhtml` files:

```html
<script src="chrome://global/content/elements/panel-list.js"></script>
```

And used as follows:

```html
<panel-list>
  <panel-item accesskey="N">New</panel-item>
  <panel-item accesskey="O">Open</panel-item>
  <hr />
  <panel-item accesskey="S">Save</panel-item>
  <hr />
  <panel-item accesskey="Q">Quit</panel-item>
</panel-list>
```

The `toggle` method takes the event you received on your anchor button and opens
the menu attached to that element.

```js
anchorButton.addEventListener("mousedown", e => panelList.toggle(e));
```

Accesskeys are activated with the bare accesskey letter when the menu is opened.
So for this example after opening the menu pressing `s` will fire a click event
on the Save `panel-item`.

Note: XUL is currently required for accesskey underlining, but can be [replaced
with `moz-label`](https://bugzilla.mozilla.org/show_bug.cgi?id=1828741) later.

### Fluent usage

The `panel-item` expects to have text content set by fluent.

```html
<panel-list>
  <panel-item data-l10n-id="menu-new"></panel-item>
  <panel-item data-l10n-id="menu-save"></panel-item>
</panel-list>
```

In which case your Fluent messages will look something like this:

```
menu-new = New
    .accesskey = N
menu-save = Save
    .accesskey = S
```

## Advanced usage

### Showing the menu

By default the menu will be hidden. It is shown when the `open` attribute is
set, but that won't position the menu by default.

To trigger the auto-positioning of the menu, it should be opened or closed using
the `toggle(event)` method.

```js
function onMenuButton(event) {
  document.querySelector("panel-list").toggle(event);
}
```

The `toggle(event)` method will use `event.target` as the anchor for the menu.

To achieve the expected behaviour, the menu should open on `mousedown` for mouse
events, and `click` for keyboard events. This can be accomplished by checking
the `event.inputSource` property in chrome contexts or `event.detail` in
non-chrome contexts (`event.detail` will be the click count which is `0` when a
click is from the keyboard).

```js
function openMenu(event) {
  if (
    event.type == "mousedown" ||
    event.inputSource == MouseEvent.MOZ_SOURCE_KEYBOARD ||
    !event.detail
  ) {
    document.querySelector("panel-list").toggle(event);
  }
}

let menuButton = document.getElementById("open-menu-button");
menuButton.addEventListener("mousedown", openMenu);
menuButton.addEventListener("click", openMenu);
```

### Icons

Icons can be added to the `panel-item`s by setting a `background-image` on
`panel-item::part(button)`.

```css
panel-item[action="new"]::part(button) {
  background-image: url("./new.svg");
}

panel-item[action="save"]::part(button) {
  background-image: url("./save.svg");
}
```

### Badging

Icons may be badged by setting the `badged` attribute. This adds a dot next to
the icon.

```html
<panel-list>
  <panel-item action="new">New</panel-item>
  <panel-item action="save" badged>Save</panel-item>
</panel-list>
```

```html story
<panel-list stay-open open>
  <panel-item action="new">New</panel-item>
  <panel-item action="save" badged>Save</panel-item>
</panel-list>
```

### Matching anchor width

When using the `panel-list` like a `select` dropdown, it's nice to have it match
the size of the anchor button. You can see this in practice in the
[Wide variant](?path=/story/ui-widgets-panel-list--wide) and the
`migration-wizard`. Setting the `min-width-from-anchor` attribute will cause the
menu to match its anchor's width when it is opened.

```html
<button class="current-selection">Apples</button>
<panel-list min-width-from-anchor>
  <panel-item>Apples</panel-list>
  <panel-item>Bananas</panel-list>
</panel-list>
```

### Usage in a XUL `panel`

The "new" (as of early 2023) migration wizard uses the `panel-list` inside of a
XUL `panel` element to let its contents escape its container dialog by creating
an OS-level window. This can be useful if the menu could be larger than its
container, however in chrome contexts you are likely better off using
`menupopup`.

By placing a `panel-list` inside of a XUL `panel` it will automatically defer
its positioning responsibilities to the XUL `panel` and it will then be able to
grow larger than its containing window if needed.

```html
<!-- Assuming we're in a XUL document. -->
<panel>
  <html:panel-list>
    <html:panel-item>Apples</html:panel-item>
    <html:panel-item>Apples</html:panel-item>
    <html:panel-item>Apples</html:panel-item>
  </html:panel-list>
</panel>
```