Skip to content

Commit

Permalink
Add support for Material-UI v5 beta (#418)
Browse files Browse the repository at this point in the history
* upgrade to material-ui v5; modify code as recommended by the material-ui docs

* upgrade to material-ui v5; modify code as recommended by the material-ui docs

* add missing anchor classes to SnackbarItem; fix child class declarations

* update dependencies; run build

* package-lock update

* Allow passing classes from props

* Adapt new MUI breakpoints

* Bind this

* Mark emotion peerDeps as optional

* 1.0.6-next.2

Co-authored-by: Hossein Dehnokhalaji <hossein.dehnavi98@yahoo.com>
  • Loading branch information
joemaffei and iamhosseindhv authored Aug 21, 2021
1 parent 6afc009 commit e09bff2
Show file tree
Hide file tree
Showing 8 changed files with 812 additions and 718 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ module.exports = {
'react/destructuring-assignment': 0,
'react/jsx-closing-bracket-location': 0,
'react/sort-comp': 0,
'arrow-parens': 0,
'react/require-default-props': 0,
'react/jsx-props-no-spreading': 0,
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'jsx-a11y/anchor-is-valid': ['error', { aspects: ['invalidHref', 'preferButton'] }],
},
Expand Down
572 changes: 328 additions & 244 deletions package-lock.json

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "notistack",
"version": "1.0.6-next.1",
"version": "1.0.6-next.2",
"description": "Highly customizable notification snackbars (toasts) that can be stacked on top of each other",
"main": "dist/index.js",
"module": "dist/notistack.esm.js",
Expand All @@ -26,11 +26,23 @@
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0",
"@material-ui/core": "^5.0.0-alpha.15"
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@material-ui/core": "^5.0.0-beta.4"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
},
"devDependencies": {
"@babel/preset-react": "^7.8.3",
"@material-ui/core": "^5.0.0-alpha.15",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@material-ui/core": "^5.0.0-beta.4",
"@types/node": "^13.9.0",
"@types/react": "^16.9.23",
"@types/react-dom": "^16.9.5",
Expand Down
39 changes: 25 additions & 14 deletions src/SnackbarContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/core/styles';
import { styled } from '@material-ui/core/styles';
import { SNACKBAR_INDENTS } from './utils/constants';
import { SnackbarProviderProps } from '.';

Expand All @@ -13,8 +13,20 @@ const collapse = {

const xsWidthMargin = 16;

const useStyle = makeStyles((theme) => ({
root: {
const componentName = 'SnackbarContainer';

const classes = {
root: `${componentName}-root`,
rootDense: `${componentName}-rootDense`,
top: `${componentName}-top`,
bottom: `${componentName}-bottom`,
left: `${componentName}-left`,
right: `${componentName}-right`,
center: `${componentName}-center`,
};

const Root = styled('div')(({ theme }) => ({
[`&.${classes.root}`]: {
boxSizing: 'border-box',
display: 'flex',
maxHeight: '100%',
Expand All @@ -33,43 +45,43 @@ const useStyle = makeStyles((theme) => ({
transition: 'padding 300ms ease 0ms',
},
maxWidth: `calc(100% - ${SNACKBAR_INDENTS.view.default * 2}px)`,
[theme.breakpoints.down('xs')]: {
[theme.breakpoints.down('sm')]: {
width: '100%',
maxWidth: `calc(100% - ${xsWidthMargin * 2}px)`,
},
},
rootDense: {
[`&.${classes.rootDense}`]: {
[collapse.wrapper]: {
padding: `${SNACKBAR_INDENTS.snackbar.dense}px 0px`,
},
},
top: {
[`&.${classes.top}`]: {
top: SNACKBAR_INDENTS.view.default - SNACKBAR_INDENTS.snackbar.default,
flexDirection: 'column',
},
bottom: {
[`&.${classes.bottom}`]: {
bottom: SNACKBAR_INDENTS.view.default - SNACKBAR_INDENTS.snackbar.default,
flexDirection: 'column-reverse',
},
left: {
[`&.${classes.left}`]: {
left: SNACKBAR_INDENTS.view.default,
[theme.breakpoints.up('sm')]: {
alignItems: 'flex-start',
},
[theme.breakpoints.down('xs')]: {
[theme.breakpoints.down('sm')]: {
left: `${xsWidthMargin}px`,
},
},
right: {
[`&.${classes.right}`]: {
right: SNACKBAR_INDENTS.view.default,
[theme.breakpoints.up('sm')]: {
alignItems: 'flex-end',
},
[theme.breakpoints.down('xs')]: {
[theme.breakpoints.down('sm')]: {
right: `${xsWidthMargin}px`,
},
},
center: {
[`&.${classes.center}`]: {
left: '50%',
transform: 'translateX(-50%)',
[theme.breakpoints.up('sm')]: {
Expand All @@ -86,7 +98,6 @@ interface SnackbarContainerProps {
}

const SnackbarContainer: React.FC<SnackbarContainerProps> = (props) => {
const classes = useStyle();
const { className, anchorOrigin, dense, ...other } = props;

const combinedClassname = clsx(
Expand All @@ -98,7 +109,7 @@ const SnackbarContainer: React.FC<SnackbarContainerProps> = (props) => {
);

return (
<div className={combinedClassname} {...other} />
<Root className={combinedClassname} {...other} />
);
};

Expand Down
26 changes: 15 additions & 11 deletions src/SnackbarContent/SnackbarContent.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import React, { forwardRef } from 'react';
import clsx from 'clsx';
import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles';
import { styled } from '@material-ui/core/styles';
import { SnackbarContentProps } from '../index';

const styles = (theme: Theme) => createStyles({
root: {
const componentName = 'SnackbarContent';

const classes = {
root: `${componentName}-root`,
};

const Root = styled('div')(({ theme }) => ({
[`&.${classes.root}`]: {
display: 'flex',
flexWrap: 'wrap',
flexGrow: 1,
[theme.breakpoints.up('sm')]: {
flexGrow: 'initial',
minWidth: 288,
},
}
});

interface Props extends WithStyles<typeof styles>, SnackbarContentProps { }
},
}));

const SnackbarContent = forwardRef<HTMLDivElement, Props>(({ classes, className, ...props }, ref) => (
<div ref={ref} className={clsx(classes.root, className)} {...props} />
))
const SnackbarContent = forwardRef<HTMLDivElement, SnackbarContentProps>(({ className, ...props }, ref) => (
<Root ref={ref} className={clsx(classes.root, className)} {...props} />
));

export default withStyles(styles)(SnackbarContent);
export default SnackbarContent;
94 changes: 54 additions & 40 deletions src/SnackbarItem/SnackbarItem.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
import React, { useState, useEffect, useRef } from 'react';
import clsx from 'clsx';
import { withStyles, WithStyles, createStyles, Theme, emphasize } from '@material-ui/core/styles';
import { emphasize, styled } from '@material-ui/core/styles';
import Collapse from '@material-ui/core/Collapse';
import type { SnackbarClassKey } from '@material-ui/core';
import SnackbarContent from '../SnackbarContent';
import { getTransitionDirection } from './SnackbarItem.util';
import { allClasses, REASONS, objectMerge, DEFAULTS, transformer } from '../utils/constants';
import { SharedProps, RequiredBy, TransitionHandlerProps, SnackbarProviderProps as ProviderProps } from '../index';
import { REASONS, objectMerge, DEFAULTS, transformer } from '../utils/constants';
import { SharedProps, RequiredBy, TransitionHandlerProps, SnackbarProviderProps as ProviderProps, ClassNameMap } from '../index';
import defaultIconVariants from '../utils/defaultIconVariants';
import createChainedFunction from '../utils/createChainedFunction';
import { Snack } from '../SnackbarProvider';
import Snackbar from './Snackbar';

const styles = (theme: Theme) => {
// @ts-ignore
const componentName = 'SnackbarItem';

const classes = {
contentRoot: `${componentName}-contentRoot`,
lessPadding: `${componentName}-lessPadding`,
variantSuccess: `${componentName}-variantSuccess`,
variantError: `${componentName}-variantError`,
variantInfo: `${componentName}-variantInfo`,
variantWarning: `${componentName}-variantWarning`,
message: `${componentName}-message`,
action: `${componentName}-action`,
wrappedRoot: `${componentName}-wrappedRoot`,
};

const StyledSnackbar = styled(Snackbar)(({ theme }) => {
const mode = theme.palette.mode || theme.palette.type;
const backgroundColor = emphasize(theme.palette.background.default, mode === 'light' ? 0.8 : 0.98);
return createStyles({
...allClasses.mui,
contentRoot: {

return {
[`&.${classes.wrappedRoot}`]: {
position: 'relative',
transform: 'translateX(0)',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
[`.${classes.contentRoot}`]: {
...theme.typography.body2,
backgroundColor,
color: theme.palette.getContrastText(backgroundColor),
Expand All @@ -26,64 +48,55 @@ const styles = (theme: Theme) => {
borderRadius: '4px',
boxShadow: '0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)',
},
lessPadding: {
[`.${classes.lessPadding}`]: {
paddingLeft: 8 * 2.5,
},
variantSuccess: {
[`.${classes.variantSuccess}`]: {
backgroundColor: '#43a047', // green
color: '#fff',
},
variantError: {
[`.${classes.variantError}`]: {
backgroundColor: '#d32f2f', // dark red
color: '#fff',
},
variantInfo: {
[`.${classes.variantInfo}`]: {
backgroundColor: '#2196f3', // nice blue
color: '#fff',
},
variantWarning: {
[`.${classes.variantWarning}`]: {
backgroundColor: '#ff9800', // amber
color: '#fff',
},
message: {
[`.${classes.message}`]: {
display: 'flex',
alignItems: 'center',
padding: '8px 0',
},
action: {
[`.${classes.action}`]: {
display: 'flex',
alignItems: 'center',
marginLeft: 'auto',
paddingLeft: 16,
marginRight: -8,
},
wrappedRoot: {
position: 'relative',
transform: 'translateX(0)',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
});
}

};
});

type RemovedProps =
| 'variant' // the one received from Provider is processed and passed to snack prop
| 'variant' // the one received from Provider is processed and passed to snack prop
| 'anchorOrigin' // same as above
| 'autoHideDuration' // same as above
| 'preventDuplicate' // the one recevied from enqueueSnackbar is processed in provider, therefore shouldn't be passed to SnackbarItem */


export interface SnackbarItemProps extends WithStyles<typeof styles>, RequiredBy<Omit<SharedProps, RemovedProps>, 'onEntered' | 'onExited' | 'onClose'> {
export interface SnackbarItemProps extends RequiredBy<Omit<SharedProps, RemovedProps>, 'onEntered' | 'onExited' | 'onClose'> {
snack: Snack;
dense: ProviderProps['dense'];
iconVariant: ProviderProps['iconVariant'];
hideIconVariant: ProviderProps['hideIconVariant'];
classes: Partial<ClassNameMap<SnackbarClassKey>>;
}

const SnackbarItem: React.FC<SnackbarItemProps> = ({ classes, ...props }) => {
const SnackbarItem: React.FC<SnackbarItemProps> = ({ classes: propClasses, ...props }) => {
const timeout = useRef<ReturnType<typeof setTimeout>>();
const [collapsed, setCollapsed] = useState(true);

Expand Down Expand Up @@ -181,11 +194,14 @@ const SnackbarItem: React.FC<SnackbarItemProps> = ({ classes, ...props }) => {
content = content(key, snack.message);
}

// eslint-disable-next-line operator-linebreak
const callbacks: { [key in keyof TransitionHandlerProps]?: any } =
['onEnter', 'onEntering', 'onEntered', 'onExit', 'onExiting', 'onExited'].reduce((acc, cbName) => ({
...acc,
// @ts-ignore
[cbName]: createChainedFunction([props.snack[cbName], props[cbName]], props.snack.key),
[cbName]: createChainedFunction([
props.snack[cbName as keyof Snack],
props[cbName as keyof SnackbarItemProps],
], props.snack.key),
}), {});

return (
Expand All @@ -195,19 +211,17 @@ const SnackbarItem: React.FC<SnackbarItemProps> = ({ classes, ...props }) => {
in={collapsed}
onExited={callbacks.onExited}
>
{/* @ts-ignore */}
<Snackbar
<StyledSnackbar
{...other}
{...singleSnackProps}
open={open}
className={clsx(
classes.root,
propClasses.root,
classes.wrappedRoot,
classes[transformer.toAnchorOrigin(anchorOrigin)],
propClasses[transformer.toAnchorOrigin(anchorOrigin)],
)}
onClose={handleClose}
>
{/* @ts-ignore */}
<TransitionComponent
appear
in={open}
Expand All @@ -233,7 +247,7 @@ const SnackbarItem: React.FC<SnackbarItemProps> = ({ classes, ...props }) => {
{ [classes.lessPadding]: !hideIconVariant && icon },
classes[transformer.toVariant(variant)],
otherClassName,
singleClassName
singleClassName,
)}
>
<div id={ariaAttributes['aria-describedby']} className={classes.message}>
Expand All @@ -246,9 +260,9 @@ const SnackbarItem: React.FC<SnackbarItemProps> = ({ classes, ...props }) => {
</SnackbarContent>
)}
</TransitionComponent>
</Snackbar>
</StyledSnackbar>
</Collapse>
);
};

export default withStyles(styles)(SnackbarItem);
export default SnackbarItem;
4 changes: 2 additions & 2 deletions src/SnackbarProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class SnackbarProvider extends Component<SnackbarProviderProps, State> {
snacks: [],
queue: [], // eslint-disable-line react/no-unused-state
contextValue: {
enqueueSnackbar: this.enqueueSnackbar,
closeSnackbar: this.closeSnackbar,
enqueueSnackbar: this.enqueueSnackbar.bind(this),
closeSnackbar: this.closeSnackbar.bind(this),
},
};
}
Expand Down
Loading

0 comments on commit e09bff2

Please sign in to comment.