mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
New SecurityProviderBannerAlert component (#19825)
This commit is contained in:
parent
112511542f
commit
59102e37b8
7
app/_locales/en/messages.json
generated
7
app/_locales/en/messages.json
generated
@ -3579,6 +3579,13 @@
|
|||||||
"securityAndPrivacy": {
|
"securityAndPrivacy": {
|
||||||
"message": "Security & privacy"
|
"message": "Security & privacy"
|
||||||
},
|
},
|
||||||
|
"securityProviderAdviceBy": {
|
||||||
|
"message": "Security advice by $1",
|
||||||
|
"description": "The security provider that is providing data"
|
||||||
|
},
|
||||||
|
"seeDetails": {
|
||||||
|
"message": "See details"
|
||||||
|
},
|
||||||
"seedPhraseConfirm": {
|
"seedPhraseConfirm": {
|
||||||
"message": "Confirm Secret Recovery Phrase"
|
"message": "Confirm Secret Recovery Phrase"
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,24 @@
|
|||||||
|
export enum SecurityProvider {
|
||||||
|
Blockaid = 'blockaid',
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecurityProviderConfig = Record<
|
||||||
|
SecurityProvider,
|
||||||
|
{
|
||||||
|
/** translation key for security provider name */
|
||||||
|
readonly tKeyName: string;
|
||||||
|
/** URL to securty provider website */
|
||||||
|
readonly url: string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const SECURITY_PROVIDER_CONFIG: Readonly<SecurityProviderConfig> = {
|
||||||
|
[SecurityProvider.Blockaid]: {
|
||||||
|
tKeyName: 'blockaid',
|
||||||
|
url: 'https://blockaid.io/',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} SecurityProviderMessageSeverity
|
* @typedef {object} SecurityProviderMessageSeverity
|
||||||
* @property {0} NOT_MALICIOUS - Indicates message is not malicious
|
* @property {0} NOT_MALICIOUS - Indicates message is not malicious
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Security Provider Banner Alert should match snapshot 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="box mm-banner-base mm-banner-alert mm-banner-alert--severity-danger box--margin-top-4 box--margin-right-4 box--margin-left-4 box--padding-3 box--padding-left-2 box--display-flex box--gap-2 box--flex-direction-row box--background-color-error-muted box--rounded-sm"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-error-default"
|
||||||
|
style="mask-image: url('./images/icons/danger.svg');"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h5
|
||||||
|
class="mm-box mm-text mm-banner-base__title mm-text--body-lg-medium mm-box--color-text-default"
|
||||||
|
data-testid="mm-banner-base-title"
|
||||||
|
>
|
||||||
|
Malicious third party detected
|
||||||
|
</h5>
|
||||||
|
<p
|
||||||
|
class="mm-box mm-text mm-text--body-md mm-box--margin-top-2 mm-box--color-text-default"
|
||||||
|
>
|
||||||
|
This is a description to warn the user of malicious or suspicious transactions.
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="disclosure"
|
||||||
|
>
|
||||||
|
<details>
|
||||||
|
<summary
|
||||||
|
class="disclosure__summary is-arrow"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="mm-box mm-text mm-text--body-md mm-box--color-primary-default"
|
||||||
|
>
|
||||||
|
[seeDetails]
|
||||||
|
</p>
|
||||||
|
<span
|
||||||
|
class="box disclosure__summary--icon mm-icon mm-icon--size-sm box--margin-inline-start-2 box--display-inline-block box--flex-direction-row box--color-primary-default"
|
||||||
|
style="mask-image: url('./images/icons/arrow-up.svg');"
|
||||||
|
/>
|
||||||
|
</summary>
|
||||||
|
<div
|
||||||
|
class="disclosure__content normal"
|
||||||
|
>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
List item
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
List item
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
List item
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="disclosure__footer"
|
||||||
|
/>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="mm-box mm-text mm-text--body-sm mm-box--margin-top-2 mm-box--align-items-center mm-box--color-text-alternative"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="box disclosure__summary--icon mm-icon mm-icon--size-sm box--margin-inline-end-1 box--display-inline-block box--flex-direction-row box--color-primary-default"
|
||||||
|
style="mask-image: url('./images/icons/security-tick.svg');"
|
||||||
|
/>
|
||||||
|
[securityProviderAdviceBy]
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './security-provider-banner-alert';
|
@ -0,0 +1,104 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
BannerAlert,
|
||||||
|
ButtonLink,
|
||||||
|
Icon,
|
||||||
|
IconName,
|
||||||
|
IconSize,
|
||||||
|
Text,
|
||||||
|
} from '../../component-library';
|
||||||
|
import Disclosure from '../../ui/disclosure';
|
||||||
|
import { DisclosureVariant } from '../../ui/disclosure/disclosure.constants';
|
||||||
|
|
||||||
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
import {
|
||||||
|
AlignItems,
|
||||||
|
Color,
|
||||||
|
IconColor,
|
||||||
|
Severity,
|
||||||
|
Size,
|
||||||
|
TextVariant,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SecurityProvider,
|
||||||
|
SECURITY_PROVIDER_CONFIG,
|
||||||
|
} from '../../../../shared/constants/security-provider';
|
||||||
|
|
||||||
|
function SecurityProviderBannerAlert({
|
||||||
|
description,
|
||||||
|
details,
|
||||||
|
provider,
|
||||||
|
severity,
|
||||||
|
title,
|
||||||
|
}) {
|
||||||
|
const t = useContext(I18nContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BannerAlert
|
||||||
|
title={title}
|
||||||
|
severity={severity}
|
||||||
|
marginTop={4}
|
||||||
|
marginRight={4}
|
||||||
|
marginLeft={4}
|
||||||
|
>
|
||||||
|
<Text marginTop={2}>{description}</Text>
|
||||||
|
|
||||||
|
{details && (
|
||||||
|
<Disclosure title={t('seeDetails')} variant={DisclosureVariant.Arrow}>
|
||||||
|
{details}
|
||||||
|
</Disclosure>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Text
|
||||||
|
marginTop={2}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
color={Color.textAlternative}
|
||||||
|
variant={TextVariant.bodySm}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className="disclosure__summary--icon"
|
||||||
|
color={IconColor.primaryDefault}
|
||||||
|
name={IconName.SecurityTick}
|
||||||
|
size={IconSize.Sm}
|
||||||
|
marginInlineEnd={1}
|
||||||
|
/>
|
||||||
|
{t('securityProviderAdviceBy', [
|
||||||
|
<ButtonLink
|
||||||
|
key={`security-provider-button-link-${provider}`}
|
||||||
|
size={Size.inherit}
|
||||||
|
href={SECURITY_PROVIDER_CONFIG[provider].url}
|
||||||
|
externalLink
|
||||||
|
>
|
||||||
|
{t(SECURITY_PROVIDER_CONFIG[provider].tKeyName)}
|
||||||
|
</ButtonLink>,
|
||||||
|
])}
|
||||||
|
</Text>
|
||||||
|
</BannerAlert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityProviderBannerAlert.propTypes = {
|
||||||
|
/** Description content that may be plain text or contain hyperlinks */
|
||||||
|
description: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
|
||||||
|
.isRequired,
|
||||||
|
|
||||||
|
/** Name of the security provider */
|
||||||
|
provider: PropTypes.oneOfType(Object.values(SecurityProvider)).isRequired,
|
||||||
|
|
||||||
|
/** Severity level */
|
||||||
|
severity: PropTypes.oneOfType([Severity.Danger, Severity.Warning]).isRequired,
|
||||||
|
|
||||||
|
/** Title to be passed as <BannerAlert> param */
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Additional details to be displayed under the description */
|
||||||
|
details: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SecurityProviderBannerAlert;
|
@ -0,0 +1,87 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Severity } from '../../../helpers/constants/design-system';
|
||||||
|
import { ButtonLink, BUTTON_LINK_SIZES, Text } from '../../component-library';
|
||||||
|
import { SecurityProvider } from '../../../../shared/constants/security-provider';
|
||||||
|
import SecurityProviderBannerAlert from './security-provider-banner-alert';
|
||||||
|
|
||||||
|
const mockPlainText =
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien tellus, elementum sit ' +
|
||||||
|
'amet laoreet vitae, semper in est. Nulla vel tristique felis. Donec non tellus eget neque cursus malesuada.';
|
||||||
|
|
||||||
|
const MockDescriptionWithLinks = () => (
|
||||||
|
<>
|
||||||
|
Description shouldn’t repeat title. 1-3 lines. Can contain a{' '}
|
||||||
|
<ButtonLink size={BUTTON_LINK_SIZES.INHERIT}>hyperlink</ButtonLink>. It can
|
||||||
|
also contain a toggle to enable progressive disclosure.
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const MockDetailsList = () => (
|
||||||
|
<Text as="ul">
|
||||||
|
<li>• List item</li>
|
||||||
|
<li>• List item</li>
|
||||||
|
<li>• List item</li>
|
||||||
|
<li>• List item</li>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/SecurityProviderBannerAlert',
|
||||||
|
argTypes: {
|
||||||
|
description: {
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
options: ['plainText', 'withLinks'],
|
||||||
|
mapping: {
|
||||||
|
plainText: mockPlainText,
|
||||||
|
withLinks: <MockDescriptionWithLinks />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
details: {
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
options: ['none', 'plainText', 'withList'],
|
||||||
|
mapping: {
|
||||||
|
none: null,
|
||||||
|
plainText: mockPlainText,
|
||||||
|
withList: <MockDetailsList />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
options: [Object.values(SecurityProvider)],
|
||||||
|
},
|
||||||
|
severity: {
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
options: [Severity.Danger, Severity.Warning],
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
title: 'Title is sentence case no period',
|
||||||
|
description: <MockDescriptionWithLinks />,
|
||||||
|
details: <MockDetailsList />,
|
||||||
|
provider: SecurityProvider.Blockaid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => (
|
||||||
|
<SecurityProviderBannerAlert severity={Severity.Warning} {...args} />
|
||||||
|
);
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
export const Danger = (args) => (
|
||||||
|
<SecurityProviderBannerAlert severity={Severity.Danger} {...args} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Warning = (args) => (
|
||||||
|
<SecurityProviderBannerAlert severity={Severity.Warning} {...args} />
|
||||||
|
);
|
@ -0,0 +1,61 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Severity } from '../../../helpers/constants/design-system';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import { SecurityProvider } from '../../../../shared/constants/security-provider';
|
||||||
|
import SecurityProviderBannerAlert from '.';
|
||||||
|
|
||||||
|
const mockTitle = 'Malicious third party detected';
|
||||||
|
const mockDescription =
|
||||||
|
'This is a description to warn the user of malicious or suspicious transactions.';
|
||||||
|
const mockDetails = (
|
||||||
|
<ul>
|
||||||
|
<li>List item</li>
|
||||||
|
<li>List item</li>
|
||||||
|
<li>List item</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('Security Provider Banner Alert', () => {
|
||||||
|
it('should match snapshot', () => {
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<SecurityProviderBannerAlert
|
||||||
|
description={mockDescription}
|
||||||
|
details={mockDetails}
|
||||||
|
provider={SecurityProvider.Blockaid}
|
||||||
|
severity={Severity.Danger}
|
||||||
|
title={mockTitle}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render', () => {
|
||||||
|
const { container, getByText } = renderWithProvider(
|
||||||
|
<SecurityProviderBannerAlert
|
||||||
|
description={mockDescription}
|
||||||
|
details={mockDetails}
|
||||||
|
provider={SecurityProvider.Blockaid}
|
||||||
|
severity={Severity.Danger}
|
||||||
|
title={mockTitle}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getByText(mockTitle)).toBeInTheDocument();
|
||||||
|
expect(getByText(mockDescription)).toBeInTheDocument();
|
||||||
|
expect(container.querySelector('.disclosure')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render disclosure component if no details were provided', () => {
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<SecurityProviderBannerAlert
|
||||||
|
description={mockDescription}
|
||||||
|
provider={SecurityProvider.Blockaid}
|
||||||
|
severity={Severity.Danger}
|
||||||
|
title={mockTitle}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container.querySelector('.disclosure')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user