Skip to content

Commit

Permalink
[#179] Add support for React Portals
Browse files Browse the repository at this point in the history
Extend functionality with `createPortal` optional approach
  • Loading branch information
iamhosseindhv authored Dec 1, 2019
2 parents 093e1bc + 56d2a8e commit 2bcb075
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 23 deletions.
56 changes: 33 additions & 23 deletions src/SnackbarProvider.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { createPortal } from 'react-dom';
import PropTypes from 'prop-types';
import Slide from '@material-ui/core/Slide';
import SnackbarContext from './SnackbarContext';
Expand Down Expand Up @@ -240,7 +241,7 @@ class SnackbarProvider extends Component {
};

render() {
const { classes, children, maxSnack, dense, ...props } = this.props;
const { classes, children, maxSnack, dense, domRoot, ...props } = this.props;
const { contextValue } = this.state;

const categ = this.state.snacks.reduce((acc, current) => {
Expand All @@ -254,36 +255,41 @@ class SnackbarProvider extends Component {

const iconVariant = Object.assign({ ...defaultIconVariant }, { ...this.props.iconVariant });

const snackbars = Object.entries(categ).map(([origin, snacks]) => (
<SnackbarContainer
key={origin}
dense={dense}
anchorOrigin={snacks[0].anchorOrigin}
className={classes[`containerAnchorOrigin${origin}`]}
>
{snacks.map(snack => (
<SnackbarItem
{...props}
key={snack.key}
dense={dense}
snack={snack}
iconVariant={iconVariant}
classes={getClasses(classes)}
onClose={this.handleCloseSnack}
onExited={this.handleExitedSnack}
onEntered={this.handleEnteredSnack}
/>
))}
</SnackbarContainer>
));

return (
<SnackbarContext.Provider value={contextValue}>
{children}
{Object.entries(categ).map(([origin, snacks]) => (
<SnackbarContainer
key={origin}
dense={dense}
anchorOrigin={snacks[0].anchorOrigin}
className={classes[`containerAnchorOrigin${origin}`]}
>
{snacks.map(snack => (
<SnackbarItem
{...props}
key={snack.key}
dense={dense}
snack={snack}
iconVariant={iconVariant}
classes={getClasses(classes)}
onClose={this.handleCloseSnack}
onExited={this.handleExitedSnack}
onEntered={this.handleEnteredSnack}
/>
))}
</SnackbarContainer>
))}
{domRoot ? createPortal(snackbars, domRoot) : snackbars}
</SnackbarContext.Provider>
);
}
}

// polyfill for Node
const Element = typeof Element === 'undefined' ? function () { } : Element;

SnackbarProvider.propTypes = {
/**
* Most of the time, this is your App. every component from this point onward
Expand Down Expand Up @@ -413,6 +419,10 @@ SnackbarProvider.propTypes = {
PropTypes.number,
PropTypes.shape({ enter: PropTypes.number, exit: PropTypes.number }),
]),
/**
* Valid and exist HTML Node element, used to target `ReactDOM.createPortal`
*/
domRoot: PropTypes.instanceOf(Element),
};

SnackbarProvider.defaultProps = {
Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface SnackbarProviderProps extends Omit<SnackbarProps, 'open' | 'mes
preventDuplicate?: boolean;
dense?: boolean;
action?: SnackbarContentProps['action'] | ((key: OptionsObject['key']) => React.ReactNode);
domRoot?: HTMLElement;
content?: React.ReactNode | ((key: OptionsObject['key']) => React.ReactNode);
}

Expand Down

0 comments on commit 2bcb075

Please sign in to comment.