ensolabs/designsystemet-elm/1.0.0

Designsystemet.ToggleGroup

A row of buttons where exactly one is chosen, like a segmented control.

Mirrors ToggleGroup from @digdir/designsystemet-react.

type Layout
    = List
    | Grid


layoutToString : Layout -> String
layoutToString layout =
    case layout of
        List ->
            "list"


        Grid ->
            "grid"


ToggleGroup.controlled
    { name = "layout"
    , label = "Visning"
    , toValue = layoutToString
    , selected = model.layout
    , onSelect = LayoutSelected
    }
    []
    [ ToggleGroup.item List [ Html.text "Liste" ]
    , ToggleGroup.item Grid [ Html.text "Rutenett" ]
    ]

It's a <fieldset> of radio buttons, so forms and screen readers treat it like any radio group. designsystemet-web makes the arrow keys move focus without choosing, and lets Enter choose.

Controlled or uncontrolled

  • uncontrolled: the browser tracks the chosen item, like any radio group. Use it in a plain <form> that gets submitted, or when nothing else on the page depends on the choice.
  • controlled: your model says which item is chosen, and clicking an item sends a message instead of choosing it.

React → Elm

  • <ToggleGroup aria-label="…" defaultValue="a"> → ToggleGroup.uncontrolled { label = "…", initial = A, … } []
  • <ToggleGroup value={v} onChange={f}> → ToggleGroup.controlled { selected = v, onSelect = F, … } []
  • <ToggleGroup.Item value="a">A</ToggleGroup.Item> → ToggleGroup.item A [ Html.text "A" ]
  • icon-only item + <Tooltip> → ToggleGroup.iconItem { value = A, label = "…", icon = … }
  • <ToggleGroup.Item disabled> → ToggleGroup.item A […] |> ToggleGroup.disabled
  • <ToggleGroup.Item aria-disabled="true"> → ToggleGroup.item A […] |> ToggleGroup.disabledFocusable
  • <ToggleGroup.Item readOnly> → ToggleGroup.item A […] |> ToggleGroup.readOnly
  • variant="secondary" → ToggleGroup.secondary

Differences from React

  • An icon item's radio button also gets the label as its aria-label, so it is named before designsystemet-web turns the tooltip into the item's aria-label.
  • The name is required. React generates one with useId.

Managed by designsystemet-web

Never set these yourself: role on the fieldset and the radios, and tabindex on the radios (the focusgroup polyfill moves it around).

{ name : String
, label : String
, toValue : a -> String
, initial : a
}
-> List (Attribute msg)
-> List (Item a msg)
-> Html msg

A toggle group the browser tracks. initial is the item chosen on first render.

ToggleGroup.uncontrolled
    { name = "folder"
    , label = "Filtrering"
    , toValue = folderToString
    , initial = Inbox
    }
    []
    [ ToggleGroup.item Inbox [ Html.text "Innboks" ]
    , ToggleGroup.item Drafts [ Html.text "Utkast" ]
    ]
  • name is the radio buttons' name, which the value is submitted under in a form. Unique per form.
  • label is the group's accessible name (aria-label). To name it with a visible heading, also pass Attr.attribute "aria-labelledby" headingId, which takes precedence.
  • toValue turns a value into the radio button's value.

If initial changes later, that item is chosen. Otherwise Elm leaves the choice to the browser.

{ name : String
, label : String
, toValue : a -> String
, selected : a
, onSelect : a -> msg
}
-> List (Attribute msg)
-> List (Item a msg)
-> Html msg

A toggle group your model tracks. Clicking an item (or pressing Enter on it) sends onSelect with its value. The chosen item changes only when selected does.

The fields are as in uncontrolled, with selected and onSelect instead of initial.

Items

type Item a msg

One choice in the group.

item : a -> List (Html msg) -> Item a msg

An item with visible content, usually text.

ToggleGroup.item Archived [ Html.text "Arkiverte" ]
{ value : a
, label : String
, icon : Html msg
}
-> Item a msg

An item showing only an icon. The label is required, since an icon means nothing to a screen reader or to someone who doesn't recognise it. It names the radio button and shows as a tooltip on hover and focus.

ToggleGroup.iconItem { value = Grid, label = "Rutenett", icon = gridIcon }

Items that can't be chosen

disabled : Item a msg -> Item a msg

An item that can't be chosen right now. It's dimmed and the keyboard skips it.

ToggleGroup.item Map [ Html.text "Kart" ]
    |> ToggleGroup.disabled
disabledFocusable : Item a msg -> Item a msg

A dimmed item the keyboard still reaches, so screen reader users find it and hear that it's unavailable (aria-disabled).

In a controlled group it can't be chosen. In an uncontrolled group it's only marked unavailable, as in React, and the browser still lets the user choose it.

readOnly : Item a msg -> Item a msg

An item that is shown but can't be switched to, for a value fixed in this context. The keyboard still reaches it, and designsystemet-web stops clicks on it.

Variants

primary : Attribute msg

The default. The chosen item is filled with the main color.

secondary : Attribute msg

A quieter look: a tinted track with the chosen item raised on it.