Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

add ability to replace existing step from catalog #149

Merged
merged 9 commits into from
Nov 9, 2021
80 changes: 46 additions & 34 deletions src/components/Visualization.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRef, useState } from 'react';
import { Circle, Group, Image, Layer, Line, Stage, Text } from 'react-konva';
import { Circle, Group, Image, Layer, Stage, Text } from 'react-konva';
import { IStepProps, IViewProps, IVizStepProps } from '../types';
import createImage from '../utils/createImage';
import truncateString from '../utils/truncateName';
Expand All @@ -8,12 +8,14 @@ import { Drawer, DrawerContent, DrawerContentBody } from '@patternfly/react-core
import './Visualization.css';
import Konva from 'konva';
import { v4 as uuidv4 } from 'uuid';
import { VisualizationSlot } from './VisualizationSlot';
import { VisualizationStep } from './VisualizationStep';

interface IVisualization {
deleteIntegrationStep: (e: any) => void;
isError?: boolean;
isLoading?: boolean;
replaceIntegrationStep: (newStep: any, oldStepIndex: any) => void;
steps: { viz: IVizStepProps; model: IStepProps }[];
views: IViewProps[];
}
Expand Down Expand Up @@ -41,8 +43,13 @@ const placeholderStep = {
views: [{}],
};

const Visualization = ({ deleteIntegrationStep, steps, views }: IVisualization) => {
const incrementAmt = 100;
const Visualization = ({
deleteIntegrationStep,
replaceIntegrationStep,
steps,
views,
}: IVisualization) => {
const layerRef = useRef<Konva.Layer>(null);
const stageRef = useRef<Konva.Stage>(null);
const [isPanelExpanded, setIsPanelExpanded] = useState(false);
const [selectedStep, setSelectedStep] =
Expand All @@ -66,10 +73,6 @@ const Visualization = ({ deleteIntegrationStep, steps, views }: IVisualization)
: deleteIntegrationStep(stepsIndex);
};

const onDragEndIntegration = () => {
//
};

const onDragEndTempStep = (e: any) => {
const newSteps = tempSteps;
let newStep = newSteps[e.target.attrs.index];
Expand Down Expand Up @@ -126,30 +129,43 @@ const Visualization = ({ deleteIntegrationStep, steps, views }: IVisualization)
className={'panelCustom'}
>
<DrawerContentBody>
{/** Stage wrapper to handle steps (DOM elements) dropped from catalog **/}
<div
onDrop={(e: any) => {
e.preventDefault();
const dataJSON = e.dataTransfer.getData('text');
// register event position
// Register event position
stageRef.current?.setPointersPositions(e);
const parsed: IStepProps = JSON.parse(dataJSON);
const currentPosition = stageRef.current?.getPointerPosition(); // e.g. {"x":158,"y":142}
const intersectingShape = stageRef.current?.getIntersection(currentPosition!);

setTempSteps(
tempSteps.concat({
model: parsed,
viz: {
id: uuidv4(),
label: parsed.name,
position: { ...stageRef.current?.getPointerPosition() },
temporary: true,
},
})
);
// Only create a temporary step if it does not intersect with an existing step
if (intersectingShape) {
const parentVizId = intersectingShape.getParent().attrs.id;
const parentIdx = steps.map((step) => step.viz.id).indexOf(parentVizId);
replaceIntegrationStep(parsed, parentIdx);
} else {
setTempSteps(
tempSteps.concat({
model: parsed,
viz: {
id: uuidv4(),
label: parsed.name,
position: { ...stageRef.current?.getPointerPosition()! },
temporary: true,
},
})
);
}
}}
onDragOver={(e) => {
e.preventDefault();
}}
onDragOver={(e) => e.preventDefault()}
>
<Stage width={window.innerWidth} height={window.innerHeight} ref={stageRef}>
<Layer>
<Layer ref={layerRef}>
{/** Create the temporary steps **/}
{tempSteps.map((step, idx) => {
const groupProps = {
x: step.viz.position.x,
Expand Down Expand Up @@ -179,20 +195,16 @@ const Visualization = ({ deleteIntegrationStep, steps, views }: IVisualization)
/>
);
})}

<Group
x={250}
y={300}
id={'Integration'}
onDragEnd={onDragEndIntegration}
draggable
name={'integration-and-slots'}
x={window.innerWidth / 5}
y={window.innerHeight / 2}
>
<Line
points={[100, 0, steps.length * incrementAmt, 0]}
stroke={'black'}
strokeWidth={3}
lineCap={'round'}
lineJoin={'round'}
/>
{/** Create the visualization slots **/}
<VisualizationSlot steps={steps} />

{/** Create the visualization steps **/}
{steps.map((item, index) => {
const imageProps = {
id: item.viz.id,
Expand All @@ -218,14 +230,14 @@ const Visualization = ({ deleteIntegrationStep, steps, views }: IVisualization)
key={index}
onClick={handleClickStep}
onMouseEnter={(e: any) => {
// style stage container:
const container = e.target.getStage().container();
container.style.cursor = 'pointer';
}}
onMouseLeave={(e: any) => {
const container = e.target.getStage().container();
container.style.cursor = 'default';
}}
id={item.viz.id}
>
<Circle
{...circleProps}
Expand Down
34 changes: 34 additions & 0 deletions src/components/VisualizationSlot.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { render } from '@testing-library/react';
import { screen } from '@testing-library/dom';
import { VisualizationSlot } from './VisualizationSlot';
import { IStepProps, IVizStepProps } from '../types';

describe.skip('VisualizationSlot.tsx', () => {
test('component renders correctly', () => {
const fakeSteps: { viz: IVizStepProps; model: IStepProps }[] = [];
const fakeStep = {
model: {
apiVersion: '',
id: '',
icon: '',
name: '',
type: 'START',
},
viz: {
id: '',
label: '',
position: {
x: 0,
y: 0,
},
temporary: false,
},
};

fakeSteps.push(fakeStep);

render(<VisualizationSlot steps={fakeSteps} />);
const element = screen.getByTestId('visualization-slot');
expect(element).toBeInTheDocument();
});
});
67 changes: 67 additions & 0 deletions src/components/VisualizationSlot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Circle, Group, Line } from 'react-konva';
import { IStepProps, IVizStepProps } from '../types';

export interface IVisualizationSlot {
children?: any;
steps: { viz: IVizStepProps; model: IStepProps }[];
}

const CIRCLE_LENGTH = 75;

const VisualizationSlot = ({ children, steps }: IVisualizationSlot) => {
const incrementAmt = 100;

return (
<Group name={'slots'} id={'slots'}>
<Line
points={[
0, // x1
0, // y1
steps.length * incrementAmt - 100, // x2 (subtract 100 for first step)
0, // y2
]}
stroke={'black'}
strokeWidth={3}
lineCap={'round'}
lineJoin={'round'}
/>
{steps.map((step, index) => {
const circleProps = {
x: step.viz.position.x,
y: 0,
};

return (
<Group
data-testid={'visualization-slot-' + index}
id={'visualization-slot-' + step.viz.id}
index={index}
key={index}
onDragOver={() => {
console.log('drag over slot');
}}
>
<Circle
name={`${index}`}
stroke={
step.model.type === 'START'
? 'rgb(0, 136, 206)'
: step.model.type === 'END'
? 'rgb(149, 213, 245)'
: 'rgb(204, 204, 204)'
}
fill={'white'}
strokeWidth={3}
width={CIRCLE_LENGTH}
height={CIRCLE_LENGTH}
{...circleProps}
/>
</Group>
);
})}
{children}
</Group>
);
};

export { VisualizationSlot };
56 changes: 32 additions & 24 deletions src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,35 +107,42 @@ const Dashboard = () => {
});
}, [previousYaml, yamlData]);

const updateIntegration = async (newSteps: any) => {
setIsError(false);
setIsLoading(true);

try {
const resp = await request.post({
endpoint: '/deployment/yaml',
contentType: 'application/json',
body: { name: 'Updated integration', steps: newSteps },
});

const data = await resp.text();
setYamlData(data);
} catch (err) {
console.error(err);
setIsError(true);
}

setIsLoading(false);
};

const deleteIntegrationStep = (stepsIndex: any) => {
// Remove Step from viewData.steps
// @ts-ignore
const newSteps = viewData.steps.filter((step, idx) => idx !== stepsIndex);

const getVizData = async () => {
setIsError(false);
setIsLoading(true);

try {
const resp = await request.post({
endpoint: '/deployment/yaml',
contentType: 'application/json',
body: { name: 'Updated integration', steps: newSteps },
});

const data = await resp.text();
console.log(JSON.stringify(data));
setYamlData(data);
console.log('Data set');
} catch (err) {
console.error(err);
setIsError(true);
}
updateIntegration(newSteps).catch((e) => {
console.error(e);
});
};

setIsLoading(false);
};
const replaceIntegrationStep = (newStep: any, oldStepIndex: number) => {
const newSteps = viewData.steps;
newSteps[oldStepIndex] = newStep;

getVizData().catch((e) => {
updateIntegration(newSteps).catch((e) => {
console.error(e);
});
};
Expand Down Expand Up @@ -173,7 +180,7 @@ const Dashboard = () => {
viz: {
data: { label: step.name },
id: uuidv4(),
position: { x: 300, y: window.innerHeight / 2 },
position: { x: 0, y: 0 },
temporary: false,
},
};
Expand All @@ -188,7 +195,7 @@ const Dashboard = () => {
switch (index) {
case 0:
// First item in `steps` array
inputStep.viz.position.x = 100;
inputStep.viz.position.x = 0; // control this in the `integration` Konva Group instead
break;
case steps.length - 1:
default:
Expand Down Expand Up @@ -256,6 +263,7 @@ const Dashboard = () => {
deleteIntegrationStep={deleteIntegrationStep}
isError={isError}
isLoading={isLoading}
replaceIntegrationStep={replaceIntegrationStep}
steps={vizData}
views={viewData.views}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export interface IVizStepProps {
id: string;
label: string;
position: {
x?: number;
y?: number;
x: number;
y: number;
};
temporary: boolean;
}
4 changes: 2 additions & 2 deletions src/utils/useDocumentTitle.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import { useEffect } from 'react';

// a custom hook for setting the page title
export function useDocumentTitle(title: string) {
React.useEffect(() => {
useEffect(() => {
const originalTitle = document.title;
document.title = title;

Expand Down
Loading