From 8706f7fbfd0f60e90b91972445e452e9f6629a57 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 20 Sep 2020 19:41:54 +0200 Subject: [PATCH 01/13] update button component with ref --- src/components/Button/Button.tsx | 93 +++++++++++++++++--------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 2c070025..754dddb9 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -20,51 +20,56 @@ interface Props { size?: 'normal' | 'small'; } -const Button: React.FC = ({ - children, - type = 'primary', - onClick, - className, - isDisabled, - isLoading, - htmlType = 'button', - prefixIcon, - suffixIcon, - name, - size = 'normal', -}: Props) => { - const buttonClassNames = classNames( - cssReset.ventura, - styles.button, +const Button = React.forwardRef( + ( { - [styles.typePrimary]: type === 'primary', - [styles.typeSecondary]: type === 'secondary', - [styles.smallButton]: size === 'small', - }, - className, - ); + children, + type = 'primary', + onClick, + className, + isDisabled, + isLoading, + htmlType = 'button', + prefixIcon, + suffixIcon, + name, + size = 'normal', + }: Props, + ref, + ) => { + const buttonClassNames = classNames( + cssReset.ventura, + styles.button, + { + [styles.typePrimary]: type === 'primary', + [styles.typeSecondary]: type === 'secondary', + [styles.smallButton]: size === 'small', + }, + className, + ); - const labelClassNames = classNames(styles.label, { - [styles.labelWithPrefixIcon]: Boolean(prefixIcon) || isLoading, - [styles.labelWithSuffixIcon]: Boolean(suffixIcon), - [styles.smallLabel]: size === 'small', - }); - - return ( - - ); -}; + const labelClassNames = classNames(styles.label, { + [styles.labelWithPrefixIcon]: Boolean(prefixIcon) || isLoading, + [styles.labelWithSuffixIcon]: Boolean(suffixIcon), + [styles.smallLabel]: size === 'small', + }); + return ( + + ); + }, +); export default Button; From a13b48851134b44cec22d2ed5d11fc9a45ff91d9 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sat, 26 Sep 2020 12:57:34 +0200 Subject: [PATCH 02/13] create dropdown menu --- package.json | 2 + src/components/DropdownMenu/DropdownMenu.mdx | 31 ++++++++++ .../DropdownMenu/DropdownMenu.module.css | 5 ++ src/components/DropdownMenu/DropdownMenu.tsx | 57 +++++++++++++++++++ src/index.ts | 1 + yarn.lock | 15 ++++- 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/components/DropdownMenu/DropdownMenu.mdx create mode 100644 src/components/DropdownMenu/DropdownMenu.module.css create mode 100644 src/components/DropdownMenu/DropdownMenu.tsx diff --git a/package.json b/package.json index 7731f1d0..2c2dd647 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,10 @@ "typescript": "^3.9.7" }, "dependencies": { + "@popperjs/core": "^2.4.4", "classnames": "^2.2.6", "react-feather": "^2.0.8", + "react-popper": "^2.2.3", "react-portal": "^4.2.1" }, "keywords": [ diff --git a/src/components/DropdownMenu/DropdownMenu.mdx b/src/components/DropdownMenu/DropdownMenu.mdx new file mode 100644 index 00000000..586b7f05 --- /dev/null +++ b/src/components/DropdownMenu/DropdownMenu.mdx @@ -0,0 +1,31 @@ +--- +name: Dropdown menu +menu: Components +route: /dropdown-menu +--- + +import { Playground, Props } from 'docz'; +import { DropdownMenu } from '../../index.ts' + +# DropdownMenu + +A DropdownMenu is used to provide extra information or actions, while using less space. + +## Best practices + +- Check + +## Examples + +### Basic usage + + + +
+
+
+
+ +## API + + diff --git a/src/components/DropdownMenu/DropdownMenu.module.css b/src/components/DropdownMenu/DropdownMenu.module.css new file mode 100644 index 00000000..a2b95670 --- /dev/null +++ b/src/components/DropdownMenu/DropdownMenu.module.css @@ -0,0 +1,5 @@ +.menu { + border-radius: var(--border-radius-small); + background-color: var(--color-secondary); + box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1); +} diff --git a/src/components/DropdownMenu/DropdownMenu.tsx b/src/components/DropdownMenu/DropdownMenu.tsx new file mode 100644 index 00000000..6e38bb98 --- /dev/null +++ b/src/components/DropdownMenu/DropdownMenu.tsx @@ -0,0 +1,57 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import React, { useRef, useState } from 'react'; +import { usePopper } from 'react-popper'; + +import Button from '../Button/Button'; + +import styles from './DropdownMenu.module.css'; + +interface Props {} + +const DropdownMenu: React.FC = () => { + const buttonRef = useRef(null); + const menuRef = useRef(null); + + const [arrowRef, setArrowRef] = useState(null); + + const [isMenuVisible, setIsMenuVisible] = useState(false); + const { styles: popperStyles, attributes } = usePopper(buttonRef.current, menuRef.current, { + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 10], + }, + }, + { + name: 'arrow', + options: { + element: arrowRef, + }, + }, + ], + placement: 'bottom-start', + }); + + return ( + <> + + + {isMenuVisible && ( +
+
+

A ridiculously long sentence for a popper.

+
+ )} + + ); +}; + +export default DropdownMenu; diff --git a/src/index.ts b/src/index.ts index fa4680e5..d68c94df 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,5 +8,6 @@ export { default as Modal } from './components/Modal/Modal'; export { default as Radio } from './components/Radio/Radio'; export { default as Table } from './components/Table/Table'; export { default as TextArea } from './components/TextArea/TextArea'; +export { default as DropdownMenu } from './components/DropdownMenu/DropdownMenu'; export * from 'react-feather'; diff --git a/yarn.lock b/yarn.lock index d2c1aa3b..0a440b46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1565,6 +1565,11 @@ lodash.debounce "^4.0.8" react-dev-utils "^9.1.0" +"@popperjs/core@^2.4.4": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398" + integrity sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg== + "@reach/router@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.3.tgz#58162860dce6c9449d49be86b0561b5ef46d80db" @@ -13979,6 +13984,14 @@ react-live@^2.2.1: react-simple-code-editor "^0.10.0" unescape "^1.0.1" +react-popper@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.3.tgz#33d425fa6975d4bd54d9acd64897a89d904b9d97" + integrity sha512-mOEiMNT1249js0jJvkrOjyHsGvqcJd3aGW/agkiMoZk3bZ1fXN1wQszIQSjHIai48fE67+zwF8Cs+C4fWqlfjw== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + react-portal@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.1.tgz#12c1599238c06fb08a9800f3070bea2a3f78b1a6" @@ -17395,7 +17408,7 @@ warning@^3.0.0: dependencies: loose-envify "^1.0.0" -warning@^4.0.3: +warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== From 3e48f2d3b0cc507e00be55f1f56304fa096aeb00 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sat, 3 Oct 2020 15:29:21 +0200 Subject: [PATCH 03/13] craete new components --- src/components/Menu/Menu.tsx | 24 +++++++++++++++++++ src/components/Menu/MenuItem/MenuItem.tsx | 18 ++++++++++++++ .../DropdownMenu.mdx => Popover/Popover.mdx} | 10 ++++---- .../Popover.module.css} | 0 .../DropdownMenu.tsx => Popover/Popover.tsx} | 6 ++--- src/index.ts | 2 +- 6 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 src/components/Menu/Menu.tsx create mode 100644 src/components/Menu/MenuItem/MenuItem.tsx rename src/components/{DropdownMenu/DropdownMenu.mdx => Popover/Popover.mdx} (54%) rename src/components/{DropdownMenu/DropdownMenu.module.css => Popover/Popover.module.css} (100%) rename src/components/{DropdownMenu/DropdownMenu.tsx => Popover/Popover.tsx} (91%) diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx new file mode 100644 index 00000000..49ec9fe4 --- /dev/null +++ b/src/components/Menu/Menu.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import classNames from 'classnames'; + +import MenuItem from './MenuItem/MenuItem'; + +import cssReset from '../../css-reset.module.css'; +import styles from './Table.module.css'; + +type Props = { + className?: string; + children?: React.ReactNode; +}; + +const Menu: React.FC & { + Item: typeof MenuItem; +} = ({ className, children }: Props) => { + const menuClassNames = classNames(cssReset.ventura, styles.table, className); + + return
{children}
; +}; + +Menu.Item = MenuItem; + +export default Menu; diff --git a/src/components/Menu/MenuItem/MenuItem.tsx b/src/components/Menu/MenuItem/MenuItem.tsx new file mode 100644 index 00000000..8ddc1847 --- /dev/null +++ b/src/components/Menu/MenuItem/MenuItem.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import classNames from 'classnames'; + +import cssReset from '../../css-reset.module.css'; +import styles from './Table.module.css'; + +type Props = { + className?: string; + children?: React.ReactNode; +}; + +const MenuItem: React.FC = ({ className, children }: Props) => { + const menuClassNames = classNames(cssReset.ventura, styles.table, className); + + return
{children}
; +}; + +export default MenuItem; diff --git a/src/components/DropdownMenu/DropdownMenu.mdx b/src/components/Popover/Popover.mdx similarity index 54% rename from src/components/DropdownMenu/DropdownMenu.mdx rename to src/components/Popover/Popover.mdx index 586b7f05..5e33b077 100644 --- a/src/components/DropdownMenu/DropdownMenu.mdx +++ b/src/components/Popover/Popover.mdx @@ -5,11 +5,11 @@ route: /dropdown-menu --- import { Playground, Props } from 'docz'; -import { DropdownMenu } from '../../index.ts' +import { Popover } from '../../index.ts'; -# DropdownMenu +# Popover -A DropdownMenu is used to provide extra information or actions, while using less space. +A Popover is used to display extra information ## Best practices @@ -20,7 +20,7 @@ A DropdownMenu is used to provide extra information or actions, while using less ### Basic usage - +


@@ -28,4 +28,4 @@ A DropdownMenu is used to provide extra information or actions, while using less ## API - + diff --git a/src/components/DropdownMenu/DropdownMenu.module.css b/src/components/Popover/Popover.module.css similarity index 100% rename from src/components/DropdownMenu/DropdownMenu.module.css rename to src/components/Popover/Popover.module.css diff --git a/src/components/DropdownMenu/DropdownMenu.tsx b/src/components/Popover/Popover.tsx similarity index 91% rename from src/components/DropdownMenu/DropdownMenu.tsx rename to src/components/Popover/Popover.tsx index 6e38bb98..42567213 100644 --- a/src/components/DropdownMenu/DropdownMenu.tsx +++ b/src/components/Popover/Popover.tsx @@ -4,11 +4,11 @@ import { usePopper } from 'react-popper'; import Button from '../Button/Button'; -import styles from './DropdownMenu.module.css'; +import styles from './Popover.module.css'; interface Props {} -const DropdownMenu: React.FC = () => { +const Popover: React.FC = () => { const buttonRef = useRef(null); const menuRef = useRef(null); @@ -54,4 +54,4 @@ const DropdownMenu: React.FC = () => { ); }; -export default DropdownMenu; +export default Popover; diff --git a/src/index.ts b/src/index.ts index d68c94df..ddbc08a7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,6 @@ export { default as Modal } from './components/Modal/Modal'; export { default as Radio } from './components/Radio/Radio'; export { default as Table } from './components/Table/Table'; export { default as TextArea } from './components/TextArea/TextArea'; -export { default as DropdownMenu } from './components/DropdownMenu/DropdownMenu'; +export { default as Popover } from './components/Popover/Popover'; export * from 'react-feather'; From 44bd8ef6f0b5d4df7799bb66584fa04f7f705746 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 12:56:56 +0200 Subject: [PATCH 04/13] update popover component --- src/components/Popover/Popover.tsx | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx index 42567213..d2210c6b 100644 --- a/src/components/Popover/Popover.tsx +++ b/src/components/Popover/Popover.tsx @@ -1,21 +1,30 @@ +/* eslint-disable react/prop-types */ /* eslint-disable react/jsx-props-no-spreading */ import React, { useRef, useState } from 'react'; import { usePopper } from 'react-popper'; - -import Button from '../Button/Button'; +import { Placement } from '@popperjs/core'; import styles from './Popover.module.css'; -interface Props {} +interface Props { + content?: React.ReactNode; + children?: React.ReactNode; + isVisible?: boolean; + placement?: Placement; +} -const Popover: React.FC = () => { - const buttonRef = useRef(null); +const Popover: React.FC = ({ + content, + children, + isVisible = false, + placement = 'bottom-start', +}) => { + const divRef = useRef(null); const menuRef = useRef(null); const [arrowRef, setArrowRef] = useState(null); - const [isMenuVisible, setIsMenuVisible] = useState(false); - const { styles: popperStyles, attributes } = usePopper(buttonRef.current, menuRef.current, { + const { styles: popperStyles, attributes } = usePopper(divRef.current, menuRef.current, { modifiers: [ { name: 'offset', @@ -30,16 +39,14 @@ const Popover: React.FC = () => { }, }, ], - placement: 'bottom-start', + placement, }); return ( <> - +
{children}
- {isMenuVisible && ( + {isVisible && (
= () => { className={styles.menu} >
-

A ridiculously long sentence for a popper.

+ {content}
)} From 00b590c1b8b8402fc059d63963df39b45160aeb6 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 13:53:53 +0200 Subject: [PATCH 05/13] create popover structure --- src/components/Menu/Menu.module.css | 0 src/components/Menu/Menu.tsx | 2 +- .../Menu/MenuItem/MenuItem.module.css | 0 src/components/Menu/MenuItem/MenuItem.tsx | 4 ++-- src/components/Popover/Popover.mdx | 23 ++++++++++++++----- src/components/Popover/Popover.tsx | 12 ++++++++-- src/index.ts | 1 + src/utils/hooks/useOutsideClick.ts | 17 ++++++++++++++ 8 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 src/components/Menu/Menu.module.css create mode 100644 src/components/Menu/MenuItem/MenuItem.module.css create mode 100644 src/utils/hooks/useOutsideClick.ts diff --git a/src/components/Menu/Menu.module.css b/src/components/Menu/Menu.module.css new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 49ec9fe4..e3c06e97 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import MenuItem from './MenuItem/MenuItem'; import cssReset from '../../css-reset.module.css'; -import styles from './Table.module.css'; +import styles from './Menu.module.css'; type Props = { className?: string; diff --git a/src/components/Menu/MenuItem/MenuItem.module.css b/src/components/Menu/MenuItem/MenuItem.module.css new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Menu/MenuItem/MenuItem.tsx b/src/components/Menu/MenuItem/MenuItem.tsx index 8ddc1847..8b487b19 100644 --- a/src/components/Menu/MenuItem/MenuItem.tsx +++ b/src/components/Menu/MenuItem/MenuItem.tsx @@ -1,8 +1,8 @@ import React from 'react'; import classNames from 'classnames'; -import cssReset from '../../css-reset.module.css'; -import styles from './Table.module.css'; +import cssReset from '../../../css-reset.module.css'; +import styles from './MenuItem.module.css'; type Props = { className?: string; diff --git a/src/components/Popover/Popover.mdx b/src/components/Popover/Popover.mdx index 5e33b077..8b7a2741 100644 --- a/src/components/Popover/Popover.mdx +++ b/src/components/Popover/Popover.mdx @@ -5,11 +5,11 @@ route: /dropdown-menu --- import { Playground, Props } from 'docz'; -import { Popover } from '../../index.ts'; +import { Popover, Menu, Button } from '../../index.ts'; # Popover -A Popover is used to display extra information +A Popover is used to display extra information ## Best practices @@ -20,10 +20,21 @@ A Popover is used to display extra information ### Basic usage - -
-
-
+ {() => { + const [isVisible, setIsVisible] = React.useState(false); + const popoverContent = (

Hello

); + return ( + <> + + + +
+
+
+ + ); + }} +
## API diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx index d2210c6b..ef94382b 100644 --- a/src/components/Popover/Popover.tsx +++ b/src/components/Popover/Popover.tsx @@ -4,19 +4,23 @@ import React, { useRef, useState } from 'react'; import { usePopper } from 'react-popper'; import { Placement } from '@popperjs/core'; +import { useOutsideClick } from '../../utils/hooks/useOutsideClick'; + import styles from './Popover.module.css'; interface Props { content?: React.ReactNode; children?: React.ReactNode; - isVisible?: boolean; + isVisible: boolean; + setIsVisible: React.Dispatch>; placement?: Placement; } const Popover: React.FC = ({ content, children, - isVisible = false, + isVisible, + setIsVisible, placement = 'bottom-start', }) => { const divRef = useRef(null); @@ -24,6 +28,10 @@ const Popover: React.FC = ({ const [arrowRef, setArrowRef] = useState(null); + useOutsideClick(() => { + setIsVisible(!isVisible); + }, menuRef); + const { styles: popperStyles, attributes } = usePopper(divRef.current, menuRef.current, { modifiers: [ { diff --git a/src/index.ts b/src/index.ts index ddbc08a7..6978418d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,5 +9,6 @@ export { default as Radio } from './components/Radio/Radio'; export { default as Table } from './components/Table/Table'; export { default as TextArea } from './components/TextArea/TextArea'; export { default as Popover } from './components/Popover/Popover'; +export { default as Menu } from './components/Menu/Menu'; export * from 'react-feather'; diff --git a/src/utils/hooks/useOutsideClick.ts b/src/utils/hooks/useOutsideClick.ts new file mode 100644 index 00000000..d6dbbe5c --- /dev/null +++ b/src/utils/hooks/useOutsideClick.ts @@ -0,0 +1,17 @@ +import React, { useEffect } from 'react'; + +export const useOutsideClick = (callback: Function, elementRef: React.RefObject) => { + useEffect(() => { + const onClick = (e: MouseEvent) => { + if (elementRef.current && !elementRef.current.contains(e.target)) { + callback(); + } + }; + + document.addEventListener('click', onClick); + + return () => { + document.removeEventListener('click', onClick); + }; + }, [elementRef, callback]); +}; From d7891683f71fc5c098543e541f19ec4347a9d988 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 14:52:57 +0200 Subject: [PATCH 06/13] finalise popover component --- src/components/Popover/Popover.mdx | 5 ++++- src/components/Popover/Popover.tsx | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/Popover/Popover.mdx b/src/components/Popover/Popover.mdx index 8b7a2741..133f4328 100644 --- a/src/components/Popover/Popover.mdx +++ b/src/components/Popover/Popover.mdx @@ -23,9 +23,12 @@ A Popover is used to display extra information {() => { const [isVisible, setIsVisible] = React.useState(false); const popoverContent = (

Hello

); + const onClose = () => { + setIsVisible(false); + } return ( <> - +
diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx index ef94382b..43df8072 100644 --- a/src/components/Popover/Popover.tsx +++ b/src/components/Popover/Popover.tsx @@ -12,7 +12,7 @@ interface Props { content?: React.ReactNode; children?: React.ReactNode; isVisible: boolean; - setIsVisible: React.Dispatch>; + onClose: Function; placement?: Placement; } @@ -20,7 +20,7 @@ const Popover: React.FC = ({ content, children, isVisible, - setIsVisible, + onClose, placement = 'bottom-start', }) => { const divRef = useRef(null); @@ -29,7 +29,7 @@ const Popover: React.FC = ({ const [arrowRef, setArrowRef] = useState(null); useOutsideClick(() => { - setIsVisible(!isVisible); + onClose(); }, menuRef); const { styles: popperStyles, attributes } = usePopper(divRef.current, menuRef.current, { From b9ded824ed8a6f33277d06d0b1d2a2b47ca5d812 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 15:08:05 +0200 Subject: [PATCH 07/13] fix weird button logic --- src/components/Button/Button.tsx | 2 +- src/components/Menu/MenuItem/MenuItem.tsx | 25 +++++++++++++++++++---- src/components/Popover/Popover.mdx | 7 +++++-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 754dddb9..747fb28f 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -60,7 +60,7 @@ const Button = React.forwardRef( className={buttonClassNames} // eslint-disable-next-line react/button-has-type type={htmlType} - onClick={isLoading || suffixIcon ? undefined : onClick} + onClick={isLoading || isDisabled ? undefined : onClick} name={name} data-testid={name && `button-${name}`} ref={ref} diff --git a/src/components/Menu/MenuItem/MenuItem.tsx b/src/components/Menu/MenuItem/MenuItem.tsx index 8b487b19..a2da9121 100644 --- a/src/components/Menu/MenuItem/MenuItem.tsx +++ b/src/components/Menu/MenuItem/MenuItem.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { MouseEventHandler } from 'react'; import classNames from 'classnames'; import cssReset from '../../../css-reset.module.css'; @@ -7,12 +7,29 @@ import styles from './MenuItem.module.css'; type Props = { className?: string; children?: React.ReactNode; + prefixIcon?: React.ReactElement; + isDisabled?: boolean; + onClick?: MouseEventHandler; }; -const MenuItem: React.FC = ({ className, children }: Props) => { - const menuClassNames = classNames(cssReset.ventura, styles.table, className); +const MenuItem: React.FC = ({ className, children, isDisabled = false, onClick }: Props) => { + const menuItemClassNames = classNames(cssReset.ventura, styles.table, className); - return
{children}
; + return ( + + ); }; export default MenuItem; diff --git a/src/components/Popover/Popover.mdx b/src/components/Popover/Popover.mdx index 133f4328..e4cf1727 100644 --- a/src/components/Popover/Popover.mdx +++ b/src/components/Popover/Popover.mdx @@ -22,13 +22,16 @@ A Popover is used to display extra information {() => { const [isVisible, setIsVisible] = React.useState(false); - const popoverContent = (

Hello

); + const popoverContent = ( + + Option 1 + ); const onClose = () => { setIsVisible(false); } return ( <> - +
From abd19acd602cc043c489aebdbfb8e23396467952 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 16:53:04 +0200 Subject: [PATCH 08/13] add extra event hooks to button --- src/components/Button/Button.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 747fb28f..740fa20c 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,4 +1,4 @@ -import React, { MouseEventHandler } from 'react'; +import React, { FocusEventHandler, MouseEventHandler } from 'react'; import classNames from 'classnames'; import Spinner from '../utils/Spinner/Spinner'; @@ -10,6 +10,8 @@ interface Props { children: React.ReactNode; type?: 'primary' | 'secondary'; onClick?: MouseEventHandler; + onFocus?: FocusEventHandler; + onBlur?: FocusEventHandler; className?: string; isDisabled?: boolean; isLoading?: boolean; @@ -26,6 +28,8 @@ const Button = React.forwardRef( children, type = 'primary', onClick, + onFocus, + onBlur, className, isDisabled, isLoading, @@ -61,6 +65,8 @@ const Button = React.forwardRef( // eslint-disable-next-line react/button-has-type type={htmlType} onClick={isLoading || isDisabled ? undefined : onClick} + onFocus={isLoading || isDisabled ? undefined : onFocus} + onBlur={onBlur} name={name} data-testid={name && `button-${name}`} ref={ref} From d4cb1ccebdab9c5d84fc8d93722251f596c984f6 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 17:14:18 +0200 Subject: [PATCH 09/13] feat(Popover): create popover component --- src/components/Menu/Menu.module.css | 6 +++ src/components/Menu/Menu.tsx | 2 +- .../Menu/MenuItem/MenuItem.module.css | 28 ++++++++++++ src/components/Menu/MenuItem/MenuItem.tsx | 42 ++++++++++-------- src/components/Popover/Popover.mdx | 44 ++++++++++++++----- src/components/Popover/Popover.module.css | 7 ++- src/components/Popover/Popover.tsx | 6 ++- 7 files changed, 101 insertions(+), 34 deletions(-) diff --git a/src/components/Menu/Menu.module.css b/src/components/Menu/Menu.module.css index e69de29b..b15366ed 100644 --- a/src/components/Menu/Menu.module.css +++ b/src/components/Menu/Menu.module.css @@ -0,0 +1,6 @@ +.menu { + display: flex; + flex-direction: column; + overflow: hidden; + border-radius: var(--border-radius-small); +} diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index e3c06e97..e8c9288a 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -14,7 +14,7 @@ type Props = { const Menu: React.FC & { Item: typeof MenuItem; } = ({ className, children }: Props) => { - const menuClassNames = classNames(cssReset.ventura, styles.table, className); + const menuClassNames = classNames(cssReset.ventura, styles.menu, className); return
{children}
; }; diff --git a/src/components/Menu/MenuItem/MenuItem.module.css b/src/components/Menu/MenuItem/MenuItem.module.css index e69de29b..b2dd0e0a 100644 --- a/src/components/Menu/MenuItem/MenuItem.module.css +++ b/src/components/Menu/MenuItem/MenuItem.module.css @@ -0,0 +1,28 @@ +.menuItem { + line-height: 14px; + font-size: 14px; + padding: 10px 14px; + border: none; + cursor: pointer; + display: inline-block; + background-color: var(--color-secondary); + text-align: left; +} + +.menuItem:focus { + background-color: var(--color-secondary-hover); + outline: none; +} + +.menuItem:hover { + background-color: var(--color-secondary-hover); +} + +.label { + line-height: inherit; +} + +.labelWithPrefixIcon { + margin-left: 8px; +} + diff --git a/src/components/Menu/MenuItem/MenuItem.tsx b/src/components/Menu/MenuItem/MenuItem.tsx index a2da9121..6c922c03 100644 --- a/src/components/Menu/MenuItem/MenuItem.tsx +++ b/src/components/Menu/MenuItem/MenuItem.tsx @@ -10,26 +10,32 @@ type Props = { prefixIcon?: React.ReactElement; isDisabled?: boolean; onClick?: MouseEventHandler; + name?: string; }; -const MenuItem: React.FC = ({ className, children, isDisabled = false, onClick }: Props) => { - const menuItemClassNames = classNames(cssReset.ventura, styles.table, className); +const MenuItem = React.forwardRef( + ({ className, children, isDisabled = false, onClick, name, prefixIcon }: Props, ref) => { + const menuItemClassNames = classNames(cssReset.ventura, styles.menuItem, className); - return ( - - ); -}; + const labelClassNames = classNames(styles.label, { + [styles.labelWithPrefixIcon]: Boolean(prefixIcon), + }); + + return ( + + ); + }, +); export default MenuItem; diff --git a/src/components/Popover/Popover.mdx b/src/components/Popover/Popover.mdx index e4cf1727..b5b0853b 100644 --- a/src/components/Popover/Popover.mdx +++ b/src/components/Popover/Popover.mdx @@ -5,15 +5,15 @@ route: /dropdown-menu --- import { Playground, Props } from 'docz'; -import { Popover, Menu, Button } from '../../index.ts'; +import { Popover, Menu, Button, Trash } from '../../index.ts'; # Popover -A Popover is used to display extra information +A Popover is used to display extra information or actions in a compact way. ## Best practices -- Check +- Make sure the correct popover events are covered, depending on the event. (Click, hover, focus, etc...). ## Examples @@ -24,20 +24,40 @@ A Popover is used to display extra information const [isVisible, setIsVisible] = React.useState(false); const popoverContent = ( - Option 1 + Option 1 + }>Option 2 ); const onClose = () => { setIsVisible(false); } return ( - <> - - - -
-
-
- + + + + ); + }} + +
+ +### Popover on hover + + + {() => { + const [isVisible, setIsVisible] = React.useState(false); + const popoverContent = ( + + A handy tooltip! + ); + const closePopover = () => { + setIsVisible(false); + } + const openPopover = () => { + setIsVisible(true); + } + return ( + + + ); }} diff --git a/src/components/Popover/Popover.module.css b/src/components/Popover/Popover.module.css index a2b95670..f1067e71 100644 --- a/src/components/Popover/Popover.module.css +++ b/src/components/Popover/Popover.module.css @@ -1,5 +1,10 @@ -.menu { +.contentContainer { border-radius: var(--border-radius-small); background-color: var(--color-secondary); box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.1); + z-index: 100; +} + +.triggerContainer { + display: inline-block; } diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx index 43df8072..ff90b6f8 100644 --- a/src/components/Popover/Popover.tsx +++ b/src/components/Popover/Popover.tsx @@ -52,14 +52,16 @@ const Popover: React.FC = ({ return ( <> -
{children}
+
+ {children} +
{isVisible && (
{content} From 23112bfac082004d9fc4b67a5d120b0a519b5927 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 17:25:06 +0200 Subject: [PATCH 10/13] test(Menu): craete basic tests --- src/components/Menu/Menu.test.tsx | 47 ++++++++++ .../Menu/__snapshots__/Menu.test.tsx.snap | 89 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 src/components/Menu/Menu.test.tsx create mode 100644 src/components/Menu/__snapshots__/Menu.test.tsx.snap diff --git a/src/components/Menu/Menu.test.tsx b/src/components/Menu/Menu.test.tsx new file mode 100644 index 00000000..d3776743 --- /dev/null +++ b/src/components/Menu/Menu.test.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; + +import { GitHub } from '../../index'; +import Menu from './Menu'; + +describe('TextArea', () => { + test('default snapshot', () => { + const component = ( + + Option 1 + }>Option 2 + + ); + const { asFragment } = render(component); + expect(asFragment()).toMatchSnapshot(); + }); + + test('default snapshot', () => { + const component = ( + + Option 1 + }>Option 2 + + ); + const { asFragment } = render(component); + expect(asFragment()).toMatchSnapshot(); + }); + + test('onClick should be triggered when clicked', () => { + const onClickFn = jest.fn(); + const component = ( + + + Option 1 + + }>Option 2 + + ); + const { getByTestId } = render(component); + + const menuItem = getByTestId('menu-item-click'); + fireEvent.click(menuItem); + + expect(onClickFn).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/components/Menu/__snapshots__/Menu.test.tsx.snap b/src/components/Menu/__snapshots__/Menu.test.tsx.snap new file mode 100644 index 00000000..09caaa99 --- /dev/null +++ b/src/components/Menu/__snapshots__/Menu.test.tsx.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TextArea default snapshot 1`] = ` + + + +`; + +exports[`TextArea default snapshot 2`] = ` + + + +`; From 76d47e9db0f065651b963a5522b15547cbbdff17 Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 17:25:32 +0200 Subject: [PATCH 11/13] test(Menu): remove duplicate test --- src/components/Menu/Menu.test.tsx | 11 ----- .../Menu/__snapshots__/Menu.test.tsx.snap | 44 ------------------- 2 files changed, 55 deletions(-) diff --git a/src/components/Menu/Menu.test.tsx b/src/components/Menu/Menu.test.tsx index d3776743..9042c962 100644 --- a/src/components/Menu/Menu.test.tsx +++ b/src/components/Menu/Menu.test.tsx @@ -16,17 +16,6 @@ describe('TextArea', () => { expect(asFragment()).toMatchSnapshot(); }); - test('default snapshot', () => { - const component = ( - - Option 1 - }>Option 2 - - ); - const { asFragment } = render(component); - expect(asFragment()).toMatchSnapshot(); - }); - test('onClick should be triggered when clicked', () => { const onClickFn = jest.fn(); const component = ( diff --git a/src/components/Menu/__snapshots__/Menu.test.tsx.snap b/src/components/Menu/__snapshots__/Menu.test.tsx.snap index 09caaa99..c2da1921 100644 --- a/src/components/Menu/__snapshots__/Menu.test.tsx.snap +++ b/src/components/Menu/__snapshots__/Menu.test.tsx.snap @@ -43,47 +43,3 @@ exports[`TextArea default snapshot 1`] = `
`; - -exports[`TextArea default snapshot 2`] = ` - - - -`; From e63e68d3e1aeae36b6fc222fc9d5c0da110fa31b Mon Sep 17 00:00:00 2001 From: Bram Vanhoutte Date: Sun, 4 Oct 2020 17:40:30 +0200 Subject: [PATCH 12/13] test(Popover): create tests for popover --- src/components/Menu/Menu.test.tsx | 2 +- .../Menu/__snapshots__/Menu.test.tsx.snap | 2 +- src/components/Popover/Popover.test.tsx | 53 ++++++++++++++ .../__snapshots__/Popover.test.tsx.snap | 73 +++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 src/components/Popover/Popover.test.tsx create mode 100644 src/components/Popover/__snapshots__/Popover.test.tsx.snap diff --git a/src/components/Menu/Menu.test.tsx b/src/components/Menu/Menu.test.tsx index 9042c962..5a11605b 100644 --- a/src/components/Menu/Menu.test.tsx +++ b/src/components/Menu/Menu.test.tsx @@ -4,7 +4,7 @@ import { fireEvent, render } from '@testing-library/react'; import { GitHub } from '../../index'; import Menu from './Menu'; -describe('TextArea', () => { +describe('Menu', () => { test('default snapshot', () => { const component = ( diff --git a/src/components/Menu/__snapshots__/Menu.test.tsx.snap b/src/components/Menu/__snapshots__/Menu.test.tsx.snap index c2da1921..8d77a07f 100644 --- a/src/components/Menu/__snapshots__/Menu.test.tsx.snap +++ b/src/components/Menu/__snapshots__/Menu.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`TextArea default snapshot 1`] = ` +exports[`Menu default snapshot 1`] = `