LeafyGreen UI Kit Drawer
npm install @leafygreen-ui/drawer``shell`
pnpm add @leafygreen-ui/drawer
`shell`
yarn add @leafygreen-ui/drawer
`shell`
npm install @leafygreen-ui/drawer
`tsx
import React, { useState } from 'react';
import Button from '@leafygreen-ui/button';
import {
DrawerLayout,
DrawerLayoutProps,
useDrawerToolbarContext,
DisplayMode,
Size,
} from '@leafygreen-ui/drawer';
// Note: DrawerContent is a placeholder for your drawer content component
// This could be any React component to render inside the drawer
const DrawerContent = () =>
const DRAWER_TOOLBAR_DATA: DrawerLayoutProps['toolbarData'] = [
{
id: 'Code',
label: 'Code',
content:
title: 'Code Title',
glyph: 'Code',
onClick: () => {
console.log('Code clicked');
},
},
{
id: 'Dashboard',
label: 'Dashboard',
content:
title: 'Dashboard Title',
glyph: 'Dashboard',
onClick: () => {
console.log('Dashboard clicked');
},
},
{
id: 'Plus',
label: "Perform some action, doesn't open a drawer",
glyph: 'Plus',
onClick: () => {
console.log('Plus clicked, does not update drawer');
},
},
{
id: 'Settings',
label: 'Settings',
content:
title: 'Settings Title',
glyph: 'Settings',
visible: false, // This item will be hidden
},
];
const App = () => {
const Main = () => {
const { openDrawer, closeDrawer } = useDrawerToolbarContext();
return (
Your main application content goes here
);
};
return (
$3
`tsx
import React, { useState } from 'react';import Button from '@leafygreen-ui/button';
import { DisplayMode, Drawer, DrawerLayout } from '@leafygreen-ui/drawer';
function App() {
const [open, setOpen] = useState(false);
return (
displayMode={DisplayMode.Embedded}
isDrawerOpen={open}
drawer={Drawer content }
onClose={() => setOpen(false)}
>
);
}
`DrawerLayout
DrawerLayout is a flexible layout wrapper that shifts the page content appropriately when a Drawer opens. It can be used in both overlay and embedded modes, with or without a toolbar.$3
To use with a
Toolbar, pass the toolbarData prop to render the Toolbar items and the Drawer content. Each object in the array defines a Toolbar item. If a Toolbar item is intended to perform an action other than opening a Drawer (for example, opening a modal), leave the content and title fields empty.The
Drawer and Toolbar component will be rendered automatically based on the toolbarData provided.#### Toolbar Visibility
Individual toolbar items can be controlled using the
visible prop. When all toolbar items have visible set to false, the entire toolbar will be automatically hidden from view. This allows for dynamic toolbar management based on application state or user permissions.$3
To control the
Drawer state, use the useDrawerToolbarContext hook from within . This hook provides the openDrawer() and closeDrawer() functions to open and close the drawer programmatically. The hook takes no arguments and returns the following functions:| Name | Signature | Description |
| ------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| openDrawer |
(id: string) => void | Opens the Drawer associated with the Toolbar item that has the matching id in toolbarData. |
| closeDrawer | () => void | Closes the Drawer. |
| toggleDrawer | (id: string) => void | If clicking the currently active Toolbar item, it closes the Drawer. Otherwise, it open the Drawer with the new content. |$3
`tsx
import React, { useState } from 'react';import Button from '@leafygreen-ui/button';
import {
DrawerLayout,
DrawerLayoutProps,
useDrawerToolbarContext,
} from '@leafygreen-ui/drawer';
// Data passed to to render the toolbar items and drawer content
const DRAWER_TOOLBAR_DATA: DrawerLayoutProps['toolbarData'] = [
{
id: 'Code',
label: 'Code',
content: ,
title: 'Code Title',
glyph: 'Code',
onClick: () => {
console.log('Code clicked');
},
},
{
id: 'Dashboard',
label: 'Dashboard',
content: ,
title: 'Dashboard Title',
glyph: 'Dashboard',
onClick: () => {
console.log('Dashboard clicked');
},
},
{
id: 'Plus',
label: "Perform some action, doesn't open a drawer",
glyph: 'Plus',
onClick: () => {
console.log('Plus clicked, does not update drawer');
},
},
];
const Component = () => {
const Main = () => {
const { openDrawer, closeDrawer } = useDrawerToolbarContext();
// Content is a placeholder for your application's main content
const Content = () =>
Main content of your application; return (
);
};
return (
toolbarData={DRAWER_TOOLBAR_DATA}
displayMode="embedded"
onClose={() => {}}
>
);
};
`$3
#### Single
Drawer instanceThere are two ways to render a
Drawer without a Toolbar:1. Pass the
Drawer component to the drawer prop of . You don't need to pass open, displayMode, or onClose props to the Drawer component, as these will be handled by the DrawerLayout. (Recommended)`tsx
import React, { useState } from 'react';import Button from '@leafygreen-ui/button';
import { DisplayMode, Drawer, DrawerLayout } from '@leafygreen-ui/drawer';
function ExampleComponent() {
const [open, setOpen] = useState(false);
return (
displayMode={DisplayMode.Embedded}
isDrawerOpen={open}
drawer={Drawer content }
onClose={() => setOpen(false)}
>
2. Pass the
Drawer component as a child of `tsx
import React, { useState } from 'react';import Button from '@leafygreen-ui/button';
import { DisplayMode, Drawer, DrawerLayout } from '@leafygreen-ui/drawer';
function App() {
const [open, setOpen] = useState(false);
return (
displayMode={DisplayMode.Overlay}
onClose={() => setOpen(false)}
open={open}
title="Drawer Title"
>
Drawer content goes here
);
}
`#### Multiple overlay
Drawer instancesWhen rendering multiple overlay
Drawer instances, it is recommended to wrap the Drawer's in a . The DrawerStackProvider will manage the stacking of multiple Drawer instances by providing the correct z-index.`tsx
import React, { useState } from 'react';import Button from '@leafygreen-ui/button';
import { DisplayMode, Drawer, DrawerLayout, DrawerStackProvider } from '@leafygreen-ui/drawer';
const MultipleDrawersComponent = () => {
const [openA, setOpenA] = useState(false);
const [openB, setOpenB] = useState(false);
const [openC, setOpenC] = useState(false);
return (
displayMode={DisplayMode.Overlay}
isDrawerOpen={openA || openB || openC}
className={css
}
>
className={css}
>
$3
DrawerLayout supports rendering resizable embedded Drawer instances with or without a toolbar. To make an embedded Drawer resizable, pass the resizable prop to the DrawerLayout component. This allows users to adjust the width of the drawer by dragging.`tsx
import React, { useState } from 'react';
import { DrawerLayout, Drawer, DisplayMode } from '@leafygreen-ui/drawer';
import Button from '@leafygreen-ui/button';function ResizableDrawerExample() {
const [open, setOpen] = useState(false);
return (
displayMode={DisplayMode.Embedded}
isDrawerOpen={open}
resizable
drawer={
Drawer content that can be resized by dragging the left edge
}
onClose={() => setOpen(false)}
>
);
}
`You can also use the resizable feature with a toolbar-based drawer:
`tsx
toolbarData={DRAWER_TOOLBAR_DATA}
displayMode={DisplayMode.Embedded}
onClose={() => {}}
resizable
>
{content}
`Props
$3
| Prop | Type | Description | Default |
| --------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
|
children | React.ReactNode | The content that should live to the left of the Drawer. If there is a Toolbar, the content will shift to accommodate the Toolbar. In addition, if the DisplayMode is embedded, the content will also be shifted to accommodate the opening of the Drawer. | |
| displayMode _(optional)_ | 'embedded' \| 'overlay' | Options to control how the drawer element is displayed
\ 'embedded' will display a drawer as a element that takes up the full parent container height and on the same elevation as container page content.
\ 'overlay' will display a drawer as a Test Harnesses
getTestUtils()
getTestUtils() is a util that allows consumers to reliably interact with LG Drawer in a product test suite. If the Drawer component cannot be found, an error will be thrown.$3
`tsx
import { Drawer } from '@leafygreen-ui/drawer';
import { getTestUtils } from '@leafygreen-ui/drawer/testing';const utils = getTestUtils(lgId?: string); // lgId refers to the custom
data-lgid attribute passed to Drawer. It defaults to 'lg-drawer' if left empty.
`#### Single
Drawer`tsx
import { render } from '@testing-library/react';
import { Drawer, DrawerStackProvider } from '@leafygreen-ui/drawer';
import { getTestUtils } from '@leafygreen-ui/drawer/testing';...
test('drawer', () => {
render(
displayMode={DisplayMode.Overlay}
onClose={() => setOpen(false)}
open={open}
title="Drawer Title"
>
content
);
const { getCloseButtonUtils, getDrawer, isOpen } = getTestUtils();
expect(getDrawer()).toBeInTheDocument();
expect(getCloseButtonUtils().getButton()).toBeInTheDocument();
expect(isOpen()).toBeTruthy();
});
`#### Multiple
Drawer instancesWhen testing multiple
Drawer instances it is recommended to add the custom data-lgid attribute to each Drawer.`tsx
import { render } from '@testing-library/react';
import { Drawer, DrawerStackProvider } from '@leafygreen-ui/drawer';
import { getTestUtils } from '@leafygreen-ui/drawer/testing';...
test('Drawer', () => {
render(
Drawer A content goes here.
Drawer B content goes here.
,
);
const utilsA = getTestUtils('lg-drawer-A');
const utilsB = getTestUtils('lg-drawer-B');
// Drawer A
expect(utilsA.getDrawer()).toBeInTheDocument();
expect(utilsA.isOpen()).toBeFalsy();
// Drawer B
expect(utilsB.getDrawer()).toBeInTheDocument();
expect(utilsB.isOpen()).toBeTruthy();
});
`#### Drawer with Toolbar
`tsx
import { render } from '@testing-library/react';
import { DrawerLayout, DrawerLayoutProps } from '@leafygreen-ui/drawer';
import { getTestUtils } from '@leafygreen-ui/drawer/testing';...
const DRAWER_TOOLBAR_DATA: DrawerLayoutProps['toolbarData'] = [
{
id: 'Code',
label: 'Code',
content: ,
title: 'Code Title',
glyph: 'Code',
onClick: () => {
console.log('Code clicked');
},
},
{
id: 'Dashboard',
label: 'Dashboard',
content: ,
title: 'Dashboard Title',
glyph: 'Dashboard',
onClick: () => {
console.log('Dashboard clicked');
},
},
{
id: 'Plus',
label: "Perform some action, doesn't open a drawer",
glyph: 'Plus',
onClick: () => {
console.log('Plus clicked, does not update drawer');
},
},
];
test('Drawer', () => {
render(
,
);
const { getToolbarTestUtils } = getTestUtils();
const { getDrawer, getToolbarIconButtonByLabel } = getToolbarTestUtils();
const codeButton = getToolbarIconButtonByLabel('Code')?.getElement();
const dashboardButton =
getToolbarIconButtonByLabel('Code')?.getElement();
await userEvent.click(codeButton);
expect(getDrawer().textContent).toContain('Code Title');
});
`$3
`tsx
const {
findDrawer,
getCloseButtonUtils,
getDrawer,
isOpen,
queryDrawer,
getToolbarTestUtils,
} = getTestUtils();
`| Util | Description | Returns |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
findDrawer | Returns a promise that resolves to the drawer element. The promise is rejected if no elements match or if more than one match is found. | Promise \| Promise |
| getCloseButtonUtils | Returns the button test utils for the close button | Button test utils return type |
| getDrawer | Returns the drawer element and throws if no elements match or if more than one match is found. | HTMLDivElement |
| isOpen | Checks the aria-hidden attribute and that the drawer element is visible based on CSS properties for display, opacity, transform, and visibility | boolean |
| queryDrawer | Returns the drawer element or null if no elements match and throws if more than one match is found. | HTMLDivElement |
| getToolbarTestUtils | Returns the Toolbar test utils for the Toolbar | Toolbar test utils return type |$3
The
Drawer component uses the native HTMLDialogElement API for better accessibility and browser-native behavior. However, JSDOM (used by Jest and other test runners) does not fully support this API. You'll need to mock the show and close methods in your test setup:`tsx
beforeAll(() => {
HTMLDialogElement.prototype.show = jest.fn(function mock(
this: HTMLDialogElement,
) {
this.open = true;
}); HTMLDialogElement.prototype.close = jest.fn(function mock(
this: HTMLDialogElement,
) {
this.open = false;
});
});
`This mock can be placed in a
beforeAll` block in your test file, or in a global test setup file.