From 80effb81c8bceb36972e3962231a80a2c3a27664 Mon Sep 17 00:00:00 2001 From: git stash Date: Mon, 5 Oct 2020 12:26:01 +0700 Subject: [PATCH] feat(snackbar): add snackbar component --- package.json | 4 +- .../core/src/snackbar/Snackbar.stories.mdx | 14 +++++ .../core/src/snackbar/Snackbar.test.tsx | 34 ++++++++++++ src/packages/core/src/snackbar/Snackbar.tsx | 55 +++++++++++++++++++ .../core/src/snackbar/SnackbarExample.tsx | 29 ++++++++++ src/packages/core/src/snackbar/index.ts | 1 + src/packages/core/src/snackbar/types.ts | 37 +++++++++++++ 7 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 src/packages/core/src/snackbar/Snackbar.stories.mdx create mode 100644 src/packages/core/src/snackbar/Snackbar.test.tsx create mode 100644 src/packages/core/src/snackbar/Snackbar.tsx create mode 100644 src/packages/core/src/snackbar/SnackbarExample.tsx create mode 100644 src/packages/core/src/snackbar/index.ts create mode 100644 src/packages/core/src/snackbar/types.ts diff --git a/package.json b/package.json index 32babe63..16be2652 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ } }, "dependencies": { - "@elephas/core": "^1.0.0", - "@elephas/layout": "^1.0.0", + "@elephas/core": "^1.1.0", + "@elephas/layout": "^1.1.0", "mime": "^2.4.6", "react": "^16.11.0", "react-dom": "^16.11.0" diff --git a/src/packages/core/src/snackbar/Snackbar.stories.mdx b/src/packages/core/src/snackbar/Snackbar.stories.mdx new file mode 100644 index 00000000..b2098e3c --- /dev/null +++ b/src/packages/core/src/snackbar/Snackbar.stories.mdx @@ -0,0 +1,14 @@ +import { Meta, Description, Props, Preview, Story } from '@storybook/addon-docs/blocks'; + +import { Snackbar } from '.'; +import { SnackbarExample } from './SnackbarExample'; + + + +# Snackbar + + + + + + diff --git a/src/packages/core/src/snackbar/Snackbar.test.tsx b/src/packages/core/src/snackbar/Snackbar.test.tsx new file mode 100644 index 00000000..cf5f0df9 --- /dev/null +++ b/src/packages/core/src/snackbar/Snackbar.test.tsx @@ -0,0 +1,34 @@ +import React, { MouseEvent } from 'react'; +import { render, fireEvent } from '@testing-library/react'; + +import { Snackbar } from '.'; + +describe('', () => { + const getButton = (el: HTMLElement): HTMLButtonElement | null => el.querySelector('button'); + + let onClose: (event?: MouseEvent) => void; + let btnText: string; + let message: string; + + beforeEach(() => { + onClose = jest.fn(); + btnText = 'Click me'; + message = 'I am Snackbar'; + }); + + it('method onClose has been called after click button', () => { + const { container } = render( + , + ); + const button = getButton(container); + + button && fireEvent.click(button); + + expect(onClose).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/packages/core/src/snackbar/Snackbar.tsx b/src/packages/core/src/snackbar/Snackbar.tsx new file mode 100644 index 00000000..b410ca83 --- /dev/null +++ b/src/packages/core/src/snackbar/Snackbar.tsx @@ -0,0 +1,55 @@ +import React, { useState, useEffect } from 'react'; +import classNames from 'classnames'; +import { SnackbarProps } from './types'; +import { Button } from '..'; + +export function Snackbar(props: SnackbarProps) { + const { + buttonText, + className, + duration = 10, + isOpen, + message, + onClose, + } = props; + + const [autoHideDuration, setAutoHideDuration] = useState(duration); + let time = autoHideDuration; + + const snackbarClassNames = classNames({ + [`${className}`]: className, + _e_snackbar: true, + _e_snackbar_open: isOpen, + _e_snackbar_hidden: !isOpen, + }); + + useEffect(() => { + let interval: number; + if (isOpen) { + time = duration; + setAutoHideDuration(time); + interval = window.setInterval(() => { + if (time !== 0) { + time -= 1; + setAutoHideDuration(time); + } else { + onClose && onClose(undefined); + } + }, 1000); + } + return () => clearInterval(interval); + }, [isOpen]); + + return ( +
+
+ {message} + + : + {autoHideDuration} + +
+ +
+ ); +} diff --git a/src/packages/core/src/snackbar/SnackbarExample.tsx b/src/packages/core/src/snackbar/SnackbarExample.tsx new file mode 100644 index 00000000..f5f7f6d4 --- /dev/null +++ b/src/packages/core/src/snackbar/SnackbarExample.tsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import { Snackbar } from '.'; +import { Button } from '../button'; + +export function SnackbarExample() { + const [isOpen, setOpen] = useState(false); + + const handleClick = () => { + setOpen(!isOpen); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( + <> + + + + ); +} diff --git a/src/packages/core/src/snackbar/index.ts b/src/packages/core/src/snackbar/index.ts new file mode 100644 index 00000000..16515d33 --- /dev/null +++ b/src/packages/core/src/snackbar/index.ts @@ -0,0 +1 @@ +export { Snackbar } from './Snackbar'; diff --git a/src/packages/core/src/snackbar/types.ts b/src/packages/core/src/snackbar/types.ts new file mode 100644 index 00000000..d301ae1f --- /dev/null +++ b/src/packages/core/src/snackbar/types.ts @@ -0,0 +1,37 @@ +import { MouseEvent } from 'react'; + +export interface SnackbarProps { + /** + * Text on the button. + */ + buttonText: string, + + /** + * Additional CSS class. + */ + className?: string, + + /** + * Number of seconds to wait before automatically calling the + * `onClose` function. + * @default 10 + */ + duration?: number, + + /** + * Controls Snackbar open state. + */ + isOpen: boolean, + + /** + * Message to display. + */ + message: string, + + /** + * Callback fired when the component requests to be closed. + * @param {object} event The event source of the callback. + * If event `undefined`, `Snackbar` closed by timeout (`duration` expired). + */ + onClose?: (event?: MouseEvent) => void, +}