mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
BETA - Add beta banner to all screens (#16307)
* Add beta home banner to home screen * Move the beta home notification to the app-header * Updates to formatting * Add beta home banner to home screen * Move the beta home notification to the app-header * Updates to formatting * Update ui/components/app/app-header/index.scss Co-authored-by: George Marshall <george.marshall@consensys.net> * Update ui/components/app/app-header/index.scss Co-authored-by: George Marshall <george.marshall@consensys.net> * Update ui/components/app/app-header/index.scss Co-authored-by: George Marshall <george.marshall@consensys.net> * Move banner to top of page * Move to own folder, update styles * Swith to BOX component * Address feedback * Add tests * Update name of component * Fix tests * Fix proptype errors * Fixups * Remove unrelated changes * Remove unwanted string changes * Update beta component name and text * Update mock data Co-authored-by: George Marshall <george.marshall@consensys.net>
This commit is contained in:
parent
83f952228b
commit
880af262a4
4
app/_locales/en/messages.json
generated
4
app/_locales/en/messages.json
generated
@ -437,6 +437,10 @@
|
|||||||
"beta": {
|
"beta": {
|
||||||
"message": "Beta"
|
"message": "Beta"
|
||||||
},
|
},
|
||||||
|
"betaHeaderText": {
|
||||||
|
"message": "This is a BETA version. Please report bugs $1",
|
||||||
|
"description": "$1 represents the word 'here' in a hyperlink"
|
||||||
|
},
|
||||||
"betaMetamaskDescription": {
|
"betaMetamaskDescription": {
|
||||||
"message": "Trusted by millions, MetaMask is a secure wallet making the world of web3 accessible to all."
|
"message": "Trusted by millions, MetaMask is a secure wallet making the world of web3 accessible to all."
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@ import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
|
|||||||
import { MINUTE } from '../../../shared/constants/time';
|
import { MINUTE } from '../../../shared/constants/time';
|
||||||
import { AUTO_LOCK_TIMEOUT_ALARM } from '../../../shared/constants/alarms';
|
import { AUTO_LOCK_TIMEOUT_ALARM } from '../../../shared/constants/alarms';
|
||||||
import { isManifestV3 } from '../../../shared/modules/mv3.utils';
|
import { isManifestV3 } from '../../../shared/modules/mv3.utils';
|
||||||
|
import { isBeta } from '../../../ui/helpers/utils/build-types';
|
||||||
|
|
||||||
export default class AppStateController extends EventEmitter {
|
export default class AppStateController extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
@ -36,6 +37,7 @@ export default class AppStateController extends EventEmitter {
|
|||||||
enableEIP1559V2NoticeDismissed: false,
|
enableEIP1559V2NoticeDismissed: false,
|
||||||
showTestnetMessageInDropdown: true,
|
showTestnetMessageInDropdown: true,
|
||||||
showPortfolioTooltip: true,
|
showPortfolioTooltip: true,
|
||||||
|
showBetaHeader: isBeta(),
|
||||||
trezorModel: null,
|
trezorModel: null,
|
||||||
...initState,
|
...initState,
|
||||||
qrHardware: {},
|
qrHardware: {},
|
||||||
@ -291,6 +293,15 @@ export default class AppStateController extends EventEmitter {
|
|||||||
this.store.updateState({ showPortfolioTooltip });
|
this.store.updateState({ showPortfolioTooltip });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the beta notification heading on the home page
|
||||||
|
*
|
||||||
|
* @param showBetaHeader
|
||||||
|
*/
|
||||||
|
setShowBetaHeader(showBetaHeader) {
|
||||||
|
this.store.updateState({ showBetaHeader });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a property indicating the model of the user's Trezor hardware wallet
|
* Sets a property indicating the model of the user's Trezor hardware wallet
|
||||||
*
|
*
|
||||||
|
@ -1697,6 +1697,8 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
),
|
),
|
||||||
setShowPortfolioTooltip:
|
setShowPortfolioTooltip:
|
||||||
appStateController.setShowPortfolioTooltip.bind(appStateController),
|
appStateController.setShowPortfolioTooltip.bind(appStateController),
|
||||||
|
setShowBetaHeader:
|
||||||
|
appStateController.setShowBetaHeader.bind(appStateController),
|
||||||
setCollectiblesDetectionNoticeDismissed:
|
setCollectiblesDetectionNoticeDismissed:
|
||||||
appStateController.setCollectiblesDetectionNoticeDismissed.bind(
|
appStateController.setCollectiblesDetectionNoticeDismissed.bind(
|
||||||
appStateController,
|
appStateController,
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
},
|
},
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"gasEstimateType": "fee-market",
|
"gasEstimateType": "fee-market",
|
||||||
|
"showBetaHeader": false,
|
||||||
"gasFeeEstimates": {
|
"gasFeeEstimates": {
|
||||||
"low": {
|
"low": {
|
||||||
"minWaitTimeEstimate": 180000,
|
"minWaitTimeEstimate": 180000,
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
@import 'alerts/alerts';
|
@import 'alerts/alerts';
|
||||||
@import 'app-header/index';
|
@import 'app-header/index';
|
||||||
@import 'asset-list-item/asset-list-item';
|
@import 'asset-list-item/asset-list-item';
|
||||||
|
@import 'beta-header/index';
|
||||||
@import 'cancel-speedup-popover/index';
|
@import 'cancel-speedup-popover/index';
|
||||||
@import 'confirm-page-container/index';
|
@import 'confirm-page-container/index';
|
||||||
@import 'confirm-page-container/enableEIP1559V2-notice';
|
@import 'confirm-page-container/enableEIP1559V2-notice';
|
||||||
|
@ -7,6 +7,10 @@ import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
|||||||
import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics';
|
import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics';
|
||||||
import NetworkDisplay from '../network-display';
|
import NetworkDisplay from '../network-display';
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
|
import BetaHeader from '../beta-header';
|
||||||
|
///: END:ONLY_INCLUDE_IN(beta)
|
||||||
|
|
||||||
export default class AppHeader extends PureComponent {
|
export default class AppHeader extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
@ -23,6 +27,9 @@ export default class AppHeader extends PureComponent {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
unreadNotificationsCount: PropTypes.number,
|
unreadNotificationsCount: PropTypes.number,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
|
showBetaHeader: PropTypes.bool,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,33 +119,44 @@ export default class AppHeader extends PureComponent {
|
|||||||
disableNetworkIndicator,
|
disableNetworkIndicator,
|
||||||
disabled,
|
disabled,
|
||||||
onClick,
|
onClick,
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
|
showBetaHeader,
|
||||||
|
///: END:ONLY_INCLUDE_IN(beta)
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app-header">
|
<>
|
||||||
<div className="app-header__contents">
|
{
|
||||||
<MetaFoxLogo
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
unsetIconHeight
|
showBetaHeader ? <BetaHeader /> : null
|
||||||
onClick={async () => {
|
///: END:ONLY_INCLUDE_IN(beta)
|
||||||
if (onClick) {
|
}
|
||||||
await onClick();
|
|
||||||
}
|
<div className="app-header">
|
||||||
history.push(DEFAULT_ROUTE);
|
<div className="app-header__contents">
|
||||||
}}
|
<MetaFoxLogo
|
||||||
/>
|
unsetIconHeight
|
||||||
<div className="app-header__account-menu-container">
|
onClick={async () => {
|
||||||
{!hideNetworkIndicator && (
|
if (onClick) {
|
||||||
<div className="app-header__network-component-wrapper">
|
await onClick();
|
||||||
<NetworkDisplay
|
}
|
||||||
onClick={(event) => this.handleNetworkIndicatorClick(event)}
|
history.push(DEFAULT_ROUTE);
|
||||||
disabled={disabled || disableNetworkIndicator}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
<div className="app-header__account-menu-container">
|
||||||
)}
|
{!hideNetworkIndicator && (
|
||||||
{this.renderAccountMenu()}
|
<div className="app-header__network-component-wrapper">
|
||||||
|
<NetworkDisplay
|
||||||
|
onClick={(event) => this.handleNetworkIndicatorClick(event)}
|
||||||
|
disabled={disabled || disableNetworkIndicator}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{this.renderAccountMenu()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { compose } from 'redux';
|
import { compose } from 'redux';
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
import {
|
||||||
import { getUnreadNotificationsCount } from '../../../selectors';
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
///: END:ONLY_INCLUDE_IN
|
getUnreadNotificationsCount,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
|
getShowBetaHeader,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
} from '../../../selectors';
|
||||||
|
|
||||||
import * as actions from '../../../store/actions';
|
import * as actions from '../../../store/actions';
|
||||||
import AppHeader from './app-header.component';
|
import AppHeader from './app-header.component';
|
||||||
@ -17,6 +22,10 @@ const mapStateToProps = (state) => {
|
|||||||
const unreadNotificationsCount = getUnreadNotificationsCount(state);
|
const unreadNotificationsCount = getUnreadNotificationsCount(state);
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
|
const showBetaHeader = getShowBetaHeader(state);
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
return {
|
return {
|
||||||
networkDropdownOpen,
|
networkDropdownOpen,
|
||||||
selectedAddress,
|
selectedAddress,
|
||||||
@ -25,6 +34,9 @@ const mapStateToProps = (state) => {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||||
unreadNotificationsCount,
|
unreadNotificationsCount,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||||
|
showBetaHeader,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Beta Header should match snapshot 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="box beta-header box--padding-2 box--display-flex box--flex-direction-row box--width-full box--background-color-warning-default"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--flex-direction-row typography beta-header__message typography--h7 typography--weight-normal typography--style-normal typography--color-warning-inverse"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
|
||||||
|
This is a BETA version. Please report bugs
|
||||||
|
<a
|
||||||
|
href="https://metamask.zendesk.com/hc/en-us"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</h6>
|
||||||
|
<button
|
||||||
|
aria-label="Close"
|
||||||
|
class="beta-header__button"
|
||||||
|
data-testid="beta-header-close"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-times"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
24
ui/components/app/beta-header/beta-header.stories.js
Normal file
24
ui/components/app/beta-header/beta-header.stories.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import testData from '../../../../.storybook/test-data';
|
||||||
|
import configureStore from '../../../store/store';
|
||||||
|
import BetaHeader from '.';
|
||||||
|
|
||||||
|
const store = configureStore({
|
||||||
|
...testData,
|
||||||
|
metamask: { ...testData.metamask, isUnlocked: true, showBetaHeader: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/BetaHeader',
|
||||||
|
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||||
|
id: __filename,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = () => (
|
||||||
|
<>
|
||||||
|
<BetaHeader />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
45
ui/components/app/beta-header/beta-header.test.js
Normal file
45
ui/components/app/beta-header/beta-header.test.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent } from '@testing-library/react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import BetaHeader from '.';
|
||||||
|
|
||||||
|
const mockHideBetaHeader = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('../../../store/actions', () => {
|
||||||
|
return {
|
||||||
|
hideBetaHeader: () => {
|
||||||
|
mockHideBetaHeader();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Beta Header', () => {
|
||||||
|
let store;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
store = configureMockStore([thunk])(mockState);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mockHideBetaHeader.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match snapshot', () => {
|
||||||
|
const { container } = renderWithProvider(<BetaHeader />, store);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Beta Header', () => {
|
||||||
|
it('gets hidden when close button is clicked', () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(<BetaHeader />, store);
|
||||||
|
|
||||||
|
const closeButton = queryByTestId('beta-header-close');
|
||||||
|
fireEvent.click(closeButton);
|
||||||
|
|
||||||
|
expect(mockHideBetaHeader).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
59
ui/components/app/beta-header/index.js
Normal file
59
ui/components/app/beta-header/index.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
|
|
||||||
|
import Box from '../../ui/box/box';
|
||||||
|
import Typography from '../../ui/typography/typography';
|
||||||
|
import {
|
||||||
|
TYPOGRAPHY,
|
||||||
|
COLORS,
|
||||||
|
BLOCK_SIZES,
|
||||||
|
DISPLAY,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
import { BETA_BUGS_URL } from '../../../helpers/constants/beta';
|
||||||
|
|
||||||
|
import { hideBetaHeader } from '../../../store/actions';
|
||||||
|
|
||||||
|
const BetaHeader = () => {
|
||||||
|
const t = useI18nContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
backgroundColor={COLORS.WARNING_DEFAULT}
|
||||||
|
padding={2}
|
||||||
|
className="beta-header"
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant={TYPOGRAPHY.H7}
|
||||||
|
marginTop={0}
|
||||||
|
marginBottom={0}
|
||||||
|
className="beta-header__message"
|
||||||
|
color={COLORS.WARNING_INVERSE}
|
||||||
|
>
|
||||||
|
{t('betaHeaderText', [
|
||||||
|
<a
|
||||||
|
href={BETA_BUGS_URL}
|
||||||
|
key="link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
>
|
||||||
|
{t('here')}
|
||||||
|
</a>,
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
<button
|
||||||
|
className="beta-header__button"
|
||||||
|
data-testid="beta-header-close"
|
||||||
|
onClick={() => {
|
||||||
|
hideBetaHeader();
|
||||||
|
}}
|
||||||
|
aria-label={t('close')}
|
||||||
|
>
|
||||||
|
<i className="fa fa-times" />
|
||||||
|
</button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BetaHeader;
|
16
ui/components/app/beta-header/index.scss
Normal file
16
ui/components/app/beta-header/index.scss
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.beta-header {
|
||||||
|
&__message {
|
||||||
|
text-align: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
background: transparent;
|
||||||
|
padding: 0 6px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--color-warning-inverse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
ui/helpers/constants/beta.js
Normal file
1
ui/helpers/constants/beta.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const BETA_BUGS_URL = 'https://metamask.zendesk.com/hc/en-us';
|
@ -978,6 +978,10 @@ export function getShowPortfolioTooltip(state) {
|
|||||||
return state.metamask.showPortfolioTooltip;
|
return state.metamask.showPortfolioTooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getShowBetaHeader(state) {
|
||||||
|
return state.metamask.showBetaHeader;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To get the useTokenDetection flag which determines whether a static or dynamic token list is used
|
* To get the useTokenDetection flag which determines whether a static or dynamic token list is used
|
||||||
*
|
*
|
||||||
|
@ -3779,6 +3779,10 @@ export function hidePortfolioTooltip() {
|
|||||||
return submitRequestToBackground('setShowPortfolioTooltip', [false]);
|
return submitRequestToBackground('setShowPortfolioTooltip', [false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hideBetaHeader() {
|
||||||
|
return submitRequestToBackground('setShowBetaHeader', [false]);
|
||||||
|
}
|
||||||
|
|
||||||
export function setCollectiblesDetectionNoticeDismissed() {
|
export function setCollectiblesDetectionNoticeDismissed() {
|
||||||
return submitRequestToBackground('setCollectiblesDetectionNoticeDismissed', [
|
return submitRequestToBackground('setCollectiblesDetectionNoticeDismissed', [
|
||||||
true,
|
true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user