From 8f8ff13cc538aa0bde22eafd27289aa08f53b7eb Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Mon, 8 Jul 2024 15:06:21 +0200 Subject: [PATCH] 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) --- .../CreateFeature/CreateFeature.test.tsx | 94 +++++++++++-------- .../feature/CreateFeature/CreateFeature.tsx | 15 +++ .../feature/FeatureForm/FeatureForm.tsx | 11 ++- 3 files changed, 80 insertions(+), 40 deletions(-) diff --git a/frontend/src/component/feature/CreateFeature/CreateFeature.test.tsx b/frontend/src/component/feature/CreateFeature/CreateFeature.test.tsx index 2c3dae6ed1..6215809f4d 100644 --- a/frontend/src/component/feature/CreateFeature/CreateFeature.test.tsx +++ b/frontend/src/component/feature/CreateFeature/CreateFeature.test.tsx @@ -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( - - } - /> - , - { - route: '/projects/default/create-toggle', - permissions: [{ permission: CREATE_FEATURE }], - }, - ); + render( + + } + /> + , + { + 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( - - } - /> - , - { - 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( + + } + /> + , + { + 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( + + } + /> + , + { + route: '/projects/default/create-toggle', + permissions: [{ permission: CREATE_FEATURE }], + }, + ); + + await screen.findByText('You are nearing the limit for feature flags'); }); }); diff --git a/frontend/src/component/feature/CreateFeature/CreateFeature.tsx b/frontend/src/component/feature/CreateFeature/CreateFeature.tsx index c2b3930ff2..52811c5833 100644 --- a/frontend/src/component/feature/CreateFeature/CreateFeature.tsx +++ b/frontend/src/component/feature/CreateFeature/CreateFeature.tsx @@ -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={ + + } + /> + } > 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 = ({ children, type, @@ -117,6 +124,7 @@ const FeatureForm: React.FC = ({ errors, mode, clearErrors, + Limit, }) => { const { featureTypes } = useFeatureTypes(); const navigate = useNavigate(); @@ -256,6 +264,7 @@ const FeatureForm: React.FC = ({ /> + {Limit} {children}