diff --git a/ui/components/app/flask/snap-permissions-list/index.js b/ui/components/app/flask/snap-permissions-list/index.js new file mode 100644 index 000000000..457784a5f --- /dev/null +++ b/ui/components/app/flask/snap-permissions-list/index.js @@ -0,0 +1 @@ +export { default } from './snap-permissions-list'; diff --git a/ui/components/app/flask/snap-permissions-list/snap-permissions-list.js b/ui/components/app/flask/snap-permissions-list/snap-permissions-list.js new file mode 100644 index 000000000..6dda11d04 --- /dev/null +++ b/ui/components/app/flask/snap-permissions-list/snap-permissions-list.js @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { getWeightedPermissions } from '../../../../helpers/utils/permission'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import PermissionCell from '../../permission-cell'; +import Box from '../../../ui/box'; + +export default function SnapPermissionsList({ + permissions, + targetSubjectMetadata, +}) { + const t = useI18nContext(); + + return ( + + {getWeightedPermissions(t, permissions, targetSubjectMetadata).map( + (permission, index) => { + return ( + + ); + }, + )} + + ); +} + +SnapPermissionsList.propTypes = { + permissions: PropTypes.object.isRequired, + targetSubjectMetadata: PropTypes.object.isRequired, +}; diff --git a/ui/components/app/flask/snap-permissions-list/snap-permissions-list.stories.js b/ui/components/app/flask/snap-permissions-list/snap-permissions-list.stories.js new file mode 100644 index 000000000..e3f24287e --- /dev/null +++ b/ui/components/app/flask/snap-permissions-list/snap-permissions-list.stories.js @@ -0,0 +1,37 @@ +import React from 'react'; + +import SnapPermissionsList from '.'; + +export default { + title: 'Components/App/flask/SnapPermissionsList', + + component: SnapPermissionsList, + argTypes: { + permissions: { + control: 'object', + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; + +DefaultStory.args = { + permissions: { + eth_accounts: {}, + snap_dialog: {}, + snap_getBip32PublicKey: { + caveats: [ + { + value: [ + { + path: ['m', `44'`, `0'`], + curve: 'secp256k1', + }, + ], + }, + ], + }, + }, +}; diff --git a/ui/components/app/flask/snap-permissions-list/snap-permissions-list.test.js b/ui/components/app/flask/snap-permissions-list/snap-permissions-list.test.js new file mode 100644 index 000000000..4ba174224 --- /dev/null +++ b/ui/components/app/flask/snap-permissions-list/snap-permissions-list.test.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { screen } from '@testing-library/react'; +import { renderWithProvider } from '../../../../../test/jest'; +import SnapPermissionsList from './snap-permissions-list'; + +describe('Snap Permission List', () => { + const mockPermissionData = { + snap_dialog: { + caveats: null, + date: 1680709920602, + id: '4dduR1BpsmS0ZJfeVtiAh', + invoker: 'local:http://localhost:8080', + parentCapability: 'snap_dialog', + }, + }; + const mockTargetSubjectMetadata = { + extensionId: null, + iconUrl: null, + name: 'TypeScript Example Snap', + origin: 'local:http://localhost:8080', + subjectType: 'snap', + version: '0.2.2', + }; + + it('renders permissions list for snaps', () => { + renderWithProvider( + , + ); + expect( + screen.getByText('Display dialog windows in MetaMask.'), + ).toBeInTheDocument(); + expect(screen.getByText('Approved on 2023-04-05')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/permission-cell/permission-cell.js b/ui/components/app/permission-cell/permission-cell.js index ea87a381e..fbbafea43 100644 --- a/ui/components/app/permission-cell/permission-cell.js +++ b/ui/components/app/permission-cell/permission-cell.js @@ -53,6 +53,11 @@ const PermissionCell = ({ iconBackgroundColor = Color.backgroundAlternative; } + let permissionIcon = avatarIcon; + if (typeof avatarIcon !== 'string' && avatarIcon?.props?.iconName) { + permissionIcon = avatarIcon.props.iconName; + } + return ( - {typeof avatarIcon === 'string' ? ( + {typeof permissionIcon === 'string' ? ( ) : ( - avatarIcon + permissionIcon )} diff --git a/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js b/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js index ffbbca22d..accab2e15 100644 --- a/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js +++ b/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js @@ -28,11 +28,14 @@ export default class PermissionPageContainerContent extends PureComponent { }; renderRequestedPermissions() { - const { selectedPermissions } = this.props; + const { selectedPermissions, subjectMetadata } = this.props; return (
- +
); } diff --git a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js index 5c5201f60..6d0ccfcb7 100644 --- a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js +++ b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js @@ -1,9 +1,29 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { getWeightedPermissions } from '../../../helpers/utils/permission'; +import { + getRightIcon, + getWeightedPermissions, +} from '../../../helpers/utils/permission'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import PermissionCell from '../permission-cell'; -import Box from '../../ui/box'; + +/** + * Get one or more permission descriptions for a permission name. + * + * @param permission - The permission to render. + * @param index - The index of the permission. + * @returns {JSX.Element} A permission description node. + */ +function getDescriptionNode(permission, index) { + const { label, leftIcon, permissionName } = permission; + + return ( +
+ {typeof leftIcon === 'string' ? : leftIcon} + {label} + {getRightIcon(permission)} +
+ ); +} export default function PermissionsConnectPermissionList({ permissions, @@ -12,22 +32,11 @@ export default function PermissionsConnectPermissionList({ const t = useI18nContext(); return ( - +
{getWeightedPermissions(t, permissions, targetSubjectMetadata).map( - (permission, index) => { - return ( - - ); - }, + getDescriptionNode, )} - +
); } diff --git a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.stories.js b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.stories.js index dd5cfb9ba..9ee800b03 100644 --- a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.stories.js +++ b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.stories.js @@ -20,18 +20,5 @@ DefaultStory.storyName = 'Default'; DefaultStory.args = { permissions: { eth_accounts: {}, - snap_dialog: {}, - snap_getBip32PublicKey: { - caveats: [ - { - value: [ - { - path: ['m', `44'`, `0'`], - curve: 'secp256k1', - }, - ], - }, - ], - }, }, }; diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js index b931f0656..09b7e1600 100644 --- a/ui/helpers/utils/permission.js +++ b/ui/helpers/utils/permission.js @@ -15,13 +15,23 @@ import { } from '../../../shared/constants/permissions'; import Tooltip from '../../components/ui/tooltip'; import { + AvatarIcon, ///: BEGIN:ONLY_INCLUDE_IN(flask) Text, + Icon, ///: END:ONLY_INCLUDE_IN } from '../../components/component-library'; -import { ICON_NAMES } from '../../components/component-library/icon/deprecated'; +import { + ICON_NAMES, + ICON_SIZES, +} from '../../components/component-library/icon/deprecated'; ///: BEGIN:ONLY_INCLUDE_IN(flask) -import { Color, FONT_WEIGHT, TextVariant } from '../constants/design-system'; +import { + Color, + FONT_WEIGHT, + IconColor, + TextVariant, +} from '../constants/design-system'; import { coinTypeToProtocolName, getSnapDerivationPathName, @@ -31,10 +41,33 @@ import { const UNKNOWN_PERMISSION = Symbol('unknown'); +///: BEGIN:ONLY_INCLUDE_IN(flask) +const RIGHT_INFO_ICON = ( + +); +///: END:ONLY_INCLUDE_IN + +function getLeftIcon(iconName) { + return ( + + ); +} + export const PERMISSION_DESCRIPTIONS = deepFreeze({ [RestrictedMethods.eth_accounts]: ({ t }) => ({ label: t('permission_ethereumAccounts'), - leftIcon: ICON_NAMES.EYE, + leftIcon: getLeftIcon(ICON_NAMES.EYE), + rightIcon: null, weight: 2, }), ///: BEGIN:ONLY_INCLUDE_IN(flask) @@ -251,7 +284,8 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({ [RestrictedMethods.wallet_snap]: ({ t, permissionValue }) => { const snaps = permissionValue.caveats[0].value; const baseDescription = { - leftIcon: ICON_NAMES.FLASH, + leftIcon: getLeftIcon(ICON_NAMES.FLASH), + rightIcon: RIGHT_INFO_ICON, }; return Object.keys(snaps).map((snapId) => { @@ -373,7 +407,8 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({ ///: END:ONLY_INCLUDE_IN [UNKNOWN_PERMISSION]: ({ t, permissionName }) => ({ label: t('permission_unknown', [permissionName ?? 'undefined']), - leftIcon: ICON_NAMES.QUESTION, + leftIcon: getLeftIcon(ICON_NAMES.QUESTION), + rightIcon: null, weight: 4, }), }); diff --git a/ui/pages/permissions-connect/flask/snap-install/snap-install.js b/ui/pages/permissions-connect/flask/snap-install/snap-install.js index 34a0cbcd3..d4b712623 100644 --- a/ui/pages/permissions-connect/flask/snap-install/snap-install.js +++ b/ui/pages/permissions-connect/flask/snap-install/snap-install.js @@ -1,7 +1,6 @@ import PropTypes from 'prop-types'; import React, { useCallback, useState } from 'react'; import { PageContainerFooter } from '../../../../components/ui/page-container'; -import PermissionsConnectPermissionList from '../../../../components/app/permissions-connect-permission-list'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import SnapInstallWarning from '../../../../components/app/flask/snap-install-warning'; import Box from '../../../../components/ui/box/box'; @@ -21,6 +20,7 @@ import SnapAuthorship from '../../../../components/app/flask/snap-authorship'; import { Text } from '../../../../components/component-library'; import { useOriginMetadata } from '../../../../hooks/useOriginMetadata'; import { getSnapName } from '../../../../helpers/utils/util'; +import SnapPermissionsList from '../../../../components/app/flask/snap-permissions-list'; export default function SnapInstall({ request, @@ -87,12 +87,16 @@ export default function SnapInstall({ className="headers" alignItems={AlignItems.center} flexDirection={FLEX_DIRECTION.COLUMN} - paddingLeft={4} - paddingRight={4} > - + + + {!hasError && ( - + {t('snapInstall')} )} @@ -114,6 +118,8 @@ export default function SnapInstall({ {t('snapInstallRequestsPermission', [ @@ -121,7 +127,7 @@ export default function SnapInstall({ {snapName}, ])} - diff --git a/ui/pages/settings/flask/view-snap/view-snap.js b/ui/pages/settings/flask/view-snap/view-snap.js index 3261652d9..a9ab3a096 100644 --- a/ui/pages/settings/flask/view-snap/view-snap.js +++ b/ui/pages/settings/flask/view-snap/view-snap.js @@ -19,7 +19,6 @@ import SnapAuthorship from '../../../../components/app/flask/snap-authorship'; import Box from '../../../../components/ui/box'; import SnapRemoveWarning from '../../../../components/app/flask/snap-remove-warning'; import ToggleButton from '../../../../components/ui/toggle-button'; -import PermissionsConnectPermissionList from '../../../../components/app/permissions-connect-permission-list/permissions-connect-permission-list'; import ConnectedSitesList from '../../../../components/app/connected-sites-list'; import Tooltip from '../../../../components/ui/tooltip'; import { SNAPS_LIST_ROUTE } from '../../../../helpers/constants/routes'; @@ -38,6 +37,7 @@ import { getTargetSubjectMetadata, } from '../../../../selectors'; import { formatDate } from '../../../../helpers/utils/util'; +import SnapPermissionsList from '../../../../components/app/flask/snap-permissions-list'; function ViewSnap() { const t = useI18nContext(); @@ -182,7 +182,7 @@ function ViewSnap() { {t('snapAccess', [snap.manifest.proposedName])} -