If you provide custom icons for react-md
using the IconProvider
or
Configuration
components, you must rename the download
icon to be upload
.
const icons: ConfiguredIcons = {
back: <KeyboardArrowLeftSVGIcon />,
checkbox: <CheckBoxSVGIcon />,
- download: <FileUploadSVGIcon />,
dropdown: <ArrowDropDownSVGIcon />,
error: <ErrorOutlineSVGIcon />,
expander: <KeyboardArrowDownSVGIcon />,
forward: <KeyboardArrowRightSVGIcon />,
menu: <MenuSVGIcon />,
notification: <NotificationsSVGIcon />,
password: <RemoveRedEyeSVGIcon />,
radio: <RadioButtonCheckedSVGIcon />,
selected: <CheckSVGIcon />,
sort: <ArrowUpwardSVGIcon />,
+ upload: <FileUploadSVGIcon />,
};
import type { ReactElement } from "react";
-import { DropdownMenu } from "@react-md/menu";
+import { DropdownMenu, MenuItem } from "@react-md/menu";
export default function Example(): ReactElement (
- <DropdownMenu
- id="example-dropdown-menu"
- items={[
- { onClick: () => console.log("Clicked Item 1"), children: "Item 1" },
- { onClick: () => console.log("Clicked Item 2"), children: "Item 2" },
- { onClick: () => console.log("Clicked Item 3"), children: "Item 3" },
- ]}
- >
- Dropdown
+ <DropdownMenu id="example-dropdown-menu" buttonChildren="Dropdown">
+ <MenuItem onClick={() => console.log("Clicked Item 1")}>Item 1</MenuItem>
+ <MenuItem onClick={() => console.log("Clicked Item 2")}>Item 2</MenuItem>
+ <MenuItem onClick={() => console.log("Clicked Item 3")}>Item 3</MenuItem>
</DropdownMenu>
);
The DropdownMenuItem
no longer exists since the nested dropdown menu behavior
has been integrated into the DropdownMenu
component:
import type { ReactElement } from "react";
-import { DropdownMenu, DropdownMenuItem } from "@react-md/menu";
+import { DropdownMenu, MenuItem } from "@react-md/menu";
export default function Example(): ReactElement (
- <DropdownMenu
- id="example-dropdown-menu"
- items={[
- "Item 1",
- "Item 2",
- "Item 3",
- <DropdownMenuItem
- id="nested-dropdown-menu"
- items={["Subitem 1", "Subitem 2", "Subitem 3"]}
- >
- Submenu
- </DropdownMenuItem>,
- ]}
- >
- Dropdown
+ <DropdownMenu id="example-dropdown-menu" buttonChildren="Dropdown">
+ <MenuItem>Item 1</MenuItem>
+ <MenuItem>Item 2</MenuItem>
+ <MenuItem>Item 3</MenuItem>
+ <DropdownMenu
+ id="nested-dropdown-menu"
+ buttonChildren="Submenu"
+ >
+ <MenuItem>Subitem 1</MenuItem>
+ <MenuItem>Subitem 2</MenuItem>
+ <MenuItem>Subitem 3</MenuItem>
+ </DropdownMenu>
</DropdownMenu>
);
const id = "table-row-id";
-const [menuProps, onContextMenu] = useContextMenu();
+const { menuProps, onContextMenu } = useContextMenu();
return (
<>
<TableRow id={id} tabIndex={0} onContextMenu={onContextMenu}>
<TableCell>Cell 1</TableCell>
<TableCell>Cell 2</TableCell>
<TableCell>Cell 3</TableCell>
</TableRow>
- <Menu aria-label="Some menu label" controlId={id} {...menuProps} portal>
- <List>
<Menu {...menuProps}>
<MenuItem>Item 1</MenuItem>
<MenuItem>Item 2</MenuItem>
<MenuItem>Item 3</MenuItem>
- </List>
</Menu>
</>
):
If you have multiple groups of MenuItemRadio
, MenuItemCheckbox
, or
MenuItemSwitch
in your menu, the MenuItemRadio
should be wrapped in this new
component for increased accessibility. The MenuItemSeparator
component should
also be used to help separate different groups.
import { ReactElement, useState } from "react";
-import { DropdownMenu } from "@react-md/menu";
+import { DropdownMenu, MenuItemGroup, MenuItemSeparator } from "@react-md/menu";
import { MenuItemRadio, MenuItemSwitch } from "@react-md/form";
function Example(): ReactElement {
const [value, setValue] = useState("value1");
const [checked, setChecked] = useState(false);
return (
<DropdownMenu id="dropdown-menu-id" buttonChildren="Button">
<MenuItemSwitch
id="switch-id"
checked={checked}
onCheckedChange={nextChecked => setChecked(nextChecked)}
>
Light mode
</MenuItemSwitch>
- <div role="group" aria-label="My Group Label">
+ <MenuItemSeparator />
+ <MenuItemGroup aria-label="My Group Label">
<MenuItemRadio
id="radio-1"
checked={value === "value1"}
onCheckedChange={() => setValue("value1")}
>
Radio 1
</MenuItemRadio>
<MenuItemRadio
id="radio-2"
checked={value === "value2"}
onCheckedChange={() => setValue("value2")}
>
Radio 2
</MenuItemRadio>
<MenuItemRadio
id="radio-3"
checked={value === "value3"}
onCheckedChange={() => setValue("value3")}
>
Radio 3
</MenuItemRadio>
- </div>
+ </MenuItemGroup>
</DropdownMenu>
);
}
Since the DropdownMenu
can now render as a Sheet
when the AppSize
is
phone
, your tests might throwing an error about a missing AppSizeListener
.
If you are using @testing-library/react
, you can just follow the example in my
template-rmd. Check out the
src/test-utils.tsx
and
src/components/__tests__/LinkUnstyled.tsx
for example usage. This is basically the custom render documentation from the
React Testing Library documentation.
If you aren't, just wrap all your tests that use a DropdownMenu
in either the
Configuration
or AppSizeListener
component.
useHoverMode
for the new API import { BELOW_CENTER_ANCHOR, useHoverMode } from "@react-md/utils";
export default function StickyHoverMode(): ReactElement {
- const { stuck, active, handlers, stickyHandlers, visible, setVisible } =
- useHoverMode({
- sticky: true,
- });
+ const { stuck, active, handlers, hoverHandlers, visible, setVisible } =
+ useHoverMode();
const buttonRef = useRef<HTMLButtonElement>(null);
return (
<>
- <Button {...stickyHandlers} ref={buttonRef}>
+ <Button {...handlers} ref={buttonRef}>
Button
</Button>
<FixedDialog
- {...handlers}
+ {...hoverHandlers}
aria-label="Additional Information"
id="some-dialog-id"
visible={visible}
If you want to start rendering menus as sheets on phones throughout your app,
update the main Configuration
component:
import type { ReactElement, ReactNode } from "react";
import { Link, useLocation } from "react-router-dom";
import {
ArrowDropDownSVGIcon,
ArrowUpwardSVGIcon,
CheckBoxSVGIcon,
CheckSVGIcon,
ConfiguredIcons,
Configuration,
ErrorOutlineSVGIcon,
FileUploadSVGIcon,
KeyboardArrowDownSVGIcon,
KeyboardArrowLeftSVGIcon,
KeyboardArrowRightSVGIcon,
Layout as RMDLayout,
+ MenuConfiguration,
MenuSVGIcon,
NotificationsSVGIcon,
RadioButtonCheckedSVGIcon,
RemoveRedEyeSVGIcon,
useLayoutNavigation,
} from "react-md";
import navItems from "./navItems";
const icons: ConfiguredIcons = {
back: <KeyboardArrowLeftSVGIcon />,
checkbox: <CheckBoxSVGIcon />,
dropdown: <ArrowDropDownSVGIcon />,
error: <ErrorOutlineSVGIcon />,
expander: <KeyboardArrowDownSVGIcon />,
forward: <KeyboardArrowRightSVGIcon />,
menu: <MenuSVGIcon />,
notification: <NotificationsSVGIcon />,
password: <RemoveRedEyeSVGIcon />,
radio: <RadioButtonCheckedSVGIcon />,
selected: <CheckSVGIcon />,
sort: <ArrowUpwardSVGIcon />,
upload: <FileUploadSVGIcon />,
};
+const menuConfiguration: MenuConfiguration = {
+ renderAsSheet: "phone",
+};
interface LayoutProps {
children: ReactNode;
}
export default function Layout({ children }: LayoutProps): ReactElement {
const { pathname } = useLocation();
return (
- <Configuration icons={icons}>
+ <Configuration icons={icons} menuConfiguration={menuConfiguration}>
<RMDLayout
tabletLayout="temporary"
landscapeTabletLayout="temporary"
desktopLayout="temporary"
largeDesktopLayout="temporary"
treeProps={useLayoutNavigation(navItems, pathname, Link)}
>
{children}
</RMDLayout>
</Configuration>
);
}