Skip to main content

Modal

A modal is an element that shows a priority dialog above the current content, where the focus should be completely pointed to the information inside of it, blocking all elements underneath with an opaque overlay.

Example

Live Editor
function displayModal() {
  const ModalButton = () => {
    const { openModal, closeModal, updateModal } = useContext(ModalContext);
    const text =
      'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.';

    const handleButtonClick = () => {
      openModal({
        content: text,
        closeOnPrimaryClick: false,
        primaryAction: {
          label: 'Save',
          kind: 'primary',
          onClick: () => {
            alert('Saved!');
            updateModal({
              title: 'Second title',
              content: 'Some new content'
            });
          }
        },
        secondaryAction: {
          label: 'Cancel',
          kind: 'secondary',
          onClick: closeModal
        },
        closeButtonAction: () => console.log('Close button clicked'),
        size: 'medium',
        title: 'Modal dialog',
        testId: 'some-unique-identifier'
      });
    };

    return <Button label="Open modal" onClick={handleButtonClick} />;
  };

  return (
    <ModalProvider>
      <ModalButton />
    </ModalProvider>
  );
}
Result
Loading...

Usage Rules

When setting the code to use a Modal, you will wrap the component tree with ModalProvider.

// App.js

import { ModalProvider } from '@adjust/components';

export default function() {
return (
<ModalProvider>
{...}
</ModalProvider>
);
}

Inside the ModalProvider scope your code has access to openModal() and closeModal() via ModalContext.

CloseModal() is a direct instruction with no arguments, where OpenModal() usage is shown on the first example. The CloseModal() function will also call any code passed via onClose property.

The ModalProvider does not receive any property itself, while the openModal function accepts all the properties documented below.

note

It is possible to close the Modal by pressing the ESC key, on the keyboard

Variants

Custom Content

It is possible to use an element and/or a React component as content for the Dialog, as shown on the next example:

Live Editor
function displayModal() {
  const TwoFactorDialog = () => {
    const { openModal } = useContext(ModalContext);
    const content = (
      <div>
        <p>
          Add greater security to your dashboard with two-factor authentication.
          Once enabled, you’ll receive a one-time access link every time you
          sign in.
        </p>
        <div>
          <Switch size="medium" aria-label="Two-Factor Authentication" />{' '}
          Two-Factor Authentication
        </div>
      </div>
    );

    const twoFactorDialog = () => {
      openModal({
        content: content,
        primaryAction: {
          label: 'Done',
          kind: 'primary'
        },
        size: 'medium',
        title: `Manage two-factor authentication of your account for enhanced security`
      });
    };

    return (
      <>
        <Button label="Two-Factor Authentication" onClick={twoFactorDialog} />
      </>
    );
  };

  return (
    <ModalProvider>
      <TwoFactorDialog />
    </ModalProvider>
  );
}
Result
Loading...

Primary Action

The primary action primaryAction is optional. It will execute the code which is added to primaryAction.onClick and then automatically close the Modal as default.

  • Using closeOnPrimaryClick as Modal prop to prevent the Modal from closing after clicking on the primary button.
  • Using closeModal() in primaryAction.onClick to close the modal.

Secondary Action

The secondary action secondaryAction is optional. This action gives you freedom to decide either to close or not. If you want to close, make sure to call closeModal() inside your custom secondaryAction.onClick, just like the first example.

Sizes

It is possible to pick between three different widths of the modal's dialog: small, medium (default) and large.

You can see them on the following example:

Live Editor
function displayModal() {
  const ModalButtons = () => {
    const { openModal } = useContext(ModalContext);
    const text = {
      small: 'One morning, Gregor Samsa woke up.',
      medium:
        'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.',
      large:
        'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections.'
    };

    const handleButtonClick = (size) => {
      openModal({
        content: text[size],
        primaryAction: {
          label: 'Close',
          kind: 'primary'
        },
        size: size,
        title: `Modal dialog: ${size}`
      });
    };

    return (
      <>
        <Button
          label="Small modal"
          onClick={() => handleButtonClick('small')}
        />
        <Button
          label="Medium modal"
          onClick={() => handleButtonClick('medium')}
        />
        <Button
          label="Large modal"
          onClick={() => handleButtonClick('large')}
        />
        <Button
          label="X-Large modal"
          onClick={() => handleButtonClick('x-large')}
        />
      </>
    );
  };

  return (
    <ModalProvider>
      <ModalButtons />
    </ModalProvider>
  );
}
Result
Loading...

Static Height

You can set the height of Modal content by adding prop staticHeight.

Live Editor
function displayModal() {
  const ModalButtons = () => {
    const { openModal } = useContext(ModalContext);

    const longText = `There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.`

    const handleButtonClick = () => {
      openModal({
        content: longText,
        primaryAction: {
          label: 'Close',
          kind: 'primary'
        },
        size: 'small',
        title: 'Static Height',
        staticHeight: '400px'
      });
    };

    return (
      <Button
        label="Open modal"
        onClick={handleButtonClick}
      />
    );
  };

  return (
    <ModalProvider>
      <ModalButtons />
    </ModalProvider>
  );
}
Result
Loading...

Update Modal

You can update existing Modal by using updateModal(). It will update the existing Modal props.

Live Editor
function displayModal() {
  const ModalButton = () => {
    const { openModal, closeModal, updateModal } = useContext(ModalContext);
    const text =
      'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.';

    const handleButtonClick = () => {
      openModal({
        content: text,
        closeOnPrimaryClick: false,
        primaryAction: {
          label: 'Save',
          kind: 'primary',
          onClick: () => {
            updateModal({
              title: 'Second title',
              content: 'Some new content'
            });
          }
        },
        secondaryAction: {
          label: 'Cancel',
          kind: 'secondary',
          onClick: closeModal
        },
        closeButtonAction: () => console.log('Close button clicked'),
        size: 'medium',
        title: 'Modal dialog',
        testId: 'some-unique-identifier'
      });
    };

    return <Button label="Open modal" onClick={handleButtonClick} />;
  };

  return (
    <ModalProvider>
      <ModalButton />
    </ModalProvider>
  );
}
Result
Loading...

You can also overwrite all Modal props by passing true as the second argument in updateModal().

Live Editor
function displayModal() {
  const ModalButton = () => {
    const { openModal, closeModal, updateModal } = useContext(ModalContext);
    const text =
      'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.';

    const handleButtonClick = () => {
      openModal({
        content: text,
        closeOnPrimaryClick: false,
        primaryAction: {
          label: 'Save',
          kind: 'primary',
          onClick: () => {
            updateModal(
              {
                title: 'Second title',
                content: 'Some new content',
                primaryAction: {
                  label: 'Close',
                  kind: 'primary',
                  onClick: closeModal
                }
              },
              true
            );
          }
        },
        secondaryAction: {
          label: 'Cancel',
          kind: 'secondary',
          onClick: closeModal
        },
        closeButtonAction: () => console.log('Close button clicked'),
        size: 'medium',
        title: 'Modal dialog',
        testId: 'some-unique-identifier'
      });
    };

    return <Button label="Open modal" onClick={handleButtonClick} />;
  };

  return (
    <ModalProvider>
      <ModalButton />
    </ModalProvider>
  );
}
Result
Loading...

Examples

You can get the open state of the modal with isModalOpen from the ModalContext

Live Editor
function displayModal() {
  const ModalButtons = () => {
    const { openModal, isModalOpen } = useContext(ModalContext);

    console.log({ isModalOpen });

    const content =
      'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections.';

    const handleButtonClick = () => {
      openModal({
        content,
        primaryAction: {
          label: 'Close',
          kind: 'primary'
        },
        size: 'large',
        title: 'Modal dialog'
      });
    };

    return (
      <div>
        <p>{isModalOpen ? 'Modal is open' : 'Modal is closed'}</p>
        <Button label="Open modal" onClick={handleButtonClick} />
      </div>
    );
  };

  return (
    <ModalProvider>
      <ModalButtons />
    </ModalProvider>
  );
}
Result
Loading...

Update loading state

You can update the loading state of a button by using updateModal().

Live Editor
function displayModal() {
  const ModalButton = () => {
    const { openModal, closeModal, updateModal } = useContext(ModalContext);
    const text =
      'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.';
    const primaryAction = {
      label: 'Save',
      kind: 'primary',
      isLoading: false,
      disabled: false,
      onClick: () => {
        updateModal({
          primaryAction: {
            ...primaryAction,
            ...{ isLoading: true, disabled: true }
          }
        });
      }
    };
    const handleButtonClick = () => {
      openModal({
        content: text,
        closeOnPrimaryClick: false,
        primaryAction: primaryAction,
        secondaryAction: {
          label: 'Cancel',
          kind: 'secondary',
          onClick: closeModal
        },
        closeButtonAction: () => console.log('Close button clicked'),
        size: 'medium',
        title: 'Modal dialog',
        testId: 'some-unique-identifier'
      });
    };

    return <Button label="Open modal" onClick={handleButtonClick} />;
  };

  return (
    <ModalProvider>
      <ModalButton />
    </ModalProvider>
  );
}
Result
Loading...

Props

The object structure for individual modal items passed into openModal:

NameTypeDefault
title *
Title of the Modal's dialog
string
content *
Content of the Modal's dialog
ReactNode
primaryAction
Primary button for the Dialog component. See the Button component for more info
ButtonProps
secondaryAction
Secondary and optional button for the Dialog component. See the Button component for more info
ButtonProps
closeButtonAction
The handler will be called only on clicking Close button
(() => void)
onClose
The handler will be called when the Modal is closed
(() => void)
onUpdate
The handler will be called when the Modal is updated
((dialog: DialogComponentProps) => void)
size
Size of the Modal's dialog
"small" | "medium" | "large" | "x-large"
"medium"
closeOnPrimaryClick
Use this prop to prevent the Modal's dialog from closing after clicking on the primary button
boolean
true
staticHeight
Static height of Modal's content and support scrollable.
string
testId
Unique identifier for Pendo tagging to be translated into data-testid attribute on Modal Actions
string
* - the prop is required.