mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
task: Add buttons for deleting/editing a constraint (#522)
* task: Add buttons for deleting/editing a constraint * task: Improve look and feel of constraints buttons - Make constraints fill their container - Move constraint buttons to material ui buttons - Move constraint buttons to top right of their container * fix: adjust positioning * fix: added project id to permissin button * fix: add correct permission * fix: update create feature path Co-authored-by: Simon Hornby <simon@getunleash.ai> Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com>
This commit is contained in:
parent
3e53a64fcf
commit
b85b326104
@ -82,7 +82,9 @@ describe('feature toggle', () => {
|
||||
|
||||
cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click();
|
||||
|
||||
cy.intercept('POST', '/api/admin/features').as('createFeature');
|
||||
cy.intercept('POST', '/api/admin/projects/default/features').as(
|
||||
'createFeature'
|
||||
);
|
||||
|
||||
cy.get("[data-test='CF_NAME_ID'").type(featureToggleName);
|
||||
cy.get("[data-test='CF_DESC_ID'").type('hellowrdada');
|
||||
|
@ -33,6 +33,7 @@
|
||||
"test": "react-scripts test",
|
||||
"prepare": "yarn run build",
|
||||
"e2e": "yarn run cypress open --config baseUrl='http://localhost:3000' --env PASSWORD_AUTH=true,AUTH_TOKEN=$AUTH_TOKEN",
|
||||
"e2e:heroku": "yarn run cypress open --config baseUrl='http://localhost:3000' --env PASSWORD_AUTH=false,AUTH_TOKEN=$AUTH_TOKEN",
|
||||
"e2e:enterprise": "yarn run cypress open --config baseUrl='http://localhost:3000' --env PASSWORD_AUTH=true,ENTERPRISE=true,AUTH_TOKEN=$AUTH_TOKEN"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -11,11 +11,21 @@ export const useStyles = makeStyles(theme => ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '0.1rem 0.5rem',
|
||||
border: `1px solid ${theme.palette.grey[300]}`,
|
||||
borderRadius: '5px',
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
margin: '0.5rem 0',
|
||||
position: 'relative',
|
||||
borderRadius: '5px',
|
||||
},
|
||||
constraintBtn: {
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: 'normal',
|
||||
marginBottom: '0.5rem',
|
||||
},
|
||||
btnContainer: {
|
||||
position: 'absolute',
|
||||
top: '6px',
|
||||
right: 0,
|
||||
},
|
||||
column: {
|
||||
flexDirection: 'column',
|
||||
|
@ -1,31 +1,75 @@
|
||||
import { Delete, Edit } from '@material-ui/icons';
|
||||
import classnames from 'classnames';
|
||||
import { useParams } from 'react-router';
|
||||
import { IFeatureViewParams } from '../../../interfaces/params';
|
||||
import { IConstraint } from '../../../interfaces/strategy';
|
||||
import FeatureStrategiesSeparator from '../../feature/FeatureView2/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesSeparator/FeatureStrategiesSeparator';
|
||||
import { UPDATE_FEATURE } from '../../providers/AccessProvider/permissions';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import PermissionIconButton from '../PermissionIconButton/PermissionIconButton';
|
||||
import StringTruncator from '../StringTruncator/StringTruncator';
|
||||
import { useStyles } from './Constraint.styles';
|
||||
|
||||
interface IConstraintProps {
|
||||
constraint: IConstraint;
|
||||
className?: string;
|
||||
deleteCallback?: () => void;
|
||||
editCallback?: () => void;
|
||||
}
|
||||
|
||||
const Constraint = ({ constraint, className, ...rest }: IConstraintProps) => {
|
||||
const Constraint = ({
|
||||
constraint,
|
||||
deleteCallback,
|
||||
editCallback,
|
||||
className,
|
||||
...rest
|
||||
}: IConstraintProps) => {
|
||||
const styles = useStyles();
|
||||
const { projectId } = useParams<IFeatureViewParams>();
|
||||
|
||||
const classes = classnames(styles.constraint, {
|
||||
[styles.column]: constraint.values.length > 2,
|
||||
});
|
||||
|
||||
const editable = !!(deleteCallback && editCallback);
|
||||
|
||||
return (
|
||||
<div className={classes + ' ' + className} {...rest}>
|
||||
<StringTruncator text={constraint.contextName} maxWidth="125" />
|
||||
<FeatureStrategiesSeparator
|
||||
text={constraint.operator}
|
||||
maxWidth="none"
|
||||
<div className={classes + ' ' + className} {...rest}>
|
||||
<StringTruncator text={constraint.contextName} maxWidth="125" />
|
||||
<FeatureStrategiesSeparator
|
||||
text={constraint.operator}
|
||||
maxWidth="none"
|
||||
/>
|
||||
<span className={styles.values}>
|
||||
{constraint.values.join(', ')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={editable}
|
||||
show={
|
||||
<div className={styles.btnContainer}>
|
||||
<PermissionIconButton
|
||||
onClick={editCallback}
|
||||
tooltip="Edit strategy"
|
||||
permission={UPDATE_FEATURE}
|
||||
projectId={projectId}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
|
||||
<PermissionIconButton
|
||||
onClick={deleteCallback}
|
||||
tooltip="Delete strategy"
|
||||
permission={UPDATE_FEATURE}
|
||||
projectId={projectId}
|
||||
>
|
||||
<Delete />
|
||||
</PermissionIconButton>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<span className={styles.values}>
|
||||
{constraint.values.join(', ')}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import AccessContext from '../../../contexts/AccessContext';
|
||||
|
||||
interface IPermissionIconButtonProps extends OverridableComponent<any> {
|
||||
permission: string;
|
||||
Icon: React.ElementType;
|
||||
Icon?: React.ElementType;
|
||||
tooltip: string;
|
||||
onClick?: (e: any) => void;
|
||||
projectId?: string;
|
||||
|
@ -7,7 +7,8 @@ export const useStyles = makeStyles(theme => ({
|
||||
marginBottom: '0.5rem',
|
||||
},
|
||||
accordionContainer: {
|
||||
width: '80%',
|
||||
width: '100%',
|
||||
paddingRight: '37px',
|
||||
[theme.breakpoints.down(800)]: {
|
||||
width: '100%',
|
||||
},
|
||||
@ -20,7 +21,4 @@ export const useStyles = makeStyles(theme => ({
|
||||
marginTop: '0.5rem',
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
},
|
||||
constraintBody: {
|
||||
maxWidth: '350px',
|
||||
},
|
||||
}));
|
||||
|
@ -12,7 +12,6 @@ import { useContext, useState } from 'react';
|
||||
import ConditionallyRender from '../../../../../common/ConditionallyRender';
|
||||
import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { C } from '../../../../../common/flags';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { useStyles } from './FeatureStrategyAccordionBody.styles';
|
||||
import Dialogue from '../../../../../common/Dialogue';
|
||||
import DefaultStrategy from '../../common/DefaultStrategy/DefaultStrategy';
|
||||
@ -20,6 +19,9 @@ import { ADD_CONSTRAINT_ID } from '../../../../../../testIds';
|
||||
import AccessContext from '../../../../../../contexts/AccessContext';
|
||||
import { UPDATE_FEATURE } from '../../../../../providers/AccessProvider/permissions';
|
||||
import Constraint from '../../../../../common/Constraint/Constraint';
|
||||
import PermissionButton from '../../../../../common/PermissionButton/PermissionButton';
|
||||
import { useParams } from 'react-router';
|
||||
import { IFeatureViewParams } from '../../../../../../interfaces/params';
|
||||
|
||||
interface IFeatureStrategyAccordionBodyProps {
|
||||
strategy: IFeatureStrategy;
|
||||
@ -40,6 +42,7 @@ const FeatureStrategyAccordionBody: React.FC<IFeatureStrategyAccordionBodyProps>
|
||||
setStrategyConstraints,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const { projectId } = useParams<IFeatureViewParams>();
|
||||
const [constraintError, setConstraintError] = useState({});
|
||||
const { strategies } = useStrategies();
|
||||
const { uiConfig } = useUiConfig();
|
||||
@ -106,13 +109,25 @@ const FeatureStrategyAccordionBody: React.FC<IFeatureStrategyAccordionBodyProps>
|
||||
return (
|
||||
<Constraint
|
||||
constraint={constraint}
|
||||
editCallback={() => {
|
||||
setShowConstraints(true);
|
||||
}}
|
||||
deleteCallback={() => {
|
||||
removeConstraint(index);
|
||||
}}
|
||||
key={`${constraint.contextName}-${index}`}
|
||||
className={styles.constraintBody}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const removeConstraint = (index: number) => {
|
||||
const updatedConstraints = [...constraints];
|
||||
updatedConstraints.splice(index, 1);
|
||||
|
||||
updateConstraints(updatedConstraints);
|
||||
};
|
||||
|
||||
const closeConstraintDialog = () => {
|
||||
setShowConstraints(false);
|
||||
const filteredConstraints = constraints.filter(constraint => {
|
||||
@ -137,18 +152,17 @@ const FeatureStrategyAccordionBody: React.FC<IFeatureStrategyAccordionBodyProps>
|
||||
Constraints
|
||||
</p>
|
||||
{renderConstraints()}
|
||||
<ConditionallyRender
|
||||
condition={hasAccess(UPDATE_FEATURE)}
|
||||
show={
|
||||
<Button
|
||||
className={styles.addConstraintBtn}
|
||||
onClick={toggleConstraints}
|
||||
data-test={ADD_CONSTRAINT_ID}
|
||||
>
|
||||
+ Edit constraints
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<PermissionButton
|
||||
className={styles.addConstraintBtn}
|
||||
onClick={toggleConstraints}
|
||||
variant={'text'}
|
||||
data-test={ADD_CONSTRAINT_ID}
|
||||
permission={UPDATE_FEATURE}
|
||||
projectId={projectId}
|
||||
>
|
||||
+ Add constraints
|
||||
</PermissionButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user