Skip to content

Commit

Permalink
feat(Canvas): Consolidate groups interactions
Browse files Browse the repository at this point in the history
Currently, expanded and collapsed groups don't have the interactions available, for instance:

* Expanded groups: Only allowed to delete the entire flow
* Collapsed groups: Only allowed to Insert step

This commit consolidates both functionalities where it makes sense, so expanded groups allow the deletion of the flow, and collapsed ones allow to insert step (which expands the group) and the deletion of the entire flow.

relates: #492
  • Loading branch information
lordrip committed Apr 16, 2024
1 parent 981c994 commit 33e03dd
Show file tree
Hide file tree
Showing 18 changed files with 416 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import {
Layer,
isNode,
observer,
withSelection,
withContextMenu,
withSelection,
} from '@patternfly/react-topology';
import { FunctionComponent } from 'react';
import { AddStepMode } from '../../../models/visualization/base-visual-entity';
import { CanvasNode } from '../Canvas/canvas.models';
import { ItemInsertChildNode } from './ItemInsertChildNode';
import { ItemRemoveGroup } from './ItemRemoveGroup';

type IDefaultGroup = Parameters<typeof DefaultGroup>[0];
Expand Down Expand Up @@ -44,5 +46,10 @@ const CustomGroup: FunctionComponent<ICustomGroup> = observer(({ element, ...res
});

export const CustomGroupWithSelection = withContextMenu(() => [
<ItemInsertChildNode
key="context-menu-item-insert-special"
data-testid="context-menu-item-insert-special"
mode={AddStepMode.InsertSpecialChildStep}
/>,
<ItemRemoveGroup key="context-menu-container-remove" data-testid="context-menu-container-remove" />,
])(withSelection()(CustomGroup));
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ItemAddNode } from './ItemAddNode';
import { ItemInsertChildNode } from './ItemInsertChildNode';
import { ItemRemoveNode } from './ItemRemoveNode';
import { ItemReplaceNode } from './ItemReplaceNode';
import { ItemRemoveGroup } from './ItemRemoveGroup';

interface CustomNodeProps extends WithSelectionProps {
element: Node<CanvasNode, CanvasNode['data']>;
Expand Down Expand Up @@ -78,4 +79,5 @@ export const CustomNodeWithSelection: typeof DefaultNode = withContextMenu(() =>
/>,
<ItemReplaceNode key="context-menu-item-replace" data-testid="context-menu-item-replace" />,
<ItemRemoveNode key="context-menu-item-remove" data-testid="context-menu-item-remove" />,
<ItemRemoveGroup key="context-menu-container-remove" data-testid="context-menu-container-remove" />,
])(withSelection()(CustomNode) as typeof DefaultNode) as typeof DefaultNode;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TrashIcon } from '@patternfly/react-icons';
import { ContextMenuItem, ElementContext, ElementModel, GraphElement } from '@patternfly/react-topology';
import { FunctionComponent, useCallback, useContext } from 'react';
import { FunctionComponent, useCallback, useContext, useMemo } from 'react';
import { IDataTestID } from '../../../models';
import { EntitiesContext } from '../../../providers/entities.provider';
import { CanvasNode } from '../Canvas/canvas.models';
Expand All @@ -10,15 +10,20 @@ export const ItemRemoveGroup: FunctionComponent<IDataTestID> = (props) => {
const element: GraphElement<ElementModel, CanvasNode['data']> = useContext(ElementContext);
const vizNode = element.getData()?.vizNode;
const flowId = vizNode?.getBaseEntity()?.getId();
const shouldRender = useMemo(() => {
const nodeInteractions = vizNode?.getNodeInteraction() ?? { canRemoveFlow: false };

return nodeInteractions.canRemoveFlow;
}, [vizNode]);

const onRemoveGroup = useCallback(() => {
entitiesContext?.camelResource.removeEntity(flowId);
entitiesContext?.updateEntitiesFromCamelResource();
}, [entitiesContext]);
}, [entitiesContext, flowId]);

return (
return shouldRender ? (
<ContextMenuItem onClick={onRemoveGroup} data-testid={props['data-testid']}>
<TrashIcon /> Delete
</ContextMenuItem>
);
) : null;
};
1 change: 1 addition & 0 deletions packages/ui/src/models/visualization/base-visual-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,5 @@ export interface NodeInteraction {
canHaveSpecialChildren: boolean;
canReplaceStep: boolean;
canRemoveStep: boolean;
canRemoveFlow: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'from' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'intercept' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'interceptFrom' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'interceptSendToEndpoint' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'log' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'onCompletion' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'onException' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'route' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`AbstractCamelVisualEntity getNodeInteraction should return the correct interaction for the 'to' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'from' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'intercept' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'interceptFrom' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'interceptSendToEndpoint' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'log' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'onCompletion' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'onException' processor 1`] = `
{
"canHaveChildren": true,
"canHaveNextStep": false,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'route' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`CamelOnExceptionVisualEntity getNodeInteraction should return the correct interaction for the 'to' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Pipe getNodeInteraction should return the correct interaction for the '#' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": true,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`Pipe getNodeInteraction should return the correct interaction for the 'sink' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": false,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`Pipe getNodeInteraction should return the correct interaction for the 'source' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": false,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;

exports[`Pipe getNodeInteraction should return the correct interaction for the 'steps.1' processor 1`] = `
{
"canHaveChildren": false,
"canHaveNextStep": true,
"canHavePreviousStep": true,
"canHaveSpecialChildren": false,
"canRemoveFlow": false,
"canRemoveStep": true,
"canReplaceStep": true,
}
`;
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as catalogIndex from '@kaoto-next/camel-catalog/index.json';
import cloneDeep from 'lodash/cloneDeep';
import { camelRouteJson } from '../../../stubs/camel-route';
import { CamelRouteVisualEntity } from './camel-route-visual-entity';
import { CamelCatalogService } from './camel-catalog.service';
import { CatalogKind } from '../../catalog-kind';
import { ICamelComponentDefinition } from '../../camel-components-catalog';
import { ICamelProcessorDefinition } from '../../camel-processors-catalog';
import { CatalogKind } from '../../catalog-kind';
import { CamelCatalogService } from './camel-catalog.service';
import { CamelRouteVisualEntity } from './camel-route-visual-entity';
import { CamelComponentSchemaService } from './support/camel-component-schema.service';

describe('AbstractCamelVisualEntity', () => {
Expand All @@ -30,6 +30,35 @@ describe('AbstractCamelVisualEntity', () => {
abstractVisualEntity = new CamelRouteVisualEntity(cloneDeep(camelRouteJson.route));
});

describe('getNodeInteraction', () => {
it('should not allow marked processors to have previous/next steps', () => {
const result = abstractVisualEntity.getNodeInteraction({ processorName: 'from' });
expect(result.canHavePreviousStep).toEqual(false);
expect(result.canHaveNextStep).toEqual(false);
});

it('should allow processors to have previous/next steps', () => {
const result = abstractVisualEntity.getNodeInteraction({ processorName: 'to' });
expect(result.canHavePreviousStep).toEqual(true);
expect(result.canHaveNextStep).toEqual(true);
});

it.each([
'route',
'from',
'to',
'log',
'onException',
'onCompletion',
'intercept',
'interceptFrom',
'interceptSendToEndpoint',
])(`should return the correct interaction for the '%s' processor`, (processorName) => {
const result = abstractVisualEntity.getNodeInteraction({ processorName });
expect(result).toMatchSnapshot();
});
});

describe('getNodeValidationText', () => {
it('should return an `undefined` if the path is `undefined`', () => {
const result = abstractVisualEntity.getNodeValidationText(undefined);
Expand Down
Loading

0 comments on commit 33e03dd

Please sign in to comment.