diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c4161d..39edeb64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,30 @@ -## `notistack@0.5.0` +## `notistack@0.5.2` ###### to be published Thanks to all contributers who improved notistack by opening an issue/PR. -**@martinmckenna** +**@Lukas-Kullmann** +* Add `displayName` to components exported by `withSnackbar` HOC [#100](https://github.com/iamhosseindhv/notistack/issues/100) + + +## `notistack@0.5.1` +###### Mar 15, 2019 + +Thanks to all contributers who improved notistack by opening an issue/PR. + +**@amakhrov** +* Fix typing for `iconVariant` props [#91](https://github.com/iamhosseindhv/notistack/issues/91) + + + +## `notistack@0.5.0` +###### Mar 5, 2019 + +Thanks to all contributers who improved notistack by opening an issue/PR. + +**@cwbuecheler @mpash @khhan1993 @Fs00 @martinmckenna** +* Rename `InjectedSnackbarProps` to `withSnackbarProps` in type definitions [#59](https://github.com/iamhosseindhv/notistack/issues/59) +* Add new prop `dense` to allow dense margins for snackbars (suitable for mobiles) [#58](https://github.com/iamhosseindhv/notistack/issues/58) * Improve performance and prevent unnecessary child re-rendering [#39](https://github.com/iamhosseindhv/notistack/issues/39) diff --git a/README.md b/README.md index 19579689..3a77102b 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ Table of Contents - [Documentation](#documentation) - [`SnackbarProvider`](#snackbarprovider) - [`withSnackbar`](#withsnackbar) - - [Add actions to snackbar](#add-actions-to-snackbar) - - [Redux example](#redux-example) + - [Redux support](#redux-support) - [Contribution](#contribution) - [Notes](#notes) - [Author - Contact me](#author---contact) @@ -82,20 +81,23 @@ Or see the code for a minimal working example: [codesandbox](https://codesandbox All material-ui Snackbar props will get passed down to a Snackbar component. See Material-ui [docs](https://material-ui.com/api/snackbar/) for more info. ```javascript // Maximum number of snackbars that can be stacked on top of eachother. -maxSnack type: number required: true default: 3 +maxSnack type: number required: false default: 3 // The little icon that is displayed in a snackbar iconVariant type: any required: false default: Material design icons -// hide or display icon variant of a snackbar +// Hide or display icon variant of a snackbar hideIconVariant type: boolean required: false default: false -// event fired when user clicks on action button (if any) -onClickAction type: func required: false default: dismiss the snackbar +// Event fired when user clicks on action button (if any) +onClickAction type: func required: false default: Dismiss the snackbar // Do not allow snackbars with the same message to be displayed multiple times preventDuplicate type: boolean required: false default: false +// Denser margins for snackbars. Recommended to be used on mobile devices +dense type: boolean required: false default: false + // Example of a Mui-Snackbar prop transitionDuration={{ exit: 380, enter: 400 }} ``` @@ -153,33 +155,8 @@ this.props.closeSnackbar(key) key type: string|number required: true ``` - -### Add actions to snackbar: -You can add actions to snackbars in the same manner specified in material-ui [docs](https://material-ui.com/demos/snackbars): -```javascript -My Action - ]} - onClickAction={() => alert('Clicked on my action button.')} -> - - -``` - -However, notice that by passing `action` to `SnackbarProvider`, you’ll be adding action to all of the snackbars. To specify action for a single snackbar, use `options` argument of `enqueueSnackbar` method instead: -```javascript -this.props.enqueueSnackbar('Item moved to recently deleted folder.', { - variant: 'default', - action: , - onClickAction={() => alert('Clicked on my action button.')} -}) -``` -Use `onClickAction` prop to handle onClick event on snackbar action. The default behaviour of `onClickAction` is to dismiss the snackbar. Also, note that multiple actions for a snackbar is not supported by notistack. - -### Redux example: -You can use notistack to send snackbars from reducers. This has lots of applications but particularly useful when a network request fails. For more information check out notistack's [minimal redux example](https://codesandbox.io/s/github/iamhosseindhv/notistack/tree/master/examples/redux-example). +### Redux support: +You can use notistack to send snackbars from reducers. See notistack [documentation](https://iamhosseindhv.com/notistack#redux-example) for more info. ## Contribution Open an issue and your problem will be solved. diff --git a/index.d.ts b/index.d.ts index cba449c0..889a584c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,49 +1,46 @@ import * as React from 'react'; -import { SvgIconProps } from '@material-ui/core/SvgIcon'; import { SnackbarProps, SnackbarClassKey } from '@material-ui/core/Snackbar'; type Omit = Pick> +type ClassNameMap = Record; export type VariantType = 'default' | 'error' | 'success' | 'warning' | 'info'; export interface OptionsObject extends Omit { key?: string | number; variant?: VariantType; - persist?: boolean; + persist?: boolean; onClickAction?: Function; preventDuplicate?: boolean; } -type NotistackClassKey = 'variantSuccess' - | 'variantError' - | 'variantInfo' - | 'variantWarning'; +export type NotistackClassKey = 'variantSuccess' | 'variantError' | 'variantInfo' | 'variantWarning'; -// class keys for both MUI and notistack -export type CombinedClassKey = NotistackClassKey | SnackbarClassKey; +type CombinedClassKey = NotistackClassKey | SnackbarClassKey; -export interface InjectedNotistackProps { +export interface withSnackbarProps { onPresentSnackbar: (variant: VariantType, message: string) => void; enqueueSnackbar: (message: string | React.ReactNode, options?: OptionsObject) => string | number | null; closeSnackbar: (key: string | number) => void } -export function withSnackbar

(component: React.ComponentType

): - React.ComponentClass> & { WrappedComponent: React.ComponentType

}; +export function withSnackbar

(component: React.ComponentType

): + React.ComponentClass> & { WrappedComponent: React.ComponentType

}; + export function useSnackbar(): InjectedNotistackProps; export type ClassNameMap = Record; -/** all MUI props, including class keys for notistack and MUI with additional notistack props */ -export interface SnackbarProviderProps - extends Omit { +// all material-ui props, including class keys for notistack and material-ui with additional notistack props +export interface SnackbarProviderProps extends Omit { classes?: Partial>; - maxSnack: number; - iconVariant?: React.ComponentType; + maxSnack?: number; + iconVariant?: Partial>; hideIconVariant?: boolean; onClickAction?: Function; preventDuplicate?: boolean; + dense?: boolean; } export const SnackbarProvider: React.ComponentType; diff --git a/package-lock.json b/package-lock.json index cf3b6d85..912e994d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "notistack", - "version": "0.4.3", + "version": "0.5.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f1158e13..234ed6f2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "notistack", - "version": "0.4.3", - "description": "Highly customisable notification snackbars (Highly customizable notification snackbars (toasts) that can be stacked on top of each other) that can be stacked on top of each other", + "version": "0.5.1", + "description": "Highly customizable notification snackbars (toasts) that can be stacked on top of each other", "main": "build/index", "license": "MIT", "author": { @@ -62,7 +62,6 @@ "react", "javascript", "material-ui", - "material ui", "toast", "redux", "snackbar", diff --git a/src/SnackbarItem/SnackbarItem.js b/src/SnackbarItem/SnackbarItem.js index a27f6447..ce7d6e60 100644 --- a/src/SnackbarItem/SnackbarItem.js +++ b/src/SnackbarItem/SnackbarItem.js @@ -59,7 +59,15 @@ class SnackbarItem extends Component { } = this.props; const { action: contentAction, className, ...otherContentProps } = ContentProps; - const { key, variant = 'default', persist, ...singleSnackProps } = snack; + + const { + key, + persist, + variant = 'default', + onClickAction: singleOnClickAction, + ...singleSnackProps + } = snack; + const icon = iconVariant[variant]; const contentProps = { @@ -68,7 +76,7 @@ class SnackbarItem extends Component { action: snack.action || contentAction || action, }; - let onClickHandler = snack.action ? snack.onClickAction : onClickAction; + let onClickHandler = snack.action ? singleOnClickAction : onClickAction; onClickHandler = onClickHandler || this.handleClose(key); const anchOrigin = singleSnackProps.anchorOrigin || anchorOrigin; diff --git a/src/SnackbarItem/SnackbarItem.styles.js b/src/SnackbarItem/SnackbarItem.styles.js index 1a205a1f..db01a331 100644 --- a/src/SnackbarItem/SnackbarItem.styles.js +++ b/src/SnackbarItem/SnackbarItem.styles.js @@ -13,7 +13,7 @@ export const styles = theme => ({ fontFamily: theme.typography.fontFamily, }, lessPadding: { - paddingLeft: theme.spacing.unit * 2.5, + paddingLeft: 8 * 2.5, }, variantSuccess: { backgroundColor: green[600], diff --git a/src/SnackbarProvider.js b/src/SnackbarProvider.js index 26b15705..bae0bd90 100644 --- a/src/SnackbarProvider.js +++ b/src/SnackbarProvider.js @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { SnackbarContext, SnackbarContextNext } from './SnackbarContext'; import { TRANSITION_DELAY, TRANSITION_DOWN_DURATION, MESSAGES } from './utils/constants'; @@ -7,9 +7,16 @@ import warning from './utils/warning'; class SnackbarProvider extends Component { - state = { - snacks: [], - }; + constructor(props) { + super(props); + this.state = { + snacks: [], + contextValue: { + handleEnqueueSnackbar: this.handleEnqueueSnackbar, + handleCloseSnackbar: this.handleDismissSnack, + }, + }; + } queue = []; @@ -17,9 +24,13 @@ class SnackbarProvider extends Component { const { snacks } = this.state; return snacks.map((item, i) => { let index = i; - let offset = 20; + const { view: viewOffset, snackbar: snackbarOffset } = this.props.dense + ? { view: 0, snackbar: 4 } + : { view: 20, snackbar: 12 }; + let offset = viewOffset; while (snacks[index - 1]) { - offset += snacks[index - 1].height + 16; + const snackHeight = snacks[index - 1].height || 48; + offset += snackHeight + snackbarOffset; index -= 1; } return offset; @@ -206,29 +217,24 @@ class SnackbarProvider extends Component { }; render() { - const { children, maxSnack, ...props } = this.props; - const { snacks } = this.state; + const { children, maxSnack, dense, ...props } = this.props; + const { contextValue, snacks } = this.state; return ( - - - {children} - {snacks.map((snack, index) => ( - - ))} - + + {children} + {snacks.map((snack, index) => ( + + ))} ); @@ -236,22 +242,23 @@ class SnackbarProvider extends Component { } SnackbarProvider.propTypes = { - children: PropTypes.element.isRequired, + children: PropTypes.node.isRequired, /** - * Maximum snackbars that can be stacked - * on top of one another + * Maximum snackbars that can be stacked on top of one another. */ + dense: PropTypes.bool, maxSnack: PropTypes.number, + preventDuplicate: PropTypes.bool, onClose: PropTypes.func, onExited: PropTypes.func, - preventDuplicate: PropTypes.bool, }; SnackbarProvider.defaultProps = { maxSnack: 3, + dense: false, + preventDuplicate: false, onClose: undefined, onExited: undefined, - preventDuplicate: false, }; export default SnackbarProvider; diff --git a/src/withSnackbar.js b/src/withSnackbar.js index 4c33c9e4..4c3f14f7 100644 --- a/src/withSnackbar.js +++ b/src/withSnackbar.js @@ -1,21 +1,27 @@ import React from 'react'; import { SnackbarContext, SnackbarContextNext } from './SnackbarContext'; -const withSnackbar = Component => props => ( - - {handlePresentSnackbar => ( - - {context => ( - - )} - - )} - -); +const withSnackbar = (Component) => { + const WrappedComponent = props => ( + + {handlePresentSnackbar => ( + + {context => ( + + )} + + )} + + ); + + WrappedComponent.displayName = `withSnackbar(${Component.displayName || Component.name || 'Component'})`; + + return WrappedComponent; +}; export default withSnackbar;