diff --git a/packages/account/src/Components/api-token/api-token-clipboard.jsx b/packages/account/src/Components/api-token/api-token-clipboard.jsx index f88871ce12ab..d9f7a3a95c1d 100644 --- a/packages/account/src/Components/api-token/api-token-clipboard.jsx +++ b/packages/account/src/Components/api-token/api-token-clipboard.jsx @@ -45,12 +45,16 @@ const ApiTokenClipboard = ({ scopes, text_copy, info_message, success_message, p if (!is_copied) setIsPopoverOpen(false); }; - const copyToClipboard = text => { + const copyToClipboard = async text => { const textField = document.createElement('textarea'); textField.innerText = text; document.body.appendChild(textField); textField.select(); - document.execCommand('copy'); + if ('clipboard' in navigator) { + await navigator.clipboard.writeText(text); + } else { + document.execCommand('copy'); + } textField.remove(); }; diff --git a/packages/components/src/components/clipboard/clipboard.jsx b/packages/components/src/components/clipboard/clipboard.tsx similarity index 58% rename from packages/components/src/components/clipboard/clipboard.jsx rename to packages/components/src/components/clipboard/clipboard.tsx index 4db50ba5d1bc..ae6120697a3e 100644 --- a/packages/components/src/components/clipboard/clipboard.jsx +++ b/packages/components/src/components/clipboard/clipboard.tsx @@ -1,9 +1,20 @@ import React from 'react'; import classNames from 'classnames'; -import PropTypes from 'prop-types'; import { useIsMounted } from '@deriv/shared'; import Popover from '../popover'; import Icon from '../icon'; +import { TPopoverProps } from '../types'; + +type TClipboard = { + text_copy: string; + icon?: string; + info_message?: string; + success_message?: string; + className?: string; + popoverClassName?: string; + popoverAlignment?: 'top' | 'right' | 'bottom' | 'left'; + popover_props?: Partial; +}; const Clipboard = ({ text_copy, @@ -14,30 +25,38 @@ const Clipboard = ({ popoverClassName, popover_props = {}, popoverAlignment = 'bottom', -}) => { +}: TClipboard) => { const [is_copied, setIsCopied] = React.useState(false); const isMounted = useIsMounted(); - let timeout_clipboard = null; + let timeout_clipboard: NodeJS.Timeout; - const copyToClipboard = async text => { - await navigator.clipboard.writeText(text); + const copyToClipboard = async (text: string) => { + const textField = document.createElement('textarea'); + textField.innerText = text; + document.body.appendChild(textField); + textField.select(); + if ('clipboard' in navigator) { + await navigator.clipboard.writeText(text); + } else { + document.execCommand('copy'); + } + textField.remove(); }; - const onClick = event => { - copyToClipboard(text_copy).then(() => { - setIsCopied(true); - timeout_clipboard = setTimeout(() => { - if (isMounted()) { - setIsCopied(false); - } - }, 2000); - event.stopPropagation(); - }); + const onClick = (event: { stopPropagation: () => void }) => { + copyToClipboard(text_copy); + setIsCopied(true); + timeout_clipboard = setTimeout(() => { + if (isMounted()) { + setIsCopied(false); + } + }, 2000); + event.stopPropagation(); }; React.useEffect(() => { return () => clearTimeout(timeout_clipboard); - }, [timeout_clipboard]); + }, []); return ( <> @@ -67,14 +86,5 @@ const Clipboard = ({ ); }; -Clipboard.propTypes = { - text_copy: PropTypes.string, - icon: PropTypes.string, - info_message: PropTypes.string, - success_message: PropTypes.string, - className: PropTypes.string, - popoverClassName: PropTypes.string, - popoverAlignment: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), - popover_props: PropTypes.object, -}; + export default Clipboard; diff --git a/packages/components/src/components/clipboard/index.js b/packages/components/src/components/clipboard/index.js index 0574bfb181c8..8b9dcd2120d3 100644 --- a/packages/components/src/components/clipboard/index.js +++ b/packages/components/src/components/clipboard/index.js @@ -1,4 +1,4 @@ -import Clipboard from './clipboard.jsx'; +import Clipboard from './clipboard'; import './clipboard.scss'; export default Clipboard; diff --git a/packages/components/src/components/popover-mobile/popover-mobile.jsx b/packages/components/src/components/popover-mobile/popover-mobile.jsx index 1afea5b44d8a..55c2bc6609d4 100644 --- a/packages/components/src/components/popover-mobile/popover-mobile.jsx +++ b/packages/components/src/components/popover-mobile/popover-mobile.jsx @@ -4,7 +4,7 @@ import React from 'react'; import { isMobile } from '@deriv/shared'; import Button from '../button/button'; import Modal from '../modal/modal'; -import Popover from '../popover/popover.jsx'; +import Popover from '../popover/popover'; import Text from '../text/text'; import './popover-mobile.scss'; diff --git a/packages/components/src/components/popover/index.js b/packages/components/src/components/popover/index.ts similarity index 57% rename from packages/components/src/components/popover/index.js rename to packages/components/src/components/popover/index.ts index 97c915ca5f66..b89dda2d87ec 100644 --- a/packages/components/src/components/popover/index.js +++ b/packages/components/src/components/popover/index.ts @@ -1,4 +1,4 @@ -import Popover from './popover.jsx'; +import Popover from './popover'; import './popover.scss'; export default Popover; diff --git a/packages/components/src/components/popover/popover.jsx b/packages/components/src/components/popover/popover.tsx similarity index 83% rename from packages/components/src/components/popover/popover.jsx rename to packages/components/src/components/popover/popover.tsx index a2b21c265352..5ed5a4a0aea5 100644 --- a/packages/components/src/components/popover/popover.jsx +++ b/packages/components/src/components/popover/popover.tsx @@ -1,10 +1,10 @@ import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; +import React, { RefObject } from 'react'; import TinyPopover, { ArrowContainer } from 'react-tiny-popover'; import Icon from '../icon'; import Text from '../text'; import { useHover, useHoverCallback } from '../../hooks/use-hover'; +import { TPopoverProps } from '../types'; const Popover = ({ alignment, @@ -21,19 +21,19 @@ const Popover = ({ id, is_open, is_bubble_hover_enabled, - margin, + margin = 0, message, onBubbleClose, onBubbleOpen, - onClick = () => {}, - relative_render, - should_disable_pointer_events, + onClick = () => undefined, + relative_render = false, + should_disable_pointer_events = false, should_show_cursor, window_border, - zIndex, -}) => { - const ref = React.useRef(); - const [popover_ref, setPopoverRef] = React.useState(undefined); + zIndex = '1', +}: React.PropsWithChildren>) => { + const ref = React.useRef(); + const [popover_ref, setPopoverRef] = React.useState(undefined); const [hover_ref, is_hovered] = useHover(null, true); const [bubble_hover_ref, is_bubble_hovered] = useHoverCallback(); @@ -54,18 +54,22 @@ const Popover = ({ const icon_class_name = classNames(classNameTargetIcon, icon); return ( -
+
} + className={classNames({ 'dc-popover__wrapper': relative_render })} + onClick={onClick} + > {relative_render && (
-
+
} className='dc-popover__container-relative' />
)} {(popover_ref || !relative_render) && ( void} > {!disable_message_icon && icon === 'info' && ( @@ -193,38 +197,4 @@ const Popover = ({ ); }; -Popover.defaultProps = { - margin: 0, - relative_render: false, - should_disable_pointer_events: false, - zIndex: 1, -}; - -Popover.propTypes = { - alignment: PropTypes.string, - children: PropTypes.node, - className: PropTypes.string, - classNameBubble: PropTypes.string, - classNameTarget: PropTypes.string, - classNameTargetIcon: PropTypes.string, - counter: PropTypes.number, - disable_message_icon: PropTypes.bool, - disable_target_icon: PropTypes.bool, - has_error: PropTypes.bool, - icon: PropTypes.oneOf(['info', 'question', 'dot', 'counter']), - id: PropTypes.string, - is_bubble_hover_enabled: PropTypes.bool, - is_open: PropTypes.bool, - relative_render: PropTypes.bool, - margin: PropTypes.number, - message: PropTypes.oneOfType([PropTypes.node, PropTypes.object, PropTypes.string]), - onBubbleOpen: PropTypes.func, - onBubbleClose: PropTypes.func, - onClick: PropTypes.func, - should_disable_pointer_events: PropTypes.bool, - should_show_cursor: PropTypes.bool, - zIndex: PropTypes.number, - window_border: PropTypes.number, -}; - export default Popover; diff --git a/packages/components/src/components/types/icons.types.ts b/packages/components/src/components/types/icons.types.ts index 32c1d28261a0..9da0763a52cd 100644 --- a/packages/components/src/components/types/icons.types.ts +++ b/packages/components/src/components/types/icons.types.ts @@ -1,3 +1,4 @@ +import { MouseEventHandler } from 'react'; import { TGenericObjectType } from './common.types'; export type TIconsManifest = Readonly<{ @@ -24,7 +25,7 @@ export type TIconProps = { data_testid?: string; height?: number | string; icon: string; - onClick?: () => void; + onClick?: MouseEventHandler; onMouseEnter?: () => void; onMouseLeave?: () => void; size?: number | string; diff --git a/packages/components/src/components/types/index.ts b/packages/components/src/components/types/index.ts index ea83d4d064c5..83bb54271728 100644 --- a/packages/components/src/components/types/index.ts +++ b/packages/components/src/components/types/index.ts @@ -1,4 +1,5 @@ import { TIconsManifest, TIconProps } from './icons.types'; import { TAccordionProps, TAccordionItem } from './accordion.types'; +import { TPopoverProps } from './popover.types'; -export type { TIconsManifest, TIconProps, TAccordionProps, TAccordionItem }; +export type { TIconsManifest, TIconProps, TAccordionProps, TAccordionItem, TPopoverProps }; diff --git a/packages/components/src/components/types/popover.types.ts b/packages/components/src/components/types/popover.types.ts new file mode 100644 index 000000000000..fa5f0da7ab5d --- /dev/null +++ b/packages/components/src/components/types/popover.types.ts @@ -0,0 +1,28 @@ +import React from 'react'; +import { Position } from 'react-tiny-popover'; + +export type TPopoverProps = { + alignment: Position; + className?: string; + classNameBubble?: string; + classNameTarget?: string; + classNameTargetIcon?: string; + counter?: number; + disable_message_icon?: boolean; + disable_target_icon?: boolean; + has_error?: boolean; + icon: 'info' | 'question' | 'dot' | 'counter'; + id: string; + is_bubble_hover_enabled?: boolean; + is_open?: boolean; + relative_render?: boolean; + margin: number; + message: React.ReactNode; + onBubbleOpen: () => void; + onBubbleClose: () => void; + onClick: () => void; + should_disable_pointer_events?: boolean; + should_show_cursor?: boolean; + zIndex: string; + window_border: number; +};