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