diff --git a/frontend/package.json b/frontend/package.json index 56c1b21f63..8c25343afa 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "unleash-frontend", "description": "unleash your features", - "version": "4.3.1", + "version": "4.3.0", "keywords": [ "unleash", "feature toggle", @@ -40,7 +40,7 @@ "@material-ui/core": "4.12.3", "@material-ui/icons": "4.11.2", "@material-ui/lab": "4.0.0-alpha.60", - "@testing-library/jest-dom": "5.16.1", + "@testing-library/jest-dom": "5.16.0", "@testing-library/react": "12.1.2", "@testing-library/user-event": "13.5.0", "@types/debounce": "1.2.1", diff --git a/frontend/src/component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog.styles.ts b/frontend/src/component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog.styles.ts new file mode 100644 index 0000000000..5c846a051b --- /dev/null +++ b/frontend/src/component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog.styles.ts @@ -0,0 +1,8 @@ +import { makeStyles } from '@material-ui/core/styles'; + +export const useStyles = makeStyles(theme => ({ + infoText: { + marginBottom: '10px', + fontSize: theme.fontSizes.bodySize, + }, +})); diff --git a/frontend/src/component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog.tsx b/frontend/src/component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog.tsx new file mode 100644 index 0000000000..ae2210910e --- /dev/null +++ b/frontend/src/component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import Dialogue from '../Dialogue'; +import { useStyles } from './EnvironmentStrategyDialog.styles'; + +interface IEnvironmentStrategyDialogProps { + open: boolean; + featureId: string; + projectId: string; + onClose: () => void; + environmentName?: string; +} +const EnvironmentStrategyDialog = ({ + open, + environmentName, + featureId, + projectId, + onClose, +}: IEnvironmentStrategyDialogProps) => { + const styles = useStyles(); + const history = useHistory(); + const strategiesLink = `/projects/${projectId}/features2/${featureId}/strategies?environment=${environmentName}&addStrategy=true`; + + return ( + history.push(strategiesLink)} + onClose={() => onClose()} + title="You need to add a strategy to your toggle" + primaryButtonText="Take me directly to add strategy" + secondaryButtonText="Cancel" + > +

+ Before you can enable the toggle in the environment, you need to + add an activation strategy. +

+

+ You can add the activation strategy by selecting the toggle, open + the environment accordion and add the activation strategy. +

+
+ ); +}; + +export default EnvironmentStrategyDialog; diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts index cd7b318748..9834c942ab 100644 --- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts +++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts @@ -48,11 +48,4 @@ export const useStyles = makeStyles(theme => ({ textDecoration: 'none', color: 'inherit', }, - envName: { - display: 'inline-block', - width: '90px', - textOverflow: 'ellipsis', - overflow: 'hidden', - whiteSpace: 'nowrap', - }, })); diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx index 58c5546e94..9b6b3f73cf 100644 --- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx +++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx @@ -17,6 +17,7 @@ import { IFeatureToggleListItem, } from '../../../interfaces/featureToggle'; import PaginateUI from '../../common/PaginateUI/PaginateUI'; +import StringTruncator from '../../common/StringTruncator/StringTruncator'; interface IFeatureToggleListNewProps { features: IFeatureToggleListItem[]; loading: boolean; @@ -236,12 +237,11 @@ const FeatureToggleListNew = ({ )} align="center" > - - {env.name} - + /> ); })} diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx index 98b103f95e..e8f9118c71 100644 --- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx +++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx @@ -1,4 +1,4 @@ -import { useRef } from 'react'; +import { useRef, useState } from 'react'; import { TableCell, TableRow } from '@material-ui/core'; import { useHistory } from 'react-router'; @@ -17,6 +17,8 @@ import useProject from '../../../../hooks/api/getters/useProject/useProject'; import { UPDATE_FEATURE } from '../../../providers/AccessProvider/permissions'; import PermissionSwitch from '../../../common/PermissionSwitch/PermissionSwitch'; import { Link } from 'react-router-dom'; +import { ENVIRONMENT_STRATEGY_ERROR } from '../../../../constants/apiErrors'; +import EnvironmentStrategyDialog from '../../../common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog'; interface IFeatureToggleListNewItemProps { name: string; @@ -46,6 +48,12 @@ const FeatureToggleListNewItem = ({ const styles = useStyles(); const history = useHistory(); const ref = useRef(null); + const [showInfoBox, setShowInfoBox] = useState(false); + const [environmentName, setEnvironmentName] = useState(''); + + const closeInfoBox = () => { + setShowInfoBox(false); + }; const onClick = (e: SyntheticEvent) => { if (!ref.current?.contains(e.target)) { @@ -53,7 +61,7 @@ const FeatureToggleListNewItem = ({ } }; - const handleToggle = (env: IEnvironments, e: SyntheticEvent) => { + const handleToggle = (env: IEnvironments) => { toggleFeatureByEnvironment(env.name, env.enabled) .then(() => { setToastData({ @@ -64,11 +72,15 @@ const FeatureToggleListNewItem = ({ refetch(); }) .catch(e => { - setToastData({ - show: true, - type: 'error', - text: e.message, - }); + if (e.message === ENVIRONMENT_STRATEGY_ERROR) { + setShowInfoBox(true); + } else { + setToastData({ + show: true, + type: 'error', + text: e.message, + }); + } }); }; @@ -140,7 +152,10 @@ const FeatureToggleListNewItem = ({ projectId={projectId} permission={UPDATE_FEATURE} ref={ref} - onClick={handleToggle.bind(this, env)} + onClick={() => { + handleToggle(env); + setEnvironmentName(env.name); + }} /> @@ -148,6 +163,13 @@ const FeatureToggleListNewItem = ({ })} {toast} + ); }; diff --git a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx index a3e7ca0d77..703c763630 100644 --- a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx +++ b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx @@ -1,4 +1,5 @@ import { useParams } from 'react-router'; +import { ENVIRONMENT_STRATEGY_ERROR } from '../../../../../../constants/apiErrors'; import useFeatureApi from '../../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature'; import { TSetToastData } from '../../../../../../hooks/useToast'; @@ -13,6 +14,7 @@ interface IFeatureOverviewEnvSwitchProps { setToastData: TSetToastData; callback?: () => void; text?: string; + showInfoBox?: () => void; } const FeatureOverviewEnvSwitch = ({ @@ -20,6 +22,7 @@ const FeatureOverviewEnvSwitch = ({ setToastData, callback, text, + showInfoBox, }: IFeatureOverviewEnvSwitchProps) => { const { featureId, projectId } = useParams(); const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } = @@ -39,11 +42,15 @@ const FeatureOverviewEnvSwitch = ({ callback(); } } catch (e: any) { - setToastData({ - show: true, - type: 'error', - text: e.toString(), - }); + if (e.message === ENVIRONMENT_STRATEGY_ERROR) { + showInfoBox(true); + } else { + setToastData({ + show: true, + type: 'error', + text: e.message, + }); + } } }; diff --git a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitches.tsx b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitches.tsx index 01449a993a..a5f2f71613 100644 --- a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitches.tsx +++ b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitches.tsx @@ -1,9 +1,11 @@ import { Tooltip } from '@material-ui/core'; +import { useState } from 'react'; import { useParams } from 'react-router'; import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature'; import useToast from '../../../../../hooks/useToast'; import { IFeatureViewParams } from '../../../../../interfaces/params'; +import EnvironmentStrategyDialog from '../../../../common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog'; import FeatureOverviewEnvSwitch from './FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch'; import { useStyles } from './FeatureOverviewEnvSwitches.styles'; @@ -13,6 +15,12 @@ const FeatureOverviewEnvSwitches = () => { useFeatureApi(); const { feature } = useFeature(projectId, featureId); const { toast, setToastData } = useToast(); + const [showInfoBox, setShowInfoBox] = useState(false); + const [environmentName, setEnvironmentName] = useState(''); + + const closeInfoBox = () => { + setShowInfoBox(false); + }; const renderEnvironmentSwitches = () => { return feature?.environments.map(env => { @@ -21,6 +29,10 @@ const FeatureOverviewEnvSwitches = () => { key={env.name} env={env} setToastData={setToastData} + showInfoBox={() => { + setEnvironmentName(env.name); + setShowInfoBox(true); + }} /> ); }); @@ -38,6 +50,13 @@ const FeatureOverviewEnvSwitches = () => { {renderEnvironmentSwitches()} {toast} + ); }; diff --git a/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironments.tsx b/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironments.tsx index dfe87e7d6f..1affd1eb1d 100644 --- a/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironments.tsx +++ b/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironments.tsx @@ -62,7 +62,6 @@ const FeatureStrategiesEnvironments = () => { useEffect(() => { if (addStrategy) { setExpandedSidebar(true); - history.replace(history.location.pathname); } if (!feature) return; diff --git a/frontend/src/constants/apiErrors.ts b/frontend/src/constants/apiErrors.ts new file mode 100644 index 0000000000..8703fabfbf --- /dev/null +++ b/frontend/src/constants/apiErrors.ts @@ -0,0 +1 @@ +export const ENVIRONMENT_STRATEGY_ERROR = 'You can not enable the environment before it has strategies'; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 07bf1f790c..cd10576188 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1886,10 +1886,10 @@ lz-string "^1.4.4" pretty-format "^27.0.2" -"@testing-library/jest-dom@5.16.1": - version "5.16.1" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.1.tgz#3db7df5ae97596264a7da9696fe14695ba02e51f" - integrity sha512-ajUJdfDIuTCadB79ukO+0l8O+QwN0LiSxDaYUTI4LndbbUsGi6rWU1SCexXzBA2NSjlVB9/vbkasQIL3tmPBjw== +"@testing-library/jest-dom@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.0.tgz#de1a7c5fedfeb80eb2be9fc81f61473973b302b3" + integrity sha512-ECygvCL6ufPfHna4fsk7o24+3PVNhRbioDpFbfSVEZaglT6EjuRP+w8I5tzigFz1fobpvCrVRoKyR4qx2QUCxw== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1"