1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

UX Multichain: Menu for Site connections and permissions (#18167)

* added site connection menu component

* reverted change for unlock page

* updated snapshot

* updated state with useSelector

* updated state for connected

* updated icons

* updated test

* updated snapshot

* moved component to multichain folder

* updated color

* added multichain connection to menu bar

* updated default color

* updated css

* updated multichain site with connected site info

* updated ui for not connected state

* removed scripts

* updated lint errors

* updated lint errors

* updated stories

* updated story for not connected to current state

* updated story for not connected to current state

* updated badge to 16px

* updated badge position

* updated snapshot

* fixed lint errors

* updated not connected state icon

* updated constants for status and added new locale string
This commit is contained in:
Nidhi Kumari 2023-03-31 22:53:27 +05:30 committed by GitHub
parent ae08035d5d
commit 0bbfd38cc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 409 additions and 6 deletions

View File

@ -3753,6 +3753,9 @@
"statusNotConnected": { "statusNotConnected": {
"message": "Not connected" "message": "Not connected"
}, },
"statusNotConnectedAccount": {
"message": "No accounts connected"
},
"step1LatticeWallet": { "step1LatticeWallet": {
"message": "Connect your Lattice1" "message": "Connect your Lattice1"
}, },
@ -4351,6 +4354,12 @@
"tooltipApproveButton": { "tooltipApproveButton": {
"message": "I understand" "message": "I understand"
}, },
"tooltipSatusConnected": {
"message": "connected"
},
"tooltipSatusNotConnected": {
"message": "not connected"
},
"total": { "total": {
"message": "Total" "message": "Total"
}, },

View File

@ -8,13 +8,17 @@ import {
STATUS_NOT_CONNECTED, STATUS_NOT_CONNECTED,
} from '../../../helpers/constants/connected-sites'; } from '../../../helpers/constants/connected-sites';
import ColorIndicator from '../../ui/color-indicator'; import ColorIndicator from '../../ui/color-indicator';
import { Color } from '../../../helpers/constants/design-system'; import {
BackgroundColor,
Color,
} from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext'; import { useI18nContext } from '../../../hooks/useI18nContext';
import { import {
getAddressConnectedSubjectMap, getAddressConnectedSubjectMap,
getOriginOfCurrentTab, getOriginOfCurrentTab,
getSelectedAddress, getSelectedAddress,
} from '../../../selectors'; } from '../../../selectors';
import { MultichainConnectedSiteMenu } from '../../multichain';
export default function ConnectedStatusIndicator({ onClick }) { export default function ConnectedStatusIndicator({ onClick }) {
const t = useI18nContext(); const t = useI18nContext();
@ -38,23 +42,39 @@ export default function ConnectedStatusIndicator({ onClick }) {
let indicatorType = ColorIndicator.TYPES.OUTLINE; let indicatorType = ColorIndicator.TYPES.OUTLINE;
let indicatorColor = Color.iconDefault; let indicatorColor = Color.iconDefault;
let globalMenuColor = Color.iconAlternative;
if (status === STATUS_CONNECTED) { if (status === STATUS_CONNECTED) {
indicatorColor = Color.successDefault; indicatorColor = Color.successDefault;
indicatorType = ColorIndicator.TYPES.PARTIAL; indicatorType = ColorIndicator.TYPES.PARTIAL;
globalMenuColor = Color.successDefault;
} else if (status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT) { } else if (status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT) {
indicatorColor = Color.errorDefault; indicatorColor = Color.errorDefault;
globalMenuColor = BackgroundColor.backgroundDefault;
} }
const text = const text =
status === STATUS_CONNECTED status === STATUS_CONNECTED
? t('statusConnected') ? t('statusConnected')
: t('statusNotConnected'); : t('statusNotConnected'); // TODO: Remove text since we only need the tooltip text for new permission icon
const tooltipText =
status === STATUS_CONNECTED
? t('tooltipSatusConnected')
: t('tooltipSatusNotConnected');
return ( return (
<button className="connected-status-indicator" onClick={onClick}> <button className="connected-status-indicator" onClick={onClick}>
<ColorIndicator color={indicatorColor} type={indicatorType} /> {process.env.MULTICHAIN ? (
<div className="connected-status-indicator__text">{text}</div> <MultichainConnectedSiteMenu
status={status}
globalMenuColor={globalMenuColor}
text={tooltipText}
/>
) : (
<>
<ColorIndicator color={indicatorColor} type={indicatorType} />
<div className="connected-status-indicator__text">{text}</div>
</>
)}
</button> </button>
); );
} }

View File

@ -30,7 +30,7 @@ export default function MenuBar() {
return ( return (
<div className="menu-bar"> <div className="menu-bar">
{showStatus ? ( {showStatus ? ( // TODO: Move the connection status menu icon to the correct position in header once we implement the new header
<ConnectedStatusIndicator <ConnectedStatusIndicator
onClick={() => history.push(CONNECTED_ACCOUNTS_ROUTE)} onClick={() => history.push(CONNECTED_ACCOUNTS_ROUTE)}
/> />

View File

@ -7,3 +7,4 @@ export { GlobalMenu } from './global-menu';
export { MultichainImportTokenLink } from './multichain-import-token-link'; export { MultichainImportTokenLink } from './multichain-import-token-link';
export { MultichainTokenListItem } from './multichain-token-list-item'; export { MultichainTokenListItem } from './multichain-token-list-item';
export { AddressCopyButton } from './address-copy-button'; export { AddressCopyButton } from './address-copy-button';
export { MultichainConnectedSiteMenu } from './multichain-connected-site-menu';

View File

@ -8,4 +8,5 @@
@import 'account-list-item/index'; @import 'account-list-item/index';
@import 'account-list-menu/index'; @import 'account-list-menu/index';
@import 'account-picker/index'; @import 'account-picker/index';
@import 'multichain-connected-site-menu/index';
@import 'multichain-token-list-item/multichain-token-list-item'; @import 'multichain-token-list-item/multichain-token-list-item';

View File

@ -0,0 +1,112 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Multichain Connected Site Menu should render the site menu in connected state 1`] = `
<div>
<div
class="box multichain-connected-site-menu box--flex-direction-row"
data-testid="connection-menu"
>
<div>
<div
aria-describedby="tippy-tooltip-1"
class=""
data-original-title="Account 2 connected"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<div
class="box mm-badge-wrapper box--display-inline-block box--flex-direction-row"
>
<span
class="box mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-icon-default"
style="mask-image: url('./images/icons/global.svg');"
/>
<div
class="box mm-badge-wrapper__badge-container box--flex-direction-row"
style="bottom: 2px; right: -4px; z-index: 1;"
>
<div
class="box multichain-connected-site-menu__badge box--flex-direction-row box--background-color-success-default box--rounded-full box--border-color-background-default box--border-width-3 box--border-style-solid"
/>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Multichain Connected Site Menu should render the site menu in not connected state 1`] = `
<div>
<div
class="box multichain-connected-site-menu box--flex-direction-row"
data-testid="connection-menu"
>
<div>
<div
aria-describedby="tippy-tooltip-2"
class=""
data-original-title="No accounts connected"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<div
class="box mm-badge-wrapper box--display-inline-block box--flex-direction-row"
>
<span
class="box mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-icon-default"
style="mask-image: url('./images/icons/global.svg');"
/>
<div
class="box mm-badge-wrapper__badge-container box--flex-direction-row"
style="bottom: 2px; right: -4px; z-index: 1;"
>
<div
class="box multichain-connected-site-menu__badge box--flex-direction-row box--background-color-icon-alternative box--rounded-full box--border-color-background-default box--border-width-3 box--border-style-solid"
/>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Multichain Connected Site Menu should render the site menu in not connected to current account state 1`] = `
<div>
<div
class="box multichain-connected-site-menu box--flex-direction-row"
data-testid="connection-menu"
>
<div>
<div
aria-describedby="tippy-tooltip-3"
class=""
data-original-title="Account 2 not connected"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<div
class="box mm-badge-wrapper box--display-inline-block box--flex-direction-row"
>
<span
class="box mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-icon-default"
style="mask-image: url('./images/icons/global.svg');"
/>
<div
class="box mm-badge-wrapper__badge-container box--flex-direction-row"
style="bottom: 4px; right: -1px; z-index: 1;"
>
<div
class="box multichain-connected-site-menu__badge not-connected box--flex-direction-row box--background-color-background-default box--rounded-full box--border-color-success-default box--border-width-2 box--border-style-solid"
/>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1 @@
export { MultichainConnectedSiteMenu } from './multichain-connected-site-menu';

View File

@ -0,0 +1,23 @@
.multichain-connected-site-menu {
&__badge {
height: 16px;
width: 16px;
}
&__badge.not-connected {
height: 10px;
width: 10px;
}
&__badge.not-connected::after {
content: '';
position: absolute;
top: -3px;
left: -3px;
right: -3px;
bottom: -3px;
background: var(--color-background-default);
z-index: -1;
border-radius: 50%;
}
}

View File

@ -0,0 +1,98 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import {
STATUS_CONNECTED_TO_ANOTHER_ACCOUNT,
STATUS_NOT_CONNECTED,
} from '../../../helpers/constants/connected-sites';
import {
BackgroundColor,
BorderColor,
BorderRadius,
IconColor,
Size,
} from '../../../helpers/constants/design-system';
import { BadgeWrapper, Icon, ICON_NAMES } from '../../component-library';
import Box from '../../ui/box';
import { getSelectedIdentity } from '../../../selectors';
import Tooltip from '../../ui/tooltip';
import { useI18nContext } from '../../../hooks/useI18nContext';
export const MultichainConnectedSiteMenu = ({
className,
globalMenuColor,
status,
text,
}) => {
const t = useI18nContext();
const selectedAccount = useSelector(getSelectedIdentity);
return (
<Box
className={classNames('multichain-connected-site-menu', className)}
data-testid="connection-menu"
>
<Tooltip
title={
status === STATUS_NOT_CONNECTED
? t('statusNotConnectedAccount')
: `${selectedAccount?.name} ${text}`
}
data-testid="multichain-connected-site-menu__tooltip"
position="bottom"
>
<BadgeWrapper
positionObj={
status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT
? { bottom: 4, right: -1, zIndex: 1 }
: { bottom: 2, right: -4, zIndex: 1 }
}
badge={
<Box
backgroundColor={globalMenuColor}
className={`multichain-connected-site-menu__badge ${
status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT
? 'not-connected'
: ''
}`}
borderRadius={BorderRadius.full}
borderColor={
status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT
? BorderColor.successDefault
: BackgroundColor.backgroundDefault
}
borderWidth={
status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT ? 2 : 3
}
/>
}
>
<Icon
name={ICON_NAMES.GLOBAL}
size={Size.LG}
color={IconColor.iconDefault}
/>
</BadgeWrapper>
</Tooltip>
</Box>
);
};
MultichainConnectedSiteMenu.propTypes = {
/**
* Additional classNames to be added to the MultichainConnectedSiteMenu
*/
className: PropTypes.string,
/**
* Background color based on the connection status
*/
globalMenuColor: PropTypes.string.isRequired,
/**
* Connection status string
*/
status: PropTypes.string.isRequired,
/**
* Connection status message
*/
text: PropTypes.string,
};

View File

@ -0,0 +1,51 @@
import React from 'react';
import {
STATUS_CONNECTED,
STATUS_CONNECTED_TO_ANOTHER_ACCOUNT,
STATUS_NOT_CONNECTED,
} from '../../../helpers/constants/connected-sites';
import {
BackgroundColor,
Color,
} from '../../../helpers/constants/design-system';
import { MultichainConnectedSiteMenu } from './multichain-connected-site-menu';
export default {
title: 'Components/Multichain/MultichainConnectedSiteMenu',
component: MultichainConnectedSiteMenu,
argTypes: {
globalMenuColor: {
control: 'text',
},
text: {
control: 'text',
},
status: {
control: 'text',
},
},
args: {
globalMenuColor: Color.iconAlternative,
status: STATUS_NOT_CONNECTED,
},
};
const Template = (args) => {
return <MultichainConnectedSiteMenu {...args} />;
};
export const DefaultStory = Template.bind({});
export const ConnectedStory = Template.bind({});
ConnectedStory.args = {
globalMenuColor: Color.successDefault,
text: 'connected',
status: STATUS_CONNECTED,
};
export const ConnectedtoAnotherAccountStory = Template.bind({});
ConnectedtoAnotherAccountStory.args = {
globalMenuColor: BackgroundColor.backgroundDefault,
text: 'not connected',
status: STATUS_CONNECTED_TO_ANOTHER_ACCOUNT,
};

View File

@ -0,0 +1,87 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import { renderWithProvider } from '../../../../test/jest';
import {
STATUS_CONNECTED,
STATUS_CONNECTED_TO_ANOTHER_ACCOUNT,
STATUS_NOT_CONNECTED,
} from '../../../helpers/constants/connected-sites';
import {
BackgroundColor,
Color,
} from '../../../helpers/constants/design-system';
import { MultichainConnectedSiteMenu } from './multichain-connected-site-menu';
describe('Multichain Connected Site Menu', () => {
const selectedAddress = '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b';
const identities = {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
name: 'Account 1',
},
'0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b': {
address: '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b',
name: 'Account 2',
},
};
const accounts = {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
balance: '0x0',
},
'0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b': {
address: '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b',
balance: '0x0',
},
};
const mockStore = {
metamask: {
selectedAddress,
identities,
accounts,
},
};
it('should render the site menu in connected state', () => {
const props = {
globalMenuColor: Color.successDefault,
text: 'connected',
status: STATUS_CONNECTED,
};
const store = configureMockStore()(mockStore);
const { getByTestId, container } = renderWithProvider(
<MultichainConnectedSiteMenu {...props} />,
store,
);
expect(getByTestId('connection-menu')).toBeDefined();
expect(container).toMatchSnapshot();
});
it('should render the site menu in not connected state', () => {
const props = {
globalMenuColor: Color.iconAlternative,
status: STATUS_NOT_CONNECTED,
};
const store = configureMockStore()(mockStore);
const { getByTestId, container } = renderWithProvider(
<MultichainConnectedSiteMenu {...props} />,
store,
);
expect(getByTestId('connection-menu')).toBeDefined();
expect(container).toMatchSnapshot();
});
it('should render the site menu in not connected to current account state', () => {
const props = {
globalMenuColor: BackgroundColor.backgroundDefault,
text: 'not connected',
status: STATUS_CONNECTED_TO_ANOTHER_ACCOUNT,
};
const store = configureMockStore()(mockStore);
const { getByTestId, container } = renderWithProvider(
<MultichainConnectedSiteMenu {...props} />,
store,
);
expect(getByTestId('connection-menu')).toBeDefined();
expect(container).toMatchSnapshot();
});
});