# DropLab

A generic dropdown for all of your custom dropdown needs.

## Install

* Add [`/dist/droplab.js`](/dist/droplab.js) to your application assets.
* Link to it in your code. See [examples](#examples).

If no modules API is detected, the library will fall back and add `window.DropLab`.

## Usage

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

The value of the `data-dropdown-trigger` should be a CSS selector that
DropLab can use to find the triggers 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`](explicit-instantiation).
If you do not provide any arguments, it will globally query and instantiate all droplab-compatible dropdowns.

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

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

<script>
  new DropLab().init();
</script>
```

As you can see, we have a "Toggle" link, that is 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>

<script>
  new DropLab().init();
</script>
```

### 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>

<script>
  const trigger = document.getElementById('trigger');
  const list = document.getElementById('list');

  new DropLab().init(trigger, list);
</script>
```

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>

<script>
  const droplab = new DropLab();

  droplab.init();

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

  droplab.addHook(trigger, list);
</script>
```


### Dynamic data

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

You can template a list item using the keys of the data object provided.

Passing an array of objects to `DropLab.prototype.addData` will render 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>

<script>
  const droplab = new DropLab();

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

Alternatively, you can specify a specific dropdown to add this data to but passing
the data as the second argument and 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>

<script>
  const droplab = new DropLab();

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

This allows you to mix static and dynamic content with ease, even with 1 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>

<script>
  const droplab = new DropLab();

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

## 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.


### Examples

Some example implementations are provided in [`/examples`](examples).

Please note: It is recommended you use `npm run serve` to view the examples
as it will allow you to perform a mock JSON request for ajax-dependent examples.

## 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 will fall back as it does with `window.DropLab` and will add `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 config 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>

<script>
  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',
    },
  });
</script>
```

Some plugins are available in [`/dist/plugins`](/dist/plugins).

#### Plugin documentation

* [ajax.datasource](/docs/plugins/ajax.datasource.md)
* [filter](/docs/plugins/filter.md)

### Development

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

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

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

export default MyPlugin;
```

## Development

Use `npm run webpack` to create the DropLab and plugin bundles.

Use `npm run karma` to run the unit tests.
Currently, this will run the tests on Chrome and PhantomJS.
It will output an Istanbul HTML coverage report to `/coverage`.

Use `npm run karma-ci` to only run PhantomJS in CI-mode (clean `exit 0` or `exit 1`).

Scripts can be compiled into [`/dist`](/dist) with `npm run compile`.
