diff --git a/Makefile b/Makefile
index 81447c9574b4cb9972b255df57106e1ab008a0a6..04b9b607cbd7d4f0416efbee3942c5f7f8f0cc6d 100644
--- a/Makefile
+++ b/Makefile
@@ -11,3 +11,8 @@
 docker: login
 	@docker build -t harbor.graphpolaris.com/graphpolaris/frontend:latest .
 	@docker push harbor.graphpolaris.com/graphpolaris/frontend\:latest
+
+push:
+	@pnpm lint
+	@pnpm test
+	@pnpm build
diff --git a/libs/shared/lib/components/Popup.tsx b/libs/shared/lib/components/Popup.tsx
index 13141bde35daacaae748891aca0c55ce085ba963..9066287af25880719faf27c5f053a41fb81d1d8e 100644
--- a/libs/shared/lib/components/Popup.tsx
+++ b/libs/shared/lib/components/Popup.tsx
@@ -1,10 +1,23 @@
-export const Popup = (props: { children: React.ReactNode; open: boolean; hAnchor: 'left' | 'right' }) => {
+import { useRef } from 'react';
+
+export const Popup = (props: {
+  children: React.ReactNode;
+  open: boolean;
+  hAnchor: 'left' | 'right';
+  className?: string;
+  offset?: string;
+}) => {
+  const ref = useRef<HTMLDivElement>(null);
+
   return (
     <>
       {props.open && (
         <div
-          className="absolute z-10 max-w-[20rem] bg-white flex flex-col gap-2 animate-openmenu p-0 m-0"
-          style={props.hAnchor === 'left' ? { left: '5rem' } : { right: '5rem' }}
+          ref={ref}
+          className={
+            'absolute z-50 max-w-[20rem] bg-white flex flex-col gap-2 animate-openmenu p-0 m-0 ' + (props.className ? props.className : '')
+          }
+          style={props.hAnchor === 'right' ? { left: props.offset || 0 } : { right: props.offset || 0 }}
         >
           {props.children}
         </div>
diff --git a/libs/shared/lib/components/forms/index.tsx b/libs/shared/lib/components/forms/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..561852353bc47599c1c5b1d46e553e401ebd8393
--- /dev/null
+++ b/libs/shared/lib/components/forms/index.tsx
@@ -0,0 +1,51 @@
+import React, { PropsWithChildren, useEffect, useLayoutEffect, useRef, useState } from 'react';
+import CloseIcon from '@mui/icons-material/Close';
+
+export const FormDiv = React.forwardRef<HTMLDivElement, PropsWithChildren<{ className?: string; hAnchor?: string; offset?: string }>>(
+  (props, ref) => {
+    return (
+      <div
+        className={'absolute opacity-100 transition-opacity group-hover:opacity-100 z-50 ' + (props.className ? props.className : '')}
+        ref={ref}
+        style={props.hAnchor === 'left' ? { left: props.offset || 0 } : { right: props.offset || '5rem' }}
+      >
+        {props.children}
+      </div>
+    );
+  }
+);
+export const FormCard = (props: PropsWithChildren<{ className?: string }>) => (
+  <div className={'card card-bordered bg-white rounded-none ' + (props.className ? props.className : '')}>{props.children}</div>
+);
+export const FormBody = ({
+  children,
+  ...props
+}: PropsWithChildren<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>>) => (
+  <form className="card-body px-0 w-72 py-5" {...props}>
+    {children}
+  </form>
+);
+export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ title: string; onClose: () => void }>) => (
+  <div className="card-title p-5 py-0 flex w-full">
+    <h2 className="w-full">{title}</h2>
+    <button className="btn btn-circle btn-sm btn-ghost" onClick={() => onClose()}>
+      <CloseIcon fontSize="small" />
+    </button>
+  </div>
+);
+export const FormHBar = () => <div className="divider m-0"></div>;
+export const FormControl = ({ children }: PropsWithChildren) => <div className="form-control px-5">{children}</div>;
+export const FormActions = (props: { onClose: () => void }) => (
+  <div className="card-actions mt-1 w-full px-5 flex flex-row">
+    <button
+      className="btn btn-secondary flex-grow"
+      onClick={(e) => {
+        e.preventDefault();
+        props.onClose();
+      }}
+    >
+      Cancel
+    </button>
+    <button className="btn btn-primary flex-grow">Apply</button>
+  </div>
+);
diff --git a/libs/shared/lib/querybuilder/panel/querySettingsDialog.tsx b/libs/shared/lib/querybuilder/panel/querySettingsDialog.tsx
index ccd4ffda4ac0e3f47209e558692dfd83c491b92a..d5ccff8e3107de3f13a0725f3ff6c680fb6476bd 100644
--- a/libs/shared/lib/querybuilder/panel/querySettingsDialog.tsx
+++ b/libs/shared/lib/querybuilder/panel/querySettingsDialog.tsx
@@ -5,6 +5,7 @@ import CloseIcon from '@mui/icons-material/Close';
 import { useAppDispatch, useQuerybuilderSettings } from '../../data-access';
 import { QueryBuilderSettings, setQuerybuilderSettings } from '../../data-access/store/querybuilderSlice';
 import { addWarning } from '../../data-access/store/configSlice';
+import { FormBody, FormCard, FormDiv, FormHBar, FormTitle } from '../../components/forms';
 
 export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, DialogProps>((props, ref) => {
   const qb = useQuerybuilderSettings();
@@ -31,22 +32,16 @@ export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, DialogProps>
   return (
     <>
       {props.open && (
-        <div className="absolute right-20 bottom-5 opacity-100 transition-opacity group-hover:opacity-100 z-50 " ref={ref}>
-          <div className="card card-bordered bg-white rounded-none">
-            <form
-              className="card-body px-0 w-72 py-5"
+        <FormDiv ref={ref} className="" hAnchor="right">
+          <FormCard>
+            <FormBody
               onSubmit={(e) => {
                 e.preventDefault();
                 submit();
               }}
             >
-              <div className="card-title p-5 py-0 flex w-full">
-                <h2 className="w-full">Query Settings</h2>
-                <button className="btn btn-circle btn-sm btn-ghost" onClick={() => props.onClose()}>
-                  <CloseIcon fontSize="small" />
-                </button>
-              </div>
-              <div className="divider m-0"></div>
+              <FormTitle title="Query Settings" onClose={props.onClose} />
+              <FormHBar />
               <div className="form-control px-5">
                 <label className="label">
                   <span className="label-text">Limit - Max number of results</span>
@@ -59,7 +54,7 @@ export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, DialogProps>
                   onChange={(e) => setState({ ...state, limit: parseInt(e.target.value) })}
                 />
               </div>
-              <div className="divider m-0"></div>
+              <FormHBar />
               <div className="form-control px-5 flex flex-row gap-3">
                 <div className="">
                   <label className="label">
@@ -100,7 +95,7 @@ export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, DialogProps>
                   />
                 </div>
               </div>
-              <div className="divider m-0"></div>
+              <FormHBar />
               <div className="card-actions mt-1 w-full px-5 flex flex-row">
                 <button
                   className="btn btn-secondary flex-grow"
@@ -113,9 +108,9 @@ export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, DialogProps>
                 </button>
                 <button className="btn btn-primary flex-grow">Apply</button>
               </div>
-            </form>
-          </div>
-        </div>
+            </FormBody>
+          </FormCard>
+        </FormDiv>
       )}
     </>
   );
diff --git a/libs/shared/lib/schema/model/reactflow.tsx b/libs/shared/lib/schema/model/reactflow.tsx
index 88dc8dc5512e9753b27e36e604de38c177fd2c5e..cecfd06a28715ad7c9567d2d685766ee6eaf0ff2 100644
--- a/libs/shared/lib/schema/model/reactflow.tsx
+++ b/libs/shared/lib/schema/model/reactflow.tsx
@@ -35,9 +35,10 @@ export interface SchemaReactflowData {
   nodeCount: number;
   summedNullAmount: number;
   label: string;
+  type: string;
 }
 
-export interface SchemaReactflowNode extends SchemaReactflowData {
+export interface SchemaReactflowEntity extends SchemaReactflowData {
   // handles: string[];
   connectedRatio: number;
   name: string;
@@ -51,7 +52,7 @@ export interface SchemaReactflowRelation extends SchemaReactflowData {
   toRatio: number;
 }
 
-export interface SchemaReactflowNodeWithFunctions extends SchemaReactflowNode {
+export interface SchemaReactflowNodeWithFunctions extends SchemaReactflowEntity {
   toggleNodeQualityPopup: (id: string) => void;
   toggleAttributeAnalyticsPopupMenu: (id: string) => void;
 }
diff --git a/libs/shared/lib/schema/panel/schema.tsx b/libs/shared/lib/schema/panel/schema.tsx
index 9a0c2c5b2878f40bd99426328c4579c292b4b385..a7ea87e3a3b703c581141d1d61534efeaf3c3a17 100644
--- a/libs/shared/lib/schema/panel/schema.tsx
+++ b/libs/shared/lib/schema/panel/schema.tsx
@@ -102,8 +102,8 @@ export const Schema = (props: Props) => {
     if (schemaGraphology == undefined || schemaGraphology.order == 0) {
       return;
     }
-    // console.log(schemaGraphology.export());
-    // console.log(schemaLayout);
+
+    console.log(schemaGraph);
 
     updateLayout();
     const expandedSchema = schemaExpandRelation(schemaGraphology);
diff --git a/libs/shared/lib/schema/panel/schemaDialog.tsx b/libs/shared/lib/schema/panel/schemaDialog.tsx
index 17a986178bd1f8bc75a091ef93ab78cfa5d817c8..46c10a721dbfb4b8370bd71605131177fee740f4 100644
--- a/libs/shared/lib/schema/panel/schemaDialog.tsx
+++ b/libs/shared/lib/schema/panel/schemaDialog.tsx
@@ -4,6 +4,7 @@ import React from 'react';
 import CloseIcon from '@mui/icons-material/Close';
 import { useAppDispatch, useSchemaSettings } from '../../data-access';
 import { SchemaSettings, setSchemaSettings } from '../../data-access/store/schemaSlice';
+import { FormActions, FormBody, FormCard, FormControl, FormHBar, FormTitle, FormDiv } from '../../components/forms';
 
 export const SchemaDialog = React.forwardRef<HTMLDivElement, DialogProps>((props, ref) => {
   const settings = useSchemaSettings();
@@ -23,23 +24,17 @@ export const SchemaDialog = React.forwardRef<HTMLDivElement, DialogProps>((props
   return (
     <>
       {props.open && (
-        <div className="absolute  opacity-100 transition-opacity group-hover:opacity-100 z-50 " ref={ref}>
-          <div className="card absolute card-bordered bg-white rounded-none">
-            <form
-              className="card-body px-0 w-72 py-5"
+        <FormDiv ref={ref}>
+          <FormCard>
+            <FormBody
               onSubmit={(e) => {
                 e.preventDefault();
                 submit();
               }}
             >
-              <div className="card-title p-5 py-0 flex w-full">
-                <h2 className="w-full">Quick Settings</h2>
-                <button className="btn btn-circle btn-sm btn-ghost" onClick={() => props.onClose()}>
-                  <CloseIcon fontSize="small" />
-                </button>
-              </div>
-              <div className="divider m-0"></div>
-              <div className="form-control px-5">
+              <FormTitle title="Quick Settings" onClose={props.onClose} />
+              <FormHBar />
+              <FormControl>
                 <label className="label cursor-pointer w-fit gap-2 px-0 py-1">
                   <input type="checkbox" checked={true} onChange={(e) => {}} className="checkbox checkbox-xs" />
                   <span className="label-text">Points</span>
@@ -52,23 +47,23 @@ export const SchemaDialog = React.forwardRef<HTMLDivElement, DialogProps>((props
                   <input type="checkbox" checked={true} onChange={(e) => {}} className="checkbox checkbox-xs" />
                   <span className="label-text">Line</span>
                 </label>
-              </div>
-              <div className="divider m-0"></div>
-              <div className="form-control px-5">
+              </FormControl>
+              <FormHBar />
+              <FormControl>
                 <label className="label">
                   <span className="label-text">Opacity</span>
                 </label>
                 <input type="range" min={0} max="100" value="40" onChange={(e) => {}} className="range range-sm" />
-              </div>
-              <div className="divider m-0"></div>
-              <div className="form-control px-5">
+              </FormControl>
+              <FormHBar />
+              <FormControl>
                 <label className="label">
                   <span className="label-text">Histogram</span>
                 </label>
                 ...
-              </div>
-              <div className="divider m-0"></div>
-              <div className="form-control px-5">
+              </FormControl>
+              <FormHBar />
+              <FormControl>
                 <label className="label">
                   <span className="label-text">Type of Connection</span>
                 </label>
@@ -92,24 +87,13 @@ export const SchemaDialog = React.forwardRef<HTMLDivElement, DialogProps>((props
                     Bezier
                   </option>
                 </select>
-              </div>
-              <div className="divider m-0"></div>
+              </FormControl>
+              <FormHBar />
 
-              <div className="card-actions mt-1 w-full px-5 flex flex-row">
-                <button
-                  className="btn btn-secondary flex-grow"
-                  onClick={(e) => {
-                    e.preventDefault();
-                    props.onClose();
-                  }}
-                >
-                  Cancel
-                </button>
-                <button className="btn btn-primary flex-grow">Apply</button>
-              </div>
-            </form>
-          </div>
-        </div>
+              <FormActions onClose={props.onClose} />
+            </FormBody>
+          </FormCard>
+        </FormDiv>
       )}
     </>
   );
diff --git a/libs/shared/lib/schema/panel/view-model/SchemaViewModel.t b/libs/shared/lib/schema/panel/view-model/SchemaViewModel.t
deleted file mode 100644
index f3a434ca5a7b5cffbe1479041ce816f9a62e94c3..0000000000000000000000000000000000000000
--- a/libs/shared/lib/schema/panel/view-model/SchemaViewModel.t
+++ /dev/null
@@ -1,928 +0,0 @@
-/**
- * This program has been developed by students from the bachelor Computer Science at
- * Utrecht University within the Software Project course.
- * © Copyright Utrecht University (Department of Information and Computing Sciences)
- */
-import SchemaViewModel from './SchemaViewModel';
-import { Node, ReactFlowInstance } from 'reactflow';
-import {
-  doBoxesOverlap,
-  makeBoundingBox,
-  calcWidthRelationNodeBox,
-  calcWidthEntityNodeBox,
-  numberPredicates,
-} from '../../schema-utils/utils';
-import {
-  AttributeAnalyticsData,
-  AttributeAnalyticsPopupMenuNode,
-  AttributeCategory,
-  AttributeWithData,
-  NodeQualityDataForEntities,
-  NodeQualityDataForRelations,
-  NodeQualityPopupNode,
-  NodeType,
-} from '../../schema-utils/Types';
-import React from 'react';
-import '../../view/graph-schema/flow-components/nodes/popupmenus/NodeQualityPopupNode.scss';
-
-import '../../view/graph-schema/flow-components/nodes/popupmenus/AttributeAnalyticsPopupMenuNode.scss';
-
-// import {
-//   exportComponentAsPNG,
-//   Params,
-//   PDFOptions,
-// } from 'react-component-export-image';
-
-import { getTextOfJSDocComment } from 'typescript';
-
-/** This class is responsible for updating and creating the graph schema. */
-export default class SchemaViewModelImpl
-  extends AbstractBaseViewModelImpl
-  implements SchemaViewModel
-{
-  private reactFlowInstance: any;
-  private relationCounter: number;
-  private drawOrderUseCase: DrawOrderUseCase;
-  private nodeUseCase: NodeUseCase;
-  private edgeUseCase: EdgeUseCase;
-  private graphUseCase: GraphUseCase;
-  private placeInQueryBuilder: (name: string, type: string) => void;
-  public elements: SchemaElements = { nodes: [], edges: [], selfEdges: [] };
-  public zoom: number;
-  public visible: boolean;
-
-  public nodeQualityPopup: NodeQualityPopupNode;
-  public attributeAnalyticsPopupMenu: AttributeAnalyticsPopupMenuNode;
-
-  public nodeQualityData: Record<
-    string,
-    NodeQualityDataForEntities | NodeQualityDataForRelations
-  >;
-  public attributeAnalyticsData: Record<string, AttributeAnalyticsData>;
-
-  private entityPopupOffsets = {
-    nodeQualityOffset: {
-      x: SchemaThemeHolder.entity.width,
-      y: SchemaThemeHolder.entity.height - 3,
-    },
-    attributeQualityOffset: {
-      x: SchemaThemeHolder.entity.width,
-      y: -SchemaThemeHolder.entity.height + 12,
-    },
-  };
-
-  private relationPopupOffsets = {
-    nodeQualityOffset: {
-      x: SchemaThemeHolder.relation.width + 50,
-      y: SchemaThemeHolder.relation.height + 2,
-    },
-    attributeQualityOffset: {
-      x: SchemaThemeHolder.relation.width + 50,
-      y: -SchemaThemeHolder.relation.height + 17,
-    },
-  };
-  // React flow reference for positioning on drop.
-  public myRef: React.RefObject<HTMLDivElement>;
-
-  public nodeTypes = {
-    entity: EntityNode,
-    relation: RelationNode,
-    nodeQualityEntityPopup: NodeQualityEntityPopupNode,
-    nodeQualityRelationPopup: NodeQualityRelationPopupNode,
-    attributeAnalyticsPopupMenu: AttributeAnalyticsPopupMenu,
-  };
-
-  public edgeTypes = {
-    nodeEdge: NodeEdge,
-    selfEdge: SelfEdge,
-  };
-
-  public constructor(
-    drawOrderUseCase: DrawOrderUseCase,
-    nodeUseCase: NodeUseCase,
-    edgeUseCase: EdgeUseCase,
-    graphUseCase: GraphUseCase,
-    addAttribute: (name: string, type: string) => void
-  ) {
-    super();
-
-    this.myRef = React.createRef();
-    // TODO: These values need to not be hardcoded.
-    this.zoom = 1.3;
-    this.relationCounter = 0;
-    this.reactFlowInstance = 0;
-    this.visible = true;
-    this.edgeUseCase = edgeUseCase;
-    this.nodeUseCase = nodeUseCase;
-    this.drawOrderUseCase = drawOrderUseCase;
-    this.graphUseCase = graphUseCase;
-    this.placeInQueryBuilder = addAttribute;
-
-    this.nodeQualityPopup = this.emptyNodeQualityPopupNode();
-    this.attributeAnalyticsPopupMenu =
-      this.emptyAttributeAnalyticsPopupMenuNode();
-
-    this.nodeQualityData = {};
-    this.attributeAnalyticsData = {};
-  }
-
-  /**
-   * Containts all function calls to create the graph schema.
-   * Notifies the view about the changes at the end.
-   */
-  public createSchema = (elements: SchemaElements): SchemaElements => {
-    let drawOrder: Node[];
-
-    drawOrder = this.drawOrderUseCase.createDrawOrder(elements);
-    // Create nodes with start position.
-    elements.nodes = this.nodeUseCase.setEntityNodePosition(
-      drawOrder,
-      {
-        x: 0,
-        y: 0,
-      },
-      this.toggleNodeQualityPopup,
-      this.toggleAttributeAnalyticsPopupMenu
-    );
-
-    // Create the relation-nodes.
-    elements.edges.forEach((relation) => {
-      this.createRelationNode(
-        relation.id,
-        relation.data.attributes,
-        relation.data.collection,
-        elements
-      );
-    });
-
-    elements.selfEdges.forEach((relation) => {
-      this.createRelationNode(
-        relation.id,
-        relation.data.attributes,
-        relation.data.collection,
-        elements
-      );
-    });
-
-    // Complement the relation-nodes with extra data that is now accessible.
-    elements.edges = this.edgeUseCase.positionEdges(
-      drawOrder,
-      elements,
-      this.setRelationNodePosition
-    );
-
-    this.visible = false;
-    this.notifyViewAboutChanges();
-    return elements;
-  };
-
-  /**
-   * consumes the schema send from the backend and uses it to create the SchemaElements for the schema
-   * @param jsonObject
-   */
-  public consumeMessageFromBackend(jsonObject: unknown): void {
-    if (isSchemaResult(jsonObject)) {
-      // This is always the first message to receive, so reset the global variables.
-      this.visible = false;
-      this.createSchema({ nodes: [], edges: [], selfEdges: [] });
-
-      this.relationCounter = 0;
-
-      this.nodeQualityPopup.isHidden = true;
-      this.attributeAnalyticsPopupMenu.isHidden = true;
-
-      /* Create the graph-schema and add the popup-menu's for as far as possible. 
-      * Runs underlying useCase trice to fix a bug with lingering selfEdges. 
-      TODO: clean this up.*/
-      this.elements = this.createSchema({
-        nodes: [],
-        edges: [],
-        selfEdges: [],
-      });
-      let schemaElements =
-        this.graphUseCase.createGraphFromInputData(jsonObject);
-      this.elements = this.createSchema(schemaElements);
-      this.notifyViewAboutChanges();
-      //End weird fix
-
-      this.visible = true;
-      schemaElements = this.graphUseCase.createGraphFromInputData(jsonObject);
-      this.elements = this.createSchema(schemaElements);
-      this.notifyViewAboutChanges();
-
-      this.addAttributesToAnalyticsPopupMenus(jsonObject);
-    } else if (isAttributeDataEntity(jsonObject)) {
-      // Add all information from the received message to the popup-menu's.
-      this.addAttributeDataToPopupMenusAndElements(
-        jsonObject,
-        'gsa_node_result',
-        this.elements
-      );
-    } else if (isAttributeDataRelation(jsonObject)) {
-      // Add all information from the received message to the popup-menu's.
-      this.addAttributeDataToPopupMenusAndElements(
-        jsonObject,
-        'gsa_edge_result',
-        this.elements
-      );
-    } else {
-      // TODO: This should be an error screen eventually.
-      console.log('This is no valid input!');
-    }
-    this.notifyViewAboutChanges();
-  }
-
-  /**
-   * Create a reference to the reactflow schema component on load
-   * @param reactFlowInstance reactflow instance
-   */
-  public onInit = (reactFlowInstance: ReactFlowInstance<any>): void => {
-    this.reactFlowInstance = reactFlowInstance;
-  };
-
-  /**
-   * Complements the relation-nodes with data that had default values before.
-   * It determines the position of the relation-node and the connected entity-nodes.
-   * @param centerX Used to determine the center of the edge.
-   * @param centerY Used to determine the center of the edge.
-   * @param id The id of the relation node.
-   * @param from The id of the entity where the edge should connect from.
-   * @param to The id of the entity where the edge should connect to.
-   * @param attributes The attributes of the relation node.
-   */
-  public setRelationNodePosition = (
-    centerX: number,
-    centerY: number,
-    id: string,
-    from: string,
-    to: string,
-    attributes: Attribute[]
-  ): void => {
-    let width: number;
-    let overlap: boolean;
-    let y: number;
-
-    // Check if the relation-node is in the list of nodes.
-    let relation = this.elements.nodes.find((node) => node.id == id);
-    if (relation == undefined)
-      throw new Error('Relation ' + id + ' does not exist.');
-
-    // Height of relation/entity + external buttons.
-    const height = 20;
-
-    width =
-      SchemaThemeHolder.relation.width +
-      calcWidthRelationNodeBox(attributes.length, 0);
-    let x = centerX - SchemaThemeHolder.relation.width / 2;
-    y = centerY - height / 2;
-
-    while (this.CheckForOverlap(x, y, width, height)) {
-      y = y + 1;
-    }
-
-    // Replace the default values for the correct values.
-    relation.position = { x: x, y: y };
-    relation.data.from = from;
-    relation.data.to = to;
-
-    this.relationCounter++;
-    if (this.relationCounter == this.elements.edges.length) {
-      this.fitToView();
-    }
-    this.notifyViewAboutChanges();
-  };
-
-  /**
-   * Creates a new relation-node with some default values.
-   * @param id The id of the relation node.
-   * @param attributes The list of attributes that this relation-node has.
-   * @param collection The collection this relation-node is in.
-   */
-  public createRelationNode = (
-    id: string,
-    attributes: Attribute[],
-    collection: string,
-    schemaElements: SchemaElements
-  ): void => {
-    // Height of relation/entity + external buttons.
-    const height = 20;
-    const width =
-      SchemaThemeHolder.relation.width +
-      calcWidthRelationNodeBox(attributes.length, 0);
-
-    schemaElements.nodes.push({
-      type: NodeType.relation,
-      id: id,
-      position: { x: 0, y: 0 },
-      data: {
-        width: width,
-        height: height,
-        collection: collection,
-        attributes: attributes,
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-        toggleNodeQualityPopup: this.toggleNodeQualityPopup,
-        toggleAttributeAnalyticsPopupMenu:
-          this.toggleAttributeAnalyticsPopupMenu,
-      },
-    });
-  };
-
-  /**
-   * Calculates the width and height of the graph-schema-panel.
-   * @returns { number; number } The width and the height of the graph-schema-panel.
-   */
-  public getWidthHeight = (): { width: number; height: number } => {
-    const reactFlow = this.myRef.current as HTMLDivElement;
-    const reactFlowBounds = reactFlow.getBoundingClientRect();
-    const width = reactFlowBounds.right - reactFlowBounds.left;
-    const height = reactFlowBounds.bottom - reactFlowBounds.top;
-    return { width, height };
-  };
-
-  /** Placeholder function for fitting the schema into the view. */
-  public fitToView = (): void => {
-    let minX = Infinity;
-    let maxX = 0;
-    let minY = Infinity;
-    let maxY = 0;
-    let xZoom = 0;
-    let yZoom = 0;
-
-    let setY = 0;
-    let setX = 0;
-    let setZoom = 0;
-
-    this.elements.nodes.forEach((node) => {
-      const attributeCount: number = node.data.attributes.length;
-      const nodeCount: number = node.data.nodeCount;
-      const nodeWidth: number =
-        node.type == NodeType.entity
-          ? SchemaThemeHolder.entity.width +
-            calcWidthEntityNodeBox(attributeCount, nodeCount)
-          : SchemaThemeHolder.relation.width +
-            calcWidthRelationNodeBox(attributeCount, nodeCount);
-
-      if (node.position.x < minX) minX = node.position.x;
-      if (node.position.x + nodeWidth > maxX)
-        maxX = node.position.x + nodeWidth;
-      if (node.position.y < minY) minY = node.position.y;
-      if (node.position.y + node.data.height > maxY)
-        maxY = node.position.y + node.data.height;
-    });
-
-    minX -= 10;
-    maxX += 90;
-
-    const { width, height } = this.getWidthHeight();
-
-    // Correct for X and Y position with width and height.
-    let nodeWidth = Math.abs(maxX - minX);
-    let nodeHeight = Math.abs(maxY - minY);
-
-    setX = minX * -1;
-    xZoom = width / nodeWidth;
-    setY = minY * -1;
-    yZoom = height / nodeHeight;
-
-    // TODO: Correct position and zoom for selfEdges.
-
-    if (xZoom >= yZoom) {
-      setZoom = yZoom;
-      setX = setX + width / 2 - nodeWidth / 2;
-    } else {
-      setZoom = xZoom;
-      setY = setY + height / 2 - nodeHeight / 2;
-    }
-
-    try {
-      this.reactFlowInstance.setTransform({ x: setX, y: setY, zoom: setZoom });
-    } catch {
-      console.log('this.reactFlowInstance is undefined!');
-    }
-  };
-
-  /**
-   * this function check for a relation node if it overlaps with any of the other nodes in the schema.
-   * It creates boundingbox for the node and checks with all the other nodes if the boxes overlap.
-   * @param x Top left x of the node.
-   * @param y Top left y of the node.
-   * @param width Width of the node.
-   * @param height Height of the node.
-   * @returns {bool} whether it overlaps.*/
-  public CheckForOverlap = (
-    x: number,
-    y: number,
-    width: number,
-    height: number
-  ): boolean => {
-    const boundingBox = makeBoundingBox(x, y, width, height);
-    let elements = this.elements;
-
-    let boundingTemporary: BoundingBox;
-    for (let i = 0; i < elements.nodes.length; i++) {
-      boundingTemporary = makeBoundingBox(
-        elements.nodes[i].position.x,
-        elements.nodes[i].position.y,
-        elements.nodes[i].data.width,
-        elements.nodes[i].data.height
-      );
-      if (doBoxesOverlap(boundingBox, boundingTemporary)) {
-        return true;
-      }
-    }
-    return false;
-  };
-
-  /** Exports the schema builder to a beautiful png file */
-  public exportToPNG(): void {
-    const { width, height } = this.getWidthHeight();
-    exportComponentAsPNG(this.myRef, {
-      fileName: 'schemaBuilder',
-      pdfOptions: {
-        x: 0,
-        y: 0,
-        w: width,
-        h: height,
-        unit: 'px',
-      } as Partial<PDFOptions>,
-    } as Params);
-  }
-
-  /** Not implemented method for exporting the schema builder visualisation to PDF. */
-  public exportToPDF(): void {
-    console.log('Method not implemented.');
-  }
-
-  /** Attach the listener to the broker. */
-  public subscribeToSchemaResult(): void {
-    Broker.instance().subscribe(this, 'schema_result');
-  }
-
-  /** Detach the listener to the broker. */
-  public unSubscribeFromSchemaResult(): void {
-    Broker.instance().unSubscribe(this, 'schema_result');
-  }
-
-  /** Attach the listeners to the broker. */
-  public subscribeToAnalyticsData(): void {
-    Broker.instance().subscribe(this, 'gsa_node_result');
-    Broker.instance().subscribe(this, 'gsa_edge_result');
-  }
-
-  /** Detach the listeners to the broker. */
-  public unSubscribeFromAnalyticsData(): void {
-    Broker.instance().unSubscribe(this, 'gsa_node_result');
-    Broker.instance().unSubscribe(this, 'gsa_edge_result');
-  }
-
-  /**
-   * This function is used by relation and entity nodes to hide or show the node-quality popup of that node.
-   * @param id of the node for which the new popup is.
-   */
-  public toggleNodeQualityPopup = (id: string): void => {
-    const popup = this.nodeQualityPopup;
-
-    // Hide the popup if the current popup is visible and if the popup belongs to the same node as the given id.
-    if (popup.nodeID == id && !popup.isHidden) popup.isHidden = true;
-    // Else make and show a new popup for the node with the given id.
-    else this.updateNodeQualityPopup(id, this.elements);
-
-    this.notifyViewAboutChanges();
-  };
-
-  /**
-   * This function shows and updates the node-quality popup for the node which has the given id.
-   * @param id of the node for which the new popup is.
-   */
-  private updateNodeQualityPopup(id: string, schemaElements: SchemaElements) {
-    let node = schemaElements.nodes.find((node) => node.id == id);
-    if (node == undefined) {
-      throw new Error('Node does not exist therefore no popup can be shown.');
-    }
-
-    const popup = this.nodeQualityPopup;
-    popup.nodeID = id;
-    popup.isHidden = false;
-    popup.data = this.nodeQualityData[id];
-
-    if (node.type == 'entity') {
-      // Make changes to the popup, to make it a popup for entities.
-      this.updateToNodeQualityEntityPopup(node);
-    } else {
-      // Make changes to the popup, to make it a popup for relations.
-      this.updateToNodeQualityRelationPopup(node);
-    }
-
-    // Hide the attributeAnalyticsPopupMenu so that only one popup is displayed.
-    this.attributeAnalyticsPopupMenu.isHidden = true;
-
-    this.notifyViewAboutChanges();
-    this.relationCounter++;
-    if (this.relationCounter == schemaElements.edges.length) {
-      this.fitToView();
-    }
-  }
-
-  /**
-   * This displays the new node-quality popup for the given entity.
-   * @param node This is the entity of which you want to display the popup.
-   */
-  private updateToNodeQualityEntityPopup(node: Node) {
-    const popup = this.nodeQualityPopup;
-    const offset = this.entityPopupOffsets.nodeQualityOffset;
-
-    popup.position = {
-      x: node.position.x + offset.x,
-      y: node.position.y + offset.y,
-    };
-
-    popup.type = 'nodeQualityEntityPopup';
-  }
-
-  /**
-   * This displays the new node-quality popup for the given relation.
-   * @param node This is the relation of which you want to display the popup.
-   */
-  private updateToNodeQualityRelationPopup(node: Node) {
-    const popup = this.nodeQualityPopup;
-    const offset = this.relationPopupOffsets.nodeQualityOffset;
-
-    popup.position = {
-      x: node.position.x + offset.x,
-      y: node.position.y + offset.y,
-    };
-
-    popup.type = 'nodeQualityRelationPopup';
-  }
-
-  /**
-   * This function is used by relation and entity nodes to hide or show the attribute analyics popup menu of that node.
-   * @param id of the node for which the popup is.
-   */
-  public toggleAttributeAnalyticsPopupMenu = (id: string): void => {
-    const popupMenu = this.attributeAnalyticsPopupMenu;
-
-    // Hide the popup menu if the current popup menu is visible and if the popup menu belongs to the same node as the given id.
-    if (popupMenu.nodeID == id && !popupMenu.isHidden)
-      popupMenu.isHidden = true;
-    // Else make and show a new popup menu for the node with the given id.
-    else this.updateAttributeAnalyticsPopupMenu(id, this.elements);
-
-    this.notifyViewAboutChanges();
-  };
-
-  /**
-   * This displays the attribute-analytics popup menu for the given node (entity or relation).
-   * It removes the other menus from the screen.
-   * @param id This is the id of the node (entity or relation) of which you want to display the menu.
-   */
-  public updateAttributeAnalyticsPopupMenu = (
-    id: string,
-    schemaElements: SchemaElements
-  ): void => {
-    const node = schemaElements.nodes.find((node) => node.id == id);
-
-    if (node == undefined)
-      throw new Error(
-        'Node ' + id + ' does not exist therefore no popup menu can be shown.'
-      );
-
-    const popupMenu = this.attributeAnalyticsPopupMenu;
-    // Make new popup menu for the node.
-    popupMenu.nodeID = id;
-    popupMenu.isHidden = false;
-    popupMenu.data = { ...this.attributeAnalyticsData[id] };
-
-    if (node.type == NodeType.entity) {
-      const offset = this.entityPopupOffsets.attributeQualityOffset;
-      popupMenu.position = {
-        x: node.position.x + offset.x,
-        y: node.position.y + offset.y,
-      };
-    } else {
-      const offset = this.relationPopupOffsets.attributeQualityOffset;
-      popupMenu.position = {
-        x: node.position.x + offset.x,
-        y: node.position.y + offset.y,
-      };
-    }
-
-    // Hide the nodeQualityPopup so that only one popup is displayed.
-    this.nodeQualityPopup.isHidden = true;
-
-    this.notifyViewAboutChanges();
-  };
-
-  /** This removes the node quality popup from the screen. */
-  public hideNodeQualityPopup = (): void => {
-    this.nodeQualityPopup.isHidden = true;
-    this.notifyViewAboutChanges();
-  };
-
-  /** This removes the attribute-analytics popup menu from the screen. */
-  public hideAttributeAnalyticsPopupMenu = (): void => {
-    this.attributeAnalyticsPopupMenu.isHidden = true;
-    this.notifyViewAboutChanges();
-  };
-
-  /**
-   * This sets all the data for the attributesPopupMenu without the attribute data.
-   * @param schemaResult This is the schema result that you get (so no attribute data yet).
-   */
-  addAttributesToAnalyticsPopupMenus = (schemaResult: Schema): void => {
-    this.nodeQualityData = {};
-    this.attributeAnalyticsData = {};
-
-    // Firstly, loop over all entities and add the quality-data (as far as possible).
-    // Then add the attribute-data (as far as possible).
-    schemaResult.nodes.forEach((node) => {
-      this.nodeQualityData[node.name] = {
-        nodeCount: 0,
-        notConnectedNodeCount: 0,
-        attributeNullCount: 0,
-        isAttributeDataIn: false,
-        onClickCloseButton: this.hideNodeQualityPopup,
-      };
-      let attributes: any = [];
-      node.attributes.forEach((attribute) => {
-        attributes.push({
-          attribute: attribute,
-          category: AttributeCategory.undefined,
-          nullAmount: 0,
-        });
-      });
-      this.attributeAnalyticsData[node.name] = {
-        nodeID: node.name,
-        nodeType: NodeType.entity,
-        attributes: attributes,
-        isAttributeDataIn: false,
-        onClickCloseButton: this.hideAttributeAnalyticsPopupMenu,
-        onClickPlaceInQueryBuilderButton: this.placeInQueryBuilder,
-        searchForAttributes: this.searchForAttributes,
-        resetAttributeFilters: this.resetAttributeFilters,
-        applyAttributeFilters: this.applyAttributeFilters,
-      };
-    });
-    // Secondly, loop over all relations and add the quality-data (as far as possible).
-    // Then add the attribute-data (as far as possible).
-    schemaResult.edges.forEach((edge) => {
-      this.nodeQualityData[edge.collection] = {
-        nodeCount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-        attributeNullCount: 0,
-        notConnectedNodeCount: 0,
-        isAttributeDataIn: false,
-        onClickCloseButton: this.hideNodeQualityPopup,
-      };
-      let attributes: any = [];
-      edge.attributes.forEach((attribute) => {
-        attributes.push({
-          attribute: attribute,
-          category: AttributeCategory.undefined,
-          nullAmount: 0,
-        });
-      });
-      this.attributeAnalyticsData[edge.collection] = {
-        nodeID: edge.collection,
-        nodeType: NodeType.relation,
-        attributes: attributes,
-        isAttributeDataIn: false,
-        onClickCloseButton: this.hideAttributeAnalyticsPopupMenu,
-        onClickPlaceInQueryBuilderButton: this.placeInQueryBuilder,
-        searchForAttributes: this.searchForAttributes,
-        resetAttributeFilters: this.resetAttributeFilters,
-        applyAttributeFilters: this.applyAttributeFilters,
-      };
-    });
-  };
-
-  /** Returns an empty quality popup node for react-flow. */
-  public emptyAttributeAnalyticsPopupMenuNode(): AttributeAnalyticsPopupMenuNode {
-    return {
-      id: 'attributeAnalyticsPopupMenu',
-      nodeID: '',
-      position: { x: 0, y: 0 },
-      data: {
-        nodeID: '',
-        nodeType: NodeType.relation,
-        attributes: [],
-        isAttributeDataIn: false,
-        onClickCloseButton: this.hideAttributeAnalyticsPopupMenu,
-        onClickPlaceInQueryBuilderButton: this.placeInQueryBuilder,
-        searchForAttributes: this.searchForAttributes,
-        resetAttributeFilters: this.resetAttributeFilters,
-        applyAttributeFilters: this.applyAttributeFilters,
-      },
-      type: 'attributeAnalyticsPopupMenu',
-      isHidden: true,
-      className: 'attributeAnalyticsPopupMenu',
-    };
-  }
-  /** Returns an empty quality popup node for react-flow. */
-  public emptyNodeQualityPopupNode(): NodeQualityPopupNode {
-    return {
-      id: 'nodeQualityPopup',
-      position: { x: 0, y: 0 },
-      data: {
-        nodeCount: 0,
-        notConnectedNodeCount: 0,
-        attributeNullCount: 0,
-        isAttributeDataIn: false,
-        onClickCloseButton: this.hideNodeQualityPopup,
-      },
-      type: 'nodeQualityEntityPopup',
-      isHidden: true,
-      nodeID: '',
-      className: 'nodeQualityPopup',
-    };
-  }
-
-  /**
-   * This function adjusts the values from the new attribute data into
-   * the attribute-analytics data and the node-quality data.
-   * @param attributeData The data that comes from the backend with (some) data about the attributes.
-   */
-  public addAttributeDataToPopupMenusAndElements = (
-    attributeData: AttributeData,
-    attributeDataType: string,
-    schemaElements: SchemaElements
-  ): void => {
-    // Check if attributeData is a node/entity.
-    if (attributeDataType === 'gsa_node_result') {
-      const entity = attributeData as NodeAttributeData;
-      // If it is a entity then add the data for the corresponding entity.
-      if (entity.id in this.attributeAnalyticsData) {
-        const attributeDataOfEntity = this.attributeAnalyticsData[entity.id];
-        attributeDataOfEntity.isAttributeDataIn = true;
-
-        entity.attributes.forEach((attribute) => {
-          // Check if attribute is in the list with attributes from the correct entity.
-          const attributeFound = attributeDataOfEntity.attributes.find(
-            (attribute_) => attribute_.attribute.name == attribute.name
-          );
-          if (attributeFound !== undefined) {
-            attributeFound.category = attribute.type;
-            attributeFound.nullAmount = attribute.nullAmount;
-          }
-        });
-      } // Not throw new error, because it should not crash, a message is enough and not all data will be shown.
-      else
-        console.log(
-          'entity ' + entity.id + ' is not in attributeAnalyticsData'
-        );
-
-      if (entity.id in this.nodeQualityData) {
-        const qualityDataOfEntity = this.nodeQualityData[
-          entity.id
-        ] as NodeQualityDataForEntities;
-        qualityDataOfEntity.nodeCount = entity.length;
-        qualityDataOfEntity.attributeNullCount = entity.summedNullAmount;
-        qualityDataOfEntity.notConnectedNodeCount = Number(
-          (1 - entity.connectedRatio).toFixed(2)
-        );
-        qualityDataOfEntity.isAttributeDataIn = true;
-      } // Not throw new error, because it should not crash, a message is enough and not all data will be shown.
-      else console.log('entity ' + entity.id + ' is not in nodeQualityData');
-
-      // Check also if the entity exists in the this.elements-list.
-      // If so, add the new data to it.
-      const elementsNode = schemaElements.nodes.find(
-        (node_) => node_.id == entity.id
-      );
-      if (elementsNode !== undefined) {
-        elementsNode.data.nodeCount = entity.length;
-        elementsNode.data.summedNullAmount = entity.summedNullAmount;
-        elementsNode.data.connectedRatio = entity.connectedRatio;
-      }
-    }
-
-    // Check if attributeData is an edge/relation.
-    else if (attributeDataType === 'gsa_edge_result') {
-      const relation = attributeData as EdgeAttributeData;
-      // If it is a relation then add the data for the corresponding relation.
-      if (relation.id in this.attributeAnalyticsData) {
-        const attributeDataOfRelation =
-          this.attributeAnalyticsData[relation.id];
-        attributeDataOfRelation.isAttributeDataIn = true;
-
-        relation.attributes.forEach((attribute) => {
-          // Check if attribute is in the list with attributes from the correct relation.
-          const attributeFound = attributeDataOfRelation.attributes.find(
-            (attribute_) => attribute_.attribute.name == attribute.name
-          );
-          if (attributeFound !== undefined) {
-            attributeFound.category = attribute.type;
-            attributeFound.nullAmount = attribute.nullAmount;
-          }
-        });
-      } // Not throw new error, because it should not crash, a message is enough and not all data will be shown.
-      else
-        console.log(
-          'relation ' + relation.id + ' is not in attributeAnalyticsData'
-        );
-
-      if (relation.id in this.nodeQualityData) {
-        const qualityDataOfRelation = this.nodeQualityData[
-          relation.id
-        ] as NodeQualityDataForRelations;
-        qualityDataOfRelation.nodeCount = relation.length;
-        qualityDataOfRelation.attributeNullCount = relation.summedNullAmount;
-        qualityDataOfRelation.fromRatio = Number(relation.fromRatio.toFixed(2));
-        qualityDataOfRelation.toRatio = Number(relation.toRatio.toFixed(2));
-        qualityDataOfRelation.isAttributeDataIn = true;
-      } // Not throw new error, because it should not crash, a message is enough and not all data will be shown.
-      else
-        console.log('relation ' + relation.id + ' is not in nodeQualityData');
-
-      // Check also if the entity exists in the this.elements-list.
-      // If so, add the new data to it.
-      const elementsNode = schemaElements.nodes.find(
-        (node_) => node_.id == relation.id
-      );
-      if (elementsNode !== undefined) {
-        elementsNode.data.nodeCount = relation.length;
-        elementsNode.data.summedNullAmount = relation.summedNullAmount;
-        elementsNode.data.fromRatio = relation.fromRatio;
-        elementsNode.data.toRatio = relation.toRatio;
-      }
-    } else throw new Error('This data is not valid!');
-  };
-
-  /**
-   * Filter out attributes that do not contain the given searchbar-value.
-   * @param id The id of the node the attributes are from.
-   * @param searchbarValue The value of the searchbar.
-   */
-  public searchForAttributes = (id: string, searchbarValue: string): void => {
-    const data = this.attributeAnalyticsData[id];
-    // Check if there is data available.
-    if (data !== undefined) {
-      let passedAttributes: AttributeWithData[] = [];
-      data.attributes.forEach((attribute) => {
-        if (
-          attribute.attribute.name
-            .toLowerCase()
-            .includes(searchbarValue.toLowerCase())
-        )
-          passedAttributes.push(attribute);
-      });
-      this.attributeAnalyticsPopupMenu.data.attributes = passedAttributes;
-      this.notifyViewAboutChanges();
-    }
-  };
-
-  /**
-   * Reset the current used filters for the attribute-list.
-   * @param id The id of the node the attributes are from.
-   */
-  public resetAttributeFilters = (id: string): void => {
-    const data = this.attributeAnalyticsData[id];
-    // Check if there is data available.
-    if (data !== undefined) {
-      this.attributeAnalyticsPopupMenu.data.attributes = data.attributes;
-      this.notifyViewAboutChanges();
-    }
-  };
-
-  /**
-   * Applies the chosen filters on the list of attributes of the particular node.
-   * @param id The id of the node the attributes are from.
-   * @param dataType The given type of the data you want to filter on (numerical, categorical, other).
-   * @param predicate The given predicate.
-   * @param percentage The given percentage you want to compare the null-values on.
-   */
-  public applyAttributeFilters = (
-    id: string,
-    dataType: AttributeCategory,
-    predicate: string,
-    percentage: number
-  ): void => {
-    const data = this.attributeAnalyticsData[id];
-    // Check if there is data available.
-    if (data !== undefined) {
-      let passedAttributes: AttributeWithData[] = [];
-      data.attributes.forEach((attribute) => {
-        // If the value is undefined it means that this filter is not chosen, so that must not be taken into account for further filtering.
-        if (
-          attribute.category == dataType ||
-          dataType == AttributeCategory.undefined
-        )
-          if (predicate == '' || percentage == -1)
-            // If the string is empty it means that this filter is not chosen, so that must not be taken into account for filtering.
-            passedAttributes.push(attribute);
-          else if (
-            numberPredicates[predicate](attribute.nullAmount, percentage)
-          )
-            passedAttributes.push(attribute);
-      });
-      this.attributeAnalyticsPopupMenu.data.attributes = passedAttributes;
-      this.notifyViewAboutChanges();
-    }
-  };
-}
diff --git a/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t b/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t
deleted file mode 100644
index 1eaaf31a0e5c3078ae3cad44b2d3b08a1a1a8672..0000000000000000000000000000000000000000
--- a/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t
+++ /dev/null
@@ -1,1320 +0,0 @@
-/**
- * This program has been developed by students from the bachelor Computer Science at
- * Utrecht University within the Software Project course.
- * © Copyright Utrecht University (Department of Information and Computing Sciences)
- */
-import {
-  schema,
-  schema2,
-} from '../../../data/mock-data/graph-schema/MockGraph';
-import {
-  mockAttributeDataNLEdge2,
-  mockAttributeDataNLEdge2IncorrectId,
-  mockAttributeDataNLNode1,
-  mockAttributeDataNLNode2,
-  mockAttributeDataNLNode2IncorrectId,
-} from '../../../data/mock-data/graph-schema/MockAttributeDataBatchedNL';
-import SecondChamberSchemaMock from '../../../data/mock-data/schema-result/2ndChamberSchemaMock';
-import GraphUseCase from '../../../domain/usecases/graph-schema/GraphUseCase';
-import SchemaViewModelImpl from './SchemaViewModel';
-import { Node, Edge, ArrowHeadType } from 'react-flow-renderer';
-import DrawOrderUseCase from '../../../domain/usecases/graph-schema/DrawOrderUseCase';
-import EdgeUseCase from '../../../domain/usecases/graph-schema/EdgeUseCase';
-import NodeUseCase from '../../../domain/usecases/graph-schema/NodeUseCase';
-import {
-  AttributeCategory,
-  BoundingBox,
-  NodeQualityDataForEntities,
-  NodeQualityDataForRelations,
-  NodeType,
-} from '../../../domain/entity/graph-schema/structures/Types';
-import {
-  Attribute,
-  AttributeData,
-} from '../../../domain/entity/graph-schema/structures/InputDataTypes';
-import mockQueryResult from '../../../data/mock-data/query-result/big2ndChamberQueryResult';
-import mockSchemaResult from '../../../data/mock-data/schema-result/2ndChamberSchemaMock';
-import Broker from '../../../domain/entity/broker/broker';
-
-jest.mock('../../view/graph-schema/SchemaStyleSheet');
-jest.mock('../../util/graph-schema/utils.tsx', () => {
-  return {
-    //TODO Is this already updated?
-    getWidthOfText: () => {
-      return 10;
-    },
-    calcWidthRelationNodeBox: () => {
-      return 75;
-    },
-    calcWidthEntityNodeBox: () => {
-      return 75;
-    },
-    makeBoundingBox: (x: number, y: number, width: number, height: number) => {
-      let boundingBox: BoundingBox;
-      boundingBox = {
-        topLeft: { x: x, y: y },
-        bottomRight: { x: x + width, y: y + height },
-      };
-      return boundingBox;
-    },
-    doBoxesOverlap: (firstBB: BoundingBox, secondBB: BoundingBox) => {
-      if (
-        firstBB.topLeft.x >= secondBB.bottomRight.x ||
-        secondBB.topLeft.x >= firstBB.bottomRight.x
-      )
-        return false;
-
-      if (
-        firstBB.topLeft.y >= secondBB.bottomRight.y ||
-        secondBB.topLeft.y >= firstBB.bottomRight.y
-      )
-        return false;
-
-      return true;
-    },
-  };
-});
-
-describe('schemaViewModelImpl', () => {
-  beforeEach(() => jest.resetModules());
-  const graphUseCase = new GraphUseCase();
-  const drawOrderUseCase = new DrawOrderUseCase();
-  const nodeUseCase = new NodeUseCase();
-  const edgeUseCase = new EdgeUseCase();
-  const attributesInQueryBuilder: any = [];
-  const addAttribute = (name: string, type: string) => {
-    attributesInQueryBuilder.push({ name: name, type: type });
-    return;
-  };
-
-  function anonymous(
-    attributes: Attribute[],
-    id: string,
-    hiddenAttributes: boolean
-  ): void {
-    //Empty methode.
-  }
-
-  it('should create a relation', () => {
-    const schemaViewModel = new SchemaViewModelImpl(
-      drawOrderUseCase,
-      nodeUseCase,
-      edgeUseCase,
-      graphUseCase,
-      addAttribute
-    );
-
-    const expectedrelationode = {
-      type: 'relation',
-      id: '5',
-      position: { x: -72.5, y: 20 },
-      data: {
-        width: 220,
-        height: 20,
-        collection: 'none',
-        attributes: [],
-        from: 'from:here',
-        to: 'to:here',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    };
-
-    schemaViewModel.createRelationNode(
-      '5',
-      [],
-      'none',
-      schemaViewModel.elements
-    );
-    schemaViewModel.setRelationNodePosition(
-      0,
-      0,
-      '5',
-      'from:here',
-      'to:here',
-      []
-    );
-    expect(JSON.stringify(schemaViewModel.elements.nodes[0])).toEqual(
-      JSON.stringify(expectedrelationode)
-    );
-
-    const expectedrelationode2 = {
-      type: 'relation',
-      id: '6',
-      position: { x: -72.5, y: 40 },
-      data: {
-        width: 220,
-        height: 20,
-        collection: 'none',
-        attributes: [],
-        from: 'from:hereagain',
-        to: 'to:hereagain',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    };
-    schemaViewModel.createRelationNode(
-      '6',
-      [],
-      'none',
-      schemaViewModel.elements
-    );
-    schemaViewModel.setRelationNodePosition(
-      0,
-      0,
-      '6',
-      'from:hereagain',
-      'to:hereagain',
-      []
-    );
-    expect(JSON.stringify(schemaViewModel.elements.nodes[1])).toEqual(
-      JSON.stringify(expectedrelationode2)
-    );
-  });
-
-  it('should console log that method is not implemented', () => {
-    const schemaViewModel = new SchemaViewModelImpl(
-      drawOrderUseCase,
-      nodeUseCase,
-      edgeUseCase,
-      graphUseCase,
-      addAttribute
-    );
-    const consoleSpy = jest.spyOn(console, 'log');
-    schemaViewModel.exportToPDF();
-    expect(consoleSpy).toHaveBeenCalledWith('Method not implemented.');
-  });
-
-  it('fitToView', () => {
-    const schemaViewModel = new SchemaViewModelImpl(
-      drawOrderUseCase,
-      nodeUseCase,
-      edgeUseCase,
-      graphUseCase,
-      addAttribute
-    );
-    const getWidthHeight = { width: 300, height: 700 };
-    const spy = jest.spyOn(schemaViewModel, 'getWidthHeight');
-    spy.mockReturnValue(getWidthHeight);
-
-    schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-    schemaViewModel.fitToView();
-
-    const consoleSpy = jest.spyOn(console, 'log');
-    expect(consoleSpy).toHaveBeenCalledWith(
-      'this.reactFlowInstance is undefined!'
-    );
-  });
-
-  /**
-   * These are the testcases for consuming messages from the backend
-   */
-  describe('consumeMessageFromBackend', () => {
-    it('should consume schema result only when subscribed to broker for schema_result', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      const mockConsumeMessages = jest.fn();
-      const message = 'test schema result';
-
-      schemaViewModel.consumeMessageFromBackend = mockConsumeMessages;
-
-      // should consume message for schema result when subscribed
-      schemaViewModel.subscribeToSchemaResult();
-      Broker.instance().publish(message, 'schema_result');
-      expect(mockConsumeMessages.mock.calls[0][0]).toEqual(message);
-      expect(mockConsumeMessages).toBeCalledTimes(1);
-
-      // should not consume message for query_result
-      Broker.instance().publish(message, 'query_result');
-      expect(mockConsumeMessages).toBeCalledTimes(1);
-
-      // should not consume message for schema result when unsubscribed
-      schemaViewModel.unSubscribeFromSchemaResult();
-      Broker.instance().publish(message, 'schema_result');
-      expect(mockConsumeMessages).toBeCalledTimes(1);
-    });
-
-    it('should consume attribute-data only when subscribed to broker for gsa_node_result & gsa_edge_result', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      const mockConsumeMessages = jest.fn();
-      const message = 'test analytics data';
-
-      schemaViewModel.consumeMessageFromBackend = mockConsumeMessages;
-
-      // should consume message for schema result when subscribed
-      schemaViewModel.subscribeToAnalyticsData();
-      Broker.instance().publish(message, 'gsa_node_result');
-      Broker.instance().publish(message, 'gsa_edge_result');
-      expect(mockConsumeMessages.mock.calls[0][0]).toEqual(message);
-      expect(mockConsumeMessages).toBeCalledTimes(2);
-
-      // should not consume message for schema result when unsubscribed
-      schemaViewModel.unSubscribeFromAnalyticsData();
-      Broker.instance().publish(message, 'schema_result');
-      Broker.instance().publish(message, 'gsa_node_result');
-      Broker.instance().publish(message, 'gsa_edge_result');
-      expect(mockConsumeMessages).toBeCalledTimes(2);
-    });
-
-    //TODO: also test the message for the analytics
-    it('should console log and should not change any elements when receiving unrelated messages', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      const consoleSpy = jest.spyOn(console, 'log');
-
-      expect(schemaViewModel.visible).toEqual(true);
-      schemaViewModel.consumeMessageFromBackend(mockQueryResult);
-      expect(consoleSpy).toHaveBeenCalledWith('This is no valid input!');
-
-      expect(schemaViewModel.visible).toEqual(true);
-      expect(schemaViewModel.elements.nodes).toEqual([]);
-      expect(schemaViewModel.elements.edges).toEqual([]);
-    });
-  });
-
-  /**
-   * These are the testcases for the attribute-analytics popup menu
-   */
-  describe('AttributeAnalyticsPopupMenu', () => {
-    it('should throw error that given id does not exist', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      // Nodes that are not in elements cannot have popups
-      const name = 'Edje';
-      expect(
-        schemaViewModel.elements.nodes.find((node) => node.id == name)
-      ).toEqual(undefined);
-      expect(() =>
-        schemaViewModel.toggleAttributeAnalyticsPopupMenu(name)
-      ).toThrowError(
-        'Node ' + name + ' does not exist therefore no popup menu can be shown.'
-      );
-    });
-
-    it('should show the attribute-analytics popup menu', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      const popup = schemaViewModel.attributeAnalyticsPopupMenu;
-      schemaViewModel.consumeMessageFromBackend(schema);
-
-      // test for entity node
-      const node = schemaViewModel.elements.nodes[0];
-      schemaViewModel.toggleAttributeAnalyticsPopupMenu(node.id);
-      expect(popup.isHidden).toEqual(false);
-      expect(popup.nodeID).toEqual(node.id);
-
-      // test for relation node
-      const edge = schemaViewModel.elements.edges[0];
-      schemaViewModel.setRelationNodePosition(
-        0,
-        0,
-        edge.id,
-        edge.source,
-        edge.target,
-        edge.data.attributes
-      );
-      schemaViewModel.toggleAttributeAnalyticsPopupMenu(edge.id);
-      expect(popup.isHidden).toEqual(false);
-      expect(popup.nodeID).toEqual(edge.id);
-    });
-
-    it('should hide the attribute-analytics popup menu as it was already open', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      schemaViewModel.attributeAnalyticsPopupMenu.nodeID = 'Thijs';
-      schemaViewModel.attributeAnalyticsPopupMenu.isHidden = false;
-      schemaViewModel.toggleAttributeAnalyticsPopupMenu('Thijs');
-      expect(schemaViewModel.attributeAnalyticsPopupMenu.isHidden).toEqual(
-        true
-      );
-    });
-
-    it('should close the attribute-analytics menu', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      schemaViewModel.attributeAnalyticsData['Thijs'].onClickCloseButton();
-      expect(schemaViewModel.attributeAnalyticsPopupMenu.isHidden).toEqual(
-        true
-      );
-    });
-
-    it('should make an empty attribute-analytics popupmenu', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.attributeAnalyticsPopupMenu =
-        schemaViewModel.emptyAttributeAnalyticsPopupMenuNode();
-      expect(schemaViewModel.attributeAnalyticsPopupMenu.id).toEqual(
-        'attributeAnalyticsPopupMenu'
-      );
-      expect(schemaViewModel.attributeAnalyticsPopupMenu.nodeID).toEqual('');
-      expect(schemaViewModel.attributeAnalyticsPopupMenu.data.nodeType).toEqual(
-        NodeType.relation
-      );
-      expect(
-        schemaViewModel.attributeAnalyticsPopupMenu.data.attributes
-      ).toEqual([]);
-      expect(
-        schemaViewModel.attributeAnalyticsPopupMenu.data.isAttributeDataIn
-      ).toEqual(false);
-    });
-
-    it('should place an attribute node in the querybuilder', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      const node = schemaViewModel.elements.nodes[0];
-      const attribute = node.data.attributes;
-      schemaViewModel.attributeAnalyticsData[
-        node.id
-      ].onClickPlaceInQueryBuilderButton(attribute.name, attribute.type);
-      expect(attributesInQueryBuilder[0]).toEqual({
-        name: attribute.name,
-        type: attribute.type,
-      });
-    });
-
-    it('should have a working searchbar', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-      schemaViewModel.searchForAttributes('kamerleden', 'aa');
-
-      let attributes =
-        schemaViewModel.attributeAnalyticsPopupMenu.data.attributes;
-      expect(attributes.length).toEqual(2);
-
-      schemaViewModel.searchForAttributes('kamerleden', '');
-      attributes = schemaViewModel.attributeAnalyticsPopupMenu.data.attributes;
-      expect(attributes.length).toEqual(6);
-    });
-
-    it('should have working filters', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-      schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLNode1);
-
-      schemaViewModel.applyAttributeFilters(
-        'kamerleden',
-        AttributeCategory.categorical,
-        'Bigger',
-        -1
-      );
-      let attributes =
-        schemaViewModel.attributeAnalyticsPopupMenu.data.attributes;
-      expect(attributes.length).toEqual(1);
-
-      schemaViewModel.resetAttributeFilters('kamerleden');
-      attributes = schemaViewModel.attributeAnalyticsPopupMenu.data.attributes;
-      expect(attributes.length).toEqual(6);
-    });
-  });
-
-  /**
-   * These are the testcases for the node-quality popup menu
-   */
-  describe('nodeQualityPopup', () => {
-    it('should throw error that given id does not exist', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      expect(() => schemaViewModel.toggleNodeQualityPopup('Edje')).toThrowError(
-        'Node does not exist therefore no popup can be shown.'
-      );
-    });
-
-    it('should show the node-quality popup menu for an entity node', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(SecondChamberSchemaMock);
-      schemaViewModel.toggleNodeQualityPopup('commissies');
-      expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(false);
-      expect(schemaViewModel.nodeQualityPopup.type).toEqual(
-        'nodeQualityEntityPopup'
-      );
-      expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('commissies');
-    });
-
-    it('should show the node-quality popup menu for a relation node', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.createRelationNode(
-        '5',
-        [],
-        '5',
-        schemaViewModel.elements
-      );
-      schemaViewModel.toggleNodeQualityPopup('5');
-      expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(false);
-      expect(schemaViewModel.nodeQualityPopup.type).toEqual(
-        'nodeQualityRelationPopup'
-      );
-      expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('5');
-    });
-
-    it('should hide the node-quality popup menu as it was already open', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      schemaViewModel.nodeQualityPopup.nodeID = 'Thijs';
-      schemaViewModel.nodeQualityPopup.isHidden = false;
-      schemaViewModel.toggleNodeQualityPopup('Thijs');
-      expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(true);
-    });
-
-    it('should close the node-quality menu', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(schema);
-      schemaViewModel.nodeQualityData['Thijs'].onClickCloseButton();
-      expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(true);
-    });
-
-    it('should make an empty node-quality popupmenu', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.nodeQualityPopup =
-        schemaViewModel.emptyNodeQualityPopupNode();
-      expect(schemaViewModel.nodeQualityPopup.id).toEqual('nodeQualityPopup');
-      expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('');
-      expect(schemaViewModel.nodeQualityPopup.data.nodeCount).toEqual(0);
-      expect(schemaViewModel.nodeQualityPopup.data.attributeNullCount).toEqual(
-        0
-      );
-      expect(schemaViewModel.nodeQualityPopup.data.isAttributeDataIn).toEqual(
-        false
-      );
-    });
-  });
-
-  describe('AttributeData', () => {
-    it('should process the incoming data correctly for node-data', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-      schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLNode2);
-
-      const attributeAnalyticsData =
-        schemaViewModel.attributeAnalyticsData['commissies'];
-      const nodeQualityData = schemaViewModel.nodeQualityData[
-        'commissies'
-      ] as NodeQualityDataForEntities;
-      expect(attributeAnalyticsData.isAttributeDataIn).toEqual(true);
-      expect(attributeAnalyticsData.attributes[0].category).toEqual(
-        AttributeCategory.other
-      );
-      expect(attributeAnalyticsData.attributes[0].nullAmount).toEqual(1);
-      expect(nodeQualityData.nodeCount).toEqual(38);
-      expect(nodeQualityData.attributeNullCount).toEqual(1);
-      expect(nodeQualityData.notConnectedNodeCount).toEqual(0.03);
-    });
-
-    it('should process the incoming data correctly for edge-data', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-      schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLEdge2);
-
-      const attributeAnalyticsData =
-        schemaViewModel.attributeAnalyticsData['lid_van'];
-      const nodeQualityData = schemaViewModel.nodeQualityData[
-        'lid_van'
-      ] as NodeQualityDataForRelations;
-      expect(attributeAnalyticsData.isAttributeDataIn).toEqual(true);
-      expect(attributeAnalyticsData.attributes[0].category).toEqual(
-        AttributeCategory.categorical
-      );
-      expect(attributeAnalyticsData.attributes[0].nullAmount).toEqual(0);
-      expect(nodeQualityData.nodeCount).toEqual(149);
-      expect(nodeQualityData.attributeNullCount).toEqual(0);
-      expect(nodeQualityData.fromRatio).toEqual(1);
-      expect(nodeQualityData.toRatio).toEqual(1);
-    });
-
-    it('should console log when the given data has no corresponding id for entities', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-
-      const consoleSpy = jest.spyOn(console, 'log');
-      schemaViewModel.consumeMessageFromBackend(
-        mockAttributeDataNLNode2IncorrectId
-      );
-
-      expect(consoleSpy).toBeCalledTimes(2);
-    });
-
-    it('should console log when the given data has no corresponding id for relations', () => {
-      const schemaViewModel = new SchemaViewModelImpl(
-        drawOrderUseCase,
-        nodeUseCase,
-        edgeUseCase,
-        graphUseCase,
-        addAttribute
-      );
-      schemaViewModel.consumeMessageFromBackend(mockSchemaResult);
-
-      const consoleSpy = jest.spyOn(console, 'log');
-      schemaViewModel.consumeMessageFromBackend(
-        mockAttributeDataNLEdge2IncorrectId
-      );
-
-      expect(consoleSpy).toBeCalledTimes(2);
-    });
-  });
-
-  /** expected results */
-
-  const expectedNodesInElements: Node[] = [
-    {
-      type: QueryElementTypes.Entity,
-      id: 'Airport',
-      position: { x: 0, y: 0 },
-      data: {
-        attributes: [
-          { name: 'city', type: 'string' },
-          { name: 'vip', type: 'bool' },
-          { name: 'state', type: 'string' },
-        ],
-        handles: [
-          'entityTargetBottom',
-          'entityTargetRight',
-          'entityTargetRight',
-          'entitySourceLeft',
-          'entitySourceLeft',
-          'entitySourceLeft',
-          'entityTargetRight',
-        ],
-        width: 165,
-        height: 20,
-        nodeCount: 0,
-        summedNullAmount: 0,
-        connectedRatio: 0,
-      },
-    },
-    {
-      type: QueryElementTypes.Entity,
-      id: 'Plane',
-      position: { x: 0, y: 150 },
-      data: {
-        attributes: [
-          { name: 'type', type: 'string' },
-          { name: 'maxFuelCapacity', type: 'int' },
-        ],
-        handles: ['entitySourceTop', 'entityTargetBottom', 'entityTargetRight'],
-        width: 165,
-        height: 20,
-        nodeCount: 0,
-        summedNullAmount: 0,
-        connectedRatio: 0,
-      },
-    },
-    {
-      type: QueryElementTypes.Entity,
-      id: 'Staff',
-      position: { x: 0, y: 300 },
-      data: {
-        attributes: [],
-        handles: ['entityTargetLeft', 'entitySourceTop', 'entitySourceBottom'],
-        width: 165,
-        height: 20,
-        nodeCount: 0,
-        summedNullAmount: 0,
-        connectedRatio: 0,
-      },
-    },
-    {
-      type: QueryElementTypes.Entity,
-      id: 'Airport2',
-      position: { x: 0, y: 450 },
-      data: {
-        attributes: [
-          { name: 'city', type: 'string' },
-          { name: 'vip', type: 'bool' },
-          { name: 'state', type: 'string' },
-        ],
-        handles: ['entitySourceRight', 'entitySourceRight', 'entityTargetTop'],
-        width: 165,
-        height: 20,
-        nodeCount: 0,
-        summedNullAmount: 0,
-        connectedRatio: 0,
-      },
-    },
-
-    {
-      type: QueryElementTypes.Entity,
-      id: 'Thijs',
-      position: { x: 0, y: 600 },
-      data: {
-        attributes: [],
-        handles: ['entitySourceRight', 'entityTargetLeft'],
-        width: 165,
-        height: 20,
-        nodeCount: 0,
-        summedNullAmount: 0,
-        connectedRatio: 0,
-      },
-    },
-
-    {
-      type: QueryElementTypes.Entity,
-      id: 'Unconnected',
-      position: { x: 0, y: 750 },
-      data: {
-        attributes: [],
-        handles: [],
-        width: 165,
-        height: 20,
-        nodeCount: 0,
-        summedNullAmount: 0,
-        connectedRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'arrivalTime',
-            type: 'int',
-          },
-          {
-            name: 'departureTime',
-            type: 'int',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'salary',
-            type: 'int',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'hallo',
-            type: 'string',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'hallo',
-            type: 'string',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'hallo',
-            type: 'string',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'hallo',
-            type: 'string',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'hallo',
-            type: 'string',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-    {
-      type: 'relation',
-      id: 'flights',
-      position: { x: 0, y: 0 },
-      data: {
-        width: 220,
-        height: 40,
-        collection: 'flights',
-        attributes: [
-          {
-            name: 'test',
-            type: 'string',
-          },
-        ],
-        from: '',
-        to: '',
-        nodeCount: 0,
-        summedNullAmount: 0,
-        fromRatio: 0,
-        toRatio: 0,
-      },
-    },
-  ];
-
-  const expectedEdgesInElements: Edge[] = [
-    {
-      id: 'flights',
-      source: 'Plane',
-      target: 'Airport',
-      type: 'nodeEdge',
-      label: 'Plane:Airport',
-      data: {
-        attributes: [],
-        d: 0,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceTop',
-      targetHandle: 'entityTargetBottom',
-    },
-    {
-      id: 'flights',
-      source: 'Airport2',
-      target: 'Airport',
-      type: 'nodeEdge',
-      label: 'Airport2:Airport',
-      data: {
-        attributes: [
-          { name: 'arrivalTime', type: 'int' },
-          { name: 'departureTime', type: 'int' },
-        ],
-        d: 40,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceRight',
-      targetHandle: 'entityTargetRight',
-    },
-    {
-      id: 'flights',
-      source: 'Thijs',
-      target: 'Airport',
-      type: 'nodeEdge',
-      label: 'Thijs:Airport',
-      data: {
-        attributes: [{ name: 'hallo', type: 'string' }],
-        d: 80,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceRight',
-      targetHandle: 'entityTargetRight',
-    },
-    {
-      id: 'flights',
-      source: 'Airport',
-      target: 'Staff',
-      type: 'nodeEdge',
-      label: 'Airport:Staff',
-      data: {
-        attributes: [{ name: 'salary', type: 'int' }],
-        d: -40,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceLeft',
-      targetHandle: 'entityTargetLeft',
-    },
-    {
-      id: 'flights',
-      source: 'Airport',
-      target: 'Thijs',
-      type: 'nodeEdge',
-      label: 'Airport:Thijs',
-      data: {
-        attributes: [{ name: 'hallo', type: 'string' }],
-        d: -80,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceLeft',
-      targetHandle: 'entityTargetLeft',
-    },
-    {
-      id: 'flights',
-      source: 'Staff',
-      target: 'Plane',
-      type: 'nodeEdge',
-      label: 'Staff:Plane',
-      data: {
-        attributes: [{ name: 'hallo', type: 'string' }],
-        d: 0,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceTop',
-      targetHandle: 'entityTargetBottom',
-    },
-    {
-      id: 'flights',
-      source: 'Airport2',
-      target: 'Plane',
-      type: 'nodeEdge',
-      label: 'Airport2:Plane',
-      data: {
-        attributes: [{ name: 'hallo', type: 'string' }],
-        d: 120,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceRight',
-      targetHandle: 'entityTargetRight',
-    },
-    {
-      id: 'flights',
-      source: 'Staff',
-      target: 'Airport2',
-      type: 'nodeEdge',
-      label: 'Staff:Airport2',
-      data: {
-        attributes: [{ name: 'hallo', type: 'string' }],
-        d: 0,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceBottom',
-      targetHandle: 'entityTargetTop',
-    },
-    {
-      id: 'flights',
-      source: 'Airport',
-      target: 'Airport',
-      type: 'selfEdge',
-      label: 'Airport:Airport',
-      data: {
-        attributes: [{ name: 'test', type: 'string' }],
-        d: 58,
-        created: false,
-        collection: 'flights',
-        edgeCount: 0,
-        view: anonymous,
-      },
-      arrowHeadType: ArrowHeadType.Arrow,
-      sourceHandle: 'entitySourceLeft',
-      targetHandle: 'entityTargetRight',
-    },
-  ];
-
-  const expectedAttributes: Node[] = [
-    {
-      type: 'attribute',
-      id: 'Airport:city',
-      position: { x: 0, y: 21 },
-      data: { name: 'city', datatype: 'string' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Airport:vip',
-      position: { x: 0, y: 41 },
-      data: { name: 'vip', datatype: 'bool' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Airport:state',
-      position: { x: 0, y: 61 },
-      data: { name: 'state', datatype: 'string' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Plane:type',
-      position: { x: 0, y: 171 },
-      data: { name: 'type', datatype: 'string' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Plane:maxFuelCapacity',
-      position: { x: 0, y: 191 },
-      data: { name: 'maxFuelCapacity', datatype: 'int' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Airport2:city',
-      position: { x: 0, y: 471 },
-      data: { name: 'city', datatype: 'string' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Airport2:vip',
-      position: { x: 0, y: 491 },
-      data: { name: 'vip', datatype: 'bool' },
-      isHidden: true,
-    },
-    {
-      type: 'attribute',
-      id: 'Airport2:state',
-      position: { x: 0, y: 511 },
-      data: { name: 'state', datatype: 'string' },
-      isHidden: true,
-    },
-  ];
-});
-
-/** Result nodes. */
-const nodes: Node[] = [
-  {
-    type: QueryElementTypes.Entity,
-    id: 'Thijs',
-    position: { x: 0, y: 0 },
-    data: { attributes: [] },
-  },
-  {
-    type: QueryElementTypes.Entity,
-    id: 'Airport',
-    position: { x: 0, y: 0 },
-    data: { attributes: [] },
-  },
-  {
-    type: QueryElementTypes.Entity,
-    id: 'Airport2',
-    position: { x: 0, y: 0 },
-    data: { attributes: [] },
-  },
-  {
-    type: QueryElementTypes.Entity,
-    id: 'Plane',
-    position: { x: 0, y: 0 },
-    data: { attributes: [] },
-  },
-  {
-    type: QueryElementTypes.Entity,
-    id: 'Staff',
-    position: { x: 0, y: 0 },
-    data: { attributes: [] },
-  },
-];
-
-/** Result links. */
-const edges: Edge[] = [
-  {
-    id: 'Airport2:Airport',
-    label: 'Airport2:Airport',
-    type: 'nodeEdge',
-    source: 'Airport2',
-    target: 'Airport',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: {
-      d: '',
-      attributes: [],
-    },
-  },
-  {
-    id: 'Airport:Staff',
-    label: 'Airport:Staff',
-    type: 'nodeEdge',
-    source: 'Airport',
-    target: 'Staff',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-  {
-    id: 'Plane:Airport',
-    label: 'Plane:Airport',
-    type: 'nodeEdge',
-    source: 'Plane',
-    target: 'Airport',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-  {
-    id: 'Airport:Thijs',
-    label: 'Airport:Thijs',
-    type: 'nodeEdge',
-    source: 'Airport',
-    target: 'Thijs',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-  {
-    id: 'Thijs:Airport',
-    label: 'Thijs:Airport',
-    type: 'nodeEdge',
-    source: 'Thijs',
-    target: 'Airport',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-  {
-    id: 'Staff:Plane',
-    label: 'Staff:Plane',
-    type: 'nodeEdge',
-    source: 'Staff',
-    target: 'Plane',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-  {
-    id: 'Staff:Airport2',
-    label: 'Staff:Airport2',
-    type: 'nodeEdge',
-    source: 'Staff',
-    target: 'Airport2',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-  {
-    id: 'Airport2:Plane',
-    label: 'Airport2:Plane',
-    type: 'nodeEdge',
-    source: 'Airport2',
-    target: 'Plane',
-    arrowHeadType: ArrowHeadType.Arrow,
-    data: { d: '', attributes: [] },
-  },
-];
diff --git a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1c96996cd23ba117c4bd2a3578953ee9f225cf56
--- /dev/null
+++ b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx
@@ -0,0 +1,61 @@
+/**
+ * This program has been developed by students from the bachelor Computer Science at
+ * Utrecht University within the Software Project course.
+ * © Copyright Utrecht University (Department of Information and Computing Sciences)
+ */
+
+/* istanbul ignore file */
+/* The comment above was added so the code coverage wouldn't count this file towards code coverage.
+ * We do not test components/renderfunctions/styling files.
+ * See testing plan for more details.*/
+
+import React from 'react';
+import { SchemaReactflowEntity } from '@graphpolaris/shared/lib/schema/model';
+
+export type SchemaEntityPopupProps = {
+  data: SchemaReactflowEntity;
+  onClose: () => void;
+};
+
+/**
+ * NodeQualityEntityPopupNode is the node that represents the popup that shows the node quality for an entity
+ * @param data Input data of type NodeQualityDataForEntities, which is for the popup.
+ */
+export const SchemaEntityPopup = (props: SchemaEntityPopupProps) => {
+  return (
+    <div className="card card-bordered rounded-none text-[0.9rem] min-w-[10rem]">
+      <div className="card-body p-0">
+        <span className="px-2.5 pt-2">
+          <span>Nodes</span>
+          <span className="float-right">TBD</span>
+        </span>
+        <div className="h-[1px] w-full bg-offwhite-300"></div>
+        <div className="px-2.5 text-[0.8rem]">
+          <p>
+            Null Values: <span className="float-right">TBD</span>
+          </p>
+          <p>
+            Not connected: <span className="float-right">TBD</span>
+          </p>
+        </div>
+        <div className="h-[1px] w-full bg-offwhite-300"></div>
+        {/* <span>Attributes:</span>
+        <div className="text-xs">
+          {data.attributes.map((attribute) => {
+            return (
+              <div className="flex flex-row" key={attribute.name}>
+                <span>{attribute.name}</span>
+              </div>
+            );
+          })}
+        </div> */}
+        <button
+          className="btn btn-outline btn-accent border-0 btn-sm p-0 m-0 text-[0.8rem] mb-2 mx-2.5 min-h-0 h-5"
+          onClick={() => props.onClose()}
+        >
+          Close
+        </button>
+      </div>
+    </div>
+  );
+};
diff --git a/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx b/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx
index aca2c74571f7f5e8286ee1aed60f02294c5d3d45..b6b8c9a3f90e4dd87dc606f6ac496dccb7b34e8a 100644
--- a/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx
+++ b/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx
@@ -11,9 +11,10 @@
 import React, { useState } from 'react';
 import { Node, Handle, Position, NodeProps } from 'reactflow';
 import styles from './entity.module.scss';
-import { calcWidthEntityNodeBox, calculateAttributeQuality, calculateEntityQuality } from '@graphpolaris/shared/lib/schema/schema-utils';
 import { SchemaReactflowNodeWithFunctions } from '../../../model/reactflow';
 import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder';
+import { SchemaEntityPopup } from './SchemaEntityPopup';
+import { Popup } from '@graphpolaris/shared/lib/components/Popup';
 
 /**
  * EntityNode is the node that represents the database entities.
@@ -22,18 +23,13 @@ import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder';
  * @param {NodeProps} param0 The data of an entity flow element.
  */
 export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNodeWithFunctions>) => {
-  // console.log(data);
-
-  const [hidden, setHidden] = useState<boolean>(true);
+  const [openPopup, setOpenPopup] = useState(false);
 
   /**
    * adds drag functionality in order to be able to drag the entityNode to the schema
    * @param event React Mouse drag event
    */
   const onDragStart = (event: React.DragEvent<HTMLDivElement>) => {
-    console.log('dragging entiry', id, data);
-    // console.log('dragging entiry', id, data);
-    // console.log(id, data);
     event.dataTransfer.setData('application/reactflow', JSON.stringify({ type: QueryElementTypes.Entity, name: id }));
     event.dataTransfer.effectAllowed = 'move';
   };
@@ -53,16 +49,25 @@ export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNod
   };
 
   return (
-    <div
-      className="border-l-2 bg-offwhite-200 border-l-entity-600 min-w-[8rem] text-[0.8rem]"
-      onDragStart={(event) => onDragStart(event)}
-      onDragStartCapture={(event) => onDragStart(event)}
-      onMouseDownCapture={(event) => {
-        if (!event.shiftKey) event.stopPropagation();
-      }}
-      draggable
-    >
-      {/* <div
+    <>
+      {openPopup && (
+        <Popup open={openPopup} hAnchor="left" className="-top-10" offset="-9rem">
+          <SchemaEntityPopup data={data} onClose={() => setOpenPopup(false)} />
+        </Popup>
+      )}
+      <div
+        className="border-l-2 bg-offwhite-200 border-l-entity-600 min-w-[8rem] text-[0.8rem]"
+        onDragStart={(event) => onDragStart(event)}
+        onDragStartCapture={(event) => onDragStart(event)}
+        onMouseDownCapture={(event) => {
+          if (!event.shiftKey) event.stopPropagation();
+        }}
+        onClickCapture={(event) => {
+          setOpenPopup(!openPopup);
+        }}
+        draggable
+      >
+        {/* <div
         className={styles.entityNodeAttributesBox}
         onClick={() => onClickToggleAttributeAnalyticsPopupMenu()}
         style={{
@@ -84,7 +89,7 @@ export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNod
         </span>
         <span className={styles.nodeSpan}>{data.attributes.length}</span>
       </div> */}
-      {/* <div
+        {/* <div
         className={styles.entityNodeNodesBox}
         onClick={() => onClickToggleNodeQualityPopup()}
         style={{
@@ -106,7 +111,7 @@ export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNod
         </span>
         <span className={styles.nodeSpan}>{data.nodeCount}</span>
       </div> */}
-      {/* <Handle
+        {/* <Handle
         style={{ pointerEvents: 'none' }}
         id="entitySourceLeft"
         position={Position.Left}
@@ -114,30 +119,30 @@ export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNod
         type="source"
         // hidden={Array.from(data.handles).includes('entitySourceLeft') ? false : true}
       ></Handle> */}
-      <Handle
-        style={{ pointerEvents: 'none' }}
-        id="entityTargetLeft"
-        position={Position.Left}
-        className={styles.handleTriangleLeft}
-        type="target"
-        // hidden={Array.from(data.handles).includes('entityTargetLeft') ? false : true}
-      ></Handle>
-      <Handle
-        style={{ pointerEvents: 'none' }}
-        id="entitySourceRight"
-        position={Position.Right}
-        className={styles.handleTriangleRight}
-        type="source"
-        // hidden={Array.from(data.handles).includes('entitySourceRight') ? false : true}
-      ></Handle>
-      {/* <Handle
+        <Handle
+          style={{ pointerEvents: 'none' }}
+          id="entityTargetLeft"
+          position={Position.Left}
+          className={styles.handleTriangleLeft}
+          type="target"
+          // hidden={Array.from(data.handles).includes('entityTargetLeft') ? false : true}
+        ></Handle>
+        <Handle
+          style={{ pointerEvents: 'none' }}
+          id="entitySourceRight"
+          position={Position.Right}
+          className={styles.handleTriangleRight}
+          type="source"
+          // hidden={Array.from(data.handles).includes('entitySourceRight') ? false : true}
+        ></Handle>
+        {/* <Handle
         style={{ pointerEvents: 'none' }}
         id="entityTargetRight"
         position={Position.Right}
         type="target"
         // hidden={Array.from(data.handles).includes('entityTargetRight') ? false : true}
       ></Handle> */}
-      {/* <Handle
+        {/* <Handle
         style={{ pointerEvents: 'none' }}
         id="entitySourceTop"
         position={Position.Top}
@@ -151,7 +156,7 @@ export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNod
         type="target"
         // hidden={Array.from(data.handles).includes('entityTargetTop') ? false : true}
       ></Handle> */}
-      {/* <Handle
+        {/* <Handle
         style={{ pointerEvents: 'none' }}
         id="entitySourceBottom"
         position={Position.Bottom}
@@ -165,10 +170,11 @@ export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNod
         type="target"
         // hidden={Array.from(data.handles).includes('entityTargetBottom') ? false : true}
       ></Handle> */}
-      <div className="p-2 py-1">
-        <span className="">{id}</span>
+        <div className="p-2 py-1">
+          <span className="">{id}</span>
+        </div>
       </div>
-    </div>
+    </>
   );
 });
 
diff --git a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx
deleted file mode 100644
index 2009a5a307129fb1dcf42ee0233d31862798cad3..0000000000000000000000000000000000000000
--- a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React from 'react';
-import { Meta } from '@storybook/react';
-import { configureStore } from '@reduxjs/toolkit';
-import { Provider } from 'react-redux';
-
-import { querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store';
-import { ReactFlowProvider } from 'reactflow';
-import { NodeQualityEntityPopupNode } from './node-quality-entity-popup';
-
-const Component: Meta<typeof NodeQualityEntityPopupNode> = {
-  /* 👇 The title prop is optional.
-   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
-   * to learn how to generate automatic titles
-   */
-  title: 'Schema/Pills/Popups/NodeQualityEntityPopupNode',
-  component: NodeQualityEntityPopupNode,
-  decorators: [
-    (story) => (
-      <Provider store={Mockstore}>
-        <ReactFlowProvider>{story()}</ReactFlowProvider>
-      </Provider>
-    ),
-  ],
-};
-
-export default Component;
-
-// A super-simple mock of a redux store
-const Mockstore = configureStore({
-  reducer: {
-    querybuilder: querybuilderSlice.reducer,
-    // schema: schemaSlice.reducer,
-  },
-});
-
-export const Default = {
-  args: {
-    data: {
-      name: 'TestEntity',
-      attributes: [{ id: 'a' }],
-      handles: [],
-      nodeCount: 10,
-      from: 1,
-      to: 2,
-    },
-  },
-};
diff --git a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx b/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx
deleted file mode 100644
index cab64e2dfeab631536de783333d98a66bad9d1c2..0000000000000000000000000000000000000000
--- a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * This program has been developed by students from the bachelor Computer Science at
- * Utrecht University within the Software Project course.
- * © Copyright Utrecht University (Department of Information and Computing Sciences)
- */
-
-/* istanbul ignore file */
-/* The comment above was added so the code coverage wouldn't count this file towards code coverage.
- * We do not test components/renderfunctions/styling files.
- * See testing plan for more details.*/
-
-import React from 'react';
-import { NodeProps } from 'reactflow';
-import { NodeQualityDataForEntities } from '../../../model/reactflow';
-
-/**
- * NodeQualityEntityPopupNode is the node that represents the popup that shows the node quality for an entity
- * @param data Input data of type NodeQualityDataForEntities, which is for the popup.
- */
-export const NodeQualityEntityPopupNode = ({ data }: NodeProps<NodeQualityDataForEntities>) => {
-  if (data == undefined) throw new Error('No node quality data is available for this node.');
-
-  if (data.isAttributeDataIn)
-    return (
-      <div>
-        <div className="title">
-          <span id="name">Nodes</span>
-          <span className="rightSideValue">{data.nodeCount}</span>
-        </div>
-        <div className="information">
-          <div>
-            <span>Null attributes</span>
-            <span className="rightSideValue">{data.attributeNullCount}</span>
-          </div>
-          <div>
-            <span>Not connected</span>
-            <span className="rightSideValue">{data.notConnectedNodeCount}</span>
-          </div>
-        </div>
-        <div className="closeButtonWrapper">
-          <button onClick={() => data.onClickCloseButton()} id="closeButton">
-            Close
-          </button>
-        </div>
-      </div>
-    );
-  else
-    return (
-      <div>
-        <div className="title">
-          <span id="name">Nodes</span>
-          <span className="rightSideValue">{data.nodeCount}</span>
-        </div>
-        <div className="information"></div>
-        <div className="closeButtonWrapper">
-          <button onClick={() => data.onClickCloseButton()} id="closeButton">
-            Close
-          </button>
-        </div>
-      </div>
-    );
-};
diff --git a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b61517f3618382df8ebfdd14b9ae32d269faaefd
--- /dev/null
+++ b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx
@@ -0,0 +1,63 @@
+/**
+ * This program has been developed by students from the bachelor Computer Science at
+ * Utrecht University within the Software Project course.
+ * © Copyright Utrecht University (Department of Information and Computing Sciences)
+ */
+
+/* istanbul ignore file */
+/* The comment above was added so the code coverage wouldn't count this file towards code coverage.
+ * We do not test components/renderfunctions/styling files.
+ * See testing plan for more details.*/
+
+import { FormBody, FormDiv, FormCard, FormHBar } from '@graphpolaris/shared/lib/components/forms';
+import { SchemaReactflowRelation } from '@graphpolaris/shared/lib/schema/model';
+import React from 'react';
+import { NodeProps } from 'reactflow';
+
+export type SchemaRelationshipPopupProps = {
+  data: SchemaReactflowRelation;
+  onClose: () => void;
+};
+
+/**
+ * NodeQualityEntityPopupNode is the node that represents the popup that shows the node quality for an entity
+ * @param data Input data of type NodeQualityDataForEntities, which is for the popup.
+ */
+export const SchemaRelationshipPopup = (props: SchemaRelationshipPopupProps) => {
+  return (
+    <div className="card card-bordered rounded-none text-[0.9rem] min-w-[10rem]">
+      <div className="card-body p-0">
+        <span className="px-2.5 pt-2">
+          <span>Relationships</span>
+          <span className="float-right">TBD</span>
+        </span>
+        <div className="h-[1px] w-full bg-offwhite-300"></div>
+        <div className="px-2.5 text-[0.8rem]">
+          <p>
+            Null Values: <span className="float-right">TBD</span>
+          </p>
+          <p>
+            Not connected: <span className="float-right">TBD</span>
+          </p>
+        </div>
+        <div className="h-[1px] w-full bg-offwhite-300"></div>
+        {/* <span>Attributes:</span>
+        <div className="text-xs">
+          {data.attributes.map((attribute) => {
+            return (
+              <div className="flex flex-row" key={attribute.name}>
+                <span>{attribute.name}</span>
+              </div>
+            );
+          })}
+        </div> */}
+        <button
+          className="btn btn-outline btn-primary border-0 btn-sm p-0 m-0 text-[0.8rem] mb-2 mx-2.5 min-h-0 h-5"
+          onClick={() => props.onClose()}
+        >
+          Close
+        </button>
+      </div>
+    </div>
+  );
+};
diff --git a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
index 7dbffdf3efd9cce72d821347c62a470907c9d537..9fdb24c358ab14b5663c7953a34e35ce1136551b 100644
--- a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
+++ b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
@@ -13,6 +13,8 @@ import { Node, Handle, Position, NodeProps } from 'reactflow';
 import styles from './relation.module.scss';
 import { SchemaReactflowRelation, SchemaReactflowRelationWithFunctions } from '../../../model/reactflow';
 import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder';
+import { Popup } from '@graphpolaris/shared/lib/components/Popup';
+import { SchemaRelationshipPopup } from './SchemaRelationshipPopup';
 
 /**
  * Relation node component that renders a relation node for the schema.
@@ -20,7 +22,7 @@ import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder';
  * @param {NodeProps} param0 The data of an entity flow element.
  */
 export const RelationNode = React.memo(({ id, data }: NodeProps<SchemaReactflowRelationWithFunctions>) => {
-  const [hidden, setHidden] = useState<boolean>(true);
+  const [openPopup, setOpenPopup] = useState(false);
 
   /**
    * Adds drag functionality in order to be able to drag the relationNode to the schema.
@@ -57,24 +59,33 @@ export const RelationNode = React.memo(({ id, data }: NodeProps<SchemaReactflowR
   };
 
   return (
-    <div
-      onDragStart={(event) => onDragStart(event)}
-      onDragStartCapture={(event) => onDragStart(event)}
-      onMouseDownCapture={(event) => {
-        if (!event.shiftKey) event.stopPropagation();
-      }}
-      draggable
-      // style={{ width: 100, height: 100 }}
-    >
-      <div className="text-[0.8rem] border-l-2 bg-offwhite-200 border-l-relation-600 min-w-[8rem]">
-        <Handle
-          style={{ pointerEvents: 'none' }}
-          className={styles.handleTriangleTop}
-          id="entitySourceLeft"
-          position={Position.Top}
-          type="target"
-        ></Handle>
-        {/* <div
+    <>
+      {openPopup && (
+        <Popup open={openPopup} hAnchor="left" className="-top-10" offset="-9rem">
+          <SchemaRelationshipPopup data={data} onClose={() => setOpenPopup(false)} />
+        </Popup>
+      )}
+      <div
+        onDragStart={(event) => onDragStart(event)}
+        onDragStartCapture={(event) => onDragStart(event)}
+        onMouseDownCapture={(event) => {
+          if (!event.shiftKey) event.stopPropagation();
+        }}
+        onClickCapture={(event) => {
+          setOpenPopup(!openPopup);
+        }}
+        draggable
+        // style={{ width: 100, height: 100 }}
+      >
+        <div className="text-[0.8rem] border-l-2 bg-offwhite-200 border-l-relation-600 min-w-[8rem]">
+          <Handle
+            style={{ pointerEvents: 'none' }}
+            className={styles.handleTriangleTop}
+            id="entitySourceLeft"
+            position={Position.Top}
+            type="target"
+          ></Handle>
+          {/* <div
           className={styles.relationNodeAttributesBox}
           onClick={() => onClickToggleAttributeAnalyticsPopupMenu()}
           style={{
@@ -95,7 +106,7 @@ export const RelationNode = React.memo(({ id, data }: NodeProps<SchemaReactflowR
           </span>
           <span className={styles.nodeSpan}>{data.attributes.length}</span>
         </div> */}
-        {/* <div
+          {/* <div
           className={styles.relationNodeNodesBox}
           onClick={() => onClickToggleNodeQualityPopup()}
           style={{
@@ -117,13 +128,19 @@ export const RelationNode = React.memo(({ id, data }: NodeProps<SchemaReactflowR
           <span className={styles.nodeSpan}>{data.nodeCount}</span>
         </div> */}
 
-        <div className="p-2 py-1">
-          <span className="">{data.collection}</span>
-        </div>
+          <div className="p-2 py-1">
+            <span className="">{data.collection}</span>
+          </div>
 
-        <Handle className={styles.handleTriangleBottom} style={{ pointerEvents: 'none' }} position={Position.Bottom} type="source"></Handle>
+          <Handle
+            className={styles.handleTriangleBottom}
+            style={{ pointerEvents: 'none' }}
+            position={Position.Bottom}
+            type="source"
+          ></Handle>
+        </div>
       </div>
-    </div>
+    </>
   );
 });
 
diff --git a/libs/shared/lib/schema/schema-utils/schema-usecases.ts b/libs/shared/lib/schema/schema-utils/schema-usecases.ts
index 3d5ae3482277281cb77ab01374196a921d7e99c5..810e2e337d6b0f900298751faffe5fef6fcc0ef8 100644
--- a/libs/shared/lib/schema/schema-utils/schema-usecases.ts
+++ b/libs/shared/lib/schema/schema-utils/schema-usecases.ts
@@ -2,7 +2,7 @@ import { SchemaReactflowNodeWithFunctions, SchemaReactflowRelation, SchemaReactf
 import Graph from 'graphology';
 import { Attributes } from 'graphology-types';
 import { MarkerType, Edge, Node } from 'reactflow';
-import { SchemaReactflowNode } from '../model/reactflow';
+import { SchemaReactflowEntity } from '../model/reactflow';
 import { QueryElementTypes } from '../../querybuilder';
 
 //TODO does not belong here; maybe should go into the GraphPolarisThemeProvider
@@ -64,9 +64,6 @@ export function schemaGraphology2Reactflow(
 
   initialElements.nodes = createReactFlowNodes(graph);
   initialElements.edges = createReactFlowEdges(graph, defaultEdgeType);
-  // initialElements.push(...createReactFlowRelationNodes(graph));
-  // initialElements.push(...createReactFlowRelationEdges(graph));
-  // console.log(initialElements);
 
   return initialElements;
 }
diff --git a/libs/shared/lib/schema/schema-utils/schema-utils.ts b/libs/shared/lib/schema/schema-utils/schema-utils.ts
index d816090981a95e6cc896fdbdd04a3adf4a385047..86cebcb2807d183385f518cae2193a5201d4a9d3 100644
--- a/libs/shared/lib/schema/schema-utils/schema-utils.ts
+++ b/libs/shared/lib/schema/schema-utils/schema-utils.ts
@@ -11,6 +11,8 @@ export class SchemaUtils {
     if (!nodes || !edges) return schemaGraphology;
 
     nodes.forEach((node) => {
+      console.log(node);
+
       const attributes: SchemaGraphologyNode = {
         ...node,
         name: node.name,