summaryrefslogtreecommitdiff
path: root/doc/development/fe_guide/droplab/droplab.md
blob: 8f1ecc115fe3b1d53f948ed5b7843f299770af0b (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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
---
stage: none
group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---

# DropLab

A generic dropdown for all of your custom dropdown needs.

## Usage

DropLab can be used by adding a `data-dropdown-trigger` HTML attribute. This
attribute allows us to find the "trigger" _(toggle)_ for the dropdown, whether
it's a button, link or input.

The value of the `data-dropdown-trigger` should be a CSS selector that DropLab
can use to find the trigger's dropdown list.

You should also add the `data-dropdown` attribute to declare the dropdown list.
The value is irrelevant.

The DropLab class has no side effects, so you must always call `.init` when the
DOM is ready. `DropLab.prototype.init` takes the same arguments as `DropLab.prototype.addHook`.
If you don't provide any arguments, it globally queries and instantiates all
DropLab-compatible dropdowns.

```html
<a href="#" data-dropdown-trigger="#list">Toggle</a>

<ul id="list" data-dropdown>
  <!-- ... -->
<ul>
```

```javascript
const droplab = new DropLab();
droplab.init();
```

As noted, we have a "Toggle" link that's declared as a trigger. It provides a
selector to find the dropdown list it should control.

### Static data

You can add static list items.

```html
<a href="#" data-dropdown-trigger="#list">Toggle</a>

<ul id="list" data-dropdown>
  <li>Static value 1</li>
  <li>Static value 2</li>
<ul>
```

```javascript
const droplab = new DropLab();
droplab.init();
```

### Explicit instantiation

You can pass the trigger and list elements as constructor arguments to return a
non-global instance of DropLab using the `DropLab.prototype.init` method.

```html
<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>

<ul id="list" data-dropdown>
  <!-- ... -->
<ul>
```

```javascript
const trigger = document.getElementById('trigger');
const list = document.getElementById('list');

const droplab = new DropLab();
droplab.init(trigger, list);
```

You can also add hooks to an existing DropLab instance using `DropLab.prototype.addHook`.

```html
<a href="#" data-dropdown-trigger="#auto-dropdown">Toggle</a>
<ul id="auto-dropdown" data-dropdown><!-- ... --><ul>

<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
<ul id="list" data-dropdown><!-- ... --><ul>
```

```javascript
const droplab = new DropLab();

droplab.init();

const trigger = document.getElementById('trigger');
const list = document.getElementById('list');

droplab.addHook(trigger, list);
```

### Dynamic data

Adding `data-dynamic` to your dropdown element enables dynamic list
rendering.

You can template a list item using the keys of the data object provided. Use the
handlebars syntax `{{ value }}` to HTML escape the value. Use the `<%= value %>`
syntax to interpolate the value. Use the `<%= value %>` syntax to evaluate the
value.

Passing an array of objects to `DropLab.prototype.addData` renders that data
for all `data-dynamic` dropdown lists tracked by that DropLab instance.

```html
<a href="#" data-dropdown-trigger="#list">Toggle</a>

<ul id="list" data-dropdown data-dynamic>
  <li><a href="#" data-id="{{id}}">{{text}}</a></li>
</ul>
```

```javascript
const droplab = new DropLab();

droplab.init().addData([{
  id: 0,
  text: 'Jacob',
}, {
  id: 1,
  text: 'Jeff',
}]);
```

Alternatively, you can specify a specific dropdown to add this data to by
passing the data as the second argument and the `id` of the trigger element as
the first argument.

```html
<a href="#" data-dropdown-trigger="#list" id="trigger">Toggle</a>

<ul id="list" data-dropdown data-dynamic>
  <li><a href="#" data-id="{{id}}">{{text}}</a></li>
</ul>
```

```javascript
const droplab = new DropLab();

droplab.init().addData('trigger', [{
  id: 0,
  text: 'Jacob',
}, {
  id: 1,
  text: 'Jeff',
}]);
```

This allows you to mix static and dynamic content, even with one trigger.

Note the use of scoping regarding the `data-dropdown` attribute to capture both
dropdown lists, one of which is dynamic.

```html
<input id="trigger" data-dropdown-trigger="#list">
<div id="list" data-dropdown>
  <ul>
    <li><a href="#">Static item 1</a></li>
    <li><a href="#">Static item 2</a></li>
  </ul>
  <ul data-dynamic>
    <li><a href="#" data-id="{{id}}">{{text}}</a></li>
  </ul>
</div>
```

```javascript
const droplab = new DropLab();

droplab.init().addData('trigger', [{
  id: 0,
  text: 'Jacob',
}, {
  id: 1,
  text: 'Jeff',
}]);
```

## Internal selectors

DropLab adds some CSS classes to help lower the barrier to integration.

For example:

- The `droplab-item-selected` CSS class is added to items that have been
  selected either by a mouse click or by enter key selection.
- The `droplab-item-active` CSS class is added to items that have been selected
  using arrow key navigation.
- You can add the `droplab-item-ignore` CSS class to any item that you don't
  want to be selectable. For example, an `<li class="divider"></li>` list
  divider element that shouldn't be interactive.

## Internal events

DropLab uses some custom events to help lower the barrier to integration.

For example:

- The `click.dl` event is fired when an `li` list item has been clicked. It's
  also fired when a list item has been selected with the keyboard. It's also
  fired when a `HookButton` button is clicked (a registered `button` tag or `a`
  tag trigger).
- The `input.dl` event is fired when a `HookInput` (a registered `input` tag
  trigger) triggers an `input` event.
- The `mousedown.dl` event is fired when a `HookInput` triggers a `mousedown`
  event.
- The `keyup.dl` event is fired when a `HookInput` triggers a `keyup` event.
- The `keydown.dl` event is fired when a `HookInput` triggers a `keydown` event.

These custom events add a `detail` object to the vanilla `Event` object that
provides some potentially useful data.

## Plugins

Plugins are objects that are registered to be executed when a hook is added (when
a DropLab trigger and dropdown are instantiated).

If no modules API is detected, the library falls back as it does with
`window.DropLab` and adds `window.DropLab.plugins.PluginName`.

### Usage

To use plugins, you can pass them in an array as the third argument of
`DropLab.prototype.init` or `DropLab.prototype.addHook`. Some plugins require
configuration values; the configuration object can be passed as the fourth argument.

```html
<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
<ul id="list" data-dropdown><!-- ... --><ul>
```

```javascript
const droplab = new DropLab();

const trigger = document.getElementById('trigger');
const list = document.getElementById('list');

droplab.init(trigger, list, [droplabAjax], {
  droplabAjax: {
    endpoint: '/some-endpoint',
    method: 'setData',
  },
});
```

### Documentation

Refer to the list of available [DropLab plugins](plugins/index.md) for
information about their use.

### Development

When plugins are initialised for a DropLab trigger+dropdown, DropLab calls the
plugins' `init` function, so this must be implemented in the plugin.

```javascript
class MyPlugin {
  static init() {
    this.someProp = 'someProp';
    this.someMethod();
  }

  static someMethod() {
    this.otherProp = 'otherProp';
  }
}

export default MyPlugin;
```