Multiaction buttons

These can be used in areas where there are a lot of options for a user to choose from but they are all variations of the same task. For example a download button where the dropdown gives options to download as a pdf, as an excel doc etc.

From a usability point of view, this type of button should only be used in conjunction with other buttons. Placing it near other actions will make it clearer what it is for. Also be careful not to hide really important functionality inside the dropdown.

Keyboard control

Expected behaviour taken from https://www.w3.org/WAI/tutorials/menus/application-menus/

Key mapping for top-level menu items
Key Action
tab ⇥ Select the next focusable element outside of the menu.
shift ⇧ + tab ⇥ Select the previous focusable element outside of the menu.
return/enter ↵ Open the submenu, select first submenu item.
space Open the submenu, select first submenu item.
down ↓ Open the submenu, select first submenu item.
up ↑ Open the submenu, select last submenu item.
Key mapping for submenu items
Key Action
tab ⇥ Close the submenu, select the next focusable element outside of the menu.
shift ⇧ + tab ⇥ Close the submenu, select the previous focusable element outside of the menu.
return/enter ↵ Carry out function of this item.
space Carry out function of this item.
down ↓ Select next submenu item.
up ↑ Select previous submenu item.
esc Close the submenu, select the current top-level menu item.

How they work

The markup is quite straight-forward: the buttons you want to show in the popup should be enclosed in an appropriate element that has the data-multiaction-button attribute and an id, as shown in the examples.

UCASDesignFramework.multiactionButtons.init() will run on load, if the page is rendered on the server. On a client-rendered page, you will need to run this after adding the elements to the DOM.

The init() will add a trigger button.

When clicked, show() will temporarily move the buttons to #multiactionButtonContainer, which is located just before the closing tag. This enables us to position the popup without any overflow or z-index issues.

When the popup is closed, hide() will move the buttons back to their original location in the DOM.

Examples

<!-- Multiaction button start -->
<div data-multiaction-button='' data-multiaction-button-label=More id="id25b9292f27">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->
<!-- Multiaction button start -->
<div data-multiaction-button='{"position": "right"}' id="id7b73d691b3">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Single, solitary multiaction button

<!-- Multiaction button start -->
<div data-multiaction-button='{"position": "left", "orientation": "vertical", "hideLabel": true}' id="id0fff0ec1a6">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Single, solitary, small, compact multiaction button

<!-- Multiaction button start -->
<div data-multiaction-button='{"orientation": "vertical", "hideLabel": true, "small": true, "position": "left", "compact": true}' id="id2073a7248e">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Multiaction button with primary or standard

Multiaction buttons are by default secondary, but this can be overridden.

<!-- Multiaction button start -->
<div data-multiaction-button='{"position": "right", "type": "standard"}' id="id2b8a5c0dff">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Disabled multiaction button

<!-- Multiaction button start -->
<div data-multiaction-button='{"orientation": "vertical", "position": "left"' id="id49ce57ebbb" data-multiaction-button-disabled='true'>
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Multiaction button with disabled button

<!-- Multiaction button with disabled start -->
<div data-multiaction-button='{"orientation": "vertical", "position": "left"' id="id638e8dc9ed">
  <button class="button" type="button">Quis</button>
  <button class="button" type="button" disabled>Et</button>
  <button class="button button--secondary" type="button">Consequatur</button>
</div>
<!-- Multiaction button with disabled end -->

Multiaction buttons in tables

Culpa dolores Rerum voluptatem recusandae magnam deleniti iusto Temporibusquoset Non sit Account status Actions
Quos quos Ut fugit ipsa Voluptatem voluptates praesentium
Est magnam Ut voluptatum ex Dolor doloribus

Theme inverse multiaction buttons

When wanting to use multiaction buttons with theme inverse you must insure you use the theme-inverse class on the data-multiaction-button element.

Single, solitary multiaction button

<!-- Multiaction button start -->
<div class="theme-inverse" data-multiaction-button='{"position": "left", "orientation": "vertical", "hideLabel": true}' id="idc41718c522">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Single, solitary, small, compact multiaction button

<!-- Multiaction button start -->
<div class="theme-inverse" data-multiaction-button='{"orientation": "vertical", "hideLabel": true, "small": true, "position": "left", "compact": true}' id="idb25f6db4f6">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Multiaction button with primary or standard

Multiaction buttons are by default secondary, but this can be overridden.

<!-- Multiaction button start -->
<div class="theme-inverse" data-multiaction-button='{"position": "right", "type": "standard"}' id="ida23833f880">
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Disabled multiaction button

<!-- Multiaction button start -->
<div class="theme-inverse" data-multiaction-button='{"orientation": "vertical", "position": "left"' id="id72a2f4c44c" data-multiaction-button-disabled='true'>
  <button class="button" type="button">Lorem ipsum</button>
  <button class="button" type="button">Dolor</button>
  <button class="button button--secondary" type="button">Sit amet</button>
</div>
<!-- Multiaction button end -->

Multiaction button with disabled button

<!-- Multiaction button with disabled start -->
<div class="theme-inverse" data-multiaction-button='{"orientation": "vertical", "position": "left"' id="idefdff507a7">
  <button class="button" type="button">Iure libero</button>
  <button class="button" type="button" disabled>Et</button>
  <button class="button button--secondary" type="button">Harum</button>
</div>
<!-- Multiaction button with disabled end -->

API methods

UCASDesignFramework.multiactionButtons.init() // Initialise multiactionButtons plugin.
UCASDesignFramework.multiactionButtons.init($context) // Initialise multiactionButtons plugin.
UCASDesignFramework.multiactionButtons.init($context, $selector) // Initialise multiactionButtons plugin.

UCASDesignFramework.multiactionButtons.show($id) // Show multiactionButtons with ID.
UCASDesignFramework.multiactionButtons.show($node) // Show multiactionButtons with node.

UCASDesignFramework.multiactionButtons.hide($id) // Hide multiactionButtons with ID.
UCASDesignFramework.multiactionButtons.hide($node) // Hide multiactionButtons with node.

UCASDesignFramework.multiactionButtons.hideAll() // Hide all multiactionButtons.

UCASDesignFramework.multiactionButtons.disable($id) // Disable multiactionButtons with ID.
UCASDesignFramework.multiactionButtons.disable($node) // Disable multiactionButtons with node.

UCASDesignFramework.multiactionButtons.enable($id) // Enable multiactionButtons with ID.
UCASDesignFramework.multiactionButtons.enable($node) // Enable multiactionButtons with node.

Global bus messages

Can be seen in UCASDesignFramework.subscriptions.df

UCASDesignFramework.bus.publish('df.multiactionButtons.init')
UCASDesignFramework.bus.publish('df.multiactionButtons.destroy')
UCASDesignFramework.bus.publish('df.multiactionButtons.hideAll')
// @param {string} id
UCASDesignFramework.bus.publish('df.multiactionButtons.hide', id)
UCASDesignFramework.bus.publish('df.multiactionButtons.show', id)
UCASDesignFramework.bus.publish('df.multiactionButtons.disable', id)
UCASDesignFramework.bus.publish('df.multiactionButtons.enable', id)

Dynamic content test

This is used to test whether stray multiaction popups are cleaned up when the buttons that trigger them are removed by dynamic content, such as in a SPA.
This is needed because the buttons in the multiaction popup are moved in the DOM from their original home to #multiactionButtonContainer while they are being displayed (so we can put them on a different "layer" above everything else on the page) and the SPA has no idea this is happening.

This cleanup is called on init, destroy and also in the middle of hiding a multiaction popup, in case the trigger button has been removed while the hide method is being called.

Use the Update content button in the multiaction below, which will replace the whole section, including the button, with new content.
View #multiactionButtonContainer in the inspector. It should remain empty and hidden when a multiaction popup is not being displayed.

We also need to handle focus in this situation, as otherwise keyboard users would be lost on the page when the original trigger button is removed from the DOM. We can react to a custom focusLost event, like this:
document.addEventListener('focusLost', function(e) {
  // The e.focusLostIdentifier property is the ID of the original element that disappeared, in case that's of use.

  // Focus has been lost, so we need to decide where it now goes.
  // In this example, we've decided to focus on the new multiaction button that was added,
  // as that will make most sense in the flow of this particular page.
  var dynamicContentTest = document.getElementById('dynamicContentTest')
  var button = dynamicContentTest.querySelector('.button--multiaction')

  // The UCASUtilities.focus component will handle checking that the element is focusable and
  // will resort to focusing other, generic elements on the page if it is not.
  UCASUtilities.focus.focus(button)
})
#multiactionButtonContainer doesn't currently exist.