From 6f4f6f049bee298780178273d852affed4866d71 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 27 Sep 2023 10:09:38 +0200 Subject: [PATCH] feat: inject project id to dependencies hooks (#4839) --- .../feature/Dependencies/AddDependency.tsx | 60 ----------- .../AddDependencyDialogue.test.tsx | 102 ++++++++++++++++++ .../Dependencies/AddDependencyDialogue.tsx | 14 ++- .../FeatureOverviewSidePanelDetails.test.tsx | 2 +- .../FeatureOverviewSidePanelDetails.tsx | 18 ++-- .../useDependentFeaturesApi.ts | 8 +- .../useParentOptions/useParentOptions.ts | 3 +- 7 files changed, 131 insertions(+), 76 deletions(-) delete mode 100644 frontend/src/component/feature/Dependencies/AddDependency.tsx create mode 100644 frontend/src/component/feature/Dependencies/AddDependencyDialogue.test.tsx diff --git a/frontend/src/component/feature/Dependencies/AddDependency.tsx b/frontend/src/component/feature/Dependencies/AddDependency.tsx deleted file mode 100644 index af8af91f20..0000000000 --- a/frontend/src/component/feature/Dependencies/AddDependency.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Box, styled } from '@mui/material'; -import { trim } from '../../common/util'; -import React, { FC, useState } from 'react'; -import Input from '../../common/Input/Input'; -import { UPDATE_FEATURE } from '../../providers/AccessProvider/permissions'; -import PermissionButton from '../../common/PermissionButton/PermissionButton'; -import { useDependentFeaturesApi } from 'hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi'; - -const StyledForm = styled('form')({}); - -const StyledInputDescription = styled('p')(({ theme }) => ({ - marginBottom: theme.spacing(1), -})); - -const StyledInput = styled(Input)(({ theme }) => ({ - marginBottom: theme.spacing(2), -})); - -interface IAddDependencyProps { - projectId: string; - featureId: string; -} -export const AddDependency: FC = ({ - projectId, - featureId, -}) => { - const [parent, setParent] = useState(''); - const { addDependency } = useDependentFeaturesApi(); - - return ( - { - addDependency(featureId, { feature: parent }); - }} - > - - What feature do you want to depend on? - - - setParent(trim(e.target.value))} - /> - { - addDependency(featureId, { feature: parent }); - }} - variant={'outlined'} - > - Add{' '} - - - - ); -}; diff --git a/frontend/src/component/feature/Dependencies/AddDependencyDialogue.test.tsx b/frontend/src/component/feature/Dependencies/AddDependencyDialogue.test.tsx new file mode 100644 index 0000000000..6c9b1d8a33 --- /dev/null +++ b/frontend/src/component/feature/Dependencies/AddDependencyDialogue.test.tsx @@ -0,0 +1,102 @@ +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { render } from 'utils/testRenderer'; +import { AddDependencyDialogue } from './AddDependencyDialogue'; +import { testServerRoute, testServerSetup } from 'utils/testServer'; +import { UIProviderContainer } from '../../providers/UIProvider/UIProviderContainer'; + +const server = testServerSetup(); + +const setupApi = () => { + testServerRoute(server, '/api/admin/ui-config', { + flags: { + dependentFeatures: true, + }, + }); + + testServerRoute( + server, + '/api/admin/projects/default/features/child/dependencies', + {}, + 'delete' + ); + + testServerRoute( + server, + '/api/admin/projects/default/features/child/dependencies', + {}, + 'post' + ); + + testServerRoute( + server, + '/api/admin/projects/default/features/child/parents', + ['parentA', 'parentB'] + ); +}; + +test('Delete dependency', async () => { + let closed = false; + setupApi(); + render( + + { + closed = true; + }} + /> + + ); + + const removeDependency = await screen.findByText('Remove'); + + await waitFor(() => { + expect(removeDependency).not.toBeDisabled(); + }); + + removeDependency.click(); + + await waitFor(() => { + expect(closed).toBe(true); + }); +}); + +test('Add dependency', async () => { + let closed = false; + setupApi(); + render( + + { + closed = true; + }} + /> + + ); + + const removeDependency = await screen.findByText('Remove'); + + await waitFor(() => { + expect(removeDependency).not.toBeDisabled(); + }); + + // Open the dropdown by selecting the role. + const dropdown = screen.queryAllByRole('button')[0]; + userEvent.click(dropdown); + + const parentAOption = await screen.findByText('parentA'); + userEvent.click(parentAOption); + + const addButton = await screen.findByText('Add'); + userEvent.click(addButton); + + await waitFor(() => { + expect(closed).toBe(true); + }); +}); diff --git a/frontend/src/component/feature/Dependencies/AddDependencyDialogue.tsx b/frontend/src/component/feature/Dependencies/AddDependencyDialogue.tsx index de55602c47..1f4dbafc26 100644 --- a/frontend/src/component/feature/Dependencies/AddDependencyDialogue.tsx +++ b/frontend/src/component/feature/Dependencies/AddDependencyDialogue.tsx @@ -6,6 +6,7 @@ import { useDependentFeaturesApi } from 'hooks/api/actions/useDependentFeaturesA import { useParentOptions } from 'hooks/api/getters/useParentOptions/useParentOptions'; interface IAddDependencyDialogueProps { + project: string; featureId: string; showDependencyDialogue: boolean; onClose: () => void; @@ -22,13 +23,15 @@ const REMOVE_DEPENDENCY_OPTION = { }; export const AddDependencyDialogue = ({ + project, featureId, showDependencyDialogue, onClose, }: IAddDependencyDialogueProps) => { - const [parent, setParent] = useState(''); - const { addDependency, removeDependencies } = useDependentFeaturesApi(); - const { parentOptions } = useParentOptions(featureId); + const [parent, setParent] = useState(REMOVE_DEPENDENCY_OPTION.key); + const { addDependency, removeDependencies } = + useDependentFeaturesApi(project); + const { parentOptions, loading } = useParentOptions(project, featureId); const options = parentOptions ? [ REMOVE_DEPENDENCY_OPTION, @@ -49,8 +52,11 @@ export const AddDependencyDialogue = ({ } onClose(); }} - primaryButtonText="Add" + primaryButtonText={ + parent === REMOVE_DEPENDENCY_OPTION.key ? 'Remove' : 'Add' + } secondaryButtonText="Cancel" + disabledPrimaryButton={loading} > You feature will be evaluated only when the selected parent diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.test.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.test.tsx index 5a5612e576..8d34dd8a0a 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.test.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.test.tsx @@ -15,7 +15,7 @@ testServerRoute(server, '/api/admin/ui-config', { test('show dependency dialogue', async () => { render( ); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx index cdfbab1a9b..37abc089bc 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/FeatureOverviewSidePanelDetails.tsx @@ -76,7 +76,7 @@ export const FeatureOverviewSidePanelDetails = ({ )} @@ -93,11 +93,17 @@ export const FeatureOverviewSidePanelDetails = ({ } /> - setShowDependencyDialogue(false)} - showDependencyDialogue={ - dependentFeatures && showDependencyDialogue + setShowDependencyDialogue(false)} + showDependencyDialogue={ + dependentFeatures && showDependencyDialogue + } + /> } /> diff --git a/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts b/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts index 1862709a62..7681beb3c2 100644 --- a/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts +++ b/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts @@ -7,7 +7,7 @@ import { useCallback } from 'react'; interface IParentFeaturePayload { feature: string; } -export const useDependentFeaturesApi = () => { +export const useDependentFeaturesApi = (project: string) => { const { makeRequest, createRequest, errors, loading } = useAPI({ propagateErrors: true, }); @@ -18,7 +18,7 @@ export const useDependentFeaturesApi = () => { parentFeaturePayload: IParentFeaturePayload ) => { const req = createRequest( - `/api/admin/projects/default/features/${childFeature}/dependencies`, + `/api/admin/projects/${project}/features/${childFeature}/dependencies`, { method: 'POST', body: JSON.stringify(parentFeaturePayload), @@ -41,7 +41,7 @@ export const useDependentFeaturesApi = () => { parentFeature: string ) => { const req = createRequest( - `/api/admin/projects/default/features/${childFeature}/dependencies/${parentFeature}`, + `/api/admin/projects/${project}/features/${childFeature}/dependencies/${parentFeature}`, { method: 'DELETE', } @@ -60,7 +60,7 @@ export const useDependentFeaturesApi = () => { const removeDependencies = async (childFeature: string) => { const req = createRequest( - `/api/admin/projects/default/features/${childFeature}/dependencies`, + `/api/admin/projects/${project}/features/${childFeature}/dependencies`, { method: 'DELETE', } diff --git a/frontend/src/hooks/api/getters/useParentOptions/useParentOptions.ts b/frontend/src/hooks/api/getters/useParentOptions/useParentOptions.ts index 5d1eddb54c..ccd2d75227 100644 --- a/frontend/src/hooks/api/getters/useParentOptions/useParentOptions.ts +++ b/frontend/src/hooks/api/getters/useParentOptions/useParentOptions.ts @@ -3,11 +3,12 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; export const useParentOptions = ( + project: string, childFeatureId: string, options: SWRConfiguration = {} ) => { const path = formatApiPath( - `/api/admin/projects/default/features/${childFeatureId}/parents` + `/api/admin/projects/${project}/features/${childFeatureId}/parents` ); const { data, error, mutate } = useSWR(path, fetcher, options);