├── examples
├── .eslintrc
├── .prettierrc
├── src
│ ├── components
│ │ ├── Footer.module.css
│ │ ├── Footer.js
│ │ ├── examples
│ │ │ ├── customChildComponent
│ │ │ │ ├── CustomSnackbarComponent.js
│ │ │ │ └── CustomChildComponentExample.js
│ │ │ ├── customComponent
│ │ │ │ ├── CustomSnackbarComponent.js
│ │ │ │ └── CustomComponentExample.js
│ │ │ ├── base
│ │ │ │ └── BaseExample.js
│ │ │ ├── top
│ │ │ │ └── TopExample.js
│ │ │ ├── theme
│ │ │ │ └── ThemeExample.js
│ │ │ ├── sticky
│ │ │ │ └── StickyExample.js
│ │ │ ├── customTimeout
│ │ │ │ └── CustomTimeoutExample.js
│ │ │ ├── dismissable
│ │ │ │ └── DismissableExample.js
│ │ │ ├── pauseOnHover
│ │ │ │ └── PauseOnHoverExample.js
│ │ │ ├── noProgressBar
│ │ │ │ └── NoProgressBarExample.js
│ │ │ ├── customAnimationTimeout
│ │ │ │ └── CustomAnimationTimeoutExample.js
│ │ │ ├── customComponentData
│ │ │ │ ├── CustomSnackbarComponent.js
│ │ │ │ └── CustomComponentDataExample.js
│ │ │ └── demo
│ │ │ │ └── DemoExample.js
│ │ ├── SourceFile.js
│ │ ├── Layout.module.css
│ │ ├── Header.js
│ │ ├── Header.module.css
│ │ ├── Navigation.module.css
│ │ ├── Layout.js
│ │ └── Navigation.js
│ └── pages
│ │ ├── api.module.css
│ │ ├── examples
│ │ ├── base.js
│ │ ├── position.js
│ │ ├── theme.js
│ │ ├── customTimeout.js
│ │ ├── pauseOnHover.js
│ │ ├── customAnimationTimeout.js
│ │ ├── noProgressBar.js
│ │ ├── sticky.js
│ │ ├── dismissable.js
│ │ ├── customComponent.js
│ │ ├── customComponentData.js
│ │ └── customChildComponent.js
│ │ ├── index.js
│ │ ├── gettingStarted.js
│ │ └── api.js
├── static
│ └── favicon.ico
├── gatsby-config.js
├── gatsby-node.js
├── package.json
└── .gitignore
├── .gitignore
├── .prettierrc
├── .babelrc
├── snackbar.gif
├── src
├── index.js
├── themes.js
├── icons.js
├── Snackbar.test.js
├── SnackbarContainer.js
├── SnackbarContext.js
├── Snackbar.js
└── SnackbarContext.test.js
├── jest.config.js
├── .circleci
└── config.yml
├── .eslintrc.js
├── rollup.config.js
├── LICENSE
├── CHANGELOG.md
├── package.json
└── README.md
/examples/.eslintrc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | dist
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/examples/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"]
3 | }
4 |
--------------------------------------------------------------------------------
/snackbar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joeattardi/react-snackbar-alert/HEAD/snackbar.gif
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as Snackbar } from './Snackbar';
2 | export * from './SnackbarContext';
3 |
--------------------------------------------------------------------------------
/examples/src/components/Footer.module.css:
--------------------------------------------------------------------------------
1 | #footer {
2 | padding: 0.5em;
3 | background: #BDC5B8;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joeattardi/react-snackbar-alert/HEAD/examples/static/favicon.ico
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rootDir: 'src',
3 | setupFilesAfterEnv: ['@testing-library/react/cleanup-after-each']
4 | };
5 |
--------------------------------------------------------------------------------
/examples/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pathPrefix: '/react-snackbar-alert',
3 | plugins: ['gatsby-plugin-react-helmet']
4 | };
5 |
--------------------------------------------------------------------------------
/examples/src/pages/api.module.css:
--------------------------------------------------------------------------------
1 | .api {
2 | border-collapse: collapse;
3 | }
4 |
5 | .api th {
6 | text-align: left;
7 | padding: 0.5em;
8 | }
9 |
10 | .api tbody tr {
11 | border-top: 1px solid #EEEEEE;
12 | }
13 |
14 | .api tbody tr td {
15 | padding: 0.5em;
16 | }
--------------------------------------------------------------------------------
/examples/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import styles from './Footer.module.css';
4 |
5 | export default function Footer() {
6 | return (
7 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customChildComponent/CustomSnackbarComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Snackbar } from 'react-snackbar-alert';
3 |
4 | export default function CustomSnackbarComponent(props) {
5 | return (
6 |
7 | {props.message}
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/examples/gatsby-node.js:
--------------------------------------------------------------------------------
1 | exports.onCreateWebpackConfig = function onCreateWebpackConfig({ actions, stage, loaders }) {
2 | if (stage === 'develop') {
3 | actions.setWebpackConfig({
4 | module: {
5 | rules: [
6 | {
7 | test: /react-hot-loader/,
8 | use: [
9 | loaders.js()
10 | ]
11 | }
12 | ]
13 | }
14 | })
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customComponent/CustomSnackbarComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function CustomSnackbarComponent({ message }) {
4 | return (
5 |
13 | {message}
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/examples/src/components/SourceFile.js:
--------------------------------------------------------------------------------
1 | import escape from 'escape-html';
2 | import React from 'react';
3 |
4 | export default function SourceFile({ src, language }) {
5 | return (
6 |
7 |
11 |
12 | );
13 | }
14 |
15 | SourceFile.defaultProps = {
16 | language: 'jsx'
17 | };
18 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Javascript Node CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details
4 | #
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | - image: circleci/node:7.10
10 |
11 | working_directory: ~/repo
12 |
13 | branches:
14 | ignore:
15 | - gh-pages
16 |
17 | steps:
18 | - checkout
19 | - run: npm install
20 | - run: npm run build
21 | - run: npm run lint
22 | - run: npm test
--------------------------------------------------------------------------------
/examples/src/components/Layout.module.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: Arial, Helvetica, sans-serif;
4 | }
5 |
6 | button {
7 | background: #92B4A7;
8 | color: #FFFFFF;
9 | font-size: 1em;
10 | padding: 0.5em 0.75em;
11 | border-radius: 5px;
12 | border: none;
13 | cursor: pointer;
14 | }
15 |
16 | #main {
17 | flex-grow: 1;
18 | display: flex;
19 | flex-direction: column;
20 | max-width: 100%;
21 | }
22 |
23 | #main #content-container {
24 | padding: 0.5em;
25 | flex-grow: 1;
26 | }
27 |
28 | #body {
29 | display: flex;
30 | }
--------------------------------------------------------------------------------
/examples/src/pages/examples/base.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import BaseExample from '../../components/examples/base/BaseExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/base/BaseExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function BasePage() {
11 | return (
12 |
13 | The default timeout is 3 seconds.
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/examples/src/components/examples/base/BaseExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function BaseExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/top/TopExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function TopExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/theme/ThemeExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function ThemeExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Great success!',
17 | theme: 'success'
18 | });
19 | }
20 |
21 | return (
22 |
23 | Show Snackbar
24 |
25 | );
26 | });
27 |
--------------------------------------------------------------------------------
/examples/src/components/examples/sticky/StickyExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function StickyExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customTimeout/CustomTimeoutExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function CustomTimeoutExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/dismissable/DismissableExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function DismissableExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/pauseOnHover/PauseOnHoverExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function PauseOnHoverExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/noProgressBar/NoProgressBarExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function NoProgressBarExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/src/themes.js:
--------------------------------------------------------------------------------
1 | import { ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from './icons';
2 |
3 | export const themes = {
4 | default: {
5 | color: 'rgba(0, 0, 0, 0.9)'
6 | },
7 | info: {
8 | color: 'rgba(0, 180, 255, 0.95)',
9 | icon: InfoIcon
10 | },
11 | success: {
12 | color: 'rgba(127, 175, 27, 0.95)',
13 | icon: SuccessIcon
14 | },
15 | warning: {
16 | color: 'rgba(240, 123, 6, 0.95)',
17 | icon: WarningIcon
18 | },
19 | error: {
20 | color: 'rgba(211, 0, 0, 0.95)',
21 | icon: ErrorIcon
22 | }
23 | };
24 |
25 | export function getTheme(name) {
26 | return themes[name] || themes.default;
27 | }
28 |
29 | export const themeNames = Object.keys(themes);
30 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customAnimationTimeout/CustomAnimationTimeoutExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | export default function CustomAnimationTimeoutExample() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | const Container = wrapComponent(function({ createSnackbar }) {
14 | function showSnackbar() {
15 | createSnackbar({
16 | message: 'Hello Snackbar!'
17 | });
18 | }
19 |
20 | return (
21 |
22 | Show Snackbar
23 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customComponent/CustomComponentExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | import CustomSnackbarComponent from './CustomSnackbarComponent';
6 |
7 | export default function CustomComponentExample() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | const Container = wrapComponent(function({ createSnackbar }) {
16 | function showSnackbar() {
17 | createSnackbar({
18 | message: 'Hello Snackbar!'
19 | });
20 | }
21 |
22 | return (
23 |
24 | Show Snackbar
25 |
26 | );
27 | });
28 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customChildComponent/CustomChildComponentExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | import CustomSnackbarComponent from './CustomSnackbarComponent';
6 |
7 | export default function CustomChildComponentExample() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | const Container = wrapComponent(function({ createSnackbar }) {
16 | function showSnackbar() {
17 | createSnackbar({
18 | message: 'Hello Snackbar!'
19 | });
20 | }
21 |
22 | return (
23 |
24 | Show Snackbar
25 |
26 | );
27 | });
28 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "jest": true
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:react/recommended",
10 | "prettier"
11 | ],
12 | "globals": {
13 | "Atomics": "readonly",
14 | "SharedArrayBuffer": "readonly"
15 | },
16 | "parserOptions": {
17 | "ecmaFeatures": {
18 | "jsx": true
19 | },
20 | "ecmaVersion": 2018,
21 | "sourceType": "module"
22 | },
23 | "plugins": [
24 | "react",
25 | "prettier"
26 | ],
27 | "settings": {
28 | "react": {
29 | "version": "detect"
30 | }
31 | },
32 | "rules": {
33 | "prettier/prettier": "error"
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customComponentData/CustomSnackbarComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Snackbar } from 'react-snackbar-alert';
3 |
4 | export default function CustomSnackbarComponent(props) {
5 | return (
6 |
7 |
8 |
{props.message}
9 |
19 | {props.data.action}
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/examples/src/components/examples/customComponentData/CustomComponentDataExample.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
4 |
5 | import CustomSnackbarComponent from './CustomSnackbarComponent';
6 |
7 | export default function CustomComponentDataExample() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | const Container = wrapComponent(function({ createSnackbar }) {
18 | function showSnackbar() {
19 | createSnackbar({
20 | data: {
21 | action: 'Retry'
22 | },
23 | theme: 'error',
24 | message: 'Connection Error'
25 | });
26 | }
27 |
28 | return (
29 |
30 | Show Snackbar
31 |
32 | );
33 | });
34 |
--------------------------------------------------------------------------------
/examples/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import styles from './Header.module.css';
4 |
5 | export default function Header({ onMenuToggle }) {
6 | return (
7 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-snackbar-alert-examples",
3 | "private": true,
4 | "description": "Docs and examples for react-snackbar-alert",
5 | "version": "0.1.0",
6 | "scripts": {
7 | "build": "gatsby build",
8 | "develop": "gatsby develop",
9 | "format": "prettier --write src/**/*.{js,jsx}",
10 | "start": "npm run develop",
11 | "serve": "gatsby serve",
12 | "deploy": "gatsby build --prefix-paths && gh-pages -d public -m \"[ci skip] update examples site\""
13 | },
14 | "dependencies": {
15 | "escape-html": "^1.0.3",
16 | "gatsby": "^2.6.0",
17 | "gatsby-plugin-react-helmet": "^3.0.12",
18 | "prismjs": "^1.16.0",
19 | "react": "^16.8.6",
20 | "react-dom": "^16.8.6",
21 | "react-helmet": "^5.2.1",
22 | "react-snackbar-alert": "^2.1.0",
23 | "react-tabs": "^3.0.0",
24 | "styled-components": "^4.2.0"
25 | },
26 | "devDependencies": {
27 | "gh-pages": "^2.0.1",
28 | "prettier": "1.17.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import commonjs from 'rollup-plugin-commonjs';
3 | import resolve from 'rollup-plugin-node-resolve';
4 | import { uglify } from 'rollup-plugin-uglify';
5 |
6 | export default {
7 | input: 'src/index.js',
8 | external: ['react', 'react-dom', 'styled-components'],
9 | output: {
10 | format: 'umd',
11 | name: 'react-snackbar-alert',
12 | file: 'dist/react-snackbar-alert.min.js',
13 | globals: {
14 | react: 'React',
15 | 'react-dom': 'ReactDOM',
16 | 'styled-components': 'styled'
17 | }
18 | },
19 | plugins: [
20 | babel({
21 | exclude: 'node_modules/**'
22 | }),
23 | resolve({
24 | browser: true
25 | }),
26 | commonjs({
27 | namedExports: {
28 | 'node_modules/prop-types/index.js': [
29 | 'bool',
30 | 'element',
31 | 'func',
32 | 'oneOfType'
33 | ]
34 | }
35 | }),
36 | uglify()
37 | ]
38 | };
39 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/position.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import TopExample from '../../components/examples/top/TopExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/top/TopExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function PositionPage() {
11 | return (
12 |
13 |
14 | By default, snackbars appear at the bottom center of the screen. This
15 | can be changed via the position prop on the
16 | SnackbarProvider. Valid values are:
17 |
18 |
19 |
20 | top
21 | top-left
22 | top-right
23 | bottom
24 | bottom-left
25 | bottom-right
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/examples/src/components/Header.module.css:
--------------------------------------------------------------------------------
1 | #header {
2 | background: #2F2F2F;
3 | color: #FFFFFF;
4 | padding: 0.5em;
5 | display: flex;
6 | align-items: center;
7 | }
8 |
9 | #header h1 {
10 | margin: 0;
11 | font-weight: normal;
12 | font-size: 1.5em;
13 | flex-grow: 1;
14 | }
15 |
16 | #header img {
17 | vertical-align: middle;
18 | }
19 |
20 | #header #menu-button {
21 | display: none;
22 | outline: none;
23 |
24 | padding: 0 0.25em;
25 | background: transparent;
26 | border: 1px solid #FFFFFF;
27 | }
28 |
29 | #header #header-links {
30 | display: flex;
31 | }
32 |
33 | #header #header-links div {
34 | margin: 0 0.5em;
35 | }
36 |
37 | #header #header-links div:first-child {
38 | margin: 0;
39 | }
40 |
41 | @media (max-width: 700px) {
42 | #header {
43 | flex-direction: column;
44 | align-items: flex-start;
45 | }
46 |
47 | #header #menu-button {
48 | display: inline;
49 | }
50 |
51 | #header #header-links {
52 | margin-top: 0.5em;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/theme.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import ThemeExample from '../../components/examples/theme/ThemeExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/theme/ThemeExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function ThemePage() {
11 | return (
12 |
13 |
14 | By default, snackbars have a black background and no icon. This can be
15 | changed by specifying the theme property on the object
16 | passed to createSnackbar.
17 |
18 |
19 | The supported themes are:
20 |
21 |
22 | default
23 | info
24 | success
25 | warning
26 | error
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/examples/src/components/Navigation.module.css:
--------------------------------------------------------------------------------
1 | #sidebar {
2 | background: #BDC5B8;
3 | padding: 0.5em;
4 | width: 16em;
5 | min-width: 16em;
6 | min-height: calc(100vh - 4em);
7 | }
8 |
9 | #sidebar h2 {
10 | font-size: 1.2em;
11 | margin: 0;
12 | }
13 |
14 | #sidebar ul {
15 | list-style: none;
16 | padding: 0;
17 | }
18 |
19 | #sidebar ul li {
20 | margin: 0.5em;
21 | }
22 |
23 | #sidebar ul li a {
24 | text-decoration: none;
25 | color: #435058;
26 | border-left: 5px solid transparent;
27 | padding-left: 0.25em;
28 | }
29 |
30 | #sidebar ul li a.active {
31 | font-weight: bold;
32 | border-left: 5px solid #435058;
33 | }
34 |
35 | #sidebar ul a:hover {
36 | text-decoration: underline;
37 | }
38 |
39 | @media (max-width: 700px) {
40 | #sidebar {
41 | position: absolute;
42 | transition: all 0.25s ease-out;
43 | min-height: inherit;
44 | }
45 |
46 | #sidebar.closed {
47 | left: -17em;
48 | opacity: 0;
49 | }
50 |
51 | #sidebar.open {
52 | left: 0;
53 | opacity: 1;
54 | }
55 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Joe Attardi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/customTimeout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import SourceFile from '../../components/SourceFile';
5 |
6 | import CustomTimeoutExample from '../../components/examples/customTimeout/CustomTimeoutExample';
7 |
8 | import src from '!!raw-loader!../../components/examples/customTimeout/CustomTimeoutExample.js';
9 |
10 | export default function CustomTimeoutPage() {
11 | return (
12 |
13 |
14 | By default, a snackbar is removed after 3 seconds. A custom timeout can
15 | be specified in one of two ways:
16 |
17 |
18 |
19 |
20 | Setting the timeout prop on the{' '}
21 | SnackbarProvider for all snackbars
22 |
23 |
24 | Setting the timeout property on the object passed to{' '}
25 | createSnackbar for a specific snackbar
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/examples/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import DemoExample from '../components/examples/demo/DemoExample';
4 |
5 | import Layout from '../components/Layout';
6 |
7 | export default function IndexPage() {
8 | return (
9 |
10 | Welcome
11 |
12 | React Snackbar Alert is a very simple library for "snackbar" style
13 | notifications. It's a quick and lightweight way to let your users know
14 | what's going on in your app.
15 |
16 |
17 | Demo
18 |
19 |
20 | Features
21 |
22 | Easy to use
23 | Supported in all modern browsers (plus IE11)
24 | Notifications can be restyled and extended
25 |
26 | Arbitrary custom data can be specified to create rich, interactive
27 | notifications
28 |
29 |
30 |
31 | Requirements
32 |
33 | React Snackbar Alert requires React 16.8 or newer. It also requires
34 | styled-components 4 to be installed.
35 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/pauseOnHover.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import PauseOnHoverExample from '../../components/examples/pauseOnHover/PauseOnHoverExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/pauseOnHover/PauseOnHoverExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function PauseOnHoverPage() {
11 | return (
12 |
13 |
14 | Pause on Hover will pause a snackbar's timeout when the mouse is hovered
15 | over it. This is disabled by default. It can be enabled in one of two
16 | ways:
17 |
18 |
19 |
20 |
21 | Setting the pauseOnHover prop on the{' '}
22 | SnackbarProvider for all snackbars
23 |
24 |
25 | Setting the pauseOnHover property on the object passed to{' '}
26 | createSnackbar for a specific snackbar
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/customAnimationTimeout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import SourceFile from '../../components/SourceFile';
5 |
6 | import CustomAnimationTimeoutExample from '../../components/examples/customAnimationTimeout/CustomAnimationTimeoutExample';
7 |
8 | import src from '!!raw-loader!../../components/examples/customAnimationTimeout/CustomAnimationTimeoutExample.js';
9 |
10 | export default function CustomAnimationTimeoutPage() {
11 | return (
12 |
13 |
14 | The snackbars are displayed with a simple animation. The duration of
15 | this animation can be changed in two ways:
16 |
17 |
18 |
19 |
20 | Specifying the animationTimeout prop on the{' '}
21 | SnackbarProvider component.
22 |
23 |
24 | Specifying the animationTimeout property on the object
25 | passed to createSnackbar
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/noProgressBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import NoProgressBarExample from '../../components/examples/noProgressBar/NoProgressBarExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/noProgressBar/NoProgressBarExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function BasePage() {
11 | return (
12 |
13 |
14 | By default, a progress bar is shown to indicate the time before the
15 | snackbar is removed. This can be done in one of two ways:
16 |
17 |
18 |
19 |
20 | Setting the progressBar prop on the{' '}
21 | SnackbarProvider to false to disable for all
22 | snackbars
23 |
24 |
25 | Setting the progressBar property on the object passed to{' '}
26 | createSnackbar to false to disable for a
27 | specific snackbar
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/sticky.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import StickyExample from '../../components/examples/sticky/StickyExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/sticky/StickyExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function StickyPage() {
11 | return (
12 |
13 |
14 | A snackbar can be sticky, which means that it is not automatically
15 | removed. This can be done in one of two ways:
16 |
17 |
18 |
19 |
20 | Setting the sticky prop on the{' '}
21 | SnackbarProvider to make all snackbars sticky
22 |
23 |
24 | Setting the sticky property on the object passed to{' '}
25 | createSnackbar to make a specific snackbar sticky
26 |
27 |
28 |
29 |
30 | It is recommended that sticky snackbars are also user-dismissable,
31 | otherwise the snackbar can never be removed.
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/dismissable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../../components/Layout';
4 | import DismissableExample from '../../components/examples/dismissable/DismissableExample';
5 |
6 | import src from '!!raw-loader!../../components/examples/dismissable/DismissableExample.js';
7 |
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | export default function BasePage() {
11 | return (
12 |
13 |
14 | Snackbar notifications will automatically disappear after the timeout
15 | has passed. They can also, however, be configured so that they can be
16 | manually dismissed before the timeout passes. This can be done in one of
17 | two ways:
18 |
19 |
20 |
21 |
22 | Setting the dismissable prop on the{' '}
23 | SnackbarProvider (to make all snackbars dismissable)
24 |
25 |
26 | Setting the dismissable property on the object passed to{' '}
27 | createSnackbar (to make only that snackbar dismissable)
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/examples/src/components/Layout.js:
--------------------------------------------------------------------------------
1 | import Prism from 'prismjs';
2 | import 'prismjs/themes/prism.css';
3 | import 'prismjs/components/prism-jsx.min';
4 | import React, { useEffect, useState } from 'react';
5 | import { Helmet } from 'react-helmet';
6 |
7 | import Footer from './Footer';
8 | import Header from './Header';
9 | import Navigation from './Navigation';
10 |
11 | import styles from './Layout.module.css';
12 |
13 | import 'react-tabs/style/react-tabs.css';
14 |
15 | export default function Layout({ children, title }) {
16 | useEffect(() => {
17 | Prism.highlightAll();
18 | });
19 |
20 | const [menuVisible, setMenuVisible] = useState(false);
21 |
22 | return (
23 |
24 |
25 | {title ? `${title} |` : ''} React Snackbar Alert
26 |
30 |
31 |
setMenuVisible(!menuVisible)} />
32 |
33 |
34 |
35 |
36 |
{title}
37 | {children}
38 |
39 |
40 |
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/customComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
3 |
4 | import customComponentExampleSrc from '!!raw-loader!../../components/examples/customComponent/CustomComponentExample.js';
5 | import customSnackbarComponentSrc from '!!raw-loader!../../components/examples/customComponent/CustomSnackbarComponent.js';
6 |
7 | import CustomComponentExample from '../../components/examples/customComponent/CustomComponentExample';
8 |
9 | import Layout from '../../components/Layout';
10 | import SourceFile from '../../components/SourceFile';
11 |
12 | export default function CustomComponentPage() {
13 | return (
14 |
15 |
16 | For greater customization of the snackbar component, a custom component
17 | can be used. Create your custom snackbar component and pass it as the{' '}
18 | component prop to the SnackbarProvider. The
19 | component will receive a message prop with the message to
20 | display.
21 |
22 |
23 |
24 |
25 |
26 |
27 | CustomComponentExample.js
28 | CustomSnackbarComponent.js
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/examples/src/pages/gettingStarted.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../components/Layout';
4 | import SourceFile from '../components/SourceFile';
5 |
6 | import src from '!!raw-loader!../components/examples/base/BaseExample';
7 |
8 | export default function GettingStartedPage() {
9 | return (
10 |
11 | Prerequisites
12 |
13 | React Snackbar Alert uses the Context API and Hooks. This means it
14 | requires React 16.8.0 or newer.
15 |
16 |
17 |
18 | This package requires react, react-dom, and{' '}
19 | styled-components to be installed in your project.
20 |
21 |
22 | $ npm install --save react react-dom styled-components
23 |
24 | Install the package
25 | $ npm install --save react-snackbar-alert
26 |
27 | Add to your app
28 |
29 |
30 | Import the SnackbarProvider component and the{' '}
31 | wrapComponent helper
32 |
33 |
34 | Wrap your entire application in the SnackbarProvider and
35 | pass any configuration to it
36 |
37 |
38 | To create snackbars from a component, wrap it via the{' '}
39 | wrapComponent helper. This will give it a{' '}
40 | createSnackbar prop.
41 |
42 |
43 | Call createSnackbar to create snackbars
44 |
45 |
46 |
47 | Example usage
48 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/customComponentData.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
3 |
4 | import customComponentDataExampleSrc from '!!raw-loader!../../components/examples/customComponentData/CustomComponentDataExample.js';
5 | import customSnackbarComponentSrc from '!!raw-loader!../../components/examples/customComponentData/CustomSnackbarComponent.js';
6 |
7 | import CustomComponentDataExample from '../../components/examples/customComponentData/CustomComponentDataExample';
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | import Layout from '../../components/Layout';
11 |
12 | export default function CustomComponentDataDocumentation() {
13 | return (
14 |
15 |
16 | You can also create more rich snackbar notifications by passing
17 | arbitrary data that can be used when rendering a custom snackbar
18 | component.
19 |
20 |
21 |
22 | Simply pass a data property in the object passed to{' '}
23 | createSnackbar, then reference the data attributes in your
24 | custom component's render function.
25 |
26 |
27 |
28 |
29 |
30 |
31 | CustomComponentDataExample.js
32 | CustomSnackbarComponent.js
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/examples/src/pages/examples/customChildComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
3 |
4 | import customChildComponentExampleSrc from '!!raw-loader!../../components/examples/customChildComponent/CustomChildComponentExample.js';
5 | import customSnackbarComponentSrc from '!!raw-loader!../../components/examples/customChildComponent/CustomSnackbarComponent.js';
6 |
7 | import CustomChildComponentExample from '../../components/examples/customChildComponent/CustomChildComponentExample';
8 | import SourceFile from '../../components/SourceFile';
9 |
10 | import Layout from '../../components/Layout';
11 |
12 | export default function CustomChildComponentPage() {
13 | return (
14 |
15 |
16 | You can also extend the default Snackbar component. This
17 | allows you to keep the default styling and animation, but also add your
18 | own child content to the snackbar.
19 |
20 |
21 |
22 | To extend the default component, import the Snackbar{' '}
23 | component and use it in your custom component. Make sure to pass all
24 | received props along to the Snackbar component, or else the
25 | animations won't work.
26 |
27 |
28 |
29 |
30 |
31 |
32 | CustomChildComponentExample.js
33 | CustomSnackbarComponent.js
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [2.1.0] - 2019-06-09
8 | ### Added
9 | - Support for themes
10 |
11 | ## [2.0.1] - 2019-06-07
12 | ### Changed
13 | - The library is now bundled in UMD format, with its dependencies
14 |
15 | ## [2.0.0] - 2019-06-07
16 | ### Changed
17 | - All-new implementation that uses the React Context API instead of refs
18 |
19 | ## [1.9.0] - 2019-06-03
20 | ### Added
21 | - Support for Internet Explorer 11
22 |
23 | ## [1.8.0] - 2019-06-01
24 | ### Added
25 | - Support for changing the position of the snackbars on screen
26 |
27 | ## [1.7.1] - 2019-06-01
28 | ### Changed
29 | - Fixed progress bar having the wrong duration when using a custom timeout
30 |
31 | ## [1.7.0] - 2019-06-01
32 | ### Changed
33 | - All styling now done via `styled-components` - no longer need to import CSS. To prevent broken imports, the CSS file will
34 | remain in place for now, but will be removed at a later date.
35 |
36 | ## [1.6.0] - 2019-05-31
37 | ### Added
38 | - Support for pause on hover - a snackbar's timeout can be paused by hovering over it (disabled by default)
39 |
40 | ## [1.5.0] - 2019-05-29
41 | ### Added
42 | - Optional animated progress bar indicating the time before a snackbar is removed
43 | - Support for sticky snackbars - no automatic removal
44 |
45 | ### Changed
46 | - Custom snackbar components now will have the show and hide animations applied.
47 |
48 | ## [1.4.1] - 2019-05-29
49 | ### Changed
50 | - Don't include test files when publishing to npm
51 |
52 | ## [1.4.0] - 2019-05-29
53 | ### Added
54 | - Support for dismissable snackbar notifications
55 |
56 | ## [1.3.0] - 2019-05-26
57 | ### Added
58 | - Support for custom arbitrary data for a snackbar
59 |
60 | ## [1.2.0] - 2019-05-26
61 | ### Added
62 | - Ability to use a custom snackbar component
63 |
64 | ## [1.1.0] - 2019-05-25
65 | ### Added
66 | - Configurable show and hide animations
67 |
68 | ## [1.0.0] - 2019-05-23
69 | Initial release.
70 |
--------------------------------------------------------------------------------
/src/icons.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Svg = styled.svg`
5 | fill: currentColor;
6 | `;
7 |
8 | export function CloseIcon() {
9 | return (
10 |
11 |
15 |
16 | );
17 | }
18 |
19 | export function ErrorIcon() {
20 | return (
21 |
22 |
26 |
27 | );
28 | }
29 |
30 | export function InfoIcon() {
31 | return (
32 |
33 |
37 |
38 | );
39 | }
40 |
41 | export function SuccessIcon() {
42 | return (
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | export function WarningIcon() {
50 | return (
51 |
52 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/src/Snackbar.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import 'jest-dom/extend-expect';
4 |
5 | import Snackbar from './Snackbar';
6 |
7 | describe('Snackbar', () => {
8 | it('should render the message', () => {
9 | const { queryByText } = render( );
10 | expect(queryByText('Hello')).toBeInTheDocument();
11 | });
12 |
13 | it('should render the children', () => {
14 | const { queryByText } = render(Hello );
15 | expect(queryByText('Hello')).toBeInTheDocument();
16 | });
17 |
18 | it('should render the children if both children and message are specified', () => {
19 | const { queryByText } = render(Bar );
20 | expect(queryByText('Bar')).toBeInTheDocument();
21 | expect(queryByText('Foo')).not.toBeInTheDocument();
22 | });
23 |
24 | describe('progress bar', () => {
25 | it('should render the progress bar if the progressBar prop is true', () => {
26 | const { queryByRole } = render(
27 |
28 | );
29 | expect(queryByRole('progressbar')).toBeInTheDocument();
30 | });
31 |
32 | it('should not render the progress bar if the progressBar prop is false', () => {
33 | const { queryByRole } = render(
34 |
35 | );
36 | expect(queryByRole('progressbar')).not.toBeInTheDocument();
37 | });
38 |
39 | it('should not render the progress bar, even if the progressBar prop is true, if the sticky prop is true', () => {
40 | const { queryByRole } = render(
41 |
42 | );
43 | expect(queryByRole('progressbar')).not.toBeInTheDocument();
44 | });
45 | });
46 |
47 | describe('close button', () => {
48 | it('should render the close button if the dismissable prop is true', () => {
49 | const { queryByTitle } = render(
50 |
51 | );
52 | expect(queryByTitle('Close')).toBeInTheDocument();
53 | });
54 |
55 | it('should not render the close button if the dismissable prop is false', () => {
56 | const { queryByTitle } = render(
57 |
58 | );
59 | expect(queryByTitle('Close')).not.toBeInTheDocument();
60 | });
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-snackbar-alert",
3 | "version": "2.1.0",
4 | "description": "Simple snackbar-style notifications for React",
5 | "main": "dist/react-snackbar-alert.min.js",
6 | "files": [
7 | "dist"
8 | ],
9 | "scripts": {
10 | "prepublishOnly": "npm run build",
11 | "build": "rollup -c",
12 | "build:watch": "rollup -c -w",
13 | "clean": "rm -rf dist",
14 | "test": "jest",
15 | "test:watch": "jest --watch",
16 | "lint": "eslint src",
17 | "prettify": "prettier --write 'src/*.js' 'examples/src/**/*.js'"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/joeattardi/react-snackbar-alert.git"
22 | },
23 | "homepage": "https://joeattardi.github.io/react-snackbar-alert/",
24 | "bugs": "https://github.com/joeattardi/react-snackbar-alert/issues",
25 | "keywords": [
26 | "react",
27 | "snackbar",
28 | "alert",
29 | "notification",
30 | "notifications"
31 | ],
32 | "author": "Joe Attardi (https://joeattardi.codes)",
33 | "license": "MIT",
34 | "devDependencies": {
35 | "@babel/cli": "^7.4.4",
36 | "@babel/core": "^7.4.5",
37 | "@babel/preset-env": "^7.4.5",
38 | "@babel/preset-react": "^7.0.0",
39 | "@testing-library/react": "^8.0.1",
40 | "babel-jest": "^24.8.0",
41 | "eslint": "^5.16.0",
42 | "eslint-config-prettier": "^4.3.0",
43 | "eslint-plugin-prettier": "^3.1.0",
44 | "eslint-plugin-react": "^7.13.0",
45 | "husky": "^2.3.0",
46 | "jest": "^24.8.0",
47 | "jest-dom": "^3.4.0",
48 | "lint-staged": "^8.1.7",
49 | "prettier": "1.17.1",
50 | "react": "^16.8.0",
51 | "react-dom": "^16.8.0",
52 | "rollup": "^1.14.3",
53 | "rollup-plugin-babel": "^4.3.2",
54 | "rollup-plugin-commonjs": "^10.0.0",
55 | "rollup-plugin-node-resolve": "^5.0.1",
56 | "rollup-plugin-uglify": "^6.0.2",
57 | "styled-components": "^4.2.0"
58 | },
59 | "peerDependencies": {
60 | "react": "^16.8.0",
61 | "react-dom": "^16.8.0",
62 | "styled-components": "^4.2.0"
63 | },
64 | "dependencies": {
65 | "prop-types": "^15.7.2",
66 | "react-transition-group": "^4.0.1",
67 | "uuid": "^3.3.2"
68 | },
69 | "husky": {
70 | "hooks": {
71 | "pre-commit": "lint-staged"
72 | }
73 | },
74 | "lint-staged": {
75 | "src/*.js": [
76 | "prettier --write",
77 | "git add"
78 | ],
79 | "examples/src/**/*.js": [
80 | "prettier --write",
81 | "git add"
82 | ]
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/examples/src/components/Navigation.js:
--------------------------------------------------------------------------------
1 | import { Link } from 'gatsby';
2 | import React from 'react';
3 |
4 | import styles from './Navigation.module.css';
5 |
6 | export default function Navigation({ visible }) {
7 | return (
8 |
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-snackbar-alert
2 | Simple snackbar-style notifications for React
3 |
4 | [](https://npmjs.com/package/react-snackbar-alert)
5 |
6 | [](https://circleci.com/gh/joeattardi/react-snackbar-alert)
7 |
8 | 
9 |
10 | ## Prerequisites
11 |
12 | React Snackbar Alert requires React 16.8.0 or newer and styled-components 4 as peer dependencies:
13 |
14 | npm install --save react react-dom styled-components
15 |
16 | ## Installation
17 |
18 | npm install --save react-snackbar-alert
19 |
20 | ## API & Examples
21 |
22 | [https://joeattardi.github.io/react-snackbar-alert/](https://joeattardi.github.io/react-snackbar-alert/)
23 |
24 | ## Usage
25 |
26 | Snackbar notifications in 3 easy steps:
27 |
28 | 1. Import the `SnackbarProvider` component and the `wrapComponent` helper
29 | 2. Wrap your application in the `SnackbarProvider` and pass any desired configuration props
30 | 3. Wrap any components you want to create snackbars from by passing them to the `wrapComponent` helper. This will pass a `createSnackbar` prop to the passed component which can be used to show a snackbar notification.
31 |
32 | ### Example
33 |
34 | ```javascript
35 | import React from 'react';
36 |
37 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
38 |
39 | export default function App() {
40 | return (
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | const Container = wrapComponent(function({ createSnackbar }) {
48 | function showSnackbar() {
49 | createSnackbar({
50 | message: 'Helo Snackbar!'
51 | });
52 | }
53 |
54 | return (
55 |
56 | Show Snackbar
57 |
58 | )
59 | });
60 | ```
61 |
62 | ## Options
63 |
64 | ### `SnackbarProvider` Props
65 |
66 | The `SnackbarProvider` accepts the following props:
67 |
68 | Unless otherwise noted, any of these props can be overridden for a specific snackbar instance by adding that same property to the object passed to `createSnackbar`.
69 |
70 | - `animationTimeout` - The duration of the show and hide animations, in milliseconds (default: `500`)
71 | - `component` - A custom component to use instead of the built-in `Snackbar` component. **Cannot be overridden for a specific snackbar.**
72 | - `dismissable` - Whether or not the created snackbars can be manually dismissed by the user
73 | - `pauseOnHover` - Whether or not a snackbar's timeout should be paused when it is hovered over (default: false)
74 | - `position` - The position on screen to show the snackbars. One of `top`, `top-left`, `top-right`, `bottom`, `bottom-left`, `bottom-right` (default: `bottom`). **Cannot be overridden for a specific snackbar.**
75 | - `progressBar` - Whether or not to show an animated progress bar showing the time before a snackbar is removed (default: `true`)
76 | - `sticky` - Whether or not snackbars should be sticky (not automatically removed) (default: `false`)
77 | - `timeout` - The time before a snackbar is removed, in milliseconds (default: `3000`)
78 |
79 | ### `createSnackbar` Options
80 |
81 | The `createSnackbar` function accepts an options object, which can have the following properties:
82 |
83 | - `animationTimeout` - The duration of the show and hide animations
84 | - `data` - Custom data to use when rendering a custom snackbar
85 | - `dismissable` - Whether or not this snackbar can be manually dismissed by the user
86 | - `message` - The message to display
87 | - `pauseOnHover` - Whether or not to pause this snackbar on hover
88 | - `progressBar` - Whether or not to show the progress bar for this snackbar
89 | - `sticky` - Whether or not this snackbar should be sticky. Sticky snackbars are not automatically removed.
90 | - `theme` - The theme to use for this snackbar. One of `default`, `info`, `success`, `warning`, `error` (default: `default`)
91 | - `timeout` - The time before this snackbar is removed
92 |
--------------------------------------------------------------------------------
/src/SnackbarContainer.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import styled from 'styled-components';
4 | import { CSSTransition, TransitionGroup } from 'react-transition-group';
5 |
6 | import Snackbar from './Snackbar';
7 |
8 | function getLeft(position) {
9 | switch (position) {
10 | case 'top':
11 | case 'bottom':
12 | return 'calc(50% - 10em)';
13 | case 'top-left':
14 | case 'bottom-left':
15 | return '0.5em';
16 | case 'top-right':
17 | case 'bottom-right':
18 | return 'calc(100vw - 20.5em)';
19 | }
20 | }
21 |
22 | const Container = styled.div`
23 | position: fixed;
24 | width: 20em;
25 | top: ${props => (props.position.indexOf('top') === 0 ? 0 : 'inherit')};
26 | bottom: ${props => (props.position.indexOf('bottom') === 0 ? 0 : 'inherit')};
27 | left: ${props => getLeft(props.position)};
28 | z-index: 10000;
29 | `;
30 |
31 | const ChildContainer = styled.div`
32 | &.react-snackbar-alert__snackbar-container-enter {
33 | opacity: 0;
34 | transform: scaleY(0.1);
35 | }
36 |
37 | &.react-snackbar-alert__snackbar-container-enter-active {
38 | opacity: 1;
39 | transform: scaleY(1);
40 | transition: all ${props => props.animationTimeout}ms;
41 | }
42 |
43 | &.react-snackbar-alert__snackbar-container-exit {
44 | opacity: 1;
45 | transform: scaleY(1);
46 | }
47 |
48 | &.react-snackbar-alert__snackbar-container-exit-active {
49 | opacity: 0;
50 | transform: scaleY(0.1);
51 | transition: all ${props => props.animationTimeout}ms;
52 | }
53 | `;
54 |
55 | export default function SnackbarContainer({
56 | animationTimeout,
57 | component: Component,
58 | dismissable,
59 | notifications,
60 | onPause,
61 | onRemove,
62 | onResume,
63 | pauseOnHover,
64 | position,
65 | progressBar,
66 | sticky
67 | }) {
68 | const orderedNotifications =
69 | position.indexOf('top') === 0
70 | ? [...notifications].reverse()
71 | : notifications;
72 |
73 | return (
74 |
75 |
76 | {orderedNotifications.map(notification => (
77 |
82 |
87 | onRemove(notification)}
106 | onPause={() => onPause(notification)}
107 | onResume={() => onResume(notification)}
108 | pauseOnHover={
109 | typeof notification.pauseOnHover !== 'undefined'
110 | ? notification.pauseOnHover
111 | : pauseOnHover
112 | }
113 | message={notification.message}
114 | data={notification.data}
115 | position={position}
116 | />
117 |
118 |
119 | ))}
120 |
121 |
122 | );
123 | }
124 |
125 | SnackbarContainer.propTypes = {
126 | animationTimeout: PropTypes.number,
127 | component: PropTypes.elementType,
128 | dismissable: PropTypes.bool,
129 | notifications: PropTypes.array,
130 | onPause: PropTypes.func.isRequired,
131 | onRemove: PropTypes.func.isRequired,
132 | onResume: PropTypes.func.isRequired,
133 | pauseOnHover: PropTypes.bool,
134 | position: PropTypes.oneOf([
135 | 'top',
136 | 'top-left',
137 | 'top-right',
138 | 'bottom',
139 | 'bottom-left',
140 | 'bottom-right'
141 | ]),
142 | progressBar: PropTypes.bool,
143 | sticky: PropTypes.bool
144 | };
145 |
146 | SnackbarContainer.defaultProps = {
147 | animationTimeout: 250,
148 | component: Snackbar,
149 | dismissable: false,
150 | pauseOnHover: false,
151 | position: 'bottom',
152 | progressBar: true,
153 | sticky: false,
154 | timeout: 3000
155 | };
156 |
--------------------------------------------------------------------------------
/src/SnackbarContext.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { useContext } from 'react';
3 | import uuidv4 from 'uuid/v4';
4 |
5 | import Snackbar from './Snackbar';
6 | import SnackbarContainer from './SnackbarContainer';
7 |
8 | export const SnackbarContext = React.createContext();
9 |
10 | export const SnackbarConsumer = SnackbarContext.Consumer;
11 |
12 | export class SnackbarProvider extends React.Component {
13 | constructor(props) {
14 | super(props);
15 |
16 | this.timeouts = {};
17 | this.startTimes = {};
18 | this.paused = {};
19 |
20 | this.state = {
21 | notifications: []
22 | };
23 |
24 | this.create = this.create.bind(this);
25 | this.pause = this.pause.bind(this);
26 | this.remove = this.remove.bind(this);
27 | this.resume = this.resume.bind(this);
28 | }
29 |
30 | create(notification) {
31 | notification.key = uuidv4();
32 |
33 | if (typeof notification.timeout === 'undefined') {
34 | notification.timeout = this.props.timeout;
35 | }
36 |
37 | if (typeof notification.sticky === 'undefined') {
38 | notification.sticky = this.props.sticky;
39 | }
40 |
41 | this.setState(
42 | {
43 | notifications: [...this.state.notifications, notification]
44 | },
45 | () => {
46 | if (!notification.sticky) {
47 | this.startTimes[notification.key] = Date.now();
48 | const timeout = setTimeout(() => {
49 | this.remove(notification);
50 | }, notification.timeout);
51 | this.timeouts[notification.key] = timeout;
52 | }
53 | }
54 | );
55 | }
56 |
57 | remove(notification) {
58 | clearTimeout(this.timeouts[notification.key]);
59 | this.setState({
60 | notifications: this.state.notifications.filter(n => n !== notification)
61 | });
62 |
63 | delete this.timeouts[notification.key];
64 | delete this.paused[notification.key];
65 | delete this.startTimes[notification.key];
66 | }
67 |
68 | pause(notification) {
69 | if (!notification.sticky) {
70 | clearTimeout(this.timeouts[notification.key]);
71 | delete this.timeouts[notification.key];
72 |
73 | let timeout = this.paused[notification.key] || notification.timeout;
74 |
75 | const timeElapsed = Date.now() - this.startTimes[notification.key];
76 | const timeRemaining = timeout - timeElapsed;
77 | this.paused[notification.key] = timeRemaining;
78 | }
79 | }
80 |
81 | resume(notification) {
82 | if (!notification.sticky) {
83 | const timeRemaining = this.paused[notification.key];
84 | this.startTimes[notification.key] = Date.now();
85 | const timeout = setTimeout(() => {
86 | this.remove(notification);
87 | }, timeRemaining);
88 | this.timeouts[notification.key] = timeout;
89 | }
90 | }
91 |
92 | componentWillUnmount() {
93 | Object.keys(this.timeouts).forEach(key => {
94 | clearTimeout(this.timeouts[key]);
95 | });
96 | }
97 |
98 | render() {
99 | return (
100 |
105 |
119 | {this.props.children}
120 |
121 | );
122 | }
123 | }
124 |
125 | SnackbarProvider.defaultProps = {
126 | animationTimeout: 250,
127 | component: Snackbar,
128 | dismissable: false,
129 | pauseOnHover: false,
130 | position: 'bottom',
131 | progressBar: true,
132 | sticky: false,
133 | timeout: 3000
134 | };
135 |
136 | SnackbarProvider.propTypes = {
137 | animationTimeout: PropTypes.number,
138 | children: PropTypes.node,
139 | component: PropTypes.elementType,
140 | dismissable: PropTypes.bool,
141 | pauseOnHover: PropTypes.bool,
142 | progressBar: PropTypes.bool,
143 | position: PropTypes.oneOf([
144 | 'top',
145 | 'top-left',
146 | 'top-right',
147 | 'bottom',
148 | 'bottom-left',
149 | 'bottom-right'
150 | ]),
151 | sticky: PropTypes.bool,
152 | timeout: PropTypes.number
153 | };
154 |
155 | export function wrapComponent(Comp) {
156 | return function WrappedComponent(props) {
157 | const { createSnackbar } = useContext(SnackbarContext);
158 | return ;
159 | };
160 | }
161 |
--------------------------------------------------------------------------------
/src/Snackbar.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 | import styled, { keyframes } from 'styled-components';
4 |
5 | import { CloseIcon } from './icons';
6 | import { getTheme, themeNames } from './themes';
7 |
8 | const progress = keyframes`
9 | 0% {
10 | width: 0;
11 | border-bottom-right-radius: 0;
12 | }
13 |
14 | 99% {
15 | border-bottom-right-radius: 0;
16 | }
17 |
18 | 100% {
19 | width: 102%;
20 | border-bottom-right-radius: 5px;
21 | }
22 | `;
23 |
24 | const Container = styled.div`
25 | background: ${props => getTheme(props.theme).color};
26 | border-radius: 5px;
27 | color: #ffffff;
28 | min-height: 3em;
29 | display: flex;
30 | flex-grow: 1;
31 | flex-direction: column;
32 | justify-content: center;
33 | align-items: center;
34 | padding: 0.25em 0.5em;
35 | margin-bottom: ${props =>
36 | props.position.indexOf('bottom') === 0 ? '0.5em' : 0};
37 | margin-top: ${props => (props.position.indexOf('top') === 0 ? '0.5em' : 0)};
38 | `;
39 |
40 | const IconContainer = styled.div`
41 | margin-right: 0.5em;
42 | `;
43 |
44 | const Main = styled.div`
45 | flex-grow: 1;
46 | max-width: 20em;
47 | display: flex;
48 | flex-direction: row;
49 | justify-content: center;
50 | align-items: center;
51 | width: 100%;
52 | `;
53 |
54 | const Content = styled.div`
55 | flex-grow: 1;
56 | `;
57 |
58 | const CloseButton = styled.button`
59 | background: transparent;
60 | display: flex;
61 | justify-content: center;
62 | padding: 0;
63 | font-size: 1.5em;
64 | outline: none;
65 | width: 1em;
66 | `;
67 |
68 | const ProgressBar = styled.div`
69 | align-self: flex-start;
70 | width: calc(100% + 1em);
71 | height: 0.25em;
72 | background: rgba(255, 255, 255, 0.4);
73 | position: relative;
74 | top: 0.25em;
75 | left: -0.5em;
76 | border-bottom-left-radius: 5px;
77 | animation-name: ${progress};
78 | animation-duration: ${props => props.timeout}ms;
79 | animation-timing-function: linear;
80 | `;
81 |
82 | export default class Snackbar extends React.Component {
83 | constructor(props) {
84 | super(props);
85 |
86 | this.state = {
87 | animationPaused: false
88 | };
89 |
90 | this.pause = this.pause.bind(this);
91 | this.resume = this.resume.bind(this);
92 | }
93 |
94 | pause() {
95 | if (this.props.pauseOnHover) {
96 | this.setState({
97 | animationPaused: true
98 | });
99 | this.props.onPause();
100 | }
101 | }
102 |
103 | resume() {
104 | if (this.props.pauseOnHover) {
105 | this.setState({
106 | animationPaused: false
107 | });
108 | this.props.onResume();
109 | }
110 | }
111 |
112 | render() {
113 | const {
114 | timeout,
115 | children,
116 | dismissable,
117 | message,
118 | onDismiss,
119 | sticky,
120 | progressBar,
121 | position,
122 | theme
123 | } = this.props;
124 |
125 | const IconComponent = getTheme(theme).icon;
126 |
127 | return (
128 |
133 |
134 |
135 | {IconComponent ? (
136 |
137 |
138 |
139 | ) : null}
140 | {children || message}
141 | {dismissable ? (
142 |
143 |
144 |
145 | ) : null}
146 |
147 | {!sticky && progressBar ? (
148 |
158 | ) : null}
159 |
160 |
161 | );
162 | }
163 | }
164 |
165 | Snackbar.defaultProps = {
166 | progressBar: true,
167 | position: 'bottom'
168 | };
169 |
170 | Snackbar.propTypes = {
171 | children: PropTypes.node,
172 | pauseOnHover: PropTypes.bool,
173 | onPause: PropTypes.func,
174 | onResume: PropTypes.func,
175 | timeout: PropTypes.number,
176 | theme: PropTypes.oneOf(themeNames),
177 | dismissable: PropTypes.bool,
178 | message: PropTypes.string,
179 | onDismiss: PropTypes.func,
180 | sticky: PropTypes.bool,
181 | progressBar: PropTypes.bool,
182 | position: PropTypes.oneOf([
183 | 'top',
184 | 'top-left',
185 | 'top-right',
186 | 'bottom',
187 | 'bottom-left',
188 | 'bottom-right'
189 | ])
190 | };
191 |
--------------------------------------------------------------------------------
/examples/src/components/examples/demo/DemoExample.js:
--------------------------------------------------------------------------------
1 | import Prism from 'prismjs';
2 | import React from 'react';
3 | import styled from 'styled-components';
4 |
5 | import { SnackbarProvider, wrapComponent } from 'react-snackbar-alert';
6 |
7 | const OptionsContainer = styled.div`
8 | display: flex;
9 | `;
10 |
11 | const OptionsSection = styled.div`
12 | margin: 0.5em;
13 |
14 | h3 {
15 | margin: 0;
16 | }
17 | `;
18 |
19 | export default class DemoExample extends React.Component {
20 | constructor(props) {
21 | super(props);
22 |
23 | this.state = {
24 | progressBar: true,
25 | pauseOnHover: false,
26 | sticky: false,
27 | dismissable: false,
28 | theme: 'default',
29 | timeout: 3000,
30 | position: 'bottom'
31 | };
32 | }
33 |
34 | checkboxStateUpdater(property) {
35 | return event => {
36 | this.setState(
37 | {
38 | [property]: event.target.checked
39 | },
40 | () => {
41 | Prism.highlightAll();
42 | }
43 | );
44 | };
45 | }
46 |
47 | numberStateUpdater(property) {
48 | return event => {
49 | let value = parseInt(event.target.value, 10);
50 | this.setState(
51 | {
52 | [property]: value
53 | },
54 | () => {
55 | Prism.highlightAll();
56 | }
57 | );
58 | };
59 | }
60 |
61 | selectStateUpdater(property) {
62 | return event => {
63 | this.setState(
64 | {
65 | [property]: event.target.value
66 | },
67 | () => {
68 | Prism.highlightAll();
69 | }
70 | );
71 | };
72 | }
73 |
74 | render() {
75 | return (
76 |
77 |
207 |
208 | );
209 | }
210 | }
211 |
212 | function SnackbarTrigger({
213 | createSnackbar,
214 | dismissable,
215 | pauseOnHover,
216 | progressBar,
217 | sticky,
218 | theme,
219 | timeout
220 | }) {
221 | function showSnackbar() {
222 | createSnackbar({
223 | message: 'Hello Snackbar!',
224 | progressBar,
225 | pauseOnHover,
226 | sticky,
227 | dismissable,
228 | theme,
229 | timeout
230 | });
231 | }
232 |
233 | return (
234 |
235 | Show Snackbar
236 |
237 | );
238 | }
239 |
240 | const WrappedSnackbarTrigger = wrapComponent(SnackbarTrigger);
241 |
--------------------------------------------------------------------------------
/examples/src/pages/api.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../components/Layout';
4 |
5 | import styles from './api.module.css';
6 |
7 | export default function ApiPage() {
8 | return (
9 |
10 |
11 | SnackbarProvider Component
12 |
13 | Props
14 |
15 | Unless otherwise noted, any of these props can be overridden for a
16 | specific snackbar instance by adding that same property to the object
17 | passed to createSnackbar.
18 |
19 |
20 |
21 |
22 | Name
23 | Type
24 | Default
25 | Description
26 |
27 |
28 |
29 |
30 |
31 | animationTimeout
32 |
33 | number
34 |
35 | 500
36 |
37 |
38 | The duration of the show and hide animations, in milliseconds
39 |
40 |
41 |
42 |
43 | component
44 |
45 | React component type
46 |
47 | Snackbar component
48 |
49 |
50 | The component type to use for rendering the snackbar.{' '}
51 | Cannot be overridden for an individual snackbar.
52 |
53 |
54 |
55 |
56 | dismissable
57 |
58 | boolean
59 |
60 | false
61 |
62 |
63 | Whether or not created snackbars can be manually dismissed by the
64 | user
65 |
66 |
67 |
68 |
69 | pauseOnHover
70 |
71 | boolean
72 |
73 | false
74 |
75 |
76 | Whether or not to pause a snackbar's timeout when the mouse is
77 | hovered over it
78 |
79 |
80 |
81 |
82 | position
83 |
84 |
85 | One of:
86 |
87 |
88 | 'top'
89 |
90 |
91 | 'top-left'
92 |
93 |
94 | 'top-right'
95 |
96 |
97 | 'bottom'
98 |
99 |
100 | 'bottom-left'
101 |
102 |
103 | 'bottom-right'
104 |
105 |
106 |
107 |
108 | 'bottom'
109 |
110 |
111 | The position on screen to show the snackbars.{' '}
112 | Cannot be overridden for an individual snackbar.
113 |
114 |
115 |
116 |
117 | progressBar
118 |
119 | boolean
120 |
121 | true
122 |
123 |
124 | Whether or not to show an animated progress bar indicating the
125 | time before a snackbar is removed
126 |
127 |
128 |
129 |
130 | sticky
131 |
132 | boolean
133 |
134 | false
135 |
136 | Whether or not the snackbars should be sticky
137 |
138 |
139 |
140 | timeout
141 |
142 | number
143 |
144 | 3000
145 |
146 |
147 | The time before a snackbar is automatically removed, in
148 | milliseconds
149 |
150 |
151 |
152 |
153 | Context
154 | createSnackbar(snackbar: SnackbarData)
155 | Creates and shows a snackbar notification.
156 |
157 | SnackbarData type
158 |
159 | When a value is specified for a given property in both the{' '}
160 | SnackbarManager component and a SnackbarData{' '}
161 | item, the value in the SnackbarData takes precedence.
162 | Otherwise, it will default to the corresponding setting in the{' '}
163 | SnackbarManager if not specified.
164 |
165 |
166 |
167 | Name
168 | Type
169 | Description
170 |
171 |
172 |
173 |
174 |
175 | animationTimeout
176 |
177 | number
178 |
179 | The duration of the show and hide animations, in milliseconds
180 |
181 |
182 |
183 |
184 | data
185 |
186 | object
187 |
188 | Custom data that can be used with a custom snackbar component
189 |
190 |
191 |
192 |
193 | dismissable
194 |
195 | boolean
196 |
197 | Whether or not this snackbar can be manually dismissed by the user
198 |
199 |
200 |
201 |
202 | message (Required)
203 |
204 | string
205 | The message to display
206 |
207 |
208 |
209 | pauseOnHover
210 |
211 | boolean
212 |
213 | Whether or not to pause this snackbar's timeout when it is hovered
214 | over
215 |
216 |
217 |
218 |
219 | progressBar
220 |
221 | boolean
222 | Whether or not to show the progress bar for this snackbar
223 |
224 |
225 |
226 | sticky
227 |
228 | boolean
229 |
230 | Whether or not this snackbar should be sticky. Sticky snackbars
231 | are not automatically removed.
232 |
233 |
234 |
235 |
236 | theme
237 |
238 |
239 | One of:
240 |
241 |
242 | 'default'
243 |
244 |
245 | 'info'
246 |
247 |
248 | 'success'
249 |
250 |
251 | 'warning'
252 |
253 |
254 | 'error'
255 |
256 |
257 |
258 |
259 | The theme to use for this snackbar. Default is{' '}
260 | 'default'.
261 |
262 |
263 |
264 |
265 | timeout
266 |
267 | number
268 |
269 | The time before this snackbar is automatically removed, in
270 | milliseconds
271 |
272 |
273 |
274 |
275 |
276 | wrapComponent helper
277 |
278 |
279 | This helper function wraps the passed component, adding the{' '}
280 | createSnackbar function prop to the wrapped component.
281 |
282 |
283 | );
284 | }
285 |
--------------------------------------------------------------------------------
/src/SnackbarContext.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { fireEvent, render } from '@testing-library/react';
4 | import 'jest-dom/extend-expect';
5 |
6 | import { SnackbarProvider, SnackbarConsumer } from './SnackbarContext';
7 | import { themes } from './themes';
8 |
9 | function renderTestApp(providerProps = {}, snackbarOptions = {}) {
10 | return render(
11 |
12 |
13 | {({ createSnackbar }) => (
14 | createSnackbar({ ...snackbarOptions })}>
15 | Show Snackbar
16 |
17 | )}
18 |
19 |
20 | );
21 | }
22 |
23 | describe('SnackbarContext', () => {
24 | jest.useFakeTimers();
25 |
26 | it('should show a snackbar and remove it after 3 seconds', () => {
27 | const { queryByText } = renderTestApp({}, { message: 'Hello Snackbar' });
28 |
29 | fireEvent.click(queryByText('Show Snackbar'));
30 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
31 |
32 | jest.advanceTimersByTime(3250);
33 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
34 | });
35 |
36 | it('should use a custom timeout on the SnackbarContext', () => {
37 | const { queryByText } = renderTestApp(
38 | { timeout: 5000 },
39 | { message: 'Hello Snackbar' }
40 | );
41 |
42 | fireEvent.click(queryByText('Show Snackbar'));
43 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
44 |
45 | jest.advanceTimersByTime(3250);
46 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
47 |
48 | jest.advanceTimersByTime(2000);
49 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
50 | });
51 |
52 | it('should override the timeout for a specific snackbar', () => {
53 | const { queryByText } = renderTestApp(
54 | { timeout: 1000 },
55 | {
56 | message: 'Hello Snackbar',
57 | timeout: 5000
58 | }
59 | );
60 |
61 | fireEvent.click(queryByText('Show Snackbar'));
62 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
63 |
64 | jest.advanceTimersByTime(1250);
65 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
66 |
67 | jest.advanceTimersByTime(4000);
68 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
69 | });
70 |
71 | it('should use a custom animation timeout', () => {
72 | const { queryByText } = renderTestApp(
73 | { animationTimeout: 1000 },
74 | { message: 'Hello Snackbar' }
75 | );
76 |
77 | fireEvent.click(queryByText('Show Snackbar'));
78 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
79 |
80 | jest.advanceTimersByTime(3250);
81 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
82 |
83 | jest.advanceTimersByTime(750);
84 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
85 | });
86 |
87 | it('should override the custom animation timeout for a specific snackbar', () => {
88 | const { queryByText } = renderTestApp(
89 | { animationTimeout: 250 },
90 | { message: 'Hello Snackbar', animationTimeout: 1000 }
91 | );
92 |
93 | fireEvent.click(queryByText('Show Snackbar'));
94 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
95 |
96 | jest.advanceTimersByTime(3250);
97 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
98 |
99 | jest.advanceTimersByTime(750);
100 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
101 | });
102 |
103 | it('should use a custom snackbar component', () => {
104 | const CustomSnackbar = () => Custom Snackbar
;
105 |
106 | const { queryByText } = renderTestApp(
107 | { component: CustomSnackbar },
108 | { message: 'Hello Snackbar' }
109 | );
110 |
111 | fireEvent.click(queryByText('Show Snackbar'));
112 |
113 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
114 | expect(queryByText('Custom Snackbar')).toBeInTheDocument();
115 | });
116 |
117 | it('should show dismissable snackbars', () => {
118 | const { queryByText, getByTitle } = renderTestApp(
119 | { dismissable: true },
120 | { message: 'Hello Snackbar' }
121 | );
122 |
123 | fireEvent.click(queryByText('Show Snackbar'));
124 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
125 |
126 | fireEvent.click(getByTitle('Close'));
127 | jest.advanceTimersByTime(250);
128 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
129 | });
130 |
131 | it('should override the dismissable property for a specific snackbar', () => {
132 | const { queryByText, getByTitle } = renderTestApp(
133 | { dismissable: false },
134 | { message: 'Hello Snackbar', dismissable: true }
135 | );
136 |
137 | fireEvent.click(queryByText('Show Snackbar'));
138 | expect(getByTitle('Close')).toBeInTheDocument();
139 | });
140 |
141 | it('should not show the close button for non-dismissable snackbars', () => {
142 | const { queryByText, queryByTitle } = renderTestApp(
143 | { dismissable: false },
144 | { message: 'Hello Snackbar' }
145 | );
146 |
147 | fireEvent.click(queryByText('Show Snackbar'));
148 | expect(queryByTitle('Close')).not.toBeInTheDocument();
149 | });
150 |
151 | it('should support pause on hover', () => {
152 | const { queryByText } = renderTestApp(
153 | { pauseOnHover: true },
154 | { message: 'Hello Snackbar' }
155 | );
156 |
157 | fireEvent.click(queryByText('Show Snackbar'));
158 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
159 |
160 | fireEvent.mouseEnter(queryByText('Hello Snackbar'));
161 | jest.advanceTimersByTime(10000);
162 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
163 |
164 | fireEvent.mouseLeave(queryByText('Hello Snackbar'));
165 | jest.advanceTimersByTime(3250);
166 | expect(queryByText('Hello Snackbar')).not.toBeInTheDocument();
167 | });
168 |
169 | it('should show sticky snackbars', () => {
170 | const { queryByText } = renderTestApp(
171 | { sticky: true },
172 | { message: 'Hello Snackbar' }
173 | );
174 |
175 | fireEvent.click(queryByText('Show Snackbar'));
176 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
177 |
178 | jest.advanceTimersByTime(10000);
179 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
180 | });
181 |
182 | it('should override the sticky property for a specific snackbar', () => {
183 | const { queryByText } = renderTestApp(
184 | { sticky: false },
185 | { message: 'Hello Snackbar', sticky: true }
186 | );
187 |
188 | fireEvent.click(queryByText('Show Snackbar'));
189 | jest.advanceTimersByTime(10000);
190 | expect(queryByText('Hello Snackbar')).toBeInTheDocument();
191 | });
192 |
193 | it('should show the progress bar by default', () => {
194 | const { queryByRole, queryByText } = renderTestApp(
195 | {},
196 | { message: 'Hello Snackbar' }
197 | );
198 |
199 | fireEvent.click(queryByText('Show Snackbar'));
200 |
201 | const progressBar = queryByRole('progressbar');
202 | expect(progressBar).toBeInTheDocument();
203 | const computedStyle = getComputedStyle(progressBar);
204 | expect(computedStyle.animationDuration).toBe('3000ms');
205 | });
206 |
207 | it('should set the animation duration to the timeout', () => {
208 | const { queryByRole, queryByText } = renderTestApp(
209 | { timeout: 5000 },
210 | { message: 'Hello Snackbar' }
211 | );
212 |
213 | fireEvent.click(queryByText('Show Snackbar'));
214 |
215 | const progressBar = queryByRole('progressbar');
216 | const computedStyle = getComputedStyle(progressBar);
217 | expect(computedStyle.animationDuration).toBe('5000ms');
218 | });
219 |
220 | it('should not show the progress bar if progressBar is false in the provider', () => {
221 | const { queryByRole, queryByText } = renderTestApp(
222 | { progressBar: false },
223 | { message: 'Hello Snackbar' }
224 | );
225 |
226 | fireEvent.click(queryByText('Show Snackbar'));
227 |
228 | const progressBar = queryByRole('progressbar');
229 | expect(progressBar).not.toBeInTheDocument();
230 | });
231 |
232 | it('should not show the progress bar if progressBar is false for a specific snackbar', () => {
233 | const { queryByRole, queryByText } = renderTestApp(
234 | { progressBar: true },
235 | { message: 'Hello Snackbar', progressBar: false }
236 | );
237 |
238 | fireEvent.click(queryByText('Show Snackbar'));
239 |
240 | const progressBar = queryByRole('progressbar');
241 | expect(progressBar).not.toBeInTheDocument();
242 | });
243 |
244 | describe('themes', () => {
245 | function testTheme(theme) {
246 | const { queryByText } = renderTestApp(
247 | {},
248 | { message: 'Hello Snackbar', theme }
249 | );
250 |
251 | fireEvent.click(queryByText('Show Snackbar'));
252 |
253 | const snackbar = queryByText('Hello Snackbar');
254 | expect(getComputedStyle(snackbar.parentNode.parentNode).background).toBe(
255 | themes[theme].color
256 | );
257 | }
258 |
259 | Object.keys(themes).forEach(theme => {
260 | it(`should create a snackbar with the "${theme}" theme`, () => {
261 | testTheme(theme);
262 | });
263 | });
264 | });
265 | });
266 |
--------------------------------------------------------------------------------