mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	refactor: improve icon labels and tooltips (#884)
This commit is contained in:
		
							parent
							
								
									8a3db090d5
								
							
						
					
					
						commit
						2e5e25bfe5
					
				@ -113,29 +113,30 @@ export const ConfiguredAddons = ({ getAddonIcon }: IConfigureAddonsProps) => {
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    permission={UPDATE_ADDON}
 | 
			
		||||
                    onClick={() => toggleAddon(addon)}
 | 
			
		||||
                    tooltip="Toggle addon"
 | 
			
		||||
                >
 | 
			
		||||
                    <ConditionallyRender
 | 
			
		||||
                        condition={addon.enabled}
 | 
			
		||||
                        show={<Visibility titleAccess="Disable addon" />}
 | 
			
		||||
                        elseShow={<VisibilityOff titleAccess="Enable addon" />}
 | 
			
		||||
                        show={<Visibility />}
 | 
			
		||||
                        elseShow={<VisibilityOff />}
 | 
			
		||||
                    />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    permission={UPDATE_ADDON}
 | 
			
		||||
                    onClick={() => {
 | 
			
		||||
                        history.push(`/addons/edit/${addon.id}`);
 | 
			
		||||
                    }}
 | 
			
		||||
                    tooltip="Edit Addon"
 | 
			
		||||
                    onClick={() => history.push(`/addons/edit/${addon.id}`)}
 | 
			
		||||
                >
 | 
			
		||||
                    <Edit titleAccess="Edit Addon" />
 | 
			
		||||
                    <Edit />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    permission={DELETE_ADDON}
 | 
			
		||||
                    tooltip="Remove Addon"
 | 
			
		||||
                    onClick={() => {
 | 
			
		||||
                        setDeletedAddon(addon);
 | 
			
		||||
                        setShowDelete(true);
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                    <Delete titleAccess="Remove Addon" />
 | 
			
		||||
                    <Delete />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
            </ListItemSecondaryAction>
 | 
			
		||||
        </ListItem>
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import {
 | 
			
		||||
    TableCell,
 | 
			
		||||
    TableHead,
 | 
			
		||||
    TableRow,
 | 
			
		||||
    Tooltip,
 | 
			
		||||
} from '@material-ui/core';
 | 
			
		||||
import AccessContext from 'contexts/AccessContext';
 | 
			
		||||
import useToast from 'hooks/useToast';
 | 
			
		||||
@ -200,6 +201,7 @@ export const ApiTokenList = () => {
 | 
			
		||||
                                    <Secret value={item.secret} />
 | 
			
		||||
                                </TableCell>
 | 
			
		||||
                                <TableCell className={styles.actionsContainer}>
 | 
			
		||||
                                    <Tooltip title="Copy token">
 | 
			
		||||
                                        <IconButton
 | 
			
		||||
                                            onClick={() => {
 | 
			
		||||
                                                copyToken(item.secret);
 | 
			
		||||
@ -207,9 +209,11 @@ export const ApiTokenList = () => {
 | 
			
		||||
                                        >
 | 
			
		||||
                                            <FileCopy />
 | 
			
		||||
                                        </IconButton>
 | 
			
		||||
                                    </Tooltip>
 | 
			
		||||
                                    <ConditionallyRender
 | 
			
		||||
                                        condition={hasAccess(DELETE_API_TOKEN)}
 | 
			
		||||
                                        show={
 | 
			
		||||
                                            <Tooltip title="Delete token">
 | 
			
		||||
                                                <IconButton
 | 
			
		||||
                                                    onClick={() => {
 | 
			
		||||
                                                        setDeleteToken(item);
 | 
			
		||||
@ -218,6 +222,7 @@ export const ApiTokenList = () => {
 | 
			
		||||
                                                >
 | 
			
		||||
                                                    <Delete />
 | 
			
		||||
                                                </IconButton>
 | 
			
		||||
                                            </Tooltip>
 | 
			
		||||
                                        }
 | 
			
		||||
                                    />
 | 
			
		||||
                                </TableCell>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IconButton } from '@material-ui/core';
 | 
			
		||||
import { IconButton, Tooltip } from '@material-ui/core';
 | 
			
		||||
import CopyIcon from '@material-ui/icons/FileCopy';
 | 
			
		||||
import copy from 'copy-to-clipboard';
 | 
			
		||||
import useToast from 'hooks/useToast';
 | 
			
		||||
@ -14,8 +14,7 @@ export const UserToken = ({ token }: IUserTokenProps) => {
 | 
			
		||||
        if (copy(token)) {
 | 
			
		||||
            setToastData({
 | 
			
		||||
                type: 'success',
 | 
			
		||||
                title: 'Token copied',
 | 
			
		||||
                text: `Token is copied to clipboard`,
 | 
			
		||||
                title: 'Token copied to clipboard',
 | 
			
		||||
            });
 | 
			
		||||
        } else
 | 
			
		||||
            setToastData({
 | 
			
		||||
@ -38,9 +37,11 @@ export const UserToken = ({ token }: IUserTokenProps) => {
 | 
			
		||||
            }}
 | 
			
		||||
        >
 | 
			
		||||
            {token}
 | 
			
		||||
            <Tooltip title="Copy token">
 | 
			
		||||
                <IconButton onClick={copyToken}>
 | 
			
		||||
                    <CopyIcon />
 | 
			
		||||
                </IconButton>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -121,7 +121,12 @@ const EnvironmentPermissionAccordion = ({
 | 
			
		||||
            <Accordion style={{ boxShadow: 'none' }}>
 | 
			
		||||
                <AccordionSummary
 | 
			
		||||
                    className={styles.accordionSummary}
 | 
			
		||||
                    expandIcon={<ExpandMore className={styles.icon} />}
 | 
			
		||||
                    expandIcon={
 | 
			
		||||
                        <ExpandMore
 | 
			
		||||
                            className={styles.icon}
 | 
			
		||||
                            titleAccess="Toggle"
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                >
 | 
			
		||||
                    <div className={styles.accordionHeader}>
 | 
			
		||||
                        <StringTruncator
 | 
			
		||||
 | 
			
		||||
@ -50,18 +50,17 @@ const RoleListItem = ({
 | 
			
		||||
                <TableCell align="right">
 | 
			
		||||
                    <PermissionIconButton
 | 
			
		||||
                        data-loading
 | 
			
		||||
                        aria-label="Edit"
 | 
			
		||||
                        disabled={type === BUILTIN_ROLE_TYPE}
 | 
			
		||||
                        onClick={() => {
 | 
			
		||||
                            history.push(`/admin/roles/${id}/edit`);
 | 
			
		||||
                        }}
 | 
			
		||||
                        permission={ADMIN}
 | 
			
		||||
                        tooltip="Edit role"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Edit />
 | 
			
		||||
                    </PermissionIconButton>
 | 
			
		||||
                    <PermissionIconButton
 | 
			
		||||
                        data-loading
 | 
			
		||||
                        aria-label="Remove role"
 | 
			
		||||
                        disabled={type === BUILTIN_ROLE_TYPE}
 | 
			
		||||
                        onClick={() => {
 | 
			
		||||
                            // @ts-expect-error
 | 
			
		||||
@ -69,6 +68,7 @@ const RoleListItem = ({
 | 
			
		||||
                            setDelDialog(true);
 | 
			
		||||
                        }}
 | 
			
		||||
                        permission={ADMIN}
 | 
			
		||||
                        tooltip="Remove role"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Delete />
 | 
			
		||||
                    </PermissionIconButton>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IconButton } from '@material-ui/core';
 | 
			
		||||
import { IconButton, Tooltip } from '@material-ui/core';
 | 
			
		||||
import CopyIcon from '@material-ui/icons/FileCopy';
 | 
			
		||||
import useToast from 'hooks/useToast';
 | 
			
		||||
 | 
			
		||||
@ -47,9 +47,11 @@ const UserInviteLink = ({ inviteLink }: IInviteLinkProps) => {
 | 
			
		||||
            }}
 | 
			
		||||
        >
 | 
			
		||||
            {inviteLink}
 | 
			
		||||
            <Tooltip title="Copy link">
 | 
			
		||||
                <IconButton onClick={handleCopy}>
 | 
			
		||||
                    <CopyIcon />
 | 
			
		||||
                </IconButton>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -87,7 +87,6 @@ const UserListItem = ({
 | 
			
		||||
                        <Tooltip title="Edit user" arrow>
 | 
			
		||||
                            <IconButton
 | 
			
		||||
                                data-loading
 | 
			
		||||
                                aria-label="Edit user"
 | 
			
		||||
                                onClick={() =>
 | 
			
		||||
                                    history.push(`/admin/users/${user.id}/edit`)
 | 
			
		||||
                                }
 | 
			
		||||
@ -98,7 +97,6 @@ const UserListItem = ({
 | 
			
		||||
                        <Tooltip title="Change password" arrow>
 | 
			
		||||
                            <IconButton
 | 
			
		||||
                                data-loading
 | 
			
		||||
                                aria-label="Change password"
 | 
			
		||||
                                onClick={openPwDialog(user)}
 | 
			
		||||
                            >
 | 
			
		||||
                                <Lock />
 | 
			
		||||
@ -107,7 +105,6 @@ const UserListItem = ({
 | 
			
		||||
                        <Tooltip title="Remove user" arrow>
 | 
			
		||||
                            <IconButton
 | 
			
		||||
                                data-loading
 | 
			
		||||
                                aria-label="Remove user"
 | 
			
		||||
                                onClick={openDelDialog(user)}
 | 
			
		||||
                            >
 | 
			
		||||
                                <Delete />
 | 
			
		||||
 | 
			
		||||
@ -112,13 +112,13 @@ export const ApplicationEdit = () => {
 | 
			
		||||
                                condition={Boolean(url)}
 | 
			
		||||
                                show={
 | 
			
		||||
                                    <IconButton component={Link} href={url}>
 | 
			
		||||
                                        <LinkIcon />
 | 
			
		||||
                                        <LinkIcon titleAccess={url} />
 | 
			
		||||
                                    </IconButton>
 | 
			
		||||
                                }
 | 
			
		||||
                            />
 | 
			
		||||
 | 
			
		||||
                            <PermissionButton
 | 
			
		||||
                                title="Delete application"
 | 
			
		||||
                                tooltip="Delete application"
 | 
			
		||||
                                onClick={toggleModal}
 | 
			
		||||
                                permission={UPDATE_APPLICATION}
 | 
			
		||||
                            >
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ export const ApplicationList = () => {
 | 
			
		||||
    const renderNoApplications = () => (
 | 
			
		||||
        <>
 | 
			
		||||
            <section style={{ textAlign: 'center' }}>
 | 
			
		||||
                <Warning /> <br />
 | 
			
		||||
                <Warning titleAccess="Warning" /> <br />
 | 
			
		||||
                <br />
 | 
			
		||||
                Oh snap, it does not seem like you have connected any
 | 
			
		||||
                applications. To connect your application to Unleash you will
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ export const AutocompleteBox = ({
 | 
			
		||||
                classes={{ inputRoot: styles.inputRoot }}
 | 
			
		||||
                options={options}
 | 
			
		||||
                value={value}
 | 
			
		||||
                popupIcon={<ArrowDropDown />}
 | 
			
		||||
                popupIcon={<ArrowDropDown titleAccess="Toggle" />}
 | 
			
		||||
                onChange={(event, value) => onChange(value || [])}
 | 
			
		||||
                renderInput={renderInput}
 | 
			
		||||
                getOptionLabel={value => value.label}
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ const CheckMarkBadge = ({ type, className }: ICheckMarkBadgeProps) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <div className={classnames(styles.badge, className)}>
 | 
			
		||||
            {type === 'error' ? (
 | 
			
		||||
                <Close className={styles.check} />
 | 
			
		||||
                <Close className={styles.check} titleAccess="Error" />
 | 
			
		||||
            ) : (
 | 
			
		||||
                <Check className={styles.check} />
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import {
 | 
			
		||||
    operatorsForContext,
 | 
			
		||||
    CURRENT_TIME_CONTEXT_FIELD,
 | 
			
		||||
} from 'utils/operatorsForContext';
 | 
			
		||||
import { Tooltip } from '@material-ui/core';
 | 
			
		||||
 | 
			
		||||
interface IConstraintAccordionViewHeader {
 | 
			
		||||
    localConstraint: IConstraint;
 | 
			
		||||
@ -121,6 +122,7 @@ export const ConstraintAccordionEditHeader = ({
 | 
			
		||||
                show={<p className={styles.editingBadge}>Updating...</p>}
 | 
			
		||||
                elseShow={<p className={styles.editingBadge}>Editing</p>}
 | 
			
		||||
            />
 | 
			
		||||
            <Tooltip title="Help">
 | 
			
		||||
                <a
 | 
			
		||||
                    href="https://docs.getunleash.io/advanced/strategy_constraints"
 | 
			
		||||
                    style={{ marginLeft: 'auto' }}
 | 
			
		||||
@ -129,6 +131,7 @@ export const ConstraintAccordionEditHeader = ({
 | 
			
		||||
                >
 | 
			
		||||
                    <Help className={styles.help} />
 | 
			
		||||
                </a>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ export const ConstraintAccordionView = ({
 | 
			
		||||
        >
 | 
			
		||||
            <AccordionSummary
 | 
			
		||||
                className={styles.summary}
 | 
			
		||||
                expandIcon={<ExpandMore />}
 | 
			
		||||
                expandIcon={<ExpandMore titleAccess="Toggle" />}
 | 
			
		||||
            >
 | 
			
		||||
                <ConstraintAccordionViewHeader
 | 
			
		||||
                    compact={compact}
 | 
			
		||||
 | 
			
		||||
@ -104,8 +104,9 @@ export const ConstraintAccordionViewHeader = ({
 | 
			
		||||
                            projectId={projectId}
 | 
			
		||||
                            environmentId={environmentId}
 | 
			
		||||
                            hidden={!onEdit}
 | 
			
		||||
                            tooltip="Edit constraint"
 | 
			
		||||
                        >
 | 
			
		||||
                            <Edit titleAccess="edit constraint" />
 | 
			
		||||
                            <Edit />
 | 
			
		||||
                        </PermissionIconButton>
 | 
			
		||||
                    }
 | 
			
		||||
                />
 | 
			
		||||
@ -116,9 +117,10 @@ export const ConstraintAccordionViewHeader = ({
 | 
			
		||||
                            onClick={onDeleteClick!}
 | 
			
		||||
                            permission={UPDATE_FEATURE_STRATEGY}
 | 
			
		||||
                            projectId={projectId}
 | 
			
		||||
                            tooltip="Delete constraint"
 | 
			
		||||
                            environmentId={environmentId}
 | 
			
		||||
                        >
 | 
			
		||||
                            <Delete titleAccess="delete constraint" />
 | 
			
		||||
                            <Delete />
 | 
			
		||||
                        </PermissionIconButton>
 | 
			
		||||
                    }
 | 
			
		||||
                />
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ export const ConstraintIcon = () => {
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div className={styles.constraintIconContainer}>
 | 
			
		||||
            <TrackChanges className={styles.constraintIcon} aria-hidden />
 | 
			
		||||
            <TrackChanges className={styles.constraintIcon} />
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ const DropdownMenu = ({
 | 
			
		||||
    id,
 | 
			
		||||
    title,
 | 
			
		||||
    callback,
 | 
			
		||||
    icon = <ArrowDropDown />,
 | 
			
		||||
    icon = <ArrowDropDown titleAccess="Toggle" />,
 | 
			
		||||
    label,
 | 
			
		||||
    style,
 | 
			
		||||
    startIcon,
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,8 @@ interface IEnvironmentIcon {
 | 
			
		||||
const EnvironmentIcon = ({ enabled, className }: IEnvironmentIcon) => {
 | 
			
		||||
    const theme = useTheme();
 | 
			
		||||
 | 
			
		||||
    const title = enabled ? 'Environment enabled' : 'Environment disabled';
 | 
			
		||||
 | 
			
		||||
    const container = {
 | 
			
		||||
        backgroundColor: enabled
 | 
			
		||||
            ? theme.palette.primary.light
 | 
			
		||||
@ -31,7 +33,7 @@ const EnvironmentIcon = ({ enabled, className }: IEnvironmentIcon) => {
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div style={container} className={className}>
 | 
			
		||||
            <Cloud style={icon} />
 | 
			
		||||
            <Cloud style={icon} titleAccess={title} />
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,12 @@
 | 
			
		||||
import { useStyles } from './FormTemplate.styles';
 | 
			
		||||
import MenuBookIcon from '@material-ui/icons/MenuBook';
 | 
			
		||||
import Codebox from '../Codebox/Codebox';
 | 
			
		||||
import { Collapse, IconButton, useMediaQuery } from '@material-ui/core';
 | 
			
		||||
import {
 | 
			
		||||
    Collapse,
 | 
			
		||||
    IconButton,
 | 
			
		||||
    useMediaQuery,
 | 
			
		||||
    Tooltip,
 | 
			
		||||
} from '@material-ui/core';
 | 
			
		||||
import { FileCopy, Info } from '@material-ui/icons';
 | 
			
		||||
import ConditionallyRender from '../ConditionallyRender';
 | 
			
		||||
import Loader from '../Loader/Loader';
 | 
			
		||||
@ -95,9 +100,11 @@ const FormTemplate: React.FC<ICreateProps> = ({
 | 
			
		||||
                    >
 | 
			
		||||
                        <h3 className={styles.subtitle}>
 | 
			
		||||
                            API Command{' '}
 | 
			
		||||
                            <Tooltip title="Copy command">
 | 
			
		||||
                                <IconButton onClick={copyCommand}>
 | 
			
		||||
                                    <FileCopy className={styles.icon} />
 | 
			
		||||
                                </IconButton>
 | 
			
		||||
                            </Tooltip>
 | 
			
		||||
                        </h3>
 | 
			
		||||
                        <Codebox text={formatApiCode()} />
 | 
			
		||||
                    </Guidance>
 | 
			
		||||
@ -126,12 +133,14 @@ const MobileGuidance = ({
 | 
			
		||||
            <div className={styles.mobileGuidanceBgContainer}>
 | 
			
		||||
                <MobileGuidanceBG className={styles.mobileGuidanceBackground} />
 | 
			
		||||
            </div>
 | 
			
		||||
            <Tooltip title="Toggle help">
 | 
			
		||||
                <IconButton
 | 
			
		||||
                    className={styles.mobileGuidanceButton}
 | 
			
		||||
                    onClick={() => setOpen(prev => !prev)}
 | 
			
		||||
                >
 | 
			
		||||
                    <Info className={styles.infoIcon} />
 | 
			
		||||
                </IconButton>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
            <Collapse in={open} timeout={500}>
 | 
			
		||||
                <Guidance
 | 
			
		||||
                    description={description}
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,9 @@ const PasswordField = ({ ...rest }) => {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const IconComponent = showPassword ? Visibility : VisibilityOff;
 | 
			
		||||
    const iconTitle = 'Toggle password visibility';
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <TextField
 | 
			
		||||
            variant="outlined"
 | 
			
		||||
@ -27,11 +30,10 @@ const PasswordField = ({ ...rest }) => {
 | 
			
		||||
                endAdornment: (
 | 
			
		||||
                    <InputAdornment position="end">
 | 
			
		||||
                        <IconButton
 | 
			
		||||
                            aria-label="toggle password visibility"
 | 
			
		||||
                            onClick={handleClickShowPassword}
 | 
			
		||||
                            onMouseDown={handleMouseDownPassword}
 | 
			
		||||
                        >
 | 
			
		||||
                            {showPassword ? <Visibility /> : <VisibilityOff />}
 | 
			
		||||
                            <IconComponent titleAccess={iconTitle} />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                    </InputAdornment>
 | 
			
		||||
                ),
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,19 @@
 | 
			
		||||
import { Button, ButtonProps, Tooltip } from '@material-ui/core';
 | 
			
		||||
import { Button, ButtonProps } from '@material-ui/core';
 | 
			
		||||
import { Lock } from '@material-ui/icons';
 | 
			
		||||
import AccessContext from 'contexts/AccessContext';
 | 
			
		||||
import React, { useContext } from 'react';
 | 
			
		||||
import ConditionallyRender from '../ConditionallyRender';
 | 
			
		||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
 | 
			
		||||
import { formatAccessText } from 'utils/formatAccessText';
 | 
			
		||||
import { useId } from 'hooks/useId';
 | 
			
		||||
 | 
			
		||||
export interface IPermissionButtonProps extends ButtonProps {
 | 
			
		||||
export interface IPermissionButtonProps extends Omit<ButtonProps, 'title'> {
 | 
			
		||||
    permission: string | string[];
 | 
			
		||||
    onClick?: (e: any) => void;
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
    projectId?: string;
 | 
			
		||||
    environmentId?: string;
 | 
			
		||||
    tooltip?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PermissionButton: React.FC<IPermissionButtonProps> = ({
 | 
			
		||||
@ -21,9 +25,11 @@ const PermissionButton: React.FC<IPermissionButtonProps> = ({
 | 
			
		||||
    disabled,
 | 
			
		||||
    projectId,
 | 
			
		||||
    environmentId,
 | 
			
		||||
    tooltip,
 | 
			
		||||
    ...rest
 | 
			
		||||
}) => {
 | 
			
		||||
    const { hasAccess } = useContext(AccessContext);
 | 
			
		||||
    const id = useId();
 | 
			
		||||
    let access;
 | 
			
		||||
 | 
			
		||||
    const handleAccess = () => {
 | 
			
		||||
@ -53,30 +59,27 @@ const PermissionButton: React.FC<IPermissionButtonProps> = ({
 | 
			
		||||
 | 
			
		||||
    access = handleAccess();
 | 
			
		||||
 | 
			
		||||
    const tooltipText = !access
 | 
			
		||||
        ? "You don't have access to perform this operation"
 | 
			
		||||
        : '';
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Tooltip title={tooltipText} arrow>
 | 
			
		||||
            <span>
 | 
			
		||||
        <TooltipResolver title={formatAccessText(access, tooltip)} arrow>
 | 
			
		||||
            <span id={id}>
 | 
			
		||||
                <Button
 | 
			
		||||
                    onClick={onClick}
 | 
			
		||||
                    disabled={disabled || !access}
 | 
			
		||||
                    aria-describedby={id}
 | 
			
		||||
                    variant={variant}
 | 
			
		||||
                    color={color}
 | 
			
		||||
                    {...rest}
 | 
			
		||||
                    endIcon={
 | 
			
		||||
                        <ConditionallyRender
 | 
			
		||||
                            condition={!access}
 | 
			
		||||
                            show={<Lock />}
 | 
			
		||||
                            show={<Lock titleAccess="Locked" />}
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                >
 | 
			
		||||
                    {children}
 | 
			
		||||
                </Button>
 | 
			
		||||
            </span>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
        </TooltipResolver>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,17 @@
 | 
			
		||||
import { IconButton, Tooltip, IconButtonProps } from '@material-ui/core';
 | 
			
		||||
import { IconButton, IconButtonProps } from '@material-ui/core';
 | 
			
		||||
import React, { useContext, ReactNode } from 'react';
 | 
			
		||||
import AccessContext from 'contexts/AccessContext';
 | 
			
		||||
import { Link } from 'react-router-dom';
 | 
			
		||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
 | 
			
		||||
import { formatAccessText } from 'utils/formatAccessText';
 | 
			
		||||
import { useId } from 'hooks/useId';
 | 
			
		||||
 | 
			
		||||
interface IPermissionIconButtonProps {
 | 
			
		||||
    permission: string;
 | 
			
		||||
    projectId?: string;
 | 
			
		||||
    environmentId?: string;
 | 
			
		||||
    className?: string;
 | 
			
		||||
    title?: string;
 | 
			
		||||
    tooltip?: string;
 | 
			
		||||
    children?: ReactNode;
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
    hidden?: boolean;
 | 
			
		||||
@ -30,9 +33,11 @@ const PermissionIconButton = ({
 | 
			
		||||
    projectId,
 | 
			
		||||
    children,
 | 
			
		||||
    environmentId,
 | 
			
		||||
    tooltip,
 | 
			
		||||
    ...rest
 | 
			
		||||
}: IButtonProps | ILinkProps) => {
 | 
			
		||||
    const { hasAccess } = useContext(AccessContext);
 | 
			
		||||
    const id = useId();
 | 
			
		||||
    let access;
 | 
			
		||||
 | 
			
		||||
    if (projectId && environmentId) {
 | 
			
		||||
@ -43,18 +48,14 @@ const PermissionIconButton = ({
 | 
			
		||||
        access = hasAccess(permission);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tooltipText = !access
 | 
			
		||||
        ? "You don't have access to perform this operation"
 | 
			
		||||
        : '';
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Tooltip title={tooltipText} arrow>
 | 
			
		||||
            <span>
 | 
			
		||||
                <IconButton disabled={!access} {...rest}>
 | 
			
		||||
        <TooltipResolver title={formatAccessText(access, tooltip)}>
 | 
			
		||||
            <span id={id}>
 | 
			
		||||
                <IconButton {...rest} disabled={!access} aria-labelledby={id}>
 | 
			
		||||
                    {children}
 | 
			
		||||
                </IconButton>
 | 
			
		||||
            </span>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
        </TooltipResolver>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
import { Switch, Tooltip, SwitchProps } from '@material-ui/core';
 | 
			
		||||
import { Switch, SwitchProps } from '@material-ui/core';
 | 
			
		||||
import AccessContext from 'contexts/AccessContext';
 | 
			
		||||
import React, { useContext } from 'react';
 | 
			
		||||
import { formatAccessText } from 'utils/formatAccessText';
 | 
			
		||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
 | 
			
		||||
 | 
			
		||||
interface IPermissionSwitchProps extends SwitchProps {
 | 
			
		||||
    permission: string;
 | 
			
		||||
@ -38,12 +40,8 @@ const PermissionSwitch = React.forwardRef<
 | 
			
		||||
        access = hasAccess(permission);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tooltipText = !access
 | 
			
		||||
        ? "You don't have access to perform this operation"
 | 
			
		||||
        : '';
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Tooltip title={tooltipText} arrow>
 | 
			
		||||
        <TooltipResolver title={formatAccessText(access, tooltip)} arrow>
 | 
			
		||||
            <span data-loading>
 | 
			
		||||
                <Switch
 | 
			
		||||
                    onChange={onChange}
 | 
			
		||||
@ -53,7 +51,7 @@ const PermissionSwitch = React.forwardRef<
 | 
			
		||||
                    {...rest}
 | 
			
		||||
                />
 | 
			
		||||
            </span>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
        </TooltipResolver>
 | 
			
		||||
    );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ export const TableActions = ({ search, onSearch }: ITableActionsProps) => {
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
            <div className={styles.verticalSeparator}></div>
 | 
			
		||||
            <div className={styles.verticalSeparator} />
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,6 @@ export const TableSearchField = ({
 | 
			
		||||
                            <Tooltip title="Clear search query" arrow>
 | 
			
		||||
                                <IconButton
 | 
			
		||||
                                    size="small"
 | 
			
		||||
                                    aria-label="Clear search query"
 | 
			
		||||
                                    onClick={() => {
 | 
			
		||||
                                        onChange('');
 | 
			
		||||
                                        onBlur?.(true);
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { useStyles } from './Toast.styles';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
import { useContext } from 'react';
 | 
			
		||||
import { IconButton } from '@material-ui/core';
 | 
			
		||||
import { IconButton, Tooltip } from '@material-ui/core';
 | 
			
		||||
import CheckMarkBadge from 'component/common/CheckmarkBadge/CheckMarkBadge';
 | 
			
		||||
import UIContext from 'contexts/UIContext';
 | 
			
		||||
import ConditionallyRender from 'component/common/ConditionallyRender';
 | 
			
		||||
@ -76,7 +76,7 @@ const Toast = ({ title, text, type, confetti }: IToast) => {
 | 
			
		||||
                                />
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                        <Tooltip title="Close">
 | 
			
		||||
                            <IconButton
 | 
			
		||||
                                color="primary"
 | 
			
		||||
                                onClick={hide}
 | 
			
		||||
@ -84,6 +84,7 @@ const Toast = ({ title, text, type, confetti }: IToast) => {
 | 
			
		||||
                            >
 | 
			
		||||
                                <Close />
 | 
			
		||||
                            </IconButton>
 | 
			
		||||
                        </Tooltip>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
import { Tooltip, TooltipProps } from '@material-ui/core';
 | 
			
		||||
 | 
			
		||||
interface ITooltipResolverProps extends Omit<TooltipProps, 'title'> {
 | 
			
		||||
    title: string | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const TooltipResolver = ({
 | 
			
		||||
    title,
 | 
			
		||||
    children,
 | 
			
		||||
    ...rest
 | 
			
		||||
}: ITooltipResolverProps) => {
 | 
			
		||||
    if (!title) {
 | 
			
		||||
        return children;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Tooltip {...rest} title={title}>
 | 
			
		||||
            {children}
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -79,7 +79,6 @@ const ContextList = () => {
 | 
			
		||||
                    show={
 | 
			
		||||
                        <Tooltip title="Edit context field">
 | 
			
		||||
                            <IconButton
 | 
			
		||||
                                aria-label="edit"
 | 
			
		||||
                                onClick={() =>
 | 
			
		||||
                                    history.push(`/context/edit/${field.name}`)
 | 
			
		||||
                                }
 | 
			
		||||
@ -94,7 +93,6 @@ const ContextList = () => {
 | 
			
		||||
                    show={
 | 
			
		||||
                        <Tooltip title="Delete context field">
 | 
			
		||||
                            <IconButton
 | 
			
		||||
                                aria-label="delete"
 | 
			
		||||
                                onClick={() => {
 | 
			
		||||
                                    setName(field.name);
 | 
			
		||||
                                    setShowDelDialogue(true);
 | 
			
		||||
 | 
			
		||||
@ -155,46 +155,38 @@ const EnvironmentListItem = ({
 | 
			
		||||
            <ConditionallyRender
 | 
			
		||||
                condition={updatePermission}
 | 
			
		||||
                show={
 | 
			
		||||
                    <Tooltip title="Drag to reorder">
 | 
			
		||||
                        <div>
 | 
			
		||||
                    <IconButton>
 | 
			
		||||
                                <DragIndicator titleAccess="Drag" />
 | 
			
		||||
                        <DragIndicator titleAccess="Drag" cursor="grab" />
 | 
			
		||||
                    </IconButton>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
            <ConditionallyRender
 | 
			
		||||
                condition={updatePermission}
 | 
			
		||||
                show={
 | 
			
		||||
                    <Tooltip title={`${tooltipText} environment`}>
 | 
			
		||||
                        <div>
 | 
			
		||||
                        <IconButton
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
                                setSelectedEnv(env);
 | 
			
		||||
                                setToggleDialog(prev => !prev);
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                                <OfflineBolt titleAccess="Toggle" />
 | 
			
		||||
                            <OfflineBolt />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
            <ConditionallyRender
 | 
			
		||||
                condition={updatePermission}
 | 
			
		||||
                show={
 | 
			
		||||
                    <Tooltip title="Update environment">
 | 
			
		||||
                        <div>
 | 
			
		||||
                    <Tooltip title="Edit environment">
 | 
			
		||||
                        <IconButton
 | 
			
		||||
                            disabled={env.protected}
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
                                history.push(`/environments/${env.name}`);
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                                <Edit titleAccess="Edit" />
 | 
			
		||||
                            <Edit />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
@ -202,7 +194,6 @@ const EnvironmentListItem = ({
 | 
			
		||||
                condition={hasAccess(DELETE_ENVIRONMENT)}
 | 
			
		||||
                show={
 | 
			
		||||
                    <Tooltip title="Delete environment">
 | 
			
		||||
                        <div>
 | 
			
		||||
                        <IconButton
 | 
			
		||||
                            disabled={env.protected}
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
@ -210,9 +201,8 @@ const EnvironmentListItem = ({
 | 
			
		||||
                                setSelectedEnv(env);
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                                <Delete titleAccess="Delete" />
 | 
			
		||||
                            <Delete />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ export const CreateFeatureButton = ({
 | 
			
		||||
                        data-testid={NAVIGATE_TO_CREATE_FEATURE}
 | 
			
		||||
                        disabled={!createFeature.access}
 | 
			
		||||
                    >
 | 
			
		||||
                        <Add titleAccess="New" />
 | 
			
		||||
                        <Add />
 | 
			
		||||
                    </IconButton>
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ export const FeatureStrategyMenuCard = ({
 | 
			
		||||
    return (
 | 
			
		||||
        <Link to={createStrategyPath} className={styles.card}>
 | 
			
		||||
            <div className={styles.icon}>
 | 
			
		||||
                <StrategyIcon aria-hidden />
 | 
			
		||||
                <StrategyIcon />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                <StringTruncator
 | 
			
		||||
 | 
			
		||||
@ -69,9 +69,10 @@ export const FeatureStrategyRemove = ({
 | 
			
		||||
                        disabled={disabled}
 | 
			
		||||
                        permission={DELETE_FEATURE_STRATEGY}
 | 
			
		||||
                        data-testid={STRATEGY_FORM_REMOVE_ID}
 | 
			
		||||
                        tooltip="Delete strategy"
 | 
			
		||||
                        type="button"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Delete titleAccess="Delete strategy" />
 | 
			
		||||
                        <Delete />
 | 
			
		||||
                    </PermissionIconButton>
 | 
			
		||||
                }
 | 
			
		||||
                elseShow={
 | 
			
		||||
 | 
			
		||||
@ -145,6 +145,7 @@ const FeatureToggleListItem = ({
 | 
			
		||||
                            !projectExists()
 | 
			
		||||
                        }
 | 
			
		||||
                        onClick={reviveFeature}
 | 
			
		||||
                        tooltip="Revive feature"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Undo />
 | 
			
		||||
                    </PermissionIconButton>
 | 
			
		||||
 | 
			
		||||
@ -128,14 +128,17 @@ exports[`renders correctly with one feature 1`] = `
 | 
			
		||||
                      className="material-icons MuiIcon-root"
 | 
			
		||||
                    >
 | 
			
		||||
                      <svg
 | 
			
		||||
                        aria-hidden={true}
 | 
			
		||||
                        className="MuiSvgIcon-root"
 | 
			
		||||
                        focusable="false"
 | 
			
		||||
                        role="img"
 | 
			
		||||
                        viewBox="0 0 24 24"
 | 
			
		||||
                      >
 | 
			
		||||
                        <path
 | 
			
		||||
                          d="M7 10l5 5 5-5z"
 | 
			
		||||
                        />
 | 
			
		||||
                        <title>
 | 
			
		||||
                          Toggle
 | 
			
		||||
                        </title>
 | 
			
		||||
                      </svg>
 | 
			
		||||
                    </span>
 | 
			
		||||
                  </span>
 | 
			
		||||
@ -296,14 +299,17 @@ exports[`renders correctly with one feature without permissions 1`] = `
 | 
			
		||||
                      className="material-icons MuiIcon-root"
 | 
			
		||||
                    >
 | 
			
		||||
                      <svg
 | 
			
		||||
                        aria-hidden={true}
 | 
			
		||||
                        className="MuiSvgIcon-root"
 | 
			
		||||
                        focusable="false"
 | 
			
		||||
                        role="img"
 | 
			
		||||
                        viewBox="0 0 24 24"
 | 
			
		||||
                      >
 | 
			
		||||
                        <path
 | 
			
		||||
                          d="M7 10l5 5 5-5z"
 | 
			
		||||
                        />
 | 
			
		||||
                        <title>
 | 
			
		||||
                          Toggle
 | 
			
		||||
                        </title>
 | 
			
		||||
                      </svg>
 | 
			
		||||
                    </span>
 | 
			
		||||
                  </span>
 | 
			
		||||
 | 
			
		||||
@ -93,7 +93,7 @@ const FeatureOverviewEnvironment = ({
 | 
			
		||||
            >
 | 
			
		||||
                <AccordionSummary
 | 
			
		||||
                    className={styles.accordionHeader}
 | 
			
		||||
                    expandIcon={<ExpandMore />}
 | 
			
		||||
                    expandIcon={<ExpandMore titleAccess="Toggle" />}
 | 
			
		||||
                >
 | 
			
		||||
                    <div className={styles.header} data-loading>
 | 
			
		||||
                        <div className={styles.headerTitle}>
 | 
			
		||||
 | 
			
		||||
@ -53,8 +53,9 @@ const FeatureOverviewEnvironmentStrategy = ({
 | 
			
		||||
                        projectId={projectId}
 | 
			
		||||
                        component={Link}
 | 
			
		||||
                        to={editStrategyPath}
 | 
			
		||||
                        tooltip="Edit"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Edit titleAccess="Edit" />
 | 
			
		||||
                        <Edit />
 | 
			
		||||
                    </PermissionIconButton>
 | 
			
		||||
                    <FeatureStrategyRemove
 | 
			
		||||
                        projectId={projectId}
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,10 @@ const FeatureOverviewMetaData = () => {
 | 
			
		||||
                                        component={Link}
 | 
			
		||||
                                        to={`/projects/${projectId}/features/${featureId}/settings`}
 | 
			
		||||
                                    >
 | 
			
		||||
                                        <Edit className={styles.editIcon} />
 | 
			
		||||
                                        <Edit
 | 
			
		||||
                                            className={styles.editIcon}
 | 
			
		||||
                                            titleAccess="Settings"
 | 
			
		||||
                                        />
 | 
			
		||||
                                    </PermissionIconButton>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </span>
 | 
			
		||||
@ -66,6 +69,7 @@ const FeatureOverviewMetaData = () => {
 | 
			
		||||
                                        permission={UPDATE_FEATURE}
 | 
			
		||||
                                        component={Link}
 | 
			
		||||
                                        to={`/projects/${projectId}/features/${featureId}/settings`}
 | 
			
		||||
                                        tooltip="Edit description"
 | 
			
		||||
                                    >
 | 
			
		||||
                                        <Edit className={styles.editIcon} />
 | 
			
		||||
                                    </PermissionIconButton>
 | 
			
		||||
 | 
			
		||||
@ -106,7 +106,9 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
 | 
			
		||||
            data-loading
 | 
			
		||||
            label={t.value}
 | 
			
		||||
            key={`${t.type}:${t.value}`}
 | 
			
		||||
            deleteIcon={<Close className={styles.closeIcon} />}
 | 
			
		||||
            deleteIcon={
 | 
			
		||||
                <Close className={styles.closeIcon} titleAccess="Remove" />
 | 
			
		||||
            }
 | 
			
		||||
            onDelete={
 | 
			
		||||
                canDeleteTag
 | 
			
		||||
                    ? () => {
 | 
			
		||||
 | 
			
		||||
@ -34,8 +34,9 @@ export const FeatureSettingsInformation = ({
 | 
			
		||||
                    projectId={projectId}
 | 
			
		||||
                    data-loading
 | 
			
		||||
                    onClick={onEdit}
 | 
			
		||||
                    tooltip="Edit"
 | 
			
		||||
                >
 | 
			
		||||
                    <Edit titleAccess="Edit" />
 | 
			
		||||
                    <Edit />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
            </div>
 | 
			
		||||
            <Typography>
 | 
			
		||||
 | 
			
		||||
@ -90,7 +90,10 @@ const FeatureSettingsProjectConfirm = ({
 | 
			
		||||
                        </p>
 | 
			
		||||
                        <div className={styles.iconContainer}>
 | 
			
		||||
                            <div className={styles.errorIconContainer}>
 | 
			
		||||
                                <Error className={styles.check} />
 | 
			
		||||
                                <Error
 | 
			
		||||
                                    className={styles.check}
 | 
			
		||||
                                    titleAccess="Error"
 | 
			
		||||
                                />
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Grid, IconButton, TextField } from '@material-ui/core';
 | 
			
		||||
import { Grid, IconButton, TextField, Tooltip } from '@material-ui/core';
 | 
			
		||||
import { Delete } from '@material-ui/icons';
 | 
			
		||||
import { useStyles } from './OverrideConfig.styles';
 | 
			
		||||
import { Autocomplete } from '@material-ui/lab';
 | 
			
		||||
@ -98,9 +98,11 @@ export const OverrideConfig = ({
 | 
			
		||||
                    />
 | 
			
		||||
                </Grid>
 | 
			
		||||
                <Grid item md={1}>
 | 
			
		||||
                    <Tooltip title="Remove">
 | 
			
		||||
                        <IconButton onClick={removeOverride(i)}>
 | 
			
		||||
                            <Delete />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                </Grid>
 | 
			
		||||
            </Grid>
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,10 @@
 | 
			
		||||
import { Chip, IconButton, TableCell, TableRow } from '@material-ui/core';
 | 
			
		||||
import {
 | 
			
		||||
    Chip,
 | 
			
		||||
    IconButton,
 | 
			
		||||
    TableCell,
 | 
			
		||||
    TableRow,
 | 
			
		||||
    Tooltip,
 | 
			
		||||
} from '@material-ui/core';
 | 
			
		||||
import { Delete, Edit } from '@material-ui/icons';
 | 
			
		||||
 | 
			
		||||
import styles from '../variants.module.scss';
 | 
			
		||||
@ -54,12 +60,15 @@ const FeatureVariantListItem = ({
 | 
			
		||||
                show={
 | 
			
		||||
                    <TableCell className={styles.actions}>
 | 
			
		||||
                        <div className={styles.actionsContainer}>
 | 
			
		||||
                            <Tooltip title="Edit variant">
 | 
			
		||||
                                <IconButton
 | 
			
		||||
                                    data-testid={'VARIANT_EDIT_BUTTON'}
 | 
			
		||||
                                    onClick={() => editVariant(variant.name)}
 | 
			
		||||
                                >
 | 
			
		||||
                                    <Edit />
 | 
			
		||||
                                </IconButton>
 | 
			
		||||
                            </Tooltip>
 | 
			
		||||
                            <Tooltip title="Delete variant">
 | 
			
		||||
                                <IconButton
 | 
			
		||||
                                    data-testid={`VARIANT_DELETE_BUTTON_${variant.name}`}
 | 
			
		||||
                                    onClick={e => {
 | 
			
		||||
@ -72,6 +81,7 @@ const FeatureVariantListItem = ({
 | 
			
		||||
                                >
 | 
			
		||||
                                    <Delete />
 | 
			
		||||
                                </IconButton>
 | 
			
		||||
                            </Tooltip>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </TableCell>
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -143,32 +143,36 @@ export const FeatureView = () => {
 | 
			
		||||
                                    data-loading
 | 
			
		||||
                                    component={Link}
 | 
			
		||||
                                    to={`/projects/${projectId}/features/${featureId}/strategies/copy`}
 | 
			
		||||
                                    tooltip="Copy feature"
 | 
			
		||||
                                >
 | 
			
		||||
                                    <FileCopy titleAccess="Copy" />
 | 
			
		||||
                                    <FileCopy />
 | 
			
		||||
                                </PermissionIconButton>
 | 
			
		||||
                                <PermissionIconButton
 | 
			
		||||
                                    permission={DELETE_FEATURE}
 | 
			
		||||
                                    projectId={projectId}
 | 
			
		||||
                                    tooltip="Archive feature toggle"
 | 
			
		||||
                                    data-loading
 | 
			
		||||
                                    onClick={() => setShowDelDialog(true)}
 | 
			
		||||
                                >
 | 
			
		||||
                                    <Archive titleAccess="Archive feature toggle" />
 | 
			
		||||
                                    <Archive />
 | 
			
		||||
                                </PermissionIconButton>
 | 
			
		||||
                                <PermissionIconButton
 | 
			
		||||
                                    onClick={() => setOpenStaleDialog(true)}
 | 
			
		||||
                                    permission={UPDATE_FEATURE}
 | 
			
		||||
                                    projectId={projectId}
 | 
			
		||||
                                    tooltip="Toggle stale status"
 | 
			
		||||
                                    data-loading
 | 
			
		||||
                                >
 | 
			
		||||
                                    <WatchLater titleAccess="Toggle stale status" />
 | 
			
		||||
                                    <WatchLater />
 | 
			
		||||
                                </PermissionIconButton>
 | 
			
		||||
                                <PermissionIconButton
 | 
			
		||||
                                    onClick={() => setOpenTagDialog(true)}
 | 
			
		||||
                                    permission={UPDATE_FEATURE}
 | 
			
		||||
                                    projectId={projectId}
 | 
			
		||||
                                    tooltip="Add tag"
 | 
			
		||||
                                    data-loading
 | 
			
		||||
                                >
 | 
			
		||||
                                    <Label titleAccess="Add tag" />
 | 
			
		||||
                                    <Label />
 | 
			
		||||
                                </PermissionIconButton>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
@ -175,7 +175,7 @@ const StrategyConstraintInputField = ({
 | 
			
		||||
            </td>
 | 
			
		||||
            <td className={styles.tableCell}>
 | 
			
		||||
                <IconButton onClick={removeConstraint}>
 | 
			
		||||
                    <Delete />
 | 
			
		||||
                    <Delete titleAccess="Remove constraint" />
 | 
			
		||||
                </IconButton>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
@ -92,7 +92,10 @@ const StrategyConstraints: React.FC<IStrategyConstraintProps> = ({
 | 
			
		||||
                <Typography variant="subtitle2">
 | 
			
		||||
                    {'Constraints '}
 | 
			
		||||
 | 
			
		||||
                    <Info style={{ fontSize: '0.9rem', color: 'gray' }} />
 | 
			
		||||
                    <Info
 | 
			
		||||
                        style={{ fontSize: '0.9rem', color: 'gray' }}
 | 
			
		||||
                        titleAccess="Help"
 | 
			
		||||
                    />
 | 
			
		||||
                </Typography>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
            <table style={{ margin: 0 }}>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { useContext, useState } from 'react';
 | 
			
		||||
import { Button, IconButton } from '@material-ui/core';
 | 
			
		||||
import { Button, IconButton, Tooltip } from '@material-ui/core';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
import CloseIcon from '@material-ui/icons/Close';
 | 
			
		||||
import { ReactComponent as Logo } from 'assets/icons/logoPlain.svg';
 | 
			
		||||
@ -73,12 +73,14 @@ export const FeedbackNPS = ({ openUrl }: IFeedbackNPSProps) => {
 | 
			
		||||
                        commonStyles.contentSpacingY
 | 
			
		||||
                    )}
 | 
			
		||||
                >
 | 
			
		||||
                    <Tooltip title="Close">
 | 
			
		||||
                        <IconButton
 | 
			
		||||
                            className={styles.close}
 | 
			
		||||
                            onClick={() => setShowFeedback(false)}
 | 
			
		||||
                        >
 | 
			
		||||
                            <CloseIcon />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                    <Logo className={styles.logo} />
 | 
			
		||||
                    <ConditionallyRender
 | 
			
		||||
                        condition={answeredNotNow}
 | 
			
		||||
 | 
			
		||||
@ -66,12 +66,14 @@ const Header = () => {
 | 
			
		||||
                    <ConditionallyRender
 | 
			
		||||
                        condition={smallScreen}
 | 
			
		||||
                        show={
 | 
			
		||||
                            <Tooltip title="Menu">
 | 
			
		||||
                                <IconButton
 | 
			
		||||
                                    className={styles.drawerButton}
 | 
			
		||||
                                    onClick={toggleDrawer}
 | 
			
		||||
                                >
 | 
			
		||||
                                <MenuIcon titleAccess="Menu" />
 | 
			
		||||
                                    <MenuIcon />
 | 
			
		||||
                                </IconButton>
 | 
			
		||||
                            </Tooltip>
 | 
			
		||||
                        }
 | 
			
		||||
                        elseShow={
 | 
			
		||||
                            <Link
 | 
			
		||||
@ -136,26 +138,29 @@ const Header = () => {
 | 
			
		||||
                                        >
 | 
			
		||||
                                            <MenuBookIcon
 | 
			
		||||
                                                className={styles.docsIcon}
 | 
			
		||||
                                                titleAccess="Documentation"
 | 
			
		||||
                                            />
 | 
			
		||||
                                        </a>
 | 
			
		||||
                                    </Tooltip>
 | 
			
		||||
                                    <ConditionallyRender
 | 
			
		||||
                                        condition={admin}
 | 
			
		||||
                                        show={
 | 
			
		||||
                                            <Tooltip title="Settings">
 | 
			
		||||
                                                <IconButton
 | 
			
		||||
                                                    onClick={e =>
 | 
			
		||||
                                                    setAnchorEl(e.currentTarget)
 | 
			
		||||
                                                        setAnchorEl(
 | 
			
		||||
                                                            e.currentTarget
 | 
			
		||||
                                                        )
 | 
			
		||||
                                                    }
 | 
			
		||||
                                                >
 | 
			
		||||
                                                    <SettingsIcon
 | 
			
		||||
                                                    className={styles.docsIcon}
 | 
			
		||||
                                                    titleAccess="Settings"
 | 
			
		||||
                                                />
 | 
			
		||||
                                            </IconButton>
 | 
			
		||||
                                                        className={
 | 
			
		||||
                                                            styles.docsIcon
 | 
			
		||||
                                                        }
 | 
			
		||||
                                                    />
 | 
			
		||||
                                                </IconButton>
 | 
			
		||||
                                            </Tooltip>
 | 
			
		||||
                                        }
 | 
			
		||||
                                    />
 | 
			
		||||
 | 
			
		||||
                                    <NavigationMenu
 | 
			
		||||
                                        id="admin-navigation"
 | 
			
		||||
                                        options={filteredMainRoutes.adminRoutes}
 | 
			
		||||
@ -166,7 +171,6 @@ const Header = () => {
 | 
			
		||||
                                </>
 | 
			
		||||
                            }
 | 
			
		||||
                        />
 | 
			
		||||
 | 
			
		||||
                        <UserProfile />
 | 
			
		||||
                    </div>
 | 
			
		||||
                </Container>
 | 
			
		||||
 | 
			
		||||
@ -128,6 +128,7 @@ const Project = () => {
 | 
			
		||||
                            permission={UPDATE_PROJECT}
 | 
			
		||||
                            projectId={project?.id}
 | 
			
		||||
                            onClick={() => history.push(`/projects/${id}/edit`)}
 | 
			
		||||
                            tooltip="Edit project"
 | 
			
		||||
                            data-loading
 | 
			
		||||
                        >
 | 
			
		||||
                            <Edit />
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,7 @@ const ProjectInfo = ({
 | 
			
		||||
            className={permissionButtonClass}
 | 
			
		||||
            data-loading
 | 
			
		||||
            to={`/projects/${id}/edit`}
 | 
			
		||||
            tooltip="Edit description"
 | 
			
		||||
        >
 | 
			
		||||
            <Edit />
 | 
			
		||||
        </PermissionIconButton>
 | 
			
		||||
@ -82,7 +83,9 @@ const ProjectInfo = ({
 | 
			
		||||
                                    elseShow={
 | 
			
		||||
                                        <Accordion className={styles.accordion}>
 | 
			
		||||
                                            <AccordionSummary
 | 
			
		||||
                                                expandIcon={<ExpandMore />}
 | 
			
		||||
                                                expandIcon={
 | 
			
		||||
                                                    <ExpandMore titleAccess="Toggle" />
 | 
			
		||||
                                                }
 | 
			
		||||
                                                className={styles.accordionBody}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                Description
 | 
			
		||||
 | 
			
		||||
@ -71,10 +71,9 @@ export const ProjectAccessListItem = ({
 | 
			
		||||
                    projectId={projectId}
 | 
			
		||||
                    className={styles.iconButton}
 | 
			
		||||
                    edge="end"
 | 
			
		||||
                    aria-label="delete"
 | 
			
		||||
                    title="Remove access"
 | 
			
		||||
                    onClick={() => handleRemoveAccess(user)}
 | 
			
		||||
                    disabled={access.users.length === 1}
 | 
			
		||||
                    tooltip="Remove access"
 | 
			
		||||
                >
 | 
			
		||||
                    <Delete />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ export const ProjectCard = ({
 | 
			
		||||
    return (
 | 
			
		||||
        <Card className={styles.projectCard} onMouseEnter={onHover}>
 | 
			
		||||
            <div className={styles.header} data-loading>
 | 
			
		||||
                <div className={styles.title}>{name}</div>
 | 
			
		||||
                <h2 className={styles.title}>{name}</h2>
 | 
			
		||||
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    permission={UPDATE_PROJECT}
 | 
			
		||||
@ -73,6 +73,7 @@ export const ProjectCard = ({
 | 
			
		||||
                    className={styles.actionsBtn}
 | 
			
		||||
                    data-loading
 | 
			
		||||
                    onClick={handleClick}
 | 
			
		||||
                    tooltip="Menu"
 | 
			
		||||
                >
 | 
			
		||||
                    <MoreVertIcon />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
 | 
			
		||||
@ -65,12 +65,12 @@ export const SegmentListItem = ({
 | 
			
		||||
                        push(`/segments/edit/${id}`);
 | 
			
		||||
                    }}
 | 
			
		||||
                    permission={UPDATE_SEGMENT}
 | 
			
		||||
                    tooltip="Edit segment"
 | 
			
		||||
                >
 | 
			
		||||
                    <Edit titleAccess="Edit segment" />
 | 
			
		||||
                    <Edit />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    data-loading
 | 
			
		||||
                    aria-label="Remove segment"
 | 
			
		||||
                    onClick={() => {
 | 
			
		||||
                        setCurrentSegment({
 | 
			
		||||
                            id,
 | 
			
		||||
@ -83,6 +83,7 @@ export const SegmentListItem = ({
 | 
			
		||||
                        setDelDialog(true);
 | 
			
		||||
                    }}
 | 
			
		||||
                    permission={ADMIN}
 | 
			
		||||
                    tooltip="Remove segment"
 | 
			
		||||
                    data-testid={`${SEGMENT_DELETE_BTN_ID}_${name}`}
 | 
			
		||||
                >
 | 
			
		||||
                    <Delete />
 | 
			
		||||
 | 
			
		||||
@ -72,6 +72,7 @@ export const StrategiesList = () => {
 | 
			
		||||
                            data-testid={ADD_NEW_STRATEGY_ID}
 | 
			
		||||
                            onClick={() => history.push('/strategies/create')}
 | 
			
		||||
                            permission={CREATE_STRATEGY}
 | 
			
		||||
                            tooltip="New strategy"
 | 
			
		||||
                        >
 | 
			
		||||
                            <Add />
 | 
			
		||||
                        </PermissionIconButton>
 | 
			
		||||
@ -162,16 +163,13 @@ export const StrategiesList = () => {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const reactivateButton = (strategy: ICustomStrategy) => (
 | 
			
		||||
        <Tooltip title="Reactivate activation strategy">
 | 
			
		||||
            <div>
 | 
			
		||||
        <PermissionIconButton
 | 
			
		||||
            onClick={() => onReactivateStrategy(strategy)}
 | 
			
		||||
            permission={UPDATE_STRATEGY}
 | 
			
		||||
            tooltip="Reactivate activation strategy"
 | 
			
		||||
        >
 | 
			
		||||
                    <VisibilityOff titleAccess="Reactivate" />
 | 
			
		||||
            <VisibilityOff />
 | 
			
		||||
        </PermissionIconButton>
 | 
			
		||||
            </div>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const deprecateButton = (strategy: ICustomStrategy) => (
 | 
			
		||||
@ -191,8 +189,9 @@ export const StrategiesList = () => {
 | 
			
		||||
                    <PermissionIconButton
 | 
			
		||||
                        onClick={() => onDeprecateStrategy(strategy)}
 | 
			
		||||
                        permission={UPDATE_STRATEGY}
 | 
			
		||||
                        tooltip="Deprecate strategy"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Visibility titleAccess="Deprecate strategy" />
 | 
			
		||||
                        <Visibility />
 | 
			
		||||
                    </PermissionIconButton>
 | 
			
		||||
                </div>
 | 
			
		||||
            }
 | 
			
		||||
@ -208,8 +207,9 @@ export const StrategiesList = () => {
 | 
			
		||||
                        history.push(`/strategies/${strategy?.name}/edit`)
 | 
			
		||||
                    }
 | 
			
		||||
                    permission={UPDATE_STRATEGY}
 | 
			
		||||
                    tooltip="Edit strategy"
 | 
			
		||||
                >
 | 
			
		||||
                    <Edit titleAccess="Edit strategy" />
 | 
			
		||||
                    <Edit />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
            }
 | 
			
		||||
            elseShow={
 | 
			
		||||
@ -231,6 +231,7 @@ export const StrategiesList = () => {
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    onClick={() => onDeleteStrategy(strategy)}
 | 
			
		||||
                    permission={DELETE_STRATEGY}
 | 
			
		||||
                    tooltip="Delete strategy"
 | 
			
		||||
                >
 | 
			
		||||
                    <Delete />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
@ -239,7 +240,7 @@ export const StrategiesList = () => {
 | 
			
		||||
                <Tooltip title="You cannot delete a built-in strategy">
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <IconButton disabled>
 | 
			
		||||
                            <Delete />
 | 
			
		||||
                            <Delete titleAccess="Delete strategy" />
 | 
			
		||||
                        </IconButton>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
 | 
			
		||||
@ -30,17 +30,10 @@ exports[`renders correctly with one strategy 1`] = `
 | 
			
		||||
        className="makeStyles-headerActions-8"
 | 
			
		||||
      >
 | 
			
		||||
        <span
 | 
			
		||||
          aria-describedby={null}
 | 
			
		||||
          className=""
 | 
			
		||||
          onBlur={[Function]}
 | 
			
		||||
          onFocus={[Function]}
 | 
			
		||||
          onMouseLeave={[Function]}
 | 
			
		||||
          onMouseOver={[Function]}
 | 
			
		||||
          onTouchEnd={[Function]}
 | 
			
		||||
          onTouchStart={[Function]}
 | 
			
		||||
          title=""
 | 
			
		||||
          id="useId-0"
 | 
			
		||||
        >
 | 
			
		||||
          <button
 | 
			
		||||
            aria-describedby="useId-0"
 | 
			
		||||
            className="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary"
 | 
			
		||||
            data-testid="ADD_NEW_STRATEGY_ID"
 | 
			
		||||
            disabled={false}
 | 
			
		||||
@ -129,15 +122,17 @@ exports[`renders correctly with one strategy 1`] = `
 | 
			
		||||
          <span
 | 
			
		||||
            aria-describedby={null}
 | 
			
		||||
            className=""
 | 
			
		||||
            id="useId-1"
 | 
			
		||||
            onBlur={[Function]}
 | 
			
		||||
            onFocus={[Function]}
 | 
			
		||||
            onMouseLeave={[Function]}
 | 
			
		||||
            onMouseOver={[Function]}
 | 
			
		||||
            onTouchEnd={[Function]}
 | 
			
		||||
            onTouchStart={[Function]}
 | 
			
		||||
            title=""
 | 
			
		||||
            title="Deprecate strategy"
 | 
			
		||||
          >
 | 
			
		||||
            <button
 | 
			
		||||
              aria-labelledby="useId-1"
 | 
			
		||||
              className="MuiButtonBase-root MuiIconButton-root"
 | 
			
		||||
              disabled={false}
 | 
			
		||||
              onBlur={[Function]}
 | 
			
		||||
@ -159,17 +154,14 @@ exports[`renders correctly with one strategy 1`] = `
 | 
			
		||||
                className="MuiIconButton-label"
 | 
			
		||||
              >
 | 
			
		||||
                <svg
 | 
			
		||||
                  aria-hidden={true}
 | 
			
		||||
                  className="MuiSvgIcon-root"
 | 
			
		||||
                  focusable="false"
 | 
			
		||||
                  role="img"
 | 
			
		||||
                  viewBox="0 0 24 24"
 | 
			
		||||
                >
 | 
			
		||||
                  <path
 | 
			
		||||
                    d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"
 | 
			
		||||
                  />
 | 
			
		||||
                  <title>
 | 
			
		||||
                    Deprecate strategy
 | 
			
		||||
                  </title>
 | 
			
		||||
                </svg>
 | 
			
		||||
              </span>
 | 
			
		||||
              <span
 | 
			
		||||
@ -257,14 +249,17 @@ exports[`renders correctly with one strategy 1`] = `
 | 
			
		||||
              className="MuiIconButton-label"
 | 
			
		||||
            >
 | 
			
		||||
              <svg
 | 
			
		||||
                aria-hidden={true}
 | 
			
		||||
                className="MuiSvgIcon-root"
 | 
			
		||||
                focusable="false"
 | 
			
		||||
                role="img"
 | 
			
		||||
                viewBox="0 0 24 24"
 | 
			
		||||
              >
 | 
			
		||||
                <path
 | 
			
		||||
                  d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"
 | 
			
		||||
                />
 | 
			
		||||
                <title>
 | 
			
		||||
                  Delete strategy
 | 
			
		||||
                </title>
 | 
			
		||||
              </svg>
 | 
			
		||||
            </span>
 | 
			
		||||
          </button>
 | 
			
		||||
@ -305,17 +300,10 @@ exports[`renders correctly with one strategy without permissions 1`] = `
 | 
			
		||||
        className="makeStyles-headerActions-8"
 | 
			
		||||
      >
 | 
			
		||||
        <span
 | 
			
		||||
          aria-describedby={null}
 | 
			
		||||
          className=""
 | 
			
		||||
          onBlur={[Function]}
 | 
			
		||||
          onFocus={[Function]}
 | 
			
		||||
          onMouseLeave={[Function]}
 | 
			
		||||
          onMouseOver={[Function]}
 | 
			
		||||
          onTouchEnd={[Function]}
 | 
			
		||||
          onTouchStart={[Function]}
 | 
			
		||||
          title=""
 | 
			
		||||
          id="useId-2"
 | 
			
		||||
        >
 | 
			
		||||
          <button
 | 
			
		||||
            aria-describedby="useId-2"
 | 
			
		||||
            className="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary"
 | 
			
		||||
            data-testid="ADD_NEW_STRATEGY_ID"
 | 
			
		||||
            disabled={false}
 | 
			
		||||
@ -404,15 +392,17 @@ exports[`renders correctly with one strategy without permissions 1`] = `
 | 
			
		||||
          <span
 | 
			
		||||
            aria-describedby={null}
 | 
			
		||||
            className=""
 | 
			
		||||
            id="useId-3"
 | 
			
		||||
            onBlur={[Function]}
 | 
			
		||||
            onFocus={[Function]}
 | 
			
		||||
            onMouseLeave={[Function]}
 | 
			
		||||
            onMouseOver={[Function]}
 | 
			
		||||
            onTouchEnd={[Function]}
 | 
			
		||||
            onTouchStart={[Function]}
 | 
			
		||||
            title=""
 | 
			
		||||
            title="Deprecate strategy"
 | 
			
		||||
          >
 | 
			
		||||
            <button
 | 
			
		||||
              aria-labelledby="useId-3"
 | 
			
		||||
              className="MuiButtonBase-root MuiIconButton-root"
 | 
			
		||||
              disabled={false}
 | 
			
		||||
              onBlur={[Function]}
 | 
			
		||||
@ -434,17 +424,14 @@ exports[`renders correctly with one strategy without permissions 1`] = `
 | 
			
		||||
                className="MuiIconButton-label"
 | 
			
		||||
              >
 | 
			
		||||
                <svg
 | 
			
		||||
                  aria-hidden={true}
 | 
			
		||||
                  className="MuiSvgIcon-root"
 | 
			
		||||
                  focusable="false"
 | 
			
		||||
                  role="img"
 | 
			
		||||
                  viewBox="0 0 24 24"
 | 
			
		||||
                >
 | 
			
		||||
                  <path
 | 
			
		||||
                    d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"
 | 
			
		||||
                  />
 | 
			
		||||
                  <title>
 | 
			
		||||
                    Deprecate strategy
 | 
			
		||||
                  </title>
 | 
			
		||||
                </svg>
 | 
			
		||||
              </span>
 | 
			
		||||
              <span
 | 
			
		||||
@ -532,14 +519,17 @@ exports[`renders correctly with one strategy without permissions 1`] = `
 | 
			
		||||
              className="MuiIconButton-label"
 | 
			
		||||
            >
 | 
			
		||||
              <svg
 | 
			
		||||
                aria-hidden={true}
 | 
			
		||||
                className="MuiSvgIcon-root"
 | 
			
		||||
                focusable="false"
 | 
			
		||||
                role="img"
 | 
			
		||||
                viewBox="0 0 24 24"
 | 
			
		||||
              >
 | 
			
		||||
                <path
 | 
			
		||||
                  d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"
 | 
			
		||||
                />
 | 
			
		||||
                <title>
 | 
			
		||||
                  Delete strategy
 | 
			
		||||
                </title>
 | 
			
		||||
              </svg>
 | 
			
		||||
            </span>
 | 
			
		||||
          </button>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,9 @@
 | 
			
		||||
import { Checkbox, FormControlLabel, IconButton } from '@material-ui/core';
 | 
			
		||||
import {
 | 
			
		||||
    Checkbox,
 | 
			
		||||
    FormControlLabel,
 | 
			
		||||
    IconButton,
 | 
			
		||||
    Tooltip,
 | 
			
		||||
} from '@material-ui/core';
 | 
			
		||||
import { Delete } from '@material-ui/icons';
 | 
			
		||||
import { useStyles } from './StrategyParameter.styles';
 | 
			
		||||
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
 | 
			
		||||
@ -74,7 +79,7 @@ export const StrategyParameter = ({
 | 
			
		||||
                condition={index === 0}
 | 
			
		||||
                show={
 | 
			
		||||
                    <p className={styles.input}>
 | 
			
		||||
                        The parameters define how the strategy will look like.
 | 
			
		||||
                        The parameters define what the strategy will look like.
 | 
			
		||||
                    </p>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
@ -88,13 +93,15 @@ export const StrategyParameter = ({
 | 
			
		||||
                    error={Boolean(errors?.[`paramName${index}`])}
 | 
			
		||||
                    errorText={errors?.[`paramName${index}`]}
 | 
			
		||||
                />
 | 
			
		||||
                <Tooltip title="Remove parameter">
 | 
			
		||||
                    <IconButton
 | 
			
		||||
                        onClick={() => {
 | 
			
		||||
                            setParams(params.filter((e, i) => i !== index));
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                    <Delete titleAccess="Delete" />
 | 
			
		||||
                        <Delete />
 | 
			
		||||
                    </IconButton>
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
            </div>
 | 
			
		||||
            <GeneralSelect
 | 
			
		||||
                label="Type*"
 | 
			
		||||
 | 
			
		||||
@ -34,18 +34,18 @@ export const StrategyDetails = ({
 | 
			
		||||
                    <ConditionallyRender
 | 
			
		||||
                        condition={required}
 | 
			
		||||
                        show={
 | 
			
		||||
                            <Tooltip title="Required">
 | 
			
		||||
                            <ListItemAvatar>
 | 
			
		||||
                                    <Add />
 | 
			
		||||
                                </ListItemAvatar>
 | 
			
		||||
                                <Tooltip title="Required parameter">
 | 
			
		||||
                                    <Add aria-hidden={false} />
 | 
			
		||||
                                </Tooltip>
 | 
			
		||||
                            </ListItemAvatar>
 | 
			
		||||
                        }
 | 
			
		||||
                        elseShow={
 | 
			
		||||
                            <Tooltip title="Optional">
 | 
			
		||||
                            <ListItemAvatar>
 | 
			
		||||
                                    <RadioButtonChecked />
 | 
			
		||||
                                </ListItemAvatar>
 | 
			
		||||
                                <Tooltip title="Optional parameter">
 | 
			
		||||
                                    <RadioButtonChecked aria-hidden={false} />
 | 
			
		||||
                                </Tooltip>
 | 
			
		||||
                            </ListItemAvatar>
 | 
			
		||||
                        }
 | 
			
		||||
                    />
 | 
			
		||||
                    <ListItemText
 | 
			
		||||
 | 
			
		||||
@ -43,8 +43,9 @@ export const StrategyView = () => {
 | 
			
		||||
                                    permission={UPDATE_STRATEGY}
 | 
			
		||||
                                    data-loading
 | 
			
		||||
                                    onClick={handleEdit}
 | 
			
		||||
                                    tooltip="Edit strategy"
 | 
			
		||||
                                >
 | 
			
		||||
                                    <Edit titleAccess="Edit strategy" />
 | 
			
		||||
                                    <Edit />
 | 
			
		||||
                                </PermissionIconButton>
 | 
			
		||||
                            }
 | 
			
		||||
                        />
 | 
			
		||||
 | 
			
		||||
@ -19,21 +19,26 @@ export const TogglesLinkList = ({ toggles }: ITogglesLinkListProps) => (
 | 
			
		||||
    <List style={{ textAlign: 'left' }} className={styles.truncate}>
 | 
			
		||||
        <ConditionallyRender
 | 
			
		||||
            condition={toggles.length > 0}
 | 
			
		||||
            show={
 | 
			
		||||
                <>
 | 
			
		||||
                    {toggles.map(({ name, description = '-', enabled }) => (
 | 
			
		||||
            show={toggles.map(({ name, description = '-', enabled }) => (
 | 
			
		||||
                <ListItem key={name}>
 | 
			
		||||
                            <Tooltip title={enabled ? 'Enabled' : 'Disabled'}>
 | 
			
		||||
                    <ListItemAvatar>
 | 
			
		||||
                                    {enabled ? <PlayArrow /> : <Pause />}
 | 
			
		||||
                                </ListItemAvatar>
 | 
			
		||||
                        <ConditionallyRender
 | 
			
		||||
                            condition={Boolean(enabled)}
 | 
			
		||||
                            show={
 | 
			
		||||
                                <Tooltip title="Enabled">
 | 
			
		||||
                                    <PlayArrow aria-hidden={false} />
 | 
			
		||||
                                </Tooltip>
 | 
			
		||||
                            }
 | 
			
		||||
                            elseShow={
 | 
			
		||||
                                <Tooltip title="Disabled">
 | 
			
		||||
                                    <Pause aria-hidden={false} />
 | 
			
		||||
                                </Tooltip>
 | 
			
		||||
                            }
 | 
			
		||||
                        />
 | 
			
		||||
                    </ListItemAvatar>
 | 
			
		||||
                    <ListItemText
 | 
			
		||||
                        primary={
 | 
			
		||||
                                    <Link
 | 
			
		||||
                                        key={name}
 | 
			
		||||
                                        to={`/features/view/${name}`}
 | 
			
		||||
                                    >
 | 
			
		||||
                            <Link key={name} to={`/features/view/${name}`}>
 | 
			
		||||
                                {name}
 | 
			
		||||
                            </Link>
 | 
			
		||||
                        }
 | 
			
		||||
@ -41,8 +46,6 @@ export const TogglesLinkList = ({ toggles }: ITogglesLinkListProps) => (
 | 
			
		||||
                    />
 | 
			
		||||
                </ListItem>
 | 
			
		||||
            ))}
 | 
			
		||||
                </>
 | 
			
		||||
            }
 | 
			
		||||
        />
 | 
			
		||||
    </List>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,6 @@ export const TagTypeList = () => {
 | 
			
		||||
                            show={
 | 
			
		||||
                                <Tooltip title="Add tag type">
 | 
			
		||||
                                    <IconButton
 | 
			
		||||
                                        aria-label="add tag type"
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                            history.push('/tag-types/create')
 | 
			
		||||
                                        }
 | 
			
		||||
@ -129,9 +128,10 @@ export const TagTypeList = () => {
 | 
			
		||||
                <PermissionIconButton
 | 
			
		||||
                    permission={UPDATE_TAG_TYPE}
 | 
			
		||||
                    component={Link}
 | 
			
		||||
                    tooltip="Edit tag type"
 | 
			
		||||
                    to={`/tag-types/edit/${tagType.name}`}
 | 
			
		||||
                >
 | 
			
		||||
                    <Edit className={styles.icon} titleAccess="Edit tag type" />
 | 
			
		||||
                    <Edit className={styles.icon} />
 | 
			
		||||
                </PermissionIconButton>
 | 
			
		||||
                <ConditionallyRender
 | 
			
		||||
                    condition={hasAccess(DELETE_TAG_TYPE)}
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ import {
 | 
			
		||||
    NotFoundError,
 | 
			
		||||
} from 'utils/apiUtils';
 | 
			
		||||
import { formatApiPath } from 'utils/formatPath';
 | 
			
		||||
import { ACCESS_DENIED_TEXT } from 'utils/formatAccessText';
 | 
			
		||||
 | 
			
		||||
type ApiErrorHandler = (
 | 
			
		||||
    setErrors: Dispatch<SetStateAction<{}>>,
 | 
			
		||||
@ -125,8 +126,7 @@ const useAPI = ({
 | 
			
		||||
            } else {
 | 
			
		||||
                setErrors(prev => ({
 | 
			
		||||
                    ...prev,
 | 
			
		||||
                    unauthorized:
 | 
			
		||||
                        'You are not authorized to perform this operation',
 | 
			
		||||
                    unauthorized: ACCESS_DENIED_TEXT,
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										40
									
								
								frontend/src/hooks/useId.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								frontend/src/hooks/useId.test.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
import { useId } from 'hooks/useId';
 | 
			
		||||
import { renderHook } from '@testing-library/react-hooks';
 | 
			
		||||
 | 
			
		||||
test('useId', () => {
 | 
			
		||||
    const { result, rerender } = renderHook(() => useId());
 | 
			
		||||
 | 
			
		||||
    rerender();
 | 
			
		||||
    rerender();
 | 
			
		||||
 | 
			
		||||
    expect(result).toMatchInlineSnapshot(`
 | 
			
		||||
        Object {
 | 
			
		||||
          "all": Array [
 | 
			
		||||
            "useId-0",
 | 
			
		||||
            "useId-0",
 | 
			
		||||
            "useId-0",
 | 
			
		||||
          ],
 | 
			
		||||
          "current": "useId-0",
 | 
			
		||||
          "error": undefined,
 | 
			
		||||
        }
 | 
			
		||||
    `);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test('useId prefix', () => {
 | 
			
		||||
    const { result, rerender } = renderHook(() => useId('prefix'));
 | 
			
		||||
 | 
			
		||||
    rerender();
 | 
			
		||||
    rerender();
 | 
			
		||||
 | 
			
		||||
    expect(result).toMatchInlineSnapshot(`
 | 
			
		||||
        Object {
 | 
			
		||||
          "all": Array [
 | 
			
		||||
            "prefix-1",
 | 
			
		||||
            "prefix-1",
 | 
			
		||||
            "prefix-1",
 | 
			
		||||
          ],
 | 
			
		||||
          "current": "prefix-1",
 | 
			
		||||
          "error": undefined,
 | 
			
		||||
        }
 | 
			
		||||
    `);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										10
									
								
								frontend/src/hooks/useId.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								frontend/src/hooks/useId.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
 | 
			
		||||
// Generate a globally unique ID that is stable across renders.
 | 
			
		||||
export const useId = (prefix = 'useId'): string => {
 | 
			
		||||
    return useMemo(() => {
 | 
			
		||||
        return `${prefix}-${counter++}`;
 | 
			
		||||
    }, [prefix]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let counter = 0;
 | 
			
		||||
							
								
								
									
										11
									
								
								frontend/src/utils/formatAccessText.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								frontend/src/utils/formatAccessText.test.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
import { formatAccessText } from 'utils/formatAccessText';
 | 
			
		||||
 | 
			
		||||
test('formatAccessText with access', () => {
 | 
			
		||||
    expect(formatAccessText(true)).toEqual(undefined);
 | 
			
		||||
    expect(formatAccessText(true, 'Foo')).toEqual('Foo');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test('formatAccessText without access', () => {
 | 
			
		||||
    expect(formatAccessText(false)).toEqual('Access denied');
 | 
			
		||||
    expect(formatAccessText(false, 'Foo')).toEqual(`Foo (Access denied)`);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										16
									
								
								frontend/src/utils/formatAccessText.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								frontend/src/utils/formatAccessText.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
export const ACCESS_DENIED_TEXT = 'Access denied';
 | 
			
		||||
 | 
			
		||||
export const formatAccessText = (
 | 
			
		||||
    hasAccess: boolean,
 | 
			
		||||
    hasAccessText?: string
 | 
			
		||||
): string | undefined => {
 | 
			
		||||
    if (hasAccess) {
 | 
			
		||||
        return hasAccessText;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (hasAccessText) {
 | 
			
		||||
        return `${hasAccessText} (${ACCESS_DENIED_TEXT})`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ACCESS_DENIED_TEXT;
 | 
			
		||||
};
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user