From 6aebbd305e3f15cd26c7c07b7ba696f3b4a72df1 Mon Sep 17 00:00:00 2001 From: Jerome Gout Date: Fri, 20 Dec 2024 11:13:37 +0100 Subject: [PATCH] [4335] Add support of column reordering in table representation Bug: https://github.com/eclipse-sirius/sirius-web/issues/4335 Signed-off-by: Jerome Gout --- CHANGELOG.adoc | 1 + .../TableWidgetPropertySection.tsx | 1 + ...TableColumnControllerIntegrationTests.java | 68 +++++++++++++++ .../tables/dto/ReorderTableColumnsInput.java | 27 ++++++ .../ReorderTableColumnsEventHandler.java | 78 +++++++++++++++++ .../src/main/resources/schema/table.graphqls | 12 +++ ...utationReorderTableColumnsDataFetcher.java | 59 +++++++++++++ .../ReorderTableColumnsMutationRunner.java | 60 +++++++++++++ .../sirius/components/tables/Column.java | 14 +++ .../tables/components/ColumnComponent.java | 17 +++- .../tables/elements/ColumnElementProps.java | 12 ++- .../events/ReorderTableColumnsEvent.java | 28 ++++++ .../tables/renderer/TableElementFactory.java | 2 +- .../src/columns/useTableColumnOrdering.ts | 87 +++++++++++++++++++ .../columns/useTableColumnOrdering.types.ts | 39 +++++++++ .../representation/TableRepresentation.tsx | 1 + .../representation/useTableSubscription.ts | 3 +- .../src/table/TableContent.tsx | 10 ++- .../src/table/TableContent.types.ts | 4 +- .../src/table/useTableColumns.tsx | 5 +- 20 files changed, 518 insertions(+), 10 deletions(-) create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/ReorderTableColumnsInput.java create mode 100644 packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ReorderTableColumnsEventHandler.java create mode 100644 packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationReorderTableColumnsDataFetcher.java create mode 100644 packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/ReorderTableColumnsMutationRunner.java create mode 100644 packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/events/ReorderTableColumnsEvent.java create mode 100644 packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.ts create mode 100644 packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.types.ts diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c8e9889bbc2..7397fd3c793 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -108,6 +108,7 @@ This is now fixed - https://github.com/eclipse-sirius/sirius-web/issues/4291[#4291] [core] Implement REST API documentation with openAPI v3 and swagger * Documentation is availlable in the url : /v3/api-docs * Swagger UI is availlable in the url : /swagger-ui/index.html#/ +- https://github.com/eclipse-sirius/sirius-web/issues/4335[#4335] [table] Add support of column reordering in table representation === Improvements diff --git a/packages/forms/frontend/sirius-components-forms/src/propertysections/TableWidgetPropertySection.tsx b/packages/forms/frontend/sirius-components-forms/src/propertysections/TableWidgetPropertySection.tsx index a728753a87f..faecd02c486 100644 --- a/packages/forms/frontend/sirius-components-forms/src/propertysections/TableWidgetPropertySection.tsx +++ b/packages/forms/frontend/sirius-components-forms/src/propertysections/TableWidgetPropertySection.tsx @@ -47,6 +47,7 @@ export const TableWidgetPropertySection: PropertySectionComponent ); diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableColumnControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableColumnControllerIntegrationTests.java index 4a1d2244a1a..9a5d44842ee 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableColumnControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/tables/PapayaTableColumnControllerIntegrationTests.java @@ -18,6 +18,8 @@ import com.jayway.jsonpath.JsonPath; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -28,10 +30,12 @@ import org.eclipse.sirius.components.collaborative.tables.TableRefreshedEventPayload; import org.eclipse.sirius.components.collaborative.tables.dto.ChangeTableColumnVisibilityInput; import org.eclipse.sirius.components.collaborative.tables.dto.ColumnVisibility; +import org.eclipse.sirius.components.collaborative.tables.dto.ReorderTableColumnsInput; import org.eclipse.sirius.components.collaborative.tables.dto.ResizeTableColumnInput; import org.eclipse.sirius.components.core.api.SuccessPayload; import org.eclipse.sirius.components.tables.Column; import org.eclipse.sirius.components.tables.tests.graphql.ChangeTableColumnVisibilityMutationRunner; +import org.eclipse.sirius.components.tables.tests.graphql.ReorderTableColumnsMutationRunner; import org.eclipse.sirius.components.tables.tests.graphql.ResizeTableColumnMutationRunner; import org.eclipse.sirius.web.AbstractIntegrationTests; import org.eclipse.sirius.web.data.PapayaIdentifiers; @@ -70,6 +74,9 @@ public class PapayaTableColumnControllerIntegrationTests extends AbstractIntegra @Autowired private ResizeTableColumnMutationRunner resizeTableColumnMutationRunner; + @Autowired + private ReorderTableColumnsMutationRunner reorderTableColumnsMutationRunner; + @Autowired private ChangeTableColumnVisibilityMutationRunner changeTableColumnVisibilityMutationRunner; @@ -210,4 +217,65 @@ public void givenTableWhenColumnVisibilityChangesMutationTriggeredThenTheReprese .verify(Duration.ofSeconds(10)); } + @Test + @DisplayName("Given a table, when columns reorder change mutation is triggered, then the representation is refreshed with the new order of columns") + @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 givenTableWhenColumnsReorderChangeMutationTriggeredThenTheRepresentationIsRefreshedWithNewOrderOfColumns() { + var flux = this.givenSubscriptionToTable(); + + var columnIdsRef = new AtomicReference>(); + var tableId = new AtomicReference(); + + 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(5); + table.getColumns().forEach(col -> assertThat(col.getIndex()).isEqualTo(0)); + columnIdsRef.set(table.getColumns().stream().map(Column::getId).map(UUID::toString).toList()); + tableId.set(table.getId()); + }, () -> fail(MISSING_TABLE)); + + Runnable changeColumnsOrder = () -> { + List ids = new ArrayList<>(columnIdsRef.get()); + Collections.swap(ids, 0, 1); // swap first two columns + var changeTableColumnsOrderInput = new ReorderTableColumnsInput( + UUID.randomUUID(), + PapayaIdentifiers.PAPAYA_PROJECT.toString(), + tableId.get(), + tableId.get(), + ids + ); + var result = this.reorderTableColumnsMutationRunner.run(changeTableColumnsOrderInput); + + String typename = JsonPath.read(result, "$.data.reorderTableColumns.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + }; + + + Consumer updatedTableContentConsumer = payload -> Optional.of(payload) + .filter(TableRefreshedEventPayload.class::isInstance) + .map(TableRefreshedEventPayload.class::cast) + .map(TableRefreshedEventPayload::table) + .ifPresentOrElse(table -> { + assertThat(table).isNotNull(); + assertThat(table.getColumns()).hasSize(5); + assertThat(table.getColumns().get(0).getIndex()).isEqualTo(1); + assertThat(table.getColumns().get(1).getIndex()).isEqualTo(0); + assertThat(table.getColumns().get(2).getIndex()).isEqualTo(2); + assertThat(table.getColumns().get(3).getIndex()).isEqualTo(3); + assertThat(table.getColumns().get(4).getIndex()).isEqualTo(4); + }, () -> fail(MISSING_TABLE)); + + StepVerifier.create(flux) + .consumeNextWith(initialTableContentConsumer) + .then(changeColumnsOrder) + .consumeNextWith(updatedTableContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + } diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/ReorderTableColumnsInput.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/ReorderTableColumnsInput.java new file mode 100644 index 00000000000..20da949a2f9 --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/dto/ReorderTableColumnsInput.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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.List; +import java.util.UUID; + +import org.eclipse.sirius.components.collaborative.tables.api.ITableInput; + +/** + * The input object for the reorder of columns mutation. + * + * @author Jerome Gout + */ +public record ReorderTableColumnsInput(UUID id, String editingContextId, String representationId, String tableId, List reorderedColumnIds) implements ITableInput { + +} diff --git a/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ReorderTableColumnsEventHandler.java b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ReorderTableColumnsEventHandler.java new file mode 100644 index 00000000000..fe75c32c6c1 --- /dev/null +++ b/packages/tables/backend/sirius-components-collaborative-tables/src/main/java/org/eclipse/sirius/components/collaborative/tables/handlers/ReorderTableColumnsEventHandler.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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 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.ReorderTableColumnsInput; +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.ReorderTableColumnsEvent; +import org.springframework.stereotype.Service; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import reactor.core.publisher.Sinks; + +/** + * Handle columns reorder event. + * + * @author Jerome Gout + */ +@Service +public class ReorderTableColumnsEventHandler implements ITableEventHandler { + + private final ICollaborativeTableMessageService messageService; + + private final Counter counter; + + public ReorderTableColumnsEventHandler(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 ReorderTableColumnsInput; + } + + @Override + public void handle(Sinks.One payloadSink, Sinks.Many 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 ReorderTableColumnsInput reorderTableColumnsInput) { + tableContext.getTableEvents().add(new ReorderTableColumnsEvent(reorderTableColumnsInput.reorderedColumnIds())); + payload = new SuccessPayload(reorderTableColumnsInput.id()); + changeDescription = new ChangeDescription(TableChangeKind.TABLE_LAYOUT_CHANGE, tableInput.representationId(), tableInput); + } + + payloadSink.tryEmitValue(payload); + changeDescriptionSink.tryEmitNext(changeDescription); + } +} 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 31b5f52384e..410aa3d97c3 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 @@ -54,6 +54,7 @@ type Column { isResizable: Boolean! hidden: Boolean! filterVariant: String! + index: Int } type Line { @@ -145,6 +146,7 @@ extend type Mutation { resetTableRowsHeight(input: ResetTableRowsHeightInput!): ResetTableRowsHeightPayload! changeGlobalFilterValue(input: ChangeGlobalFilterValueInput!): ChangeGlobalFilterValuePayload! changeColumnFilter(input: ChangeColumnFilterInput!): ChangeColumnFilterPayload! + reorderTableColumns(input: ReorderTableColumnsInput!): ReorderTableColumnsPayload! } input EditCheckboxCellInput { @@ -261,3 +263,13 @@ input ResetTableRowsHeightInput { } union ResetTableRowsHeightPayload = ErrorPayload | SuccessPayload + +input ReorderTableColumnsInput { + id: ID! + editingContextId: ID! + representationId: ID! + tableId: ID! + reorderedColumnIds: [ID!]! +} + +union ReorderTableColumnsPayload = ErrorPayload | SuccessPayload diff --git a/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationReorderTableColumnsDataFetcher.java b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationReorderTableColumnsDataFetcher.java new file mode 100644 index 00000000000..ce798e12048 --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-graphql/src/main/java/org/eclipse/sirius/components/tables/graphql/datafetchers/mutation/MutationReorderTableColumnsDataFetcher.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.ReorderTableColumnsInput; +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 reorder the columns of a table. + * + * @author Jerome Gout + */ +@MutationDataFetcher(type = "Mutation", field = "reorderTableColumns") +public class MutationReorderTableColumnsDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEditingContextDispatcher editingContextDispatcher; + + public MutationReorderTableColumnsDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, ReorderTableColumnsInput.class); + + return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture(); + } + +} diff --git a/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/ReorderTableColumnsMutationRunner.java b/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/ReorderTableColumnsMutationRunner.java new file mode 100644 index 00000000000..bc0cf3034e7 --- /dev/null +++ b/packages/tables/backend/sirius-components-tables-tests/src/main/java/org/eclipse/sirius/components/tables/tests/graphql/ReorderTableColumnsMutationRunner.java @@ -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.tests.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.tables.dto.ReorderTableColumnsInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.springframework.stereotype.Service; + +/** + * Used to change the order of columns in table with the GraphQL API. + * + * @author Jerome Gout + */ +@Service +public class ReorderTableColumnsMutationRunner implements IMutationRunner { + + private static final String REORDER_COLUMNS_MUTATION = """ + mutation reorderTableColumns($input: ReorderTableColumnsInput!) { + reorderTableColumns(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on SuccessPayload { + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public ReorderTableColumnsMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public String run(ReorderTableColumnsInput input) { + return this.graphQLRequestor.execute(REORDER_COLUMNS_MUTATION, input); + } +} diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Column.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Column.java index 2527e0186a8..fb212b590c9 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Column.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/Column.java @@ -50,6 +50,8 @@ public final class Column { private String filterVariant; + private int index; + private Column() { // Prevent instantiation } @@ -98,6 +100,10 @@ public String getFilterVariant() { return this.filterVariant; } + public int getIndex() { + return this.index; + } + public static Builder newColumn(UUID id) { return new Builder(id); } @@ -138,6 +144,8 @@ public static final class Builder { private String filterVariant; + private int index; + private Builder(UUID id) { this.id = Objects.requireNonNull(id); } @@ -192,6 +200,11 @@ public Builder filterVariant(String filterVariant) { return this; } + public Builder index(int index) { + this.index = index; + return this; + } + public Column build() { Column column = new Column(); column.id = Objects.requireNonNull(this.id); @@ -205,6 +218,7 @@ public Column build() { column.resizable = this.resizable; column.hidden = this.hidden; column.filterVariant = Objects.requireNonNull(this.filterVariant); + column.index = this.index; return column; } } diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/ColumnComponent.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/ColumnComponent.java index dc826c9ad3b..ca384a78c76 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/ColumnComponent.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/components/ColumnComponent.java @@ -26,6 +26,7 @@ import org.eclipse.sirius.components.tables.descriptions.ColumnDescription; import org.eclipse.sirius.components.tables.elements.ColumnElementProps; import org.eclipse.sirius.components.tables.events.ChangeTableColumnVisibilityEvent; +import org.eclipse.sirius.components.tables.events.ReorderTableColumnsEvent; import org.eclipse.sirius.components.tables.events.ResizeTableColumnEvent; /** @@ -61,12 +62,12 @@ public Element render() { return new Fragment(fragmentProps); } - private Element doRender(VariableManager variableManager, Object object, int index) { + private Element doRender(VariableManager variableManager, Object object, int columnIndex) { ColumnDescription columnDescription = this.props.columnDescription(); VariableManager columnVariableManager = variableManager.createChild(); columnVariableManager.put(VariableManager.SELF, object); - columnVariableManager.put("columnIndex", index); + columnVariableManager.put("columnIndex", columnIndex); String targetObjectId = columnDescription.getTargetObjectIdProvider().apply(columnVariableManager); String targetObjectKind = columnDescription.getTargetObjectKindProvider().apply(columnVariableManager); @@ -91,6 +92,17 @@ private Element doRender(VariableManager variableManager, Object object, int ind .map(Column::getWidth) .findFirst().orElse(initialWidth)); + var index = this.props.tableEvents().stream() + .filter(ReorderTableColumnsEvent.class::isInstance) + .map(ReorderTableColumnsEvent.class::cast) + .filter(event -> event.reorderedColumIds().contains(columnId.toString())) + .findFirst() + .map(event -> event.reorderedColumIds().indexOf(columnId.toString())) + .orElseGet(() -> this.props.previousColumns().stream() + .filter(column -> column.getId().equals(columnId)) + .map(Column::getIndex) + .findFirst().orElse(0)); + boolean hidden = this.props.tableEvents().stream() .filter(ChangeTableColumnVisibilityEvent.class::isInstance) .map(ChangeTableColumnVisibilityEvent.class::cast) @@ -112,6 +124,7 @@ private Element doRender(VariableManager variableManager, Object object, int ind .targetObjectKind(targetObjectKind) .resizable(resizable) .width(width) + .index(index) .hidden(hidden) .filterVariant(filterVariant); diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/ColumnElementProps.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/ColumnElementProps.java index 439f0fdfe60..af2777c3f11 100644 --- a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/ColumnElementProps.java +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/elements/ColumnElementProps.java @@ -34,7 +34,8 @@ public record ColumnElementProps( int width, boolean resizable, boolean hidden, - String filterVariant) implements IProps { + String filterVariant, + int index) implements IProps { public static final String TYPE = "Column"; @@ -83,6 +84,8 @@ public static final class Builder { private String filterVariant; + private int index; + private Builder(UUID id) { this.id = Objects.requireNonNull(id); } @@ -137,9 +140,14 @@ public Builder filterVariant(String filterVariant) { return this; } + public Builder index(int index) { + this.index = index; + return this; + } + public ColumnElementProps build() { return new ColumnElementProps(this.id, this.descriptionId, this.headerLabel, this.headerIconURLs, this.headerIndexLabel, this.targetObjectId, this.targetObjectKind, this.width, - this.resizable, this.hidden, this.filterVariant); + this.resizable, this.hidden, this.filterVariant, this.index); } } } diff --git a/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/events/ReorderTableColumnsEvent.java b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/events/ReorderTableColumnsEvent.java new file mode 100644 index 00000000000..e1182d60078 --- /dev/null +++ b/packages/tables/backend/sirius-components-tables/src/main/java/org/eclipse/sirius/components/tables/events/ReorderTableColumnsEvent.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.events; + +import java.util.List; +import java.util.Objects; + +/** + * Table Event to handle columns reordering. + * + * @author Jerome Gout + */ +public record ReorderTableColumnsEvent(List reorderedColumIds) implements ITableEvent { + + public ReorderTableColumnsEvent { + Objects.requireNonNull(reorderedColumIds); + } +} 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 4e1e05d283b..06036960d37 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 @@ -138,8 +138,8 @@ private Column instantiateColumn(IProps props) { .resizable(columnElementProps.resizable()) .hidden(columnElementProps.hidden()) .filterVariant(columnElementProps.filterVariant()) + .index(columnElementProps.index()) .build(); - } return null; } diff --git a/packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.ts b/packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.ts new file mode 100644 index 00000000000..be3c6bba898 --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.ts @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +import { gql, useMutation } from '@apollo/client'; +import { useReporting } from '@eclipse-sirius/sirius-components-core'; +import { MRT_ColumnOrderState } from 'material-react-table'; +import { useEffect, useState } from 'react'; +import { GQLTable } from '../table/TableContent.types'; +import { + GQLReorderColumnsData, + GQLReorderColumnsInput, + GQLReorderColumnsVariables, + UseTableColumnOrderingValue, +} from './useTableColumnOrdering.types'; + +const reorderColumnsMutation = gql` + mutation reorderTableColumns($input: ReorderTableColumnsInput!) { + reorderTableColumns(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on SuccessPayload { + messages { + body + level + } + } + } + } +`; + +export const useTableColumnOrdering = ( + editingContextId: string, + representationId: string, + table: GQLTable +): UseTableColumnOrderingValue => { + const [columnOrder, setColumnOrder] = useState( + [...table.columns].sort((a, b) => a.index - b.index).map((col) => col.id) + ); + const [mutationReorderColumns, mutationReorderColumnsResult] = useMutation< + GQLReorderColumnsData, + GQLReorderColumnsVariables + >(reorderColumnsMutation); + useReporting(mutationReorderColumnsResult, (data: GQLReorderColumnsData) => data.reorderTableColumns); + + const reorderColumns = (reorderedColumnIds: string[]) => { + const input: GQLReorderColumnsInput = { + id: crypto.randomUUID(), + editingContextId, + representationId, + tableId: table.id, + reorderedColumnIds, + }; + + mutationReorderColumns({ variables: { input } }); + }; + + useEffect(() => { + if (columnOrder.length > 0) { + reorderColumns(columnOrder.filter((row: string) => !row.startsWith('mrt-row'))); + } + }, [columnOrder]); + + useEffect(() => { + //Once the table is up to date, we don't want to keep the column order in the state but use the data coming from + // the backend. + setColumnOrder([]); + }, [table.columns.map((column) => column.id).join()]); + + return { + columnOrder, + setColumnOrder, + }; +}; diff --git a/packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.types.ts b/packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.types.ts new file mode 100644 index 00000000000..f0c196637e9 --- /dev/null +++ b/packages/tables/frontend/sirius-components-tables/src/columns/useTableColumnOrdering.types.ts @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +import { GQLErrorPayload, GQLSuccessPayload } from '@eclipse-sirius/sirius-components-core'; +import { MRT_ColumnOrderState } from 'material-react-table'; + +export interface UseTableColumnOrderingValue { + columnOrder: MRT_ColumnOrderState; + setColumnOrder: ( + columnOrder: MRT_ColumnOrderState | ((prevState: MRT_ColumnOrderState) => MRT_ColumnOrderState) + ) => void; +} + +export interface GQLReorderColumnsInput { + id: string; + editingContextId: string; + representationId: string; + tableId: string; + reorderedColumnIds: string[]; +} + +export interface GQLReorderColumnsVariables { + input: GQLReorderColumnsInput; +} + +export interface GQLReorderColumnsData { + reorderTableColumns: GQLReorderTableColumnsPayload; +} + +export type GQLReorderTableColumnsPayload = GQLErrorPayload | GQLSuccessPayload; diff --git a/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx b/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx index 3a297085c9b..63c00f9a22d 100644 --- a/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/representation/TableRepresentation.tsx @@ -89,6 +89,7 @@ export const TableRepresentation = ({ editingContextId, representationId, readOn enableRowSizing enableGlobalFilter enablePagination + enableColumnOrdering /> ) : null} {completeMessage} 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 c9d122d7b32..2f499d4b10f 100644 --- a/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts +++ b/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts @@ -61,9 +61,10 @@ export const getTableEventSubscription = ` targetObjectId targetObjectKind width - isResizable + isResizable hidden filterVariant + index } lines { id 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 c85bf29492d..249232cdad2 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/table/TableContent.tsx @@ -16,6 +16,7 @@ import { MaterialReactTable, MRT_DensityState, MRT_TableOptions, useMaterialReac import { memo, useEffect, useState } from 'react'; import { SettingsButton } from '../actions/SettingsButton'; import { useTableColumnFiltering } from '../columns/useTableColumnFiltering'; +import { useTableColumnOrdering } from '../columns/useTableColumnOrdering'; import { useTableColumnSizing } from '../columns/useTableColumnSizing'; import { useTableColumnVisibility } from '../columns/useTableColumnVisibility'; import { ResizeRowHandler } from '../rows/ResizeRowHandler'; @@ -41,6 +42,7 @@ export const TableContent = memo( enableRowSizing, enableGlobalFilter, enablePagination, + enableColumnOrdering, }: TableProps) => { const { selection, setSelection } = useSelection(); @@ -51,7 +53,8 @@ export const TableContent = memo( readOnly, enableColumnVisibility, enableColumnResizing, - enableColumnFilters + enableColumnFilters, + enableColumnOrdering ); const { columnSizing, setColumnSizing } = useTableColumnSizing( editingContextId, @@ -59,6 +62,7 @@ export const TableContent = memo( table, enableColumnResizing ); + const { columnOrder, setColumnOrder } = useTableColumnOrdering(editingContextId, representationId, table); const { columnVisibility, setColumnVisibility } = useTableColumnVisibility( editingContextId, representationId, @@ -162,7 +166,9 @@ export const TableContent = memo( onColumnSizingChange: setColumnSizing, onColumnVisibilityChange: setColumnVisibility, onDensityChange: setDensity, - state: { columnSizing, columnVisibility, globalFilter, density, columnFilters }, + enableColumnOrdering: true, + onColumnOrderChange: setColumnOrder, + state: { columnSizing, columnVisibility, globalFilter, density, columnFilters, columnOrder }, muiTableBodyRowProps: ({ row }) => { return { onClick: () => { 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 b7259cd642f..bf00d7a0eb9 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 @@ -19,13 +19,14 @@ export interface TableProps { readOnly: boolean; onPaginationChange: (cursor: string | null, direction: 'PREV' | 'NEXT', size: number) => void; onGlobalFilterChange: (globalFilter: string) => void; - onColumnFiltersChange: (columFilters: ColumnFilter[]) => void; + onColumnFiltersChange: (columnFilters: ColumnFilter[]) => void; enableColumnVisibility: boolean; enableColumnResizing: boolean; enableColumnFilters: boolean; enableRowSizing: boolean; enableGlobalFilter: boolean; enablePagination: boolean; + enableColumnOrdering: boolean; } export interface TablePaginationState { @@ -81,6 +82,7 @@ export interface GQLColumn { | 'time' | 'time-range' | undefined; + index: number; } export interface GQLLine { 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 f1b2e09a1ea..32680cee395 100644 --- a/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx +++ b/packages/tables/frontend/sirius-components-tables/src/table/useTableColumns.tsx @@ -24,7 +24,8 @@ export const useTableColumns = ( readOnly: boolean, enableColumnVisibility: boolean, enableColumnSizing: boolean, - enableColumnFilters: boolean + enableColumnFilters: boolean, + enableColumnOrdering: boolean ): UseTableColumnsValue => { const columns = useMemo[]>(() => { const columnDefs: MRT_ColumnDef[] = table.columns.map((column) => { @@ -39,6 +40,8 @@ export const useTableColumns = ( size: enableColumnSizing && column.width > 0 ? column.width : undefined, enableResizing: enableColumnSizing && column.isResizable, visibleInShowHideMenu: enableColumnVisibility, + enableColumnDragging: enableColumnOrdering, + enableColumnOrdering, Cell: ({ row }) => { const cell: GQLCell | null = row.original.cells.find((cell) => column.id === cell.columnId) ?? null; return (