Skip to content

Commit

Permalink
Confirmation redesign: add message section to personal sign page (#22766
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jpuri authored Mar 6, 2024
1 parent bf6a7a9 commit 75a91b0
Show file tree
Hide file tree
Showing 27 changed files with 378 additions and 127 deletions.
6 changes: 6 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ exports[`ConfirmInfo should match snapshot 1`] = `
</p>
</div>
<div
class="mm-box mm-box--display-flex mm-box--flex-direction-row mm-box--flex-wrap-wrap mm-box--align-items-center"
style="column-gap: 8px;"
class="mm-box mm-box--display-flex mm-box--gap-2 mm-box--flex-direction-row mm-box--flex-wrap-wrap mm-box--align-items-center"
>
<p
class="mm-box mm-text mm-text--body-md mm-box--color-text-muted"
Expand Down
7 changes: 7 additions & 0 deletions ui/components/app/confirm/info/info.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ const mockRowConfigs: ConfirmInfoRowConfig[] = [
{
type: ConfirmInfoRowType.Divider,
},
{
label: 'Origin',
type: ConfirmInfoRowType.UrlType,
rowProps: {
address: 'https://metamask.github.io',
},
},
{
label: 'Account',
type: ConfirmInfoRowType.ValueDouble,
Expand Down
9 changes: 8 additions & 1 deletion ui/components/app/confirm/info/info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
ConfirmInfoRowAddressProps,
ConfirmInfoRowDivider,
ConfirmInfoRowProps,
ConfirmInfoRowText,
ConfirmInfoRowTextProps,
ConfirmInfoRowUrl,
ConfirmInfoRowUrlProps,
ConfirmInfoRowValueDouble,
Expand All @@ -25,12 +27,14 @@ import {
export enum ConfirmInfoRowType {
Address = 'address',
Divider = 'divider',
ValueDouble = 'value-double',
Text = 'text',
UrlType = 'url',
ValueDouble = 'value-double',
}

type ConfirmInfoTypeProps =
| ConfirmInfoRowAddressProps
| ConfirmInfoRowTextProps
| ConfirmInfoRowUrlProps
| ConfirmInfoRowValueDoubleProps;

Expand All @@ -41,6 +45,9 @@ const TYPE_TO_COMPONENT: Record<ConfirmInfoRowType, any> = {
[ConfirmInfoRowType.Divider]: () => {
return <ConfirmInfoRowDivider />;
},
[ConfirmInfoRowType.Text]: ({ text }: ConfirmInfoRowTextProps) => {
return <ConfirmInfoRowText text={text} />;
},
[ConfirmInfoRowType.UrlType]: ({ url }: ConfirmInfoRowUrlProps) => {
return <ConfirmInfoRowUrl url={url} />;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
exports[`ConfirmInfoRowUrl renders a URL, protocol, and warning icon when the protocol is "http" 1`] = `
<div>
<div
class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap mm-box--align-items-center"
style="column-gap: 8px;"
class="mm-box mm-box--display-flex mm-box--gap-2 mm-box--flex-wrap-wrap mm-box--align-items-center"
>
<p
class="mm-box mm-text mm-text--body-sm mm-box--padding-right-1 mm-box--padding-left-1 mm-box--display-flex mm-box--align-items-center mm-box--color-warning-default mm-box--background-color-warning-muted mm-box--rounded-sm"
Expand All @@ -27,8 +26,7 @@ exports[`ConfirmInfoRowUrl renders a URL, protocol, and warning icon when the pr
exports[`ConfirmInfoRowUrl should match snapshot 1`] = `
<div>
<div
class="mm-box mm-box--display-flex mm-box--flex-wrap-wrap mm-box--align-items-center"
style="column-gap: 8px;"
class="mm-box mm-box--display-flex mm-box--gap-2 mm-box--flex-wrap-wrap mm-box--align-items-center"
>
<p
class="mm-box mm-text mm-text--body-md mm-box--color-inherit"
Expand Down
1 change: 1 addition & 0 deletions ui/components/app/confirm/info/row/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './address';
export * from './divider';
export * from './row';
export * from './text';
export * from './url';
export * from './value-double';
25 changes: 25 additions & 0 deletions ui/components/app/confirm/info/row/text.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { ConfirmInfoRow } from './row';
import { ConfirmInfoRowText } from './text';

const ConfirmInfoRowTextStory = {
title: 'Components/App/Confirm/InfoRowText',
component: ConfirmInfoRowText,

decorators: [
(story) => <ConfirmInfoRow label="Message">{story()}</ConfirmInfoRow>,
],

argTypes: {
url: {
control: 'text',
},
},
};

export const DefaultStory = ({ text }) => <ConfirmInfoRowText text={text} />;
DefaultStory.args = {
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
};

export default ConfirmInfoRowTextStory;
25 changes: 25 additions & 0 deletions ui/components/app/confirm/info/row/text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Box, Text } from '../../../../component-library';
import {
AlignItems,
Display,
FlexWrap,
TextColor,
} from '../../../../../helpers/constants/design-system';

export type ConfirmInfoRowTextProps = {
text: string;
};

export const ConfirmInfoRowText = ({ text }: ConfirmInfoRowTextProps) => {
return (
<Box
display={Display.Flex}
alignItems={AlignItems.center}
flexWrap={FlexWrap.Wrap}
gap={2}
>
<Text color={TextColor.inherit}>{text}</Text>
</Box>
);
};
5 changes: 1 addition & 4 deletions ui/components/app/confirm/info/row/url.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ export const ConfirmInfoRowUrl = ({ url }: ConfirmInfoRowUrlProps) => {
display={Display.Flex}
alignItems={AlignItems.center}
flexWrap={FlexWrap.Wrap}
style={{
// TODO: Box should support this
columnGap: '8px',
}}
gap={2}
>
{isHTTP && (
<Text
Expand Down
5 changes: 1 addition & 4 deletions ui/components/app/confirm/info/row/value-double.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ export const ConfirmInfoRowValueDouble = ({
flexDirection={FlexDirection.Row}
alignItems={AlignItems.center}
flexWrap={FlexWrap.Wrap}
style={{
// TODO: Box should support this
columnGap: '8px',
}}
gap={2}
>
<Text color={LEFT_TEXT_COLORS[variant] as TextColor}>{left}</Text>
<Text color={TextColor.inherit}>{right}</Text>
Expand Down
13 changes: 13 additions & 0 deletions ui/helpers/utils/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -705,3 +705,16 @@ export const fetchTokenExchangeRates = async (
return {};
}
};

export const hexToText = (hex) => {
if (!hex) {
return hex;
}
try {
const stripped = stripHexPrefix(hex);
const buff = Buffer.from(stripped, 'hex');
return buff.length === 32 ? hex : buff.toString('utf8');
} catch (e) {
return hex;
}
};
17 changes: 17 additions & 0 deletions ui/helpers/utils/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1017,4 +1017,21 @@ describe('util', () => {
).toBeFalsy();
});
});

describe('hexToText', () => {
const hexValue =
'0x4578616d706c652060706572736f6e616c5f7369676e60206d657373616765';
it('return correct string value for hex data passed', () => {
expect(util.hexToText(hexValue)).toBe('Example `personal_sign` message');
});
it('return no value if hex is not defined', () => {
expect(util.hexToText()).toBe(undefined);
});
it('return the hex vale unchanged in case an exception occurs', () => {
jest.spyOn(Buffer, 'from').mockImplementation(() => {
throw new Error('some error');
});
expect(util.hexToText(hexValue)).toBe(hexValue);
});
});
});
38 changes: 4 additions & 34 deletions ui/pages/confirmations/components/confirm/info/info.test.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,21 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';

import { unapprovedPersonalMsg } from '../../../../../../test/data/confirmations/personal_sign';
import { renderWithProvider } from '../../../../../../test/lib/render-helpers';
import ConfirmTitle from './info';

const mockPersonalSign = {
id: '0050d5b0-c023-11ee-a0cb-3390a510a0ab',
status: 'unapproved',
time: new Date().getTime(),
type: 'personal_sign',
securityProviderResponse: null,
msgParams: {
from: '0x8eeee1781fd885ff5ddef7789486676961873d12',
data: '0x4578616d706c652060706572736f6e616c5f7369676e60206d657373616765',
origin: 'https://metamask.github.io',
siwe: { isSIWEMessage: false, parsedMessage: null },
},
};

describe('Info', () => {
it('renders origin for personal sign request', () => {
it('renders info section for personal sign request', () => {
const mockState = {
confirm: {
currentConfirmation: mockPersonalSign,
currentConfirmation: unapprovedPersonalMsg,
},
};
const mockStore = configureMockStore([])(mockState);
const { getByText } = renderWithProvider(<ConfirmTitle />, mockStore);

expect(getByText('Origin')).toBeInTheDocument();
expect(getByText('Request from')).toBeInTheDocument();
expect(getByText('https://metamask.github.io')).toBeInTheDocument();
});

it('does not render if required data is not present in the transaction', () => {
const mockState = {
confirm: {
currentConfirmation: {
id: '0050d5b0-c023-11ee-a0cb-3390a510a0ab',
status: 'unapproved',
time: new Date().getTime(),
type: 'json_request',
},
},
};
const mockStore = configureMockStore([])(mockState);
const { queryByText } = renderWithProvider(<ConfirmTitle />, mockStore);

expect(queryByText('Origin')).not.toBeInTheDocument();
});
});
46 changes: 16 additions & 30 deletions ui/pages/confirmations/components/confirm/info/info.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,40 @@
import React, { memo, useMemo } from 'react';
import React, { memo } from 'react';
import { useSelector } from 'react-redux';

import { MESSAGE_TYPE } from '../../../../../../shared/constants/app';
import { TransactionType } from '@metamask/transaction-controller';

import { Box } from '../../../../../components/component-library';
import {
BackgroundColor,
BorderRadius,
} from '../../../../../helpers/constants/design-system';
import { useI18nContext } from '../../../../../hooks/useI18nContext';
import { currentConfirmationSelector } from '../../../../../selectors';
import { Box } from '../../../../../components/component-library';
import {
ConfirmInfo,
ConfirmInfoRowType,
} from '../../../../../components/app/confirm/info/info';
import PersonalSignInfo from './personal-sign/personalSign';

const ConfirmationInfoConponentMap = {
[TransactionType.personalSign]: PersonalSignInfo,
};

type ConfirmationType = keyof typeof ConfirmationInfoConponentMap;

const Info: React.FC = memo(() => {
const t = useI18nContext();
const currentConfirmation = useSelector(currentConfirmationSelector);

const infoRows = useMemo(() => {
if (
!currentConfirmation ||
currentConfirmation.type !== MESSAGE_TYPE.PERSONAL_SIGN ||
!currentConfirmation.msgParams?.origin
) {
return undefined;
}
return [
{
label: t('origin'),
type: ConfirmInfoRowType.UrlType,
rowProps: {
url: currentConfirmation.msgParams?.origin,
},
},
];
}, [currentConfirmation]);

if (!infoRows?.length) {
if (!currentConfirmation?.type) {
return null;
}

const InfoComponent =
ConfirmationInfoConponentMap[currentConfirmation?.type as ConfirmationType];

return (
<Box
backgroundColor={BackgroundColor.backgroundDefault}
borderRadius={BorderRadius.MD}
padding={2}
marginBottom={4}
>
<ConfirmInfo rowConfigs={infoRows} />
<InfoComponent />
</Box>
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';

import { renderWithProvider } from '../../../../../../../test/lib/render-helpers';
import { unapprovedPersonalMsg } from '../../../../../../../test/data/confirmations/personal_sign';
import PersonalSign from './personalSign';

describe('personalSign', () => {
it('renders origin for personal sign request', () => {
const mockState = {
confirm: {
currentConfirmation: unapprovedPersonalMsg,
},
};
const mockStore = configureMockStore([])(mockState);
const { getByText } = renderWithProvider(<PersonalSign />, mockStore);

expect(getByText('Request from')).toBeInTheDocument();
expect(getByText('https://metamask.github.io')).toBeInTheDocument();
});

it('does not render if required data is not present in the transaction', () => {
const mockState = {
confirm: {
currentConfirmation: {
id: '0050d5b0-c023-11ee-a0cb-3390a510a0ab',
status: 'unapproved',
time: new Date().getTime(),
type: 'json_request',
},
},
};
const mockStore = configureMockStore([])(mockState);
const { queryByText } = renderWithProvider(<PersonalSign />, mockStore);

expect(queryByText('Request from')).not.toBeInTheDocument();
});
});
Loading

0 comments on commit 75a91b0

Please sign in to comment.