Skip to content

Commit

Permalink
[4403] Add support of actions in table rows context menu
Browse files Browse the repository at this point in the history
Bug: #4403
Signed-off-by: Jerome Gout <[email protected]>
  • Loading branch information
jerome-obeo committed Jan 15, 2025
1 parent 5b0440c commit 833d045
Show file tree
Hide file tree
Showing 36 changed files with 1,399 additions and 156 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Some log messages have been updated in order to provide more information and mak
The configuration property `sirius.web.graphql.tracing` has also been added to active the tracing mode of the GraphQL API.
It can be activated using `sirius.web.graphql.tracing=true` since it is not enabled by default to not have any impact on the performance of the application.
Some additional log has also been contributed on the frontend in order to view more easily the order and time of the GraphQL requests and responses.

- https://github.com/eclipse-sirius/sirius-web/issues/4403[#4403] [table] Add support of actions in table rows context menu

=== Improvements

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* 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
*******************************************************************************/

package org.eclipse.sirius.web.papaya.representations.table;

import java.util.Map;
import java.util.Objects;

import org.eclipse.sirius.components.collaborative.api.ChangeKind;
import org.eclipse.sirius.components.collaborative.tables.api.IRowContextMenuEntryExecutor;
import org.eclipse.sirius.components.core.api.IEditService;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.components.representations.IStatus;
import org.eclipse.sirius.components.representations.Success;
import org.eclipse.sirius.components.tables.Line;
import org.eclipse.sirius.components.tables.Table;
import org.eclipse.sirius.components.tables.descriptions.TableDescription;
import org.springframework.stereotype.Service;

/**
* This class is the implementation of {@link IRowContextMenuEntryExecutor} for the example Delete row action.
*
* @author Jerome Gout
*/
@Service
public class DeleteRowContextMenuEntryExecutor implements IRowContextMenuEntryExecutor {

private final IEditService editService;

private final IObjectService objectService;

public DeleteRowContextMenuEntryExecutor(IEditService editService, IObjectService objectService) {
this.editService = Objects.requireNonNull(editService);
this.objectService = Objects.requireNonNull(objectService);
}

@Override
public boolean canExecute(TableDescription tableDescription, String tableId, String rowId, String rowMenuContextEntryId) {
return PackageTableRowContextMenuProvider.DELETE_ID.equals(rowMenuContextEntryId);
}

@Override
public IStatus execute(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row, String rowMenuContextEntryId) {
this.objectService.getObject(editingContext, row.getTargetObjectId()).ifPresent(this.editService::delete);
return new Success(ChangeKind.SEMANTIC_CHANGE, Map.of());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*******************************************************************************
* 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
*******************************************************************************/

package org.eclipse.sirius.web.papaya.representations.table;

import java.util.List;
import java.util.Objects;

import org.eclipse.sirius.components.collaborative.tables.api.IRowContextMenuEntryProvider;
import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuEntry;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.tables.Line;
import org.eclipse.sirius.components.tables.Table;
import org.eclipse.sirius.components.tables.descriptions.TableDescription;
import org.springframework.stereotype.Service;

/**
* Example of row context menu entries used inside the papaya package table.
*
* @author Jerome Gout
*/
@Service
public class PackageTableRowContextMenuProvider implements IRowContextMenuEntryProvider {

public static final String DELETE_ID = "papaya-package-table-delete-row";

public static final String DELETE_LABEL = "Delete row";

@Override
public boolean canHandle(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row) {
return Objects.equals(tableDescription.getId(), PackageTableRepresentationDescriptionProvider.TABLE_DESCRIPTION_ID);
}

@Override
public List<RowContextMenuEntry> getRowContextMenuEntries(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row) {
return List.of(new RowContextMenuEntry(DELETE_ID, DELETE_LABEL, List.of("/icons/full/obj16/DeleteTool.svg")));
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,9 +15,12 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
Expand All @@ -26,15 +29,19 @@
import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput;
import org.eclipse.sirius.components.collaborative.tables.TableEventInput;
import org.eclipse.sirius.components.collaborative.tables.TableRefreshedEventPayload;
import org.eclipse.sirius.components.collaborative.tables.dto.InvokeRowContextMenuEntryInput;
import org.eclipse.sirius.components.collaborative.tables.dto.ResetTableRowsHeightInput;
import org.eclipse.sirius.components.collaborative.tables.dto.ResizeTableRowInput;
import org.eclipse.sirius.components.core.api.SuccessPayload;
import org.eclipse.sirius.components.tables.Line;
import org.eclipse.sirius.components.tables.tests.graphql.InvokeRowContextMenuEntryMutationRunner;
import org.eclipse.sirius.components.tables.tests.graphql.ResetTableRowsHeightMutationRunner;
import org.eclipse.sirius.components.tables.tests.graphql.ResizeTableRowMutationRunner;
import org.eclipse.sirius.components.tables.tests.graphql.RowContextMenuQueryRunner;
import org.eclipse.sirius.components.tables.tests.graphql.TableEventSubscriptionRunner;
import org.eclipse.sirius.web.AbstractIntegrationTests;
import org.eclipse.sirius.web.data.PapayaIdentifiers;
import org.eclipse.sirius.web.papaya.representations.table.PackageTableRowContextMenuProvider;
import org.eclipse.sirius.web.tests.services.api.IGivenCommittedTransaction;
import org.eclipse.sirius.web.tests.services.api.IGivenCreatedTableSubscription;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
Expand Down Expand Up @@ -76,6 +83,12 @@ public class PapayaTableRowControllerIntegrationTests extends AbstractIntegratio
@Autowired
private ResetTableRowsHeightMutationRunner resetTableRowsHeightMutationRunner;

@Autowired
private InvokeRowContextMenuEntryMutationRunner invokeRowContextMenuEntryMutationRunner;

@Autowired
private RowContextMenuQueryRunner rowContextMenuQueryRunner;

@Autowired
private TableEventSubscriptionRunner tableEventSubscriptionRunner;

Expand Down Expand Up @@ -211,4 +224,124 @@ public void givenTableWithAResizedRowWhenRowAResetRowsHeightMutationIsTriggeredT
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@Test
@DisplayName("Given a table, when row context menu entries are queried, then the correct entries are returned")
@Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void giveATableWhenRowContextMenuEntriesAreQueriedThenTheCorrectEntriesAreReturned() {
this.givenCommittedTransaction.commit();

var tableEventInput = new TableEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), PapayaIdentifiers.PAPAYA_PACKAGE_TABLE_REPRESENTATION.toString());
var flux = this.tableEventSubscriptionRunner.run(tableEventInput);

TestTransaction.flagForCommit();
TestTransaction.end();
TestTransaction.start();

var tableId = new AtomicReference<String>();
var rowId = new AtomicReference<UUID>();

Consumer<Object> initialTableContentConsumer = payload -> Optional.of(payload)
.filter(DataFetcherResult.class::isInstance)
.map(DataFetcherResult.class::cast)
.map(DataFetcherResult::getData)
.filter(TableRefreshedEventPayload.class::isInstance)
.map(TableRefreshedEventPayload.class::cast)
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getLines()).hasSize(2);
tableId.set(table.getId());
rowId.set(table.getLines().get(0).getId());
}, () -> fail(MISSING_TABLE));

Runnable getContextMenuActions = () -> {
Map<String, Object> variables = Map.of(
"editingContextId", PapayaIdentifiers.PAPAYA_PROJECT.toString(),
"representationId", tableId.get(),
"tableId", tableId.get(),
"rowId", rowId.get().toString()
);
var result = this.rowContextMenuQueryRunner.run(variables);
Object document = Configuration.defaultConfiguration().jsonProvider().parse(result);

List<String> actionLabels = JsonPath.read(document, "$.data.viewer.editingContext.representation.description.contextMenuEntries[*].label");
assertThat(actionLabels).isNotEmpty().hasSize(1);
assertThat(actionLabels.get(0)).isEqualTo("Delete row");
};

StepVerifier.create(flux)
.consumeNextWith(initialTableContentConsumer)
.then(getContextMenuActions)
.thenCancel()
.verify(Duration.ofSeconds(10));
}


@Test
@DisplayName("Given a table, when a row context menu entry is triggered, then the entry is correctly invoked ")
@Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void giveATableWhenARowContextMenuEntryIsTriggeredThenTheEntryIsCorrectlyInvoked() {
this.givenCommittedTransaction.commit();

var tableEventInput = new TableEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), PapayaIdentifiers.PAPAYA_PACKAGE_TABLE_REPRESENTATION.toString());
var flux = this.tableEventSubscriptionRunner.run(tableEventInput);

TestTransaction.flagForCommit();
TestTransaction.end();
TestTransaction.start();

var tableId = new AtomicReference<String>();
var rowId = new AtomicReference<UUID>();

Consumer<Object> initialTableContentConsumer = payload -> Optional.of(payload)
.filter(DataFetcherResult.class::isInstance)
.map(DataFetcherResult.class::cast)
.map(DataFetcherResult::getData)
.filter(TableRefreshedEventPayload.class::isInstance)
.map(TableRefreshedEventPayload.class::cast)
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getLines()).hasSize(2);
tableId.set(table.getId());
rowId.set(table.getLines().get(0).getId());
}, () -> fail(MISSING_TABLE));

Runnable invokeDeleteRowAction = () -> {
var invokeRowContextMenuEntryInput = new InvokeRowContextMenuEntryInput(
UUID.randomUUID(),
PapayaIdentifiers.PAPAYA_PROJECT.toString(),
tableId.get(),
tableId.get(),
rowId.get(),
PackageTableRowContextMenuProvider.DELETE_ID
);
var result = this.invokeRowContextMenuEntryMutationRunner.run(invokeRowContextMenuEntryInput);

String typename = JsonPath.read(result, "$.data.invokeRowContextMenuEntry.__typename");
assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName());
};

Consumer<Object> updatedTableContentConsumer = payload -> Optional.of(payload)
.filter(DataFetcherResult.class::isInstance)
.map(DataFetcherResult.class::cast)
.map(DataFetcherResult::getData)
.filter(TableRefreshedEventPayload.class::isInstance)
.map(TableRefreshedEventPayload.class::cast)
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getLines()).hasSize(1);
}, () -> fail(MISSING_TABLE));

StepVerifier.create(flux)
.consumeNextWith(initialTableContentConsumer)
.then(invokeDeleteRowAction)
.consumeNextWith(updatedTableContentConsumer)
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* Copyright (c) 2024, 2025 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 @@ -43,6 +43,13 @@ public Optional<Line> findLineByCellId(Table table, UUID cellId) {
.findFirst();
}

@Override
public Optional<Line> findLineById(Table table, UUID rowId) {
return table.getLines().stream()
.filter(row -> Objects.equals(rowId, row.getId()))
.findFirst();
}

@Override
public Optional<Column> findColumnById(Table table, UUID columnId) {
return table.getColumns().stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*******************************************************************************
* 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
*******************************************************************************/

package org.eclipse.sirius.components.collaborative.tables.api;

import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.representations.IStatus;
import org.eclipse.sirius.components.tables.Line;
import org.eclipse.sirius.components.tables.Table;
import org.eclipse.sirius.components.tables.descriptions.TableDescription;

/**
* Interface allowing to perform row context menu entries.
*
* @author Jerome Gout
*/
public interface IRowContextMenuEntryExecutor {
boolean canExecute(TableDescription tableDescription, String tableId, String rowId, String rowMenuContextEntryId);

IStatus execute(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row, String rowMenuContextEntryId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*******************************************************************************
* 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
*******************************************************************************/

package org.eclipse.sirius.components.collaborative.tables.api;

import java.util.List;

import org.eclipse.sirius.components.collaborative.tables.dto.RowContextMenuEntry;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.tables.Line;
import org.eclipse.sirius.components.tables.Table;
import org.eclipse.sirius.components.tables.descriptions.TableDescription;

/**
* Interface allowing to provide context menu entries in a table row.
*
* @author Jerome Gout
*/
public interface IRowContextMenuEntryProvider {

boolean canHandle(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row);

List<RowContextMenuEntry> getRowContextMenuEntries(IEditingContext editingContext, TableDescription tableDescription, Table table, Line row);

}
Loading

0 comments on commit 833d045

Please sign in to comment.