diff --git a/ui/components/ui/tabs/dropdown-tab/dropdown-tab.js b/ui/components/ui/tabs/dropdown-tab/dropdown-tab.js deleted file mode 100644 index 6da690afc..000000000 --- a/ui/components/ui/tabs/dropdown-tab/dropdown-tab.js +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import Dropdown from '../../dropdown'; -import Box from '../../box'; - -export const DropdownTab = (props) => { - const { - activeClassName, - className, - 'data-testid': dataTestId, - isActive, - onClick, - onChange, - tabIndex, - options, - selectedOption, - } = props; - - return ( - { - event.preventDefault(); - onClick(tabIndex); - }} - > - - - ); -}; - -DropdownTab.propTypes = { - activeClassName: PropTypes.string, - className: PropTypes.string, - 'data-testid': PropTypes.string, - isActive: PropTypes.bool, // required, but added using React.cloneElement - options: PropTypes.arrayOf( - PropTypes.exact({ - name: PropTypes.string, - value: PropTypes.string.isRequired, - }), - ).isRequired, - selectedOption: PropTypes.string, - onChange: PropTypes.func, - onClick: PropTypes.func, - tabIndex: PropTypes.number, // required, but added using React.cloneElement -}; - -DropdownTab.defaultProps = { - activeClassName: undefined, - className: undefined, - onChange: undefined, - onClick: undefined, - selectedOption: undefined, -}; diff --git a/ui/components/ui/tabs/dropdown-tab/index.scss b/ui/components/ui/tabs/dropdown-tab/index.scss deleted file mode 100644 index 01ee7f077..000000000 --- a/ui/components/ui/tabs/dropdown-tab/index.scss +++ /dev/null @@ -1,23 +0,0 @@ -.tab { - .dropdown__select { - border: none; - font-size: unset; - width: 100%; - background-color: unset; - padding-left: 8px; - padding-right: 20px; - line-height: unset; - - option { - background-color: var(--color-background-default); - } - - &:focus-visible { - outline: none; - } - } - - .dropdown__icon-caret-down { - right: 0; - } -} diff --git a/ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.js b/ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.js new file mode 100644 index 000000000..bbb48602c --- /dev/null +++ b/ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.js @@ -0,0 +1,160 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import Box from '../../../box'; +import { + AlignItems, + BLOCK_SIZES, + BackgroundColor, + BorderColor, + BorderRadius, + BorderStyle, + DISPLAY, + FLEX_DIRECTION, + FLEX_WRAP, + TextVariant, +} from '../../../../../helpers/constants/design-system'; +import { Icon, IconName, IconSize, Text } from '../../../../component-library'; + +export const DropdownTab = ({ + activeClassName, + className, + 'data-testid': dataTestId, + isActive, + onClick, + onChange, + tabIndex, + options, + selectedOption, +}) => { + const [isOpen, setIsOpen] = useState(false); + + const dropdownRef = useRef(null); + + const selectOption = useCallback( + (event, option) => { + event.stopPropagation(); + onChange(option.value); + setIsOpen(false); + }, + [onChange], + ); + + const openDropdown = (event) => { + event.preventDefault(); + setIsOpen(true); + onClick(tabIndex); + }; + + const selectedOptionName = options.find( + (option) => option.value === selectedOption, + )?.name; + + useEffect(() => { + function handleClickOutside(event) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target) && + isOpen + ) { + setIsOpen(false); + } + } + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [dropdownRef, isOpen]); + + return ( + + + + {selectedOptionName} + + + + {isOpen && ( + + {options.map((option, i) => ( + selectOption(event, option)} + style={{ + cursor: 'pointer', + textTransform: 'none', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }} + > + {option.name} + + ))} + + )} + + ); +}; + +DropdownTab.propTypes = { + activeClassName: PropTypes.string, + className: PropTypes.string, + 'data-testid': PropTypes.string, + isActive: PropTypes.bool, // required, but added using React.cloneElement + options: PropTypes.arrayOf( + PropTypes.exact({ + name: PropTypes.string, + value: PropTypes.string.isRequired, + }), + ).isRequired, + selectedOption: PropTypes.string, + onChange: PropTypes.func, + onClick: PropTypes.func, + tabIndex: PropTypes.number, // required, but added using React.cloneElement +}; + +DropdownTab.defaultProps = { + activeClassName: undefined, + className: undefined, + onChange: undefined, + onClick: undefined, + selectedOption: undefined, +}; diff --git a/ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.test.js b/ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.test.js new file mode 100644 index 000000000..61b19a847 --- /dev/null +++ b/ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.test.js @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import DropdownTab from '.'; + +describe('DropdownTab', () => { + const onChange = jest.fn(); + const onClick = jest.fn(); + let args; + beforeEach(() => { + args = { + activeClassName: 'active', + tabIndex: 1, + options: [ + { name: 'foo', value: 'foo' }, + { name: 'bar', value: 'bar' }, + ], + selectedOption: 'foo', + onChange, + onClick, + }; + }); + it('should render the DropdownTab component without crashing', () => { + const { getByText } = render(); + + expect(getByText(args.options[0].name)).toBeDefined(); + }); + + it('registers click', () => { + const { container } = render(); + + fireEvent.click(container.firstChild); + + expect(onClick).toHaveBeenCalledWith(args.tabIndex); + }); + + it('registers selection', () => { + const { container, getByText } = render(); + + fireEvent.click(container.firstChild); + + const element = getByText(args.options[1].name); + + fireEvent.click(element); + + expect(onClick).toHaveBeenCalledWith(args.tabIndex); + expect(onChange).toHaveBeenCalledWith(args.options[1].value); + }); +}); diff --git a/ui/components/ui/tabs/dropdown-tab/index.js b/ui/components/ui/tabs/flask/dropdown-tab/index.js similarity index 100% rename from ui/components/ui/tabs/dropdown-tab/index.js rename to ui/components/ui/tabs/flask/dropdown-tab/index.js diff --git a/ui/components/ui/tabs/index.js b/ui/components/ui/tabs/index.js index c20ebbfc1..43366ec6f 100644 --- a/ui/components/ui/tabs/index.js +++ b/ui/components/ui/tabs/index.js @@ -1,5 +1,4 @@ import Tabs from './tabs.component'; import Tab from './tab'; -import DropdownTab from './dropdown-tab'; -export { Tabs, Tab, DropdownTab }; +export { Tabs, Tab }; diff --git a/ui/components/ui/tabs/index.scss b/ui/components/ui/tabs/index.scss index 4398ddf50..20ee131b7 100644 --- a/ui/components/ui/tabs/index.scss +++ b/ui/components/ui/tabs/index.scss @@ -1,5 +1,4 @@ @import 'tab/index'; -@import 'dropdown-tab/index'; .tabs { flex-grow: 1; @@ -11,6 +10,5 @@ position: sticky; top: 0; z-index: 2; - overflow: hidden; } } diff --git a/ui/components/ui/tabs/tabs.stories.js b/ui/components/ui/tabs/tabs.stories.js index d8d4c4b39..09f19f0bc 100644 --- a/ui/components/ui/tabs/tabs.stories.js +++ b/ui/components/ui/tabs/tabs.stories.js @@ -1,5 +1,5 @@ import React from 'react'; -import DropdownTab from './dropdown-tab'; +import DropdownTab from './flask/dropdown-tab'; import Tab from './tab/tab.component'; import Tabs from './tabs.component'; diff --git a/ui/hooks/useTransactionInsights.js b/ui/hooks/useTransactionInsights.js index ed7916c4f..d20dbfcc7 100644 --- a/ui/hooks/useTransactionInsights.js +++ b/ui/hooks/useTransactionInsights.js @@ -5,7 +5,8 @@ import { CHAIN_ID_TO_NETWORK_ID_MAP } from '../../shared/constants/network'; import { stripHexPrefix } from '../../shared/modules/hexstring-utils'; import { TransactionType } from '../../shared/constants/transaction'; import { getInsightSnaps } from '../selectors'; -import { DropdownTab, Tab } from '../components/ui/tabs'; +import { Tab } from '../components/ui/tabs'; +import DropdownTab from '../components/ui/tabs/flask/dropdown-tab'; import { SnapInsight } from '../components/app/confirm-page-container/flask/snap-insight'; const isAllowedTransactionTypes = (transactionType) =>