mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: inject project id to dependencies hooks (#4839)
This commit is contained in:
parent
40b9c46018
commit
6f4f6f049b
@ -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<IAddDependencyProps> = ({
|
||||
projectId,
|
||||
featureId,
|
||||
}) => {
|
||||
const [parent, setParent] = useState('');
|
||||
const { addDependency } = useDependentFeaturesApi();
|
||||
|
||||
return (
|
||||
<StyledForm
|
||||
onSubmit={() => {
|
||||
addDependency(featureId, { feature: parent });
|
||||
}}
|
||||
>
|
||||
<StyledInputDescription>
|
||||
What feature do you want to depend on?
|
||||
</StyledInputDescription>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<StyledInput
|
||||
autoFocus
|
||||
label="Dependency"
|
||||
id="dependency-feature"
|
||||
value={parent}
|
||||
onChange={e => setParent(trim(e.target.value))}
|
||||
/>
|
||||
<PermissionButton
|
||||
permission={UPDATE_FEATURE}
|
||||
projectId={projectId}
|
||||
onClick={() => {
|
||||
addDependency(featureId, { feature: parent });
|
||||
}}
|
||||
variant={'outlined'}
|
||||
>
|
||||
Add{' '}
|
||||
</PermissionButton>
|
||||
</Box>
|
||||
</StyledForm>
|
||||
);
|
||||
};
|
@ -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(
|
||||
<UIProviderContainer>
|
||||
<AddDependencyDialogue
|
||||
project="default"
|
||||
featureId="child"
|
||||
showDependencyDialogue={true}
|
||||
onClose={() => {
|
||||
closed = true;
|
||||
}}
|
||||
/>
|
||||
</UIProviderContainer>
|
||||
);
|
||||
|
||||
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(
|
||||
<UIProviderContainer>
|
||||
<AddDependencyDialogue
|
||||
project="default"
|
||||
featureId="child"
|
||||
showDependencyDialogue={true}
|
||||
onClose={() => {
|
||||
closed = true;
|
||||
}}
|
||||
/>
|
||||
</UIProviderContainer>
|
||||
);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
@ -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}
|
||||
>
|
||||
<Box>
|
||||
You feature will be evaluated only when the selected parent
|
||||
|
@ -15,7 +15,7 @@ testServerRoute(server, '/api/admin/ui-config', {
|
||||
test('show dependency dialogue', async () => {
|
||||
render(
|
||||
<FeatureOverviewSidePanelDetails
|
||||
feature={{ name: 'feature' } as IFeatureToggle}
|
||||
feature={{ name: 'feature', project: 'default' } as IFeatureToggle}
|
||||
header={''}
|
||||
/>
|
||||
);
|
||||
|
@ -76,7 +76,7 @@ export const FeatureOverviewSidePanelDetails = ({
|
||||
)}
|
||||
</FlexRow>
|
||||
<ConditionallyRender
|
||||
condition={dependentFeatures}
|
||||
condition={dependentFeatures && Boolean(feature.project)}
|
||||
show={
|
||||
<FlexRow>
|
||||
<StyledDetail>
|
||||
@ -93,11 +93,17 @@ export const FeatureOverviewSidePanelDetails = ({
|
||||
</FlexRow>
|
||||
}
|
||||
/>
|
||||
<AddDependencyDialogue
|
||||
featureId={feature.name}
|
||||
onClose={() => setShowDependencyDialogue(false)}
|
||||
showDependencyDialogue={
|
||||
dependentFeatures && showDependencyDialogue
|
||||
<ConditionallyRender
|
||||
condition={dependentFeatures && Boolean(feature.project)}
|
||||
show={
|
||||
<AddDependencyDialogue
|
||||
project={feature.project}
|
||||
featureId={feature.name}
|
||||
onClose={() => setShowDependencyDialogue(false)}
|
||||
showDependencyDialogue={
|
||||
dependentFeatures && showDependencyDialogue
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StyledContainer>
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user