mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02: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,24 +201,28 @@ export const ApiTokenList = () => {
|
||||
<Secret value={item.secret} />
|
||||
</TableCell>
|
||||
<TableCell className={styles.actionsContainer}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
copyToken(item.secret);
|
||||
}}
|
||||
>
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
<Tooltip title="Copy token">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
copyToken(item.secret);
|
||||
}}
|
||||
>
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<ConditionallyRender
|
||||
condition={hasAccess(DELETE_API_TOKEN)}
|
||||
show={
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setDeleteToken(item);
|
||||
setShowDelete(true);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<Tooltip title="Delete token">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setDeleteToken(item);
|
||||
setShowDelete(true);
|
||||
}}
|
||||
>
|
||||
<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}
|
||||
<IconButton onClick={copyToken}>
|
||||
<CopyIcon />
|
||||
</IconButton>
|
||||
<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}
|
||||
<IconButton onClick={handleCopy}>
|
||||
<CopyIcon />
|
||||
</IconButton>
|
||||
<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,14 +122,16 @@ export const ConstraintAccordionEditHeader = ({
|
||||
show={<p className={styles.editingBadge}>Updating...</p>}
|
||||
elseShow={<p className={styles.editingBadge}>Editing</p>}
|
||||
/>
|
||||
<a
|
||||
href="https://docs.getunleash.io/advanced/strategy_constraints"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Help className={styles.help} />
|
||||
</a>
|
||||
<Tooltip title="Help">
|
||||
<a
|
||||
href="https://docs.getunleash.io/advanced/strategy_constraints"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<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{' '}
|
||||
<IconButton onClick={copyCommand}>
|
||||
<FileCopy className={styles.icon} />
|
||||
</IconButton>
|
||||
<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>
|
||||
<IconButton
|
||||
className={styles.mobileGuidanceButton}
|
||||
onClick={() => setOpen(prev => !prev)}
|
||||
>
|
||||
<Info className={styles.infoIcon} />
|
||||
</IconButton>
|
||||
<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,14 +76,15 @@ const Toast = ({ title, text, type, confetti }: IToast) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={hide}
|
||||
className={styles.buttonStyle}
|
||||
>
|
||||
<Close />
|
||||
</IconButton>
|
||||
<Tooltip title="Close">
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={hide}
|
||||
className={styles.buttonStyle}
|
||||
>
|
||||
<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" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<IconButton>
|
||||
<DragIndicator titleAccess="Drag" cursor="grab" />
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={updatePermission}
|
||||
show={
|
||||
<Tooltip title={`${tooltipText} environment`}>
|
||||
<div>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setSelectedEnv(env);
|
||||
setToggleDialog(prev => !prev);
|
||||
}}
|
||||
>
|
||||
<OfflineBolt titleAccess="Toggle" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setSelectedEnv(env);
|
||||
setToggleDialog(prev => !prev);
|
||||
}}
|
||||
>
|
||||
<OfflineBolt />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={updatePermission}
|
||||
show={
|
||||
<Tooltip title="Update environment">
|
||||
<div>
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
history.push(`/environments/${env.name}`);
|
||||
}}
|
||||
>
|
||||
<Edit titleAccess="Edit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<Tooltip title="Edit environment">
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
history.push(`/environments/${env.name}`);
|
||||
}}
|
||||
>
|
||||
<Edit />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
@ -202,17 +194,15 @@ const EnvironmentListItem = ({
|
||||
condition={hasAccess(DELETE_ENVIRONMENT)}
|
||||
show={
|
||||
<Tooltip title="Delete environment">
|
||||
<div>
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
setDeldialogue(true);
|
||||
setSelectedEnv(env);
|
||||
}}
|
||||
>
|
||||
<Delete titleAccess="Delete" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
setDeldialogue(true);
|
||||
setSelectedEnv(env);
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</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}>
|
||||
<IconButton onClick={removeOverride(i)}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<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,24 +60,28 @@ const FeatureVariantListItem = ({
|
||||
show={
|
||||
<TableCell className={styles.actions}>
|
||||
<div className={styles.actionsContainer}>
|
||||
<IconButton
|
||||
data-testid={'VARIANT_EDIT_BUTTON'}
|
||||
onClick={() => editVariant(variant.name)}
|
||||
>
|
||||
<Edit />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
data-testid={`VARIANT_DELETE_BUTTON_${variant.name}`}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setDelDialog({
|
||||
show: true,
|
||||
name: variant.name,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<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 => {
|
||||
e.stopPropagation();
|
||||
setDelDialog({
|
||||
show: true,
|
||||
name: variant.name,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<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
|
||||
)}
|
||||
>
|
||||
<IconButton
|
||||
className={styles.close}
|
||||
onClick={() => setShowFeedback(false)}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<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={
|
||||
<IconButton
|
||||
className={styles.drawerButton}
|
||||
onClick={toggleDrawer}
|
||||
>
|
||||
<MenuIcon titleAccess="Menu" />
|
||||
</IconButton>
|
||||
<Tooltip title="Menu">
|
||||
<IconButton
|
||||
className={styles.drawerButton}
|
||||
onClick={toggleDrawer}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
elseShow={
|
||||
<Link
|
||||
@ -136,26 +138,29 @@ const Header = () => {
|
||||
>
|
||||
<MenuBookIcon
|
||||
className={styles.docsIcon}
|
||||
titleAccess="Documentation"
|
||||
/>
|
||||
</a>
|
||||
</Tooltip>
|
||||
<ConditionallyRender
|
||||
condition={admin}
|
||||
show={
|
||||
<IconButton
|
||||
onClick={e =>
|
||||
setAnchorEl(e.currentTarget)
|
||||
}
|
||||
>
|
||||
<SettingsIcon
|
||||
className={styles.docsIcon}
|
||||
titleAccess="Settings"
|
||||
/>
|
||||
</IconButton>
|
||||
<Tooltip title="Settings">
|
||||
<IconButton
|
||||
onClick={e =>
|
||||
setAnchorEl(
|
||||
e.currentTarget
|
||||
)
|
||||
}
|
||||
>
|
||||
<SettingsIcon
|
||||
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}
|
||||
>
|
||||
<VisibilityOff titleAccess="Reactivate" />
|
||||
</PermissionIconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<PermissionIconButton
|
||||
onClick={() => onReactivateStrategy(strategy)}
|
||||
permission={UPDATE_STRATEGY}
|
||||
tooltip="Reactivate activation strategy"
|
||||
>
|
||||
<VisibilityOff />
|
||||
</PermissionIconButton>
|
||||
);
|
||||
|
||||
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}`]}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setParams(params.filter((e, i) => i !== index));
|
||||
}}
|
||||
>
|
||||
<Delete titleAccess="Delete" />
|
||||
</IconButton>
|
||||
<Tooltip title="Remove parameter">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setParams(params.filter((e, i) => i !== index));
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
<ListItemAvatar>
|
||||
<Tooltip title="Required parameter">
|
||||
<Add aria-hidden={false} />
|
||||
</Tooltip>
|
||||
</ListItemAvatar>
|
||||
}
|
||||
elseShow={
|
||||
<Tooltip title="Optional">
|
||||
<ListItemAvatar>
|
||||
<RadioButtonChecked />
|
||||
</ListItemAvatar>
|
||||
</Tooltip>
|
||||
<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,30 +19,33 @@ export const TogglesLinkList = ({ toggles }: ITogglesLinkListProps) => (
|
||||
<List style={{ textAlign: 'left' }} className={styles.truncate}>
|
||||
<ConditionallyRender
|
||||
condition={toggles.length > 0}
|
||||
show={
|
||||
<>
|
||||
{toggles.map(({ name, description = '-', enabled }) => (
|
||||
<ListItem key={name}>
|
||||
<Tooltip title={enabled ? 'Enabled' : 'Disabled'}>
|
||||
<ListItemAvatar>
|
||||
{enabled ? <PlayArrow /> : <Pause />}
|
||||
</ListItemAvatar>
|
||||
</Tooltip>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Link
|
||||
key={name}
|
||||
to={`/features/view/${name}`}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
}
|
||||
secondary={description}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
show={toggles.map(({ name, description = '-', enabled }) => (
|
||||
<ListItem key={name}>
|
||||
<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}`}>
|
||||
{name}
|
||||
</Link>
|
||||
}
|
||||
secondary={description}
|
||||
/>
|
||||
</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