A drawer without a backdrop allows a user to continue to interact with the rest of the page. This can be useful when you wants users to have the ability to interact with the page and the drawer at the same time.

noPadding: true
---
const Example = () => {
    const [open, setOpen] = React.useState(true);
    const toggleOpen = () => setOpen(!open);

    return (
        <div style={{ height: '200px', overflow: 'hidden', position: 'relative' }}>
            <Button onClick={toggleOpen}>Toggle Drawer</Button>
            <Drawer
                header="Drawer Header"
                open={open}
                onClose={toggleOpen}
                portal={false}
            >
                You can always set up this campaign at a later time.
            </Drawer>
        </div>
    );
};
render (Example);

Over all Content

frame: true
---
const Example = () => {
    const [open, setOpen] = React.useState(false);
    const toggleOpen = () => setOpen(!open);

    return (
        <div>
            <Button onClick={toggleOpen}>Toggle Drawer</Button>
            <Drawer
                header="Drawer Header"
                open={open}
                onClose={toggleOpen}
            >
                You can always set up this campaign at a later time.
            </Drawer>
        </div>
    );
};
render (Example);

Closing a Drawer

Drawers without a backdrop are closed through an explicit control. This is typically through clicking a ×, a Drawer footer button, or a labeled button on the page. Clicking outside the drawer should not close it.


Drawer with a Backdrop

A backdrop restricts user interaction to just the Drawer. This is useful when a task is focused. Drawers with backdrops close similar to the Modal component.

const Example = () => {
    const [open, setOpen] = React.useState(false);
    const toggleOpen = () => setOpen(!open);

    return (
        <div>
            <Button onClick={toggleOpen}>Drawer with Backdrop</Button>
            <Drawer
                header="Drawer Header"
                open={open}
                onClose={toggleOpen}
                portal={false}
                backdrop
            >
                You can always set up this campaign at a later time.
            </Drawer>
        </div>
    );
};
render (Example);

Non-closable Drawers

When a workflow is mandatory, the close functionalities — clicking the backdrop or × — can be removed. A user would have to complete a task to proceed.

const Example = () => {
    const [open, setOpen] = React.useState(false);
    const toggleOpen = () => setOpen(!open);

    return (
        <div>
            <Button onClick={toggleOpen}>Non-closable Drawer</Button>
            <Drawer
                header="Drawer Header"
                open={open}
                portal={false}
                backdrop
                footer={<Button primary onClick={toggleOpen}>I Accept</Button>}
                footerAlign="right"
            >
                You can always set up this campaign at a later time.
            </Drawer>
        </div>
    );
};
render (Example);

When to use a backdrop

When a Drawer is being used as an informational display, or supplementary to a page task, a backdrop is usually not needed on a Drawer. When a Drawer is an essential part of a workflow, or involves a lot of data manipulation where data conflicts with the page can occur, a backdrop is preferred.


Drawer Sizes

Drawer sizes mimic the sizes of the Modal. Each size has a built in 90% width value and a maximum width value. If a design needs to occupy 100% of the screen, consider using a Takeover.

const Example = () => {
    const [open, setOpen] = React.useState(false);
    const [drawerWidth, setWidth] = React.useState('l');
    const toggleOpen = () => setOpen(!open);
    const widthValue = (width) => setWidth(width);
    const widths = ["xs", "s", "m", "l"];

    return (
        <div>
            <ButtonGroup>
                {widths.map( (width, index) => (
                    <Button key={index} onClick={() => { widthValue(width); toggleOpen(); }}><span className="tt-uppercase m-r-half">{width}</span> Drawer</Button>
                ))}
            </ButtonGroup>
            <Drawer
                header="Drawer Header"
                open={open}
                onClose={toggleOpen}
                portal={false}
                backdrop
                width={drawerWidth}
            >
                You can always set up this campaign at a later time.
            </Drawer>
        </div>
    );
};
render (Example);

Drawer Header

Drawer headers typically have a title and a close icon. A header can also be customized to use other Anvil components. Any action element added to the header (such as a Button) should not close the Drawer, and should use a secondary style.

Header Examples

noPadding: true
---
<div style={{ height: '150px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header="Drawer Header without Close"
        open={true}
        portal={false}
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '150px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header="Drawer Header with Close"
        open={true}
        portal={false}
        onClose={() => {}}
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '150px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header="Drawer Header with a really long name that stretches to two lines"
        open={true}
        portal={false}
        onClose={() => {}}
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '150px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={
            <div>
                <Headline>Icemaker Flex Line 1/4</Headline>
                <BodyText>Bob's Wacky Wonders – Glendale, CA 01</BodyText>
            </div>
        }
        open={true}
        portal={false}
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '150px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={
            <Stack className="w-100" alignItems="center" justifyContent="space-between">
                <Headline>Invoice #12345</Headline>
                <ButtonGroup>
                    <Button outline small>Print</Button>
                    <Button outline small>Download</Button>
                </ButtonGroup>
            </Stack>
        }
        open={true}
        portal={false}
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '150px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={
            <Stack className="w-100" alignItems="center" justifyContent="space-between">
                <Stack spacing={2} alignItems="center">
                    <Avatar name="Rose Tico" autoColor size="m" />
                    <BodyText>Rose Tico</BodyText>
                </Stack>
                <Tag color="warning">Needs Approval</Tag>
            </Stack>
        }
        open={true}
        portal={false}
        onClose={() => {}}
    >
        Drawer Content
    </Drawer>
</div>

Drawer Footer

Drawer footers generally provide flow controls such as cancel and submit, or secondary actions for the Drawer. These controls can also close the Drawer when applicable. While it is possible to have multi-step Drawers, it should be avoided. Footers are not required, which can be useful when a Drawer has no actions or when real estate is critical.

noPadding: true
---
<div style={{ height: '170px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={"Right Aligned Footer"}
        open={true}
        portal={false}
        footer={
            <ButtonGroup>
                <Button>Cancel</Button>
                <Button primary>Apply and Save</Button>
            </ButtonGroup>
        }
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '170px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={"Split Button Footer"}
        open={true}
        portal={false}
        footerAlign="space-between"
        footer={
            <React.Fragment>
                <Button>Cancel</Button>
                <Button primary>Apply and Save</Button>
            </React.Fragment>
        }
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '170px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={"Secondary Actions Footer"}
        open={true}
        portal={false}
        footer={
            <ButtonGroup>
                <Button fill='subtle'>View Full Job</Button>
                <Button>Close Job</Button>
            </ButtonGroup>
        }
    >
        Drawer Content
    </Drawer>
</div>
noPadding: true
---
<div style={{ height: '170px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header={"Multiple Actions"}
        open={true}
        portal={false}
        footer={
            <ButtonGroup>
                <Button primary outline>See Availability</Button>
                <Button primary>Update Invoice</Button>
            </ButtonGroup>
        }
    >
        Drawer Content
    </Drawer>
</div>

Drawer Body

The body section is where most of the Drawer content exists.

noPadding: true
---
<div style={{ height: '740px', overflow: 'hidden', position: 'relative' }} className="p-3">
    <Drawer
        header="Reschedule Job"
        open={true}
        onClose={() => {}}
        portal={false}
        footerAlign="space-between"
        footer={
            <React.Fragment>
                <Button>Cancel</Button>
                <ButtonGroup>
                    <Button outline primary>View Availability</Button>
                    <Button primary>Reschedule</Button>
                </ButtonGroup>
            </React.Fragment>
        }
        width="l"
    >
        <Headline size="small" className="m-b-3">Item #123456</Headline>
        <Form>
            <Form.Group widths="equal">
                <Form.Input label="First Name" placeholder="Leia" />
                <Form.Input label="Last Name" placeholder="Organa" />
            </Form.Group>
            <Form.Select
                label="Home Planet"
                placeholder="Choose Planet"
                options={[
                    {key:1, value: 1, text: 'Alderaan'},
                    {key:2, value: 2, text: 'Bespin'},
                    {key:3, value: 3, text: 'Coruscant'},
                    {key:4, value: 4, text: 'Dagobah'},
                    {key:5, value: 5, text: 'Hoth'},
                    {key:6, value: 6, text: 'Kashyyyk'},
                    {key:7, value: 7, text: 'Naboo',},
                    {key:8, value: 8, text: 'Tatooine'},
                    {key:9, value: 9, text: 'Yavin'},
                ]}
            />

            <BodyText size="small" bold className="m-b-2-i m-t-4-i">What is the reason for Rescheduling?</BodyText>
            <Form.Field>
                <Form.Togglebox
                    value="value1"
                    name="Toggle1"
                    id="Toggle1"
                    label={"Material"}
                />
                <Form.Togglebox
                    value="value2"
                    name="Toggle1"
                    id="Toggle2"
                    label={"Equipment"}
                />
                <Form.Togglebox
                    value="value3"
                    name="Toggle1"
                    id="Toggle3"
                    label={"Purchase Order"}
                />
                <Form.Togglebox
                    value="value4"
                    name="Toggle1"
                    id="Toggle4"
                    label={"Labor"}
                />
            </Form.Field>
        </Form>
    </Drawer>
</div>

Scrollable Content

Drawer header and footers are fixed to the top and bottom of the page. The drawer's body automatically scrolls when not enough space is available.

const Example = () => {
    const [open, setOpen] = React.useState(false);
    const toggleOpen = () => setOpen(!open);

    return (
        <div>
            <Button onClick={toggleOpen}>Show Scrolling Drawer</Button>
            <Drawer
                header="Available Foods"
                open={open}
                onClose={toggleOpen}
                backdrop
                footer={
                    <ButtonGroup>
                        <Button>Cancel</Button>
                        <Button primary>Apply and Save</Button>
                    </ButtonGroup>
                }
            >
                <BodyText>
                    Chia cillum etsy pabst, in paleo fashion axe.
                    Gastropub pariatur tilde wayfarers chia laboris.
                    Salvia cred franzen chambray cillum slow-carb.
                    Beard letterpress distillery, knausgaard readymade YOLO in.
                    Aesthetic ea aliqua, blog vaporware kombucha gluten-free art party VHS pariatur cray raclette.
                </BodyText>
                <BodyText>
                    Pop-up kale chips four dollar toast gastropub you probably haven't heard of them prism tote bag.
                    Paleo thundercats godard glossier +1, iceland anim.
                    Readymade sriracha occaecat, crucifix bicycle rights retro seitan exercitation craft beer kale
                    chips minim.
                    Do post-ironic wayfarers, seitan etsy small batch hammock green juice hexagon whatever hoodie
                    ipsum fashion axe copper mug.
                    Fingerstache put a bird on it palo santo craft beer.
                </BodyText>
                <BodyText>
                    Chia cillum etsy pabst, in paleo fashion axe.
                    Gastropub pariatur tilde wayfarers chia laboris.
                    Salvia cred franzen chambray cillum slow-carb.
                    Beard letterpress distillery, knausgaard readymade YOLO in.
                    Aesthetic ea aliqua, blog vaporware kombucha gluten-free art party VHS pariatur cray raclette.
                </BodyText>
                <BodyText>
                    Pop-up kale chips four dollar toast gastropub you probably haven't heard of them prism tote bag.
                    Paleo thundercats godard glossier +1, iceland anim.
                    Readymade sriracha occaecat, crucifix bicycle rights retro seitan exercitation craft beer kale
                    chips minim.
                    Do post-ironic wayfarers, seitan etsy small batch hammock green juice hexagon whatever hoodie
                    ipsum fashion axe copper mug.
                    Fingerstache put a bird on it palo santo craft beer.
                </BodyText>
                <BodyText>
                    Chia cillum etsy pabst, in paleo fashion axe.
                    Gastropub pariatur tilde wayfarers chia laboris.
                    Salvia cred franzen chambray cillum slow-carb.
                    Beard letterpress distillery, knausgaard readymade YOLO in.
                    Aesthetic ea aliqua, blog vaporware kombucha gluten-free art party VHS pariatur cray raclette.
                </BodyText>
                <BodyText>
                    Pop-up kale chips four dollar toast gastropub you probably haven't heard of them prism tote bag.
                    Paleo thundercats godard glossier +1, iceland anim.
                    Readymade sriracha occaecat, crucifix bicycle rights retro seitan exercitation craft beer kale
                    chips minim.
                    Do post-ironic wayfarers, seitan etsy small batch hammock green juice hexagon whatever hoodie
                    ipsum fashion axe copper mug.
                    Fingerstache put a bird on it palo santo craft beer.
                </BodyText>
                <BodyText>
                    Chia cillum etsy pabst, in paleo fashion axe.
                    Gastropub pariatur tilde wayfarers chia laboris.
                    Salvia cred franzen chambray cillum slow-carb.
                    Beard letterpress distillery, knausgaard readymade YOLO in.
                    Aesthetic ea aliqua, blog vaporware kombucha gluten-free art party VHS pariatur cray raclette.
                </BodyText>
                <BodyText>
                    Pop-up kale chips four dollar toast gastropub you probably haven't heard of them prism tote bag.
                    Paleo thundercats godard glossier +1, iceland anim.
                    Readymade sriracha occaecat, crucifix bicycle rights retro seitan exercitation craft beer kale
                    chips minim.
                    Do post-ironic wayfarers, seitan etsy small batch hammock green juice hexagon whatever hoodie
                    ipsum fashion axe copper mug.
                    Fingerstache put a bird on it palo santo craft beer.
                </BodyText>
                <BodyText>
                    Chia cillum etsy pabst, in paleo fashion axe.
                    Gastropub pariatur tilde wayfarers chia laboris.
                    Salvia cred franzen chambray cillum slow-carb.
                    Beard letterpress distillery, knausgaard readymade YOLO in.
                    Aesthetic ea aliqua, blog vaporware kombucha gluten-free art party VHS pariatur cray raclette.
                </BodyText>
                <BodyText>
                    Pop-up kale chips four dollar toast gastropub you probably haven't heard of them prism tote bag.
                    Paleo thundercats godard glossier +1, iceland anim.
                    Readymade sriracha occaecat, crucifix bicycle rights retro seitan exercitation craft beer kale
                    chips minim.
                    Do post-ironic wayfarers, seitan etsy small batch hammock green juice hexagon whatever hoodie
                    ipsum fashion axe copper mug.
                    Fingerstache put a bird on it palo santo craft beer.
                </BodyText>
            </Drawer>
        </div>
    );
};
render (Example);

Examples

Drawer Content Modifying Main Content

const Example = () => {

    const [value, setValue] = React.useState([1, 3]);
    const [open, setOpen] = React.useState(false);
    const toggleOpen = () => setOpen(!open);
    const options = [
        { key: 1, value: 1, text: "Mercury" },
        { key: 2, value: 2, text: "Venus" },
        { key: 3, value: 3, text: "Earth" },
        { key: 4, value: 4, text: "Mars" }
    ];

    const onChange = (value, checked) => {
        if (checked) {
            setValue(prevState => [...prevState, value])
        } else {
            setValue(prevState => [...prevState].filter(item => item !== value))
        }
    };

    return (
        <div>
            <Card>
                <BodyText size="large" bold className="m-b-1">Items to Request</BodyText>
                <ul className="p-l-0 lh-default" style={{listStyle: 'none'}}>
                    {value.length === 0
                        ? <BodyText subdued>No Items Requested</BodyText>
                        : (
                            options.map((option, index) => (
                                value.includes(option.value)
                                ? <li key={index}>{option.text}</li>
                                : ''
                            ))
                        )
                    }
                </ul>
                <Link primary onClick={toggleOpen} className="p-t-1">Edit Selection</Link>
            </Card>
            <Drawer
                header="Available Items"
                open={open}
                onClose={toggleOpen}
            >
                <Form><Form.Group grouped>
                    {options.map((option) => (
                        <Form.Checkbox
                            label={option.text}
                            key={option.key}
                            value={option.value}
                            checked={value.includes(option.value)}
                            onChange={onChange}
                        />
                    ))}
                </Form.Group></Form>
            </Drawer>
        </div>
    );
};
render (Example);

Best Practices

  • Drawers work well for:
    • Completing quick sub-tasks in a flow.
    • Aiding in completing a task on the page.
  • Drawer actions shouldn't reset when closed.

Related Components

  • To completely hide the context of the page, use the Takeover component.
  • For a small, floating container of information, use the Popover component.
  • To more directly interrupt the user flow with content, use the modal component.

Importing

import { Drawer } from '@servicetitan/design-system';