[Ingest Pipelines] Processor Editor Move Tooltip (#72239)
* first implementation of tooltip * Add processor tooltip component files * remove init position from code for now * colocate on change handler and make code a bit cleaner * removed document.body.appendChild logic because EuiPortal does that for us * use correct toggle button api * added test to check button disabled while editing * remove cursor not allowed * simplify logic * assert if against positive * remove unused variable * Remove unused actions const Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
11182c8ef7
commit
e5c7e9a474
13 changed files with 151 additions and 21 deletions
|
@ -178,5 +178,13 @@ describe('Pipeline Editor', () => {
|
|||
expect(data2.processors).toEqual(testProcessors.processors);
|
||||
expect(data2.on_failure).toEqual([{ test: { if: '1 == 5' } }]);
|
||||
});
|
||||
|
||||
it('prevents moving a processor while in edit mode', () => {
|
||||
const { find, exists } = testBed;
|
||||
find('processors>0.editItemButton').simulate('click');
|
||||
expect(exists('processorSettingsForm')).toBe(true);
|
||||
expect(find('processors>0.moveItemButton').props().disabled).toBe(true);
|
||||
expect(find('processors>1.moveItemButton').props().disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
$dropZoneZIndex: $euiZLevel1; /* Prevent the next item down from obscuring the button */
|
||||
$cancelButtonZIndex: $euiZLevel2;
|
||||
$processorItemMouseTooltipZIndex: $euiZLevel3;
|
||||
|
|
|
@ -19,3 +19,5 @@ export { PipelineProcessorsEditorItem } from './pipeline_processors_editor_item'
|
|||
export { ProcessorRemoveModal } from './processor_remove_modal';
|
||||
|
||||
export { OnDoneLoadJsonHandler, LoadFromJsonButton } from './load_from_json';
|
||||
|
||||
export { PipelineProcessorsItemTooltip, Position } from './pipeline_processors_editor_item_tooltip';
|
||||
|
|
|
@ -51,7 +51,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
|||
editor,
|
||||
processorsDispatch,
|
||||
}) {
|
||||
const isDisabled = editor.mode.id !== 'idle';
|
||||
const isEditorNotInIdleMode = editor.mode.id !== 'idle';
|
||||
const isInMoveMode = Boolean(movingProcessor);
|
||||
const isMovingThisProcessor = processor.id === movingProcessor?.id;
|
||||
const isEditingThisProcessor =
|
||||
|
@ -83,6 +83,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
|||
'pipelineProcessorsEditor__item__moveButton--cancel': isMovingThisProcessor,
|
||||
});
|
||||
const icon = isMovingThisProcessor ? 'cross' : 'sortable';
|
||||
const disabled = isEditorNotInIdleMode && !isMovingThisProcessor;
|
||||
const moveButton = (
|
||||
<EuiButtonToggle
|
||||
isEmpty={!isMovingThisProcessor}
|
||||
|
@ -91,10 +92,16 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
|||
iconType={icon}
|
||||
data-test-subj={dataTestSubj}
|
||||
size="s"
|
||||
disabled={isDisabled && !isMovingThisProcessor}
|
||||
isDisabled={disabled}
|
||||
label={label}
|
||||
aria-label={label}
|
||||
onChange={() => (!isMovingThisProcessor ? onMove() : onCancelMove())}
|
||||
onChange={() => {
|
||||
if (isMovingThisProcessor) {
|
||||
onCancelMove();
|
||||
} else {
|
||||
onMove();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
// Remove the tooltip from the DOM to prevent it from lingering if the mouse leave event
|
||||
|
@ -132,7 +139,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem className={inlineTextInputContainerClasses} grow={false}>
|
||||
<InlineTextInput
|
||||
disabled={isDisabled}
|
||||
disabled={isEditorNotInIdleMode}
|
||||
onChange={(nextDescription) => {
|
||||
let nextOptions: Record<string, any>;
|
||||
if (!nextDescription) {
|
||||
|
@ -164,7 +171,8 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
|||
{!isInMoveMode && (
|
||||
<EuiToolTip content={i18nTexts.editButtonLabel}>
|
||||
<EuiButtonIcon
|
||||
disabled={isDisabled}
|
||||
data-test-subj="editItemButton"
|
||||
disabled={isEditorNotInIdleMode}
|
||||
aria-label={i18nTexts.editButtonLabel}
|
||||
iconType="pencil"
|
||||
size="s"
|
||||
|
@ -183,7 +191,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
|||
<EuiFlexItem grow={false}>
|
||||
<ContextMenu
|
||||
data-test-subj="moreMenu"
|
||||
disabled={isDisabled}
|
||||
disabled={isEditorNotInIdleMode}
|
||||
hidden={isInMoveMode}
|
||||
showAddOnFailure={!processor.onFailure?.length}
|
||||
onAddOnFailure={() => {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { PipelineProcessorsItemTooltip, Position } from './pipeline_processors_editor_item_tooltip';
|
|
@ -0,0 +1,7 @@
|
|||
@import '../shared';
|
||||
|
||||
.pipelineProcessorsEditor__itemTooltip {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
z-index: $processorItemMouseTooltipZIndex;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { EuiPortal } from '@elastic/eui';
|
||||
import { ProcessorInternal } from '../../types';
|
||||
|
||||
import './pipeline_processors_editor_item_toolip.scss';
|
||||
import { ProcessorInformation } from './processor_information';
|
||||
|
||||
export interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
processor: ProcessorInternal;
|
||||
}
|
||||
|
||||
const MOUSE_PADDING_RIGHT = 20;
|
||||
const MOUSE_PADDING_BOTTOM = 20;
|
||||
|
||||
export const PipelineProcessorsItemTooltip: FunctionComponent<Props> = ({ processor }) => {
|
||||
const [position, setPosition] = useState<Position | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
const mouseMoveListener = (event: MouseEvent) => {
|
||||
setPosition({ x: event.pageX, y: event.pageY - window.scrollY });
|
||||
};
|
||||
document.addEventListener('mousemove', mouseMoveListener);
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', mouseMoveListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
/**
|
||||
* To get around issues with parent elements potentially being position: relative or
|
||||
* overflow: hidden we use a portal to render this tooltip in the document body so
|
||||
* that we can render it anywhere the cursor can go.
|
||||
*/
|
||||
<EuiPortal>
|
||||
<div
|
||||
className="pipelineProcessorsEditor__itemTooltip"
|
||||
style={{
|
||||
left: position.x + MOUSE_PADDING_RIGHT,
|
||||
top: position.y + MOUSE_PADDING_BOTTOM,
|
||||
}}
|
||||
>
|
||||
<ProcessorInformation processor={processor} />
|
||||
</div>
|
||||
</EuiPortal>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FunctionComponent, memo } from 'react';
|
||||
import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
|
||||
import { ProcessorInternal } from '../../types';
|
||||
|
||||
interface Props {
|
||||
processor: ProcessorInternal;
|
||||
}
|
||||
|
||||
export const ProcessorInformation: FunctionComponent<Props> = memo(({ processor }) => {
|
||||
return (
|
||||
<EuiPanel>
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center" responsive={false} gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<b>{processor.type}</b>
|
||||
</EuiFlexItem>
|
||||
{processor.options.description ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued" size="m">
|
||||
{processor.options.description}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
) : undefined}
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
});
|
|
@ -37,7 +37,6 @@ export const DropZoneButton: FunctionComponent<Props> = (props) => {
|
|||
});
|
||||
const buttonClasses = classNames({
|
||||
'pipelineProcessorsEditor__tree__dropZoneButton--visible': isVisible,
|
||||
'pipelineProcessorsEditor__tree__dropZoneButton--unavailable': isUnavailable,
|
||||
});
|
||||
|
||||
const content = (
|
||||
|
|
|
@ -50,10 +50,6 @@
|
|||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&--unavailable {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__onFailureHandlerContainer {
|
||||
|
|
|
@ -4,12 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { ON_FAILURE_STATE_SCOPE, PROCESSOR_STATE_SCOPE } from './processors_reducer';
|
||||
|
||||
export enum DropSpecialLocations {
|
||||
top = 'TOP',
|
||||
bottom = 'BOTTOM',
|
||||
}
|
||||
|
||||
export const PROCESSORS_BASE_SELECTOR = [PROCESSOR_STATE_SCOPE];
|
||||
export const ON_FAILURE_BASE_SELECTOR = [ON_FAILURE_STATE_SCOPE];
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
ContextValue,
|
||||
ContextValueState,
|
||||
Links,
|
||||
ProcessorInternal,
|
||||
} from './types';
|
||||
|
||||
import { useProcessorsState, isOnFailureSelector } from './processors_reducer';
|
||||
|
@ -33,11 +34,14 @@ import { deserialize } from './deserialize';
|
|||
|
||||
import { serialize } from './serialize';
|
||||
|
||||
import { OnSubmitHandler, ProcessorSettingsForm } from './components/processor_settings_form';
|
||||
|
||||
import { OnActionHandler } from './components/processors_tree';
|
||||
|
||||
import { ProcessorRemoveModal } from './components';
|
||||
import {
|
||||
ProcessorRemoveModal,
|
||||
PipelineProcessorsItemTooltip,
|
||||
ProcessorSettingsForm,
|
||||
OnSubmitHandler,
|
||||
} from './components';
|
||||
|
||||
import { getValue } from './utils';
|
||||
|
||||
|
@ -64,6 +68,7 @@ export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
|
|||
children,
|
||||
}) => {
|
||||
const initRef = useRef(false);
|
||||
|
||||
const [mode, setMode] = useState<EditorMode>(() => ({
|
||||
id: 'idle',
|
||||
}));
|
||||
|
@ -206,6 +211,15 @@ export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
|
|||
>
|
||||
{children}
|
||||
|
||||
{mode.id === 'movingProcessor' && (
|
||||
<PipelineProcessorsItemTooltip
|
||||
processor={getValue<ProcessorInternal>(mode.arg.selector, {
|
||||
processors,
|
||||
onFailure: onFailureProcessors,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
{mode.id === 'editingProcessor' || mode.id === 'creatingProcessor' ? (
|
||||
<ProcessorSettingsForm
|
||||
isOnFailure={isOnFailureSelector(mode.arg.selector)}
|
||||
|
@ -219,7 +233,7 @@ export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
|
|||
{mode.id === 'removingProcessor' && (
|
||||
<ProcessorRemoveModal
|
||||
selector={mode.arg.selector}
|
||||
processor={getValue(mode.arg.selector, {
|
||||
processor={getValue<ProcessorInternal>(mode.arg.selector, {
|
||||
processors,
|
||||
onFailure: onFailureProcessors,
|
||||
})}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { Dispatch } from 'react';
|
||||
import { OnFormUpdateArg } from '../../../shared_imports';
|
||||
import { SerializeResult } from './serialize';
|
||||
import { OnActionHandler, ProcessorInfo } from './components/processors_tree';
|
||||
import { OnActionHandler, ProcessorInfo } from './components';
|
||||
import { ProcessorsDispatch, State as ProcessorsReducerState } from './processors_reducer';
|
||||
|
||||
export interface Links {
|
||||
|
|
Loading…
Reference in a new issue