[Ingest pipelines] Implement tabs in processor flyout (#74469) (#74764)

This commit is contained in:
Alison Goryachev 2020-08-11 12:03:02 -04:00 committed by GitHub
parent f4b44c70d6
commit 646b58b76b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 158 additions and 97 deletions

View file

@ -186,7 +186,7 @@ describe('Pipeline Editor', () => {
it('prevents moving a processor while in edit mode', () => {
const { find, exists } = testBed;
find('processors>0.editItemButton').simulate('click');
find('processors>0.manageItemButton').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);

View file

@ -5,10 +5,10 @@
*/
export {
ProcessorSettingsForm,
ProcessorSettingsFromOnSubmitArg,
ManageProcessorForm,
ManageProcessorFormOnSubmitArg,
OnSubmitHandler,
} from './processor_settings_form';
} from './manage_processor_form';
export { ProcessorsTree, ProcessorInfo, OnActionHandler } from './processors_tree';

View file

@ -5,7 +5,7 @@
*/
export {
ProcessorSettingsForm,
ProcessorSettingsFromOnSubmitArg,
ManageProcessorForm,
ManageProcessorFormOnSubmitArg,
OnSubmitHandler,
} from './processor_settings_form.container';
} from './manage_processor_form.container';

View file

@ -9,12 +9,12 @@ import React, { FunctionComponent, useCallback, useEffect } from 'react';
import { useForm, OnFormUpdateArg, FormData } from '../../../../../shared_imports';
import { ProcessorInternal } from '../../types';
import { ProcessorSettingsForm as ViewComponent } from './processor_settings_form';
import { ManageProcessorForm as ViewComponent } from './manage_processor_form';
import { usePipelineProcessorsContext } from '../../context';
export type ProcessorSettingsFromOnSubmitArg = Omit<ProcessorInternal, 'id'>;
export type ManageProcessorFormOnSubmitArg = Omit<ProcessorInternal, 'id'>;
export type OnSubmitHandler = (processor: ProcessorSettingsFromOnSubmitArg) => void;
export type OnSubmitHandler = (processor: ManageProcessorFormOnSubmitArg) => void;
export type OnFormUpdateHandler = (form: OnFormUpdateArg<any>) => void;
@ -27,7 +27,7 @@ interface Props {
processor?: ProcessorInternal;
}
export const ProcessorSettingsForm: FunctionComponent<Props> = ({
export const ManageProcessorForm: FunctionComponent<Props> = ({
processor,
onFormUpdate,
onSubmit,

View file

@ -6,15 +6,17 @@
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { FunctionComponent, memo, useEffect } from 'react';
import React, { FunctionComponent, memo, useEffect, useState } from 'react';
import {
EuiButton,
EuiButtonEmpty,
EuiHorizontalRule,
EuiFlyout,
EuiFlyoutHeader,
EuiFlyoutBody,
EuiFlyoutFooter,
EuiSpacer,
EuiTabs,
EuiTab,
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
@ -22,12 +24,10 @@ import {
import { Form, FormDataProvider, FormHook } from '../../../../../shared_imports';
import { ProcessorInternal } from '../../types';
import { getProcessorDescriptor } from '../shared';
import { ProcessorSettingsFields } from './processor_settings_fields';
import { DocumentationButton } from './documentation_button';
import { CommonProcessorFields, ProcessorTypeField } from './processors/common_fields';
import { Custom } from './processors/custom';
export interface Props {
isOnFailure: boolean;
@ -42,6 +42,7 @@ const updateButtonLabel = i18n.translate(
'xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel',
{ defaultMessage: 'Update' }
);
const addButtonLabel = i18n.translate(
'xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel',
{ defaultMessage: 'Add' }
@ -52,20 +53,55 @@ const cancelButtonLabel = i18n.translate(
{ defaultMessage: 'Cancel' }
);
export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
({ processor, form, isOnFailure, onClose, onOpen, esDocsBasePath }) => {
const flyoutTitleContent = isOnFailure ? (
export type TabType = 'configuration';
interface Tab {
id: TabType;
name: string;
}
const tabs: Tab[] = [
{
id: 'configuration',
name: i18n.translate(
'xpack.ingestPipelines.settingsFormOnFailureFlyout.configurationTabTitle',
{
defaultMessage: 'Configuration',
}
),
},
];
const getFlyoutTitle = (isOnFailure: boolean, isExistingProcessor: boolean) => {
if (isExistingProcessor) {
return isOnFailure ? (
<FormattedMessage
id="xpack.ingestPipelines.settingsFormOnFailureFlyout.title"
defaultMessage="Configure on-failure processor"
id="xpack.ingestPipelines.settingsFormOnFailureFlyout.manageOnFailureTitle"
defaultMessage="Manage on-failure processor"
/>
) : (
<FormattedMessage
id="xpack.ingestPipelines.settingsFormFlyout.title"
defaultMessage="Configure processor"
id="xpack.ingestPipelines.settingsFormOnFailureFlyout.manageTitle"
defaultMessage="Manage processor"
/>
);
}
return isOnFailure ? (
<FormattedMessage
id="xpack.ingestPipelines.settingsFormOnFailureFlyout.configureOnFailureTitle"
defaultMessage="Configure on-failure processor"
/>
) : (
<FormattedMessage
id="xpack.ingestPipelines.settingsFormOnFailureFlyout.configureTitle"
defaultMessage="Configure processor"
/>
);
};
export const ManageProcessorForm: FunctionComponent<Props> = memo(
({ processor, form, isOnFailure, onClose, onOpen, esDocsBasePath }) => {
useEffect(
() => {
onOpen();
@ -73,6 +109,10 @@ export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
[] /* eslint-disable-line react-hooks/exhaustive-deps */
);
const [activeTab, setActiveTab] = useState<TabType>('configuration');
const flyoutContent = <ProcessorSettingsFields processor={processor} />;
return (
<Form data-test-subj="processorSettingsForm" form={form}>
<EuiFlyout size="m" maxWidth={720} onClose={onClose}>
@ -81,11 +121,10 @@ export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
<EuiFlexItem>
<div>
<EuiTitle size="m">
<h2>{flyoutTitleContent}</h2>
<h2>{getFlyoutTitle(isOnFailure, Boolean(processor))}</h2>
</EuiTitle>
</div>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormDataProvider pathsToWatch="type">
{({ type }) => {
@ -106,32 +145,27 @@ export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
</EuiFlexGroup>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<ProcessorTypeField initialType={processor?.type} />
{processor ? (
<>
<EuiTabs>
{tabs.map((tab) => (
<EuiTab
onClick={() => {
setActiveTab(tab.id);
}}
isSelected={tab.id === activeTab}
key={tab.id}
data-test-subj={`${tab.id}Tab`}
>
{tab.name}
</EuiTab>
))}
</EuiTabs>
<EuiSpacer />
</>
) : undefined}
<EuiHorizontalRule />
<FormDataProvider pathsToWatch="type">
{(arg: any) => {
const { type } = arg;
if (type?.length) {
const formDescriptor = getProcessorDescriptor(type as any);
if (formDescriptor?.FieldsComponent) {
return (
<>
<formDescriptor.FieldsComponent />
<CommonProcessorFields />
</>
);
}
return <Custom defaultOptions={processor?.options} />;
}
// If the user has not yet defined a type, we do not show any settings fields
return null;
}}
</FormDataProvider>
{flyoutContent}
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="flexEnd">
@ -139,13 +173,7 @@ export const ProcessorSettingsForm: FunctionComponent<Props> = memo(
<EuiButtonEmpty onClick={onClose}>{cancelButtonLabel}</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill
data-test-subj="submitButton"
onClick={() => {
form.submit();
}}
>
<EuiButton fill data-test-subj="submitButton" onClick={form.submit}>
{processor ? updateButtonLabel : addButtonLabel}
</EuiButton>
</EuiFlexItem>

View file

@ -0,0 +1,52 @@
/*
* 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 } from 'react';
import { EuiHorizontalRule } from '@elastic/eui';
import { FormDataProvider } from '../../../../../shared_imports';
import { ProcessorInternal } from '../../types';
import { getProcessorDescriptor } from '../shared';
import { CommonProcessorFields, ProcessorTypeField } from './processors/common_fields';
import { Custom } from './processors/custom';
export interface Props {
processor?: ProcessorInternal;
}
export const ProcessorSettingsFields: FunctionComponent<Props> = ({ processor }) => {
return (
<>
<ProcessorTypeField initialType={processor?.type} />
<EuiHorizontalRule />
<FormDataProvider pathsToWatch="type">
{(arg: any) => {
const { type } = arg;
if (type?.length) {
const formDescriptor = getProcessorDescriptor(type as any);
if (formDescriptor?.FieldsComponent) {
return (
<>
<formDescriptor.FieldsComponent />
<CommonProcessorFields />
</>
);
}
return <Custom defaultOptions={processor?.options} />;
}
// If the user has not yet defined a type, we do not show any settings fields
return null;
}}
</FormDataProvider>
</>
);
};

View file

@ -7,10 +7,10 @@
import classNames from 'classnames';
import React, { FunctionComponent, memo } from 'react';
import {
EuiButtonIcon,
EuiButtonToggle,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiPanel,
EuiText,
EuiToolTip,
@ -57,9 +57,9 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
const isInMoveMode = Boolean(movingProcessor);
const isMovingThisProcessor = processor.id === movingProcessor?.id;
const isEditingThisProcessor =
editor.mode.id === 'editingProcessor' && processor.id === editor.mode.arg.processor.id;
editor.mode.id === 'managingProcessor' && processor.id === editor.mode.arg.processor.id;
const isEditingOtherProcessor =
editor.mode.id === 'editingProcessor' && !isEditingThisProcessor;
editor.mode.id === 'managingProcessor' && !isEditingThisProcessor;
const isMovingOtherProcessor = editor.mode.id === 'movingProcessor' && !isMovingThisProcessor;
const isDimmed = isEditingOtherProcessor || isMovingOtherProcessor;
@ -70,11 +70,6 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
'pipelineProcessorsEditor__item--dimmed': isDimmed,
});
const actionElementClasses = classNames({
// eslint-disable-next-line @typescript-eslint/naming-convention
'pipelineProcessorsEditor__item--displayNone': isInMoveMode,
});
const inlineTextInputContainerClasses = classNames({
// eslint-disable-next-line @typescript-eslint/naming-convention
'pipelineProcessorsEditor__item--displayNone': isInMoveMode && !processor.options.description,
@ -141,7 +136,18 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
className="pipelineProcessorsEditor__item__processorTypeLabel"
color={isDimmed ? 'subdued' : undefined}
>
<b>{getProcessorDescriptor(processor.type)?.label ?? processor.type}</b>
<EuiLink
disabled={isEditorNotInIdleMode}
onClick={() => {
editor.setMode({
id: 'managingProcessor',
arg: { processor, selector },
});
}}
data-test-subj="manageItemButton"
>
<b>{getProcessorDescriptor(processor.type)?.label ?? processor.type}</b>
</EuiLink>
</EuiText>
</EuiFlexItem>
<EuiFlexItem className={inlineTextInputContainerClasses} grow={false}>
@ -174,25 +180,6 @@ export const PipelineProcessorsEditorItem: FunctionComponent<Props> = memo(
placeholder={i18nTexts.descriptionPlaceholder}
/>
</EuiFlexItem>
<EuiFlexItem className={actionElementClasses} grow={false}>
{!isInMoveMode && (
<EuiToolTip content={i18nTexts.editButtonLabel}>
<EuiButtonIcon
data-test-subj="editItemButton"
disabled={isEditorNotInIdleMode}
aria-label={i18nTexts.editButtonLabel}
iconType="pencil"
size="s"
onClick={() => {
editor.setMode({
id: 'editingProcessor',
arg: { processor, selector },
});
}}
/>
</EuiToolTip>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -42,7 +42,7 @@ import { OnActionHandler } from '../components/processors_tree';
import {
ProcessorRemoveModal,
PipelineProcessorsItemTooltip,
ProcessorSettingsForm,
ManageProcessorForm,
OnSubmitHandler,
} from '../components';
@ -148,7 +148,7 @@ export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
},
});
break;
case 'editingProcessor':
case 'managingProcessor':
processorsDispatch({
type: 'updateProcessor',
payload: {
@ -229,10 +229,10 @@ export const PipelineProcessorsContextProvider: FunctionComponent<Props> = ({
/>
)}
{mode.id === 'editingProcessor' || mode.id === 'creatingProcessor' ? (
<ProcessorSettingsForm
{mode.id === 'managingProcessor' || mode.id === 'creatingProcessor' ? (
<ManageProcessorForm
isOnFailure={isOnFailureSelector(mode.arg.selector)}
processor={mode.id === 'editingProcessor' ? mode.arg.processor : undefined}
processor={mode.id === 'managingProcessor' ? mode.arg.processor : undefined}
onOpen={onFlyoutOpen}
onFormUpdate={onFormUpdate}
onSubmit={onSubmit}

View file

@ -56,7 +56,7 @@ export type OnUpdateHandler = (arg: OnUpdateHandlerArg) => void;
export type EditorMode =
| { id: 'creatingProcessor'; arg: { selector: ProcessorSelector } }
| { id: 'movingProcessor'; arg: ProcessorInfo }
| { id: 'editingProcessor'; arg: { processor: ProcessorInternal; selector: ProcessorSelector } }
| { id: 'managingProcessor'; arg: { processor: ProcessorInternal; selector: ProcessorSelector } }
| { id: 'removingProcessor'; arg: { selector: ProcessorSelector } }
| { id: 'idle' };

View file

@ -9849,11 +9849,8 @@
"xpack.ingestPipelines.requestFlyout.descriptionText": "このElasticsearchリクエストは、このパイプラインを作成または更新します。",
"xpack.ingestPipelines.requestFlyout.namedTitle": "「{name}」のリクエスト",
"xpack.ingestPipelines.requestFlyout.unnamedTitle": "リクエスト",
"xpack.ingestPipelines.settingsFormFlyout.title": "プロセッサーの構成",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel": "追加",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel": "キャンセル",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.title": "エラープロセッサーの構成",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel": "更新",
"xpack.ingestPipelines.tabs.documentsTabTitle": "ドキュメント",
"xpack.ingestPipelines.tabs.outputTabTitle": "アウトプット",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel": "ドキュメント",

View file

@ -9851,11 +9851,8 @@
"xpack.ingestPipelines.requestFlyout.descriptionText": "此 Elasticsearch 请求将创建或更新管道。",
"xpack.ingestPipelines.requestFlyout.namedTitle": "对“{name}”的请求",
"xpack.ingestPipelines.requestFlyout.unnamedTitle": "请求",
"xpack.ingestPipelines.settingsFormFlyout.title": "配置处理器",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.addButtonLabel": "添加",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.cancelButtonLabel": "取消",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.title": "配置失败时处理器",
"xpack.ingestPipelines.settingsFormOnFailureFlyout.updateButtonLabel": "更新",
"xpack.ingestPipelines.tabs.documentsTabTitle": "文档",
"xpack.ingestPipelines.tabs.outputTabTitle": "输出",
"xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel": "文档",