Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
align-items: center;
justify-content: center;
background-color: var(--custom-node-BackgroundColor);
width: 25px;
height: 25px;
border-radius: 50%;
min-width: 25px;
min-height: 25px;
width: fit-content;
height: fit-content;
padding: 0 6px;
border-radius: 12.5px;
border: 1px solid var(--custom-node-BorderColor);
box-shadow: var(--custom-node-Shadow);

&.step-icon-collection {
gap: 4px;
}

[data-selected='true'] & {
@include custom.selected;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ import {
} from '../customComponentUtils';
import { FloatingCircle } from '../FloatingCircle/FloatingCircle';
import { CustomNodeContainer } from '../Node/CustomNodeContainer';
import { checkNodeDropCompatibility, getNodeDragAndDropDirection, handleValidNodeDrop } from '../Node/CustomNodeUtils';
import {
checkNodeDropCompatibility,
getNodeDragAndDropDirection,
getVizNodeChildrenInfo,
handleValidNodeDrop,
} from '../Node/CustomNodeUtils';
import { TargetAnchor } from '../target-anchor';
import { CustomGroupProps } from './Group.models';

Expand All @@ -70,7 +75,7 @@ export const CustomGroupExpandedInner: FunctionComponent<CustomGroupProps> = obs
const isDisabled = !!groupVizNode?.getNodeDefinition()?.disabled;
const validationText = groupVizNode?.getNodeValidationText();
const doesHaveWarnings = !isDisabled && !!validationText;
const childCount = element.getAllNodeChildren().length;
const { childCount, hasGroupChildren } = getVizNodeChildrenInfo(groupVizNode);
const [isGHover, gHoverRef] = useHover<SVGGElement>(CanvasDefaults.HOVER_DELAY_IN, CanvasDefaults.HOVER_DELAY_OUT);
const [isToolbarHover, toolbarHoverRef] = useHover<SVGForeignObjectElement>(
CanvasDefaults.HOVER_DELAY_IN,
Expand Down Expand Up @@ -254,7 +259,9 @@ export const CustomGroupExpandedInner: FunctionComponent<CustomGroupProps> = obs
transform={`translate(${dragGroupProps.dragEvent!.x - 20}, ${dragGroupProps.dragEvent!.y - 20})`}
dataTestId={groupVizNode.id}
vizNode={groupVizNode}
isCollapsed={element.isCollapsed()}
childCount={childCount}
hasGroupChildren={hasGroupChildren}
containerClassNames={{
'custom-node__container__draggedNode': true,
'custom-node__container__grabbing-cursor': true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ import { NodeContextMenuFn } from '../ContextMenu/NodeContextMenu';
import { getDropTargetContainerClassNames, GROUP_DRAG_TYPE, NODE_DRAG_TYPE } from '../customComponentUtils';
import { TargetAnchor } from '../target-anchor';
import { CustomNodeContainer } from './CustomNodeContainer';
import { checkNodeDropCompatibility, getNodeDragAndDropDirection, handleValidNodeDrop } from './CustomNodeUtils';
import {
checkNodeDropCompatibility,
getNodeDragAndDropDirection,
getVizNodeChildrenInfo,
handleValidNodeDrop,
} from './CustomNodeUtils';

type DefaultNodeProps = Parameters<typeof DefaultNode>[0];

Expand Down Expand Up @@ -135,7 +140,7 @@ const CustomNodeInner: FunctionComponent<CustomNodeProps> = observer(
CanvasDefaults.HOVER_DELAY_IN,
CanvasDefaults.HOVER_DELAY_OUT,
);
const childCount = element.getAllNodeChildren().length;
const { childCount, hasGroupChildren, isCollapsedGroup } = getVizNodeChildrenInfo(vizNode);
const shouldShowToolbar = getShouldShowToolbar(
settingsAdapter.getSettings().nodeToolbarTrigger,
isGHover,
Expand Down Expand Up @@ -303,7 +308,9 @@ const CustomNodeInner: FunctionComponent<CustomNodeProps> = observer(
dataTestId={vizNode.id}
containerClassNames={mainContainerClassNames}
vizNode={vizNode}
isCollapsed={element.isCollapsed() || isCollapsedGroup}
childCount={childCount}
hasGroupChildren={hasGroupChildren}
ProcessorIcon={ProcessorIcon}
processorDescription={processorDescription}
isDisabled={isDisabled}
Expand All @@ -320,7 +327,9 @@ const CustomNodeInner: FunctionComponent<CustomNodeProps> = observer(
dataTestId={`${vizNode.id}-dummy`}
containerClassNames={{ 'custom-node__container__draggedNode': isDraggedNode }}
vizNode={vizNode}
isCollapsed={element.isCollapsed() || isCollapsedGroup}
childCount={childCount}
hasGroupChildren={hasGroupChildren}
ProcessorIcon={ProcessorIcon}
processorDescription={processorDescription}
isDisabled={isDisabled}
Expand Down Expand Up @@ -372,7 +381,7 @@ const CustomNodeInner: FunctionComponent<CustomNodeProps> = observer(
<StepToolbar
data-testid="step-toolbar"
vizNode={vizNode}
isCollapsed={element.isCollapsed()}
isCollapsed={element.isCollapsed() || isCollapsedGroup}
onCollapseToggle={onCollapseToggle}
/>
</foreignObject>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('CustomNodeContainer', () => {
width: 90,
height: 75,
dataTestId: 'test-node',
isCollapsed: true,
};

it('should render the CustomNodeContainer correctly', () => {
Expand All @@ -49,6 +50,7 @@ describe('CustomNodeContainer', () => {
<CustomNodeContainer
{...defaultContainerProps}
vizNode={vizNode}
isCollapsed={true}
childCount={5}
ProcessorIcon={null}
processorDescription={''}
Expand All @@ -61,6 +63,42 @@ describe('CustomNodeContainer', () => {
expect(childCountElement).toHaveTextContent('5');
});

it('should not render child count when isCollapsed is false', () => {
const vizNode = createMockVizNode();

render(
<CustomNodeContainer
{...defaultContainerProps}
vizNode={vizNode}
tooltipContent={undefined}
isCollapsed={false}
childCount={5}
ProcessorIcon={null}
processorDescription={undefined}
isDisabled={false}
/>,
);

expect(screen.queryByTitle('5')).not.toBeInTheDocument();
});

it('should not render child count when isCollapsed is false', () => {
const vizNode = createMockVizNode();

render(
<CustomNodeContainer
{...defaultContainerProps}
vizNode={vizNode}
childCount={0}
ProcessorIcon={null}
processorDescription={''}
isDisabled={false}
/>,
);

expect(screen.queryByTitle('5')).not.toBeInTheDocument();
});

it('should not render child count when childCount is 0', () => {
const vizNode = createMockVizNode();

Expand Down Expand Up @@ -186,4 +224,26 @@ describe('CustomNodeContainer', () => {
expect(screen.getByTestId('processor-icon')).toBeInTheDocument();
expect(container.querySelector('.step-icon__disabled')).toBeInTheDocument();
});

it('should render Layers icon when hasGroupChildren is true', () => {
const vizNode = createMockVizNode();

const { container } = render(
<CustomNodeContainer
{...defaultContainerProps}
vizNode={vizNode}
tooltipContent={undefined}
isCollapsed={true}
childCount={1}
hasGroupChildren={true}
ProcessorIcon={null}
processorDescription={undefined}
isDisabled={false}
/>,
);

expect(container.querySelector('.step-icon-collection')).toBeInTheDocument();
// Carbon icon is rendered inside PF Icon
expect(container.querySelector('svg')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Layers } from '@carbon/icons-react';
import { Icon } from '@patternfly/react-core';
import { BanIcon } from '@patternfly/react-icons';
import clsx from 'clsx';
Expand All @@ -15,7 +16,9 @@ export interface CustomNodeContainerProps {
dataTestId: string;
containerClassNames?: Record<string, boolean>;
vizNode: IVisualizationNode;
isCollapsed: boolean;
childCount: number;
hasGroupChildren?: boolean;
ProcessorIcon: ElementType | null;
processorDescription: string;
isDisabled: boolean;
Expand All @@ -30,7 +33,9 @@ export const CustomNodeContainer: FunctionComponent<CustomNodeContainerProps> =
dataTestId,
containerClassNames = {},
vizNode,
isCollapsed,
childCount,
hasGroupChildren,
ProcessorIcon,
processorDescription,
isDisabled,
Expand All @@ -46,8 +51,15 @@ export const CustomNodeContainer: FunctionComponent<CustomNodeContainerProps> =
<div title={vizNode.data.description} className="custom-node__container__image">
<img src={vizNode.data.iconUrl} alt={vizNode.data.description?.trim() || (vizNode.data.iconAlt as string)} />

{childCount > 0 && (
<FloatingCircle className="step-icon step-icon__processor">
{isCollapsed && childCount > 0 && (
<FloatingCircle
className={clsx('step-icon step-icon__processor', { 'step-icon-collection': hasGroupChildren })}
>
{hasGroupChildren && (
<Icon size="sm" aria-label="contains nested collections">
<Layers />
</Icon>
)}
<span title={`${childCount}`}>{childCount}</span>
</FloatingCircle>
)}
Comment thread
matheusandre1 marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ElementModel, GraphElement, Node } from '@patternfly/react-topology';
import { PlaceholderType } from '../../../../models/placeholder.constants';
import { AddStepMode, IVisualizationNode } from '../../../../models/visualization/base-visual-entity';
import { IClipboardCopyObject } from '../../../../models/visualization/clipboard';
import { CamelComponentSchemaService } from '../../../../models/visualization/flows/support/camel-component-schema.service';
import { CamelRouteVisualEntityData } from '../../../../models/visualization/flows/support/camel-component-types';
import { EntitiesContextResult } from '../../../../providers/entities.provider';
import {
IInteractionType,
Expand Down Expand Up @@ -174,3 +176,25 @@ const performForwardDrop = (
entitiesContext.camelResource.removeEntity(flowId ? [flowId] : undefined);
}
};

export interface VizNodeChildrenInfo {
childCount: number;
hasGroupChildren: boolean;
isCollapsedGroup: boolean;
}

export const getVizNodeChildrenInfo = (vizNode: IVisualizationNode | undefined): VizNodeChildrenInfo => {
const processorName = (vizNode?.data as CamelRouteVisualEntityData)?.processorName;
const vizNodeChildren = vizNode?.getChildren() ?? [];
const childCount = vizNodeChildren.filter((c) => !c.data.isPlaceholder).length;
const hasGroupChildren = vizNodeChildren.some((child) => {
const childProcessorName = (child.data as CamelRouteVisualEntityData)?.processorName;
return childProcessorName
? CamelComponentSchemaService.getProcessorStepsProperties(childProcessorName).length > 0
: false;
});
const isContainerType =
!!processorName && CamelComponentSchemaService.getProcessorStepsProperties(processorName).length > 0;
const isCollapsedGroup = isContainerType && childCount > 0;
return { childCount, hasGroupChildren, isCollapsedGroup };
};