Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple nested Contexts #115

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
81 changes: 58 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const FormWithToasts = () => {
} else {
addToast('Saved Successfully', { appearance: 'success' })
}
}
};

return <form onSubmit={onSubmit}>...</form>
}
};

const App = () => (
<ToastProvider>
Expand All @@ -49,37 +49,44 @@ For brevity:
- `PlacementType` is equal to `'bottom-left' | 'bottom-center' | 'bottom-right' | 'top-left' | 'top-center' | 'top-right'`.
- `TransitionState` is equal to `'entering' | 'entered' | 'exiting' | 'exited'`.

| Property | Description |
| -------------------------------------- | ---------------------------------------------------------------------------------------- |
| autoDismissTimeout `number` | Default `5000`. The time until a toast will be dismissed automatically, in milliseconds. |
| autoDismiss `boolean` | Default: `false`. Whether or not to dismiss the toast automatically after a timeout. |
| Property | Description |
| --------------------------- | ---------------------------------------------------------------------------------------- |
| autoDismissTimeout `number` | Default `5000`. The time until a toast will be dismissed automatically, in milliseconds. |
| autoDismiss `boolean` | Default: `false`. Whether or not to dismiss the toast automatically after a timeout. |

| children `Node` | Required. Your app content. |
| components `{ ToastContainer, Toast }` | Replace the underlying components. |
| placement `PlacementType` | Default `top-right`. Where, in relation to the viewport, to place the toasts. |
| transitionDuration `number` | Default `220`. The duration of the CSS transition on the `Toast` component. |
| children `Node` | Required. Your app content. |
| components `{ ToastContainer, Toast }` | Replace the underlying components. |
| placement `PlacementType` | Default `top-right`. Where, in relation to the viewport, to place the toasts. |
| transitionDuration `number` | Default `220`. The duration of the CSS transition on the `Toast` component. |
| name `string` | Default `default`. Provide a unique name when using [Nested Providers](#nested-providers). |

## Toast Props

| Property | Description |
| ---------------------------------- | ------------------------------------------------------------------ |
| appearance | Required. One of `success`, `error`, `warning`, `info` |
| children | Required. The content of the toast notification. |
| autoDismiss `boolean` | Inherited from `ToastProvider` if not provided. |
| autoDismissTimeout `number` | Inherited from `ToastProvider`. |
| onDismiss: `Id => void` | Passed in dynamically. Can be called in a custom toast to dismiss it.|
| placement `PlacementType` | Inherited from `ToastProvider`. |
| transitionDuration `number` | Inherited from `ToastProvider`. |
| transitionState: `TransitionState` | Passed in dynamically. |
| Property | Description |
| ---------------------------------- | --------------------------------------------------------------------- |
| appearance | Required. One of `success`, `error`, `warning`, `info` |
| children | Required. The content of the toast notification. |
| autoDismiss `boolean` | Inherited from `ToastProvider` if not provided. |
| autoDismissTimeout `number` | Inherited from `ToastProvider`. |
| onDismiss: `Id => void` | Passed in dynamically. Can be called in a custom toast to dismiss it. |
| placement `PlacementType` | Inherited from `ToastProvider`. |
| transitionDuration `number` | Inherited from `ToastProvider`. |
| transitionState: `TransitionState` | Passed in dynamically. |

## Hook

The `useToast` hook has the following signature:

```jsx
const { addToast, removeToast, removeAllToasts, updateToast, toastStack } = useToasts();
const { addToast, removeToast, removeAllToasts, updateToast, toastStack } = useToasts(options);
```

`options` passed to `useToasts` are:

| Option | Description |
| ------------- | ------------------------------------------------------------------------------------------ |
| name `string` | Default `default`. Provide a unique name when using [Nested Providers](#nested-providers). |

The `addToast` method has three arguments:

1. The first is the content of the toast, which can be any renderable `Node`.
Expand All @@ -104,8 +111,8 @@ The `toastStack` is an array of objects representing the current toasts, e.g.
```jsx
[
{ content: 'Something went wrong', id: 'generated-string', appearance: 'error' },
{ content: 'Item saved', id: 'generated-string', appearance: 'success' }
]
{ content: 'Item saved', id: 'generated-string', appearance: 'success' },
];
```

## Replaceable Components
Expand Down Expand Up @@ -137,6 +144,34 @@ export const MyCustomToast = ({ children, ...props }) => (
);
```

## Nested Providers

Displaying individual toasts differently is done using nested `<ToastProvider>`s.

For example, [the docs
page](https://jossmac.github.io/react-toast-notifications) displays both
"notification" style & "snack bar" style toasts simultaneously on the same page.

Nested Providers must be given a unique `name` prop, which is then also passed
to the `<ToastConsumer>` component or `useToasts()` hook.

```jsx
import { ToastProvider, ToastConsumer } from 'react-toast-notifications';

const App = () => (
<ToastProvider>
<ToastProvider name="snack">
<ToastConsumer>
{({ add }) => <button onClick={() => add('A toast')}>Add Toast</button>}
</ToastConsumer>
<ToastConsumer name="snack">
{({ add }) => <button onClick={() => add('A snack')}>Add Snack</button>}
</ToastConsumer>
</ToastProvider>
</ToastProvider>
);
```

## Alternatives

This library may not meet your needs. Here are some alternative I came across whilst searching for this solution:
Expand Down
138 changes: 71 additions & 67 deletions examples/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,49 +208,50 @@ function App() {

return (
<ToastProvider>
<ConnectivityListener />
<Section area="intro">
<Container>
<Header>
<Repo href={repoUrl}>
<Icon role="img">🍞</Icon>
<span>React Toast Notifications</span>
</Repo>
<GithubLogo href={repoUrl} target="_blank" />
</Header>
<ToastProvider
name="snack"
autoDismiss
autoDismissTimeout={6000}
components={{ Toast: Snack }}
placement="bottom-center"
>
<ConnectivityListener />
<Section area="intro">
<Container>
<Header>
<Repo href={repoUrl}>
<Icon role="img">🍞</Icon>
<span>React Toast Notifications</span>
</Repo>
<GithubLogo href={repoUrl} target="_blank" />
</Header>

<Body>
<Toasts />
</Body>
<Body>
<Toasts />
</Body>

<Footer>
<div>
<span>by </span>
<a href="https://twitter.com/jossmackison" target="_blank">
@jossmac
</a>
</div>
<div>
paragraphs from{' '}
<a href="http://www.cupcakeipsum.com" target="_blank">
Cupcake Ipsum
</a>{' '}
</div>
</Footer>
</Container>
</Section>
{/*
<Footer>
<div>
<span>by </span>
<a href="https://twitter.com/jossmackison" target="_blank">
@jossmac
</a>
</div>
<div>
paragraphs from{' '}
<a href="http://www.cupcakeipsum.com" target="_blank">
Cupcake Ipsum
</a>{' '}
</div>
</Footer>
</Container>
</Section>
{/*
==============================
CONFIGURATION
==============================
*/}
<Section area="config">
<ToastProvider
autoDismiss
autoDismissTimeout={6000}
components={{ Toast: Snack }}
placement="bottom-center"
>
<Section area="config">
<Container>
<Body>
<StretchGroup reverse>
Expand All @@ -260,7 +261,7 @@ function App() {
<p>
Replace or configure any part of the notification system.
</p>
<ToastConsumer>
<ToastConsumer name="snack">
{({ add, toasts }) => (
<Button
appearance="snack"
Expand Down Expand Up @@ -292,39 +293,42 @@ const App = () => (
</StretchGroup>
</Body>
</Container>
</ToastProvider>
</Section>
{/*
</Section>
{/*
==============================
EXAMPLE
==============================
*/}
<Section area="example">
<Container>
<Body>
<StretchGroup>
<ContentBlock align="left">
<Title>Let&apos;s get real.</Title>
<div>
<p>
You&apos;re probably not firing off notifications
haphazardly, from some random buttons in your app.*
</p>
<p>
To see an example of how you might use this IRL, toggle the{' '}
<code>Offline</code> checkbox in the Network pane of your
dev tools. If you're on mobile, just turn on flight-mode.
</p>
<p>
<small>* It's totally cool if you are, no judgement.</small>
</p>
</div>
</ContentBlock>
<CodeBlock>{exampleText}</CodeBlock>
</StretchGroup>
</Body>
</Container>
</Section>
<Section area="example">
<Container>
<Body>
<StretchGroup>
<ContentBlock align="left">
<Title>Let&apos;s get real.</Title>
<div>
<p>
You&apos;re probably not firing off notifications
haphazardly, from some random buttons in your app.*
</p>
<p>
To see an example of how you might use this IRL, toggle
the <code>Offline</code> checkbox in the Network pane of
your dev tools. If you're on mobile, just turn on
flight-mode.
</p>
<p>
<small>
* It's totally cool if you are, no judgement.
</small>
</p>
</div>
</ContentBlock>
<CodeBlock>{exampleText}</CodeBlock>
</StretchGroup>
</Body>
</Container>
</Section>
</ToastProvider>
</ToastProvider>
);
}
Expand Down