I’ve been looking for a dropdown menu to use with
elm-ui for a while, and finally came across this article recently. The basic approach is adopted directly for the implementation described here.
The idea is simple – a dropdown is either showing a specific item (specifically, no item to start with), or a list of possible items.
type Dropdown a = ShowItem (Maybe a) | SelectItem (List a)
Translating the same idea into an
E.el [ E.width E.fill , Border.width 1 , E.padding 5 , E.below (viewOptionList options) ] (E.text selected)
This works because of
Element.below, which prevents the layout from being modified by the dropdown options when the menu is expanded.
elm-ui docs (which I should have read more carefully before, as it specifically mentions dropdowns…):
Let's say we want a dropdown menu. Essentially we want to say: put this element below this other element, but don't affect the layout when you do.
Element.row  [ Element.el [ Element.below (Element.text "I'm below!") ] (Element.text "I'm normal!") ]
Other than the actual rendering, a basic dropdown needs two or three messages to be wired through
- Select an item
- Expand the dropdown
- Clear the selection
Accordingly, the type signature for a generic dropdown looks like this:
view : String -> Dropdown a -> (a -> String) -> msg -> (a -> msg) -> msg -> E.Element msg view title dropdown toString openMsg clickMsg clearMsg
The full implementation is short and available for reference here.
This implementation only stores the current state in the dropdown. So, for example, when a specific item is selected, dropdown has type
ShowItem (Maybe a) and isn’t aware of all its possible options.
While it’d be simple to store all possible options in the dropdown itself, loading the options dynamically when the user expands the dropdown can be cleaner to model, especially if the options should be filtered by other data in the model.
An exampe of multiple dropwdowns that filter different features of shared data is available here.
From GUI-based testing, a few items emerge:
The dropdown options can be very many, rendering a long menu; using
scrollbarYwith a fixed height is a good alternative.
It’s natural user behavior to click outside the dropdown menu when it’s open, to indicate a “cancel” action. How should that be supported? (But how to specify a click target that’s everything except the dropdown?)
When there are many filters, it can be difficult to identify which filters are selected at a glance, so some visual cues like background coloring are helpful
It turns out that rolling one’s own dropdown with
elm-ui isn’t difficult – at least for something rudimentary that’s nonetheless very usable. The below screenshot shows some dropwown filters incorporating the test observations. The most noticeable missing feature is the ability to click outside the dropdown to close it, for which I haven’t found a good solution yet..