# RSuite Interactions

> Call RSuite Modals like a boss.

[![npm][npm-svg]][npm-home]
[![GitHub Actions][actions-svg]][actions-home]
[![codecov](https://codecov.io/gh/rsuite/interactions/branch/master/graph/badge.svg)](https://codecov.io/gh/rsuite/interactions)

    npm install @rsuite/interactions@next --save

## Features

- Easy to call out `alert()`, `confirm()`, `prompt()` styles modals as you already know how.
- Await their return values.
- Multiple calls are queued.

## API

- [`alert`](#alert)
- [`confirm`](#confirm)
- [`prompt`](#prompt)

### `alert`

Use it like you are using `window.alert()`, and you can await it.

```tsx
const buyNewPhone = useCallback(async () => {
  await alert("Congrats! You've got a new iPhone!");
  console.log('alert closed');
}, []);
```

#### Signatures

```tsx
alert(
  message?: React.ReactNode,
  modalConfig?: AlertModalProps
): Promise<void>;

interface WrappedModalProps {
  modalProps: ModalProps;
}

interface AlertModalProps extends WrappedModalProps {
  okButtonText?: string;
  onOk?: (() => void) | (() => Promise<any>);
}
```

- `okButtonText`: Customize "OK" button text.
- `onOk`: Callback function when "OK" is clicked. If `onOk` returns a `Promise`, "OK" button shows loading status until the promise finishes.

### `confirm`

Use it like you are using `window.confirm()` but await its return value.

```tsx
const confirmSmashPhone = useCallback(async () => {
  if (await confirm('Are you sure you what to do this?')) {
    alert('Rest in pieces.');
  }
}, []);
```

#### Signatures

```tsx
confirm(
  message?: React.ReactNode,
  modalConfig?: ConfirmModalProps
): Promise<boolean>;

interface WrappedModalProps {
  modalProps: ModalProps;
}
interface ConfirmModalProps extends WrappedModalProps {
  okButtonText?: string;
  okButtonDangerous?: boolean;
  cancelButtonText?: string;
  onOk?: (() => void) | (() => Promise<any>);
  onCancel?: (isSubmitLoading?: boolean) => any;
  canCancelOnLoading?: boolean;
}
```

- `okButtonText`: Customize "OK" button text.
- `okButtonDangerous`: When set `true`, "OK" button is red colored.
- `cancelButtonText`: Customize "Cancel" button text.
- `onOk`: Callback function when "OK" is clicked. If `onOk` returns a `Promise`, "OK" button shows loading status until the promise finishes.
- `onCancel`: Callback function when "Cancel" is clicked. If not provided, "Cancel" is disabled when "OK" is loading.
- `canCancelOnLoading`: When `onCancel` is set, you can still use this option to force disable "Cancel" button.

### `prompt`

Use it like you are using `window.prompt()` but await its return value.

```tsx
const promptForName = useCallback(async () => {
  const name = await prompt('What is your name?');
  if (name) {
    alert(`It\'s ok, ${name}.`);
  }
}, []);
```

#### Signatures

```tsx
prompt(
  message?: React.ReactNode,
  _default?: string,
  modalConfig?: PromptModalProps
): Promise<string | null>;

interface WrappedModalProps {
  modalProps: ModalProps;
}
interface PromptModalProps extends WrappedModalProps {
  okButtonText?: string;
  okButtonDangerous?: boolean;
  cancelButtonText?: string;
  validate?: (inputValue: string) => boolean;
  onOk?: ((inputVal?: string) => void) | ((inputVal: string) => Promise<any>);
  onCancel?: (isSubmitLoading?: boolean) => any;
  canCancelOnLoading?: boolean;
  inputProps?: InputProps;
}
```

- `okButtonText`: Customize "OK" button text.
- `okButtonDangerous`: When set `true`, "OK" button is red colored.
- `cancelButtonText`: Customize "Cancel" button text.
- `validate`: Validate current input value. Disable OK button if validation fails.
- `onOk`: Callback function when "OK" is clicked, receiving a string representing the user input. If `onOk` returns a `Promise`, "OK" button shows loading status until the promise finishes.
- `onCancel`: Callback function when "Cancel" is clicked. If not provided, "Cancel" is disabled when "OK" is loading.
- `canCancelOnLoading`: When `onCancel` is set, you can still use this option to force disable "Cancel" button.
- `inputProps`: custom input properties, learn more information about `InputProps` from [here](https://rsuitejs.com/components/input#code-lt-input-gt-code)

## Testing

If you use `@rsuite/interactions` to call alert dialogs in your app, you can easily test it with `@testing-library/react`.

Say you want to show a confirm dialog when user clicks a button that will delete a post:

```jsx
import { confirm } from '@rsuite/interactions';

function App() {
  async function confirmDeletePost(id) {
    if (await confirm('Are you sure?')) {
      await api.deletePost(id);
    }
  }

  return <button onClick={() => confirmDeletePost(1)}>Delete post 1</button>;
}
```

And you want to test that the dialog is shown when the button is clicked:

```jsx
import { render, fireEvent, screen } from '@testing-library/react';

it('Should show a confirm dialog', () => {
  const { getByRole } = render(<App />);

  fireEvent.click(getByRole('button', { name: /delete post/i }));

  // Assume you're using Jest with @testing-library/jest-dom
  const dialog = screen.getByRole('alertdialog');
  expect(dialog).toBeVisible();
  // Assert on its a11y description
  expect(dialog).toHaveAccessibleDescription('Are you sure?');
  // Or if you're using jest-dom < 5.14, you can assert on its textContent
  expect(dialog).toHaveTextContent('Are you sure?');
});
```

## License

MIT licensed

[npm-svg]: https://badgen.net/npm/v/@rsuite/interactions/next
[npm-home]: https://www.npmjs.com/package/@rsuite/interactions
[actions-svg]: https://github.com/rsuite/interactions/workflows/Node.js%20CI/badge.svg
[actions-home]: https://github.com/rsuite/interactions/actions?query=workflow%3A%22Node.js+CI%22
