mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-25 11:28:51 +01:00
Adding network status section on edit gas fee modal (#12704)
This commit is contained in:
parent
bf06025615
commit
fbc6f4e603
@ -323,6 +323,9 @@
|
||||
"builtAroundTheWorld": {
|
||||
"message": "MetaMask is designed and built around the world."
|
||||
},
|
||||
"busy": {
|
||||
"message": "Busy"
|
||||
},
|
||||
"buy": {
|
||||
"message": "Buy"
|
||||
},
|
||||
@ -1318,8 +1321,11 @@
|
||||
"layer1Fees": {
|
||||
"message": "Layer 1 fees"
|
||||
},
|
||||
"learmMoreAboutGas": {
|
||||
"message": "Want to $1 about gas?"
|
||||
},
|
||||
"learnMore": {
|
||||
"message": "Learn more"
|
||||
"message": "learn more"
|
||||
},
|
||||
"learnScamRisk": {
|
||||
"message": "scams and security risks."
|
||||
@ -1585,6 +1591,9 @@
|
||||
"networkSettingsDescription": {
|
||||
"message": "Add and edit custom RPC networks"
|
||||
},
|
||||
"networkStatus": {
|
||||
"message": "Network status"
|
||||
},
|
||||
"networkURL": {
|
||||
"message": "Network URL"
|
||||
},
|
||||
@ -1689,6 +1698,9 @@
|
||||
"nonceFieldHeading": {
|
||||
"message": "Custom Nonce"
|
||||
},
|
||||
"notBusy": {
|
||||
"message": "Not busy"
|
||||
},
|
||||
"notCurrentAccount": {
|
||||
"message": "Is this the correct account? It's different from the currently selected account in your wallet"
|
||||
},
|
||||
@ -2381,6 +2393,9 @@
|
||||
"spendLimitTooLarge": {
|
||||
"message": "Spend limit too large"
|
||||
},
|
||||
"stable": {
|
||||
"message": "Stable"
|
||||
},
|
||||
"stateLogError": {
|
||||
"message": "Error in retrieving state logs."
|
||||
},
|
||||
|
@ -15,6 +15,8 @@
|
||||
@import 'edit-gas-display-education/index';
|
||||
@import 'edit-gas-fee-popover/index';
|
||||
@import 'edit-gas-fee-popover/edit-gas-item/index';
|
||||
@import 'edit-gas-fee-popover/network-status/index';
|
||||
@import 'edit-gas-fee-popover/network-status/status-slider/index';
|
||||
@import 'gas-customization/gas-modal-page-container/index';
|
||||
@import 'gas-customization/gas-price-button-group/index';
|
||||
@import 'gas-customization/index';
|
||||
|
@ -3,11 +3,14 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { PRIORITY_LEVELS } from '../../../../shared/constants/gas';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import Popover from '../../ui/popover';
|
||||
import I18nValue from '../../ui/i18n-value';
|
||||
import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
||||
import Popover from '../../ui/popover';
|
||||
import Typography from '../../ui/typography/typography';
|
||||
|
||||
import { COLORS } from '../../../helpers/constants/design-system';
|
||||
import EditGasItem from './edit-gas-item';
|
||||
import NetworkStatus from './network-status';
|
||||
|
||||
const EditGasFeePopover = ({ onClose }) => {
|
||||
const t = useI18nContext();
|
||||
@ -54,6 +57,27 @@ const EditGasFeePopover = ({ onClose }) => {
|
||||
priorityLevel={PRIORITY_LEVELS.CUSTOM}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<NetworkStatus />
|
||||
<Typography
|
||||
className="edit-gas-fee-popover__know-more"
|
||||
align="center"
|
||||
color={COLORS.UI4}
|
||||
fontSize="12px"
|
||||
>
|
||||
<I18nValue
|
||||
messageKey="learmMoreAboutGas"
|
||||
options={[
|
||||
<a
|
||||
key="learnMoreLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://metamask.zendesk.com/hc/en-us/articles/4404600179227-User-Guide-Gas"
|
||||
>
|
||||
<I18nValue messageKey="learnMore" />
|
||||
</a>,
|
||||
]}
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,7 +1,5 @@
|
||||
.edit-gas-fee-popover {
|
||||
@media screen and (min-width: $break-large) {
|
||||
max-height: 84vh;
|
||||
}
|
||||
height: 500px;
|
||||
|
||||
&__wrapper {
|
||||
border-top: 1px solid $ui-grey;
|
||||
@ -37,4 +35,12 @@
|
||||
margin: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__network-status {
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
&__know-more a {
|
||||
color: $primary-1;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
export { default } from './network-status';
|
@ -0,0 +1,41 @@
|
||||
.network-status {
|
||||
margin: 24px 0 12px;
|
||||
|
||||
&__info {
|
||||
border-top: 1px solid $ui-2;
|
||||
border-bottom: 1px solid $ui-2;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__separator {
|
||||
border-left: 1px solid $ui-2;
|
||||
height: 65%;
|
||||
}
|
||||
|
||||
&__field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30%;
|
||||
|
||||
&--priority-fee {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
&-data {
|
||||
color: $ui-4;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-label {
|
||||
color: $Black-100;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
|
||||
import { toBigNumber } from '../../../../../shared/modules/conversion.utils';
|
||||
import { COLORS } from '../../../../helpers/constants/design-system';
|
||||
import { useGasFeeContext } from '../../../../contexts/gasFee';
|
||||
import I18nValue from '../../../ui/i18n-value';
|
||||
import Typography from '../../../ui/typography/typography';
|
||||
|
||||
import StatusSlider from './status-slider';
|
||||
|
||||
const NetworkStatus = () => {
|
||||
const { gasFeeEstimates } = useGasFeeContext();
|
||||
let estBaseFee = null;
|
||||
if (gasFeeEstimates?.estimatedBaseFee) {
|
||||
// estimatedBaseFee is not likely to be below 1, value .01 is used as test networks sometimes
|
||||
// show have small values for it and more decimal places may cause UI to look broken.
|
||||
estBaseFee = toBigNumber.dec(gasFeeEstimates?.estimatedBaseFee);
|
||||
estBaseFee = estBaseFee.lessThan(0.01) ? 0.01 : estBaseFee.toFixed(2);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="network-status">
|
||||
<Typography
|
||||
color={COLORS.UI4}
|
||||
fontSize="10px"
|
||||
fontWeight="bold"
|
||||
margin={[3, 0]}
|
||||
variant="h6"
|
||||
>
|
||||
<I18nValue messageKey="networkStatus" />
|
||||
</Typography>
|
||||
<div className="network-status__info">
|
||||
<div className="network-status__info__field">
|
||||
<span className="network-status__info__field-data">
|
||||
{estBaseFee !== null && `${estBaseFee} GWEI`}
|
||||
</span>
|
||||
<span className="network-status__info__field-label">Base fee</span>
|
||||
</div>
|
||||
<div className="network-status__info__separator" />
|
||||
<div className="network-status__info__field network-status__info__field--priority-fee">
|
||||
<span className="network-status__info__field-data">
|
||||
0.5 - 22 GWEI
|
||||
</span>
|
||||
<span className="network-status__info__field-label">
|
||||
Priority fee
|
||||
</span>
|
||||
</div>
|
||||
<div className="network-status__info__separator" />
|
||||
<div className="network-status__info__field">
|
||||
<StatusSlider />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
NetworkStatus.propTypes = {};
|
||||
|
||||
export default NetworkStatus;
|
@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import { renderWithProvider } from '../../../../../test/jest';
|
||||
import { ETH } from '../../../../helpers/constants/common';
|
||||
import { GasFeeContextProvider } from '../../../../contexts/gasFee';
|
||||
import configureStore from '../../../../store/store';
|
||||
|
||||
import NetworkStatus from './network-status';
|
||||
|
||||
jest.mock('../../../../store/actions', () => ({
|
||||
disconnectGasFeeEstimatePoller: jest.fn(),
|
||||
getGasFeeEstimatesAndStartPolling: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
getGasFeeTimeEstimate: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve('unknown')),
|
||||
}));
|
||||
|
||||
const MOCK_FEE_ESTIMATE = {
|
||||
estimatedBaseFee: '50.0112',
|
||||
};
|
||||
|
||||
const renderComponent = (props) => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
provider: {},
|
||||
cachedBalances: {},
|
||||
accounts: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
balance: '0x176e5b6f173ebe66',
|
||||
},
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
featureFlags: { advancedInlineGas: true },
|
||||
gasFeeEstimates: MOCK_FEE_ESTIMATE,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider>
|
||||
<NetworkStatus />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
};
|
||||
|
||||
describe('NetworkStatus', () => {
|
||||
it('should renders labels', () => {
|
||||
renderComponent();
|
||||
expect(screen.queryByText('Base fee')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Priority fee')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should renders current base fee value rounded to 2 decimal places', () => {
|
||||
renderComponent();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
`${parseFloat(MOCK_FEE_ESTIMATE.estimatedBaseFee).toFixed(2)} GWEI`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should .01 as estimates base fee if estimated base fee is < .01', () => {
|
||||
renderComponent({
|
||||
gasFeeEstimates: {
|
||||
estimatedBaseFee: '0.0012',
|
||||
},
|
||||
});
|
||||
expect(screen.queryByText('0.01 GWEI')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -0,0 +1 @@
|
||||
export { default } from './status-slider';
|
@ -0,0 +1,42 @@
|
||||
.status-slider {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 65%;
|
||||
|
||||
&__line {
|
||||
background-image: linear-gradient(to right, #037dd6, #d73a49);
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
border-radius: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
&__arrow-border {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 10px solid white;
|
||||
position: relative;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid black;
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
left: -5px;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
|
||||
import I18nValue from '../../../../ui/i18n-value';
|
||||
|
||||
const GRADIENT_COLORS = [
|
||||
'#037DD6',
|
||||
'#1876C8',
|
||||
'#2D70BA',
|
||||
'#4369AB',
|
||||
'#57629E',
|
||||
'#6A5D92',
|
||||
'#805683',
|
||||
'#9A4D71',
|
||||
'#B44561',
|
||||
'#C54055',
|
||||
];
|
||||
|
||||
const StatusSlider = () => {
|
||||
// todo: value below to be replaced with dynamic values from api once it is available
|
||||
// corresponding test cases also to be added
|
||||
const statusValue = 0.5;
|
||||
const sliderValueNumeric = Math.round(statusValue * 10);
|
||||
|
||||
let statusLabel = 'stable';
|
||||
if (statusValue <= 0.33) {
|
||||
statusLabel = 'notBusy';
|
||||
} else if (statusValue > 0.66) {
|
||||
statusLabel = 'busy';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="status-slider">
|
||||
<div className="status-slider__arrow-border">
|
||||
<div
|
||||
className="status-slider__arrow"
|
||||
style={{
|
||||
borderTopColor: GRADIENT_COLORS[sliderValueNumeric],
|
||||
marginLeft: `${sliderValueNumeric * 10}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="status-slider__line" />
|
||||
<div
|
||||
className="status-slider__label"
|
||||
style={{ color: GRADIENT_COLORS[sliderValueNumeric] }}
|
||||
>
|
||||
<I18nValue messageKey={statusLabel} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusSlider;
|
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import { renderWithProvider } from '../../../../../../test/jest';
|
||||
import { ETH } from '../../../../../helpers/constants/common';
|
||||
import { GasFeeContextProvider } from '../../../../../contexts/gasFee';
|
||||
import configureStore from '../../../../../store/store';
|
||||
|
||||
import StatusSlider from './status-slider';
|
||||
|
||||
jest.mock('../../../../../store/actions', () => ({
|
||||
disconnectGasFeeEstimatePoller: jest.fn(),
|
||||
getGasFeeEstimatesAndStartPolling: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
getGasFeeTimeEstimate: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve('unknown')),
|
||||
}));
|
||||
|
||||
const renderComponent = () => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
nativeCurrency: ETH,
|
||||
provider: {},
|
||||
cachedBalances: {},
|
||||
accounts: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
balance: '0x176e5b6f173ebe66',
|
||||
},
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
},
|
||||
});
|
||||
|
||||
return renderWithProvider(
|
||||
<GasFeeContextProvider>
|
||||
<StatusSlider />
|
||||
</GasFeeContextProvider>,
|
||||
store,
|
||||
);
|
||||
};
|
||||
|
||||
describe('NetworkStatus', () => {
|
||||
it('should renders stable for statusValue > 0.33 and <= 0.66', () => {
|
||||
renderComponent();
|
||||
expect(screen.queryByText('Stable')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -82,4 +82,4 @@ $display: block, grid, flex, inline-block, inline-grid, inline-flex, list-item;
|
||||
$text-align: left, right, center, justify, end;
|
||||
$font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900;
|
||||
$font-style: normal, italic, oblique;
|
||||
$font-size: 12px;
|
||||
$font-size: 10px, 12px;
|
||||
|
Loading…
Reference in New Issue
Block a user