Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[doc] Add initial ADR for the selection dialog as a tree #3723

Merged
merged 1 commit into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [ADR-153] Restore the Selection Dialog support in diagrams
- [ADR-154] Add support for finding parent in tree representation
- [ADR-155] Add max-width expression for diagram labels
- [ADR-156] Make it possible to display the semantic elements as a tree in the Selection Dialog

=== Deprecation warning

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
= [ADR-155] Make it possible to display the semantic elements as a tree in the Selection Dialog

== Context

We have restored the previous deactivated Selection Dialog in the context of the ADR: _[ADR-153] Restore the Selection Dialog support in diagrams
_.

We want now to improve this Selection Dialog.

The Selection dialog displayed the candidates elements (returned by the `candidatesExpression` in the `SelectionDialogDescription`) as a flat list without any information about the hierarchical structure.

Since the selection dialog is intended to be used to select semantic elements, it will be necessary to have the ability to display them as they are in their structure (as a tree).

image:images/155/currentSelectionDialog.png[Current Selection Dialog, 50%]

=== Current behavior

The Selection dialog opens a subscription to the selection representation. The `SelectionRefreshedEventPayload` returns the Selection that only owns a list of objects to display.

Here is the current GraphQL API:


```
type SelectionRefreshedEventPayload {
id: ID!
selection: Selection!
}

type Selection implements Representation {
id: ID!
metadata: RepresentationMetadata!
targetObjectId: String!
message: String
objects: [SelectionObject!]!
}

type SelectionObject {
id: ID!
label: String!
iconURL: [String!]!
}
```

== Decision

=== Preferred option

==== View model

The specifier will choose whether the objects returned by the candidates expression will be displayed as a tree or as a flat list.

The `SelectionDialogDescription` from the Diagram View model will have an additional boolean attributes:

* `displayedAsTree`: to indicate whether the dialog layout should be a tree.
Comment on lines +50 to +54
Copy link
Member

@sbegaudeau sbegaudeau Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be enough? How can you figure out how to display a simple list as a tree? We should probably hardcore a TreeDescription in the view converter for now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand. For me, we will have an hard-coded TreeDescription but provider by an IEditingContextRepresentationDescriptionProvider ?



==== Backend and GraphQL API

If the displayedAsTree is true, we will rely on the TreeRepresentation instead of the current SelectionRepresentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will the specifier be able to configure that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, the specifier will only indicate by a boolean (a checkbox in the studio) in the SelectionDescription whether he want to display the selectable element as a list or as a tree.


We will need to contribute a new `IEditingContextRepresentationDescriptionProvider` as the `ModelBrowsersDescriptionProvider` for the Reference widget.

This `DescriptionProvider` will rely on the `SelectionDialogDescription` to compute the `TreeDescription`.

The GraphQL API will evolve to handle the layout information:

```
type Selection implements Representation {
id: ID!
metadata: RepresentationMetadata!
targetObjectId: String!
message: String
displayedAsTree: Boolean!
objects: [SelectionObject!]!
Comment on lines +73 to +74
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to reuse a tree subscription instead of the raw selection subscription with its list of objects. I don't understand why this needs to be modified and why displayedAsTree should exist on both Selection and its description.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok with that but I still have a problem with the "message"

I agree to avoid opening a subscription toward the SelectionRepresentation if the SelectionDialog is intended to represent selectable elements as a tree (in this case we will rely on the TreeView).

The message specified by the specifier is for now not interpreted but the SelectionDescription java pojo code relies on a messageProvider (Function<VariableManager, String>) so it could be interpreted in the future.

So in this case, I'm not sure what I should do to compute the dialog message which is common for both flat and tree representations.

Should I provide the displayAsTree and the message statically by passing them in the graphQL API :

instead of :

type SingleClickOnDiagramElementTool implements Tool {
  id: ID!
  label: String!
  iconURL: [String!]!
  appliesToDiagramRoot: Boolean!
  dialogDescriptionId: String
  targetDescriptions: [DiagramElementDescription!]!
}

We will have :

type SingleClickOnDiagramElementTool implements Tool {
  id: ID!
  label: String!
  iconURL: [String!]!
  appliesToDiagramRoot: Boolean!
  dialogDescription: DialogDescription
  targetDescriptions: [DiagramElementDescription!]!
}



type DialogDescription {
  id: String!
  parameters; [DialogDescriptionParamter!]!
}

type DialogDescriptionParamter {
  key: String!
  value; String!
}

}
```


==== Tree computation Algorithm

Starting from objects returned by the candidates expression, we will compute all the ancestors hierarchy until we reach the root document.

Comment on lines +79 to +82
Copy link
Member

@sbegaudeau sbegaudeau Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will use the tree subscription which let the specifier define how the tree structure is organized. We won't define any magic algorithm to create a tree, it's the plain old tree representation which can be organized any way we want. It may not follow EMF's EObject#eContents topological structure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to describe how we will represent selectable elements.
It will be similar to org.eclipse.sirius.components.collaborative.widget.reference.browser.ModelBrowsersDescriptionProvider which remove all non selectable leaf from the result in getChildren

For example, supposing we have the following semantic model:

```
Root
|_ A
|_ D
|_ H
|_ E
|_ B
|_ F
|_ C
|_ G
```

If the candidates expression return three elements: D,H and F, then We will display these elements as follow:

```
Root
|_ A
|_ (D)
|_ (H)
|_ B
|_ (F)
```

==== Frontend

Elements that are not selectable (out of the computed candidates set) will by faded in the same way than the reference widget semantic browser do:

image:images/155/referenceWidgetSemanticBrowser.png[Reference Widget Semantic Browser, 50%]

The frontend `SelectionDialog` component will be modified to handle both flat and tree layouts.

If the value of `displayedAsTree` is false, then we keep the current behavior.

If the value of `displayedAsTree` is true, then we will use the `TreeView` component to display the content. We will have something similar to the following code:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, and if it's not true, we will fallback on the existing selection subscription which means that this displayedAsTree boolean cannot be part of the selection subscription in the first place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


```
<SelectionContext.Provider
value={{
selection: [],
setSelection: setDialogSelection,
}}>
<TreeView
editingContextId={editingContextId}
readOnly={true}
treeId={`selectionDialog://?targetObjectId=${encodeURIComponent(targetObjectId}&descriptionId=${encodeURIComponent(descriptionId)}`}
enableMultiSelection={enableMultiSelection}
synchronizedWithSelection={true}
activeFilterIds={[]}
/>
</SelectionContext.Provider>
```

We need to provide a `SelectionContext` to capture the selection from the `TreeView`.

We need the layout information (tree or flat) and the message in both case. In the case of the tree layout, we will have two subscriptions:
* The one two retrieve the SelectionDialog (to have the message)
* The one for the Tree representation.

Note that for now, the message is not interpreted but the `SelectionDescription#messageProvider` is already returning a function taking the variableManager as parameter.
That why we can't provide the message directly from the graphQL query (by modifying the `SingleClickOnDiagramElementTool` GraphQL API for instance)

=== Second option

The second option would consist in changing the current Selection Dialog representation to handle both flat and tree layout.

That would mean computing the tree structure in the backend and providing it to the frontend with the containment information.

The frontend would handle both the flat and tree layout.

The option has finally not been retained to reuse the existing `TreeView`.


== Status

Work in progress

== Consequences

Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
= [ADR-156] Make it possible to display the semantic elements as a tree in the Selection Dialog

== Context

We have restored the previous deactivated Selection Dialog in the context of the ADR: _[ADR-152] Reactivate the Selection Dialog Tool_.

We want now to improve this Selection Dialog.

The Selection dialog displayed the candidates elements (returned by the `candidatesExpression` in the `SelectionDialogDescription`) as a flat list without any information about the hierarchical structure.

Since the selection dialog is intended to be used to select semantic elements, it will be necessary to have the ability to display them as they are in their structure (as a tree).

image:images/153/currentSelectionDialog.png[Current Selection Dialog, 50%]

=== Current behavior

The Selection dialog opens a subscription to the selection representation. The `SelectionRefreshedEventPayload` returns the Selection that only owns a list of objects to display.

Here is the current GraphQL API:


```
type SelectionRefreshedEventPayload {
id: ID!
selection: Selection!
}

type Selection implements Representation {
id: ID!
metadata: RepresentationMetadata!
targetObjectId: String!
message: String
objects: [SelectionObject!]!
}

type SelectionObject {
id: ID!
label: String!
iconURL: [String!]!
}
```

== Decision

=== Preferred option

==== View model

The specifier will choose whether the objects returned by the candidates expression will be displayed as a tree or as a flat list.

The `SelectionDialogDescription` from the Diagram View model will have two additional boolean attributes:

* `displayedAsTree`: to indicate whether the dialog layout should be a tree.
* `expandedAtOpening`: to indicate if the tree should be expanded by default (that means all tree items are expanded at opening)

==== Backend and GraphQL API

In addition, the Selection computed by the `SelectionRender` by the server will now return the containment information.

Since the tree depth is not known by the frontend beforehand, the backend needs to return the whole tree items in a flat way.

The GraphQL subscription query will thus have a fixed depth. The containment information will be owned by a field called `parentId`.

To do so, we will keep the same GraphQL structure but with the following changes:

* A `SelectionObject` will have a `parentId` field (optional), that will be a reference to another `SelectionObject#id` field.
* A `SelectionObject` will contain the information whether it can be selected or not in order to differentiate elements from the candidatesExpression to the hierarchical nodes.
* The `Selection` will also contains the information whether the dialog should be displayed as a tree or not and if we should expand all nodes at opening.

Thus, the GraphQL API will evolved as following:

```
type Selection implements Representation {
id: ID!
metadata: RepresentationMetadata!
targetObjectId: String!
message: String
objects: [SelectionObject!]!
displayedAsTree: Boolean!
expandedAtOpening: Boolean!
}

type SelectionObject {
id: ID!
label: String!
iconURL: [String!]!
parentId: String
isSelectable: Boolean!
}
```

==== Tree computation Algorithm

Starting from objects returned by the candidates expression, we will compute all the ancestors hierarchy until we reach the root document.

For example, supposing we have the following semantic model:

```
Root
|_ A
|_ D
|_ H
|_ E
|_ B
|_ F
|_ C
|_ G
```

If the candidates expression return three elements: D,H and F, then We will display these elements as follow:

```
Root
|_ A
|_ (D)
|_ (H)
|_ B
|_ (F)
```

==== Frontend

Elements that are not selectable (out of the computed candidates set) will by faded in the same way than the reference widget semantic browser do:

image:images/153/referenceWidgetSemanticBrowser.png[Reference Widget Semantic Browser, 50%]

The frontend `SelectionDialog` component will be modified to handle both flat and tree layouts.

If the value of `displayedAsTree` is false, then we keep the current behavior.

If the value of `displayedAsTree` is true, then we will represent the structure returned by the backend in a similar way than the `TreePropertySection` component.

We will rely on the MaterialUI `TreeItem` and `TreeView` in the same way than the internal `TreeItem` component from the `TreePropertySection`:

```
const childNodes = nodes.filter((childNode) => childNode.parentId === node.id);
return (
<MuiTreeItem nodeId={node.id} label={label}>
{childNodes.map((childNode) => (
<TreeItem
node={childNode}
nodes={nodes}
key={childNode.id}
readOnly={readOnly}
aria-role="treeitem"
editingContextId={editingContextId}
formId={formId}
widgetId={widgetId}
/>
))}
</MuiTreeItem>
);
```

If the Selection is refreshed by the backend, following a semantic change from another client for instance, a new Selection from the subscription will be received by the SelectionDialog and trigger the component rerendering.

If the end-user has changed the expanded state, we want to keep them expanded in that situation.

To do so, We will modify the SelectionDialog state machine as follow:

* A new `expandedSelectionObjectIds : string[]` will be added in the `SelectionDialogContext`.
* A new event `HandleExpandUpdatedEvent` = { type: 'HANDLE_EXPAND_UPDATED'; expandedObjectId: string }; This event will add or remove the expandedObjectId from the expandedSelectionObjectIds array.

A TreeItem will be expanded if:

* The expandedAtOpening is activated: in that case, the `expandedSelectionObjectIds` will be initialized with all selectedObject ids.
* The user expanded the item manually.

=== Second option

The second option but not the preferred one, consists in relying on the TreeRepresentation.

With this option, we would need to contribute a new `IEditingContextRepresentationDescriptionProvider` as the `ModelBrowsersDescriptionProvider` for the Reference widget.

This DescriptionProvider would rely on the SelectionDialogDescription to compute the TreeDescription.

This option appears to be more complicated to implement. In addition, the first option seems to be a good answer to the Selection dialog need.


== Status

Work in progress

== Consequences

Binary file added doc/adrs/images/155/currentSelectionDialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.