Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat #734: Delete a node using the delete key #1566

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion packages/ui/src/components/Visualization/Canvas/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import layoutVerticalIcon from '../../../assets/layout-vertical.png';
import { useLocalStorage } from '../../../hooks';
import { LocalStorageKeys } from '../../../models';
import { BaseVisualCamelEntity } from '../../../models/visualization/base-visual-entity';
import { BaseVisualCamelEntity, IVisualizationNode } from '../../../models/visualization/base-visual-entity';
import { CatalogModalContext } from '../../../providers/catalog-modal.provider';
import { VisibleFlowsContext } from '../../../providers/visible-flows.provider';
import { VisualizationEmptyState } from '../EmptyState';
Expand All @@ -38,6 +38,9 @@
import { CanvasDefaults } from './canvas.defaults';
import { CanvasEdge, CanvasNode, LayoutType } from './canvas.models';
import { FlowService } from './flow.service';
import { ActionConfirmationModalContext, EntitiesContext } from '../../../providers';
import { deleteRoute } from '../Custom/ContextMenu/ItemDeleteGroup';
import { deleteStep } from '../Custom/ContextMenu/ItemDeleteStep';

interface CanvasProps {
entities: BaseVisualCamelEntity[];
Expand All @@ -58,6 +61,9 @@
/** Context to interact with the Canvas catalog */
const catalogModalContext = useContext(CatalogModalContext);

const entitiesContext = useContext(EntitiesContext);
const deleteModalContext = useContext(ActionConfirmationModalContext);

const controller = useVisualizationController();
const { visibleFlows } = useContext(VisibleFlowsContext)!;
const shouldShowEmptyState = useMemo(() => {
Expand Down Expand Up @@ -125,6 +131,44 @@
}
}, [selectedIds, controller]);

const removeNode = useCallback(
async (vizNode: IVisualizationNode) => {

Check warning on line 135 in packages/ui/src/components/Visualization/Canvas/Canvas.tsx

View check run for this annotation

Codecov / codecov/patch

packages/ui/src/components/Visualization/Canvas/Canvas.tsx#L135

Added line #L135 was not covered by tests
if (vizNode?.getParentNode() == undefined) {
deleteRoute(deleteModalContext, entitiesContext, vizNode);
} else {
deleteStep(deleteModalContext, entitiesContext, vizNode);

Check warning on line 139 in packages/ui/src/components/Visualization/Canvas/Canvas.tsx

View check run for this annotation

Codecov / codecov/patch

packages/ui/src/components/Visualization/Canvas/Canvas.tsx#L137-L139

Added lines #L137 - L139 were not covered by tests
}
},
[deleteModalContext, entitiesContext],
);

/** Handle Delete key press */
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (
selectedNode &&
((event.target as HTMLElement).tagName === 'MAIN' || (event.target as HTMLElement).tagName === 'g')

Check warning on line 150 in packages/ui/src/components/Visualization/Canvas/Canvas.tsx

View check run for this annotation

Codecov / codecov/patch

packages/ui/src/components/Visualization/Canvas/Canvas.tsx#L150

Added line #L150 was not covered by tests
) {
if (event.key === 'Delete') {
const vizNode: IVisualizationNode = controller.getElementById(selectedNode.id)?.getData()?.vizNode;
removeNode(vizNode);
setSelectedIds([]);
setSelectedNode(undefined);

Check warning on line 156 in packages/ui/src/components/Visualization/Canvas/Canvas.tsx

View check run for this annotation

Codecov / codecov/patch

packages/ui/src/components/Visualization/Canvas/Canvas.tsx#L153-L156

Added lines #L153 - L156 were not covered by tests
/** TODO: Implement selection of next node after deletion when ids are fixed
const selectNext = vizNode?.getNextNode()?.id || vizNode?.getPreviousNode()?.id;
removeNode(vizNode);
controller.fireEvent(SELECTION_EVENT, [selectNext]);
*/
}
}
};

window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [selectedNode, controller, removeNode]);

const controlButtons = useMemo(() => {
const customButtons: TopologyControlButton[] = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,45 @@ import { TrashIcon } from '@patternfly/react-icons';
import { ContextMenuItem } from '@patternfly/react-topology';
import { FunctionComponent, PropsWithChildren, useCallback, useContext } from 'react';
import { IDataTestID } from '../../../../models';
import { IVisualizationNode } from '../../../../models/visualization/base-visual-entity';
import {
ACTION_ID_CONFIRM,
ActionConfirmationModalContext,
} from '../../../../providers/action-confirmation-modal.provider';
import { IVisualizationNode, IVisualizationNodeData } from '../../../../models/visualization/base-visual-entity';
import { EntitiesContext } from '../../../../providers/entities.provider';
import { EntitiesContextResult } from '../../../../hooks';

interface ItemDeleteGroupProps extends PropsWithChildren<IDataTestID> {
vizNode: IVisualizationNode;
}

export const deleteRoute = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deleteModalContext: any,
entitiesContext: EntitiesContextResult | null,
vizNode: IVisualizationNode<IVisualizationNodeData>,
) => {
/** Open delete confirm modal, get the confirmation */
const isDeleteConfirmed = await deleteModalContext?.actionConfirmation({
title: 'Permanently delete flow?',
text: 'All steps will be lost.',
});

if (isDeleteConfirmed !== ACTION_ID_CONFIRM) return;
const flowId = vizNode?.getId();

entitiesContext?.camelResource.removeEntity(flowId);

entitiesContext?.updateEntitiesFromCamelResource();
};

export const ItemDeleteGroup: FunctionComponent<ItemDeleteGroupProps> = (props) => {
const entitiesContext = useContext(EntitiesContext);
const deleteModalContext = useContext(ActionConfirmationModalContext);
const flowId = props.vizNode?.getId();

const onRemoveGroup = useCallback(async () => {
/** Open delete confirm modal, get the confirmation */
const isDeleteConfirmed = await deleteModalContext?.actionConfirmation({
title: 'Permanently delete flow?',
text: 'All steps will be lost.',
});

if (isDeleteConfirmed !== ACTION_ID_CONFIRM) return;

entitiesContext?.camelResource.removeEntity(flowId);
entitiesContext?.updateEntitiesFromCamelResource();
}, [deleteModalContext, entitiesContext, flowId]);
deleteRoute(deleteModalContext, entitiesContext, props.vizNode);
}, [deleteModalContext, entitiesContext, props.vizNode]);

return (
<ContextMenuItem onClick={onRemoveGroup} data-testid={props['data-testid']}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,46 @@ import { TrashIcon } from '@patternfly/react-icons';
import { ContextMenuItem } from '@patternfly/react-topology';
import { FunctionComponent, PropsWithChildren, useCallback, useContext } from 'react';
import { IDataTestID } from '../../../../models';
import { IVisualizationNode } from '../../../../models/visualization/base-visual-entity';
import { IVisualizationNode, IVisualizationNodeData } from '../../../../models/visualization/base-visual-entity';
import { EntitiesContext } from '../../../../providers/entities.provider';
import {
ACTION_ID_CONFIRM,
ActionConfirmationModalContext,
} from '../../../../providers/action-confirmation-modal.provider';
import { EntitiesContextResult } from '../../../../hooks';

interface ItemDeleteStepProps extends PropsWithChildren<IDataTestID> {
vizNode: IVisualizationNode;
loadActionConfirmationModal: boolean;
}

export const deleteStep = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deleteModalContext: any,
entitiesContext: EntitiesContextResult | null,
vizNode: IVisualizationNode<IVisualizationNodeData>,
loadActionConfirmationModal: boolean = false,
) => {
if (loadActionConfirmationModal || (vizNode?.getChildren() ?? []).length > 0) {
/** Open delete confirm modal, get the confirmation */
const isDeleteConfirmed = await deleteModalContext?.actionConfirmation({
title: 'Permanently delete step?',
text: 'Step and its children will be lost.',
});

if (isDeleteConfirmed !== ACTION_ID_CONFIRM) return;
}

vizNode?.removeChild();
entitiesContext?.updateEntitiesFromCamelResource();
};

export const ItemDeleteStep: FunctionComponent<ItemDeleteStepProps> = (props) => {
const entitiesContext = useContext(EntitiesContext);
const deleteModalContext = useContext(ActionConfirmationModalContext);

const onRemoveNode = useCallback(async () => {
if (props.loadActionConfirmationModal) {
/** Open delete confirm modal, get the confirmation */
const isDeleteConfirmed = await deleteModalContext?.actionConfirmation({
title: 'Permanently delete step?',
text: 'Step and its children will be lost.',
});

if (isDeleteConfirmed !== ACTION_ID_CONFIRM) return;
}

props.vizNode?.removeChild();
entitiesContext?.updateEntitiesFromCamelResource();
deleteStep(deleteModalContext, entitiesContext, props.vizNode, props.loadActionConfirmationModal);
}, [deleteModalContext, entitiesContext, props.loadActionConfirmationModal, props.vizNode]);

return (
Expand Down
Loading