1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: convert environment actions to a popover menu, add clone option (#2214)

* feat: convert environment actions to a popover menu, add clone option

* add cloneEnviroment feature flag, hide the clone option behind it

* fix: update snap
This commit is contained in:
Nuno Góis 2022-10-21 08:11:14 +01:00 committed by GitHub
parent a1dc8339aa
commit d696863a51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 172 additions and 32 deletions

View File

@ -1,9 +1,5 @@
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
import {
DELETE_ENVIRONMENT,
UPDATE_ENVIRONMENT,
} from 'component/providers/AccessProvider/permissions';
import { Edit, Delete } from '@mui/icons-material';
import { UPDATE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
import { useNavigate } from 'react-router-dom';
import { useState } from 'react';
import { IEnvironment } from 'interfaces/environments';
@ -15,7 +11,7 @@ import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissio
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
import useToast from 'hooks/useToast';
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { EnvironmentActionCellPopover } from './EnvironmentActionCellPopover/EnvironmentActionCellPopover';
interface IEnvironmentTableActionsProps {
environment: IEnvironment;
@ -103,32 +99,12 @@ export const EnvironmentActionCell = ({
onClick={() => setToggleModal(true)}
/>
<ActionCell.Divider />
<PermissionIconButton
permission={UPDATE_ENVIRONMENT}
disabled={environment.protected}
size="large"
tooltipProps={{
title: environment.protected
? 'You cannot edit protected environment'
: 'Edit environment',
}}
onClick={() => navigate(`/environments/${environment.name}`)}
>
<Edit />
</PermissionIconButton>
<PermissionIconButton
permission={DELETE_ENVIRONMENT}
disabled={environment.protected}
size="large"
tooltipProps={{
title: environment.protected
? 'You cannot delete protected environment'
: 'Delete environment',
}}
onClick={() => setDeleteModal(true)}
>
<Delete />
</PermissionIconButton>
<EnvironmentActionCellPopover
environment={environment}
onEdit={() => navigate(`/environments/${environment.name}`)}
onClone={() => console.log('TODO: CLONE')}
onDelete={() => setDeleteModal(true)}
/>
<EnvironmentDeleteConfirm
env={environment}
setDeldialogue={setDeleteModal}

View File

@ -0,0 +1,154 @@
import {
IconButton,
ListItemIcon,
ListItemText,
MenuItem,
MenuList,
Popover,
styled,
Tooltip,
Typography,
} from '@mui/material';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { useState } from 'react';
import { IEnvironment } from 'interfaces/environments';
import { PermissionHOC } from 'component/common/PermissionHOC/PermissionHOC';
import {
ADMIN,
DELETE_ENVIRONMENT,
UPDATE_ENVIRONMENT,
} from 'component/providers/AccessProvider/permissions';
import { Delete, Edit, AddToPhotos as CopyIcon } from '@mui/icons-material';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
const StyledMenuList = styled(MenuList)(({ theme }) => ({
padding: theme.spacing(1),
}));
const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
borderRadius: theme.shape.borderRadius,
}));
interface IEnvironmentActionCellPopoverProps {
environment: IEnvironment;
onEdit: () => void;
onClone: () => void;
onDelete: () => void;
}
export const EnvironmentActionCellPopover = ({
environment,
onEdit,
onClone,
onDelete,
}: IEnvironmentActionCellPopoverProps) => {
const { uiConfig } = useUiConfig();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const id = `environment-${environment.name}-actions`;
const menuId = `${id}-menu`;
return (
<>
<Tooltip title="Environment actions" arrow describeChild>
<IconButton
id={id}
aria-controls={open ? menuId : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
type="button"
>
<MoreVertIcon />
</IconButton>
</Tooltip>
<Popover
id={menuId}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
disableScrollLock={true}
>
<StyledMenuList aria-labelledby={id}>
<PermissionHOC permission={UPDATE_ENVIRONMENT}>
{({ hasAccess }) => (
<StyledMenuItem
onClick={() => {
onEdit();
handleClose();
}}
disabled={!hasAccess || environment.protected}
>
<ListItemIcon>
<Edit />
</ListItemIcon>
<ListItemText>
<Typography variant="body2">
Edit
</Typography>
</ListItemText>
</StyledMenuItem>
)}
</PermissionHOC>
<ConditionallyRender
condition={Boolean(uiConfig.flags.cloneEnvironment)}
show={
<PermissionHOC permission={ADMIN}>
{({ hasAccess }) => (
<StyledMenuItem
onClick={() => {
onClone();
handleClose();
}}
disabled={!hasAccess}
>
<ListItemIcon>
<CopyIcon />
</ListItemIcon>
<ListItemText>
<Typography variant="body2">
Clone
</Typography>
</ListItemText>
</StyledMenuItem>
)}
</PermissionHOC>
}
/>
<PermissionHOC permission={DELETE_ENVIRONMENT}>
{({ hasAccess }) => (
<StyledMenuItem
onClick={() => {
onDelete();
handleClose();
}}
disabled={!hasAccess || environment.protected}
>
<ListItemIcon>
<Delete />
</ListItemIcon>
<ListItemText>
<Typography variant="body2">
Delete
</Typography>
</ListItemText>
</StyledMenuItem>
)}
</PermissionHOC>
</StyledMenuList>
</Popover>
</>
);
};

View File

@ -44,6 +44,7 @@ export interface IFlags {
personalAccessTokens?: boolean;
syncSSOGroups?: boolean;
suggestChanges?: boolean;
cloneEnvironment?: boolean;
}
export interface IVersionInfo {

View File

@ -69,6 +69,7 @@ exports[`should create default config 1`] = `
"ENABLE_DARK_MODE_SUPPORT": false,
"anonymiseEventLog": false,
"batchMetrics": false,
"cloneEnvironment": false,
"embedProxy": false,
"embedProxyFrontend": false,
"personalAccessTokens": false,
@ -82,6 +83,7 @@ exports[`should create default config 1`] = `
"ENABLE_DARK_MODE_SUPPORT": false,
"anonymiseEventLog": false,
"batchMetrics": false,
"cloneEnvironment": false,
"embedProxy": false,
"embedProxyFrontend": false,
"personalAccessTokens": false,

View File

@ -34,6 +34,10 @@ export const defaultExperimentalOptions = {
process.env.UNLEASH_EXPERIMENTAL_RESPONSE_TIME_WITH_APP_NAME,
false,
),
cloneEnvironment: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_CLONE_ENVIRONMENT,
false,
),
},
externalResolver: { isEnabled: (): boolean => false },
};
@ -48,6 +52,7 @@ export interface IExperimentalOptions {
anonymiseEventLog?: boolean;
personalAccessTokens?: boolean;
syncSSOGroups?: boolean;
cloneEnvironment?: boolean;
};
externalResolver: IExternalFlagResolver;
}

View File

@ -41,6 +41,7 @@ process.nextTick(async () => {
personalAccessTokens: true,
syncSSOGroups: true,
suggestChanges: true,
cloneEnvironment: true,
},
},
authentication: {

View File

@ -29,6 +29,7 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig {
batchMetrics: true,
personalAccessTokens: true,
syncSSOGroups: true,
cloneEnvironment: true,
},
},
};