diff --git a/src/components/icons/icons.xml b/src/components/icons/icons.xml index 4305754246..cfe6394ced 100644 --- a/src/components/icons/icons.xml +++ b/src/components/icons/icons.xml @@ -841,6 +841,13 @@ + + + + + + + void; + onSelectionReordered?: (indexes: number[]) => void; onSelectionConfirmed?: () => void; colors?: Color[]; } @@ -73,13 +78,16 @@ export class SelectionInput extends Component { class: { type: String, optional: true }, onSelectionChanged: { type: Function, optional: true }, onSelectionConfirmed: { type: Function, optional: true }, + onSelectionReordered: { type: Function, optional: true }, colors: { type: Array, optional: true, default: [] }, }; private state: State = useState({ isMissing: false, mode: "select-range", }); + private dragAndDrop = useDragAndDropListItems(); private focusedInput = useRef("focusedInput"); + private selectionRef = useRef("o-selection"); private store!: Store; get ranges(): SelectionRange[] { @@ -126,6 +134,43 @@ export class SelectionInput extends Component { }); } + startDragAndDrop(rangeId: number, event: MouseEvent) { + if (event.button !== 0 || (event.target as HTMLElement).tagName === "SELECT") { + return; + } + + const rects = this.getRangeElementsRects(); + const draggableIds = this.ranges.map((range) => range.id); + const draggableItems = draggableIds.map((id, index) => ({ + id: id.toString(), + size: rects[index].height, + position: rects[index].y, + })); + this.dragAndDrop.start("vertical", { + draggedItemId: rangeId.toString(), + initialMousePosition: event.clientY, + items: draggableItems, + containerEl: this.selectionRef.el!, + onDragEnd: (dimensionName, finalIndex) => { + const originalIndex = draggableIds.findIndex((id) => id === rangeId); + if (originalIndex === finalIndex) { + return; + } + const draggedItems = [...draggableIds]; + draggedItems.splice(originalIndex, 1); + draggedItems.splice(finalIndex, 0, rangeId); + this.props.onSelectionReordered?.( + this.store.selectionInputs.map((range) => draggedItems.indexOf(range.id)) + ); + this.props.onSelectionConfirmed?.(); + }, + }); + } + + getRangeElementsRects() { + return Array.from(this.selectionRef.el!.children).map((el) => el.getBoundingClientRect()); + } + getColor(range: SelectionRange) { if (!range.color) { return ""; diff --git a/src/components/selection_input/selection_input.xml b/src/components/selection_input/selection_input.xml index f4e109640c..839bdd8330 100644 --- a/src/components/selection_input/selection_input.xml +++ b/src/components/selection_input/selection_input.xml @@ -1,12 +1,20 @@ -
+
+ + +
void; + onSelectionReordered?: (indexes: number[]) => void; onSelectionConfirmed: () => void; } @@ -18,6 +19,7 @@ export class ChartDataSeries extends Component { ranges: Array, hasSingleRange: { type: Boolean, optional: true }, onSelectionChanged: Function, + onSelectionReordered: { type: Function, optional: true }, onSelectionConfirmed: Function, }; diff --git a/src/components/side_panel/chart/building_blocks/data_series/data_series.xml b/src/components/side_panel/chart/building_blocks/data_series/data_series.xml index e520373a44..a80ab6fe00 100644 --- a/src/components/side_panel/chart/building_blocks/data_series/data_series.xml +++ b/src/components/side_panel/chart/building_blocks/data_series/data_series.xml @@ -7,6 +7,7 @@ hasSingleRange="props.hasSingleRange" onSelectionChanged="(ranges) => props.onSelectionChanged(ranges)" onSelectionConfirmed="() => props.onSelectionConfirmed()" + onSelectionReordered="(indexes) => props.onSelectionReordered(indexes)" colors="colors" /> diff --git a/src/components/side_panel/chart/building_blocks/generic_side_panel/config_panel.ts b/src/components/side_panel/chart/building_blocks/generic_side_panel/config_panel.ts index e330a55bb1..c4bebbd042 100644 --- a/src/components/side_panel/chart/building_blocks/generic_side_panel/config_panel.ts +++ b/src/components/side_panel/chart/building_blocks/generic_side_panel/config_panel.ts @@ -53,13 +53,13 @@ export class GenericChartConfigPanel extends Component ({ - ...this.dataSeriesRanges?.[i], + this.dataSets = ranges.map((dataRange, i) => ({ + ...this.dataSets?.[i], dataRange, })); this.state.datasetDispatchResult = this.props.canUpdateChart(this.props.figureId, { - dataSets: this.dataSeriesRanges, + dataSets: this.dataSets, + }); + } + + onDataSeriesReordered(indexes: number[]) { + this.dataSets = indexes.map((i) => this.dataSets[i]); + this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, { + dataSets: this.dataSets, }); } onDataSeriesConfirmed() { - this.dataSeriesRanges = spreadRange(this.env.model.getters, this.dataSeriesRanges); + this.dataSets = spreadRange(this.env.model.getters, this.dataSets); this.state.datasetDispatchResult = this.props.updateChart(this.props.figureId, { - dataSets: this.dataSeriesRanges, + dataSets: this.dataSets, }); } getDataSeriesRanges() { - return this.dataSeriesRanges; + return this.dataSets; } /** @@ -169,7 +176,7 @@ export class GenericChartConfigPanel extends Component - -
- - - - -
-
- diff --git a/src/components/side_panel/chart/line_chart/line_chart_config_panel.xml b/src/components/side_panel/chart/line_chart/line_chart_config_panel.xml index c898f9e277..5829c07be1 100644 --- a/src/components/side_panel/chart/line_chart/line_chart_config_panel.xml +++ b/src/components/side_panel/chart/line_chart/line_chart_config_panel.xml @@ -20,6 +20,7 @@ ranges="this.getDataSeriesRanges()" onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)" onSelectionConfirmed="() => this.onDataSeriesConfirmed()" + onSelectionReordered="(indexes) => this.onDataSeriesReordered(indexes)" /> { } }); + test("can reorder ranges in chart panel", async () => { + mockGetBoundingClientRect({ + "o-selection-input": (el: HTMLElement) => ({ + y: Array.from(el.parentElement!.children).indexOf(el) * 100, + height: 100, + }), + "o-selection": (el: HTMLElement) => ({ + y: 0, + height: 200, + }), + }); + createChart( + model, + { + dataSets: [ + { dataRange: "B1:B4", label: "serie_1" }, + { dataRange: "C1:C4", label: "serie_2" }, + ], + labelRange: "A2:A4", + type: "line", + }, + chartId + ); + await mountSpreadsheet(); + await openChartConfigSidePanel(model, env, chartId); + await dragElement( + fixture.querySelectorAll(".o-selection-input")[0], + { x: 0, y: 150 }, + undefined, + true + ); + const definition = model.getters.getChartDefinition(chartId) as LineChartDefinition; + expect(definition.dataSets).toMatchObject([ + { dataRange: "C1:C4", label: "serie_2" }, + { dataRange: "B1:B4", label: "serie_1" }, + ]); + }); + test("drawing of chart will receive new data after update", async () => { createTestChart("basicChart"); await mountSpreadsheet(); diff --git a/tests/pivots/spreadsheet_pivot/__snapshots__/spreadsheet_pivot_side_panel.test.ts.snap b/tests/pivots/spreadsheet_pivot/__snapshots__/spreadsheet_pivot_side_panel.test.ts.snap index f9e085fc70..94eb5e3331 100644 --- a/tests/pivots/spreadsheet_pivot/__snapshots__/spreadsheet_pivot_side_panel.test.ts.snap +++ b/tests/pivots/spreadsheet_pivot/__snapshots__/spreadsheet_pivot_side_panel.test.ts.snap @@ -113,6 +113,7 @@ exports[`Spreadsheet pivot side panel It should correctly be displayed 1`] = `
+
@@ -327,6 +328,7 @@ exports[`Spreadsheet pivot side panel It should display only the selection input
+
diff --git a/tests/side_panels/building_blocks/__snapshots__/data_series.test.ts.snap b/tests/side_panels/building_blocks/__snapshots__/data_series.test.ts.snap index f9d615adda..ac49172e45 100644 --- a/tests/side_panels/building_blocks/__snapshots__/data_series.test.ts.snap +++ b/tests/side_panels/building_blocks/__snapshots__/data_series.test.ts.snap @@ -19,6 +19,7 @@ exports[`Data Series Can render a data series component 1`] = `
+
diff --git a/tests/side_panels/building_blocks/__snapshots__/label_range.test.ts.snap b/tests/side_panels/building_blocks/__snapshots__/label_range.test.ts.snap index 9eaa90800a..1d0a21252b 100644 --- a/tests/side_panels/building_blocks/__snapshots__/label_range.test.ts.snap +++ b/tests/side_panels/building_blocks/__snapshots__/label_range.test.ts.snap @@ -19,6 +19,7 @@ exports[`Label range Can add options to the label range component 1`] = `
+
@@ -78,6 +79,7 @@ exports[`Label range Can render a label range component 1`] = `
+