1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

Extracting out title component from confirm-transaction-base (#17991)

This commit is contained in:
Jyoti Puri 2023-03-23 22:21:33 +04:00 committed by GitHub
parent 2fc0d93789
commit 5dee7904d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 463 additions and 120 deletions

View File

@ -313,11 +313,10 @@ describe('MetaMask', function () {
text: 'Transfer',
});
const tokenAmount = await driver.findElement(
'.confirm-page-container-summary__title-text',
);
const tokenAmountText = await tokenAmount.getText();
assert.equal(tokenAmountText, '1 TST');
await driver.findElement({
tag: 'h1',
text: '1 TST',
});
await driver.waitForSelector({
tag: 'p',
@ -419,11 +418,10 @@ describe('MetaMask', function () {
});
it('submits the transaction', async function () {
const tokenAmount = await driver.findElement(
'.confirm-page-container-summary__title-text',
);
const tokenAmountText = await tokenAmount.getText();
assert.equal(tokenAmountText, '1.5 TST');
await driver.findElement({
tag: 'h1',
text: '1.5 TST',
});
await driver.clickElement({ text: 'Confirm', tag: 'button' });
await driver.delay(regularDelayMs);

View File

@ -29,11 +29,9 @@ export default class ConfirmPageContainerContent extends Component {
///: END:ONLY_INCLUDE_IN
errorKey: PropTypes.string,
errorMessage: PropTypes.string,
hideSubtitle: PropTypes.bool,
tokenAddress: PropTypes.string,
nonce: PropTypes.string,
subtitleComponent: PropTypes.node,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
image: PropTypes.string,
titleComponent: PropTypes.node,
warning: PropTypes.string,
@ -48,7 +46,6 @@ export default class ConfirmPageContainerContent extends Component {
disabled: PropTypes.bool,
unapprovedTxCount: PropTypes.number,
rejectNText: PropTypes.string,
hideTitle: PropTypes.bool,
supportsEIP1559: PropTypes.bool,
hasTopBorder: PropTypes.bool,
nativeCurrency: PropTypes.string,
@ -136,11 +133,9 @@ export default class ConfirmPageContainerContent extends Component {
action,
errorKey,
errorMessage,
title,
image,
titleComponent,
subtitleComponent,
hideSubtitle,
tokenAddress,
nonce,
detailsComponent,
@ -156,7 +151,6 @@ export default class ConfirmPageContainerContent extends Component {
rejectNText,
origin,
ethGasPriceWarning,
hideTitle,
supportsEIP1559,
hasTopBorder,
nativeCurrency,
@ -199,15 +193,12 @@ export default class ConfirmPageContainerContent extends Component {
!detailsComponent || !dataComponent,
})}
action={action}
title={title}
image={image}
titleComponent={titleComponent}
subtitleComponent={subtitleComponent}
hideSubtitle={hideSubtitle}
tokenAddress={tokenAddress}
nonce={nonce}
origin={origin}
hideTitle={hideTitle}
toAddress={toAddress}
transactionType={transactionType}
/>

View File

@ -14,8 +14,6 @@ import { getIpfsGateway } from '../../../../../selectors';
import Identicon from '../../../../ui/identicon';
import InfoTooltip from '../../../../ui/info-tooltip';
import NicknamePopovers from '../../../modals/nickname-popovers';
import { Text } from '../../../../component-library';
import { TextVariant } from '../../../../../helpers/constants/design-system';
import { ORIGIN_METAMASK } from '../../../../../../shared/constants/app';
import SiteOrigin from '../../../../ui/site-origin';
import { getAssetImageURL } from '../../../../../helpers/utils/util';
@ -23,16 +21,13 @@ import { getAssetImageURL } from '../../../../../helpers/utils/util';
const ConfirmPageContainerSummary = (props) => {
const {
action,
title,
titleComponent,
subtitleComponent,
hideSubtitle,
className,
tokenAddress,
toAddress,
nonce,
origin,
hideTitle,
image,
transactionType,
} = props;
@ -130,26 +125,9 @@ const ConfirmPageContainerSummary = (props) => {
<>
<div className="confirm-page-container-summary__title">
{renderImage()}
{!hideTitle ? (
<Text
className="confirm-page-container-summary__title-text"
variant={
title && title.length < 10
? TextVariant.displayMd
: TextVariant.headingMd
}
as={title && title.length < 10 ? 'h1' : 'h3'}
title={title}
>
{titleComponent || title}
</Text>
) : null}
{titleComponent}
</div>
{hideSubtitle ? null : (
<div className="confirm-page-container-summary__subtitle">
{subtitleComponent}
</div>
)}
</>
{showNicknamePopovers && (
<NicknamePopovers
@ -163,17 +141,14 @@ const ConfirmPageContainerSummary = (props) => {
ConfirmPageContainerSummary.propTypes = {
action: PropTypes.string,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
image: PropTypes.string,
titleComponent: PropTypes.node,
subtitleComponent: PropTypes.node,
hideSubtitle: PropTypes.bool,
className: PropTypes.string,
tokenAddress: PropTypes.string,
toAddress: PropTypes.string,
nonce: PropTypes.string,
origin: PropTypes.string.isRequired,
hideTitle: PropTypes.bool,
transactionType: PropTypes.string,
};

View File

@ -69,31 +69,6 @@
margin-right: 8px;
}
&__title-text {
@include H1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__title-text-long {
@include H3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__subtitle {
@include H5;
color: var(--color-text-alternative);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&--border {
border-bottom: 1px solid var(--color-border-muted);
}

View File

@ -66,7 +66,6 @@ const ConfirmPageContainer = (props) => {
image,
titleComponent,
subtitleComponent,
hideSubtitle,
detailsComponent,
dataComponent,
dataHexComponent,
@ -123,11 +122,6 @@ const ConfirmPageContainer = (props) => {
const shouldDisplayWarning =
contentComponent && disabled && (errorKey || errorMessage);
const hideTitle =
(currentTransaction.type === TransactionType.contractInteraction ||
currentTransaction.type === TransactionType.deployContract) &&
currentTransaction.txParams?.value === '0x0';
const networkName =
NETWORK_TO_NAME_MAP[currentTransaction.chainId] || networkIdentifier;
@ -203,7 +197,6 @@ const ConfirmPageContainer = (props) => {
image={image}
titleComponent={titleComponent}
subtitleComponent={subtitleComponent}
hideSubtitle={hideSubtitle}
detailsComponent={detailsComponent}
dataComponent={dataComponent}
dataHexComponent={dataHexComponent}
@ -225,7 +218,6 @@ const ConfirmPageContainer = (props) => {
rejectNText={t('rejectTxsN', [unapprovedTxCount])}
origin={origin}
ethGasPriceWarning={ethGasPriceWarning}
hideTitle={hideTitle}
supportsEIP1559={supportsEIP1559}
currentTransaction={currentTransaction}
nativeCurrency={nativeCurrency}
@ -341,7 +333,6 @@ const ConfirmPageContainer = (props) => {
ConfirmPageContainer.propTypes = {
// Header
action: PropTypes.string,
hideSubtitle: PropTypes.bool,
onEdit: PropTypes.func,
showEdit: PropTypes.bool,
subtitleComponent: PropTypes.node,

View File

@ -0,0 +1,10 @@
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { ConfirmSubTitle } from '.';
# Confirm Sub Title
Confirm Sub Title is used on confirmation screen to display transaction amoutn in header.
<Canvas>
<Story id="components-app-ConfirmSubTitle--default-story" />
</Canvas>

View File

@ -0,0 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { SECONDARY } from '../../../helpers/constants/common';
import { Text } from '../../component-library';
import {
Color,
FONT_WEIGHT,
TextVariant,
} from '../../../helpers/constants/design-system';
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
import { getShouldShowFiat } from '../../../selectors';
import { useTransactionInfo } from '../../../hooks/useTransactionInfo';
const ConfirmSubTitle = ({
txData,
hexTransactionAmount,
subtitleComponent,
}) => {
const shouldShowFiat = useSelector(getShouldShowFiat);
const { isNftTransfer } = useTransactionInfo(txData);
if (!shouldShowFiat && !isNftTransfer) {
return null;
}
if (subtitleComponent) {
return subtitleComponent;
}
return (
<Text
as="h5"
ellipsis
fontWeight={FONT_WEIGHT.NORMAL}
variant={TextVariant.bodyMd}
color={Color.textAlternative}
>
<UserPreferencedCurrencyDisplay
value={hexTransactionAmount}
type={SECONDARY}
showEthLogo
hideLabel
/>
</Text>
);
};
ConfirmSubTitle.propTypes = {
hexTransactionAmount: PropTypes.string,
subtitleComponent: PropTypes.element,
txData: PropTypes.object.isRequired,
};
export default ConfirmSubTitle;

View File

@ -0,0 +1,51 @@
import React from 'react';
import { Provider } from 'react-redux';
import mockState from '../../../../test/data/mock-state.json';
import configureStore from '../../../store/store';
import README from './README.mdx';
import ConfirmSubTitle from './confirm-subtitle';
mockState.metamask.preferences.showFiatInTestnets = true;
const store = configureStore(mockState);
export default {
title: 'Components/App/ConfirmSubTitle',
component: ConfirmSubTitle,
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
parameters: {
docs: {
page: README,
},
},
argTypes: {
txData: 'object',
hexTransactionAmount: 'number',
title: 'string',
},
args: {
txData: {
txParams: {},
type: 'transfer',
},
hexTransactionAmount: '0x9184e72a000',
subtitleComponent: undefined,
},
};
export const DefaultStory = (args) => {
return <ConfirmSubTitle {...args} />;
};
DefaultStory.storyName = 'Default';
export const CustomSubTitleStory = (args) => {
return <ConfirmSubTitle {...args} />;
};
CustomSubTitleStory.storyName = 'CustomSubTitle';
CustomSubTitleStory.args = {
subtitleComponent: 'Any custom sub title passed',
};

View File

@ -0,0 +1,80 @@
import React from 'react';
import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import configureStore from '../../../store/store';
import ConfirmSubTitle from './confirm-subtitle';
describe('ConfirmSubTitle', () => {
let store;
beforeEach(() => {
mockState.metamask.preferences.showFiatInTestnets = true;
store = configureStore(mockState);
});
it('should render subtitle correctly', async () => {
const { findByText } = renderWithProvider(
<ConfirmSubTitle
txData={{
txParams: {},
}}
hexTransactionAmount="0x9184e72a000"
/>,
store,
);
expect(await findByText('$0.01')).toBeInTheDocument();
});
it('should return null if showFiatInTestnets preference if false', () => {
mockState.metamask.preferences.showFiatInTestnets = false;
store = configureStore(mockState);
const { container } = renderWithProvider(
<ConfirmSubTitle
txData={{
txParams: {},
}}
hexTransactionAmount="0x9184e72a000"
/>,
store,
);
expect(container.firstChild).toStrictEqual(null);
});
it('should not null if showFiatInTestnets preference if false but it is NFT Transfer', async () => {
mockState.metamask.preferences.showFiatInTestnets = false;
mockState.metamask.allNftContracts = {
[mockState.metamask.selectedAddress]: {
[mockState.metamask.provider.chainId]: [{ address: '0x9' }],
},
};
store = configureStore(mockState);
const { findByText } = renderWithProvider(
<ConfirmSubTitle
txData={{
txParams: {
to: '0x9',
},
}}
hexTransactionAmount="0x9184e72a000"
/>,
store,
);
expect(await findByText('0.00001')).toBeInTheDocument();
});
it('should render subtitleComponent if passed', () => {
const { getByText } = renderWithProvider(
<ConfirmSubTitle
txData={{
txParams: {},
}}
hexTransactionAmount="0x9184e72a000"
subtitleComponent={<div>dummy_sub_title_passed</div>}
/>,
store,
);
expect(getByText('dummy_sub_title_passed')).toBeInTheDocument();
});
});

View File

@ -0,0 +1 @@
export { default as ConfirmSubTitle } from './confirm-subtitle';

View File

@ -0,0 +1,10 @@
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { ConfirmTitle } from '.';
# Confirm Title
Confirm Title is used on the confirmation screen to display the transaction amount in the header.
<Canvas>
<Story id="components-app-ConfirmTitle--default-story" />
</Canvas>

View File

@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import { TransactionType } from '../../../../shared/constants/transaction';
import { PRIMARY } from '../../../helpers/constants/common';
import { Text } from '../../component-library';
import {
FONT_WEIGHT,
TextVariant,
} from '../../../helpers/constants/design-system';
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
const ConfirmTitle = ({ title, hexTransactionAmount, txData }) => {
const isContractInteraction =
txData.type === TransactionType.contractInteraction;
const hideTitle =
(isContractInteraction || txData.type === TransactionType.deployContract) &&
txData.txParams?.value === '0x0';
if (hideTitle) {
return null;
}
if (title) {
return (
<Text
as={title && title.length < 10 ? 'h1' : 'h3'}
ellipsis
title={title}
variant={
title && title.length < 10
? TextVariant.displayMd
: TextVariant.headingMd
}
fontWeight={FONT_WEIGHT.NORMAL}
>
{title}
</Text>
);
}
return (
<Text
as="h3"
ellipsis
fontWeight={FONT_WEIGHT.NORMAL}
variant={TextVariant.headingMd}
>
<UserPreferencedCurrencyDisplay
ethLogoHeight={24}
hideLabel={!isContractInteraction}
showCurrencySuffix={isContractInteraction}
showEthLogo
type={PRIMARY}
value={hexTransactionAmount}
/>
</Text>
);
};
ConfirmTitle.propTypes = {
txData: PropTypes.object.isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
hexTransactionAmount: PropTypes.string,
};
export default ConfirmTitle;

View File

@ -0,0 +1,54 @@
import React from 'react';
import README from './README.mdx';
import ConfirmTitle from './confirm-title';
export default {
title: 'Components/App/ConfirmTitle',
component: ConfirmTitle,
parameters: {
docs: {
page: README,
},
},
argTypes: {
txData: 'object',
hexTransactionAmount: 'string',
title: 'string',
},
args: {
txData: {
txParams: {},
type: 'transfer',
},
hexTransactionAmount: '0x9184e72a000',
title: undefined,
},
};
export const DefaultStory = (args) => {
return <ConfirmTitle {...args} />;
};
DefaultStory.storyName = 'Default';
export const ContractInteractionStory = (args) => {
return <ConfirmTitle {...args} />;
};
ContractInteractionStory.storyName = 'ContractInteraction';
ContractInteractionStory.args = {
txData: {
txParams: {},
type: 'contractInteraction',
},
};
export const CustomTitleStory = (args) => {
return <ConfirmTitle {...args} />;
};
CustomTitleStory.storyName = 'CustomTitle';
CustomTitleStory.args = {
title: 'Any custom title passed',
};

View File

@ -0,0 +1,54 @@
import React from 'react';
import { TransactionType } from '../../../../shared/constants/transaction';
import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import configureStore from '../../../store/store';
import ConfirmTitle from './confirm-title';
describe('ConfirmTitle', () => {
const store = configureStore(mockState);
it('should render title correctly', async () => {
const { findByText } = renderWithProvider(
<ConfirmTitle
txData={{
txParams: {},
}}
hexTransactionAmount="0x9184e72a000"
/>,
store,
);
expect(await findByText('0.00001')).toBeInTheDocument();
});
it('should return null if transaction is contract interation with 0 value', () => {
const { container } = renderWithProvider(
<ConfirmTitle
txData={{
txParams: {
value: '0x0',
},
type: TransactionType.contractInteraction,
}}
/>,
store,
);
expect(container.firstChild).toStrictEqual(null);
});
it('should render title if passed', () => {
const { getByText } = renderWithProvider(
<ConfirmTitle
txData={{
txParams: {},
}}
hexTransactionAmount="0x5"
title="dummy_title_passed"
/>,
store,
);
expect(getByText('dummy_title_passed')).toBeInTheDocument();
});
});

View File

@ -0,0 +1 @@
export { default as ConfirmTitle } from './confirm-title';

View File

@ -0,0 +1,19 @@
import { useSelector } from 'react-redux';
import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
export const useTransactionInfo = (txData = {}) => {
const {
allNftContracts,
selectedAddress,
provider: { chainId },
} = useSelector((state) => state.metamask);
const isNftTransfer = Boolean(
allNftContracts?.[selectedAddress]?.[chainId]?.find((contract) => {
return isEqualCaseInsensitive(contract.address, txData.txParams.to);
}),
);
return { isNftTransfer };
};

View File

@ -0,0 +1,36 @@
import { renderHookWithProvider } from '../../test/lib/render-helpers';
import mockState from '../../test/data/mock-state.json';
import { useTransactionInfo } from './useTransactionInfo';
describe('useTransactionInfo', () => {
describe('isNftTransfer', () => {
it('should return false if transaction is not NFT transfer', () => {
const { result } = renderHookWithProvider(
() =>
useTransactionInfo({
txParams: {},
}),
mockState,
);
expect(result.current.isNftTransfer).toStrictEqual(false);
});
it('should return true if transaction is NFT transfer', () => {
mockState.metamask.allNftContracts = {
[mockState.metamask.selectedAddress]: {
[mockState.metamask.provider.chainId]: [{ address: '0x9' }],
},
};
const { result } = renderHookWithProvider(
() =>
useTransactionInfo({
txParams: {
to: '0x9',
},
}),
mockState,
);
expect(result.current.isNftTransfer).toStrictEqual(true);
});
});
});

View File

@ -254,7 +254,7 @@ exports[`Confirm Transaction Base should match snapshot 1`] = `
class="confirm-page-container-summary__title"
>
<h3
class="box mm-text confirm-page-container-summary__title-text mm-text--heading-md mm-text--color-text-default box--flex-direction-row"
class="box mm-text mm-text--heading-md mm-text--font-weight-normal mm-text--ellipsis mm-text--color-text-default box--flex-direction-row"
>
<div
class="currency-display-component"

View File

@ -58,6 +58,8 @@ import {
import TransactionAlerts from '../../components/app/transaction-alerts';
import { ConfirmHexData } from '../../components/app/confirm-hexdata';
import { ConfirmData } from '../../components/app/confirm-data';
import { ConfirmTitle } from '../../components/app/confirm-title';
import { ConfirmSubTitle } from '../../components/app/confirm-subtitle';
const renderHeartBeatIfNotInTest = () =>
process.env.IN_TEST ? null : <LoadingHeartBeat />;
@ -108,7 +110,6 @@ export default class ConfirmTransactionBase extends Component {
contentComponent: PropTypes.node,
dataComponent: PropTypes.node,
dataHexComponent: PropTypes.node,
hideSubtitle: PropTypes.bool,
tokenAddress: PropTypes.string,
customTokenAmount: PropTypes.string,
dappProposedTokenAmount: PropTypes.string,
@ -837,38 +838,24 @@ export default class ConfirmTransactionBase extends Component {
renderTitleComponent() {
const { title, hexTransactionAmount, txData } = this.props;
// Title string passed in by props takes priority
if (title) {
return null;
}
const isContractInteraction =
txData.type === TransactionType.contractInteraction;
return (
<UserPreferencedCurrencyDisplay
value={hexTransactionAmount}
type={PRIMARY}
showEthLogo
ethLogoHeight={24}
hideLabel={!isContractInteraction}
showCurrencySuffix={isContractInteraction}
<ConfirmTitle
title={title}
hexTransactionAmount={hexTransactionAmount}
txData={txData}
/>
);
}
renderSubtitleComponent() {
const { subtitleComponent, hexTransactionAmount } = this.props;
const { subtitleComponent, hexTransactionAmount, txData } = this.props;
return (
subtitleComponent || (
<UserPreferencedCurrencyDisplay
value={hexTransactionAmount}
type={SECONDARY}
showEthLogo
hideLabel
<ConfirmSubTitle
hexTransactionAmount={hexTransactionAmount}
subtitleComponent={subtitleComponent}
txData={txData}
/>
)
);
}
@ -946,8 +933,6 @@ export default class ConfirmTransactionBase extends Component {
toEns,
toNickname,
methodData,
title,
hideSubtitle,
tokenAddress,
contentComponent,
onEdit,
@ -1024,11 +1009,9 @@ export default class ConfirmTransactionBase extends Component {
toNickname={toNickname}
showEdit={!isContractInteractionFromDapp && Boolean(onEdit)}
action={functionType}
title={title}
image={image}
titleComponent={this.renderTitleComponent()}
subtitleComponent={this.renderSubtitleComponent()}
hideSubtitle={hideSubtitle}
detailsComponent={this.renderDetails()}
dataComponent={this.renderData(functionType)}
dataHexComponent={this.renderDataHex(functionType)}

View File

@ -63,7 +63,6 @@ import {
TransactionStatus,
TransactionType,
} from '../../../shared/constants/transaction';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import { getTokenAddressParam } from '../../helpers/utils/token-util';
import { calcGasTotal } from '../../../shared/lib/transactions-controller-utils';
import ConfirmTransactionBase from './confirm-transaction-base.component';
@ -105,8 +104,6 @@ const mapStateToProps = (state, ownProps) => {
network,
unapprovedTxs,
nextNonce,
allNftContracts,
selectedAddress,
provider: { chainId },
} = metamask;
const { tokenData, txData, tokenProps, nonce } = confirmTransaction;
@ -184,12 +181,6 @@ const mapStateToProps = (state, ownProps) => {
customTxParamsData,
);
const isNftTransfer = Boolean(
allNftContracts?.[selectedAddress]?.[chainId]?.find((contract) => {
return isEqualCaseInsensitive(contract.address, fullTxData.txParams.to);
}),
);
customNonceValue = getCustomNonceValue(state);
const isEthGasPrice = getIsEthGasPriceFetched(state);
const noGasPrice = !supportsEIP1559 && getNoGasPriceFetched(state);
@ -235,7 +226,6 @@ const mapStateToProps = (state, ownProps) => {
useNonceField: getUseNonceField(state),
customNonceValue,
insufficientBalance,
hideSubtitle: !getShouldShowFiat(state) && !isNftTransfer,
hideFiatConversion: !getShouldShowFiat(state),
type,
nextNonce,