mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Ingest Pipelines Editor] First round of UX improvements (#69381)
* First round of UX tweaks - Fixed potential text overflow issue on descriptions - Removed border around text input when editing description * Updated the on-failure pipeline description copy * Properly encode URI component pipeline names * use xjson editor in flyout * also hide the test flyout if we are editing a component * add much stronger dimming effect when in edit mode * also added dimming effect to moving state * remove box shadow if dimmed * add tooltips to dropzones * fix CITs after master merge * fix nested rendering of processors tree * only show the tooltip when the dropzone is unavaiable and visible * keep white background on dim * hide controls when moving * fix on blur bug * Rename variables and prefix booleans with "is" * Remove box shadow on all nested tree items * use classNames as it is intended to be used * Refactor SCSS values to variables * Added cancel move button - also hide the description in move mode when it is empty - update and refactor some shared sass variables - some number of sass changes to make labels play nice in move mode - changed the logic to not render the buttons when in move mode instead of display: none on them. The issue is with the tooltip not hiding when when we change to move mode and the mouse event "leave" does get through the tooltip element causing tooltips to hang even though the mouse has left them. * Fixes for monaco XJSON grammar parser and update form copy - Monaco XJSON worker was not handling trailing whitespace - Update copy in the processor configuration form Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
9ebf41c77c
commit
c8089a5aa2
25 changed files with 513 additions and 318 deletions
|
@ -200,12 +200,13 @@ export const createParser = () => {
|
|||
|
||||
try {
|
||||
value();
|
||||
white();
|
||||
} catch (e) {
|
||||
errored = true;
|
||||
annos.push({ type: AnnoTypes.error, at: e.at - 1, text: e.message });
|
||||
}
|
||||
if (!errored && ch) {
|
||||
error('Syntax error');
|
||||
annos.push({ type: AnnoTypes.error, at: at, text: 'Syntax Error' });
|
||||
}
|
||||
return { annotations: annos };
|
||||
}
|
||||
|
|
|
@ -52,7 +52,10 @@ export const registerGrammarChecker = (editor: monaco.editor.IEditor) => {
|
|||
|
||||
const updateAnnos = async () => {
|
||||
const { annotations } = await wps.getAnnos();
|
||||
const model = editor.getModel() as monaco.editor.ITextModel;
|
||||
const model = editor.getModel() as monaco.editor.ITextModel | null;
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
monaco.editor.setModelMarkers(
|
||||
model,
|
||||
OWNER,
|
||||
|
|
|
@ -81,6 +81,7 @@ export const PipelineForm: React.FunctionComponent<PipelineFormProps> = ({
|
|||
});
|
||||
|
||||
const onEditorFlyoutOpen = useCallback(() => {
|
||||
setIsTestingPipeline(false);
|
||||
setIsRequestVisible(false);
|
||||
}, [setIsRequestVisible]);
|
||||
|
||||
|
|
|
@ -24,8 +24,15 @@ jest.mock('@elastic/eui', () => {
|
|||
}}
|
||||
/>
|
||||
),
|
||||
// Mocking EuiCodeEditor, which uses React Ace under the hood
|
||||
EuiCodeEditor: (props: any) => (
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../../../../src/plugins/kibana_react/public', () => {
|
||||
const original = jest.requireActual('../../../../../../../../src/plugins/kibana_react/public');
|
||||
return {
|
||||
...original,
|
||||
// Mocking CodeEditor, which uses React Monaco under the hood
|
||||
CodeEditor: (props: any) => (
|
||||
<input
|
||||
data-test-subj={props['data-test-subj'] || 'mockCodeEditor'}
|
||||
data-currentvalue={props.value}
|
||||
|
@ -95,8 +102,9 @@ const createActions = (testBed: TestBed<TestSubject>) => {
|
|||
act(() => {
|
||||
find(`${processorSelector}.moveItemButton`).simulate('click');
|
||||
});
|
||||
component.update();
|
||||
act(() => {
|
||||
find(dropZoneSelector).last().simulate('click');
|
||||
find(dropZoneSelector).simulate('click');
|
||||
});
|
||||
component.update();
|
||||
},
|
||||
|
@ -122,13 +130,6 @@ const createActions = (testBed: TestBed<TestSubject>) => {
|
|||
});
|
||||
},
|
||||
|
||||
duplicateProcessor(processorSelector: string) {
|
||||
find(`${processorSelector}.moreMenu.button`).simulate('click');
|
||||
act(() => {
|
||||
find(`${processorSelector}.moreMenu.duplicateButton`).simulate('click');
|
||||
});
|
||||
},
|
||||
|
||||
startAndCancelMove(processorSelector: string) {
|
||||
act(() => {
|
||||
find(`${processorSelector}.moveItemButton`).simulate('click');
|
||||
|
@ -139,6 +140,13 @@ const createActions = (testBed: TestBed<TestSubject>) => {
|
|||
});
|
||||
},
|
||||
|
||||
duplicateProcessor(processorSelector: string) {
|
||||
find(`${processorSelector}.moreMenu.button`).simulate('click');
|
||||
act(() => {
|
||||
find(`${processorSelector}.moreMenu.duplicateButton`).simulate('click');
|
||||
});
|
||||
},
|
||||
|
||||
toggleOnFailure() {
|
||||
find('pipelineEditorOnFailureToggle').simulate('click');
|
||||
},
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
$dropZoneZIndex: 1; /* Prevent the next item down from obscuring the button */
|
||||
$cancelButtonZIndex: 2;
|
|
@ -31,7 +31,7 @@ export const OnFailureProcessorsTitle: FunctionComponent = () => {
|
|||
<EuiText size="s" color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.ingestPipelines.pipelineEditor.onFailureTreeDescription"
|
||||
defaultMessage="The processors used to pre-process documents before indexing. {learnMoreLink}"
|
||||
defaultMessage="The processors used to handle exceptions in this pipeline. {learnMoreLink}"
|
||||
values={{
|
||||
learnMoreLink: (
|
||||
<EuiLink
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { FunctionComponent, useState } from 'react';
|
||||
|
||||
import { EuiContextMenuItem, EuiContextMenuPanel, EuiPopover, EuiButtonIcon } from '@elastic/eui';
|
||||
|
@ -12,6 +13,7 @@ import { editorItemMessages } from './messages';
|
|||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
hidden: boolean;
|
||||
showAddOnFailure: boolean;
|
||||
onDuplicate: () => void;
|
||||
onDelete: () => void;
|
||||
|
@ -20,9 +22,13 @@ interface Props {
|
|||
}
|
||||
|
||||
export const ContextMenu: FunctionComponent<Props> = (props) => {
|
||||
const { showAddOnFailure, onDuplicate, onAddOnFailure, onDelete, disabled } = props;
|
||||
const { showAddOnFailure, onDuplicate, onAddOnFailure, onDelete, disabled, hidden } = props;
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
|
||||
const containerClasses = classNames({
|
||||
'pipelineProcessorsEditor__item--displayNone': hidden,
|
||||
});
|
||||
|
||||
const contextMenuItems = [
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="duplicateButton"
|
||||
|
@ -63,23 +69,25 @@ export const ContextMenu: FunctionComponent<Props> = (props) => {
|
|||
].filter(Boolean) as JSX.Element[];
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
data-test-subj={props['data-test-subj']}
|
||||
anchorPosition="leftCenter"
|
||||
panelPaddingSize="none"
|
||||
isOpen={isOpen}
|
||||
closePopover={() => setIsOpen(false)}
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
data-test-subj="button"
|
||||
disabled={disabled}
|
||||
onClick={() => setIsOpen((v) => !v)}
|
||||
iconType="boxesHorizontal"
|
||||
aria-label={editorItemMessages.moreButtonAriaLabel}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiContextMenuPanel items={contextMenuItems} />
|
||||
</EuiPopover>
|
||||
<div className={containerClasses}>
|
||||
<EuiPopover
|
||||
data-test-subj={props['data-test-subj']}
|
||||
anchorPosition="leftCenter"
|
||||
panelPaddingSize="none"
|
||||
isOpen={isOpen}
|
||||
closePopover={() => setIsOpen(false)}
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
data-test-subj="button"
|
||||
disabled={disabled}
|
||||
onClick={() => setIsOpen((v) => !v)}
|
||||
iconType="boxesHorizontal"
|
||||
aria-label={editorItemMessages.moreButtonAriaLabel}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiContextMenuPanel items={contextMenuItems} />
|
||||
</EuiPopover>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { FunctionComponent, useState, useEffect, useCallback } from 'react';
|
||||
import { EuiFieldText, EuiText, keyCodes } from '@elastic/eui';
|
||||
|
||||
|
@ -11,10 +11,12 @@ export interface Props {
|
|||
placeholder: string;
|
||||
ariaLabel: string;
|
||||
onChange: (value: string) => void;
|
||||
disabled: boolean;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
export const InlineTextInput: FunctionComponent<Props> = ({
|
||||
disabled,
|
||||
placeholder,
|
||||
text,
|
||||
ariaLabel,
|
||||
|
@ -23,26 +25,17 @@ export const InlineTextInput: FunctionComponent<Props> = ({
|
|||
const [isShowingTextInput, setIsShowingTextInput] = useState<boolean>(false);
|
||||
const [textValue, setTextValue] = useState<string>(text ?? '');
|
||||
|
||||
const content = isShowingTextInput ? (
|
||||
<EuiFieldText
|
||||
controlOnly
|
||||
fullWidth
|
||||
compressed
|
||||
value={textValue}
|
||||
aria-label={ariaLabel}
|
||||
className="pipelineProcessorsEditor__item__textInput"
|
||||
inputRef={(el) => el?.focus()}
|
||||
onChange={(event) => setTextValue(event.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<EuiText size="s" color="subdued">
|
||||
{text || <em>{placeholder}</em>}
|
||||
</EuiText>
|
||||
);
|
||||
const containerClasses = classNames('pipelineProcessorsEditor__item__textContainer', {
|
||||
'pipelineProcessorsEditor__item__textContainer--notEditing': !isShowingTextInput && !disabled,
|
||||
});
|
||||
|
||||
const submitChange = useCallback(() => {
|
||||
setIsShowingTextInput(false);
|
||||
onChange(textValue);
|
||||
// Give any on blur handlers the chance to complete if the user is
|
||||
// tabbing over this component.
|
||||
setTimeout(() => {
|
||||
setIsShowingTextInput(false);
|
||||
onChange(textValue);
|
||||
});
|
||||
}, [setIsShowingTextInput, onChange, textValue]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -62,14 +55,27 @@ export const InlineTextInput: FunctionComponent<Props> = ({
|
|||
};
|
||||
}, [isShowingTextInput, submitChange, setIsShowingTextInput]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="pipelineProcessorsEditor__item__textContainer"
|
||||
tabIndex={0}
|
||||
onFocus={() => setIsShowingTextInput(true)}
|
||||
onBlur={submitChange}
|
||||
>
|
||||
{content}
|
||||
return isShowingTextInput && !disabled ? (
|
||||
<div className={`pipelineProcessorsEditor__item__textContainer ${containerClasses}`}>
|
||||
<EuiFieldText
|
||||
controlOnly
|
||||
onBlur={submitChange}
|
||||
fullWidth
|
||||
compressed
|
||||
value={textValue}
|
||||
aria-label={ariaLabel}
|
||||
className="pipelineProcessorsEditor__item__textInput"
|
||||
inputRef={(el) => el?.focus()}
|
||||
onChange={(event) => setTextValue(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={containerClasses} tabIndex={0} onFocus={() => setIsShowingTextInput(true)}>
|
||||
<EuiText size="s" color="subdued">
|
||||
<div className="pipelineProcessorsEditor__item__description">
|
||||
{text || <em>{placeholder}</em>}
|
||||
</div>
|
||||
</EuiText>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -10,12 +10,9 @@ export const editorItemMessages = {
|
|||
moveButtonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.item.moveButtonLabel', {
|
||||
defaultMessage: 'Move this processor',
|
||||
}),
|
||||
editorButtonLabel: i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.item.editButtonAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Edit this processor',
|
||||
}
|
||||
),
|
||||
editButtonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.item.editButtonAriaLabel', {
|
||||
defaultMessage: 'Edit this processor',
|
||||
}),
|
||||
duplicateButtonLabel: i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.item.moreMenu.duplicateButtonLabel',
|
||||
{
|
||||
|
@ -31,7 +28,7 @@ export const editorItemMessages = {
|
|||
cancelMoveButtonLabel: i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.item.cancelMoveButtonAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Cancel moving this processor',
|
||||
defaultMessage: 'Cancel move',
|
||||
}
|
||||
),
|
||||
deleteButtonLabel: i18n.translate(
|
||||
|
|
|
@ -1,17 +1,57 @@
|
|||
@import '../shared';
|
||||
|
||||
.pipelineProcessorsEditor__item {
|
||||
transition: border-color 1s;
|
||||
min-height: 50px;
|
||||
&--selected {
|
||||
border: 1px solid $euiColorPrimary;
|
||||
}
|
||||
|
||||
&--displayNone {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--dimmed {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
// Remove the box-shadow on all nested items
|
||||
.pipelineProcessorsEditor__item {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&__processorTypeLabel {
|
||||
line-height: $euiButtonHeightSmall;
|
||||
}
|
||||
|
||||
&__textContainer {
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
|
||||
transition: border-color .3s;
|
||||
border: 2px solid #FFF;
|
||||
transition: border-color 0.3s;
|
||||
border: 2px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border: 2px solid $euiColorLightShade;
|
||||
&--notEditing {
|
||||
&:hover {
|
||||
border: 2px solid $euiColorLightShade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
&__textInput {
|
||||
height: 21px;
|
||||
min-width: 100px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
&__cancelMoveButton {
|
||||
// Ensure that the cancel button is above the drop zones
|
||||
z-index: $cancelButtonZIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { FunctionComponent, memo } from 'react';
|
||||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui';
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { ProcessorInternal, ProcessorSelector } from '../../types';
|
||||
import { selectorToDataTestSubject } from '../../utils';
|
||||
|
@ -17,6 +26,7 @@ import './pipeline_processors_editor_item.scss';
|
|||
import { InlineTextInput } from './inline_text_input';
|
||||
import { ContextMenu } from './context_menu';
|
||||
import { editorItemMessages } from './messages';
|
||||
import { ProcessorInfo } from '../processors_tree';
|
||||
|
||||
export interface Handlers {
|
||||
onMove: () => void;
|
||||
|
@ -25,127 +35,166 @@ export interface Handlers {
|
|||
|
||||
export interface Props {
|
||||
processor: ProcessorInternal;
|
||||
selected: boolean;
|
||||
handlers: Handlers;
|
||||
selector: ProcessorSelector;
|
||||
description?: string;
|
||||
movingProcessor?: ProcessorInfo;
|
||||
renderOnFailureHandlers?: () => React.ReactNode;
|
||||
}
|
||||
|
||||
export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
|
||||
({ processor, description, handlers: { onCancelMove, onMove }, selector, selected }) => {
|
||||
({
|
||||
processor,
|
||||
description,
|
||||
handlers: { onCancelMove, onMove },
|
||||
selector,
|
||||
movingProcessor,
|
||||
renderOnFailureHandlers,
|
||||
}) => {
|
||||
const {
|
||||
state: { editor, processorsDispatch },
|
||||
} = usePipelineProcessorsContext();
|
||||
|
||||
const disabled = editor.mode.id !== 'idle';
|
||||
const isDarkBold =
|
||||
editor.mode.id !== 'editingProcessor' || processor.id === editor.mode.arg.processor.id;
|
||||
const isDisabled = editor.mode.id !== 'idle';
|
||||
const isInMoveMode = Boolean(movingProcessor);
|
||||
const isMovingThisProcessor = processor.id === movingProcessor?.id;
|
||||
const isEditingThisProcessor =
|
||||
editor.mode.id === 'editingProcessor' && processor.id === editor.mode.arg.processor.id;
|
||||
const isEditingOtherProcessor =
|
||||
editor.mode.id === 'editingProcessor' && !isEditingThisProcessor;
|
||||
const isMovingOtherProcessor = editor.mode.id === 'movingProcessor' && !isMovingThisProcessor;
|
||||
const isDimmed = isEditingOtherProcessor || isMovingOtherProcessor;
|
||||
|
||||
const panelClasses = classNames('pipelineProcessorsEditor__item', {
|
||||
'pipelineProcessorsEditor__item--selected': isMovingThisProcessor || isEditingThisProcessor,
|
||||
'pipelineProcessorsEditor__item--dimmed': isDimmed,
|
||||
});
|
||||
|
||||
const actionElementClasses = classNames({
|
||||
'pipelineProcessorsEditor__item--displayNone': isInMoveMode,
|
||||
});
|
||||
|
||||
const inlineTextInputContainerClasses = classNames({
|
||||
'pipelineProcessorsEditor__item--displayNone': isInMoveMode && !processor.options.description,
|
||||
});
|
||||
|
||||
const cancelMoveButtonClasses = classNames('pipelineProcessorsEditor__item__cancelMoveButton', {
|
||||
'pipelineProcessorsEditor__item--displayNone': !isMovingThisProcessor,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
alignItems="center"
|
||||
justifyContent="spaceBetween"
|
||||
data-test-subj={selectorToDataTestSubject(selector)}
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="m" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color={isDarkBold ? undefined : 'subdued'}>
|
||||
<b>{processor.type}</b>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<InlineTextInput
|
||||
onChange={(nextDescription) => {
|
||||
let nextOptions: Record<string, any>;
|
||||
if (!nextDescription) {
|
||||
const { description: __, ...restOptions } = processor.options;
|
||||
nextOptions = restOptions;
|
||||
} else {
|
||||
nextOptions = {
|
||||
...processor.options,
|
||||
description: nextDescription,
|
||||
};
|
||||
}
|
||||
processorsDispatch({
|
||||
type: 'updateProcessor',
|
||||
payload: {
|
||||
processor: {
|
||||
...processor,
|
||||
options: nextOptions,
|
||||
<EuiPanel className={panelClasses} paddingSize="s">
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
alignItems="center"
|
||||
justifyContent="spaceBetween"
|
||||
data-test-subj={selectorToDataTestSubject(selector)}
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="m" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText
|
||||
className="pipelineProcessorsEditor__item__processorTypeLabel"
|
||||
color={isDimmed ? 'subdued' : undefined}
|
||||
>
|
||||
<b>{processor.type}</b>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem className={inlineTextInputContainerClasses} grow={false}>
|
||||
<InlineTextInput
|
||||
disabled={isDisabled}
|
||||
onChange={(nextDescription) => {
|
||||
let nextOptions: Record<string, any>;
|
||||
if (!nextDescription) {
|
||||
const { description: __, ...restOptions } = processor.options;
|
||||
nextOptions = restOptions;
|
||||
} else {
|
||||
nextOptions = {
|
||||
...processor.options,
|
||||
description: nextDescription,
|
||||
};
|
||||
}
|
||||
processorsDispatch({
|
||||
type: 'updateProcessor',
|
||||
payload: {
|
||||
processor: {
|
||||
...processor,
|
||||
options: nextOptions,
|
||||
},
|
||||
selector,
|
||||
},
|
||||
selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
ariaLabel={editorItemMessages.processorTypeLabel({ type: processor.type })}
|
||||
text={description}
|
||||
placeholder={editorItemMessages.descriptionPlaceholder}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="editItemButton"
|
||||
disabled={disabled}
|
||||
aria-label={editorItemMessages.editorButtonLabel}
|
||||
iconType="pencil"
|
||||
size="s"
|
||||
onClick={() => {
|
||||
editor.setMode({
|
||||
id: 'editingProcessor',
|
||||
arg: { processor, selector },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{selected ? (
|
||||
<EuiButtonIcon
|
||||
data-test-subj="cancelMoveItemButton"
|
||||
aria-label={editorItemMessages.cancelMoveButtonLabel}
|
||||
size="s"
|
||||
onClick={onCancelMove}
|
||||
iconType="crossInACircleFilled"
|
||||
});
|
||||
}}
|
||||
ariaLabel={editorItemMessages.processorTypeLabel({ type: processor.type })}
|
||||
text={description}
|
||||
placeholder={editorItemMessages.descriptionPlaceholder}
|
||||
/>
|
||||
) : (
|
||||
<EuiToolTip content={editorItemMessages.moveButtonLabel}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="moveItemButton"
|
||||
disabled={disabled}
|
||||
aria-label={editorItemMessages.moveButtonLabel}
|
||||
size="s"
|
||||
onClick={onMove}
|
||||
iconType="sortable"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ContextMenu
|
||||
data-test-subj="moreMenu"
|
||||
disabled={disabled}
|
||||
showAddOnFailure={!processor.onFailure?.length}
|
||||
onAddOnFailure={() => {
|
||||
editor.setMode({ id: 'creatingProcessor', arg: { selector } });
|
||||
}}
|
||||
onDelete={() => {
|
||||
editor.setMode({ id: 'removingProcessor', arg: { selector } });
|
||||
}}
|
||||
onDuplicate={() => {
|
||||
processorsDispatch({
|
||||
type: 'duplicateProcessor',
|
||||
payload: {
|
||||
source: selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem className={actionElementClasses} grow={false}>
|
||||
{!isInMoveMode && (
|
||||
<EuiToolTip content={editorItemMessages.editButtonLabel}>
|
||||
<EuiButtonIcon
|
||||
disabled={isDisabled}
|
||||
aria-label={editorItemMessages.editButtonLabel}
|
||||
iconType="pencil"
|
||||
size="s"
|
||||
onClick={() => {
|
||||
editor.setMode({
|
||||
id: 'editingProcessor',
|
||||
arg: { processor, selector },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem className={actionElementClasses} grow={false}>
|
||||
{!isInMoveMode && (
|
||||
<EuiToolTip content={editorItemMessages.moveButtonLabel}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="moveItemButton"
|
||||
size="s"
|
||||
disabled={isDisabled}
|
||||
aria-label={editorItemMessages.moveButtonLabel}
|
||||
onClick={onMove}
|
||||
iconType="sortable"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} className={cancelMoveButtonClasses}>
|
||||
<EuiButton data-test-subj="cancelMoveItemButton" size="s" onClick={onCancelMove}>
|
||||
{editorItemMessages.cancelMoveButtonLabel}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ContextMenu
|
||||
data-test-subj="moreMenu"
|
||||
disabled={isDisabled}
|
||||
hidden={isInMoveMode}
|
||||
showAddOnFailure={!processor.onFailure?.length}
|
||||
onAddOnFailure={() => {
|
||||
editor.setMode({ id: 'creatingProcessor', arg: { selector } });
|
||||
}}
|
||||
onDelete={() => {
|
||||
editor.setMode({ id: 'removingProcessor', arg: { selector } });
|
||||
}}
|
||||
onDuplicate={() => {
|
||||
processorsDispatch({
|
||||
type: 'duplicateProcessor',
|
||||
payload: {
|
||||
source: selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{renderOnFailureHandlers && renderOnFailureHandlers()}
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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 { OnXJsonEditorUpdateHandler, XJsonEditor } from './xjson_editor';
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 { EuiPanel } from '@elastic/eui';
|
||||
import { XJsonLang } from '@kbn/monaco';
|
||||
import React, { FunctionComponent, useCallback } from 'react';
|
||||
import { EuiFormRow } from '@elastic/eui';
|
||||
import {
|
||||
CodeEditor,
|
||||
FieldHook,
|
||||
getFieldValidityAndErrorMessage,
|
||||
Monaco,
|
||||
} from '../../../../../../shared_imports';
|
||||
|
||||
export type OnXJsonEditorUpdateHandler<T = { [key: string]: any }> = (arg: {
|
||||
data: {
|
||||
raw: string;
|
||||
format(): T;
|
||||
};
|
||||
validate(): boolean;
|
||||
isValid: boolean | undefined;
|
||||
}) => void;
|
||||
|
||||
interface Props {
|
||||
field: FieldHook<string>;
|
||||
editorProps: { [key: string]: any };
|
||||
}
|
||||
|
||||
export const XJsonEditor: FunctionComponent<Props> = ({ field, editorProps }) => {
|
||||
const { value, helpText, setValue, label } = field;
|
||||
const { xJson, setXJson, convertToJson } = Monaco.useXJsonMode(value);
|
||||
const { errorMessage } = getFieldValidityAndErrorMessage(field);
|
||||
|
||||
const onChange = useCallback(
|
||||
(s) => {
|
||||
setXJson(s);
|
||||
setValue(convertToJson(s));
|
||||
},
|
||||
[setValue, setXJson, convertToJson]
|
||||
);
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={label}
|
||||
helpText={helpText}
|
||||
isInvalid={typeof errorMessage === 'string'}
|
||||
error={errorMessage}
|
||||
fullWidth
|
||||
>
|
||||
<EuiPanel paddingSize="s" hasShadow={false}>
|
||||
<CodeEditor
|
||||
value={xJson}
|
||||
languageId={XJsonLang.ID}
|
||||
editorDidMount={(m) => {
|
||||
XJsonLang.registerGrammarChecker(m);
|
||||
}}
|
||||
options={{ minimap: { enabled: false } }}
|
||||
onChange={onChange}
|
||||
{...(editorProps as any)}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
|
@ -18,26 +18,32 @@ import {
|
|||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { Form, useForm, FormDataProvider } from '../../../../../shared_imports';
|
||||
import { Form, FormDataProvider, FormHook } from '../../../../../shared_imports';
|
||||
import { usePipelineProcessorsContext } from '../../context';
|
||||
import { ProcessorInternal } from '../../types';
|
||||
|
||||
import { DocumentationButton } from './documentation_button';
|
||||
import { ProcessorSettingsFromOnSubmitArg } from './processor_settings_form.container';
|
||||
import { getProcessorFormDescriptor } from './map_processor_type_to_form';
|
||||
import { CommonProcessorFields, ProcessorTypeField } from './processors/common_fields';
|
||||
import { Custom } from './processors/custom';
|
||||
|
||||
export type OnSubmitHandler = (processor: ProcessorSettingsFromOnSubmitArg) => void;
|
||||
|
||||
export interface Props {
|
||||
isOnFailure: boolean;
|
||||
processor?: ProcessorInternal;
|
||||
form: ReturnType<typeof useForm>['form'];
|
||||
form: FormHook;
|
||||
onClose: () => void;
|
||||
onOpen: () => void;
|
||||
}
|
||||
|
||||
const updateButtonLabel = i18n.translate(
|
||||
'xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel',
|
||||
{ defaultMessage: 'Update' }
|
||||
);
|
||||
const addButtonLabel = i18n.translate(
|
||||
'xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel',
|
||||
{ defaultMessage: 'Add' }
|
||||
);
|
||||
|
||||
export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
|
||||
({ processor, form, isOnFailure, onClose, onOpen }) => {
|
||||
const {
|
||||
|
@ -123,10 +129,7 @@ export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
|
|||
<>
|
||||
{formContent}
|
||||
<EuiButton data-test-subj="submitButton" onClick={form.submit}>
|
||||
{i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.settingsForm.submitButtonLabel',
|
||||
{ defaultMessage: 'Submit' }
|
||||
)}
|
||||
{processor ? updateButtonLabel : addButtonLabel}
|
||||
</EuiButton>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -12,15 +12,16 @@ import {
|
|||
FIELD_TYPES,
|
||||
fieldValidators,
|
||||
UseField,
|
||||
JsonEditorField,
|
||||
} from '../../../../../../shared_imports';
|
||||
|
||||
const { emptyField, isJsonField } = fieldValidators;
|
||||
|
||||
import { XJsonEditor } from '../field_components';
|
||||
|
||||
const customConfig: FieldConfig = {
|
||||
type: FIELD_TYPES.TEXT,
|
||||
label: i18n.translate('xpack.ingestPipelines.pipelineEditor.customForm.optionsFieldLabel', {
|
||||
defaultMessage: 'Configuration options',
|
||||
defaultMessage: 'Configuration',
|
||||
}),
|
||||
serializer: (value: string) => {
|
||||
try {
|
||||
|
@ -42,7 +43,7 @@ const customConfig: FieldConfig = {
|
|||
i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.customForm.configurationRequiredError',
|
||||
{
|
||||
defaultMessage: 'Configuration options are required.',
|
||||
defaultMessage: 'Configuration is required.',
|
||||
}
|
||||
)
|
||||
),
|
||||
|
@ -71,17 +72,17 @@ export const Custom: FunctionComponent<Props> = ({ defaultOptions }) => {
|
|||
return (
|
||||
<UseField
|
||||
path="customOptions"
|
||||
component={JsonEditorField}
|
||||
component={XJsonEditor}
|
||||
config={customConfig}
|
||||
defaultValue={defaultOptions}
|
||||
componentProps={{
|
||||
euiCodeEditorProps: {
|
||||
editorProps: {
|
||||
'data-test-subj': 'processorOptionsEditor',
|
||||
height: '300px',
|
||||
height: 300,
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.customForm.optionsFieldAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Configuration options JSON editor',
|
||||
defaultMessage: 'Configuration JSON editor',
|
||||
}
|
||||
),
|
||||
},
|
||||
|
|
|
@ -7,39 +7,61 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { EuiButtonIcon, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
|
||||
|
||||
export interface Props {
|
||||
isVisible: boolean;
|
||||
isDisabled: boolean;
|
||||
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
const MOVE_HERE_LABEL = i18n.translate('xpack.ingestPipelines.pipelineEditor.moveTargetLabel', {
|
||||
defaultMessage: 'Move here',
|
||||
});
|
||||
const moveHereLabel = i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.dropZoneButton.moveHereToolTip',
|
||||
{
|
||||
defaultMessage: 'Move here',
|
||||
}
|
||||
);
|
||||
|
||||
const cannotMoveHereLabel = i18n.translate(
|
||||
'xpack.ingestPipelines.pipelineEditor.dropZoneButton.unavailableToolTip',
|
||||
{ defaultMessage: 'Cannot move here' }
|
||||
);
|
||||
|
||||
export const DropZoneButton: FunctionComponent<Props> = (props) => {
|
||||
const { onClick, isDisabled } = props;
|
||||
const { onClick, isDisabled, isVisible } = props;
|
||||
const isUnavailable = isVisible && isDisabled;
|
||||
const containerClasses = classNames({
|
||||
'pipelineProcessorsEditor__tree__dropZoneContainer--active': !isDisabled,
|
||||
'pipelineProcessorsEditor__tree__dropZoneContainer--visible': isVisible,
|
||||
'pipelineProcessorsEditor__tree__dropZoneContainer--unavailable': isUnavailable,
|
||||
});
|
||||
const buttonClasses = classNames({
|
||||
'pipelineProcessorsEditor__tree__dropZoneButton--active': !isDisabled,
|
||||
'pipelineProcessorsEditor__tree__dropZoneButton--visible': isVisible,
|
||||
'pipelineProcessorsEditor__tree__dropZoneButton--unavailable': isUnavailable,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlexItem
|
||||
className={`pipelineProcessorsEditor__tree__dropZoneContainer ${containerClasses}`}
|
||||
>
|
||||
const content = (
|
||||
<div className={`pipelineProcessorsEditor__tree__dropZoneContainer ${containerClasses}`}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj={props['data-test-subj']}
|
||||
className={`pipelineProcessorsEditor__tree__dropZoneButton ${buttonClasses}`}
|
||||
aria-label={MOVE_HERE_LABEL}
|
||||
disabled={isDisabled}
|
||||
onClick={onClick}
|
||||
aria-label={moveHereLabel}
|
||||
// We artificially disable the button so that hover and pointer events are
|
||||
// still enabled
|
||||
onClick={isDisabled ? () => {} : onClick}
|
||||
iconType="empty"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</div>
|
||||
);
|
||||
|
||||
return isUnavailable ? (
|
||||
<EuiToolTip
|
||||
className="pipelineProcessorsEditor__tree__dropZoneContainer__toolTip"
|
||||
content={cannotMoveHereLabel}
|
||||
>
|
||||
{content}
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
content
|
||||
);
|
||||
};
|
||||
|
|
|
@ -78,22 +78,23 @@ export const PrivateTree: FunctionComponent<PrivateProps> = ({
|
|||
return (
|
||||
<>
|
||||
{idx === 0 ? (
|
||||
<DropZoneButton
|
||||
data-test-subj={`dropButtonAbove-${stringifiedSelector}`}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onAction({
|
||||
type: 'move',
|
||||
payload: {
|
||||
destination: selector.concat(DropSpecialLocations.top),
|
||||
source: movingProcessor!.selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
isDisabled={Boolean(
|
||||
!movingProcessor || isDropZoneAboveDisabled(info, movingProcessor!)
|
||||
)}
|
||||
/>
|
||||
<EuiFlexItem>
|
||||
<DropZoneButton
|
||||
data-test-subj={`dropButtonAbove-${stringifiedSelector}`}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onAction({
|
||||
type: 'move',
|
||||
payload: {
|
||||
destination: selector.concat(DropSpecialLocations.top),
|
||||
source: movingProcessor!.selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
isVisible={Boolean(movingProcessor)}
|
||||
isDisabled={!movingProcessor || isDropZoneAboveDisabled(info, movingProcessor)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : undefined}
|
||||
<EuiFlexItem>
|
||||
<TreeNode
|
||||
|
@ -104,20 +105,23 @@ export const PrivateTree: FunctionComponent<PrivateProps> = ({
|
|||
movingProcessor={movingProcessor}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<DropZoneButton
|
||||
data-test-subj={`dropButtonBelow-${stringifiedSelector}`}
|
||||
isDisabled={Boolean(!movingProcessor || isDropZoneBelowDisabled(info, movingProcessor!))}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onAction({
|
||||
type: 'move',
|
||||
payload: {
|
||||
destination: selector.concat(String(idx + 1)),
|
||||
source: movingProcessor!.selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<EuiFlexItem>
|
||||
<DropZoneButton
|
||||
data-test-subj={`dropButtonBelow-${stringifiedSelector}`}
|
||||
isVisible={Boolean(movingProcessor)}
|
||||
isDisabled={!movingProcessor || isDropZoneBelowDisabled(info, movingProcessor)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onAction({
|
||||
type: 'move',
|
||||
payload: {
|
||||
destination: selector.concat(String(idx + 1)),
|
||||
source: movingProcessor!.selector,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
*/
|
||||
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiPanel, EuiText } from '@elastic/eui';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
|
||||
import { ProcessorInternal } from '../../../types';
|
||||
|
||||
|
@ -47,40 +46,21 @@ export const TreeNode: FunctionComponent<Props> = ({
|
|||
};
|
||||
}, [onAction, stringSelector, processor]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const selected = movingProcessor?.id === processor.id;
|
||||
|
||||
const panelClasses = classNames({
|
||||
'pipelineProcessorsEditor__tree__item--selected': selected,
|
||||
});
|
||||
|
||||
const renderOnFailureHandlersTree = () => {
|
||||
if (!processor.onFailure?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onFailureHandlerLabelClasses = classNames({
|
||||
'pipelineProcessorsEditor__tree__onFailureHandlerLabel--withDropZone':
|
||||
movingProcessor != null &&
|
||||
movingProcessor.id !== processor.onFailure[0].id &&
|
||||
movingProcessor.id !== processor.id,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className="pipelineProcessorsEditor__tree__onFailureHandlerContainer"
|
||||
style={{ marginLeft: `${level * INDENTATION_PX}px` }}
|
||||
>
|
||||
<div className="pipelineProcessorsEditor__tree__onFailureHandlerLabelContainer">
|
||||
<EuiText
|
||||
size="m"
|
||||
className={`pipelineProcessorsEditor__tree__onFailureHandlerLabel ${onFailureHandlerLabelClasses}`}
|
||||
color="subdued"
|
||||
>
|
||||
{i18n.translate('xpack.ingestPipelines.pipelineEditor.onFailureProcessorsLabel', {
|
||||
defaultMessage: 'Failure handlers',
|
||||
})}
|
||||
</EuiText>
|
||||
</div>
|
||||
<EuiText size="m" color="subdued">
|
||||
{i18n.translate('xpack.ingestPipelines.pipelineEditor.onFailureProcessorsLabel', {
|
||||
defaultMessage: 'Failure handlers',
|
||||
})}
|
||||
</EuiText>
|
||||
<PrivateTree
|
||||
level={level + 1}
|
||||
movingProcessor={movingProcessor}
|
||||
|
@ -102,15 +82,13 @@ export const TreeNode: FunctionComponent<Props> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<EuiPanel className={`pipelineProcessorsEditor__tree__item ${panelClasses}`} paddingSize="s">
|
||||
<PipelineProcessorsEditorItem
|
||||
selector={processorInfo.selector}
|
||||
processor={processor}
|
||||
handlers={handlers}
|
||||
description={processor.options.description}
|
||||
selected={Boolean(movingProcessor?.id === processor.id)}
|
||||
/>
|
||||
{renderOnFailureHandlersTree()}
|
||||
</EuiPanel>
|
||||
<PipelineProcessorsEditorItem
|
||||
movingProcessor={movingProcessor}
|
||||
selector={processorInfo.selector}
|
||||
processor={processor}
|
||||
handlers={handlers}
|
||||
description={processor.options.description}
|
||||
renderOnFailureHandlers={renderOnFailureHandlersTree}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
@import '@elastic/eui/src/global_styling/variables/size';
|
||||
@import '../shared';
|
||||
|
||||
.pipelineProcessorsEditor__tree {
|
||||
|
||||
&__container {
|
||||
background-color: $euiColorLightestShade;
|
||||
padding: $euiSizeS;
|
||||
}
|
||||
|
||||
&__dropZoneContainer {
|
||||
position: relative;
|
||||
margin: 2px;
|
||||
visibility: hidden;
|
||||
border: 2px dashed $euiColorLightShade;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
height: 2px;
|
||||
|
||||
transition: border .5s;
|
||||
|
||||
&--active {
|
||||
&--visible {
|
||||
&:hover {
|
||||
border: 2px dashed $euiColorPrimary;
|
||||
background-color: $euiColorPrimary;
|
||||
}
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&--unavailable {
|
||||
&:hover {
|
||||
background-color: $euiColorMediumShade;
|
||||
}
|
||||
}
|
||||
|
||||
&__toolTip {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
$dropZoneButtonHeight: 60px;
|
||||
$dropZoneButtonOffsetY: $dropZoneButtonHeight * -0.5;
|
||||
|
||||
&__dropZoneButton {
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
height: $dropZoneButtonHeight;
|
||||
margin-top: $dropZoneButtonOffsetY;
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
text-decoration: none !important;
|
||||
z-index: $dropZoneZIndex;
|
||||
|
||||
&--active {
|
||||
&--visible {
|
||||
pointer-events: visible !important;
|
||||
&:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default !important;
|
||||
& > * {
|
||||
cursor: default !important;
|
||||
}
|
||||
&--unavailable {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__onFailureHandlerLabelContainer {
|
||||
position: relative;
|
||||
height: 14px;
|
||||
}
|
||||
&__onFailureHandlerLabel {
|
||||
position: absolute;
|
||||
bottom: -16px;
|
||||
&--withDropZone {
|
||||
bottom: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__onFailureHandlerContainer {
|
||||
margin-top: $euiSizeS;
|
||||
margin-bottom: $euiSizeS;
|
||||
|
@ -63,12 +63,4 @@
|
|||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
transition: border-color 1s;
|
||||
min-height: 50px;
|
||||
&--selected {
|
||||
border: 1px solid $euiColorPrimary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ import { ProcessorInternal } from '../../types';
|
|||
// - ./components/drop_zone_button.tsx
|
||||
// - ./components/pipeline_processors_editor_item.tsx
|
||||
const itemHeightsPx = {
|
||||
WITHOUT_NESTED_ITEMS: 67,
|
||||
WITHOUT_NESTED_ITEMS: 57,
|
||||
WITH_NESTED_ITEMS: 137,
|
||||
TOP_PADDING: 16,
|
||||
TOP_PADDING: 6,
|
||||
};
|
||||
|
||||
export const calculateItemHeight = ({
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.pipelineProcessorsEditor {
|
||||
margin-bottom: $euiSize;
|
||||
margin-bottom: $euiSizeXL;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ export const PipelineProcessorsEditor: FunctionComponent<Props> = memo(
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<OnFailureProcessorsTitle />
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { HttpSetup } from 'kibana/public';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { NotificationsSetup } from 'kibana/public';
|
||||
import { NotificationsSetup, IUiSettingsClient } from 'kibana/public';
|
||||
import { ManagementAppMountParams } from 'src/plugins/management/public';
|
||||
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
|
@ -25,6 +25,7 @@ export interface AppServices {
|
|||
api: ApiService;
|
||||
notifications: NotificationsSetup;
|
||||
history: ManagementAppMountParams['history'];
|
||||
uiSettings: IUiSettingsClient;
|
||||
}
|
||||
|
||||
export interface CoreServices {
|
||||
|
|
|
@ -30,6 +30,7 @@ export async function mountManagementSection(
|
|||
api: apiService,
|
||||
notifications,
|
||||
history,
|
||||
uiSettings: coreStart.uiSettings,
|
||||
};
|
||||
|
||||
return renderApp(element, I18nContext, services, { http });
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { useKibana as _useKibana } from '../../../../src/plugins/kibana_react/public';
|
||||
import { useKibana as _useKibana, CodeEditor } from '../../../../src/plugins/kibana_react/public';
|
||||
import { AppServices } from './application';
|
||||
|
||||
export { CodeEditor };
|
||||
|
||||
export {
|
||||
AuthorizationProvider,
|
||||
Error,
|
||||
|
@ -19,6 +21,7 @@ export {
|
|||
useRequest,
|
||||
UseRequestConfig,
|
||||
WithPrivileges,
|
||||
Monaco,
|
||||
} from '../../../../src/plugins/es_ui_shared/public/';
|
||||
|
||||
export {
|
||||
|
@ -36,6 +39,8 @@ export {
|
|||
FormDataProvider,
|
||||
OnFormUpdateArg,
|
||||
FieldConfig,
|
||||
FieldHook,
|
||||
getFieldValidityAndErrorMessage,
|
||||
} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
|
||||
|
||||
export {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue