swisspost / design-system

The Swiss Post Design System pattern library for a consistent and accessible user experience across the web platform.
https://design-system.post.ch
Apache License 2.0
121 stars 14 forks source link

Create an action menu button component #1184

Closed gfellerph closed 1 year ago

gfellerph commented 1 year ago

Create an action menu button. This button shows a dropdown with a list of links on click and offers various ways to browse that list.

Requirement URL
Accessibility https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/examples/menu-button-actions-active-descendant/
Design https://www.figma.com/file/xZ0IW0MJO0vnFicmrHiKaY/Components?node-id=134%3A1328&t=Aq1KrFMZrhO2HRgt-1 (needs revision)
Existing implementation Internet Header Language Switch on Desktop
https://next.design-system.post.ch/?path=/story/components-internet-header-header--default (does not fulfill accessibility requirements)

Proposed API

In order to provide flexibility to the list item template, there are (at least) two options:

Option 1: Custom template for list items

<post-menu-button items="Array<{ url: string, text: string, target: string }>">
  Button text
</post-menu-button>

This approach uses a input attribute accepting an array of items. The control over the list markup is with the component and accessibility can be guaranteed. Still, there is a possibility to customize the list item template in a general way. A possible implementation could look like this: https://lit.dev/playground/#gist=78bf6997e7ecf38d8d981f66d7e3f472.

Custom list item template implementation

A slot might not be best suited for this use case, therefore an external reference could make more sense.

<post-menu-button items="Array<string>" list-item-template="custom-menu-button-list-item-template">
  Button text
</post-menu-button>

<template id="custom-menu-button-list-item-template">
  <post-icon name="1001" />
  <slot></slot>
</template>

++ Simple implementation ++ Items are passed via JS API in most cases anyway ++ Full control over list items from within web component -- Limited possibility to define custom template, just one template for all items. A language selector dropdown with custom flag icons for every language would not be possible (easily) -- Needs JS to render items

Option 2: User defined list of items

Another approach for supporting custom templates could be to take a list of links as slotted content instead of an array of items as property.

Test implementation (WIP): https://lit.dev/playground/#gist=212821f67bab636ff2d53552251373e7

<post-menu-button>
  <span slot="button">Button text</span>
  <a href="/">Apples</a>
  <a href="/">Pears</a>
  <a href="/">Oranges</a>
  <a href="/">Strawberries</a>
</post-menu-button>

++ Items are defined with standard markup and fully customizable, a language selection with custom flag icons would easily be possible ++ Items are available before JS loads (when not rendered client side) -- Limited control over slotted items from within web component, their state could change any moment -- Implementation has to listen to slot changes in order to register correct event handlers

Option 3

There is also the possibility to split up the component into smaller parts in order to give more flexibility as to the contents of each element.

<post-menu-button>
  <post-button variant="primary">Button text</post-button>
  <post-menu-button-list>
    <div>Optional header content</div>
    <a href="/">Link 1</a>
    <a href="/">Link 2</a>
    <h3>Optional category heading</h3>
    <a href="/">Link 3</a>
    <div>Optional footer content</div>
  </post-menu-button-list>
</post-menu-button>

++ Maximum content flexibility -- More complicated interaction between components from component perspective (quagmire of slots) -- More markup required -- Implementation has to listen to slot changes in order to register correct event handlers

Option 4

Following the headless-ui listbox example, a third component is used for rendering the links. This gives control over the links back to the library authors.

<post-listbox>
  <button class="btn btn-primary">Button text</button>
  <post-listbox-list>
    <post-listbox-item href="/">Link 1</post-listbox-item>
    <post-listbox-item href="/">Link 2</post-listbox-item>
    <post-listbox-item href="/">Link 3</post-listbox-item>
  </post-listbox-list>
</post-listbox>

++ Good content flexibility ++ Control over important elements is with the library author -- More markup required -- Implementation has to listen to slot changes in order to register correct event handlers

Edge cases

Examples of possible use cases

Language switch image

Breadcrumbs on mobile image

Login widget Might not be ideal because when using aria-activedescendant, the additional content in the dropdown header might not be accessible or skipped by assistive tech. image

gfellerph commented 1 year ago

https://headlessui.com/react/listbox