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

feat: add limit warning for feature flags (#7556)

This PR adds the Limit component to the feature flag creation form.

At the limit: 

![image](https://github.com/Unleash/unleash/assets/17786332/86f17565-5c75-4265-8e3b-5200222345ec)

Approaching the limit:

![image](https://github.com/Unleash/unleash/assets/17786332/af041d78-fcd3-4aa6-b415-9738cbfbae1b)

Below the limit threshold (no change):

![image](https://github.com/Unleash/unleash/assets/17786332/79ddc3ee-6e52-44d3-8d0b-0ebae90707a7)
This commit is contained in:
Thomas Heartman 2024-07-08 15:06:21 +02:00 committed by GitHub
parent 5ed4ccc981
commit 8f8ff13cc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 40 deletions

View File

@ -28,50 +28,66 @@ const setupApi = ({
});
};
test("should allow you to create feature flags when you're below the global limit", async () => {
setupApi({ flagLimit: 3, flagCount: 2 });
describe('button states', () => {
test("should allow you to create feature flags when you're below the global limit", async () => {
setupApi({ flagLimit: 3, flagCount: 2 });
render(
<Routes>
<Route
path='/projects/:projectId/create-toggle'
element={<CreateFeature />}
/>
</Routes>,
{
route: '/projects/default/create-toggle',
permissions: [{ permission: CREATE_FEATURE }],
},
);
render(
<Routes>
<Route
path='/projects/:projectId/create-toggle'
element={<CreateFeature />}
/>
</Routes>,
{
route: '/projects/default/create-toggle',
permissions: [{ permission: CREATE_FEATURE }],
},
);
await waitFor(async () => {
const button = await screen.findByRole('button', {
name: /create feature flag/i,
});
expect(button).not.toBeDisabled();
});
});
test("should not allow you to create API tokens when you're at the global limit", async () => {
setupApi({ flagLimit: 3, flagCount: 3 });
render(
<Routes>
<Route
path='/projects/:projectId/create-toggle'
element={<CreateFeature />}
/>
</Routes>,
{
route: '/projects/default/create-toggle',
permissions: [{ permission: CREATE_FEATURE }],
},
);
await waitFor(async () => {
const button = await screen.findByRole('button', {
name: /create feature flag/i,
await waitFor(() => {
expect(button).not.toBeDisabled();
});
expect(button).toBeDisabled();
});
});
describe('limit component', () => {
test('should show limit reached info', async () => {
setupApi({ flagLimit: 1, flagCount: 1 });
render(
<Routes>
<Route
path='/projects/:projectId/create-toggle'
element={<CreateFeature />}
/>
</Routes>,
{
route: '/projects/default/create-toggle',
permissions: [{ permission: CREATE_FEATURE }],
},
);
await screen.findByText('You have reached the limit for feature flags');
});
test('should show approaching limit info', async () => {
setupApi({ flagLimit: 10, flagCount: 9 });
render(
<Routes>
<Route
path='/projects/:projectId/create-toggle'
element={<CreateFeature />}
/>
</Routes>,
{
route: '/projects/default/create-toggle',
permissions: [{ permission: CREATE_FEATURE }],
},
);
await screen.findByText('You are nearing the limit for feature flags');
});
});

View File

@ -19,6 +19,7 @@ import useProjectOverview, {
} from 'hooks/api/getters/useProjectOverview/useProjectOverview';
import { useUiFlag } from 'hooks/useUiFlag';
import { useGlobalFeatureSearch } from '../FeatureToggleList/useGlobalFeatureSearch';
import { Limit } from 'component/common/Limit/Limit';
const StyledAlert = styled(Alert)(({ theme }) => ({
marginBottom: theme.spacing(2),
@ -106,6 +107,8 @@ const CreateFeature = () => {
const { total: totalFlags, loading: loadingTotalFlagCount } =
useGlobalFeatureSearch();
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { globalFlagLimitReached, projectFlagLimitReached, limitMessage } =
useFlagLimits({
global: {
@ -196,6 +199,18 @@ const CreateFeature = () => {
mode='Create'
clearErrors={clearErrors}
featureNaming={projectInfo.featureNaming}
Limit={
<ConditionallyRender
condition={resourceLimitsEnabled}
show={
<Limit
name='feature flags'
limit={uiConfig.resourceLimits.featureFlags}
currentValue={totalFlags ?? 0}
/>
}
/>
}
>
<CreateButton
name='feature flag'

View File

@ -7,6 +7,7 @@ import {
type Theme,
Typography,
Link,
Box,
} from '@mui/material';
import FeatureTypeSelect from '../FeatureView/FeatureSettings/FeatureSettingsMetadata/FeatureTypeSelect/FeatureTypeSelect';
import { CF_DESC_ID, CF_NAME_ID, CF_TYPE_ID } from 'utils/testIds';
@ -43,6 +44,7 @@ interface IFeatureToggleForm {
mode: 'Create' | 'Edit';
clearErrors: () => void;
children?: React.ReactNode;
Limit?: React.ReactNode;
}
const StyledForm = styled('form')({
@ -79,7 +81,6 @@ const StyledTypeDescription = styled('p')(({ theme }) => ({
}));
const StyledButtonContainer = styled('div')({
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
});
@ -98,6 +99,12 @@ const styledTypography = (theme: Theme) => ({
margin: theme.spacing(1, 0),
});
const LimitContainer = styled(Box)(({ theme }) => ({
'&:has(*)': {
marginBottom: theme.spacing(2),
},
}));
const FeatureForm: React.FC<IFeatureToggleForm> = ({
children,
type,
@ -117,6 +124,7 @@ const FeatureForm: React.FC<IFeatureToggleForm> = ({
errors,
mode,
clearErrors,
Limit,
}) => {
const { featureTypes } = useFeatureTypes();
const navigate = useNavigate();
@ -256,6 +264,7 @@ const FeatureForm: React.FC<IFeatureToggleForm> = ({
/>
</StyledRow>
</StyledFormControl>
<LimitContainer>{Limit}</LimitContainer>
<StyledButtonContainer>
{children}
<StyledCancelButton onClick={handleCancel}>