Menus within react-md
can be created using the DropdownMenu
component which
is a wrapper for the lower level components and should work for most of your use
cases. The menu will handle the visibility of the menu and renders the button to
show the menu, the menu itself, and all items within the menu.
All the props provided to the DropdownMenu
will be passed down to the Button
component so you can style your button like normal. To render dropdown items,
you must supply a list of items to the dropdown menu which will be rendered in
MenuItem
s with the following logic by default:
null
(so nothing)MenuItemSeparator
role="separator"
, render a MenuItemSeparator
and pass the object as props.MenuItem
component.href
, to
, or component
props exist, render it
as a MenuItemLink
MenuItem
Note that links must be rendered with the
MenuItemLink
component instead ofMenuItem
. TheMenuItemLink
updates the HTML structure a bit to be accessible and still ensure the link is focusable.
In addition, the MenuButton
will render with a dropdown icon by default if the
buttonType !== "icon"
, but this can be disabled or changed with the
disableDropdownIcon
and dropdownIcon
props.
You can add event handlers to items in multiple ways:
onClick
to each item
Updating each item to be an object with an onClick
attribute might be the
easiest way to add an event handler. An example would be:
12345678910111213141516171819-const items = ["Item 1", "Item 2", "Item 3"];
+const items = [
+ {
+ children: "Item 1",
+ onClick: () => console.log("Clicked Item 1"),
+ },
+ {
+ children: "Item 2",
+ onClick: () => console.log("Clicked Item 2"),
+ },
+ {
+ children: "Item 3",
+ onClick: () => console.log("Clicked Item 3"),
+ },
+ {
+ children: "Item 4",
+ onClick: () => console.log("Clicked Item 4"),
+ },
+]
MenuItem
The most "reusable" option will be to create custom MenuItem
and/or
MenuItemLink
components that have their own click handlers attached. This will
also make it so you can have reusable MenuItem
s if they need to appear in
multiple menus or need to make them have additional functionality (like
connecting to redux actions). You'll just want to import the MenuItem
component and add it to the items
list like normal. The default item renderer
will automatically clone each item with a unique key as well, so you won't need
to manually define keys yourself.
onClick
Last clicked value: None
MenuItem
Last clicked value: None
After reading over a couple of the other examples, you might be wondering why all this work is going into positioning menus since this seems a bit overkill. If you've made a pop-out menu before, you'll know that you can pretty easily position two elements together with:
123456789101112.container {
display: inline-block;
position: relative;
}
.child {
position: absolute;
// or whatever positioning options you'd like
right: 0;
top: 0;
}
1234<div class="container">
<button type="button">Button</button>
<div class="child" role="menu">Content</div>
</div>
This works for simple menus but has a few drawbacks:
overflow
set for a parent element in the DOM, the menu will
only be visible within that overflow container.To work around these issues, the Menu
component is rendered using
position: fixed
and all that additional positioning logic goes on behind the
scenes to handle everything for you to ensure that your menu is visible within
the viewport. This is also great just in-case you want to be able to portal
(@react-md/portal) the menu as well.
This example below will use the position: relative
approach to show these
problems as well as the DropdownMenu
implementation that fixes them.
Menus within react-md
are fully accessible for screen readers and keyboard
users. The menu can be opened in a few different ways:
If the menu was opened with an up arrow key, the last item in the menu will be focused initially while all the other event types will focus the first item. While the menu is open, the user can navigate through the items by:
The menu will also automatically have an aria-labelledby="BUTTON_ID"
by
default since the MenuButton
normally describes the menu. If this is not the
case, you can provide your own menuLabel
or menuLabelledBy
props to
correctly label your menu.
Just a reminder, if you render an icon
MenuButton
, you'll need to correctly provide an accessible label viaaria-label
,aria-labelledby
, or add a screen reader visible only text within the button.
It is possible to also use a custom renderer if you need more control over the
menu or menu item generation using the menuRenderer
and/or itemRenderer
props. The menuRenderer
will provide the base Menu
props as the first
argument as well as the items
list as the second argument. The itemRenderer
will provide the current item and the key
to use for the item.
A great example for this would be using a library like react-virtualized to render a giant list of items. When virtualizing the menu's list you will gain a giant performance boost in the mounting and unmounting of the menu, but the built in keyboard accessibility will be broken.
The keyboard movement and focus behavior only works for items that are currently rendered in the DOM. Since it is now a virtual list, you will be unable to:
This example below will show a non-virtualized and virtualized example to show the performance differences between the two and some drawbacks/limitations to the custom renderer.
Last clicked value: None
Last clicked value: None