mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
[MMI] Add-compliance-details component (#18575)
Co-authored-by: Antonio Regadas <antonio.regadas@consensys.net> Co-authored-by: Ariella Vu <20778143+digiwand@users.noreply.github.com>
This commit is contained in:
parent
d2779c9ef1
commit
4f64e3d6c3
21
app/_locales/en/messages.json
generated
21
app/_locales/en/messages.json
generated
@ -2402,6 +2402,9 @@
|
|||||||
"noNFTs": {
|
"noNFTs": {
|
||||||
"message": "No NFTs yet"
|
"message": "No NFTs yet"
|
||||||
},
|
},
|
||||||
|
"noReport": {
|
||||||
|
"message": "No Report"
|
||||||
|
},
|
||||||
"noSnaps": {
|
"noSnaps": {
|
||||||
"message": "You don't have any snaps installed."
|
"message": "You don't have any snaps installed."
|
||||||
},
|
},
|
||||||
@ -3245,6 +3248,12 @@
|
|||||||
"replace": {
|
"replace": {
|
||||||
"message": "replace"
|
"message": "replace"
|
||||||
},
|
},
|
||||||
|
"reportLastRun": {
|
||||||
|
"message": "Report last run"
|
||||||
|
},
|
||||||
|
"reportLastRunTooltip": {
|
||||||
|
"message": "The date and time of when the last AML/CFT report was run"
|
||||||
|
},
|
||||||
"requestFailed": {
|
"requestFailed": {
|
||||||
"message": "Request failed"
|
"message": "Request failed"
|
||||||
},
|
},
|
||||||
@ -3374,9 +3383,18 @@
|
|||||||
"revokeSpendingCapTooltipText": {
|
"revokeSpendingCapTooltipText": {
|
||||||
"message": "This third party will be unable to spend any more of your current or future tokens."
|
"message": "This third party will be unable to spend any more of your current or future tokens."
|
||||||
},
|
},
|
||||||
|
"riskRating": {
|
||||||
|
"message": "Risk rating"
|
||||||
|
},
|
||||||
|
"riskRatingTooltip": {
|
||||||
|
"message": "The risk rating of the address you are interacting with based on your risk settings"
|
||||||
|
},
|
||||||
"rpcUrl": {
|
"rpcUrl": {
|
||||||
"message": "New RPC URL"
|
"message": "New RPC URL"
|
||||||
},
|
},
|
||||||
|
"runReport": {
|
||||||
|
"message": "Run report"
|
||||||
|
},
|
||||||
"safeTransferFrom": {
|
"safeTransferFrom": {
|
||||||
"message": "Safe transfer from"
|
"message": "Safe transfer from"
|
||||||
},
|
},
|
||||||
@ -3591,6 +3609,9 @@
|
|||||||
"showPrivateKeys": {
|
"showPrivateKeys": {
|
||||||
"message": "Show Private Keys"
|
"message": "Show Private Keys"
|
||||||
},
|
},
|
||||||
|
"showReport": {
|
||||||
|
"message": "Show report"
|
||||||
|
},
|
||||||
"showTestnetNetworks": {
|
"showTestnetNetworks": {
|
||||||
"message": "Show test networks"
|
"message": "Show test networks"
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`ComplianceDetails should render correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="box compliance-details box--padding-right-4 box--padding-left-4 box--display-flex box--flex-direction-column"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box compliance-details__row box--padding-top-4 box--padding-bottom-4 box--display-flex box--flex-direction-column box--justify-content-center box--height-2/3"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="box mm-text mm-text--body-md box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
Address
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="box mm-text mm-text--body-xs box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
0xAddress
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box compliance-details__row box--padding-top-4 box--padding-bottom-4 box--display-flex box--flex-direction-column box--justify-content-center box--height-2/3"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--margin-bottom-1 box--display-flex box--flex-direction-row box--align-items-center box--color-text-alternative"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="box mm-text mm-text--body-md box--margin-right-2 box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
Risk rating
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="info-tooltip"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-1"
|
||||||
|
class="info-tooltip__tooltip-container"
|
||||||
|
data-original-title="null"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
|
||||||
|
fill="var(--color-icon-alternative)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box compliance-row__column-risk compliance-row__column-risk--green box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="box mm-text mm-text--body-md box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
low
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box compliance-details__row box--padding-top-4 box--padding-bottom-4 box--display-flex box--flex-direction-column box--justify-content-center box--height-2/3"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row box--align-items-center box--color-text-alternative"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="box mm-text mm-text--body-md box--margin-right-2 box--flex-direction-row box--color-text-default"
|
||||||
|
>
|
||||||
|
Report last run
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="info-tooltip"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-2"
|
||||||
|
class="info-tooltip__tooltip-container"
|
||||||
|
data-original-title="null"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
|
||||||
|
fill="var(--color-icon-alternative)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="box mm-text mm-text--body-md box--flex-direction-row box--color-text-default"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="swaps-footer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="swaps-footer__buttons swaps-footer__buttons--border"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="page-container__footer swaps-footer__custom-page-container-footer-class"
|
||||||
|
>
|
||||||
|
<footer>
|
||||||
|
<button
|
||||||
|
class="button btn--rounded btn-secondary page-container__footer-button page-container__footer-button__cancel swaps-footer__custom-page-container-footer-button-class"
|
||||||
|
data-testid="page-container-footer-cancel"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Show report
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button btn--rounded btn-primary page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
|
||||||
|
data-testid="page-container-footer-next"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Run report
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,159 @@
|
|||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
import InfoTooltip from '../../ui/info-tooltip';
|
||||||
|
import SwapsFooter from '../../../pages/swaps/swaps-footer';
|
||||||
|
import {
|
||||||
|
fetchHistoricalReports,
|
||||||
|
getComplianceHistoricalReportsByAddress,
|
||||||
|
getComplianceTenantSubdomain,
|
||||||
|
} from '../../../ducks/institutional/institutional';
|
||||||
|
import { formatDate } from '../../../helpers/utils/util';
|
||||||
|
import Box from '../../ui/box';
|
||||||
|
import { Text } from '../../component-library';
|
||||||
|
import {
|
||||||
|
TextColor,
|
||||||
|
TextVariant,
|
||||||
|
JustifyContent,
|
||||||
|
AlignItems,
|
||||||
|
BLOCK_SIZES,
|
||||||
|
DISPLAY,
|
||||||
|
FLEX_DIRECTION,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
const ComplianceDetails = ({ address, onClose, onGenerate }) => {
|
||||||
|
const t = useContext(I18nContext);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchHistoricalReports(address));
|
||||||
|
}, [address, dispatch]);
|
||||||
|
|
||||||
|
const [lastReport, setLastReport] = useState(null);
|
||||||
|
const historicalReports = useSelector(
|
||||||
|
getComplianceHistoricalReportsByAddress(address),
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (historicalReports && historicalReports.length) {
|
||||||
|
setLastReport(
|
||||||
|
historicalReports.reduce((prev, cur) =>
|
||||||
|
prev.createTime > cur.createTime ? prev : cur,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [historicalReports]);
|
||||||
|
|
||||||
|
const complianceTenantSubdomain = useSelector(getComplianceTenantSubdomain);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
paddingLeft={4}
|
||||||
|
paddingRight={4}
|
||||||
|
className="compliance-details"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
height={BLOCK_SIZES.TWO_THIRDS}
|
||||||
|
paddingTop={4}
|
||||||
|
paddingBottom={4}
|
||||||
|
className="compliance-details__row"
|
||||||
|
>
|
||||||
|
<Text>{t('address')}</Text>
|
||||||
|
<Text variant={TextVariant.bodyXs}>{address}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
height={BLOCK_SIZES.TWO_THIRDS}
|
||||||
|
paddingTop={4}
|
||||||
|
paddingBottom={4}
|
||||||
|
className="compliance-details__row"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
marginBottom={1}
|
||||||
|
color={TextColor.textAlternative}
|
||||||
|
>
|
||||||
|
<Text marginRight={2}>{t('riskRating')}</Text>
|
||||||
|
<InfoTooltip
|
||||||
|
position="bottom"
|
||||||
|
contentText={<span>{t('riskRatingTooltip')}</span>}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
className={classnames('compliance-row__column-risk', {
|
||||||
|
'compliance-row__column-risk--green': lastReport?.risk === 'low',
|
||||||
|
'compliance-row__column-risk--yellow':
|
||||||
|
lastReport?.risk === 'medium',
|
||||||
|
'compliance-row__column-risk--orange': lastReport?.risk === 'high',
|
||||||
|
'compliance-row__column-risk--red':
|
||||||
|
lastReport?.risk === 'unacceptable',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Text>{lastReport ? lastReport.risk : t('noReport')}</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
height={BLOCK_SIZES.TWO_THIRDS}
|
||||||
|
paddingTop={4}
|
||||||
|
paddingBottom={4}
|
||||||
|
className="compliance-details__row"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
color={TextColor.textAlternative}
|
||||||
|
>
|
||||||
|
<Text marginRight={2}>{t('reportLastRun')}</Text>
|
||||||
|
<InfoTooltip
|
||||||
|
position="bottom"
|
||||||
|
contentText={<span>{t('reportLastRunTooltip')}</span>}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Text color={TextColor.textDefault}>
|
||||||
|
{lastReport
|
||||||
|
? formatDate(new Date(lastReport.createTime).getTime())
|
||||||
|
: 'N/A'}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<SwapsFooter
|
||||||
|
onSubmit={() => {
|
||||||
|
onGenerate(address);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
submitText={t('runReport')}
|
||||||
|
onCancel={() =>
|
||||||
|
global.platform.openTab({
|
||||||
|
url: `https://${complianceTenantSubdomain}.compliance.codefi.network/app/kyt/addresses/${lastReport.address}/${lastReport.reportId}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
cancelText={t('showReport')}
|
||||||
|
hideCancel={!lastReport}
|
||||||
|
approveActive={lastReport}
|
||||||
|
showTopBorder
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ComplianceDetails.propTypes = {
|
||||||
|
address: PropTypes.string,
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
onGenerate: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComplianceDetails;
|
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import configureStore from '../../../store/store';
|
||||||
|
import testData from '../../../../.storybook/test-data';
|
||||||
|
import ComplianceDetails from '.';
|
||||||
|
|
||||||
|
const customData = {
|
||||||
|
...testData,
|
||||||
|
metamask: {
|
||||||
|
institutionalFeatures: {
|
||||||
|
complianceProjectId: '',
|
||||||
|
complianceClientId: '',
|
||||||
|
reportsInProgress: {},
|
||||||
|
historicalReports: {
|
||||||
|
'0xAddress': [
|
||||||
|
{
|
||||||
|
reportId: 'reportId',
|
||||||
|
address: '0xAddress',
|
||||||
|
risk: 'low',
|
||||||
|
creatTime: new Date(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = configureStore(customData);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/Institutional/ComplianceDetails',
|
||||||
|
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||||
|
component: ComplianceDetails,
|
||||||
|
args: {
|
||||||
|
address: '0xAddress',
|
||||||
|
onClose: () => undefined,
|
||||||
|
onGenerate: () => undefined,
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
onClick: {
|
||||||
|
action: 'onClick',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <ComplianceDetails {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'ComplianceDetails';
|
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import configureStore from 'redux-mock-store';
|
||||||
|
import { fireEvent, screen } from '@testing-library/react';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import ComplianceDetails from './compliance-details';
|
||||||
|
|
||||||
|
const initState = {
|
||||||
|
metamask: {
|
||||||
|
institutionalFeatures: {
|
||||||
|
complianceProjectId: '',
|
||||||
|
complianceClientId: '',
|
||||||
|
reportsInProgress: {},
|
||||||
|
historicalReports: {
|
||||||
|
'0xAddress': [
|
||||||
|
{
|
||||||
|
reportId: 'reportId',
|
||||||
|
address: '0xAddress',
|
||||||
|
risk: 'low',
|
||||||
|
creatTime: new Date(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const middlewares = [thunk];
|
||||||
|
const mockStore = configureStore(middlewares);
|
||||||
|
|
||||||
|
describe('ComplianceDetails', () => {
|
||||||
|
const props = {
|
||||||
|
address: '0xAddress',
|
||||||
|
onClose: jest.fn(),
|
||||||
|
onGenerate: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = mockStore(initState);
|
||||||
|
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<ComplianceDetails
|
||||||
|
address={props.address}
|
||||||
|
onClose={props.onClose}
|
||||||
|
onGenerate={props.onGenerate}
|
||||||
|
/>,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs onGenerate fuction', () => {
|
||||||
|
renderWithProvider(
|
||||||
|
<ComplianceDetails
|
||||||
|
address={props.address}
|
||||||
|
onClose={props.onClose}
|
||||||
|
onGenerate={props.onGenerate}
|
||||||
|
/>,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(screen.queryByTestId('page-container-footer-next'));
|
||||||
|
|
||||||
|
expect(props.onGenerate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(props.onGenerate).toHaveBeenCalledWith(props.address);
|
||||||
|
});
|
||||||
|
});
|
1
ui/components/institutional/compliance-details/index.js
Normal file
1
ui/components/institutional/compliance-details/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './compliance-details';
|
@ -0,0 +1,5 @@
|
|||||||
|
.compliance-details {
|
||||||
|
&__row {
|
||||||
|
border-top: 1px solid var(--color-border-muted);
|
||||||
|
}
|
||||||
|
}
|
11
ui/components/institutional/institutional-components.scss
Normal file
11
ui/components/institutional/institutional-components.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Please import your styles in order of atomicity.
|
||||||
|
* The most atomic styles should be imported first.
|
||||||
|
* This will help improve specificity and reduce the chance of
|
||||||
|
* unintended overrides.
|
||||||
|
**/
|
||||||
|
@import 'compliance-details/index';
|
||||||
|
@import 'compliance-settings/index';
|
||||||
|
@import 'jwt-dropdown/jwt-dropdown';
|
||||||
|
@import 'jwt-url-form/jwt-url-form';
|
||||||
|
@import 'note-to-trader/index';
|
@ -11,6 +11,9 @@
|
|||||||
@import '../components/component-library/component-library-components.scss';
|
@import '../components/component-library/component-library-components.scss';
|
||||||
@import '../components/app/app-components';
|
@import '../components/app/app-components';
|
||||||
@import '../components/ui/ui-components';
|
@import '../components/ui/ui-components';
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||||
|
@import '../components/institutional/institutional-components';
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
@import '../components/multichain/multichain-components.scss';
|
@import '../components/multichain/multichain-components.scss';
|
||||||
@import '../pages/pages';
|
@import '../pages/pages';
|
||||||
@import './errors.scss';
|
@import './errors.scss';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user