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

Add Drag & Drop Across Axis Functionality to Vis Builder #7107

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
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;
schemaDisplayStates: SchemaDisplayStates;
setSchemaDisplayStates: React.Dispatch<React.SetStateAction<SchemaDisplayStates>>;
}

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

const mainPanel = mapSchemaToAggPanel(schemas);
const mainPanel = mapSchemaToAggPanel(

Check warning on line 45 in src/plugins/vis_builder/public/application/components/data_tab/config_panel.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/vis_builder/public/application/components/data_tab/config_panel.tsx#L45

Added line #L45 was not covered by tests
schemas,
aggProps,
schemaDisplayStates,
setSchemaDisplayStates
);

return (
<EuiForm className={`vbConfig ${editingState ? 'showSecondary' : ''}`}>
Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
import { i18n } from '@osd/i18n';
import {
EuiButtonIcon,
EuiDragDropContext,
EuiDraggable,
EuiDroppable,
EuiFormRow,
EuiPanel,
EuiText,
DropResult,
} from '@elastic/eui';
import React, { useCallback, useState } from 'react';
import { IDropAttributes, IDropState } from '../../utils/drag_drop';
Expand Down Expand Up @@ -51,25 +49,13 @@
onAddField,
onDeleteField,
onEditField,
onReorderField,
limit = 1,
isValidDropTarget,
canDrop,
dropProps,
}: DropboxProps) => {
const prefersReducedMotion = usePrefersReducedMotion();
const [closing, setClosing] = useState<boolean | string>(false);
const handleDragEnd = useCallback(
({ source, destination }: DropResult) => {
if (!destination) return;

onReorderField({
sourceAggId: fields[source.index].id,
destinationAggId: fields[destination.index].id,
});
},
[fields, onReorderField]
);

const animateDelete = useCallback(
(id: string) => {
Expand All @@ -86,71 +72,72 @@
);

return (
<EuiDragDropContext onDragEnd={handleDragEnd}>
<EuiFormRow label={boxLabel} className="dropBox" fullWidth>
<div className="dropBox__container">
<EuiDroppable droppableId={dropboxId}>
{fields.map(({ id, label }, index) => (
<EuiDraggable
className={`dropBox__draggable ${id === closing && 'closing'}`}
key={id}
draggableId={id}
index={index}
>
<EuiPanel
key={index}
paddingSize="s"
className="dropBox__field"
data-test-subj={`dropBoxField-${dropboxId}-${index}`}
>
<EuiText size="s" className="dropBox__field_text" onClick={() => onEditField(id)}>
<a role="button" tabIndex={0}>
{label}
</a>
</EuiText>
<EuiButtonIcon
color="subdued"
iconType="cross"
aria-label="clear-field"
iconSize="s"
onClick={() => animateDelete(id)}
data-test-subj="dropBoxRemoveBtn"
/>
</EuiPanel>
</EuiDraggable>
))}
</EuiDroppable>
{fields.length < limit && (
<EuiPanel
data-test-subj={`dropBoxAddField-${dropboxId}`}
className={`dropBox__field dropBox__dropTarget ${
isValidDropTarget ? 'validField' : ''
} ${canDrop ? 'canDrop' : ''}`}
{...(isValidDropTarget && dropProps)}
<EuiFormRow label={boxLabel} className="dropBox" fullWidth>
<div className="dropBox__container">
<EuiDroppable
className="dropBox__droppable"
droppableId={dropboxId}
isCombineEnabled={true}
>
{fields.map(({ id, label }, index) => (
<EuiDraggable

Check warning on line 83 in src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx#L83

Added line #L83 was not covered by tests
className={`dropBox__draggable ${id === closing && 'closing'}`}
key={id}
draggableId={id}
index={index}
>
<EuiText size="s">
{i18n.translate('visBuilder.dropbox.addField.title', {
defaultMessage: 'Click or drop to add',
})}
</EuiText>
<EuiButtonIcon
iconType="plusInCircle"
aria-label="clear-field"
iconSize="s"
onClick={() => onAddField()}
data-test-subj="dropBoxAddBtn"
/>
</EuiPanel>
)}
</div>
</EuiFormRow>
</EuiDragDropContext>
<EuiPanel
key={index}
paddingSize="s"
className="dropBox__field"
data-test-subj={`dropBoxField-${dropboxId}-${index}`}
>
<EuiText size="s" className="dropBox__field_text" onClick={() => onEditField(id)}>

Check warning on line 95 in src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx#L95

Added line #L95 was not covered by tests
<a role="button" tabIndex={0}>
{label}
</a>
</EuiText>
<EuiButtonIcon
color="subdued"
iconType="cross"
aria-label="clear-field"
iconSize="s"
onClick={() => animateDelete(id)}

Check warning on line 105 in src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx#L105

Added line #L105 was not covered by tests
data-test-subj="dropBoxRemoveBtn"
/>
</EuiPanel>
</EuiDraggable>
))}
</EuiDroppable>
{fields.length < limit && (
<EuiPanel
data-test-subj={`dropBoxAddField-${dropboxId}`}
className={`dropBox__field dropBox__dropTarget ${
isValidDropTarget ? 'validField' : ''
} ${canDrop ? 'canDrop' : ''}`}
{...(isValidDropTarget && dropProps)}
>
<EuiText size="s">
{i18n.translate('visBuilder.dropbox.addField.title', {
defaultMessage: 'Click or drop to add',
})}
</EuiText>
<EuiButtonIcon
iconType="plusInCircle"
aria-label="clear-field"
iconSize="s"
onClick={() => onAddField()}

Check warning on line 129 in src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/vis_builder/public/application/components/data_tab/dropbox.tsx#L129

Added line #L129 was not covered by tests
data-test-subj="dropBoxAddBtn"
/>
</EuiPanel>
)}
</div>
</EuiFormRow>
);
};

const Dropbox = React.memo((dropBox: UseDropboxProps) => {
const props = useDropbox(dropBox);

return <DropboxComponent {...props} />;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { render, screen } from '@testing-library/react';
import { IndexPatternField } from '../../../../../data/public';

import { DraggableFieldButton } from './field';
import { DropResult, EuiDragDropContext, EuiDroppable } from '@elastic/eui';

describe('visBuilder field', function () {
describe('DraggableFieldButton', () => {
Expand All @@ -28,7 +29,13 @@ describe('visBuilder field', function () {
'bytes'
),
};
render(<DraggableFieldButton {...props} />);
render(
<EuiDragDropContext onDragEnd={(result: DropResult) => {}}>
<EuiDroppable droppableId="1">
<DraggableFieldButton index={1} {...props} />
</EuiDroppable>
</EuiDragDropContext>
);

const button = screen.getByTestId('field-bytes-showDetails');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
*/

import React, { useState } from 'react';
import { EuiPopover } from '@elastic/eui';
import { EuiDraggable, EuiPopover } from '@elastic/eui';

import { IndexPatternField } from '../../../../../data/public';
import {
Expand All @@ -46,10 +46,11 @@ import './field.scss';
export interface FieldProps {
field: IndexPatternField;
getDetails: (field) => FieldDetails;
id: number;
}

// TODO: Add field sections (Available fields, popular fields from src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx)
export const Field = ({ field, getDetails }: FieldProps) => {
export const Field = ({ field, getDetails, id }: FieldProps) => {
const [infoIsOpen, setOpen] = useState(false);

function togglePopover() {
Expand All @@ -60,7 +61,14 @@ export const Field = ({ field, getDetails }: FieldProps) => {
<EuiPopover
ownFocus
display="block"
button={<DraggableFieldButton isActive={infoIsOpen} onClick={togglePopover} field={field} />}
button={
<DraggableFieldButton
isActive={infoIsOpen}
onClick={togglePopover}
field={field}
index={id}
/>
}
isOpen={infoIsOpen}
closePopover={() => setOpen(false)}
anchorPosition="rightUp"
Expand All @@ -77,9 +85,15 @@ export const Field = ({ field, getDetails }: FieldProps) => {
export interface DraggableFieldButtonProps extends Partial<FieldButtonProps> {
dragValue?: IndexPatternField['name'] | null | typeof COUNT_FIELD;
field: Partial<IndexPatternField> & Pick<IndexPatternField, 'displayName' | 'name' | 'type'>;
index: number;
}

export const DraggableFieldButton = ({ dragValue, field, ...rest }: DraggableFieldButtonProps) => {
export const DraggableFieldButton = ({
dragValue,
field,
index,
...rest
}: DraggableFieldButtonProps) => {
const { name, displayName, type, scripted = false } = field;
const [dragProps] = useDrag({
namespace: 'field-data',
Expand Down Expand Up @@ -109,5 +123,14 @@ export const DraggableFieldButton = ({ dragValue, field, ...rest }: DraggableFie
onClick: () => {},
};

return <FieldButton {...defaultProps} {...rest} {...dragProps} />;
return (
<EuiDraggable
draggableId={name}
index={index}
disableInteractiveElementBlocking={true}
shouldRespectForcePress
>
<FieldButton {...defaultProps} {...rest} {...dragProps} />
</EuiDraggable>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
padding: $euiSizeS;

&__fieldGroups {
@include euiYScrollWithShadows;

overflow-y: auto;
margin-right: -$euiSizeS;
padding-right: $euiSizeS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { FilterManager, IndexPatternField } from '../../../../../data/public';
import { FieldGroup } from './field_selector';
import { DropResult, EuiDragDropContext, EuiDroppable } from '@elastic/eui';

const mockUseIndexPatterns = jest.fn(() => ({ selected: 'mockIndexPattern' }));
const mockUseOnAddFilter = jest.fn();
Expand Down Expand Up @@ -68,7 +69,13 @@ describe('visBuilder sidebar field selector', function () {
...defaultProps,
fields: ['bytes', 'machine.ram', 'memory', 'phpmemory'].map(getFields),
};
const { container } = render(<FieldGroup {...props} />);
const { container } = render(
<EuiDragDropContext onDragEnd={(result: DropResult) => {}}>
<EuiDroppable droppableId="1">
<FieldGroup {...props} />
</EuiDroppable>
</EuiDragDropContext>
);

expect(container).toHaveTextContent(props.header);
expect(container).toHaveTextContent(props.fields.length.toString());
Expand Down
Loading
Loading