mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
[FLASK] Redesign dropdown-tab
(#18546)
This commit is contained in:
parent
ae0af1b283
commit
76d79d9cce
@ -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 (
|
||||
<Box
|
||||
as="li"
|
||||
className={classnames('tab', className, {
|
||||
'tab--active': isActive,
|
||||
[activeClassName]: activeClassName && isActive,
|
||||
})}
|
||||
data-testid={dataTestId}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onClick(tabIndex);
|
||||
}}
|
||||
>
|
||||
<Dropdown
|
||||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
@ -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;
|
||||
}
|
||||
}
|
160
ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.js
Normal file
160
ui/components/ui/tabs/flask/dropdown-tab/dropdown-tab.js
Normal file
@ -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 (
|
||||
<Box
|
||||
as="li"
|
||||
className={classnames('tab', className, {
|
||||
'tab--active': isActive,
|
||||
[activeClassName]: activeClassName && isActive,
|
||||
})}
|
||||
data-testid={dataTestId}
|
||||
onClick={openDropdown}
|
||||
dataTestId={dataTestId}
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
flexWrap={FLEX_WRAP.NO_WRAP}
|
||||
height={BLOCK_SIZES.FULL}
|
||||
style={{ cursor: 'pointer', overflow: 'hidden' }}
|
||||
title={selectedOptionName}
|
||||
>
|
||||
<Box alignItems={AlignItems.center} padding={2}>
|
||||
<Text
|
||||
variant={TextVariant.inherit}
|
||||
style={{
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{selectedOptionName}
|
||||
</Text>
|
||||
<Icon marginLeft={2} name={IconName.ArrowDown} size={IconSize.Sm} />
|
||||
</Box>
|
||||
{isOpen && (
|
||||
<Box
|
||||
backgroundColor={BackgroundColor.backgroundDefault}
|
||||
borderStyle={BorderStyle.solid}
|
||||
borderColor={BorderColor.borderDefault}
|
||||
borderRadius={BorderRadius.SM}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
flexWrap={FLEX_WRAP.NO_WRAP}
|
||||
style={{ position: 'absolute', maxWidth: '170px' }}
|
||||
ref={dropdownRef}
|
||||
>
|
||||
{options.map((option, i) => (
|
||||
<Text
|
||||
key={i}
|
||||
marginTop={1}
|
||||
marginBottom={1}
|
||||
variant={TextVariant.bodySm}
|
||||
onClick={(event) => selectOption(event, option)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
textTransform: 'none',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{option.name}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
@ -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(<DropdownTab {...args} />);
|
||||
|
||||
expect(getByText(args.options[0].name)).toBeDefined();
|
||||
});
|
||||
|
||||
it('registers click', () => {
|
||||
const { container } = render(<DropdownTab {...args} />);
|
||||
|
||||
fireEvent.click(container.firstChild);
|
||||
|
||||
expect(onClick).toHaveBeenCalledWith(args.tabIndex);
|
||||
});
|
||||
|
||||
it('registers selection', () => {
|
||||
const { container, getByText } = render(<DropdownTab {...args} />);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
@ -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 };
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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) =>
|
||||
|
Loading…
Reference in New Issue
Block a user