mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +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": {
|
||||
"message": "No NFTs yet"
|
||||
},
|
||||
"noReport": {
|
||||
"message": "No Report"
|
||||
},
|
||||
"noSnaps": {
|
||||
"message": "You don't have any snaps installed."
|
||||
},
|
||||
@ -3245,6 +3248,12 @@
|
||||
"replace": {
|
||||
"message": "replace"
|
||||
},
|
||||
"reportLastRun": {
|
||||
"message": "Report last run"
|
||||
},
|
||||
"reportLastRunTooltip": {
|
||||
"message": "The date and time of when the last AML/CFT report was run"
|
||||
},
|
||||
"requestFailed": {
|
||||
"message": "Request failed"
|
||||
},
|
||||
@ -3374,9 +3383,18 @@
|
||||
"revokeSpendingCapTooltipText": {
|
||||
"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": {
|
||||
"message": "New RPC URL"
|
||||
},
|
||||
"runReport": {
|
||||
"message": "Run report"
|
||||
},
|
||||
"safeTransferFrom": {
|
||||
"message": "Safe transfer from"
|
||||
},
|
||||
@ -3591,6 +3609,9 @@
|
||||
"showPrivateKeys": {
|
||||
"message": "Show Private Keys"
|
||||
},
|
||||
"showReport": {
|
||||
"message": "Show report"
|
||||
},
|
||||
"showTestnetNetworks": {
|
||||
"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/app/app-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 '../pages/pages';
|
||||
@import './errors.scss';
|
||||
|
Loading…
Reference in New Issue
Block a user