mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
UXMultichain: update segmented tabs (#18283)
* update tabs to functional component * updated Tabs to use Box * updated css for tab * updated css on home screen * updated snapshot * fixed defaultkey and active key value for new keys
This commit is contained in:
parent
01057f9824
commit
0614b3db68
@ -2,6 +2,7 @@ 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 {
|
||||
@ -17,7 +18,8 @@ export const DropdownTab = (props) => {
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<li
|
||||
<Box
|
||||
as="li"
|
||||
className={classnames('tab', className, {
|
||||
'tab--active': isActive,
|
||||
[activeClassName]: activeClassName && isActive,
|
||||
@ -33,7 +35,7 @@ export const DropdownTab = (props) => {
|
||||
selectedOption={selectedOption}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</li>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,10 +7,7 @@
|
||||
-moz-transform: translateZ(0);
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
border-bottom: 1px solid var(--color-border-default);
|
||||
background-color: var(--color-background-default);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
|
@ -1,11 +1,6 @@
|
||||
.tab {
|
||||
button {
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: unset;
|
||||
font-size: unset;
|
||||
color: unset;
|
||||
|
@ -1,6 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
BLOCK_SIZES,
|
||||
DISPLAY,
|
||||
TEXT_ALIGN,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import Box from '../../box';
|
||||
|
||||
const Tab = (props) => {
|
||||
const {
|
||||
@ -15,7 +21,8 @@ const Tab = (props) => {
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<li
|
||||
<Box
|
||||
as="li"
|
||||
className={classnames('tab', className, {
|
||||
'tab--active': isActive,
|
||||
[activeClassName]: activeClassName && isActive,
|
||||
@ -27,8 +34,16 @@ const Tab = (props) => {
|
||||
}}
|
||||
key={tabKey}
|
||||
>
|
||||
<button>{name}</button>
|
||||
</li>
|
||||
<Box
|
||||
as="button"
|
||||
padding={2}
|
||||
textAlign={TEXT_ALIGN.CENTER}
|
||||
display={DISPLAY.BLOCK}
|
||||
width={BLOCK_SIZES.FULL}
|
||||
>
|
||||
{name}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,93 +1,26 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import Box from '../box';
|
||||
import {
|
||||
BackgroundColor,
|
||||
DISPLAY,
|
||||
JustifyContent,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
export default class Tabs extends Component {
|
||||
static defaultProps = {
|
||||
defaultActiveTabKey: null,
|
||||
onTabClick: null,
|
||||
tabsClassName: undefined,
|
||||
subHeader: null,
|
||||
const Tabs = ({
|
||||
defaultActiveTabKey,
|
||||
onTabClick,
|
||||
children,
|
||||
tabsClassName,
|
||||
subHeader,
|
||||
}) => {
|
||||
// This ignores any 'null' child elements that are a result of a conditional
|
||||
// based on a feature flag setting.
|
||||
const _getValidChildren = () => {
|
||||
return React.Children.toArray(children).filter(Boolean);
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
defaultActiveTabKey: PropTypes.string,
|
||||
onTabClick: PropTypes.func,
|
||||
children: PropTypes.node.isRequired,
|
||||
tabsClassName: PropTypes.string,
|
||||
subHeader: PropTypes.node,
|
||||
};
|
||||
|
||||
state = {
|
||||
activeTabIndex: Math.max(
|
||||
this._findChildByKey(this.props.defaultActiveTabKey),
|
||||
0,
|
||||
),
|
||||
};
|
||||
|
||||
handleTabClick(tabIndex, tabKey) {
|
||||
const { onTabClick } = this.props;
|
||||
const { activeTabIndex } = this.state;
|
||||
|
||||
if (tabIndex !== activeTabIndex) {
|
||||
this.setState(
|
||||
{
|
||||
activeTabIndex: tabIndex,
|
||||
},
|
||||
() => {
|
||||
if (onTabClick) {
|
||||
onTabClick(tabKey);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderTabs() {
|
||||
const numberOfTabs = React.Children.count(this._getValidChildren());
|
||||
|
||||
return React.Children.map(this._getValidChildren(), (child, index) => {
|
||||
const tabKey = child?.props.tabKey;
|
||||
return (
|
||||
child &&
|
||||
React.cloneElement(child, {
|
||||
onClick: (idx) => this.handleTabClick(idx, tabKey),
|
||||
tabIndex: index,
|
||||
isActive: numberOfTabs > 1 && index === this.state.activeTabIndex,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderActiveTabContent() {
|
||||
const children = this._getValidChildren();
|
||||
const { activeTabIndex } = this.state;
|
||||
|
||||
if (
|
||||
(Array.isArray(children) && !children[activeTabIndex]) ||
|
||||
(!Array.isArray(children) && activeTabIndex !== 0)
|
||||
) {
|
||||
throw new Error(`Tab at index '${activeTabIndex}' does not exist`);
|
||||
}
|
||||
|
||||
return children[activeTabIndex]
|
||||
? children[activeTabIndex].props.children
|
||||
: children.props.children;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tabsClassName, subHeader } = this.props;
|
||||
return (
|
||||
<div className="tabs">
|
||||
<ul className={classnames('tabs__list', tabsClassName)}>
|
||||
{this.renderTabs()}
|
||||
</ul>
|
||||
{subHeader}
|
||||
<div className="tabs__content">{this.renderActiveTabContent()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the child with the given key
|
||||
*
|
||||
@ -95,15 +28,74 @@ export default class Tabs extends Component {
|
||||
* @returns {number} the index of the child with the given name
|
||||
* @private
|
||||
*/
|
||||
_findChildByKey(tabKey) {
|
||||
return this._getValidChildren().findIndex(
|
||||
(c) => c?.props.tabKey === tabKey,
|
||||
);
|
||||
}
|
||||
const _findChildByKey = (tabKey) => {
|
||||
return _getValidChildren().findIndex((c) => c?.props.tabKey === tabKey);
|
||||
};
|
||||
|
||||
// This ignores any 'null' child elements that are a result of a conditional
|
||||
// based on a feature flag setting.
|
||||
_getValidChildren() {
|
||||
return React.Children.toArray(this.props.children).filter(Boolean);
|
||||
}
|
||||
}
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(() =>
|
||||
Math.max(_findChildByKey(defaultActiveTabKey), 0),
|
||||
);
|
||||
|
||||
const handleTabClick = (tabIndex, tabKey) => {
|
||||
if (tabIndex !== activeTabIndex) {
|
||||
setActiveTabIndex(tabIndex);
|
||||
onTabClick?.(tabKey);
|
||||
}
|
||||
};
|
||||
|
||||
const renderTabs = () => {
|
||||
const numberOfTabs = React.Children.count(_getValidChildren());
|
||||
|
||||
return React.Children.map(_getValidChildren(), (child, index) => {
|
||||
const tabKey = child?.props.tabKey;
|
||||
return (
|
||||
child &&
|
||||
React.cloneElement(child, {
|
||||
onClick: (idx) => handleTabClick(idx, tabKey),
|
||||
tabIndex: index,
|
||||
isActive: numberOfTabs > 1 && index === activeTabIndex,
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
const renderActiveTabContent = () => {
|
||||
const validChildren = _getValidChildren();
|
||||
|
||||
if (
|
||||
(Array.isArray(validChildren) && !validChildren[activeTabIndex]) ||
|
||||
(!Array.isArray(validChildren) && activeTabIndex !== 0)
|
||||
) {
|
||||
throw new Error(`Tab at index '${activeTabIndex}' does not exist`);
|
||||
}
|
||||
|
||||
return validChildren[activeTabIndex]
|
||||
? validChildren[activeTabIndex].props.children
|
||||
: validChildren.props.children;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className="tabs">
|
||||
<Box
|
||||
as="ul"
|
||||
display={DISPLAY.FLEX}
|
||||
justifyContent={JustifyContent.flexStart}
|
||||
backgroundColor={BackgroundColor.backgroundDefault}
|
||||
className={classnames('tabs__list', tabsClassName)}
|
||||
gap={1}
|
||||
>
|
||||
{renderTabs()}
|
||||
</Box>
|
||||
{subHeader}
|
||||
<Box className="tabs__content">{renderActiveTabContent()}</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tabs;
|
||||
Tabs.propTypes = {
|
||||
defaultActiveTabKey: PropTypes.string,
|
||||
onTabClick: PropTypes.func,
|
||||
children: PropTypes.node.isRequired,
|
||||
tabsClassName: PropTypes.string,
|
||||
subHeader: PropTypes.node,
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ export default {
|
||||
},
|
||||
},
|
||||
onTabClick: { action: 'onTabClick' },
|
||||
onChange: { action: 'onChange' },
|
||||
},
|
||||
args: {
|
||||
tabs: [
|
||||
@ -42,16 +43,24 @@ export const DefaultStory = (args) => {
|
||||
onTabClick={args.onTabClick}
|
||||
>
|
||||
{args.tabs.map((tabProps, i) => renderTab(tabProps, i, args.t))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
||||
export const DropdownStory = (args) => {
|
||||
return (
|
||||
<Tabs>
|
||||
<DropdownTab
|
||||
options={[
|
||||
{ name: 'Insight Snap', value: 'Insight Snap' },
|
||||
{ name: 'Tenderly Insight', value: 'Tenderly Insight' },
|
||||
]}
|
||||
onChange={args.onChange}
|
||||
>
|
||||
This is a dropdown Tab
|
||||
</DropdownTab>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
@ -278,35 +278,41 @@ exports[`Confirm Transaction Base should match snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tabs"
|
||||
class="box tabs box--flex-direction-row"
|
||||
>
|
||||
<ul
|
||||
class="tabs__list"
|
||||
class="box tabs__list box--display-flex box--gap-1 box--flex-direction-row box--justify-content-flex-start box--background-color-background-default"
|
||||
>
|
||||
<li
|
||||
class="tab confirm-page-container-content__tab tab--active"
|
||||
class="box tab confirm-page-container-content__tab tab--active box--flex-direction-row"
|
||||
>
|
||||
<button>
|
||||
<button
|
||||
class="box box--padding-2 box--display-block box--flex-direction-row box--text-align-center box--width-full"
|
||||
>
|
||||
Details
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="tab confirm-page-container-content__tab"
|
||||
class="box tab confirm-page-container-content__tab box--flex-direction-row"
|
||||
>
|
||||
<button>
|
||||
<button
|
||||
class="box box--padding-2 box--display-block box--flex-direction-row box--text-align-center box--width-full"
|
||||
>
|
||||
Data
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="tab confirm-page-container-content__tab"
|
||||
class="box tab confirm-page-container-content__tab box--flex-direction-row"
|
||||
>
|
||||
<button>
|
||||
<button
|
||||
class="box box--padding-2 box--display-block box--flex-direction-row box--text-align-center box--width-full"
|
||||
>
|
||||
Hex
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="tabs__content"
|
||||
class="box tabs__content box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="confirm-page-container-content__details"
|
||||
|
@ -23,7 +23,7 @@
|
||||
// TODO: fix style import order so this isn't required to override specificity of `tab` class
|
||||
&__main-view &__tabs {
|
||||
border: none;
|
||||
box-shadow: var(--shadow-size-xs) var(--color-shadow-default);
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
&__main-view &__tab {
|
||||
@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
&__main-view &__tab button {
|
||||
padding: 16px 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
&__connect-status-text {
|
||||
|
Loading…
Reference in New Issue
Block a user