mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
[MMI] compliance feature page (#18320)
* MMI-2657 adds the compliance settings screen * MMI-2657 adds stories file * wip compliance feature page * adds stories and fixes imports * adds storybook and tests * lint fix
This commit is contained in:
parent
12bd9b0b94
commit
8fbd1e30e8
36
app/_locales/en/messages.json
generated
36
app/_locales/en/messages.json
generated
@ -150,6 +150,9 @@
|
||||
"accountSelectionRequired": {
|
||||
"message": "You need to select an account!"
|
||||
},
|
||||
"activated": {
|
||||
"message": "Active"
|
||||
},
|
||||
"active": {
|
||||
"message": "Active"
|
||||
},
|
||||
@ -639,9 +642,39 @@
|
||||
"close": {
|
||||
"message": "Close"
|
||||
},
|
||||
"codefiCompliance": {
|
||||
"message": "Codefi Compliance"
|
||||
},
|
||||
"coingecko": {
|
||||
"message": "CoinGecko"
|
||||
},
|
||||
"complianceBlurb0": {
|
||||
"message": "DeFi raises AML/CFT risk for institutions, given the decentralised pools and pseudonymous counterparties."
|
||||
},
|
||||
"complianceBlurb1": {
|
||||
"message": "Codefi Compliance is the only product capable of running AML/CFT analysis on DeFi pools. This allows you to identify and avoid pools and counterparties that fail your risk setting."
|
||||
},
|
||||
"complianceBlurbStep1": {
|
||||
"message": "Sign up to Codefi Compliance below"
|
||||
},
|
||||
"complianceBlurbStep2": {
|
||||
"message": "Create an organisation"
|
||||
},
|
||||
"complianceBlurbStep3": {
|
||||
"message": "Create a project"
|
||||
},
|
||||
"complianceBlurbStep4": {
|
||||
"message": "Set your compliance settings"
|
||||
},
|
||||
"complianceBlurbStep5": {
|
||||
"message": "Click the \"Enable Compliance in MMI\" button"
|
||||
},
|
||||
"complianceBlurpStep0": {
|
||||
"message": "Steps to enable AML/CFT Compliance:"
|
||||
},
|
||||
"complianceSettingsExplanation": {
|
||||
"message": "Change your settings or view reports by opening up Codefi Compliance or disconnect below."
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
@ -2676,6 +2709,9 @@
|
||||
"onlyConnectTrust": {
|
||||
"message": "Only connect with sites you trust."
|
||||
},
|
||||
"openCodefiCompliance": {
|
||||
"message": "Open Codefi Compliance"
|
||||
},
|
||||
"openFullScreenForLedgerWebHid": {
|
||||
"message": "Open MetaMask in full screen to connect your ledger via WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
|
@ -0,0 +1,98 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Compliance Settings shows disconnect when Compliance is activated 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="box institutional-feature__content box--flex-direction-row"
|
||||
>
|
||||
Change your settings or view reports by opening up Codefi Compliance or disconnect below.
|
||||
</div>
|
||||
<footer
|
||||
class="institutional-feature__footer"
|
||||
>
|
||||
<button
|
||||
class="button btn--rounded btn-default btn--large"
|
||||
data-testid="disconnect-compliance"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Disconnect
|
||||
</button>
|
||||
<button
|
||||
class="button btn--rounded btn-primary"
|
||||
data-testid="start-compliance"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Open Codefi Compliance
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Compliance Settings shows start btn when Compliance its not activated 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box box--sm:padding-6 box--flex-direction-row box--color-text-alternative"
|
||||
data-testid="institutional-content"
|
||||
>
|
||||
<div
|
||||
class="box institutional-feature__content box--flex-direction-row"
|
||||
>
|
||||
<p
|
||||
class="box mm-text mm-text--body-md mm-text--color-text-default box--padding-bottom-3 box--flex-direction-row"
|
||||
>
|
||||
DeFi raises AML/CFT risk for institutions, given the decentralised pools and pseudonymous counterparties.
|
||||
</p>
|
||||
<p
|
||||
class="box mm-text mm-text--body-md mm-text--color-text-default box--padding-bottom-3 box--flex-direction-row"
|
||||
>
|
||||
Codefi Compliance is the only product capable of running AML/CFT analysis on DeFi pools. This allows you to identify and avoid pools and counterparties that fail your risk setting.
|
||||
</p>
|
||||
<p
|
||||
class="box mm-text mm-text--body-md mm-text--color-text-default box--padding-bottom-3 box--flex-direction-row"
|
||||
>
|
||||
Steps to enable AML/CFT Compliance:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Sign up to Codefi Compliance below
|
||||
</li>
|
||||
<li>
|
||||
Create an organisation
|
||||
</li>
|
||||
<li>
|
||||
Create a project
|
||||
</li>
|
||||
<li>
|
||||
Set your compliance settings
|
||||
</li>
|
||||
<li>
|
||||
Click the "Enable Compliance in MMI" button
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div
|
||||
class="box box--display-flex box--flex-direction-row box--justify-content-center"
|
||||
>
|
||||
<footer
|
||||
class="institutional-feature__footer"
|
||||
padding="4,6"
|
||||
>
|
||||
<button
|
||||
class="button btn--rounded btn-primary"
|
||||
data-testid="start-compliance"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Open Codefi Compliance
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,98 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
JustifyContent,
|
||||
DISPLAY,
|
||||
TextColor,
|
||||
FLEX_DIRECTION,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
|
||||
import { Text } from '../../component-library';
|
||||
import Box from '../../ui/box';
|
||||
import Button from '../../ui/button';
|
||||
|
||||
const ComplianceSettings = () => {
|
||||
const t = useContext(I18nContext);
|
||||
const dispatch = useDispatch();
|
||||
const mmiActions = mmiActionsFactory();
|
||||
|
||||
const complianceActivated = useSelector((state) =>
|
||||
Boolean(state.metamask.institutionalFeatures?.complianceProjectId),
|
||||
);
|
||||
|
||||
const disconnectFromCompliance = async () => {
|
||||
await dispatch(mmiActions.deleteComplianceAuthData());
|
||||
};
|
||||
|
||||
const renderDisconnect = () => {
|
||||
return (
|
||||
<Button
|
||||
type="default"
|
||||
large
|
||||
onClick={disconnectFromCompliance}
|
||||
data-testid="disconnect-compliance"
|
||||
>
|
||||
{t('disconnect')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const renderLinkButton = () => {
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
data-testid="start-compliance"
|
||||
onClick={() => {
|
||||
global.platform.openTab({
|
||||
url: 'https://start.compliance.codefi.network/',
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('openCodefiCompliance')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
return complianceActivated ? (
|
||||
<Box>
|
||||
<Box className="institutional-feature__content">
|
||||
{t('complianceSettingsExplanation')}
|
||||
</Box>
|
||||
<footer className="institutional-feature__footer">
|
||||
{renderDisconnect()}
|
||||
{renderLinkButton()}
|
||||
</footer>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
padding={[0, 6]}
|
||||
color={TextColor.textAlternative}
|
||||
data-testid="institutional-content"
|
||||
>
|
||||
<Box className="institutional-feature__content">
|
||||
<Text paddingBottom={3}>{t('complianceBlurb0')}</Text>
|
||||
<Text paddingBottom={3}>{t('complianceBlurb1')}</Text>
|
||||
<Text paddingBottom={3}>{t('complianceBlurpStep0')}</Text>
|
||||
<ol>
|
||||
<li>{t('complianceBlurbStep1')}</li>
|
||||
<li>{t('complianceBlurbStep2')}</li>
|
||||
<li>{t('complianceBlurbStep3')}</li>
|
||||
<li>{t('complianceBlurbStep4')}</li>
|
||||
<li>{t('complianceBlurbStep5')}</li>
|
||||
</ol>
|
||||
</Box>
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.ROW}
|
||||
justifyContent={JustifyContent.center}
|
||||
>
|
||||
<footer padding={[4, 6]} className="institutional-feature__footer">
|
||||
{renderLinkButton()}
|
||||
</footer>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComplianceSettings;
|
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import configureStore from '../../../store/store';
|
||||
import testData from '../../../../.storybook/test-data';
|
||||
import ComplianceSettings from '.';
|
||||
|
||||
const customData = {
|
||||
...testData,
|
||||
metamask: {
|
||||
...testData.metamask,
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '',
|
||||
complianceClientId: '',
|
||||
reportsInProgress: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureStore(customData);
|
||||
|
||||
export default {
|
||||
title: 'Components/Institutional/ComplianceSettings',
|
||||
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||
component: ComplianceSettings,
|
||||
args: {
|
||||
onClick: () => {
|
||||
action('onClick');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => <ComplianceSettings {...args} />;
|
||||
|
||||
DefaultStory.storyName = 'ComplianceSettings';
|
@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import ComplianceSettings from '.';
|
||||
|
||||
const mockedDeleteComplianceAuthData = jest
|
||||
.fn()
|
||||
.mockReturnValue({ type: 'TYPE' });
|
||||
jest.mock('../../../store/institutional/institution-background', () => ({
|
||||
mmiActionsFactory: () => ({
|
||||
deleteComplianceAuthData: mockedDeleteComplianceAuthData,
|
||||
}),
|
||||
}));
|
||||
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '',
|
||||
complianceClientId: '',
|
||||
reportsInProgress: {},
|
||||
},
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('Compliance Settings', () => {
|
||||
it('shows start btn when Compliance its not activated', () => {
|
||||
const store = configureMockStore()(mockStore);
|
||||
|
||||
const { container, getByTestId } = renderWithProvider(
|
||||
<ComplianceSettings />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(getByTestId('start-compliance')).toBeVisible();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('shows disconnect when Compliance is activated', () => {
|
||||
const customMockStore = {
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...mockStore.metamask,
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '123',
|
||||
complianceClientId: '123',
|
||||
reportsInProgress: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureMockStore()(customMockStore);
|
||||
|
||||
const { container, getByTestId } = renderWithProvider(
|
||||
<ComplianceSettings />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(getByTestId('disconnect-compliance')).toBeVisible();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
1
ui/components/institutional/compliance-settings/index.js
Normal file
1
ui/components/institutional/compliance-settings/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './compliance-settings';
|
27
ui/components/institutional/compliance-settings/index.scss
Normal file
27
ui/components/institutional/compliance-settings/index.scss
Normal file
@ -0,0 +1,27 @@
|
||||
.institutional-feature {
|
||||
&__footer {
|
||||
border-top: 1px solid var(--color-text-muted);
|
||||
padding: 16px 30px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
button {
|
||||
min-width: 0;
|
||||
margin-right: 16px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
@include H6;
|
||||
|
||||
line-height: 22px;
|
||||
|
||||
ol {
|
||||
list-style: decimal;
|
||||
list-style-position: inside;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
||||
import {
|
||||
JustifyContent,
|
||||
DISPLAY,
|
||||
AlignItems,
|
||||
TextColor,
|
||||
TEXT_ALIGN,
|
||||
BackgroundColor,
|
||||
Color,
|
||||
FLEX_DIRECTION,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import {
|
||||
ButtonIcon,
|
||||
ICON_NAMES,
|
||||
ICON_SIZES,
|
||||
Text,
|
||||
} from '../../../components/component-library';
|
||||
import Box from '../../../components/ui/box';
|
||||
import ComplianceSettings from '../../../components/institutional/compliance-settings';
|
||||
|
||||
const ComplianceFeaturePage = () => {
|
||||
const t = useContext(I18nContext);
|
||||
const history = useHistory();
|
||||
|
||||
const complianceActivated = useSelector((state) =>
|
||||
Boolean(state.metamask.institutionalFeatures?.complianceProjectId),
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="institutional-entity"
|
||||
backgroundColor={BackgroundColor.backgroundDefault}
|
||||
>
|
||||
<>
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
padding={[0, 6, 6]}
|
||||
className="feature-connect__header"
|
||||
>
|
||||
<ButtonIcon
|
||||
ariaLabel={t('back')}
|
||||
iconName={ICON_NAMES.ARROW_LEFT}
|
||||
size={ICON_SIZES.SM}
|
||||
className="settings-page__back-button"
|
||||
color={Color.iconDefault}
|
||||
onClick={() => history.push(DEFAULT_ROUTE)}
|
||||
display={[DISPLAY.FLEX]}
|
||||
/>
|
||||
<Text
|
||||
as="h4"
|
||||
marginTop={4}
|
||||
marginBottom={4}
|
||||
className="feature-connect__header__title"
|
||||
>
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
alignItems={AlignItems.center}
|
||||
color={TextColor.textDefault}
|
||||
>
|
||||
<img
|
||||
className="feature-connect__list__list-item__img"
|
||||
src="images/compliance-logo-small.svg"
|
||||
alt="Codefi Compliance"
|
||||
/>
|
||||
{t('codefiCompliance')}
|
||||
{complianceActivated && (
|
||||
<Text
|
||||
as="h6"
|
||||
margin={[2, 2, 0, 2]}
|
||||
color={TextColor.textMuted}
|
||||
display={DISPLAY.FLEX}
|
||||
textAlign={TEXT_ALIGN.LEFT}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
justifyContent={JustifyContent.center}
|
||||
className="feature-connect__label__text feature-connect__label__text--activated"
|
||||
data-testid="activated-label"
|
||||
>
|
||||
{t('activated')}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Text>
|
||||
</Box>
|
||||
<ComplianceSettings />
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComplianceFeaturePage;
|
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import configureStore from '../../../store/store';
|
||||
import testData from '../../../../.storybook/test-data';
|
||||
import ComplianceFeaturePage from '.';
|
||||
|
||||
const customData = {
|
||||
...testData,
|
||||
metamask: {
|
||||
...testData.metamask,
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '',
|
||||
complianceClientId: '',
|
||||
reportsInProgress: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureStore(customData);
|
||||
|
||||
export default {
|
||||
title: 'Pages/Institutional/ComplianceFeaturePage',
|
||||
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||
component: ComplianceFeaturePage,
|
||||
args: {
|
||||
onClick: () => {
|
||||
action('onClick');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => <ComplianceFeaturePage {...args} />;
|
||||
|
||||
DefaultStory.storyName = 'ComplianceFeaturePage';
|
@ -0,0 +1,107 @@
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import ComplianceFeaturePage from '.';
|
||||
|
||||
const mockedDeleteComplianceAuthData = jest
|
||||
.fn()
|
||||
.mockReturnValue({ type: 'TYPE' });
|
||||
jest.mock('../../../store/institutional/institution-background', () => ({
|
||||
mmiActionsFactory: () => ({
|
||||
deleteComplianceAuthData: mockedDeleteComplianceAuthData,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Compliance Feature, connect', function () {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '',
|
||||
},
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('shows compliance feature button as activated', () => {
|
||||
const customMockStore = {
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...mockStore.metamask,
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '123',
|
||||
complianceClientId: '123',
|
||||
reportsInProgress: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureMockStore()(customMockStore);
|
||||
|
||||
const { getByText, getByTestId } = renderWithProvider(
|
||||
<ComplianceFeaturePage />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(getByTestId('activated-label')).toBeVisible();
|
||||
expect(getByText('Active')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows ComplianceSettings when feature is not activated', () => {
|
||||
const store = configureMockStore()(mockStore);
|
||||
|
||||
const { getByTestId } = renderWithProvider(
|
||||
<ComplianceFeaturePage />,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(getByTestId('institutional-content')).toBeVisible();
|
||||
});
|
||||
|
||||
it('opens new tab on Open Codefi Compliance click', async () => {
|
||||
global.platform = { openTab: sinon.spy() };
|
||||
const store = configureMockStore()(mockStore);
|
||||
|
||||
const { queryByTestId } = renderWithProvider(
|
||||
<ComplianceFeaturePage />,
|
||||
store,
|
||||
);
|
||||
|
||||
const startBtn = queryByTestId('start-compliance');
|
||||
fireEvent.click(startBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(global.platform.openTab.calledOnce).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls deleteComplianceAuthData on disconnect click', async () => {
|
||||
const customMockStore = {
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...mockStore.metamask,
|
||||
institutionalFeatures: {
|
||||
complianceProjectId: '123',
|
||||
complianceClientId: '123',
|
||||
reportsInProgress: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureMockStore()(customMockStore);
|
||||
const { getByTestId } = renderWithProvider(
|
||||
<ComplianceFeaturePage />,
|
||||
store,
|
||||
);
|
||||
|
||||
const disconnectBtn = getByTestId('disconnect-compliance');
|
||||
fireEvent.click(disconnectBtn);
|
||||
expect(mockedDeleteComplianceAuthData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
1
ui/pages/institutional/compliance-feature-page/index.js
Normal file
1
ui/pages/institutional/compliance-feature-page/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './compliance-feature-page';
|
29
ui/pages/institutional/compliance-feature-page/index.scss
Normal file
29
ui/pages/institutional/compliance-feature-page/index.scss
Normal file
@ -0,0 +1,29 @@
|
||||
.institutional-entity {
|
||||
box-shadow: 0 0 7px 0 rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.feature-connect {
|
||||
&__list {
|
||||
&__list-item {
|
||||
&__img {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
&__text {
|
||||
font-size: 0.6rem;
|
||||
|
||||
&--activated {
|
||||
color: var(--color-text-default);
|
||||
background: var(--color-primary-default);
|
||||
padding: 3px 10px;
|
||||
border-radius: 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user