Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.16] [Backport 2.x] Add Drag & Drop Across Axis Functionality to Vis Builder #7416

Merged
merged 2 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/7107.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Enhance Drag & Drop functionality in Vis Builder ([#7107](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7107))
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,49 @@

import { EuiForm } from '@elastic/eui';
import React from 'react';
import { useVisualizationType } from '../../utils/use';
import { useTypedSelector } from '../../utils/state_management';

import './config_panel.scss';
import { mapSchemaToAggPanel } from './schema_to_dropbox';
import { SecondaryPanel } from './secondary_panel';
import { Schemas } from '../../../../../vis_default_editor/public';
import {
AggConfig,
AggConfigs,
CreateAggConfigParams,
} from '../../../../../data/common/search/aggs';
import { IndexPattern, TimeRange } from '../../../../../data/public';
import { SchemaDisplayStates } from '.';

export function ConfigPanel() {
const vizType = useVisualizationType();
const editingState = useTypedSelector(
(state) => state.visualization.activeVisualization?.draftAgg
);
const schemas = vizType.ui.containerConfig.data.schemas;
export interface AggProps {
indexPattern: IndexPattern | undefined;
aggConfigs: AggConfigs | undefined;
aggs: AggConfig[];
timeRange: TimeRange;
}

export interface ConfigPanelProps {
schemas: Schemas;
editingState?: CreateAggConfigParams;
aggProps: AggProps;
activeSchemaFields: SchemaDisplayStates;
setActiveSchemaFields: React.Dispatch<React.SetStateAction<SchemaDisplayStates>>;
}

export function ConfigPanel({
schemas,
editingState,
aggProps,
activeSchemaFields,
setActiveSchemaFields,
}: ConfigPanelProps) {
if (!schemas) return null;

const mainPanel = mapSchemaToAggPanel(schemas);
const mainPanel = mapSchemaToAggPanel(
schemas,
aggProps,
activeSchemaFields,
setActiveSchemaFields
);

return (
<EuiForm className={`vbConfig ${editingState ? 'showSecondary' : ''}`}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export enum FIELD_SELECTOR_ID {
COUNT = 'preDefinedCountMetric',
CATEGORICAL = 'categoricalFields',
NUMERICAL = 'numericalFields',
META = 'metaFields',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { DropResult } from '@elastic/eui';
import { AnyAction } from 'redux';
import { createNewAggConfig } from '../utils/get_valid_aggregations';
import { updateAggConfigParams } from '../../../utils/state_management/visualization_slice';
import { Schemas } from '../../../../../../vis_default_editor/public';
import { AggProps } from '../config_panel';
import { SchemaDisplayStates } from '../index';
import { Dispatch } from '../../../../../../opensearch_dashboards_utils/common/state_containers/types';
import { AggsStart } from '../../../../../../data/common';

export interface DragDropProperties {
dropResult: DropResult;
schemas: Schemas;
aggProps: AggProps;
aggService: AggsStart;
activeSchemaFields: SchemaDisplayStates;
dispatch: Dispatch<AnyAction>;
}

export function addFieldToConfiguration({
dropResult,
schemas,
aggProps,
aggService,
activeSchemaFields,
dispatch,
}: DragDropProperties) {
const { source, destination, combine, draggableId } = dropResult;

const destinationSchemaName = destination?.droppableId;
const destinationSchema = schemas.all.find((schema) => schema.name === destinationSchemaName);

const newFieldToAdd = draggableId;

if (!destinationSchema || !destinationSchemaName) {
// Invalid drop target selected
return;
}

const destinationFields = activeSchemaFields[destinationSchemaName];

if (!combine && destination && destinationFields.length > destinationSchema?.max) {
// Can't Add additional Fields
return;
}

// Adding the new field
createNewAggConfig({
fieldName: newFieldToAdd,
sourceGroup: source.droppableId,
destinationSchema,
aggProps,
aggService,
sourceAgg: null,
});

const updatedAggConfigs = aggProps.aggConfigs?.aggs;

if (updatedAggConfigs) {
dispatch(updateAggConfigParams(updatedAggConfigs.map((agg) => agg.serialize())));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { updateAggConfigParams } from '../../../utils/state_management/visualization_slice';
import { createNewAggConfig } from '../utils/get_valid_aggregations';
import { DragDropProperties } from './add_field_to_configuration';

export function moveFieldBetweenSchemas({
dropResult,
schemas,
aggProps,
aggService,
activeSchemaFields,
dispatch,
}: DragDropProperties) {
const { source, destination, combine, draggableId } = dropResult;

const destinationSchemaName = destination?.droppableId;
if (!destinationSchemaName) {
// Invalid Transition
return;
}
const sourceAggId = draggableId;

const destinationSchema = schemas.all.find(
(schema) => schema.name === (destination?.droppableId || combine?.droppableId)
);

if (!destinationSchema) {
// Invalid Transition
return;
}

const sourceAgg = aggProps.aggConfigs?.aggs.find((agg) => agg.id === sourceAggId);
const sourceFieldName = sourceAgg?.fieldName();

const destinationAggFields = activeSchemaFields[destinationSchemaName];

const destinationLimit = destinationSchema?.max;

if (destinationLimit && destinationAggFields.length <= destinationLimit) {
// destination schema has space for more items to be added
// We Need to update sourceAgg

createNewAggConfig({
fieldName: sourceFieldName,
sourceGroup: source.droppableId,
destinationSchema,
aggProps,
aggService,
sourceAgg,
});

// Remove the sourceAggConfig from the updated Config
const updatedAggConfig = aggProps.aggConfigs?.aggs.filter((agg) => agg.id !== sourceAggId);

if (updatedAggConfig?.length) {
dispatch(updateAggConfigParams(updatedAggConfig.map((agg) => agg.serialize())));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { reorderAgg } from '../../../utils/state_management/visualization_slice';
import { DragDropProperties } from './add_field_to_configuration';

export function reorderFieldsWithinSchema({
dropResult,
schemas,
activeSchemaFields,
dispatch,
}: DragDropProperties) {
const { destination, draggableId } = dropResult;

const destinationSchemaName = destination?.droppableId;
if (!destinationSchemaName) {
// Invalid Transition
return;
}
const destinationAggFields = activeSchemaFields[destinationSchemaName];

const sourceAggId = draggableId;
const destinationAggId = destinationAggFields[destination?.index].id;

const destinationSchema = schemas.all.find((schema) => schema.name === destination?.droppableId);

if (!destinationSchema) {
// Invalid Transition
return;
}

dispatch(
reorderAgg({
sourceId: sourceAggId,
destinationId: destinationAggId,
})
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { updateAggConfigParams } from '../../../utils/state_management/visualization_slice';
import { FIELD_SELECTOR_ID } from '../constants';
import { createNewAggConfig } from '../utils/get_valid_aggregations';
import { DragDropProperties } from './add_field_to_configuration';

export function replaceFieldInConfiguration({
dropResult,
schemas,
aggProps,
aggService,
dispatch,
}: DragDropProperties) {
const { source, combine, draggableId } = dropResult;

const destinationSchemaName = combine?.droppableId;
if (!destinationSchemaName) {
return;
}

const sourceAggId = draggableId;
const destinationAggId = combine?.draggableId;

const destinationSchema = schemas.all.find((schema) => schema.name === combine?.droppableId);

if (!destinationSchema) {
// Invalid Transition
return;
}

const sourceSchema = source.droppableId;

if (Object.values(FIELD_SELECTOR_ID).includes(sourceSchema as FIELD_SELECTOR_ID)) {
// Replacing an exisitng configuration with a new field from field selector panel

const newFieldToAdd = draggableId;
createNewAggConfig({
fieldName: newFieldToAdd,
sourceGroup: source.droppableId,
destinationSchema,
aggProps,
aggService,
sourceAgg: null,
});

// Removing the exisiting destination Aggregation
const updatedAggConfig = aggProps.aggConfigs?.aggs.filter((agg) => agg.id !== destinationAggId);

if (updatedAggConfig) {
dispatch(updateAggConfigParams(updatedAggConfig.map((agg) => agg.serialize())));
}
} else {
// Replacing an existing configuration with another exisiting configuration

const sourceAgg = aggProps.aggConfigs?.aggs.find((agg) => agg.id === sourceAggId);
const sourceFieldName = sourceAgg?.fieldName();

createNewAggConfig({
fieldName: sourceFieldName,
sourceGroup: source.droppableId,
destinationSchema,
aggProps,
aggService,
sourceAgg,
});

// Removing the exisiting destination and source Aggregation
const updatedAggConfig = aggProps.aggConfigs?.aggs.filter(
(agg) => agg.id !== destinationAggId && agg.id !== sourceAggId
);

if (updatedAggConfig) {
dispatch(updateAggConfigParams(updatedAggConfig.map((agg) => agg.serialize())));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
border-bottom: none;
}

&__droppable {
min-height: 1px;
}

&__container {
display: grid;
grid-gap: calc($euiSizeXS / 2);
Expand Down
Loading
Loading