ensolabs/designsystemet-elm/1.0.0

Designsystemet.Pagination

Navigation between the pages of a long list or table.

Mirrors Pagination and usePagination from @digdir/designsystemet-react.

Pagination.buttons
    { current = model.page
    , total = model.pageCount
    , onSelect = PageSelected
    , labels = paginationLabels
    }
    |> Pagination.view []


paginationLabels : Pagination.Labels
paginationLabels =
    { navigation = "Bla i sider"
    , previous = "Forrige"
    , next = "Neste"
    , page = \page -> "Side " ++ String.fromInt page
    }

which renders

Forrige  1  …  4  [5]  6  …  12  Neste

Visible pages are picked with the same algorithm as designsystemet-web, so the output matches React. Pages are numbered from 1.

Buttons or links

  • buttons send a message. Use them when the page number lives only in your model.
  • links are <a href>s. Use them when the page number is in the URL, so pages can be bookmarked and opened in new tabs.

React → Elm

  • usePagination({ currentPage, totalPages, onChange }) → Pagination.buttons { current, total, onSelect, … }
  • <Pagination.Button asChild><a href> → Pagination.links { current, total, toHref, … }
  • showPages={5} → Pagination.withMaxVisible 5
  • empty Pagination.Button with aria-label at the ends → Pagination.withArrowsOnly
  • aria-label="Bla i sider" → labels.navigation
  • <Pagination> + List + Item + Button → all rendered by Pagination.view
  • pagination({ current, total, show }) (web) → Pagination.items

Differences from React

data-current, data-total and data-href are never set. With them, designsystemet-web would render the pages itself and fight Elm over the markup.

The previous and next buttons always have aria-hidden ("true" when there is no such page), as with React's usePagination. Their visible text is also their accessible name. The upstream stories give them a longer aria-label ("Forrige side" next to "Forrige"), which hides the visible text from screen readers. With withArrowsOnly the text becomes the aria-label, as in the upstream mobile story.

Managed by designsystemet-web

Never set role on the pagination yourself.

{ current : Int
, total : Int
, onSelect : Int -> msg
, labels : Labels
}
-> Pagination msg

Page buttons that send onSelect with the page number.

type alias Labels =
{ navigation : String
, previous : String
, next : String
, page : Int -> String
}

The texts in a pagination. Define them once per app or language.

  • navigation names the whole thing for screen readers (aria-label).
  • previous and next are shown next to the arrow on the end buttons.
  • page names one page button for screen readers. The button shows only the number.

Max page items to show, counting each "…" but not the previous and next buttons. Defaults to 7. Use 5 or fewer on narrow screens.

Show only the arrows on the previous and next buttons, for narrow screens. labels.previous and labels.next become their aria-labels.

Pagination.buttons { current = model.page, total = 10, onSelect = PageSelected, labels = labels }
    |> Pagination.withMaxVisible 3
    |> Pagination.withArrowsOnly
    |> Pagination.view []
view : List (Attribute msg) -> Pagination msg -> Html msg

Render the <ds-pagination class="ds-pagination">.

The algorithm

type Item
= Page Int
| Ellipsis

One item between the previous and next buttons.

{ current : Int
, total : Int
, show : Int
}
-> List Item

Which items to show. A port of pagination() from designsystemet-web:

items { current = 5, total = 12, show = 7 }
--> [ Page 1, Ellipsis, Page 4, Page 5, Page 6, Ellipsis, Page 12 ]

The first and last page are always included (when show is at least 5 and 4 respectively), and the current page is kept in the middle when possible. Use it to render your own pagination.