mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Provide New UI Components for EIP-1559 Designs (#11357)
This commit is contained in:
parent
85de65f470
commit
6fa36cdf51
@ -798,6 +798,9 @@
|
||||
"gasFee": {
|
||||
"message": "Gas Fee"
|
||||
},
|
||||
"gasFeeEstimate": {
|
||||
"message": "Estimate"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Gas Limit"
|
||||
},
|
||||
@ -1085,6 +1088,12 @@
|
||||
"max": {
|
||||
"message": "Max"
|
||||
},
|
||||
"maxFee": {
|
||||
"message": "Max fee"
|
||||
},
|
||||
"maxPriorityFee": {
|
||||
"message": "Max priority fee"
|
||||
},
|
||||
"memo": {
|
||||
"message": "memo"
|
||||
},
|
||||
@ -1478,6 +1487,9 @@
|
||||
"recipientAddressPlaceholder": {
|
||||
"message": "Search, public address (0x), or ENS"
|
||||
},
|
||||
"recommendedGasLabel": {
|
||||
"message": "Recommended"
|
||||
},
|
||||
"recoveryPhraseReminderBackupStart": {
|
||||
"message": "Start here"
|
||||
},
|
||||
|
@ -433,6 +433,7 @@ function getEnvironmentVariables({ devMode, testing }) {
|
||||
PUBNUB_SUB_KEY: process.env.PUBNUB_SUB_KEY || '',
|
||||
PUBNUB_PUB_KEY: process.env.PUBNUB_PUB_KEY || '',
|
||||
CONF: devMode ? metamaskrc : {},
|
||||
SHOW_EIP_1559_UI: process.env.SHOW_EIP_1559_UI === '1',
|
||||
SENTRY_DSN: process.env.SENTRY_DSN,
|
||||
SENTRY_DSN_DEV: metamaskrc.SENTRY_DSN_DEV,
|
||||
INFURA_PROJECT_ID: testing
|
||||
|
@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Typography from '../../ui/typography/typography';
|
||||
import {
|
||||
COLORS,
|
||||
TEXT_ALIGN,
|
||||
DISPLAY,
|
||||
TYPOGRAPHY,
|
||||
FONT_WEIGHT,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
import NumericInput from '../../ui/numeric-input/numeric-input.component';
|
||||
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
|
||||
|
||||
export default function AdvancedGasControlsRow({
|
||||
titleText,
|
||||
tooltipText,
|
||||
titleDetailText,
|
||||
error,
|
||||
onChange,
|
||||
value,
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={classNames('advanced-gas-controls__row', {
|
||||
'advanced-gas-controls__row--error': error,
|
||||
})}
|
||||
>
|
||||
<label>
|
||||
<div className="advanced-gas-controls__row-heading">
|
||||
<div className="advanced-gas-controls__row-heading-title">
|
||||
<Typography
|
||||
tag={TYPOGRAPHY.H6}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
variant={TYPOGRAPHY.H6}
|
||||
boxProps={{ display: DISPLAY.INLINE_BLOCK }}
|
||||
>
|
||||
{titleText}
|
||||
</Typography>
|
||||
|
||||
<InfoTooltip position="top" contentText={tooltipText} />
|
||||
</div>
|
||||
{titleDetailText && (
|
||||
<Typography
|
||||
className="advanced-gas-controls__row-heading-detail"
|
||||
align={TEXT_ALIGN.END}
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
>
|
||||
{titleDetailText}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<NumericInput error={error} onChange={onChange} value={value} />
|
||||
{error && (
|
||||
<Typography
|
||||
color={COLORS.ERROR1}
|
||||
variant={TYPOGRAPHY.H7}
|
||||
className="advanced-gas-controls__row-error"
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
AdvancedGasControlsRow.propTypes = {
|
||||
titleText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
tooltipText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
titleDetailText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
error: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
value: PropTypes.number,
|
||||
};
|
||||
|
||||
AdvancedGasControlsRow.defaultProps = {
|
||||
titleText: '',
|
||||
tooltipText: '',
|
||||
titleDetailText: '',
|
||||
error: '',
|
||||
onChange: undefined,
|
||||
value: 0,
|
||||
};
|
@ -0,0 +1,75 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import Typography from '../../ui/typography/typography';
|
||||
import {
|
||||
FONT_WEIGHT,
|
||||
TYPOGRAPHY,
|
||||
COLORS,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import AdvancedGasControlsRow from './advanced-gas-controls-row.component';
|
||||
|
||||
export default function AdvancedGasControls() {
|
||||
const t = useContext(I18nContext);
|
||||
|
||||
const [gasLimit, setGasLimit] = useState(0);
|
||||
const [maxPriorityFee, setMaxPriorityFee] = useState(0);
|
||||
const [maxFee, setMaxFee] = useState(0);
|
||||
|
||||
return (
|
||||
<div className="advanced-gas-controls">
|
||||
<AdvancedGasControlsRow
|
||||
titleText={t('gasLimit')}
|
||||
onChange={setGasLimit}
|
||||
tooltipText=""
|
||||
titleDetailText=""
|
||||
value={gasLimit}
|
||||
/>
|
||||
<AdvancedGasControlsRow
|
||||
titleText={t('maxPriorityFee')}
|
||||
tooltipText=""
|
||||
onChange={setMaxPriorityFee}
|
||||
value={maxPriorityFee}
|
||||
titleDetailText={
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
></Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<AdvancedGasControlsRow
|
||||
titleText={t('maxFee')}
|
||||
tooltipText=""
|
||||
onChange={setMaxFee}
|
||||
value={maxFee}
|
||||
titleDetailText={
|
||||
<>
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('gasFeeEstimate')}:
|
||||
</Typography>{' '}
|
||||
<Typography
|
||||
tag="span"
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H8}
|
||||
></Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import AdvancedGasControls from '.';
|
||||
|
||||
export default {
|
||||
title: 'Advanced Gas Controls',
|
||||
};
|
||||
|
||||
export const simple = () => {
|
||||
return (
|
||||
<div style={{ width: '600px' }}>
|
||||
<AdvancedGasControls />
|
||||
</div>
|
||||
);
|
||||
};
|
1
ui/components/app/advanced-gas-controls/index.js
Normal file
1
ui/components/app/advanced-gas-controls/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './advanced-gas-controls.component';
|
34
ui/components/app/advanced-gas-controls/index.scss
Normal file
34
ui/components/app/advanced-gas-controls/index.scss
Normal file
@ -0,0 +1,34 @@
|
||||
.advanced-gas-controls {
|
||||
&__row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__row-heading {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info-tooltip {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__row-heading-detail {
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
&__row-error,
|
||||
&__row--error h6 {
|
||||
color: $error-1 !important;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
padding-bottom: 6px;
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
|
||||
i {
|
||||
color: #dadada;
|
||||
font-size: $font-size-h7;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
@import 'account-list-item/index';
|
||||
@import 'account-menu/index';
|
||||
@import 'add-token-button/index';
|
||||
@import 'advanced-gas-controls/index';
|
||||
@import 'alerts/alerts';
|
||||
@import 'app-header/index';
|
||||
@import 'asset-list-item/asset-list-item';
|
||||
@ -10,6 +11,7 @@
|
||||
@import 'connected-accounts-permissions/index';
|
||||
@import 'connected-sites-list/index';
|
||||
@import 'connected-status-indicator/index';
|
||||
@import 'edit-gas-display/index';
|
||||
@import 'gas-customization/gas-modal-page-container/index';
|
||||
@import 'gas-customization/gas-price-button-group/index';
|
||||
@import 'gas-customization/index';
|
||||
@ -32,10 +34,13 @@
|
||||
@import 'token-cell/token-cell';
|
||||
@import 'transaction-activity-log/index';
|
||||
@import 'transaction-breakdown/index';
|
||||
@import 'transaction-detail/index';
|
||||
@import 'transaction-detail-item/index';
|
||||
@import 'transaction-icon/transaction-icon';
|
||||
@import 'transaction-list-item-details/index';
|
||||
@import 'transaction-list-item/index';
|
||||
@import 'transaction-list/index';
|
||||
@import 'transaction-status/index';
|
||||
@import 'transaction-total-banner/index';
|
||||
@import 'wallet-overview/index';
|
||||
@import 'whats-new-popup/index';
|
||||
|
45
ui/components/app/edit-gas-display/edit-gas-display.js
Normal file
45
ui/components/app/edit-gas-display/edit-gas-display.js
Normal file
@ -0,0 +1,45 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
|
||||
import TransactionTotalBanner from '../transaction-total-banner/transaction-total-banner.component';
|
||||
import RadioGroup from '../../ui/radio-group/radio-group.component';
|
||||
|
||||
import AdvancedGasControls from '../advanced-gas-controls/advanced-gas-controls.component';
|
||||
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
|
||||
export default function EditGasDisplay() {
|
||||
const t = useContext(I18nContext);
|
||||
const [showAdvancedForm, setShowAdvancedForm] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="edit-gas-display">
|
||||
<TransactionTotalBanner
|
||||
total="9.99"
|
||||
detail="Up to $17.79 (0.01234 ETH)"
|
||||
timing="Likely in < 30 seconds
|
||||
"
|
||||
/>
|
||||
<RadioGroup
|
||||
name="gas-recommendation"
|
||||
options={[
|
||||
{ value: 'low', label: 'Low', recommended: false },
|
||||
{ value: 'medium', label: 'Medium', recommended: false },
|
||||
{ value: 'high', label: 'High', recommended: true },
|
||||
]}
|
||||
selectedValue="high"
|
||||
/>
|
||||
<button
|
||||
className="edit-gas-display__advanced-button"
|
||||
onClick={() => setShowAdvancedForm(!showAdvancedForm)}
|
||||
>
|
||||
{t('advancedOptions')}{' '}
|
||||
{showAdvancedForm ? (
|
||||
<i className="fa fa-caret-up"></i>
|
||||
) : (
|
||||
<i className="fa fa-caret-down"></i>
|
||||
)}
|
||||
</button>
|
||||
{showAdvancedForm && <AdvancedGasControls />}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import PopoverPortal from '../../ui/popover/popover.component';
|
||||
import Button from '../../ui/button';
|
||||
import EditGasDisplay from '.';
|
||||
|
||||
export default {
|
||||
title: 'Edit Gas Display',
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<div style={{ width: '600px' }}>
|
||||
<EditGasDisplay />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const insidePopover = () => {
|
||||
return (
|
||||
<div style={{ width: '600px' }}>
|
||||
<PopoverPortal
|
||||
title="Edit gas fee"
|
||||
onClose={() => console.log('Closing!')}
|
||||
footer={
|
||||
<>
|
||||
<Button type="primary">Save</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div style={{ padding: '20px' }}>
|
||||
<EditGasDisplay />
|
||||
</div>
|
||||
</PopoverPortal>
|
||||
</div>
|
||||
);
|
||||
};
|
1
ui/components/app/edit-gas-display/index.js
Normal file
1
ui/components/app/edit-gas-display/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './edit-gas-display';
|
17
ui/components/app/edit-gas-display/index.scss
Normal file
17
ui/components/app/edit-gas-display/index.scss
Normal file
@ -0,0 +1,17 @@
|
||||
.edit-gas-display {
|
||||
.radio-group {
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
&__advanced-button {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
background: transparent;
|
||||
color: $primary-1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.advanced-gas-controls {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
1
ui/components/app/transaction-detail-item/index.js
Normal file
1
ui/components/app/transaction-detail-item/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './transaction-detail-item.component';
|
29
ui/components/app/transaction-detail-item/index.scss
Normal file
29
ui/components/app/transaction-detail-item/index.scss
Normal file
@ -0,0 +1,29 @@
|
||||
.transaction-detail-item {
|
||||
color: $ui-4;
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__title {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.info-tooltip {
|
||||
display: inline-block;
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
|
||||
&__detail-text {
|
||||
margin-inline-end: 20px !important;
|
||||
}
|
||||
|
||||
&__total {
|
||||
font-weight: bold;
|
||||
color: $ui-black;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Typography from '../../ui/typography/typography';
|
||||
import {
|
||||
COLORS,
|
||||
FONT_WEIGHT,
|
||||
TYPOGRAPHY,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
export default function TransactionDetailItem({
|
||||
detailTitle,
|
||||
detailText,
|
||||
detailTotal,
|
||||
subTitle,
|
||||
subText,
|
||||
}) {
|
||||
return (
|
||||
<div className="transaction-detail-item">
|
||||
<div className="transaction-detail-item__row">
|
||||
<Typography
|
||||
color={COLORS.BLACK}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
variant={TYPOGRAPHY.H6}
|
||||
className="transaction-detail-item__title"
|
||||
>
|
||||
{detailTitle}
|
||||
</Typography>
|
||||
{detailText && (
|
||||
<Typography className="transaction-detail-item__detail-text">
|
||||
{detailText}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography
|
||||
color={COLORS.BLACK}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
className="transaction-detail-item__total"
|
||||
>
|
||||
{detailTotal}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="transaction-detail-item__row">
|
||||
{subTitle && (
|
||||
<Typography
|
||||
variant={TYPOGRAPHY.H7}
|
||||
className="transaction-detail-item__subtitle"
|
||||
>
|
||||
{subTitle}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography variant={TYPOGRAPHY.H7}>{subText}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
TransactionDetailItem.propTypes = {
|
||||
detailTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
detailText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
detailTotal: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
subTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
subText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
};
|
||||
|
||||
TransactionDetailItem.defaultProps = {
|
||||
detailTitle: '',
|
||||
detailText: '',
|
||||
detailTotal: '',
|
||||
subTitle: '',
|
||||
subText: '',
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
|
||||
import TransactionDetailItem from '.';
|
||||
|
||||
export default {
|
||||
title: 'Transaction Detail Item',
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<div style={{ width: '400px' }}>
|
||||
<TransactionDetailItem
|
||||
detailTitle={
|
||||
<>
|
||||
<strong>Estimated gas fee</strong>
|
||||
<InfoTooltip contentText="This is the tooltip text" position="top">
|
||||
<i className="fa fa-info-circle" />
|
||||
</InfoTooltip>
|
||||
</>
|
||||
}
|
||||
detailText="16565.30"
|
||||
detailTotal="0.0089 ETH"
|
||||
subTitle="Very likely in < 15 seconds"
|
||||
subText={
|
||||
<>
|
||||
From <strong>$16565 - $19000</strong>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
1
ui/components/app/transaction-detail/index.js
Normal file
1
ui/components/app/transaction-detail/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './transaction-detail.component';
|
11
ui/components/app/transaction-detail/index.scss
Normal file
11
ui/components/app/transaction-detail/index.scss
Normal file
@ -0,0 +1,11 @@
|
||||
.transaction-detail {
|
||||
.transaction-detail-item {
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid $ui-3;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
||||
|
||||
export default function TransactionDetail({ rows }) {
|
||||
return <div className="transaction-detail">{rows}</div>;
|
||||
}
|
||||
|
||||
TransactionDetail.propTypes = {
|
||||
rows: PropTypes.arrayOf(TransactionDetailItem),
|
||||
};
|
||||
|
||||
TransactionDetail.defaultProps = {
|
||||
rows: [],
|
||||
};
|
@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
|
||||
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
||||
import TransactionDetail from '.';
|
||||
|
||||
export default {
|
||||
title: 'Transaction Detail',
|
||||
};
|
||||
|
||||
const rows = [
|
||||
<TransactionDetailItem
|
||||
key="line-1"
|
||||
detailTitle={
|
||||
<>
|
||||
Estimated gas fee
|
||||
<InfoTooltip contentText="This is the tooltip text" position="top">
|
||||
<i className="fa fa-info-circle" />
|
||||
</InfoTooltip>
|
||||
</>
|
||||
}
|
||||
detailText="0.00896 ETH"
|
||||
detailTotal="$15.73"
|
||||
subTitle="Very likely in < 15 seconds"
|
||||
subText={
|
||||
<>
|
||||
From <strong>$15.73 - $19.81</strong>
|
||||
</>
|
||||
}
|
||||
/>,
|
||||
<TransactionDetailItem
|
||||
key="line-2"
|
||||
detailTitle="Total"
|
||||
detailText=".0312 ETH"
|
||||
detailTotal="$15.77"
|
||||
subTitle="Amount + gas fee"
|
||||
subText={
|
||||
<>
|
||||
Up to <strong>$19.85</strong>
|
||||
</>
|
||||
}
|
||||
/>,
|
||||
];
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<div style={{ width: '400px' }}>
|
||||
<TransactionDetail rows={rows} />
|
||||
</div>
|
||||
);
|
||||
};
|
1
ui/components/app/transaction-total-banner/index.js
Normal file
1
ui/components/app/transaction-total-banner/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './transaction-total-banner.component';
|
7
ui/components/app/transaction-total-banner/index.scss
Normal file
7
ui/components/app/transaction-total-banner/index.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.transaction-total-banner {
|
||||
text-align: center;
|
||||
|
||||
&__detail {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Typography from '../../ui/typography/typography';
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
||||
|
||||
export default function TransactionTotalBanner({ total, detail, timing }) {
|
||||
return (
|
||||
<div className="transaction-total-banner">
|
||||
<Typography color={COLORS.BLACK} variant={TYPOGRAPHY.H1}>
|
||||
{total}
|
||||
</Typography>
|
||||
{detail && (
|
||||
<Typography
|
||||
color={COLORS.BLACK}
|
||||
variant={TYPOGRAPHY.H6}
|
||||
className="transaction-total-banner__detail"
|
||||
>
|
||||
{detail}
|
||||
</Typography>
|
||||
)}
|
||||
{timing && (
|
||||
<Typography
|
||||
color={COLORS.UI4}
|
||||
variant={TYPOGRAPHY.H7}
|
||||
className="transaction-total-banner__timing"
|
||||
>
|
||||
{timing}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
TransactionTotalBanner.propTypes = {
|
||||
total: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
detail: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
timing: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
};
|
||||
|
||||
TransactionTotalBanner.defaultProps = {
|
||||
total: '',
|
||||
detail: '',
|
||||
timing: '',
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import TransactionTotalBanner from '.';
|
||||
|
||||
export default {
|
||||
title: 'Transaction Total Banner',
|
||||
};
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<TransactionTotalBanner
|
||||
total="~18.73"
|
||||
detail={
|
||||
<>
|
||||
Up to <strong>$19.81</strong> (0.01234 ETH)
|
||||
</>
|
||||
}
|
||||
timing="Very likely in < 15 seconds"
|
||||
/>
|
||||
);
|
||||
};
|
1
ui/components/ui/numeric-input/index.js
Normal file
1
ui/components/ui/numeric-input/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './numeric-input.component';
|
39
ui/components/ui/numeric-input/numeric-input.component.js
Normal file
39
ui/components/ui/numeric-input/numeric-input.component.js
Normal file
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import Typography from '../typography/typography';
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
|
||||
|
||||
export default function NumericInput({ detailText, value, onChange, error }) {
|
||||
return (
|
||||
<div
|
||||
className={classNames('numeric-input', { 'numeric-input--error': error })}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value={value}
|
||||
onChange={(e) => onChange?.(Number(e.target.value))}
|
||||
min="0"
|
||||
/>
|
||||
{detailText && (
|
||||
<Typography color={COLORS.UI4} variant={TYPOGRAPHY.H7} tag="span">
|
||||
{detailText}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
NumericInput.propTypes = {
|
||||
value: PropTypes.number,
|
||||
detailText: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
error: PropTypes.string,
|
||||
};
|
||||
|
||||
NumericInput.defaultProps = {
|
||||
value: 0,
|
||||
detailText: '',
|
||||
onChange: undefined,
|
||||
error: '',
|
||||
};
|
28
ui/components/ui/numeric-input/numeric-input.scss
Normal file
28
ui/components/ui/numeric-input/numeric-input.scss
Normal file
@ -0,0 +1,28 @@
|
||||
.numeric-input {
|
||||
border: 1px solid $ui-3;
|
||||
position: relative;
|
||||
border-radius: 6px;
|
||||
|
||||
&--error {
|
||||
border-color: $error-1;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
|
||||
/* ensures the increment/decrement arrows always display */
|
||||
&::-webkit-inner-spin-button,
|
||||
&::-webkit-outer-spin-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
top: 7px;
|
||||
}
|
||||
}
|
36
ui/components/ui/numeric-input/numeric-input.stories.js
Normal file
36
ui/components/ui/numeric-input/numeric-input.stories.js
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import NumericInput from '.';
|
||||
|
||||
export default {
|
||||
title: 'NumericInput',
|
||||
};
|
||||
|
||||
const onChange = (e) => console.log('changed value: ', e.target.value);
|
||||
|
||||
export const numericInput = () => {
|
||||
return (
|
||||
<div style={{ width: '600px' }}>
|
||||
<NumericInput onChange={onChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const numericInputWithDetail = () => {
|
||||
return (
|
||||
<div style={{ width: '600px' }}>
|
||||
<NumericInput detailText="= $0.06" onChange={onChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const numericInputWithError = () => {
|
||||
return (
|
||||
<div style={{ width: '600px' }}>
|
||||
<NumericInput
|
||||
detailText="= $0.06"
|
||||
error="This number isn't great"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
1
ui/components/ui/radio-group/index.js
Normal file
1
ui/components/ui/radio-group/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './radio-group.component';
|
49
ui/components/ui/radio-group/index.scss
Normal file
49
ui/components/ui/radio-group/index.scss
Normal file
@ -0,0 +1,49 @@
|
||||
.radio-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 100px;
|
||||
width: 300px;
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__column-recommended {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&__column-line {
|
||||
width: 1px;
|
||||
height: 5px;
|
||||
background-color: $ui-2;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&__column-horizontal-line {
|
||||
height: 1px;
|
||||
background-color: $ui-2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__column:first-child &__column-horizontal-line {
|
||||
width: 50px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
&__column:last-child &__column-horizontal-line {
|
||||
width: 51px;
|
||||
}
|
||||
|
||||
&__column-radio {
|
||||
margin-inline-end: 1px;
|
||||
}
|
||||
|
||||
&__column-radio,
|
||||
&__column-label {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__column-label {
|
||||
padding-top: 6px;
|
||||
}
|
||||
}
|
64
ui/components/ui/radio-group/radio-group.component.js
Normal file
64
ui/components/ui/radio-group/radio-group.component.js
Normal file
@ -0,0 +1,64 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import Typography from '../typography/typography';
|
||||
import {
|
||||
COLORS,
|
||||
FONT_WEIGHT,
|
||||
TYPOGRAPHY,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
export default function RadioGroup({ options, name, selectedValue, onChange }) {
|
||||
const t = useContext(I18nContext);
|
||||
|
||||
return (
|
||||
<div className="radio-group">
|
||||
{options.map((option) => {
|
||||
return (
|
||||
<div className="radio-group__column" key={`${name}-${option.value}`}>
|
||||
<label>
|
||||
<Typography
|
||||
color={COLORS.SUCCESS3}
|
||||
className="radio-group__column-recommended"
|
||||
variant={TYPOGRAPHY.H7}
|
||||
>
|
||||
{option.recommended ? t('recommendedGasLabel') : ''}
|
||||
</Typography>
|
||||
|
||||
<div className="radio-group__column-radio">
|
||||
<input
|
||||
type="radio"
|
||||
name={name}
|
||||
defaultChecked={option.value === selectedValue}
|
||||
value={option.value}
|
||||
onChange={() => onChange?.(option.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="radio-group__column-line"></div>
|
||||
<div className="radio-group__column-horizontal-line"></div>
|
||||
<Typography
|
||||
color={COLORS.UI4}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
variant={TYPOGRAPHY.H7}
|
||||
className="radio-group__column-label"
|
||||
>
|
||||
{option.label}
|
||||
</Typography>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
RadioGroup.propTypes = {
|
||||
options: PropTypes.array,
|
||||
selectedValue: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
RadioGroup.defaultProps = {
|
||||
options: [],
|
||||
};
|
22
ui/components/ui/radio-group/radio-group.stories.js
Normal file
22
ui/components/ui/radio-group/radio-group.stories.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import RadioGroup from '.';
|
||||
|
||||
export default {
|
||||
title: 'RadioGroup',
|
||||
};
|
||||
|
||||
export const radioGroup = () => {
|
||||
return (
|
||||
<div className="radio-group" style={{ minWidth: '600px' }}>
|
||||
<RadioGroup
|
||||
name="gas-recommendation"
|
||||
options={[
|
||||
{ value: 'low', label: 'Low', recommended: false },
|
||||
{ value: 'medium', label: 'Medium', recommended: false },
|
||||
{ value: 'high', label: 'High', recommended: true },
|
||||
]}
|
||||
selectedValue="high"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -25,9 +25,11 @@ export default function Typography({
|
||||
'typography',
|
||||
className,
|
||||
`typography--${variant}`,
|
||||
`typography--align-${align}`,
|
||||
`typography--color-${color}`,
|
||||
`typography--weight-${fontWeight}`,
|
||||
{
|
||||
[`typography--align-${align}`]: Boolean(align),
|
||||
[`typography--color-${color}`]: Boolean(color),
|
||||
},
|
||||
);
|
||||
|
||||
let Tag = tag ?? variant;
|
||||
|
@ -33,10 +33,12 @@
|
||||
@import 'loading-indicator/loading-indicator';
|
||||
@import 'loading-screen/index';
|
||||
@import 'menu/menu';
|
||||
@import 'numeric-input/numeric-input';
|
||||
@import 'page-container/index';
|
||||
@import 'popover/index';
|
||||
@import 'pulse-loader/index';
|
||||
@import 'qr-code/index';
|
||||
@import 'radio-group/index';
|
||||
@import 'readonly-input/index';
|
||||
@import 'sender-to-recipient/index';
|
||||
@import 'snackbar/index';
|
||||
|
@ -68,5 +68,5 @@ $sizes-strings:
|
||||
$border-style: solid, double, none, dashed, dotted;
|
||||
$directions: top, right, bottom, left;
|
||||
$display: block, grid, flex, inline-block, inline-grid, inline-flex, list-item;
|
||||
$text-align: left, right, center, justify;
|
||||
$text-align: left, right, center, justify, end;
|
||||
$font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900;
|
||||
|
@ -139,6 +139,7 @@ export const TEXT_ALIGN = {
|
||||
CENTER: 'center',
|
||||
RIGHT: 'right',
|
||||
JUSTIFY: 'justify',
|
||||
END: 'end',
|
||||
};
|
||||
|
||||
export const FONT_WEIGHT = {
|
||||
|
Loading…
Reference in New Issue
Block a user