diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 0b2d6ab7f4..3b37112076 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -177,6 +177,7 @@ To achieve that a new concept, `ProjectSemanticData` has been added along with a - https://github.com/eclipse-sirius/sirius-web/issues/4514[#4514] [sirius-web] Added support for Ctrl+Shift+Z (Linux/macOS) to trigger redo in addition to Ctrl+Y (Windows) - https://github.com/eclipse-sirius/sirius-web/issues/4372[#4372] [sirius-web] Lower the coupling between project and editing context +- https://github.com/eclipse-sirius/sirius-web/issues/4556[#4556] [table] Add the support of row hierarchy in tables == v2025.1.0 diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRepresentationDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRepresentationDescriptionProvider.java index 9cc841f149..f3bfa4cdde 100644 --- a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRepresentationDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/PackageTableRepresentationDescriptionProvider.java @@ -33,8 +33,10 @@ import org.eclipse.sirius.components.core.api.ILabelService; import org.eclipse.sirius.components.emf.tables.CursorBasedNavigationServices; import org.eclipse.sirius.components.papaya.AnnotableElement; +import org.eclipse.sirius.components.papaya.Operation; import org.eclipse.sirius.components.papaya.PapayaFactory; import org.eclipse.sirius.components.papaya.PapayaPackage; +import org.eclipse.sirius.components.papaya.Parameter; import org.eclipse.sirius.components.papaya.Type; import org.eclipse.sirius.components.papaya.spec.PackageSpec; import org.eclipse.sirius.components.representations.IRepresentationDescription; @@ -101,6 +103,7 @@ public List getRepresentationDescriptions(IEditingCo .headerIndexLabelProvider(headerIndexLabelProvider) .isResizablePredicate(variableManager -> true) .initialHeightProvider(variableManager -> 53) + .depthLevelProvider(this::getSemanticElementDepthLevel) .build(); var tableDescription = TableDescription.newTableDescription(TABLE_DESCRIPTION_ID) @@ -135,9 +138,10 @@ private PaginatedData getSemanticElements(VariableManager variableManager) { List columnFilters = variableManager.get(TableRenderer.COLUMN_FILTERS, List.class).orElse(List.of()); Predicate predicate = eObject -> { - boolean isValidCandidate = eObject instanceof Type && EcoreUtil.isAncestor(self, eObject); - if (isValidCandidate) { - var type = (Type) eObject; + boolean isValidCandidate = (eObject instanceof Type && EcoreUtil.isAncestor(self, eObject)) || + eObject instanceof Operation || + eObject instanceof Parameter; + if (isValidCandidate && eObject instanceof Type type) { if (globalFilter != null && !globalFilter.isBlank()) { isValidCandidate = type.getName() != null && type.getName().contains(globalFilter); isValidCandidate = isValidCandidate || type.getDescription() != null && type.getDescription().contains(globalFilter); @@ -152,6 +156,17 @@ private PaginatedData getSemanticElements(VariableManager variableManager) { return new CursorBasedNavigationServices().collect(self, cursor, direction, size, predicate); } + private Integer getSemanticElementDepthLevel(VariableManager variableManager) { + int result = 0; + var self = variableManager.get(VariableManager.SELF, EObject.class).orElse(null); + if (self instanceof Operation) { + result = 1; + } else if (self instanceof Parameter) { + result = 2; + } + return result; + } + private List getColumnDescriptions() { var provider = new StructuralFeatureToDisplayNameProvider(new DisplayNameProvider(this.composedAdapterFactory)); Map featureToDisplayName = provider.getColumnsStructuralFeaturesDisplayName(PapayaFactory.eINSTANCE.createClass(), PapayaPackage.eINSTANCE.getType()); diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/ProjectTableRepresentationDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/ProjectTableRepresentationDescriptionProvider.java index b6bd1df041..a3705beee1 100644 --- a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/ProjectTableRepresentationDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/representations/table/ProjectTableRepresentationDescriptionProvider.java @@ -78,6 +78,7 @@ public List getRepresentationDescriptions(IEditingCo .headerIndexLabelProvider(variableManager -> "") .isResizablePredicate(variableManager -> false) .initialHeightProvider(variableManager -> -1) + .depthLevelProvider(variableManager -> 0) .build(); var tableDescription = TableDescription.newTableDescription(TABLE_DESCRIPTION_ID) diff --git a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/views/details/AttributesTableDescriptionProvider.java b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/views/details/AttributesTableDescriptionProvider.java index c27cb7a43f..98dd07851e 100644 --- a/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/views/details/AttributesTableDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web-papaya/src/main/java/org/eclipse/sirius/web/papaya/views/details/AttributesTableDescriptionProvider.java @@ -85,6 +85,7 @@ public TableDescription getTableDescription() { .headerIndexLabelProvider(variableManager -> "") .isResizablePredicate(variableManager -> false) .initialHeightProvider(variableManager -> 0) + .depthLevelProvider(variableManager -> 0) .build(); var nameColumnDescription = ColumnDescription.newColumnDescription("name") diff --git a/packages/sirius-web/backend/sirius-web-tests-data/src/main/resources/sirius-web-scripts/papaya.sql b/packages/sirius-web/backend/sirius-web-tests-data/src/main/resources/sirius-web-scripts/papaya.sql index 1f058d09cb..c3171cc73e 100644 --- a/packages/sirius-web/backend/sirius-web-tests-data/src/main/resources/sirius-web-scripts/papaya.sql +++ b/packages/sirius-web/backend/sirius-web-tests-data/src/main/resources/sirius-web-scripts/papaya.sql @@ -96,7 +96,25 @@ INSERT INTO document ( "id": "b0f27d20-4705-40a7-9d28-67d605b5e9d1", "eClass": "papaya:Class", "data": { - "name": "Failure" + "name": "Failure", + "operations": [ + { + "eClass": "papaya:Operation", + "id": "6f531172-8314-4145-8b36-d8fa45bf3b20", + "data": { + "name": "fooOperation", + "parameters": [ + { + "eClass": "papaya:Parameter", + "id": "69ead9da-9302-45a7-86d8-c4ad54056e39", + "data": { + "name": "fooParameter" + } + } + ] + } + } + ] } } ] @@ -471,6 +489,134 @@ INSERT INTO public.representation_content ( "initialHeight": 53, "height":100, "resizable": true + }, + { + "cells": [ + { + "columnId": "d0fd98f3-dfae-3a2d-9cbe-f9a1d6ebee56", + "id": "4bf6dc01-4da7-330e-a7f6-18c9789408d7", + "targetObjectId": "6f531172-8314-4145-8b36-d8fa45bf3b20", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Operation", + "type": "TEXTFIELD", + "value": "barOperation" + }, + { + "columnId": "b6e82202-fe33-3b19-bf2c-5c9648cb96ce", + "id": "8b566c5e-6877-3f35-91dc-dd27f3ead414", + "targetObjectId": "6f531172-8314-4145-8b36-d8fa45bf3b20", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Operation", + "type": "TEXTAREA", + "value": "" + }, + { + "columnId": "eabb569d-f99e-3e6e-86a6-db600b1fa526", + "id": "c4dafa63-2bf8-3e8c-bdc0-fe8f93f70ca2", + "options": [ + { + "id": "PUBLIC", + "label": "PUBLIC" + }, + { + "id": "PROTECTED", + "label": "PROTECTED" + }, + { + "id": "PACKAGE", + "label": "PACKAGE" + }, + { + "id": "PRIVATE", + "label": "PRIVATE" + } + ], + "targetObjectId": "6f531172-8314-4145-8b36-d8fa45bf3b20", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Operation", + "type": "SELECT", + "value": "" + }, + { + "columnId": "94277463-12c8-34bc-b69b-99bb5bfb0fc4", + "id": "580e07af-51cd-39f3-bffd-272ad274dcbd", + "options": [ + ], + "targetObjectId": "6f531172-8314-4145-8b36-d8fa45bf3b20", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Operation", + "type": "MULTI_SELECT", + "values": [ + ] + } + ], + "id": "40ffe0b3-3fb4-35f1-b2bd-3634679398ad", + "targetObjectId": "6f531172-8314-4145-8b36-d8fa45bf3b20", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Operation", + "descriptionId": "6c9154c7-a924-3bd1-b5e4-28aff1d4e5c8", + "depthLevel": 1, + "initialHeight": 53, + "height": 53, + "resizable": true + }, + { + "id": "7127b7a2-50b0-3b39-8fcd-ad2910856e0a", + "targetObjectId": "69ead9da-9302-45a7-86d8-c4ad54056e39", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Parameter", + "cells": [ + { + "columnId": "d0fd98f3-dfae-3a2d-9cbe-f9a1d6ebee56", + "id": "40c134b9-3969-398e-85fc-573ae8b18c05", + "targetObjectId": "69ead9da-9302-45a7-86d8-c4ad54056e39", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Parameter", + "type": "TEXTFIELD", + "value": "fooParameter" + }, + { + "columnId": "b6e82202-fe33-3b19-bf2c-5c9648cb96ce", + "id": "f672ab49-a2d8-38d8-9600-60f736642cff", + "targetObjectId": "69ead9da-9302-45a7-86d8-c4ad54056e39", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Parameter", + "type": "TEXTAREA", + "value": "" + }, + { + "columnId": "eabb569d-f99e-3e6e-86a6-db600b1fa526", + "id": "5404877a-fe46-3e00-ad3d-1dfe476a714f", + "options": [ + { + "id": "PUBLIC", + "label": "PUBLIC" + }, + { + "id": "PROTECTED", + "label": "PROTECTED" + }, + { + "id": "PACKAGE", + "label": "PACKAGE" + }, + { + "id": "PRIVATE", + "label": "PRIVATE" + } + ], + "targetObjectId": "69ead9da-9302-45a7-86d8-c4ad54056e39", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Parameter", + "type": "SELECT", + "value": "" + }, + { + "columnId": "94277463-12c8-34bc-b69b-99bb5bfb0fc4", + "id": "7b212e76-3220-3235-9f25-df8724633bfc", + "options": [], + "targetObjectId": "69ead9da-9302-45a7-86d8-c4ad54056e39", + "targetObjectKind": "siriusComponents://semantic?domain=papaya&entity=Parameter", + "type": "MULTI_SELECT", + "values": [] + } + ], + "depthLevel": 2, + "descriptionId": "6c9154c7-a924-3bd1-b5e4-28aff1d4e5c8", + "headerLabel": "fooParameter", + "height": 53, + "resizable": true } ], "columns": [ diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableCellControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableCellControllerIntegrationTests.java index 10de69cb27..c792bc9d1e 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableCellControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableCellControllerIntegrationTests.java @@ -94,7 +94,7 @@ public void givenTableWhenEditTextareaMutationTriggeredThenTheRepresentationIsRe Consumer initialTableContentConsumer = this.getTableSubscriptionConsumer(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(0).getCells()).hasSize(6); assertThat(table.getLines().get(0).getCells().get(1)).isInstanceOf(TextareaCell.class); assertThat(table.getLines().get(0).getCells().get(1)).isInstanceOf(TextareaCell.class); @@ -120,7 +120,7 @@ public void givenTableWhenEditTextareaMutationTriggeredThenTheRepresentationIsRe Consumer updatedTableContentConsumer = this.getTableSubscriptionConsumer(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(0).getCells()).hasSize(6); assertThat(table.getLines().get(0).getCells().get(1)).isInstanceOf(TextareaCell.class); assertThat(((TextareaCell) table.getLines().get(0).getCells().get(1)).getValue()).isEqualTo("new description"); diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableControllerIntegrationTests.java index c2d5a1d8dd..270667992f 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableControllerIntegrationTests.java @@ -66,7 +66,7 @@ */ @Transactional @SuppressWarnings("checkstyle:MultipleStringLiterals") -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"sirius.web.test.enabled=studio"}) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "sirius.web.test.enabled=studio" }) public class PapayaTableControllerIntegrationTests extends AbstractIntegrationTests { private static final String MISSING_TABLE = "Missing table"; @@ -122,7 +122,7 @@ public void givenTableRepresentationWhenWeSubscribeToItsEventThenTheRepresentati .ifPresentOrElse(table -> { assertThat(table).isNotNull(); assertThat(table.getColumns()).hasSize(6); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); }, () -> fail(MISSING_TABLE)); StepVerifier.create(flux) @@ -146,7 +146,7 @@ public void givenTableWhenRefreshTriggeredThenTableIsRefreshed() { .ifPresentOrElse(table -> { assertThat(table).isNotNull(); assertThat(table.getColumns()).hasSize(6); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); tableId.set(table.getId()); }, () -> fail("Missing table")); diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java index ef3a57a7c6..50dfeb5e17 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableRowControllerIntegrationTests.java @@ -125,7 +125,7 @@ public void givenTableWhenRowResizeMutationTriggeredThenTheRepresentationIsRefre .ifPresentOrElse(table -> { tableId.set(table.getId()); assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); rowRef.set(table.getLines().get(0)); assertThat(table.getLines().get(0).getHeight()).isEqualTo(53); }, () -> fail(MISSING_TABLE)); @@ -148,7 +148,7 @@ public void givenTableWhenRowResizeMutationTriggeredThenTheRepresentationIsRefre .map(TableRefreshedEventPayload::table) .ifPresentOrElse(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(0).getHeight()).isEqualTo(100); }, () -> fail(MISSING_TABLE)); @@ -185,7 +185,7 @@ public void givenTableWithAResizedRowWhenRowAResetRowsHeightMutationIsTriggeredT .ifPresentOrElse(table -> { tableId.set(table.getId()); assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(1).getHeight()).isEqualTo(100); }, () -> fail(MISSING_TABLE)); @@ -209,7 +209,7 @@ public void givenTableWithAResizedRowWhenRowAResetRowsHeightMutationIsTriggeredT .map(TableRefreshedEventPayload::table) .ifPresentOrElse(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(1).getHeight()).isEqualTo(53); }, () -> fail(MISSING_TABLE)); @@ -246,7 +246,7 @@ public void giveATableWhenRowContextMenuEntriesAreQueriedThenTheCorrectEntriesAr .map(TableRefreshedEventPayload::table) .ifPresentOrElse(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); tableId.set(table.getId()); rowId.set(table.getLines().get(0).getId()); }, () -> fail(MISSING_TABLE)); @@ -298,7 +298,7 @@ public void giveATableWhenARowContextMenuEntryIsTriggeredThenTheEntryIsCorrectly .map(TableRefreshedEventPayload::table) .ifPresentOrElse(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); tableId.set(table.getId()); rowId.set(table.getLines().get(0).getId()); }, () -> fail(MISSING_TABLE)); @@ -327,7 +327,7 @@ public void giveATableWhenARowContextMenuEntryIsTriggeredThenTheEntryIsCorrectly .map(TableRefreshedEventPayload::table) .ifPresentOrElse(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(1); + assertThat(table.getLines()).hasSize(3); }, () -> fail(MISSING_TABLE)); StepVerifier.create(flux) @@ -337,4 +337,30 @@ public void giveATableWhenARowContextMenuEntryIsTriggeredThenTheEntryIsCorrectly .thenCancel() .verify(Duration.ofSeconds(10)); } + + @Test + @GivenSiriusWebServer + @DisplayName("Given a table representation, when we subscribe to its event, then the representation data has the correct row depth levels") + public void givenTableRepresentationWhenWeSubscribeToItsEventThenTheRepresentationDataHasTheCorrectRowDepthLevels() { + var flux = this.givenSubscriptionToTable(); + + Consumer initialTableContentConsumer = payload -> Optional.of(payload) + .filter(TableRefreshedEventPayload.class::isInstance) + .map(TableRefreshedEventPayload.class::cast) + .map(TableRefreshedEventPayload::table) + .ifPresentOrElse(table -> { + assertThat(table).isNotNull(); + assertThat(table.getColumns()).hasSize(6); + assertThat(table.getLines()).hasSize(4); + assertThat(table.getLines().get(0).getDepthLevel()).isEqualTo(0); + assertThat(table.getLines().get(1).getDepthLevel()).isEqualTo(0); + assertThat(table.getLines().get(2).getDepthLevel()).isEqualTo(1); + assertThat(table.getLines().get(3).getDepthLevel()).isEqualTo(2); + }, () -> fail(MISSING_TABLE)); + + StepVerifier.create(flux) + .consumeNextWith(initialTableContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } } diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaViewTableControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaViewTableControllerIntegrationTests.java index a33bd1874c..0153fabd64 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaViewTableControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaViewTableControllerIntegrationTests.java @@ -57,7 +57,7 @@ */ @Transactional @SuppressWarnings("checkstyle:MultipleStringLiterals") -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"sirius.web.test.enabled=studio"}) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "sirius.web.test.enabled=studio" }) public class PapayaViewTableControllerIntegrationTests extends AbstractIntegrationTests { @Autowired @@ -104,7 +104,7 @@ public void givenSimpleViewTableDescriptionWhenSubscriptionIsCreatedThenTableIsR assertThat(table.getColumns().get(0).getHeaderIndexLabel()).isEqualTo("0"); assertThat(table.getColumns().get(1).getHeaderLabel()).isEqualTo("Description"); assertThat(table.getColumns().get(1).getHeaderIndexLabel()).isEqualTo("1"); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(0).getHeaderIndexLabel()).isEqualTo("0"); assertThat(table.getLines().get(0).getCells().get(0)).isInstanceOf(TextfieldCell.class); assertThat(table.getLines().get(0).getCells().get(1)).isInstanceOf(TextareaCell.class); @@ -131,7 +131,7 @@ public void givenSimpleViewTableDescriptionWhenRowContextMenuEntryIsInvokedThenR var rowLabel = new AtomicReference(); Consumer tableContentConsumer = this.getTableSubscriptionConsumer(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); tableId.set(table.getId()); rowId.set(table.getLines().get(0).getId()); rowLabel.set(table.getLines().get(0).getHeaderLabel()); @@ -185,13 +185,13 @@ public void givenSimpleViewTableDescriptionWhenRowContextMenuEntryIsInvokedThenR @Test @GivenSiriusWebServer - @DisplayName("Given a view table description with a selected target object expression , when a subscription is created, then the cell target object data are correct") + @DisplayName("Given a view table description with a selected target object expression, when a subscription is created, then the cell target object data are correct") public void givenViewTableWithSelectedTargetObjectExpressionWhenSubscriptionIsCreatedThenCellTargetObjectDataAreCorrectlyExecuted() { var flux = this.givenSubscriptionToViewTableRepresentation(); Consumer tableContentConsumer = this.getTableSubscriptionConsumer(table -> { assertThat(table).isNotNull(); - assertThat(table.getLines()).hasSize(2); + assertThat(table.getLines()).hasSize(4); assertThat(table.getLines().get(0).getCells().get(0).getTargetObjectId()).isEqualTo(PapayaIdentifiers.SIRIUS_WEB_DOMAIN_PACKAGE.toString()); assertThat(table.getLines().get(0).getCells().get(0).getTargetObjectKind()).isEqualTo("siriusComponents://semantic?domain=papaya&entity=Package"); }); @@ -202,6 +202,27 @@ public void givenViewTableWithSelectedTargetObjectExpressionWhenSubscriptionIsCr .verify(Duration.ofSeconds(10)); } + @Test + @GivenSiriusWebServer + @DisplayName("Given a view table description with sub elements, when a subscription is created, then the depth levels are correct") + public void givenViewTableWithSubElementsWhenSubscriptionIsCreatedThenTheDepthLevelsAreCorrect() { + var flux = this.givenSubscriptionToViewTableRepresentation(); + + Consumer tableContentConsumer = this.getTableSubscriptionConsumer(table -> { + assertThat(table).isNotNull(); + assertThat(table.getLines()).hasSize(4); + assertThat(table.getLines().get(0).getDepthLevel()).isEqualTo(0); + assertThat(table.getLines().get(1).getDepthLevel()).isEqualTo(0); + assertThat(table.getLines().get(2).getDepthLevel()).isEqualTo(1); + assertThat(table.getLines().get(3).getDepthLevel()).isEqualTo(2); + }); + + StepVerifier.create(flux) + .consumeNextWith(tableContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + private Consumer getTableSubscriptionConsumer(Consumer tableConsumer) { return payload -> Optional.of(payload) .filter(TableRefreshedEventPayload.class::isInstance) diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/TableIconURLControllerTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/TableIconURLControllerTests.java index 5046674826..ffaa034eca 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/TableIconURLControllerTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/TableIconURLControllerTests.java @@ -134,12 +134,14 @@ public void givenPapayaPackageWhenWeSubscribeToTableWithIconThenURLOfItsIconsAre }); List> rowIconURLs = JsonPath.read(body, "$.data.tableEvent.table.lines[*].headerIconURLs"); + assertThat(rowIconURLs).hasSize(4); + assertThat(rowIconURLs.get(0)).hasSize(2); + assertThat(rowIconURLs.get(1)).hasSize(2); + assertThat(rowIconURLs.get(2)).hasSize(2); + assertThat(rowIconURLs.get(3)).hasSize(1); assertThat(rowIconURLs) - .isNotEmpty() .allSatisfy(iconURLs -> { assertThat(iconURLs) - .isNotEmpty() - .hasSize(2) .allSatisfy(iconURL -> assertThat(iconURL).startsWith(URLConstants.IMAGE_BASE_PATH)); }); }, () -> fail("Missing table")); @@ -176,11 +178,15 @@ public void givenPapayaPackageWhenWeSubscribeToTableWithIconLabelCellThenURLOfIt assertThat(typename).isEqualTo(TableRefreshedEventPayload.class.getSimpleName()); List> iconLabelCellIconURLs = JsonPath.read(body, "$.data.tableEvent.table.lines[*].cells[*].iconURLs"); - assertThat(iconLabelCellIconURLs.stream().filter(iconURL -> !iconURL.isEmpty()).toList()) + List> filteredIconLavelCellIconURLs = iconLabelCellIconURLs.stream().filter(iconURL -> !iconURL.isEmpty()).toList(); + assertThat(filteredIconLavelCellIconURLs).hasSize(4); + assertThat(filteredIconLavelCellIconURLs.get(0)).hasSize(2); + assertThat(filteredIconLavelCellIconURLs.get(1)).hasSize(2); + assertThat(filteredIconLavelCellIconURLs.get(2)).hasSize(2); + assertThat(filteredIconLavelCellIconURLs.get(3)).hasSize(1); + assertThat(filteredIconLavelCellIconURLs) .isNotEmpty() .allSatisfy(iconURLs -> assertThat(iconURLs) - .isNotEmpty() - .hasSize(2) .allSatisfy(iconURL -> assertThat(iconURL).startsWith(URLConstants.IMAGE_BASE_PATH))); }, () -> fail("Missing table")); diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/forms/FormWithTableDescriptionProvider.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/forms/FormWithTableDescriptionProvider.java index b0fcda9c1e..5818e9e3b3 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/forms/FormWithTableDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/forms/FormWithTableDescriptionProvider.java @@ -143,6 +143,7 @@ private TableWidgetDescription getTableWidgetDescription() { .headerIndexLabelProvider(variableManager -> "") .isResizablePredicate(variableManager -> false) .initialHeightProvider(variableManager -> 0) + .depthLevelProvider(variableManager -> 0) .build(); TableDescription tableDescription = TableDescription.newTableDescription(FORM_WITH_TABLE_ID) diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/tables/ViewTableDescriptionProvider.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/tables/ViewTableDescriptionProvider.java index cea8dd7108..cbeb602b77 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/tables/ViewTableDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/tables/ViewTableDescriptionProvider.java @@ -102,10 +102,12 @@ private TableDescription createTableDescription() { .build(); var rowDescription = new TableBuilders().newRowDescription() - .semanticCandidatesExpression("aql:self.eAllContents()->filter(papaya::Type)->toPaginatedData(cursor,direction,size)") + .semanticCandidatesExpression("aql:self.eAllContents()->filter({papaya::Type | papaya::Operation | papaya::Parameter})->toPaginatedData(cursor,direction,size)") .headerIndexLabelExpression("aql:rowIndex") .headerLabelExpression("aql:self.name") .contextMenuEntries(contextMenuEntry) + .depthLevelExpression( + "aql:if self.oclIsKindOf(papaya::Type) then 0 else if self.oclIsKindOf(papaya::Operation) then 1 else if self.oclIsKindOf(papaya::Parameter) then 2 else endif endif endif") .build(); var nameCellDescription = new TableBuilders().newCellDescription() diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls b/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls index 58dcfe9394..fa25664d6e 100644 --- a/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/resources/schema/table.graphqls @@ -66,6 +66,7 @@ type Line { headerIndexLabel: String! height: Int! isResizable: Boolean! + depthLevel: Int! } type PaginationData { diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Line.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Line.java index 676a351b4c..e3d8e5d475 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Line.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Line.java @@ -47,6 +47,8 @@ public final class Line { private boolean resizable; + private int depthLevel; + private Line() { // Prevent instantiation } @@ -91,6 +93,10 @@ public boolean isResizable() { return this.resizable; } + public int getDepthLevel() { + return this.depthLevel; + } + public static Builder newLine(UUID id) { return new Builder(id); } @@ -129,6 +135,8 @@ public static final class Builder { private boolean resizable; + private int depthLevel; + private Builder(UUID id) { this.id = Objects.requireNonNull(id); } @@ -178,6 +186,11 @@ public Builder resizable(boolean resizable) { return this; } + public Builder depthLevel(int depthLevel) { + this.depthLevel = depthLevel; + return this; + } + public Line build() { Line line = new Line(); line.id = Objects.requireNonNull(this.id); @@ -190,6 +203,7 @@ public Line build() { line.headerIndexLabel = Objects.requireNonNull(this.headerIndexLabel); line.height = this.height; line.resizable = this.resizable; + line.depthLevel = this.depthLevel; return line; } } diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/LineComponent.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/LineComponent.java index ef2019d8e8..21061864a0 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/LineComponent.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/LineComponent.java @@ -86,6 +86,7 @@ private Element doRender(VariableManager lineVariableManager, String targetObjec String headerLabel = lineDescription.getHeaderLabelProvider().apply(lineVariableManager); List headerIconURLs = lineDescription.getHeaderIconURLsProvider().apply(lineVariableManager); String headerIndexLabel = lineDescription.getHeaderIndexLabelProvider().apply(lineVariableManager); + Integer depthLevel = lineDescription.getDepthLevelProvider().apply(lineVariableManager); var cells = this.getCells(lineVariableManager, rowId); boolean resizable = lineDescription.getIsResizablePredicate().test(lineVariableManager); @@ -114,6 +115,7 @@ private Element doRender(VariableManager lineVariableManager, String targetObjec .headerIndexLabel(headerIndexLabel) .children(children) .resizable(resizable) + .depthLevel(depthLevel) .height(height); this.props.tableEvents().stream() diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/descriptions/LineDescription.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/descriptions/LineDescription.java index 7757acd1ce..54b07c2d39 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/descriptions/LineDescription.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/descriptions/LineDescription.java @@ -52,6 +52,8 @@ public final class LineDescription { private Predicate isResizablePredicate; + private Function depthLevelProvider; + private LineDescription() { // Prevent instantiation } @@ -96,6 +98,10 @@ public Predicate getIsResizablePredicate() { return this.isResizablePredicate; } + public Function getDepthLevelProvider() { + return this.depthLevelProvider; + } + @Override public String toString() { String pattern = "{0} '{'id: {1}'}'"; @@ -128,6 +134,8 @@ public static final class Builder { private Predicate isResizablePredicate; + private Function depthLevelProvider; + public Builder(String id) { this.id = Objects.requireNonNull(id); } @@ -172,6 +180,11 @@ public Builder isResizablePredicate(Predicate isResizablePredic return this; } + public Builder depthLevelProvider(Function depthLevelProvider) { + this.depthLevelProvider = Objects.requireNonNull(depthLevelProvider); + return this; + } + public LineDescription build() { LineDescription lineDescription = new LineDescription(); lineDescription.id = Objects.requireNonNull(this.id); @@ -183,6 +196,7 @@ public LineDescription build() { lineDescription.headerIndexLabelProvider = Objects.requireNonNull(this.headerIndexLabelProvider); lineDescription.initialHeightProvider = Objects.requireNonNull(this.initialHeightProvider); lineDescription.isResizablePredicate = Objects.requireNonNull(this.isResizablePredicate); + lineDescription.depthLevelProvider = Objects.requireNonNull(this.depthLevelProvider); return lineDescription; } } diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/LineElementProps.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/LineElementProps.java index b087452266..ba68c431a4 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/LineElementProps.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/LineElementProps.java @@ -34,7 +34,8 @@ public record LineElementProps( List headerIconURLs, String headerIndexLabel, int height, - boolean resizable) implements IProps { + boolean resizable, + int depthLevel) implements IProps { public static final String TYPE = "Line"; @@ -86,6 +87,8 @@ public static final class Builder { private boolean resizable; + private int depthLevel; + private Builder(UUID id) { this.id = Objects.requireNonNull(id); } @@ -135,9 +138,15 @@ public Builder resizable(boolean resizable) { return this; } + public Builder depthLevel(int depthLevel) { + this.depthLevel = depthLevel; + return this; + } + public LineElementProps build() { - return new LineElementProps(this.id, this.descriptionId, this.targetObjectId, this.targetObjectKind, this.children, this.headerLabel, this.headerIconURLs, this.headerIndexLabel, this.height, this.resizable); + return new LineElementProps(this.id, this.descriptionId, this.targetObjectId, this.targetObjectKind, this.children, this.headerLabel, this.headerIconURLs, this.headerIndexLabel, + this.height, this.resizable, this.depthLevel); } } } diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/renderer/TableElementFactory.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/renderer/TableElementFactory.java index 0d08350709..7ec5f887eb 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/renderer/TableElementFactory.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/renderer/TableElementFactory.java @@ -128,6 +128,7 @@ private Line instantiateLine(IProps props, List children) { .cells(cells) .resizable(lineElementProps.resizable()) .height(lineElementProps.height()) + .depthLevel(lineElementProps.depthLevel()) .build(); } return null; diff --git a/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts b/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts index 3a79ef120a..c7899bf989 100644 --- a/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts +++ b/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts @@ -74,6 +74,7 @@ export const getTableEventSubscription = ` headerIndexLabel height isResizable + depthLevel cells { __typename id diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/RowChevronButton.tsx b/packages/tables/frontend/sirius-components-tables/src/rows/RowChevronButton.tsx new file mode 100644 index 0000000000..13d1d5b3f0 --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/rows/RowChevronButton.tsx @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import IconButton from '@mui/material/IconButton'; +import { useTheme } from '@mui/material/styles'; +import { RowChevronButtonProps } from './RowChevronButton.types'; + +export const RowChevronButton = ({ row, isExpanded, onExpandCollapse, hasChildren }: RowChevronButtonProps) => { + const theme = useTheme(); + return ( + onExpandCollapse(row.id)} + // the contrast between disabled and default text color is not enough + sx={{ marginLeft: theme.spacing(row.depthLevel), color: 'black' }} + disabled={!hasChildren}> + {isExpanded ? : } + + ); +}; diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/RowChevronButton.types.ts b/packages/tables/frontend/sirius-components-tables/src/rows/RowChevronButton.types.ts new file mode 100644 index 0000000000..5b193bb7bc --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/rows/RowChevronButton.types.ts @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2025 CEA LIST. + * 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 { GQLLine } from '../table/TableContent.types'; + +export interface RowChevronButtonProps { + row: GQLLine; + isExpanded: boolean; + onExpandCollapse: (rowId: string) => void; + hasChildren: boolean; +} diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.tsx b/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.tsx index ff0dca38c8..8a53044435 100644 --- a/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 CEA LIST. + * Copyright (c) 2024, 2025 CEA LIST. * 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 @@ -11,18 +11,27 @@ * Obeo - initial API and implementation *******************************************************************************/ import { IconOverlay } from '@eclipse-sirius/sirius-components-core'; + import Box from '@mui/material/Box'; import { useTheme } from '@mui/material/styles'; import Typography from '@mui/material/Typography'; +import { RowChevronButton } from './RowChevronButton'; import { RowHeaderProps } from './RowHeader.types'; -export const RowHeader = ({ row }: RowHeaderProps) => { +export const RowHeader = ({ row, isExpanded, hasChildren, onExpandCollapse }: RowHeaderProps) => { const theme = useTheme(); + return ( - - {row.headerIndexLabel} - + + + {row.headerIndexLabel} + {row.headerLabel} diff --git a/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.types.ts b/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.types.ts index 8610393f92..dbf61a347f 100644 --- a/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.types.ts +++ b/packages/tables/frontend/sirius-components-tables/src/rows/RowHeader.types.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 CEA LIST. + * Copyright (c) 2024, 2025 CEA LIST. * 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 @@ -14,4 +14,7 @@ import { GQLLine } from '../table/TableContent.types'; export interface RowHeaderProps { row: GQLLine; + isExpanded: boolean; + onExpandCollapse: (rowId: string) => void; + hasChildren: boolean; } diff --git a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx index 5918b00df3..ebb297b624 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx @@ -51,6 +51,48 @@ export const TableContent = memo( setLinesState((prev) => prev.map((line) => (line.id === rowId ? { ...line, height } : line))); }; + const getChildrenById = (id: string): GQLLine[] => { + const rowIndex = table.lines.findIndex((row) => row.id === id); + const children: GQLLine[] = []; + if (rowIndex >= 0 && rowIndex + 1 < table.lines.length) { + let index = rowIndex + 1; + const level = table.lines.at(rowIndex)!.depthLevel; + while (index < table.lines.length && table.lines.at(index)!.depthLevel > level) { + // keep only direct children + if (table.lines.at(index)!.depthLevel === level + 1) { + children.push(table.lines.at(index)!); + } + index++; + } + } + return children; + }; + + const rowIdsToCollapse = (rowId: string): string[] => { + const result: string[] = []; + + result.push(rowId); + // need to check if some sub rows are expanded + const children = getChildrenById(rowId); + for (const child of children) { + if (expandedRowIds.includes(child.id)) { + result.push(...rowIdsToCollapse(child.id)); + } + } + return result; + }; + + const onRowExpandCollapse = (rowId: string) => { + if (expandedRowIds.includes(rowId)) { + const toRemove = rowIdsToCollapse(rowId); + setExpandedRowIds((prev) => [...prev.filter((id) => !toRemove.includes(id))]); + } else { + setExpandedRowIds((prev) => [...prev, rowId]); + } + }; + + const [expandedRowIds, setExpandedRowIds] = useState([]); + const { columns } = useTableColumns( editingContextId, representationId, @@ -61,7 +103,9 @@ export const TableContent = memo( enableColumnFilters, enableColumnOrdering, enableRowSizing, - handleRowHeightChange + handleRowHeightChange, + onRowExpandCollapse, + expandedRowIds ); const { columnSizing, setColumnSizing } = useTableColumnSizing( editingContextId, @@ -84,7 +128,7 @@ export const TableContent = memo( enableColumnFilters ); const [density, setDensity] = useState('comfortable'); - const [linesState, setLinesState] = useState(table.lines); + const [linesState, setLinesState] = useState([]); const { resetRowsHeight } = useResetRowsMutation(editingContextId, representationId, table.id, enableRowSizing); @@ -138,8 +182,19 @@ export const TableContent = memo( }, [pagination.cursor, pagination.size, pagination.direction]); useEffect(() => { - setLinesState([...table.lines]); - }, [table]); + let filteredRows: GQLLine[] = []; + const firstLevel = table.lines.length > 0 ? table.lines.at(0)!.depthLevel : 0; + table.lines.forEach((row) => { + if (row.depthLevel <= firstLevel) { + filteredRows.push(row); + } + }); + expandedRowIds.forEach((id) => { + const indexInFiltered = filteredRows.findIndex((row) => row.id === id); + filteredRows.splice(indexInFiltered + 1, 0, ...getChildrenById(id)); + }); + setLinesState([...filteredRows]); + }, [table, expandedRowIds]); useEffect(() => { if (density != 'comfortable') { @@ -165,7 +220,7 @@ export const TableContent = memo( enableGlobalFilter, manualFiltering: true, onGlobalFilterChange: setGlobalFilter, - enableColumnPinning: false, + enableColumnPinning: true, initialState: { showGlobalFilter: enableGlobalFilter, columnPinning: { left: ['mrt-row-header'], right: ['mrt-row-actions'] }, diff --git a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.types.ts b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.types.ts index 3c400670ed..f84a1de428 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.types.ts +++ b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.types.ts @@ -94,6 +94,7 @@ export interface GQLLine { headerIndexLabel: string; height: number; isResizable: boolean; + depthLevel: number; } export interface GQLPaginationData { @@ -146,3 +147,7 @@ export interface ColumnFilter { id: string; value: unknown; } + +export interface TreeRow extends GQLLine { + children: TreeRow[]; +} diff --git a/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx b/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx index b4bb7984c2..7fe67974e0 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx @@ -30,8 +30,17 @@ export const useTableColumns = ( enableColumnFilters: boolean, enableColumnOrdering: boolean, enableRowSizing: boolean, - handleRowHeightChange: (rowId: string, height: number) => void + handleRowHeightChange: (rowId: string, height: number) => void, + onExpandCollapse: (rowId: string) => void, + expandedRowIds: string[] ): UseTableColumnsValue => { + const hasChildren = (id: string) => { + const index = table.lines.findIndex((row) => row.id === id); + return ( + index < table.lines.length - 1 && table.lines.at(index + 1)!.depthLevel === table.lines.at(index)!.depthLevel + 1 + ); + }; + const { setSelection } = useSelection(); const columns = useMemo[]>(() => { const columnDefs: MRT_ColumnDef[] = table.columns.map((column) => { @@ -80,7 +89,12 @@ export const useTableColumns = ( }; setSelection(newSelection); }}> - + {enableRowSizing ? ( this.evaluateString(interpreter, variableManager, rowDescription.getHeaderIndexLabelExpression())) .isResizablePredicate(variableManager -> interpreter.evaluateExpression(variableManager.getVariables(), rowDescription.getIsResizableExpression()).asBoolean().orElse(false)) .initialHeightProvider(variableManager -> interpreter.evaluateExpression(variableManager.getVariables(), rowDescription.getInitialHeightExpression()).asInt().orElse(-1)) + .depthLevelProvider(variableManager -> interpreter.evaluateExpression(variableManager.getVariables(), rowDescription.getDepthLevelExpression()).asInt().orElse(0)) .build(); } diff --git a/packages/view/backend/sirius-components-view-table-edit/src/main/java/org/eclipse/sirius/components/view/table/provider/RowDescriptionItemProvider.java b/packages/view/backend/sirius-components-view-table-edit/src/main/java/org/eclipse/sirius/components/view/table/provider/RowDescriptionItemProvider.java index f463c8e6c5..8d44f9a96d 100644 --- a/packages/view/backend/sirius-components-view-table-edit/src/main/java/org/eclipse/sirius/components/view/table/provider/RowDescriptionItemProvider.java +++ b/packages/view/backend/sirius-components-view-table-edit/src/main/java/org/eclipse/sirius/components/view/table/provider/RowDescriptionItemProvider.java @@ -72,6 +72,7 @@ public List getPropertyDescriptors(Object object) { this.addHeaderIndexLabelExpressionPropertyDescriptor(object); this.addInitialHeightExpressionPropertyDescriptor(object); this.addIsResizableExpressionPropertyDescriptor(object); + this.addDepthLevelExpressionPropertyDescriptor(object); } return this.itemPropertyDescriptors; } @@ -188,6 +189,19 @@ protected void addIsResizableExpressionPropertyDescriptor(Object object) { ItemPropertyDescriptor.GENERIC_VALUE_IMAGE, null, null)); } + /** + * This adds a property descriptor for the Depth Level Expression feature. + * + * @generated + */ + protected void addDepthLevelExpressionPropertyDescriptor(Object object) { + this.itemPropertyDescriptors.add(this.createItemPropertyDescriptor(((ComposeableAdapterFactory) this.adapterFactory).getRootAdapterFactory(), this.getResourceLocator(), + this.getString("_UI_RowDescription_depthLevelExpression_feature"), + this.getString("_UI_PropertyDescriptor_description", "_UI_RowDescription_depthLevelExpression_feature", "_UI_RowDescription_type"), + TablePackage.Literals.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION, true, false, false, ItemPropertyDescriptor.GENERIC_VALUE_IMAGE, null, null)); + } + /** * This specifies how to implement {@link #getChildren} and is used to deduce an * appropriate feature for an {@link org.eclipse.emf.edit.command.AddCommand}, @@ -273,6 +287,7 @@ public void notifyChanged(Notification notification) { case TablePackage.ROW_DESCRIPTION__HEADER_INDEX_LABEL_EXPRESSION: case TablePackage.ROW_DESCRIPTION__INITIAL_HEIGHT_EXPRESSION: case TablePackage.ROW_DESCRIPTION__IS_RESIZABLE_EXPRESSION: + case TablePackage.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION: this.fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), false, true)); return; case TablePackage.ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES: diff --git a/packages/view/backend/sirius-components-view-table-edit/src/main/resources/plugin.properties b/packages/view/backend/sirius-components-view-table-edit/src/main/resources/plugin.properties index 63c2fe3962..f0b948f6be 100644 --- a/packages/view/backend/sirius-components-view-table-edit/src/main/resources/plugin.properties +++ b/packages/view/backend/sirius-components-view-table-edit/src/main/resources/plugin.properties @@ -58,6 +58,7 @@ _UI_RowDescription_headerIndexLabelExpression_feature=Header Index Label Express _UI_RowDescription_initialHeightExpression_feature=Initial Height Expression _UI_RowDescription_isResizableExpression_feature=Is Resizable Expression _UI_RowDescription_contextMenuEntries_feature=Context Menu Entries +_UI_RowDescription_depthLevelExpression_feature=Depth Level Expression _UI_CellDescription_name_feature=Name _UI_CellDescription_preconditionExpression_feature=Precondition Expression _UI_CellDescription_selectedTargetObjectExpression_feature=Selected Target Object Expression diff --git a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/RowDescription.java b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/RowDescription.java index 2b27bb082d..696950aca0 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/RowDescription.java +++ b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/RowDescription.java @@ -37,6 +37,8 @@ * Expression} *
  • {@link org.eclipse.sirius.components.view.table.RowDescription#getContextMenuEntries Context Menu * Entries}
  • + *
  • {@link org.eclipse.sirius.components.view.table.RowDescription#getDepthLevelExpression Depth Level + * Expression}
  • * * * @model @@ -219,4 +221,27 @@ public interface RowDescription extends EObject { */ EList getContextMenuEntries(); + /** + * Returns the value of the 'Depth Level Expression' attribute. + * + * @return the value of the 'Depth Level Expression' attribute. + * @model dataType="org.eclipse.sirius.components.view.InterpretedExpression" + * @generated + * @see #setDepthLevelExpression(String) + * @see org.eclipse.sirius.components.view.table.TablePackage#getRowDescription_DepthLevelExpression() + */ + String getDepthLevelExpression(); + + /** + * Sets the value of the '{@link org.eclipse.sirius.components.view.table.RowDescription#getDepthLevelExpression + * Depth Level Expression}' attribute. + * + * @param value + * the new value of the 'Depth Level Expression' attribute. + * @generated + * @see #getDepthLevelExpression() + */ + void setDepthLevelExpression(String value); + } // RowDescription diff --git a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/TablePackage.java b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/TablePackage.java index 912164e201..62679ac7e4 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/TablePackage.java +++ b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/TablePackage.java @@ -358,6 +358,15 @@ public interface TablePackage extends EPackage { */ int ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES = 7; + /** + * The feature id for the 'Depth Level Expression' attribute. + * + * @generated + * @ordered + */ + int ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION = 8; + /** * The number of structural features of the 'Row Description' class. @@ -365,7 +374,7 @@ public interface TablePackage extends EPackage { * @generated * @ordered */ - int ROW_DESCRIPTION_FEATURE_COUNT = 8; + int ROW_DESCRIPTION_FEATURE_COUNT = 9; /** * The number of operations of the 'Row Description' class. @@ -967,6 +976,18 @@ public interface TablePackage extends EPackage { */ EReference getRowDescription_ContextMenuEntries(); + /** + * Returns the meta object for the attribute + * '{@link org.eclipse.sirius.components.view.table.RowDescription#getDepthLevelExpression Depth Level + * Expression}'. + * + * @return the meta object for the attribute 'Depth Level Expression'. + * @generated + * @see org.eclipse.sirius.components.view.table.RowDescription#getDepthLevelExpression() + * @see #getRowDescription() + */ + EAttribute getRowDescription_DepthLevelExpression(); + /** * Returns the meta object for class '{@link org.eclipse.sirius.components.view.table.CellDescription Cell * Description}'. @@ -1424,6 +1445,14 @@ interface Literals { */ EReference ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES = eINSTANCE.getRowDescription_ContextMenuEntries(); + /** + * The meta object literal for the 'Depth Level Expression' attribute feature. + * + * @generated + */ + EAttribute ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION = eINSTANCE.getRowDescription_DepthLevelExpression(); + /** * The meta object literal for the '{@link org.eclipse.sirius.components.view.table.impl.CellDescriptionImpl * Cell Description}' class. diff --git a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/RowDescriptionImpl.java b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/RowDescriptionImpl.java index b4953479a4..81d989ac05 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/RowDescriptionImpl.java +++ b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/RowDescriptionImpl.java @@ -108,6 +108,15 @@ public class RowDescriptionImpl extends MinimalEObjectImpl.Container implements * @see #getIsResizableExpression() */ protected static final String IS_RESIZABLE_EXPRESSION_EDEFAULT = ""; + /** + * The default value of the '{@link #getDepthLevelExpression() Depth Level Expression}' attribute. + * + * @generated + * @ordered + * @see #getDepthLevelExpression() + */ + protected static final String DEPTH_LEVEL_EXPRESSION_EDEFAULT = null; /** * The cached value of the '{@link #getName() Name}' attribute. @@ -171,7 +180,6 @@ public class RowDescriptionImpl extends MinimalEObjectImpl.Container implements * @see #getIsResizableExpression() */ protected String isResizableExpression = IS_RESIZABLE_EXPRESSION_EDEFAULT; - /** * The cached value of the '{@link #getContextMenuEntries() Context Menu Entries}' containment reference * list. @@ -181,6 +189,15 @@ public class RowDescriptionImpl extends MinimalEObjectImpl.Container implements * @see #getContextMenuEntries() */ protected EList contextMenuEntries; + /** + * The cached value of the '{@link #getDepthLevelExpression() Depth Level Expression}' attribute. + * + * @generated + * @ordered + * @see #getDepthLevelExpression() + */ + protected String depthLevelExpression = DEPTH_LEVEL_EXPRESSION_EDEFAULT; /** * @@ -376,6 +393,29 @@ public EList getContextMenuEntries() { return this.contextMenuEntries; } + /** + * + * + * @generated + */ + @Override + public String getDepthLevelExpression() { + return this.depthLevelExpression; + } + + /** + * + * + * @generated + */ + @Override + public void setDepthLevelExpression(String newDepthLevelExpression) { + String oldDepthLevelExpression = this.depthLevelExpression; + this.depthLevelExpression = newDepthLevelExpression; + if (this.eNotificationRequired()) + this.eNotify(new ENotificationImpl(this, Notification.SET, TablePackage.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION, oldDepthLevelExpression, this.depthLevelExpression)); + } + /** * * @@ -414,6 +454,8 @@ public Object eGet(int featureID, boolean resolve, boolean coreType) { return this.getIsResizableExpression(); case TablePackage.ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES: return this.getContextMenuEntries(); + case TablePackage.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION: + return this.getDepthLevelExpression(); } return super.eGet(featureID, resolve, coreType); } @@ -452,6 +494,9 @@ public void eSet(int featureID, Object newValue) { this.getContextMenuEntries().clear(); this.getContextMenuEntries().addAll((Collection) newValue); return; + case TablePackage.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION: + this.setDepthLevelExpression((String) newValue); + return; } super.eSet(featureID, newValue); } @@ -488,6 +533,9 @@ public void eUnset(int featureID) { case TablePackage.ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES: this.getContextMenuEntries().clear(); return; + case TablePackage.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION: + this.setDepthLevelExpression(DEPTH_LEVEL_EXPRESSION_EDEFAULT); + return; } super.eUnset(featureID); } @@ -516,6 +564,8 @@ public boolean eIsSet(int featureID) { return IS_RESIZABLE_EXPRESSION_EDEFAULT == null ? this.isResizableExpression != null : !IS_RESIZABLE_EXPRESSION_EDEFAULT.equals(this.isResizableExpression); case TablePackage.ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES: return this.contextMenuEntries != null && !this.contextMenuEntries.isEmpty(); + case TablePackage.ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION: + return !Objects.equals(DEPTH_LEVEL_EXPRESSION_EDEFAULT, this.depthLevelExpression); } return super.eIsSet(featureID); } @@ -544,6 +594,8 @@ public String toString() { this.initialHeightExpression + ", isResizableExpression: " + this.isResizableExpression + + ", depthLevelExpression: " + + this.depthLevelExpression + ')'; return result; } diff --git a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/TablePackageImpl.java b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/TablePackageImpl.java index 895335de76..70f5077ddb 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/TablePackageImpl.java +++ b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/impl/TablePackageImpl.java @@ -428,6 +428,16 @@ public EReference getRowDescription_ContextMenuEntries() { return (EReference) this.rowDescriptionEClass.getEStructuralFeatures().get(7); } + /** + * + * + * @generated + */ + @Override + public EAttribute getRowDescription_DepthLevelExpression() { + return (EAttribute) this.rowDescriptionEClass.getEStructuralFeatures().get(8); + } + /** * * @@ -677,6 +687,7 @@ public void createPackageContents() { this.createEAttribute(this.rowDescriptionEClass, ROW_DESCRIPTION__INITIAL_HEIGHT_EXPRESSION); this.createEAttribute(this.rowDescriptionEClass, ROW_DESCRIPTION__IS_RESIZABLE_EXPRESSION); this.createEReference(this.rowDescriptionEClass, ROW_DESCRIPTION__CONTEXT_MENU_ENTRIES); + this.createEAttribute(this.rowDescriptionEClass, ROW_DESCRIPTION__DEPTH_LEVEL_EXPRESSION); this.cellDescriptionEClass = this.createEClass(CELL_DESCRIPTION); this.createEAttribute(this.cellDescriptionEClass, CELL_DESCRIPTION__NAME); @@ -787,6 +798,8 @@ public void initializePackageContents() { !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); this.initEReference(this.getRowDescription_ContextMenuEntries(), this.getRowContextMenuEntry(), null, "contextMenuEntries", null, 0, -1, RowDescription.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); + this.initEAttribute(this.getRowDescription_DepthLevelExpression(), theViewPackage.getInterpretedExpression(), "depthLevelExpression", null, 0, 1, RowDescription.class, !IS_TRANSIENT, + !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); this.initEClass(this.cellDescriptionEClass, CellDescription.class, "CellDescription", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS); this.initEAttribute(this.getCellDescription_Name(), theViewPackage.getIdentifier(), "name", null, 0, 1, CellDescription.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, diff --git a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableAdapterFactory.java b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableAdapterFactory.java index d9b3468033..827ba553b5 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableAdapterFactory.java +++ b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableAdapterFactory.java @@ -86,13 +86,13 @@ public Adapter caseCellLabelWidgetDescription(CellLabelWidgetDescription object) } @Override - public Adapter caseCellTextareaWidgetDescription(CellTextareaWidgetDescription object) { - return TableAdapterFactory.this.createCellTextareaWidgetDescriptionAdapter(); + public Adapter caseRowContextMenuEntry(RowContextMenuEntry object) { + return TableAdapterFactory.this.createRowContextMenuEntryAdapter(); } @Override - public Adapter caseRowContextMenuEntry(RowContextMenuEntry object) { - return TableAdapterFactory.this.createRowContextMenuEntryAdapter(); + public Adapter caseCellTextareaWidgetDescription(CellTextareaWidgetDescription object) { + return TableAdapterFactory.this.createCellTextareaWidgetDescriptionAdapter(); } @Override @@ -268,8 +268,8 @@ public Adapter createCellTextareaWidgetDescriptionAdapter() { * end-user-doc --> * * @return the new adapter. - * @see org.eclipse.sirius.components.view.table.RowContextMenuEntry * @generated + * @see org.eclipse.sirius.components.view.table.RowContextMenuEntry */ public Adapter createRowContextMenuEntryAdapter() { return null; diff --git a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableSwitch.java b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableSwitch.java index 62c8691f90..55e905d227 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableSwitch.java +++ b/packages/view/backend/sirius-components-view-table/src/main/java/org/eclipse/sirius/components/view/table/util/TableSwitch.java @@ -134,6 +134,13 @@ protected T doSwitch(int classifierID, EObject theEObject) { result = this.defaultCase(theEObject); return result; } + case TablePackage.ROW_CONTEXT_MENU_ENTRY: { + RowContextMenuEntry rowContextMenuEntry = (RowContextMenuEntry) theEObject; + T result = this.caseRowContextMenuEntry(rowContextMenuEntry); + if (result == null) + result = this.defaultCase(theEObject); + return result; + } case TablePackage.CELL_TEXTAREA_WIDGET_DESCRIPTION: { CellTextareaWidgetDescription cellTextareaWidgetDescription = (CellTextareaWidgetDescription) theEObject; T result = this.caseCellTextareaWidgetDescription(cellTextareaWidgetDescription); @@ -143,13 +150,6 @@ protected T doSwitch(int classifierID, EObject theEObject) { result = this.defaultCase(theEObject); return result; } - case TablePackage.ROW_CONTEXT_MENU_ENTRY: { - RowContextMenuEntry rowContextMenuEntry = (RowContextMenuEntry) theEObject; - T result = this.caseRowContextMenuEntry(rowContextMenuEntry); - if (result == null) - result = this.defaultCase(theEObject); - return result; - } default: return this.defaultCase(theEObject); } @@ -280,10 +280,10 @@ public T caseCellTextareaWidgetDescription(CellTextareaWidgetDescription object) * end-user-doc --> * * @param object - * the target of the switch. + * the target of the switch. * @return the result of interpreting the object as an instance of 'Row Context Menu Entry'. - * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) * @generated + * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public T caseRowContextMenuEntry(RowContextMenuEntry object) { return null; diff --git a/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.ecore b/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.ecore index ae74c07d09..3ad8094150 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.ecore +++ b/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.ecore @@ -58,6 +58,7 @@ defaultValueLiteral=""/> + diff --git a/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.genmodel b/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.genmodel index e7c97008ef..cbcef5c343 100644 --- a/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.genmodel +++ b/packages/view/backend/sirius-components-view-table/src/main/resources/model/table.genmodel @@ -40,6 +40,7 @@ +