1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 03:12:42 +02:00

[FLASK] Add tooltips to show info about a permission (#17685)

* Add tooltips to show info about a permission

* Make Tooltip component in scope regardless of build type

* Add descriptions

* Add translation variables and fix tooltip

* Use new icons

* Add missing description after rebase

* Fix build issues

* Fix icons

* Fix translation

* Add missing description

* Fix Ethereum icon

* Update coverage
This commit is contained in:
Maarten Zuidhoorn 2023-03-20 12:32:03 +01:00 committed by GitHub
parent c940f744a5
commit c59e2b4d44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 331 additions and 71 deletions

View File

@ -2773,22 +2773,42 @@
"message": "Access the internet.",
"description": "The description of the `endowment:network-access` permission."
},
"permission_accessNetworkDescription": {
"message": "Allow the snap to access the internet. This can be used to both send and receive data with third-party servers.",
"description": "An extended description of the `endowment:network-access` permission."
},
"permission_accessSnap": {
"message": "Connect to the $1 snap.",
"description": "The description for the `wallet_snap` permission. $1 is the name of the snap."
},
"permission_accessSnapDescription": {
"message": "Allow the website or snap to interact with $1.",
"description": "The description for the `wallet_snap_*` permission. $1 is the name of the Snap."
},
"permission_cronjob": {
"message": "Schedule and execute periodic actions.",
"description": "The description for the `snap_cronjob` permission"
},
"permission_cronjobDescription": {
"message": "Allow the snap to perform actions that run periodically at fixed times, dates, or intervals. This can be used to trigger time-sensitive interactions or notifications.",
"description": "An extended description for the `snap_cronjob` permission"
},
"permission_customConfirmation": {
"message": "Display a confirmation in MetaMask.",
"description": "The description for the `snap_confirm` permission"
},
"permission_customConfirmationDescription": {
"message": "Allow the snap to display MetaMask popups with custom text, and buttons to approve or reject an action.",
"description": "An extended description for the `snap_confirm` permission"
},
"permission_dialog": {
"message": "Display dialog windows in MetaMask.",
"description": "The description for the `snap_dialog` permission"
},
"permission_dialogDescription": {
"message": "Allow the snap to display MetaMask popups with custom text, input field, and buttons to approve or reject an action.\nCan be used to create e.g. alerts, confirmations, and opt-in flows for a snap.",
"description": "An extended description for the `snap_dialog` permission"
},
"permission_ethereumAccounts": {
"message": "See address, account balance, activity and suggest transactions to approve",
"description": "The description for the `eth_accounts` permission"
@ -2797,22 +2817,42 @@
"message": "Access the Ethereum provider.",
"description": "The description for the `endowment:ethereum-provider` permission"
},
"permission_ethereumProviderDescription": {
"message": "Allow the snap to communicate with MetaMask directly, in order for it to read data from the blockchain and suggest messages and transactions.",
"description": "An extended description for the `endowment:ethereum-provider` permission"
},
"permission_getEntropy": {
"message": "Derive arbitrary keys unique to this snap.",
"description": "The description for the `snap_getEntropy` permission"
},
"permission_getEntropyDescription": {
"message": "Allow the snap to derive arbitrary keys unique to this snap, without exposing them. These keys are separate from your MetaMask account(s) and not related to your private keys or Secret Recovery Phrase. Other snaps cannot access this information.",
"description": "An extended description for the `snap_getEntropy` permission"
},
"permission_longRunning": {
"message": "Run indefinitely.",
"description": "The description for the `endowment:long-running` permission"
},
"permission_longRunningDescription": {
"message": "Allow the snap to run indefinitely while, for example, processing large amounts of data.",
"description": "An extended description for the `endowment:long-running` permission"
},
"permission_manageBip32Keys": {
"message": "Control your accounts and assets under $1 ($2).",
"description": "The description for the `snap_getBip32Entropy` permission. $1 is a derivation path, e.g. 'm/44'/0'/0''. $2 is the elliptic curve name, e.g. 'secp256k1'."
},
"permission_manageBip32KeysDescription": {
"message": "Allow the snap to derive BIP-32 key pairs based on your Secret Recovery Phrase without exposing it. This grants full access to all accounts and assets on $1.\nWith the power to manage keys, the snap can support a variety of blockchain protocols beyond Ethereum (EVMs).",
"description": "An extended description for the `snap_getBip32Entropy` permission. $1 is a derivation path (name)"
},
"permission_manageBip44Keys": {
"message": "Control your \"$1\" accounts and assets.",
"description": "The description for the `snap_getBip44Entropy` permission. $1 is the name of a protocol, e.g. 'Filecoin'."
},
"permission_manageBip44KeysDescription": {
"message": "Allow the snap to derive BIP-44 key pairs based on your Secret Recovery Phrase without exposing it. This grants full access to all accounts and assets on $1.\nWith the power to manage keys, the snap can support a variety of blockchain protocols beyond Ethereum (EVMs).",
"description": "An extended description for the `snap_getBip44Entropy` permission. $1 is the name of a protocol, e.g., 'Filecoin'."
},
"permission_manageNamedBip32Keys": {
"message": "Control your $1 accounts and assets.",
"description": "The description for the `snap_getBip32Entropy` permission. $1 is a name for the derivation path, e.g., 'Ethereum accounts'. $2 is the plain derivation path, e.g. 'm/44'/0'/0''."
@ -2821,22 +2861,42 @@
"message": "Store and manage its data on your device.",
"description": "The description for the `snap_manageState` permission"
},
"permission_manageStateDescription": {
"message": "Allow the snap to store, update, and retrieve data securely with encryption. Other snaps cannot access this information.",
"description": "An extended description for the `snap_manageState` permission"
},
"permission_notifications": {
"message": "Show notifications.",
"description": "The description for the `snap_notify` permission"
},
"permission_notificationsDescription": {
"message": "Allow the snap to display notifications within MetaMask. A short notification text can be triggered by a snap for actionable or time-sensitive information.",
"description": "An extended description for the `snap_notify` permission"
},
"permission_rpc": {
"message": "Allow $1 to communicate directly with this snap.",
"description": "The description for the `endowment:rpc` permission. $1 is 'other snaps' or 'websites'."
},
"permission_rpcDescription": {
"message": "Allow $1 to send messages to the snap and receive a response from the snap.",
"description": "An extended description for the `endowment:rpc` permission. $1 is 'other snaps' or 'websites'."
},
"permission_transactionInsight": {
"message": "Fetch and display transaction insights.",
"description": "The description for the `endowment:transaction-insight` permission"
},
"permission_transactionInsightDescription": {
"message": "Allow the snap to decode transactions and show insights within the MetaMask UI. This can be used for anti-phishing and security solutions.",
"description": "An extended description for the `endowment:transaction-insight` permission"
},
"permission_transactionInsightOrigin": {
"message": "See the origins of websites that suggest transactions",
"description": "The description for the `transactionOrigin` caveat, to be used with the `endowment:transaction-insight` permission"
},
"permission_transactionInsightOriginDescription": {
"message": "Allow the snap to see the origin (URI) of websites that suggest transactions. This can be used for anti-phishing and security solutions.",
"description": "An extended description for the `transactionOrigin` caveat, to be used with the `endowment:transaction-insight` permission"
},
"permission_unknown": {
"message": "Unknown permission: $1",
"description": "$1 is the name of a requested permission that is not recognized."
@ -2845,6 +2905,10 @@
"message": "View your public key for $1 ($2).",
"description": "The description for the `snap_getBip32PublicKey` permission. $1 is a derivation path, e.g. 'm/44'/0'/0''. $2 is the elliptic curve name, e.g. 'secp256k1'."
},
"permission_viewBip32PublicKeysDescription": {
"message": "Allow the snap to view your public keys (and addresses) for $1. This does not grant any control of accounts or assets.",
"description": "An extended description for the `snap_getBip32PublicKey` permission. $1 is a derivation path (name)"
},
"permission_viewNamedBip32PublicKeys": {
"message": "View your public key for $1.",
"description": "The description for the `snap_getBip32PublicKey` permission. $1 is a name for the derivation path, e.g., 'Ethereum accounts'."
@ -2853,6 +2917,10 @@
"message": "Support for WebAssembly.",
"description": "The description of the `endowment:webassembly` permission."
},
"permission_webAssemblyDescription": {
"message": "Allow the snap to access low-level execution environments via WebAssembly.",
"description": "An extended description of the `endowment:webassembly` permission."
},
"permissions": {
"message": "Permissions"
},

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 417 417">
<path d="m80.5 212.3 64 37.8 64 37.9 127.9-75.7L208.5 0l-128 212.3z"/><path d="m336.5 236.6-128 75.6-128-75.6 128 180.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 196 B

View File

@ -9,7 +9,7 @@ module.exports = {
lines: 65,
branches: 53.5,
statements: 64,
functions: 57.5,
functions: 57.4,
},
transforms: {
branches: 100,

View File

@ -44,4 +44,19 @@
.permission-description-subtext {
@include H7;
}
.permission {
&__tooltip-icon {
margin-left: auto !important;
padding-left: 16px;
i {
color: var(--color-icon-muted);
}
&__warning i {
color: var(--color-warning-default);
}
}
}
}

View File

@ -1,7 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { isFunction } from 'lodash';
import { getWeightedPermissions } from '../../../../helpers/utils/permission';
import {
getRightIcon,
getWeightedPermissions,
} from '../../../../helpers/utils/permission';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { formatDate } from '../../../../helpers/utils/util';
import Typography from '../../../ui/typography/typography';
@ -15,8 +18,9 @@ export default function UpdateSnapPermissionList({
const t = useI18nContext();
const Permissions = ({ className, permissions, subText }) => {
return getWeightedPermissions(t, permissions).map(
({ label, rightIcon, permissionName, permissionValue }) => (
return getWeightedPermissions(t, permissions).map((permission) => {
const { label, permissionName, permissionValue } = permission;
return (
<div className={className} key={permissionName}>
<i className="fas fa-x" />
<div className="permission-description">
@ -31,10 +35,10 @@ export default function UpdateSnapPermissionList({
: subText}
</Typography>
</div>
{rightIcon && <i className={rightIcon} />}
{getRightIcon(permission)}
</div>
),
);
);
});
};
return (

View File

@ -6,7 +6,7 @@
width: 100%;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-border-default);
border-bottom: 1px solid var(--color-border-muted);
display: flex;
flex-direction: row;
align-items: center;
@ -21,9 +21,30 @@
font-size: 1rem;
text-align: center;
}
.mm-avatar-icon {
margin: 16px 16px 16px 0;
}
&__tooltip-icon {
margin-left: auto !important;
padding-left: 16px;
i {
color: var(--color-icon-muted);
}
&__warning i {
color: var(--color-warning-default);
}
}
}
.permission-label-item {
font-weight: bold;
}
}
.tooltip-label-item {
font-weight: bold;
}

View File

@ -1,28 +1,26 @@
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';
/**
* Get one or more permission descriptions for a permission name.
*
* @param permission - The permission to render.
* @param permission.label - The text label.
* @param permission.leftIcon - The left icon.
* @param permission.rightIcon - The right icon.
* @param permission.permissionName - The name of the permission.
* @param index - The index of the permission in the permissions array.
* @returns {JSX.Element[]} An array of permission description nodes.
* @param index - The index of the permission.
* @returns {JSX.Element} A permission description node.
*/
function getDescriptionNode(
{ label, leftIcon, rightIcon, permissionName },
index,
) {
function getDescriptionNode(permission, index) {
const { label, leftIcon, permissionName } = permission;
return (
<div className="permission" key={`${permissionName}-${index}`}>
{typeof leftIcon === 'string' ? <i className={leftIcon} /> : leftIcon}
{label}
{rightIcon && <i className={rightIcon} />}
{getRightIcon(permission)}
</div>
);
}

View File

@ -6,15 +6,24 @@ import { getRpcCaveatOrigins } from '@metamask/snaps-controllers/dist/snaps/endo
import { SnapCaveatType } from '@metamask/snaps-utils';
import { isNonEmptyArray } from '@metamask/controller-utils';
///: END:ONLY_INCLUDE_IN
import classnames from 'classnames';
import {
RestrictedMethods,
///: BEGIN:ONLY_INCLUDE_IN(flask)
EndowmentPermissions,
///: END:ONLY_INCLUDE_IN
} from '../../../shared/constants/permissions';
import { Icon, ICON_NAMES } from '../../components/component-library';
import { Color } from '../constants/design-system';
import Tooltip from '../../components/ui/tooltip';
import {
AvatarIcon,
///: BEGIN:ONLY_INCLUDE_IN(flask)
Icon,
///: END:ONLY_INCLUDE_IN
ICON_NAMES,
ICON_SIZES,
} from '../../components/component-library';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { IconColor } from '../constants/design-system';
import {
coinTypeToProtocolName,
getSnapDerivationPathName,
@ -24,45 +33,70 @@ import {
const UNKNOWN_PERMISSION = Symbol('unknown');
///: BEGIN:ONLY_INCLUDE_IN(flask)
const RIGHT_WARNING_ICON = (
<Icon
name={ICON_NAMES.DANGER}
size={ICON_SIZES.SM}
color={IconColor.warningDefault}
/>
);
const RIGHT_INFO_ICON = (
<Icon
name={ICON_NAMES.INFO}
size={ICON_SIZES.SM}
color={IconColor.iconMuted}
/>
);
///: END:ONLY_INCLUDE_IN
function getLeftIcon(iconName) {
return (
<AvatarIcon
iconName={iconName}
size={ICON_SIZES.SM}
iconProps={{
size: ICON_SIZES.XS,
}}
/>
);
}
const PERMISSION_DESCRIPTIONS = deepFreeze({
[RestrictedMethods.eth_accounts]: (t) => ({
label: t('permission_ethereumAccounts'),
leftIcon: (
<Icon name={ICON_NAMES.EYE} margin={4} color={Color.iconAlternative} />
),
leftIcon: getLeftIcon(ICON_NAMES.EYE),
rightIcon: null,
weight: 2,
}),
///: BEGIN:ONLY_INCLUDE_IN(flask)
[RestrictedMethods.snap_confirm]: (t) => ({
label: t('permission_customConfirmation'),
leftIcon: 'fas fa-user-check',
rightIcon: null,
description: t('permission_customConfirmationDescription'),
leftIcon: getLeftIcon(ICON_NAMES.SECURITY_TICK),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
}),
[RestrictedMethods.snap_dialog]: (t) => ({
label: t('permission_dialog'),
leftIcon: 'fas fa-user-check',
rightIcon: null,
description: t('permission_dialogDescription'),
leftIcon: getLeftIcon(ICON_NAMES.MESSAGES),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
}),
[RestrictedMethods.snap_notify]: (t) => ({
leftIcon: <Icon name={ICON_NAMES.NOTIFICATION} />,
label: t('permission_notifications'),
rightIcon: null,
description: t('permission_notificationsDescription'),
leftIcon: getLeftIcon(ICON_NAMES.NOTIFICATION),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
}),
[RestrictedMethods.snap_getBip32PublicKey]: (t, _, permissionValue) =>
permissionValue.caveats[0].value.map(({ path, curve }) => {
const baseDescription = {
leftIcon: (
<Icon
name={ICON_NAMES.EYE}
margin={4}
color={Color.iconAlternative}
/>
),
rightIcon: null,
leftIcon: getLeftIcon(ICON_NAMES.SECURITY_SEARCH),
rightIcon: RIGHT_WARNING_ICON,
weight: 1,
};
@ -76,6 +110,15 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
</span>,
path.join('/'),
]),
description: t('permission_viewBip32PublicKeysDescription', [
<span
className="tooltip-label-item"
key={`description-${path.join('/')}`}
>
{friendlyName}
</span>,
path.join('/'),
]),
};
}
@ -87,13 +130,22 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
</span>,
curve,
]),
description: t('permission_viewBip32PublicKeysDescription', [
<span
className="tooltip-label-item"
key={`description-${path.join('/')}`}
>
{path.join('/')}
</span>,
path.join('/'),
]),
};
}),
[RestrictedMethods.snap_getBip32Entropy]: (t, _, permissionValue) =>
permissionValue.caveats[0].value.map(({ path, curve }) => {
const baseDescription = {
leftIcon: 'fas fa-door-open',
rightIcon: null,
leftIcon: getLeftIcon(ICON_NAMES.KEY),
rightIcon: RIGHT_WARNING_ICON,
weight: 1,
};
@ -107,6 +159,15 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
</span>,
path.join('/'),
]),
description: t('permission_manageBip32KeysDescription', [
<span
className="tooltip-label-item"
key={`description-${path.join('/')}`}
>
{friendlyName}
</span>,
curve,
]),
};
}
@ -118,6 +179,15 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
</span>,
curve,
]),
description: t('permission_manageBip32KeysDescription', [
<span
className="tooltip-label-item"
key={`description-${path.join('/')}`}
>
{path.join('/')}
</span>,
curve,
]),
};
}),
[RestrictedMethods.snap_getBip44Entropy]: (t, _, permissionValue) =>
@ -125,31 +195,43 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
label: t('permission_manageBip44Keys', [
<span className="permission-label-item" key={`coin-type-${coinType}`}>
{coinTypeToProtocolName(coinType) ||
`${coinType} (Unrecognized protocol)`}
t('unrecognizedProtocol', [coinType])}
</span>,
]),
leftIcon: 'fas fa-door-open',
rightIcon: null,
description: t('permission_manageBip44KeysDescription', [
<span
className="tooltip-label-item"
key={`description-coin-type-${coinType}`}
>
{coinTypeToProtocolName(coinType) ||
t('unrecognizedProtocol', [coinType])}
</span>,
]),
leftIcon: getLeftIcon(ICON_NAMES.KEY),
rightIcon: RIGHT_WARNING_ICON,
weight: 1,
})),
[RestrictedMethods.snap_getEntropy]: (t) => ({
label: t('permission_getEntropy'),
leftIcon: 'fas fa-key',
rightIcon: null,
description: t('permission_getEntropyDescription'),
leftIcon: getLeftIcon(ICON_NAMES.SECURITY_KEY),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
}),
[RestrictedMethods.snap_manageState]: (t) => ({
label: t('permission_manageState'),
leftIcon: 'fas fa-download',
rightIcon: null,
description: t('permission_manageStateDescription'),
leftIcon: getLeftIcon(ICON_NAMES.ADD_SQUARE),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
}),
[RestrictedMethods.wallet_snap]: (t, _, permissionValue) => {
const snaps = permissionValue.caveats[0].value;
const baseDescription = {
leftIcon: 'fas fa-bolt',
rightIcon: null,
leftIcon: getLeftIcon(ICON_NAMES.FLASH),
rightIcon: RIGHT_INFO_ICON,
};
return Object.keys(snaps).map((snapId) => {
const friendlyName = getSnapName(snapId);
if (friendlyName) {
@ -160,30 +242,36 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
{friendlyName}
</span>,
]),
description: t('permission_accessSnapDescription', [friendlyName]),
};
}
return {
...baseDescription,
label: t('permission_accessSnap', [snapId]),
description: t('permission_accessSnapDescription', [snapId]),
};
});
},
[EndowmentPermissions['endowment:network-access']]: (t) => ({
label: t('permission_accessNetwork'),
leftIcon: 'fas fa-wifi',
rightIcon: null,
description: t('permission_accessNetworkDescription'),
leftIcon: getLeftIcon(ICON_NAMES.GLOBAL),
rightIcon: RIGHT_INFO_ICON,
weight: 2,
}),
[EndowmentPermissions['endowment:webassembly']]: (t) => ({
label: t('permission_webAssembly'),
description: t('permission_webAssemblyDescription'),
leftIcon: 'fas fa-microchip',
rightIcon: null,
weight: 2,
}),
[EndowmentPermissions['endowment:long-running']]: (t) => ({
label: t('permission_longRunning'),
leftIcon: 'fas fa-infinity',
rightIcon: null,
description: t('permission_longRunningDescription'),
leftIcon: getLeftIcon(ICON_NAMES.LINK),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
}),
[EndowmentPermissions['endowment:transaction-insight']]: (
@ -192,14 +280,16 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
permissionValue,
) => {
const baseDescription = {
leftIcon: 'fas fa-info',
rightIcon: null,
leftIcon: getLeftIcon(ICON_NAMES.SPEEDOMETER),
rightIcon: RIGHT_INFO_ICON,
weight: 3,
};
const result = [
{
...baseDescription,
label: t('permission_transactionInsight'),
description: t('permission_transactionInsightDescription'),
},
];
@ -211,7 +301,8 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
result.push({
...baseDescription,
label: t('permission_transactionInsightOrigin'),
leftIcon: 'fas fa-compass',
description: t('permission_transactionInsightOriginDescription'),
leftIcon: getLeftIcon(ICON_NAMES.EXPLORE),
});
}
@ -219,39 +310,50 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
},
[EndowmentPermissions['endowment:cronjob']]: (t) => ({
label: t('permission_cronjob'),
leftIcon: 'fas fa-clock',
rightIcon: null,
description: t('permission_cronjobDescription'),
leftIcon: getLeftIcon(ICON_NAMES.CLOCK),
rightIcon: RIGHT_INFO_ICON,
weight: 2,
}),
[EndowmentPermissions['endowment:ethereum-provider']]: (t) => ({
label: t('permission_ethereumProvider'),
leftIcon: 'fab fa-ethereum',
rightIcon: null,
description: t('permission_ethereumProviderDescription'),
leftIcon: getLeftIcon(ICON_NAMES.ETHEREUM),
rightIcon: RIGHT_INFO_ICON,
weight: 1,
}),
[EndowmentPermissions['endowment:rpc']]: (t, _, permissionValue) => {
const baseDescription = {
leftIcon: getLeftIcon(ICON_NAMES.HIERARCHY),
rightIcon: RIGHT_INFO_ICON,
weight: 2,
};
const { snaps, dapps } = getRpcCaveatOrigins(permissionValue);
const labels = [];
const results = [];
if (snaps) {
labels.push(t('permission_rpc', [t('otherSnaps')]));
results.push({
...baseDescription,
label: t('permission_rpc', [t('otherSnaps')]),
description: t('permission_rpcDescription', [t('otherSnaps')]),
});
}
if (dapps) {
labels.push(t('permission_rpc', [t('websites')]));
results.push({
...baseDescription,
label: t('permission_rpc', [t('websites')]),
description: t('permission_rpcDescription', [t('websites')]),
});
}
return labels.map((label) => ({
label,
leftIcon: 'fas fa-plug',
rightIcon: null,
weight: 2,
}));
return results;
},
///: END:ONLY_INCLUDE_IN
[UNKNOWN_PERMISSION]: (t, permissionName) => ({
label: t('permission_unknown', [permissionName ?? 'undefined']),
leftIcon: 'fas fa-times-circle',
leftIcon: getLeftIcon(ICON_NAMES.QUESTION),
rightIcon: null,
weight: 4,
}),
@ -260,6 +362,8 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({
/**
* @typedef {object} PermissionLabelObject
* @property {string} label - The text label.
* @property {string} [description] - An optional description, shown when the
* `rightIcon` is hovered.
* @property {string} leftIcon - The left icon.
* @property {string} rightIcon - The right icon.
* @property {number} weight - The weight of the permission.
@ -315,3 +419,50 @@ export function getWeightedPermissions(t, permissions) {
)
.sort((left, right) => left.weight - right.weight);
}
/**
* Get the right icon for a permission. If a description is provided, the icon
* will be wrapped in a tooltip. Otherwise, the icon will be rendered as-is. If
* there's no right icon, this function will return null.
*
* If the weight is 1, the icon will be rendered with a warning color.
*
* @param {PermissionLabelObject} permission - The permission object.
* @param {JSX.Element | string} permission.rightIcon - The right icon.
* @param {string} permission.description - The description.
* @param {number} permission.weight - The weight.
* @returns {JSX.Element | null} The right icon, or null if there's no
* right icon.
*/
export function getRightIcon({ rightIcon, description, weight }) {
if (rightIcon && description) {
return (
<Tooltip
wrapperClassName={classnames(
'permission__tooltip-icon',
weight === 1 && 'permission__tooltip-icon__warning',
)}
html={<div>{description}</div>}
position="bottom"
>
{typeof rightIcon === 'string' ? (
<i className={rightIcon} />
) : (
rightIcon
)}
</Tooltip>
);
}
if (rightIcon) {
if (typeof rightIcon === 'string') {
return (
<i className={classnames(rightIcon, 'permission__tooltip-icon')} />
);
}
return rightIcon;
}
return null;
}