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

Add multiple container support (optional). #337

Merged
merged 3 commits into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"gzipped": 5540
},
"cjs/react-toastify.min.js": {
"bundled": 29343,
"minified": 16443,
"gzipped": 5216
"bundled": 29647,
"minified": 16602,
"gzipped": 5263
},
"./esm/index.esm.js": {
"bundled": 32847,
Expand All @@ -24,16 +24,16 @@
}
},
"esm/react-toastify.js": {
"bundled": 32948,
"minified": 18322,
"gzipped": 5466,
"bundled": 33602,
"minified": 18660,
"gzipped": 5528,
"treeshaked": {
"rollup": {
"code": 16013,
"code": 16295,
"import_statements": 442
},
"webpack": {
"code": 17592
"code": 17888
}
}
}
Expand Down
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [One component to rule them all](#one-component-to-rule-them-all)
- [One ToastContainer to render them](#one-toastcontainer-to-render-them)
- [What if I told you that the ToastContainer is optional](#what-if-i-told-you-that-the-toastcontainer-is-optional)
- [Multi container support](#multi-container-support)
- [Positioning toast](#positioning-toast)
- [Set autoclose delay or disable it](#set-autoclose-delay-or-disable-it)
- [Render a component](#render-a-component)
Expand Down Expand Up @@ -158,6 +159,50 @@ toast.configure({
});
```


#### Multi container support

To enable multiple container support, you have to pass `enableMultiContainer` and specify a `containerId` and use it in
each toast, to do so add `toastContainerId` to the toast's options object.



Note: adding `enableMultiContainer` prop to the `<ToastContainer/ >` will:
- Check each toast to verify if its `toastContainerId` match `containerId` so it can be rendered.
- Ensure not to render any `toast` that has `toastContainerId`.
- Render any toast if both the `toast` and `<ToastContainer/ >` does not include `toastContainerId` and `containerId` respectively.

A simple example to demonstrate multi toast container capability.

- Notify A button will show a toast on the bottom left.
- Notify B button will show a toast on the top right.

```javascript
import React, { Component } from 'react';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';


class App extends Component {
notifyA = () => toast('Wow so easy !', {toastContainerId: 'A'});
notifyB = () => toast('Wow so easy !', {toastContainerId: 'B'});

render(){
return (
<div>
<ToastContainer enableMultiContainer containerId={'A'} position={toast.POSITION.BOTTOM_LEFT} />
<ToastContainer enableMultiContainer containerId={'B'} position={toast.POSITION.TOP_RIGHT} />

<button onClick={this.notifyA}>Notify A !</button>
<button onClick={this.notifyB}>Notify B !</button>
</div>
);
}
}

```


### Positioning toast

By default, all the toasts will be positioned on the top right of your browser. If a position is set on a `toast`, the one defined on ToastContainer will be replaced.
Expand Down Expand Up @@ -1152,9 +1197,12 @@ On mobile the toast will take all the available width.
| toastClassName | string\|object | - | Add optional classes to the toast |
| bodyClassName | string\|object | - | Add optional classes to the toast body |
| progressClassName | string\|object | - | Add optional classes to the progress bar |
| progressStyle | object | - | Add optional inline style to the progress bar |
| progressStyle | object | - | Add optional inline style to the progress bar |
| draggable | bool | true | Allow toast to be draggable |
| draggablePercent | number | 80 | The percentage of the toast's width it takes for a drag to dismiss a toast(value between 0 and 100) |
| enableMultiContainer | bool | - | Enable multi toast container support |
| containerId | string\number | - | Container id used to match toast with the same toastContainerId |



### toast
Expand Down Expand Up @@ -1189,6 +1237,7 @@ The **toastId** can be used to remove a toast programmatically or to check if th
- `progress`: a value between 0..1 to control the progress bar
- `render`: string or React Element, only available when calling update
- `delay`: a number to let you delay the toast appearance
- `toastContainerId`: string or number to match a specific Toast container

:warning:️ *Toast options supersede ToastContainer props* :warning:

Expand Down
65 changes: 65 additions & 0 deletions src/__tests__/components/ToastContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,71 @@ describe('ToastContainer', () => {
});
});

describe('Multiple container support', ()=> {
describe('Disabled', ()=> {
it('Should render toasts in all container', () => {
const toastContainerComponent1 = mount(<ToastContainer enableMultiContainer={false} />);
const toastContainerComponent2 = mount(<ToastContainer />);
const toastContainerComponent3 = mount(<ToastContainer containerId={1}/>);

toast('Toast 1');
toast('Toast 2', {toastContainerId: 1});
jest.runAllTimers();

expect(toastContainerComponent1.state().toast).toHaveLength(2);
expect(toastContainerComponent2.state().toast).toHaveLength(2);
expect(toastContainerComponent3.state().toast).toHaveLength(2);
});
});

describe('Enabled', () => {
describe('With containerId', () => {
it('Should show only related toasts aka- same containerId and toastContainerId', () => {
const toastContainerComponent1 = mount(<ToastContainer containerId={1} enableMultiContainer/>);
const toastContainerComponent2 = mount(<ToastContainer containerId={2} enableMultiContainer/>);

toast('Toast with toastContainerId 1', {toastContainerId: 1});
toast('Toast with toastContainerId 2', {toastContainerId: 2});
toast('Another toast with toastContainerId 2', {toastContainerId: 2});
jest.runAllTimers();

expect(toastContainerComponent1.state().toast).toHaveLength(1);
expect(toastContainerComponent2.state().toast).toHaveLength(2);
});

it('Should not display unrelated toasts', () => {
const toastContainerComponent = mount(<ToastContainer containerId={1} enableMultiContainer/>);

toast('Toast with toastContainerId 1', {toastContainerId: 2});
toast('Toast with toastContainerId 2', {toastContainerId: 2});
jest.runAllTimers();

expect(toastContainerComponent.state().toast).toHaveLength(0);
});
});

describe('Has no containerId', () => {
it('Should display toasts with no toastContainerId', () => {
const toastContainerComponent = mount(<ToastContainer enableMultiContainer />);

toast('Toast');
jest.runAllTimers();

expect(toastContainerComponent.state().toast).toHaveLength(1);
});

it('Should not display any toasts with toastContainerId', () => {
const toastContainerComponent = mount(<ToastContainer enableMultiContainer />);

toast('Toast', {toastContainerId: 1});
jest.runAllTimers();

expect(toastContainerComponent.state().toast).toHaveLength(0);
});
});
});
});

it("Should throw an error if can't render a toast", () => {
expect(() => {
mount(<ToastContainer />);
Expand Down
3 changes: 2 additions & 1 deletion src/components/Toast.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class Toast extends Component {
progress: PropTypes.number,
isProgressDone: PropTypes.bool,
updateId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
ariaLabel: PropTypes.string
ariaLabel: PropTypes.string,
toastContainerId: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};

static defaultProps = {
Expand Down
23 changes: 21 additions & 2 deletions src/components/ToastContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,17 @@ class ToastContainer extends Component {
/**
* Pause the toast on focus loss
*/
pauseOnFocusLoss: PropTypes.bool
pauseOnFocusLoss: PropTypes.bool,

/**
* Show the toast only if it includes toastContainerId and it's the same as containerId
*/
enableMultiContainer: PropTypes.bool,

/**
* Set id to handle multiple container
*/
containerId: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};

static defaultProps = {
Expand Down Expand Up @@ -229,12 +239,21 @@ class ToastContainer extends Component {
return null;
}

belongToContainer({toastContainerId}) {
return toastContainerId === this.props.containerId;
}

buildToast(content, { delay, ...options }) {
if (!this.canBeRendered(content)) {
throw new Error(
`The element you provided cannot be rendered. You provided an element of type ${typeof content}`
`The element you provided cannot be rendered. You provided an element of type ${typeof content}`
);
}
if (this.props.enableMultiContainer) {
if (!this.belongToContainer(options)) {
return null;
}
}
const toastId = options.toastId;
const closeToast = () => this.removeToast(toastId);
const toastOptions = {
Expand Down