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: eclipse-sirius#4403
Signed-off-by: Jerome Gout <[email protected]>
  • Loading branch information
jerome-obeo committed Jan 13, 2025
1 parent d4df511 commit 7f1f89e
Show file tree
Hide file tree
Showing 35 changed files with 1,414 additions and 153 deletions.
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.contextMenu[*].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);

}
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 @@ -29,7 +29,9 @@ public interface ITableQueryService {

Optional<ICell> findCellById(Table table, UUID cellId);

Optional<Line> findLineByCellId(Table table, UUID lineId);
Optional<Line> findLineByCellId(Table table, UUID cellId);

Optional<Line> findLineById(Table table, UUID lineId);

Optional<Column> findColumnById(Table table, UUID columnId);

Expand Down
Loading

0 comments on commit 7f1f89e

Please sign in to comment.