UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

571 lines (521 loc) 17.9 kB
--- title: 'Drawer' description: 'The Drawer component is a Modal variation that appears as a side panel at any chosen side of the page.' version: 10.104.0 generatedAt: 2026-04-17T18:46:09.755Z checksum: 8953eeeb2cb867c589fb63b790f1712d76c5101e998194e31f21f9ce479c2da0 --- # Drawer ## Import ```tsx import { Drawer } from '@dnb/eufemia' ``` ## Description The Drawer component is a [Modal](/uilib/components/modal) variation that appears as a side panel at any chosen side of the page: top, bottom, left, or right (default `right`). A Drawer is typically used to show additional information. It can also be used for easy/quick tasks while staying in context. ## Relevant links - [Figma](https://www.figma.com/design/cdtwQD8IJ7pTeE45U148r1/%F0%9F%92%BB-Eufemia---Web?node-id=12331-4011) - [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/drawer) - [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/components/drawer) ### Parts in Drawer To provide custom content to parts of the Drawer, a set of component parts are provided: - `<Drawer.Navigation>`: The navigation field at the top of the component, default with a close button (Equal to the property `navContent`). - `<Drawer.Header>`: The header field of the component, where the `title` will appear (Equal to the property `headerContent`). - `<Drawer.Body>`: The body of the Drawer, provided with a section background color, default `black-3` (Equal to the property `modalContent`). ### More detailed information For more details regarding the component functionality, check out the [Modal documentation](/uilib/components/modal). ### Root Element (React Portal) The Drawer component uses [PortalRoot](/uilib/components/portal-root) internally to render its content. See the [PortalRoot documentation](/uilib/components/portal-root) for information on how to control where the portal content appears in the DOM. ## Demos ### Basic Drawer ```tsx render( <Drawer title="Drawer title" triggerAttributes={{ text: 'Open drawer', }} > <P top>Some informational content</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet mus aptent luctus imperdiet netus natoque cubilia mattis nostra proin ornare scelerisque sodales faucibus placerat sem bibendum pretium rutrum vitae sociis ligula inceptos morbi quam mi sed pharetra fermentum tortor ullamcorper ipsum tellus eros euismod volutpat nisl dui lectus fames suscipit phasellus praesent justo mollis montes velit taciti gravida </P> </Drawer> ) ``` ### Basic Drawer left placement ```tsx render( <Drawer title="Drawer title" containerPlacement="left"> <P top>Some informational content</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet </P> </Drawer> ) ``` ### Basic Drawer in fullscreen ```tsx render( <Drawer fullscreen title="Drawer title" triggerAttributes={{ text: 'Open drawer', }} > <P top>Some informational content</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet </P> </Drawer> ) ``` ### Drawer with custom content ```tsx render( <Drawer title="Custom title"> <Drawer.Navigation> <Breadcrumb onClick={handleBack} /> </Drawer.Navigation> <Drawer.Header> <P bottom>This is a lorem ipsum dolor</P> <Button bottom size="large"> Lorem ipsum </Button> <Button bottom size="large" variant="secondary"> Dolor sit </Button> <FormStatus state="info">This is a lorem ipsum dolor</FormStatus> <Tabs id="unique-linked-id" data={[ { title: 'One', key: 'one', }, { title: 'Two', key: 'two', }, ]} /> </Drawer.Header> <Drawer.Body> <Tabs.Content id="unique-linked-id" key="contentKey"> {({ title }) => { return ( <> <H2>{title}</H2> <P top>This is a left aligned Drawer content.</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet mus aptent luctus imperdiet netus natoque cubilia mattis nostra proin ornare scelerisque sodales faucibus placerat sem bibendum pretium rutrum vitae sociis ligula inceptos morbi quam mi sed pharetra fermentum tortor ullamcorper ipsum tellus eros euismod volutpat nisl dui lectus fames suscipit phasellus praesent justo mollis montes velit taciti gravida lacus commodo senectus feugiat lorem etiam consequat penatibus cum hendrerit accumsan orci potenti purus nulla </P> </> ) }} </Tabs.Content> </Drawer.Body> </Drawer> ) ``` ### Customize trigger button ```tsx render( <Drawer title="Drawer with custom trigger" triggerAttributes={{ text: 'Custom trigger', variant: 'primary', size: 'large', icon: 'loupe', icon_position: 'left', }} > <Drawer.Body spacing> <P>Opened a Drawer with a custom trigger button!</P> </Drawer.Body> </Drawer> ) ``` ### Close Drawer by callback method ```tsx render( <Drawer title="Drawer title" triggerAttributes={{ text: 'Open drawer', }} hideCloseButton > {({ close }) => ( <> <Button text="Close by callback" on_click={close} /> </> )} </Drawer> ) ``` ### Remove animation and spacing ```tsx render( <Drawer title="No spacing or animation" noAnimation spacing={false} hideCloseButton > <Drawer.Body> <P top bottom> This is a lorem ipsum dolor </P> <Button bottom size="large"> Lorem ipsum </Button> <Button bottom size="large" variant="secondary"> Dolor sit </Button> <FormStatus state="info">This is a lorem ipsum dolor</FormStatus> </Drawer.Body> </Drawer> ) ``` ```tsx const DrawerMock = () => { const scrollRef = React.useRef(null) const innerRef = React.useRef(null) const [errorMessage, setErrorMessage] = React.useState(null) const message = errorMessage ? errorMessage : 'Yes, the "dnb-scroll-view" is used!' return ( <Drawer contentRef={innerRef} scrollRef={scrollRef} onOpen={() => { const innerOverflowY = window.getComputedStyle( innerRef.current ).overflowY const contentElem = scrollRef.current.querySelector( '.dnb-drawer__content' ) const contentOverflowY = window.getComputedStyle(contentElem)?.overflowY const scrollOverflowY = window.getComputedStyle( scrollRef.current ).overflowY if (contentOverflowY !== 'visible') { setErrorMessage( `.dnb-drawer__content was "${contentOverflowY}" and not "visible"` ) } else if (innerOverflowY !== 'visible') { setErrorMessage( `.dnb-drawer__inner was "${innerOverflowY}" and not "visible"` ) } else if (scrollOverflowY !== 'auto') { setErrorMessage('.dnb-scroll-view was not "auto"') } }} > <Drawer.Body> <div style={{ height: '100rem', }} > <div className="drawer-scroll-view"> <P size="x-large">{message}</P> </div> </div> </Drawer.Body> </Drawer> ) } render(<DrawerMock />) ``` ```tsx render( <Drawer title="Drawer title" containerPlacement="top"> <P top>Some informational content</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet </P> </Drawer> ) ``` ```tsx render( <Drawer title="Drawer title" containerPlacement="bottom"> <P top>Some informational content</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet </P> </Drawer> ) ``` ```tsx const MockComponent = () => { const scrollRef = React.useRef(null) return ( <Drawer title="Drawer title" scrollRef={scrollRef} onOpen={() => { scrollRef.current.scrollTop = 50 }} > <P top>Some informational content</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet mus aptent luctus imperdiet netus natoque cubilia mattis nostra proin ornare scelerisque sodales faucibus placerat sem bibendum pretium rutrum vitae sociis ligula inceptos morbi quam mi sed pharetra fermentum tortor ullamcorper ipsum tellus eros euismod volutpat nisl dui lectus fames suscipit phasellus praesent justo mollis montes velit taciti gravida </P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet mus aptent luctus imperdiet netus natoque cubilia mattis nostra proin ornare scelerisque sodales faucibus placerat sem bibendum pretium rutrum vitae sociis ligula inceptos morbi quam mi sed pharetra fermentum tortor ullamcorper ipsum tellus eros euismod volutpat nisl dui lectus fames suscipit phasellus praesent justo mollis montes velit taciti gravida </P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis varius pellentesque nunc egestas risus amet mus aptent luctus imperdiet netus natoque cubilia mattis nostra proin ornare scelerisque sodales faucibus placerat sem bibendum pretium rutrum vitae sociis ligula inceptos morbi quam mi sed pharetra fermentum tortor ullamcorper ipsum tellus eros euismod volutpat nisl dui lectus fames suscipit phasellus praesent justo mollis montes velit taciti gravida </P> </Drawer> ) } render(<MockComponent />) ``` ```tsx const MockComponent = () => { const [showMe, setShowMe] = React.useState(false) const scrollRef = React.useRef(null) return ( <Drawer title="Drawer title" scrollRef={scrollRef} onOpen={() => { setShowMe(!showMe) scrollRef.current.scrollTop = 50 }} > <Drawer.Navigation> {showMe ? ( <> <P bottom>This is a lorem ipsum dolor</P> <P bottom>This is a lorem ipsum dolor</P> <P bottom>This is a lorem ipsum dolor</P> <P bottom>This is a lorem ipsum dolor</P> </> ) : null} </Drawer.Navigation> <Drawer.Header> <P bottom>This is a lorem ipsum dolor</P> <Button bottom size="large"> Lorem ipsum </Button> <Button bottom size="large" variant="secondary"> Dolor sit </Button> <FormStatus state="info">This is a lorem ipsum dolor</FormStatus> <Tabs id="unique-linked-id-x" data={[ { title: 'One', key: 'one', }, { title: 'Two', key: 'two', }, ]} /> </Drawer.Header> <Drawer.Body> <Tabs.Content id="unique-linked-id-x" key="contentKey0"> {({ title }) => { return ( <> <H2>{title}</H2> <P top>This is a left aligned Drawer content.</P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis </P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis </P> <P top> Elementum eu suspendisse sit platea elit porttitor magna laoreet ad ultrices tempus urna curae parturient conubia quisque viverra eget vestibulum neque pulvinar semper vulputate id dis </P> </> ) }} </Tabs.Content> </Drawer.Body> </Drawer> ) } render(<MockComponent />) ``` ## Properties ```json { "props": { "containerPlacement": { "doc": "Defines on what side the Drawer should be opened. Can be set to `left`, `right`, `top` and `bottom`. Defaults to `right`.", "type": ["left", "right", "top", "bottom"], "status": "optional" }, "title": { "doc": "The drawer title. Displays on the very top of the content.", "type": "React.ReactNode", "status": "optional" }, "minWidth": { "doc": "The minimum Drawer content width, defined by a CSS width value like `50vw` (50% of the viewport). Be careful on using fixed `minWidth` so you don't break responsiveness. Defaults to `30rem` (average width is set to `60vw`).", "type": ["string", "number"], "status": "optional" }, "maxWidth": { "doc": "The maximum Drawer content width, defined by a CSS width value like `20rem`. Defaults to `60rem` (average width is set to `60vw`).", "type": ["string", "number"], "status": "optional" }, "className": { "doc": "Give the Drawer content a class name (maps to `dnb-drawer`).", "type": "string", "status": "optional" }, "spacing": { "doc": "If set to `false` then the drawer content will be shown without any spacing. Defaults to `true`.", "type": "boolean", "status": "optional" }, "preventCoreStyle": { "doc": "By default the drawer content gets added the core style class `dnb-core-style`. Use `false` to disable this behavior.", "type": "boolean", "status": "optional" }, "navContent": { "doc": "The content which will appear in the navigation, above the header, and side-by-side the close button.", "type": "React.ReactNode", "status": "optional" }, "headerContent": { "doc": "The content which will appear in the header of the drawer.", "type": "React.ReactNode", "status": "optional" }, "modalContent": { "doc": "The content which will appear when triggering the drawer.", "type": "React.ReactNode", "status": "optional" }, "alignContent": { "doc": "Define the inner horizontal alignment of the content. Can be set to `left`, `center`, `right` and `centered`. If `centered`, then the content will also be centered vertically. Defaults to `left`.", "type": ["left", "right", "centered", "center"], "status": "optional" }, "fullscreen": { "doc": "If set to `true` then the drawer content will be shown as fullscreen, without showing the original content behind. Can be set to `false` to omit the auto fullscreen. Defaults to `auto`.", "type": ["boolean", "string"], "status": "optional" } } } ``` ### More properties The properties of [Modal](/uilib/components/modal) formatted as camel case are also provided. See the table below: <PropertiesTable props={ModalProperties} /> ## Translations ```json { "locales": ["da-DK", "en-GB", "nb-NO", "sv-SE"], "entries": { "Modal.close_title": { "nb-NO": "Lukk", "en-GB": "Close", "sv-SE": "Stäng", "da-DK": "Luk" }, "Modal.dialog_title": { "nb-NO": "Separat Vindu", "en-GB": "Dialog Window", "sv-SE": "Separat fönster", "da-DK": "Separat vindue" } } } ``` ### Drawer sizes The Drawer is responsive with the following properties: ```css --drawer-width: 40vw; --drawer-min-width: 384px; --drawer-max-width: 40rem; ``` ## Events Drawer includes the same events as [Modal](/uilib/components/modal), only formatted as camel case. <PropertiesTable props={ModalEvents} />