Skip to content

Commit

Permalink
[4234] Make rows resizable in table representation
Browse files Browse the repository at this point in the history
Bug: eclipse-sirius#4234
Signed-off-by: Jerome Gout <[email protected]>
Signed-off-by: Florian ROUËNÉ <[email protected]>
  • Loading branch information
jerome-obeo authored and frouene committed Dec 6, 2024
1 parent b3e6ba9 commit bd2b42a
Show file tree
Hide file tree
Showing 24 changed files with 722 additions and 23 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ Specifiers are also encouraged to implement their own `IRestDataVersionPayloadSe

- https://github.com/eclipse-sirius/sirius-web/issues/4219[#4219] [table] Improve front-end performance
- https://github.com/eclipse-sirius/sirius-web/issues/3823[#3823] [table] Remove cell type to use `ICellDescription`

- https://github.com/eclipse-sirius/sirius-web/issues/4231[#4231] [table] Make table's column resizable
- https://github.com/eclipse-sirius/sirius-web/issues/4234[#4234] [table] Make table's row resizable

== v2024.11.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public List<IRepresentationDescription> getRepresentationDescriptions(IEditingCo
.headerLabelProvider(headerLabelProvider)
.headerIconURLsProvider(headerIconURLsProvider)
.headerIndexLabelProvider(headerIndexLabelProvider)
.isResizablePredicate(variableManager -> true)
.initialHeightProvider(variableManager -> 53)
.build();

var tableDescription = TableDescription.newTableDescription(TABLE_DESCRIPTION_ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public List<IRepresentationDescription> getRepresentationDescriptions(IEditingCo
.headerLabelProvider(variableManager -> "")
.headerIconURLsProvider(variableManager -> List.of())
.headerIndexLabelProvider(variableManager -> "")
.isResizablePredicate(variableManager -> true)
.initialHeightProvider(variableManager -> 53)
.build();

var tableDescription = TableDescription.newTableDescription(TABLE_DESCRIPTION_ID)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*******************************************************************************
* Copyright (c) 2024 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.application.controllers.tables;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import com.jayway.jsonpath.JsonPath;

import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput;
import org.eclipse.sirius.components.collaborative.tables.TableRefreshedEventPayload;
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.ResizeTableRowMutationRunner;
import org.eclipse.sirius.web.AbstractIntegrationTests;
import org.eclipse.sirius.web.data.PapayaIdentifiers;
import org.eclipse.sirius.web.tests.services.api.IGivenCreatedTableSubscription;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.transaction.annotation.Transactional;

import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

/**
* Integration tests of the table's row with a papaya model.
*
* @author Jerome Gout
*/
@Transactional
@SuppressWarnings("checkstyle:MultipleStringLiterals")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"sirius.web.test.enabled=studio"})
public class PapayaTableRowControllerIntegrationTests extends AbstractIntegrationTests {

private static final String MISSING_TABLE = "Missing table";

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenCreatedTableSubscription givenCreatedTableSubscription;

@Autowired
private ResizeTableRowMutationRunner resizeTableRowMutationRunner;


@BeforeEach
public void beforeEach() {
this.givenInitialServerState.initialize();
}

private Flux<Object> givenSubscriptionToTable() {
var input = new CreateRepresentationInput(
UUID.randomUUID(),
PapayaIdentifiers.PAPAYA_PROJECT.toString(),
"papaya_package_table_description",
PapayaIdentifiers.SIRIUS_WEB_DOMAIN_PACKAGE.toString(),
"Table"
);
return this.givenCreatedTableSubscription.createAndSubscribe(input);
}

@Test
@DisplayName("Given a table, when a row resize mutation is triggered, then the representation is refreshed with the new row height")
@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 givenTableWhenRowResizeMutationTriggeredThenTheRepresentationIsRefreshedWithNewRowHeight() {
var flux = this.givenSubscriptionToTable();

var rowRef = new AtomicReference<Line>();
var tableId = new AtomicReference<String>();

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

Runnable resizeRow = () -> {
var lineToChange = rowRef.get();
var resizeTableRowInput = new ResizeTableRowInput(
UUID.randomUUID(),
PapayaIdentifiers.PAPAYA_PROJECT.toString(),
tableId.get(), tableId.get(), lineToChange.getId().toString(), 100);
var result = this.resizeTableRowMutationRunner.run(resizeTableRowInput);

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

Consumer<Object> updatedTableContentConsumer = payload -> Optional.of(payload)
.filter(TableRefreshedEventPayload.class::isInstance)
.map(TableRefreshedEventPayload.class::cast)
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getLines()).hasSize(2);
assertThat(table.getLines().get(0).getHeight()).isEqualTo(100);
}, () -> fail(MISSING_TABLE));

StepVerifier.create(flux)
.consumeNextWith(initialTableContentConsumer)
.then(resizeRow)
.consumeNextWith(updatedTableContentConsumer)
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ private TableWidgetDescription getTableWidgetDescription() {
.headerLabelProvider(variableManager -> "")
.headerIconURLsProvider(variableManager -> List.of())
.headerIndexLabelProvider(variableManager -> "")
.isResizablePredicate(variableManager -> false)
.initialHeightProvider(variableManager -> 0)
.build();

TableDescription tableDescription = TableDescription.newTableDescription(FORM_WITH_TABLE_ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private boolean shouldRefresh(ChangeDescription changeDescription) {

private IRepresentationRefreshPolicy getDefaultRefreshPolicy() {
return changeDescription -> switch (changeDescription.getKind()) {
case ChangeKind.SEMANTIC_CHANGE, ChangeKind.REPRESENTATION_RENAMING, ChangeKind.REPRESENTATION_DELETION, ChangeKind.REPRESENTATION_CREATION, TableChangeKind.TABLE_LAYOUT_CHANGE -> true;
case ChangeKind.SEMANTIC_CHANGE, TableChangeKind.TABLE_LAYOUT_CHANGE -> true;
default -> false;
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2024 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.dto;

import java.util.UUID;

import org.eclipse.sirius.components.collaborative.tables.api.ITableInput;

/**
* The input object for the resize of a row mutation.
*
* @author Jerome Gout
*/
public record ResizeTableRowInput(UUID id, String editingContextId, String representationId, String tableId, String rowId, int height) implements ITableInput {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*******************************************************************************
* Copyright (c) 2024 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.handlers;

import java.util.List;
import java.util.Map;

import org.eclipse.sirius.components.collaborative.api.ChangeDescription;
import org.eclipse.sirius.components.collaborative.api.ChangeKind;
import org.eclipse.sirius.components.collaborative.api.Monitoring;
import org.eclipse.sirius.components.collaborative.tables.TableChangeKind;
import org.eclipse.sirius.components.collaborative.tables.api.ITableContext;
import org.eclipse.sirius.components.collaborative.tables.api.ITableEventHandler;
import org.eclipse.sirius.components.collaborative.tables.api.ITableInput;
import org.eclipse.sirius.components.collaborative.tables.dto.EditTextfieldCellInput;
import org.eclipse.sirius.components.collaborative.tables.dto.ResizeTableRowInput;
import org.eclipse.sirius.components.collaborative.tables.messages.ICollaborativeTableMessageService;
import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.core.api.SuccessPayload;
import org.eclipse.sirius.components.tables.descriptions.TableDescription;
import org.eclipse.sirius.components.tables.events.ResizeTableRowEvent;
import org.springframework.stereotype.Service;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import reactor.core.publisher.Sinks;

/**
* Handle row resize event.
*
* @author Jerome Gout
*/
@Service
public class ResizeTableRowEventHandler implements ITableEventHandler {

private final ICollaborativeTableMessageService messageService;

private final Counter counter;

public ResizeTableRowEventHandler(ICollaborativeTableMessageService messageService, MeterRegistry meterRegistry) {
this.messageService = messageService;
this.counter = Counter.builder(Monitoring.EVENT_HANDLER)
.tag(Monitoring.NAME, this.getClass().getSimpleName())
.register(meterRegistry);
}

@Override
public boolean canHandle(ITableInput tableInput) {
return tableInput instanceof ResizeTableRowInput;
}

@Override
public void handle(Sinks.One<IPayload> payloadSink, Sinks.Many<ChangeDescription> changeDescriptionSink, IEditingContext editingContext, ITableContext tableContext, TableDescription tableDescription, ITableInput tableInput) {
this.counter.increment();

ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, tableInput.representationId(), tableInput);
String message = this.messageService.invalidInput(tableInput.getClass().getSimpleName(), EditTextfieldCellInput.class.getSimpleName());
IPayload payload = new ErrorPayload(tableInput.id(), message);

if (tableInput instanceof ResizeTableRowInput resizeTableRowInput) {
var resizeTableRowEvent = new ResizeTableRowEvent(resizeTableRowInput.rowId(), resizeTableRowInput.height());
tableContext.getTableEvents().add(new ResizeTableRowEvent(resizeTableRowInput.rowId(), resizeTableRowInput.height()));
payload = new SuccessPayload(resizeTableRowInput.id());
changeDescription = new ChangeDescription(TableChangeKind.TABLE_LAYOUT_CHANGE, tableInput.representationId(), tableInput,
Map.of(TableChangeKind.TABLE_EVENTS_PARAM, List.of(resizeTableRowEvent)));
}

payloadSink.tryEmitValue(payload);
changeDescriptionSink.tryEmitNext(changeDescription);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type Line {
headerLabel: String!
headerIconURLs: [String!]!
headerIndexLabel: String!
height: Int!
isResizable: Boolean!
}

type PaginationData {
Expand Down Expand Up @@ -121,6 +123,7 @@ extend type Mutation {
editTextfieldCell(input: EditTextfieldCellInput!): EditTextfieldCellPayload!
resizeTableColumn(input: ResizeTableColumnInput!): ResizeTableColumnPayload!
changeTableColumnVisibility(input: ChangeTableColumnVisibilityInput!): ChangeTableColumnVisibilityPayload!
resizeTableRow(input: ResizeTableRowInput!): ResizeTableRowPayload!
}

input EditCheckboxCellInput {
Expand Down Expand Up @@ -192,3 +195,14 @@ input ColumnVisibility {
}

union ChangeTableColumnVisibilityPayload = ErrorPayload | SuccessPayload

input ResizeTableRowInput {
id: ID!
editingContextId: ID!
representationId: ID!
tableId: ID!
rowId: ID!
height: Int!
}

union ResizeTableRowPayload = ErrorPayload | SuccessPayload
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2024 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.tables.graphql.datafetchers.mutation;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;

import org.eclipse.sirius.components.annotations.spring.graphql.MutationDataFetcher;
import org.eclipse.sirius.components.collaborative.tables.dto.ResizeTableRowInput;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher;
import org.eclipse.sirius.components.graphql.api.IExceptionWrapper;

import graphql.schema.DataFetchingEnvironment;

/**
* Data fetcher used to resize a table row.
*
* @author Jerome Gout
*/
@MutationDataFetcher(type = "Mutation", field = "resizeTableRow")
public class MutationResizeTableRowDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<IPayload>> {

private static final String INPUT_ARGUMENT = "input";

private final ObjectMapper objectMapper;

private final IExceptionWrapper exceptionWrapper;

private final IEditingContextDispatcher editingContextDispatcher;

public MutationResizeTableRowDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) {
this.objectMapper = Objects.requireNonNull(objectMapper);
this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper);
this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher);
}

@Override
public CompletableFuture<IPayload> get(DataFetchingEnvironment environment) throws Exception {
Object argument = environment.getArgument(INPUT_ARGUMENT);
var input = this.objectMapper.convertValue(argument, ResizeTableRowInput.class);

return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture();
}

}
Loading

0 comments on commit bd2b42a

Please sign in to comment.