Skip to content

Commit

Permalink
Merge branch 'master' of github.com:inmanta/web-console into issue/60…
Browse files Browse the repository at this point in the history
…07-e2e-removal
  • Loading branch information
matborowczyk committed Nov 26, 2024
2 parents 444b216 + f5ef81e commit e98df38
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 156 deletions.
6 changes: 6 additions & 0 deletions changelogs/unreleased/5997-improve-useeffects.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
description: Improve behavior of useEffects in the Instance Composer when related inventories are updated
issue-nr: 5997
change-type: patch
destination-branches: [master]
sections:
minor-improvement: "{{description}}"
83 changes: 44 additions & 39 deletions src/UI/Components/Diagram/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DictModal, RightSidebar } from "./components";
import { Validation } from "./components/Validation";
import { createConnectionRules, createStencilState } from "./helpers";
import { diagramInit } from "./init";
import { ActionEnum } from "./interfaces";
import { StencilSidebar } from "./stencil";
import { CanvasWrapper } from "./styles";
import { ZoomHandlerService } from "./zoomHandler";
Expand Down Expand Up @@ -38,6 +39,7 @@ export const Canvas: React.FC<Props> = ({ editable }) => {
setServiceOrderItems,
diagramHandlers,
setDiagramHandlers,
setCellToEdit,
} = useContext(CanvasContext);
const Canvas = useRef<HTMLDivElement>(null);
const LeftSidebar = useRef<HTMLDivElement>(null);
Expand All @@ -54,7 +56,8 @@ export const Canvas: React.FC<Props> = ({ editable }) => {

// create the diagram & set diagram handlers and the scroller only when service models and main service is defined and the stencil state is ready
useEffect(() => {
if (!isStencilStateReady) {
//if diagram handlers are already set or the stencil state is not ready, return early to avoid re-creating the diagram or to creating it too early
if (!isStencilStateReady || diagramHandlers) {
return;
}

Expand All @@ -71,20 +74,19 @@ export const Canvas: React.FC<Props> = ({ editable }) => {

setDiagramHandlers(actions);

return () => {
setStencilState(createStencilState(mainService));
setIsStencilStateReady(false);
actions.removeCanvas();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mainService, serviceModels, isStencilStateReady]);

/**
* create the left-sidebar only if the left-sidebar ref, the scroller, the related inventories by inter-service relations, the main service and service models are defined
* It's done in separate useEffect to enable eventual re-renders of the sidebar independently of the diagram, e.g. when the related inventories by inter-service relations are loaded
* create the stencil sidebar and zoom handler only when the scroller, related inventories and main service are defined and the ref for sidebar and zoomHandler is there
*/
useEffect(() => {
if (!LeftSidebar.current || !scroller || !relatedInventoriesQuery.data) {
if (
!LeftSidebar.current ||
!ZoomHandler.current ||
!scroller ||
!relatedInventoriesQuery.data
) {
return;
}

Expand All @@ -95,10 +97,14 @@ export const Canvas: React.FC<Props> = ({ editable }) => {
mainService,
serviceModels,
);
const zoomHandler = new ZoomHandlerService(ZoomHandler.current, scroller);

setLeftSidebar(leftSidebar);

return () => leftSidebar.remove();
return () => {
leftSidebar.remove();
zoomHandler.remove();
};
}, [scroller, relatedInventoriesQuery.data, mainService, serviceModels]);

/**
Expand All @@ -109,42 +115,41 @@ export const Canvas: React.FC<Props> = ({ editable }) => {
if (!leftSidebar || !diagramHandlers || !isStencilStateReady) {
return;
}
const newInstances = new Map();

const cells = diagramHandlers.addInstance(serviceModels, instance);

cells.forEach((cell) => {
newInstances.set(cell.id, {
instance_id: cell.id,
service_entity: cell.get("entityName"),
config: {},
action: instance ? null : "create",
attributes: cell.get("instanceAttributes"),
embeddedTo: cell.get("embeddedTo"),
relatedTo: cell.get("relatedTo"),
const newInstances = new Map();
const copiedGraph = diagramHandlers.saveAndClearCanvas();

if (copiedGraph.cells.length > 0) {
diagramHandlers.loadState(copiedGraph);
} else {
const cells = diagramHandlers.addInstance(serviceModels, instance);

cells.forEach((cell) => {
newInstances.set(cell.id, {
instance_id: cell.id,
service_entity: cell.get("entityName"),
config: {},
action: instance ? null : ActionEnum.CREATE,
attributes: cell.get("instanceAttributes"),
embeddedTo: cell.get("embeddedTo"),
relatedTo: cell.get("relatedTo"),
});
});
});

setServiceOrderItems(newInstances);
setServiceOrderItems(newInstances);
}

return () => {
setStencilState(createStencilState(mainService));
setIsStencilStateReady(false);
setCellToEdit(null);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [diagramHandlers, isStencilStateReady, leftSidebar]);

/**
* add the zoom handler only when the scroller is defined as it's needed to create the zoom handler
*/
useEffect(() => {
if (!ZoomHandler.current || !scroller) {
return;
}
const zoomHandler = new ZoomHandlerService(ZoomHandler.current, scroller);

return () => zoomHandler.remove();
}, [scroller]);
}, [
diagramHandlers,
isStencilStateReady,
leftSidebar,
serviceModels,
instance,
]);

return (
<EventWrapper>
Expand Down
16 changes: 2 additions & 14 deletions src/UI/Components/Diagram/Context/EventWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
RelationCounterForCell,
} from "../interfaces";
import { ServiceEntityBlock } from "../shapes";
import { toggleDisabledStencil } from "../stencil/helpers";
import { CanvasContext } from "./Context";

/**
Expand Down Expand Up @@ -144,25 +145,12 @@ export const EventWrapper: React.FC<React.PropsWithChildren> = ({
break;
}

const elements = [
{ selector: `.body_${name}`, className: "stencil_accent-disabled" },
{ selector: `.bodyTwo_${name}`, className: "stencil_body-disabled" },
{ selector: `.text_${name}`, className: "stencil_text-disabled" },
];

const shouldDisable =
stencil.max !== null &&
stencil.max !== undefined &&
stencil.current >= stencil.max;

// As in the docstrings mentioned, If the current count of the instances created from given stencil is more than or equal to the max count, disable the stencil of given embedded entity
elements.forEach(({ selector, className }) => {
const element = document.querySelector(selector);

if (element) {
element.classList.toggle(className, shouldDisable);
}
});
toggleDisabledStencil(name, shouldDisable);

return stencilStateCopy;
});
Expand Down
25 changes: 2 additions & 23 deletions src/UI/Components/Diagram/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
relationId,
} from "./interfaces";
import { Link, ServiceEntityBlock } from "./shapes";
import { toggleDisabledStencil } from "./stencil/helpers";

/**
* Function that creates, appends and returns created Entity
Expand Down Expand Up @@ -215,29 +216,7 @@ export function appendInstance(
isBlockedFromEditing,
);

//disable Inventory Stencil for inter-service relation instance
const elements = [
{
selector: `.body_${appendedInstances[0].get("stencilName")}`,
className: "stencil_accent-disabled",
},
{
selector: `.bodyTwo_${appendedInstances[0].get("stencilName")}`,
className: "stencil_body-disabled",
},
{
selector: `.text_${appendedInstances[0].get("stencilName")}`,
className: "stencil_text-disabled",
},
];

elements.forEach(({ selector, className }) => {
const element = document.querySelector(selector);

if (element) {
element.classList.add(className);
}
});
toggleDisabledStencil(appendedInstances[0].get("stencilName"), true);
} else {
//If cell is already in the graph, we need to check if it got in its inter-service relations the one with id that corresponds with created instanceAsTable
let isConnected = false;
Expand Down
25 changes: 2 additions & 23 deletions src/UI/Components/Diagram/components/RightSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { words } from "@/UI/words";
import { CanvasContext, InstanceComposerContext } from "../Context/Context";
import { updateServiceOrderItems } from "../helpers";
import { ActionEnum, EventActionEnum } from "../interfaces";
import { toggleDisabledStencil } from "../stencil/helpers";
import { EntityForm } from "./EntityForm";

interface Props {
Expand Down Expand Up @@ -104,29 +105,7 @@ export const RightSidebar: React.FC<Props> = ({ editable }) => {
const stencilName = model.get("stencilName");

if (stencilName) {
//enable Inventory Stencil element for inter-service relation instance
const elements = [
{
selector: `.body_${stencilName}`,
className: "stencil_accent-disabled",
},
{
selector: `.bodyTwo_${stencilName}`,
className: "stencil_body-disabled",
},
{
selector: `.text_${stencilName}`,
className: "stencil_text-disabled",
},
];

elements.forEach(({ selector, className }) => {
const element = document.querySelector(selector);

if (element) {
element.classList.remove(className);
}
});
toggleDisabledStencil(stencilName, false);
}
};

Expand Down
53 changes: 46 additions & 7 deletions src/UI/Components/Diagram/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "./interfaces";
import { ComposerPaper } from "./paper";
import { ServiceEntityBlock } from "./shapes";
import { toggleDisabledStencil } from "./stencil/helpers";

/**
* Initializes the diagram.
Expand Down Expand Up @@ -114,9 +115,38 @@ export function diagramInit(
paper.unfreeze();

return {
removeCanvas: () => {
scroller.remove();
paper.remove();
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
loadState: (graphJSON: any) => {
graph.fromJSON(graphJSON);
graph.getCells().forEach((cell) => {
if (cell.get("type") !== "Link") {
const copy = graphJSON.cells.find((c) => c.id === cell.id);
const stencilName = cell.get("stencilName");

if (cell.get("isEmbedded")) {
document.dispatchEvent(
new CustomEvent("updateStencil", {
detail: {
name: cell.get("entityName"),
},
}),
);
}

if (stencilName) {
toggleDisabledStencil(stencilName, true);
}
cell.set("items", copy.items); // converted cells lacks "items" attribute
}
});
},

saveAndClearCanvas: () => {
const copy = graph.toJSON();

graph.getCells().forEach((cell) => cell.remove());

return copy;
},

addInstance: (
Expand Down Expand Up @@ -181,12 +211,21 @@ export function diagramInit(

export interface DiagramHandlers {
/**
* Removes the canvas.
* Saves the canvas state to JSON format and then clear the canvas.
*
* This function is responsible for cleaning up the canvas, and it's used when stencil/sidebar is updated to keep them aligned with each other
*/
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
saveAndClearCanvas: () => any; //graph.toJSON() return object of Any type, the type is different from dia.Graph so I couldn't use it there and left as explicit any

/**
* it loads the state of the canvas from the provided JSON object.
*
* This function is responsible for cleaning up the canvas when it is no longer needed.
* removes the scroller and paper elements.
* @param {any} graphJSON - The JSON object representing the state of the canvas. graph.toJSON() return object of Any type, the type is different from dia.Graph so I couldn't use it there and left as explicit any
* @returns {void}
*/
removeCanvas: () => void;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
loadState: (graphJSON: any) => void;

/**
* Adds an instance to the canvas.
Expand Down
Loading

0 comments on commit e98df38

Please sign in to comment.