Skip to content

Commit

Permalink
[1139] Add support for a new tree widget in forms
Browse files Browse the repository at this point in the history
Bug: #1139
Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
  • Loading branch information
pcdavid authored and sbegaudeau committed Jul 20, 2022
1 parent 4608d99 commit d95cf87
Show file tree
Hide file tree
Showing 26 changed files with 3,306 additions and 31 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [ADR-62] Switch to a monorepo layout
- [ADR-63] Simplify the Sprotty integration
- [ADR-64] Add support for a ToggableAreaContainer in forms
- [ADR-65] Add support for a tree widget in forms

=== Deprecation warnings

Expand All @@ -17,6 +18,8 @@

=== Dependency update

- The frontend now depends on `@material-ui/lab` to support the new https://github.com/eclipse-sirius/sirius-components/issues/1139[tree widget] (see ADR-065).

=== Bug fixes

- https://github.com/eclipse-sirius/sirius-components/issues/1245[#1245] [form] Fix Dropping widget from FormDescriptionEditor opens new tab on Firefox
Expand All @@ -41,6 +44,7 @@
- https://github.com/eclipse-sirius/sirius-components/issues/1248[#1248] [charts] Add support for pie-chart in Form representation
- https://github.com/eclipse-sirius/sirius-components/issues/1255[#1255] [form] Add support for charts in form descriptions editor
- https://github.com/eclipse-sirius/sirius-components/issues/1244[#1244] [form] Add support for flexbox containers on FormDescriptions
- https://github.com/eclipse-sirius/sirius-components/issues/1139[#1139] [form] Add support for a new Tree widget
- https://github.com/eclipse-sirius/sirius-components/issues/1140[#1140] [form] Widget groups now support a new setting (`displayMode = TOGGLEABLE_AREAS`) which allows end-users to select which of the widgets in the group are visible, and adapts their layout to the available horizontal space.

== v2022.5.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,24 @@ type FlexboxContainer implements Widget {
children: [Widget!]!
}

type TreeWidget implements Widget {
id: ID!
diagnostics: [Diagnostic!]!
label: String!
iconURL: String
nodes: [TreeNode!]!
expandedNodesIds: [ID!]!
}

type TreeNode {
id: ID!
parentId: ID
label: String!
kind: String!
imageURL: String!
selectable: Boolean!
}

type FormDescription implements RepresentationDescription {
id: ID!
label: String!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2020 Obeo.
* Copyright (c) 2019, 2020, 2022 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
Expand All @@ -16,6 +16,10 @@
import static org.assertj.core.api.Assertions.assertThatObject;
import static org.eclipse.sirius.components.forms.tests.FormAssertions.assertThat;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.assertj.core.api.AbstractAssert;
import org.eclipse.sirius.components.forms.AbstractWidget;
import org.eclipse.sirius.components.forms.Checkbox;
Expand All @@ -27,6 +31,8 @@
import org.eclipse.sirius.components.forms.SelectOption;
import org.eclipse.sirius.components.forms.Textarea;
import org.eclipse.sirius.components.forms.Textfield;
import org.eclipse.sirius.components.forms.TreeNode;
import org.eclipse.sirius.components.forms.TreeWidget;

/**
* Custom assertion class used to perform some tests on a widget.
Expand All @@ -50,7 +56,7 @@ public WidgetAssert matches(AbstractWidget widget, IdPolicy idPolicy) {
List actualList = (List) this.actual;
List list = (List) widget;
assertThat(actualList.getLabel()).isEqualTo(list.getLabel());
assertThat(actualList.getItems().size()).isEqualTo(list.getItems().size());
assertThat(actualList.getItems()).hasSameSizeAs(list.getItems());

int size = actualList.getItems().size();
for (int i = 0; i < size; i++) {
Expand All @@ -67,6 +73,8 @@ public WidgetAssert matches(AbstractWidget widget, IdPolicy idPolicy) {
this.assertRadio((Radio) this.actual, (Radio) widget, idPolicy);
} else if (this.actual instanceof Select && widget instanceof Select) {
this.assertSelect((Select) this.actual, (Select) widget, idPolicy);
} else if (this.actual instanceof TreeWidget && widget instanceof TreeWidget) {
this.assertTree((TreeWidget) this.actual, (TreeWidget) widget, idPolicy);
} else if (this.actual instanceof Textarea && widget instanceof Textarea) {
Textarea actualTextarea = (Textarea) this.actual;
Textarea textarea = (Textarea) widget;
Expand All @@ -84,7 +92,7 @@ public WidgetAssert matches(AbstractWidget widget, IdPolicy idPolicy) {

private void assertSelect(Select actualSelect, Select select, IdPolicy idPolicy) {
assertThat(actualSelect.getLabel()).isEqualTo(select.getLabel());
assertThat(actualSelect.getOptions().size()).isEqualTo(select.getOptions().size());
assertThat(actualSelect.getOptions()).hasSameSizeAs(select.getOptions());

int size = actualSelect.getOptions().size();
for (int i = 0; i < size; i++) {
Expand All @@ -101,7 +109,7 @@ private void assertSelect(Select actualSelect, Select select, IdPolicy idPolicy)

private void assertRadio(Radio actualRadio, Radio radio, IdPolicy idPolicy) {
assertThat(actualRadio.getLabel()).isEqualTo(radio.getLabel());
assertThat(actualRadio.getOptions().size()).isEqualTo(radio.getOptions().size());
assertThat(actualRadio.getOptions()).hasSameSizeAs(radio.getOptions());

int size = actualRadio.getOptions().size();
for (int i = 0; i < size; i++) {
Expand All @@ -117,4 +125,50 @@ private void assertRadio(Radio actualRadio, Radio radio, IdPolicy idPolicy) {
}
}

private void assertTree(TreeWidget actualTree, TreeWidget expectedTree, IdPolicy idPolicy) {
assertThat(actualTree.getLabel()).isEqualTo(expectedTree.getLabel());
assertThat(actualTree.getNodes()).hasSameSizeAs(expectedTree.getNodes());
assertThat(actualTree.getExpandedNodesIds()).hasSameSizeAs(expectedTree.getExpandedNodesIds());

if (idPolicy == IdPolicy.WITH_ID) {
assertThat(actualTree.getExpandedNodesIds()).hasSameElementsAs(expectedTree.getExpandedNodesIds());
}

int size = actualTree.getNodes().size();
for (int i = 0; i < size; i++) {
TreeNode actualNode = actualTree.getNodes().get(i);
TreeNode expectedNode = expectedTree.getNodes().get(i);

assertThat(actualNode.getLabel()).isEqualTo(expectedNode.getLabel());
assertThat("/api/images" + actualNode.getImageURL()).isEqualTo(expectedNode.getImageURL()); //$NON-NLS-1$
assertThat(actualNode.getKind()).isEqualTo(expectedNode.getKind());
if (idPolicy == IdPolicy.WITH_ID) {
assertThat(actualNode.getId()).isEqualTo(expectedNode.getId());
}
}
if (idPolicy == IdPolicy.WITHOUT_ID) {
// Even if we do not test that nodes have the same id equality, we need to check that the logically
// equivalent nodes are expanded and have the same parent/child relationship.
Map<String, String> expectedToActualIds = new HashMap<>();
for (int i = 0; i < size; i++) {
TreeNode actualNode = actualTree.getNodes().get(i);
TreeNode expectedNode = expectedTree.getNodes().get(i);
expectedToActualIds.put(expectedNode.getId(), actualNode.getId());
}

// @formatter:off
assertThat(actualTree.getExpandedNodesIds())
.containsAll(expectedTree.getExpandedNodesIds().stream().map(expectedToActualIds::get).collect(Collectors.toList()));
// @formatter:on

for (int i = 0; i < size; i++) {
TreeNode actualNode = actualTree.getNodes().get(i);
TreeNode expectedNode = expectedTree.getNodes().get(i);
assertThat(actualNode.getId()).isEqualTo(expectedToActualIds.get(expectedNode.getId()));
assertThat(actualNode.getParentId()).isEqualTo(expectedToActualIds.get(expectedNode.getParentId()));
}
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2020 Obeo.
* Copyright (c) 2019, 2020, 2022 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
Expand Down Expand Up @@ -30,6 +30,7 @@
import org.eclipse.sirius.components.forms.Select;
import org.eclipse.sirius.components.forms.Textarea;
import org.eclipse.sirius.components.forms.Textfield;
import org.eclipse.sirius.components.forms.TreeWidget;

/**
* Custom deserializer for AbstractWidget since Jackson need to know how to find the concrete class matching the JSON
Expand Down Expand Up @@ -70,6 +71,8 @@ public AbstractWidget deserialize(JsonParser jsonParser, DeserializationContext
nodeStyle = mapper.readValue(root.toString(), Textfield.class);
} else if (List.class.getSimpleName().equals(typeName.asText())) {
nodeStyle = mapper.readValue(root.toString(), List.class);
} else if (TreeWidget.class.getSimpleName().equals(typeName.asText())) {
nodeStyle = mapper.readValue(root.toString(), TreeWidget.class);
}
}
return nodeStyle;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*******************************************************************************
* Copyright (c) 2022 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
*******************************************************************************/
package org.eclipse.sirius.components.forms;

import java.text.MessageFormat;
import java.util.Objects;

import org.eclipse.sirius.components.annotations.Immutable;

/**
* Represents a node (and its sub-nodes if it has any) inside a {@link TreeWidget}.
*
* @author pcdavid
*/
@Immutable
public final class TreeNode {
private String id;

private String parentId;

private String label;

private String kind;

private String imageURL;

private boolean selectable;

private TreeNode() {
// Prevent instantiation
}

public String getId() {
return this.id;
}

public String getParentId() {
return this.parentId;
}

public String getLabel() {
return this.label;
}

public String getKind() {
return this.kind;
}

public String getImageURL() {
return this.imageURL;
}

public boolean isSelectable() {
return this.selectable;
}

public static Builder newTreeNode(String id) {
return new Builder(id);
}

@Override
public String toString() {
String pattern = "{0} '{'id: {1}, label: {2}, kind: {3}, selectable: {4}, imageURL: {5}, parentId: {6}'}'"; //$NON-NLS-1$
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.label, this.kind, this.selectable, this.imageURL, this.parentId);
}

/**
* The builder used to create the tree node.
*
* @author pcdavid
*/
@SuppressWarnings("checkstyle:HiddenField")
public static final class Builder {
private String id;

private String parentId;

private String label;

private String kind;

private String imageURL;

private boolean selectable;

private Builder(String id) {
this.id = Objects.requireNonNull(id);
}

public Builder parentId(String parentId) {
this.parentId = parentId;
return this;
}

public Builder label(String label) {
this.label = Objects.requireNonNull(label);
return this;
}

public Builder kind(String kind) {
this.kind = Objects.requireNonNull(kind);
return this;
}

public Builder imageURL(String imageURL) {
this.imageURL = Objects.requireNonNull(imageURL);
return this;
}

public Builder selectable(boolean selectable) {
this.selectable = Objects.requireNonNull(selectable);
return this;
}

public TreeNode build() {
TreeNode treeNode = new TreeNode();
treeNode.id = Objects.requireNonNull(this.id);
treeNode.parentId = this.parentId;
treeNode.label = Objects.requireNonNull(this.label);
treeNode.kind = Objects.requireNonNull(this.kind);
treeNode.selectable = Objects.requireNonNull(this.selectable);
treeNode.imageURL = Objects.requireNonNull(this.imageURL);
return treeNode;
}
}
}
Loading

0 comments on commit d95cf87

Please sign in to comment.