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 support for Material-UI v5 beta #418

Merged
merged 12 commits into from
Aug 21, 2021
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