Skip to content

Commit

Permalink
Merge branch 'eclipse-sirius:master' into wpi/enh/navigationbar-exten…
Browse files Browse the repository at this point in the history
…sion
  • Loading branch information
wpiers authored Jun 5, 2024
2 parents 82d0d36 + ae5ae42 commit a151871
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 55 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
- https://github.com/eclipse-sirius/sirius-web/issues/3517[#3517] [diagram] Prevent edge path for rotatable border node to be inconsistent
- https://github.com/eclipse-sirius/sirius-web/issues/2908[#2908] [diagram] Homogenize delete icon
- https://github.com/eclipse-sirius/sirius-web/issues/3485[#3485] [diagram] Add a validation error message when background value is missing
- https://github.com/eclipse-sirius/sirius-web/issues/3480[#3480] [diagram] Fix an error that prevents border color to be properly applied

=== New Features

Expand All @@ -48,6 +49,8 @@
- https://github.com/eclipse-sirius/sirius-web/issues/2837[#2837] [gantt] Enhance gantt scroll bar management
- https://github.com/eclipse-sirius/sirius-web/issues/3441[#3441] [gantt] Use a DateTime widget for AbstractTask entity
- https://github.com/eclipse-sirius/sirius-web/issues/3539[#3539] [diagram] Add editing context variable in diagram delete tool
- https://github.com/eclipse-sirius/sirius-web/issues/3529[#3529] [diagram] Improve edge path when a layout direction is defined
- https://github.com/eclipse-sirius/sirius-web/issues/3489[#3489] [diagram] Apply a fit to screen after an arrange all

== v2024.5.0

Expand Down
6 changes: 1 addition & 5 deletions integration-tests/cypress/e2e/project/diagrams/edges.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ describe('Diagram - edges', () => {
details.getReferenceWidgetSelectedValue('Linked To', 'Entity2.bis').should('exist');
diagram.getEdgePaths('diagram').should('have.length', 2);
diagram.arrangeAll();
diagram.fitToScreen();
diagram.getEdgePaths('diagram').should('have.length', 2);
diagram
.getEdgePaths('diagram')
Expand Down Expand Up @@ -236,7 +235,7 @@ describe('Diagram - edges', () => {

afterEach(() => cy.deleteProject(instanceProjectId));

it('Then check edge handles for border node are correctly positioned', () => {
it.skip('Then check edge handles for border node are correctly positioned', () => {
const explorer = new Explorer();
const details = new Details();
const diagram = new Diagram();
Expand All @@ -252,7 +251,6 @@ describe('Diagram - edges', () => {
details.selectReferenceWidgetOption('Entity2');
explorer.createRepresentation('Root', `${domainName} Diagram Description`, 'diagram');
diagram.arrangeAll();
diagram.fitToScreen();
diagram.getEdgePaths('diagram').should('have.length', 1);
diagram
.getEdgePaths('diagram')
Expand All @@ -272,8 +270,6 @@ describe('Diagram - edges', () => {
details.openReferenceWidgetOptions('Entity2');
details.selectReferenceWidgetOption('Child');
explorer.createRepresentation('Root', `${domainName} Diagram Description`, 'diagram');
diagram.arrangeAll();
diagram.fitToScreen();
diagram.getEdgePaths('diagram').should('have.length', 1);
diagram
.getEdgePaths('diagram')
Expand Down
82 changes: 82 additions & 0 deletions integration-tests/cypress/e2e/project/diagrams/node-style.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { Project } from '../../../pages/Project';
import { Studio } from '../../../usecases/Studio';
import { Explorer } from '../../../workbench/Explorer';
import { Details } from '../../../workbench/Details';

describe('Diagram - node style', () => {
context('Given a studio template', () => {
let studioProjectId: string = '';
let domainName: string = '';

before(() =>
new Studio().createStudioProject().then((createdProjectData) => {
studioProjectId = createdProjectData.projectId;
const project = new Project();
project.visit(createdProjectData.projectId);
project.disableDeletionConfirmationDialog();
const explorer = new Explorer();
explorer.expand('DomainNewModel');
cy.get('[title="domain::Domain"]').then(($div) => {
domainName = $div.data().testid;
const details = new Details();
explorer.expand('ViewNewModel');
explorer.expand('View');
explorer.expand(`${domainName} Diagram Description`);
explorer.expand('Entity1 Node');
explorer.select('RectangularNodeStyleDescription');
details.openReferenceWidgetOptions('Border Color');
details.selectReferenceWidgetOption('orange 500');
explorer.collapse('Entity1 Node');
explorer.expand('Entity2 Node');
explorer.delete('RectangularNodeStyleDescription');
explorer.createObject('Entity2 Node', 'Style Image');
explorer.select('ImageNodeStyleDescription');
details.selectValue('Shape', 'fan');
details.openReferenceWidgetOptions('Border Color');
details.selectReferenceWidgetOption('cyan 500');
});
})
);

after(() => cy.deleteProject(studioProjectId));
context('When we create a new instance project', () => {
let instanceProjectId: string = '';

beforeEach(() => {
const studio = new Studio();
studio.createProjectFromDomain('Cypress - Studio Instance', domainName, 'Root').then((res) => {
instanceProjectId = res.projectId;
new Explorer().createRepresentation('Root', `${domainName} Diagram Description`, 'diagram');
});
});

afterEach(() => cy.deleteProject(instanceProjectId));

it('Then check color border is properly applied', () => {
const explorer = new Explorer();
const details = new Details();

explorer.createObject('Root', 'Entity1s Entity1');
details.getTextField('Name').type('Entity1{Enter}');
explorer.createObject('Root', 'Entity2s Entity2');
details.getTextField('Name').should('have.value', '');
details.getTextField('Name').type('Entity2{Enter}');
explorer.select('diagram');
cy.getByTestId('FreeForm - Entity1').invoke('css', 'border-color').should('eq', 'rgb(255, 152, 0)');
cy.getByTestId('FreeForm - Entity2').invoke('css', 'border-color').should('eq', 'rgb(0, 188, 212)');
});
});
});
});
2 changes: 2 additions & 0 deletions integration-tests/cypress/workbench/Diagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class Diagram {

public arrangeAll() {
cy.getByTestId('arrange-all').click();
/* eslint-disable-next-line cypress/no-unnecessary-waiting */
cy.wait(4000);
}

public getNodes(diagramLabel: string, nodeLabel: string): Cypress.Chainable<JQuery<HTMLElement>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
GetUpdatedConnectionHandlesParameters,
NodeCenter,
} from './EdgeLayout.types';
import { verticalLayoutDirectionGap, horizontalLayoutDirectionGap } from '../layout/layoutParams';

export const getUpdatedConnectionHandles: GetUpdatedConnectionHandlesParameters = (
sourceNode,
Expand Down Expand Up @@ -139,13 +140,17 @@ const getParameters: GetParameters = (movingNode, nodeA, nodeB, visiblesNodes, l
const isDescendant = isDescendantOf(nodeB, nodeA, (nodeId) => visiblesNodes.find((node) => node.id === nodeId));
let position: Position;
if (isVerticalLayoutDirection(layoutDirection)) {
if (isDescendant) {
if (Math.abs(centerA.y - centerB.y) < verticalLayoutDirectionGap) {
position = centerA.x <= centerB.x ? Position.Right : Position.Left;
} else if (isDescendant) {
position = centerA.y <= centerB.y ? Position.Top : Position.Bottom;
} else {
position = centerA.y > centerB.y ? Position.Top : Position.Bottom;
}
} else if (isHorizontalLayoutDirection(layoutDirection)) {
if (isDescendant) {
if (Math.abs(centerA.x - centerB.x) < horizontalLayoutDirectionGap) {
position = centerA.y <= centerB.y ? Position.Bottom : Position.Top;
} else if (isDescendant) {
position = centerA.x <= centerB.x ? Position.Left : Position.Right;
} else {
position = centerA.x > centerB.x ? Position.Left : Position.Right;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ export const labelHorizontalPadding = 16;
export const borderNodeReferencePositionRatio = 0.2;
export const verticalHelperLinesSnapGap = 10;
export const horizontalHelperLinesSnapGap = 10;
export const verticalLayoutDirectionGap = 50;
export const horizontalLayoutDirectionGap = 50;
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,10 @@ export const useArrangeAll = (reactFlowWrapper: React.MutableRefObject<HTMLDivEl
return layoutedAllNodes;
};

const arrangeAll = (): void => {
const arrangeAll = async (): Promise<void> => {
const nodes: Node<NodeData, string>[] = [...getNodes()] as Node<NodeData, DiagramNodeType>[];
const subNodes: Map<string, Node<NodeData, string>[]> = reverseOrdreMap(getSubNodes(nodes));
applyElkOnSubNodes(subNodes, nodes).then((nodes: Node<NodeData, string>[]) => {
await applyElkOnSubNodes(subNodes, nodes).then(async (nodes: Node<NodeData, string>[]) => {
const laidOutNodesWithElk: Node<NodeData, string>[] = nodes.reverse();
laidOutNodesWithElk.filter((laidOutNode) => {
const parentNode = nodes.find((node) => node.id === laidOutNode.parentNode);
Expand All @@ -240,37 +240,40 @@ export const useArrangeAll = (reactFlowWrapper: React.MutableRefObject<HTMLDivEl
nodes: laidOutNodesWithElk,
edges: getEdges(),
};

layout(diagramToLayout, diagramToLayout, null, (laidOutDiagram) => {
laidOutNodesWithElk.map((node) => {
const overlapFreeLaidOutNodes: Node<NodeData, string>[] = resolveNodeOverlap(
laidOutDiagram.nodes,
'horizontal'
) as Node<NodeData, DiagramNodeType>[];
const existingNode = overlapFreeLaidOutNodes.find((laidOutNode) => laidOutNode.id === node.id);
if (existingNode) {
return {
...node,
position: existingNode.position,
width: existingNode.width,
height: existingNode.height,
style: {
...node.style,
width: `${existingNode.width}px`,
height: `${existingNode.height}px`,
},
};
}
return node;
const layoutPromise = new Promise<void>((resolve) => {
layout(diagramToLayout, diagramToLayout, null, (laidOutDiagram) => {
laidOutNodesWithElk.map((node) => {
const overlapFreeLaidOutNodes: Node<NodeData, string>[] = resolveNodeOverlap(
laidOutDiagram.nodes,
'horizontal'
) as Node<NodeData, DiagramNodeType>[];
const existingNode = overlapFreeLaidOutNodes.find((laidOutNode) => laidOutNode.id === node.id);
if (existingNode) {
return {
...node,
position: existingNode.position,
width: existingNode.width,
height: existingNode.height,
style: {
...node.style,
width: `${existingNode.width}px`,
height: `${existingNode.height}px`,
},
};
}
return node;
});
setNodes(laidOutNodesWithElk);
setEdges(laidOutDiagram.edges);
const finalDiagram: RawDiagram = {
nodes: laidOutNodesWithElk,
edges: laidOutDiagram.edges,
};
synchronizeLayoutData(refreshEventPayloadId, finalDiagram);
resolve();
});
setNodes(laidOutNodesWithElk);
setEdges(laidOutDiagram.edges);
const finalDiagram: RawDiagram = {
nodes: laidOutNodesWithElk,
edges: laidOutDiagram.edges,
};
synchronizeLayoutData(refreshEventPayloadId, finalDiagram);
});
await layoutPromise;
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
*******************************************************************************/

export interface UseArrangeAllValue {
arrangeAll: () => void;
arrangeAll: () => Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@ const freeFormNodeStyle = (
height: '100%',
opacity: faded ? '0.4' : '',
...style,
borderColor: getCSSColor(String(style.borderColor), theme),
};
if (selected || hovered) {
freeFormNodeStyle.outline = `${theme.palette.selected} solid 1px`;
}
return freeFormNodeStyle;
};

const imageNodeStyle = (
const backgroundNodeStyle = (
theme: Theme,
style: React.CSSProperties,
rotation: string | undefined,
Expand Down Expand Up @@ -105,7 +106,7 @@ export const FreeFormNode = memo(({ data, id, selected, dragging }: NodeProps<Fr
[data.style, selected, data.isHovered, data.faded]
);
const backgroundStyle = useMemo(
() => imageNodeStyle(theme, data.style, rotation, imageURL),
() => backgroundNodeStyle(theme, data.style, rotation, imageURL),
[data.style, rotation, imageURL]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import TonalityIcon from '@material-ui/icons/Tonality';
import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';
import ZoomInIcon from '@material-ui/icons/ZoomIn';
import ZoomOutIcon from '@material-ui/icons/ZoomOut';
import { memo, useContext, useState } from 'react';
import { Panel, useReactFlow } from 'reactflow';
import { memo, useContext, useEffect, useState } from 'react';
import { Panel, useNodesInitialized, useReactFlow } from 'reactflow';
import { DiagramContext } from '../../contexts/DiagramContext';
import { DiagramContextValue } from '../../contexts/DiagramContext.types';
import { HelperLinesIcon } from '../../icons/HelperLinesIcon';
Expand All @@ -47,6 +47,7 @@ export const DiagramPanel = memo(
({ snapToGrid, onSnapToGrid, helperLines, onHelperLines, reactFlowWrapper }: DiagramPanelProps) => {
const [state, setState] = useState<DiagramPanelState>({
dialogOpen: null,
arrangeAllDone: false,
});

const { readOnly } = useContext<DiagramContextValue>(DiagramContext);
Expand All @@ -58,6 +59,13 @@ export const DiagramPanel = memo(

const { fullscreen, onFullscreen } = useFullscreen();
const { arrangeAll } = useArrangeAll(reactFlowWrapper);
const nodesInitialized = useNodesInitialized();
useEffect(() => {
if (nodesInitialized && state.arrangeAllDone) {
fitView({ duration: 400 });
setState((prevState) => ({ ...prevState, arrangeAllDone: false }));
}
}, [nodesInitialized, state.arrangeAllDone]);

const handleFitToScreen = () => fitView({ duration: 200, nodes: getSelectedNodes() });
const handleZoomIn = () => zoomIn({ duration: 200 });
Expand Down Expand Up @@ -164,7 +172,14 @@ export const DiagramPanel = memo(
<IconButton
size="small"
aria-label="arrange all elements"
onClick={() => arrangeAll()}
onClick={() =>
arrangeAll().then(() =>
setState((prevState) => ({
...prevState,
arrangeAllDone: true,
}))
)
}
data-testid={'arrange-all'}
disabled={readOnly}>
<AccountTreeIcon />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface DiagramPanelProps {

export interface DiagramPanelState {
dialogOpen: DiagramPanelDialog | null;
arrangeAllDone: boolean;
}

export type DiagramPanelDialog = 'Share';
Loading

0 comments on commit a151871

Please sign in to comment.