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

[Ingest Node Pipelines] New patterns component for Grok processor #76533

Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
329188d
wip, issues with use fields getting cleared somehow
jloleysens Aug 21, 2020
ca45ac6
New drag and drop text list component
jloleysens Sep 2, 2020
2c87c65
remove box shadow from editor fields
jloleysens Sep 3, 2020
10d1d12
Style grok patterns based on drag and drop in component templates
jloleysens Sep 3, 2020
32228bc
fix i18n
jloleysens Sep 3, 2020
8f365fa
Merge branch 'master' of github.com:elastic/kibana into ingest-node/g…
jloleysens Sep 4, 2020
1be8bfc
update use_array - maintain the same API though
jloleysens Sep 4, 2020
e59e7de
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 7, 2020
f4a29c4
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 9, 2020
e4489fe
Merge branch 'master' of github.com:elastic/kibana into ingest-node/g…
jloleysens Sep 10, 2020
c8e6f1c
Grok processor should use the new use array interface
jloleysens Sep 10, 2020
8fd740c
fix patterns field validation to check validity of pattern entires
jloleysens Sep 10, 2020
1dd2f78
fix drag item styling
jloleysens Sep 10, 2020
25ff6be
fix use of form in use effect and update behaviour of submit button
jloleysens Sep 10, 2020
cbc94d2
added smoke test for grok component
jloleysens Sep 10, 2020
dec25dc
fix i18n
jloleysens Sep 14, 2020
7a847b0
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 14, 2020
9f65205
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 14, 2020
6852cb8
Implement PR feedback
jloleysens Sep 14, 2020
6a85bac
Merge branch 'master' of github.com:elastic/kibana into ingest-node/g…
jloleysens Sep 18, 2020
f5c707b
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 18, 2020
c4235f9
Implemented design feedback
jloleysens Sep 18, 2020
eb632de
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 21, 2020
e856141
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 22, 2020
9e72584
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 22, 2020
de6f2d6
Merge branch 'master' into ingest-node/grok/new-patterns-component-us…
elasticmachine Sep 24, 2020
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
@@ -0,0 +1,24 @@
.pipelineProcessorsEditor__form__dragAndDropList {
&__panel {
box-shadow: none;
}

&__grabIcon {
margin-right: $euiSizeS;
}

&__removeButton {
margin-left: $euiSizeS;
}

&__errorIcon {
margin-left: -$euiSizeXL;
}

&__item {
background-color: $euiColorEmptyShade;
padding-top: $euiSizeM;
padding-bottom: $euiSizeM;
border-bottom: $euiBorderThin;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* 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 { i18n } from '@kbn/i18n';
import React, { useState, useCallback, memo } from 'react';
import uuid from 'uuid';
import {
EuiButtonEmpty,
EuiButtonIcon,
EuiDragDropContext,
EuiDraggable,
EuiDroppable,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiPanel,
EuiSpacer,
EuiFieldText,
EuiIconTip,
} from '@elastic/eui';

import {
UseField,
ArrayItem,
ValidationFunc,
getFieldValidityAndErrorMessage,
} from '../../../../../../shared_imports';

import './drag_and_drop_text_list.scss';

interface Props {
value: ArrayItem[];
onMove: (sourceIdx: number, destinationIdx: number) => void;
onAdd: () => void;
onRemove: (id: number) => void;
addLabel: string;
/**
* Validation to be applied to every text item
*/
textValidation?: ValidationFunc<any, string, string>;
}

const i18nTexts = {
removeItemButtonAriaLabel: i18n.translate(
'xpack.ingestPipelines.pipelineEditor.dragAndDropList.removeItemLabel',
{ defaultMessage: 'Remove item' }
),
};

function DragAndDropTextListComponent({
value,
onMove,
onAdd,
onRemove,
addLabel,
textValidation,
}: Props): JSX.Element {
const [droppableId] = useState(() => uuid.v4());

const onDragEnd = useCallback(
({ source, destination }) => {
if (source && destination) {
onMove(source.index, destination.index);
}
},
[onMove]
);
return (
<EuiPanel
className="pipelineProcessorsEditor__form__dragAndDropList__panel"
hasShadow={false}
paddingSize="s"
>
<EuiDragDropContext onDragEnd={onDragEnd}>
<EuiDroppable droppableId={droppableId}>
{value.map((item, idx) => {
return (
<EuiDraggable
customDragHandle
spacing="none"
draggableId={String(item.id)}
index={idx}
key={item.id}
>
{(provided) => {
return (
<EuiFlexGroup
className="pipelineProcessorsEditor__form__dragAndDropList__item"
justifyContent="center"
alignItems="center"
gutterSize="none"
>
<EuiFlexItem grow={false}>
<div {...provided.dragHandleProps}>
<EuiIcon
className="pipelineProcessorsEditor__form__dragAndDropList__grabIcon"
type="grab"
/>
</div>
</EuiFlexItem>
<EuiFlexItem>
<UseField<string>
path={item.path}
config={{
validations: textValidation
? [{ validator: textValidation }]
: undefined,
}}
readDefaultValueOnForm={!item.isNew}
>
{(field) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(
field
);
return (
<EuiFlexGroup gutterSize="none" alignItems="center">
<EuiFlexItem>
<EuiFieldText
isInvalid={isInvalid}
value={field.value}
onChange={field.onChange}
compressed
fullWidth
/>
</EuiFlexItem>
{typeof errorMessage === 'string' && (
<EuiFlexItem grow={false}>
<div className="pipelineProcessorsEditor__form__dragAndDropList__errorIcon">
<EuiIconTip
aria-label={errorMessage}
content={errorMessage}
type="alert"
color="danger"
/>
</div>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
}}
</UseField>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{value.length > 1 ? (
<EuiButtonIcon
aria-label={i18nTexts.removeItemButtonAriaLabel}
className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
iconType="minusInCircle"
color="danger"
onClick={() => onRemove(item.id)}
/>
) : (
// Render a no-op placeholder button
<EuiIcon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I didn't know about the "empty" icon 👍

className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
type="empty"
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
);
}}
</EuiDraggable>
);
})}
</EuiDroppable>
</EuiDragDropContext>
{value.length ? <EuiSpacer size="s" /> : null}
<EuiButtonEmpty iconType="plusInCircle" onClick={onAdd}>
{addLabel}
</EuiButtonEmpty>
</EuiPanel>
);
}

export const DragAndDropTextList = memo(
DragAndDropTextListComponent
) as typeof DragAndDropTextListComponent;
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

export { DragAndDropTextList } from './drag_and_drop_text_list';
export { XJsonEditor } from './xjson_editor';
export { TextEditor } from './text_editor';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.pipelineProcessorsEditor__form__textEditor {
&__panel {
box-shadow: none;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
getFieldValidityAndErrorMessage,
} from '../../../../../../shared_imports';

import './text_editor.scss';

interface Props {
field: FieldHook<string>;
editorProps: { [key: string]: any };
Expand All @@ -30,7 +32,11 @@ export const TextEditor: FunctionComponent<Props> = ({ field, editorProps }) =>
error={errorMessage}
fullWidth
>
<EuiPanel paddingSize="s" hasShadow={false}>
<EuiPanel
className="pipelineProcessorsEditor__form__textEditor__panel"
paddingSize="s"
hasShadow={false}
>
<CodeEditor value={value} onChange={setValue} {...(editorProps as any)} />
</EuiPanel>
</EuiFormRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,12 @@ export const ManageProcessorForm: FunctionComponent<Props> = ({
onSubmit: handleSubmit,
});

const { subscribe } = form;

useEffect(() => {
const subscription = form.subscribe(onFormUpdate);
const subscription = subscribe(onFormUpdate);
return subscription.unsubscribe;

// TODO: Address this issue
// For some reason adding `form` object to the dependencies array here is causing an
// infinite update loop.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [onFormUpdate]);
}, [onFormUpdate, subscribe]);

return (
<ViewComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,12 @@ export const ManageProcessorForm: FunctionComponent<Props> = memo(
<EuiButtonEmpty onClick={onClose}>{cancelButtonLabel}</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill data-test-subj="submitButton" onClick={form.submit}>
<EuiButton
disabled={(!form.isValid && form.isSubmitted) || form.isSubmitting}
fill
data-test-subj="submitButton"
onClick={form.submit}
>
{processor ? updateButtonLabel : addButtonLabel}
</EuiButton>
</EuiFlexItem>
Expand All @@ -229,8 +234,5 @@ export const ManageProcessorForm: FunctionComponent<Props> = memo(
</EuiFlyout>
</Form>
);
},
(previous, current) => {
return previous.processor === current.processor;
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
FieldConfig,
UseField,
fieldValidators,
useKibana,
} from '../../../../../../../shared_imports';

import { getProcessorDescriptor, mapProcessorTypeToDescriptor } from '../../../shared';
Expand Down Expand Up @@ -64,6 +65,10 @@ const typeConfig: FieldConfig<any, string> = {
};

export const ProcessorTypeField: FunctionComponent<Props> = ({ initialType }) => {
const {
services: { documentation },
} = useKibana();
const esDocUrl = documentation.getEsDocsBasePath();
return (
<UseField<string> config={typeConfig} defaultValue={initialType} path="type">
{(typeField) => {
Expand Down Expand Up @@ -107,7 +112,7 @@ export const ProcessorTypeField: FunctionComponent<Props> = ({ initialType }) =>
<EuiFormRow
label={typeField.label}
labelAppend={typeField.labelAppend}
helpText={typeof description === 'function' ? description() : description}
helpText={typeof description === 'function' ? description(esDocUrl) : description}
error={error}
isInvalid={isInvalid}
fullWidth
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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 from 'react';
import { mount } from 'enzyme';

import {
uiSettingsServiceMock,
i18nServiceMock,
} from '../../../../../../../../../../src/core/public/mocks';

import { Form, useForm, KibanaContextProvider } from '../../../../../../shared_imports';
import { Grok } from './grok';

// @ts-ignore
window.Worker = function () {
this.postMessage = () => {};
(this as any).terminate = () => {};
};

describe('<Grok/>', () => {
const setup = (props?: { defaultValue: Record<string, any> }) => {
function MyComponent() {
const { form } = useForm({ defaultValue: props?.defaultValue });
const i18n = i18nServiceMock.createStartContract();
return (
<KibanaContextProvider
services={{ uiSettings: uiSettingsServiceMock.createSetupContract() }}
>
<i18n.Context>
<Form form={form}>
<Grok />
</Form>
</i18n.Context>
</KibanaContextProvider>
);
}
return mount(<MyComponent />);
};

beforeAll(() => {
// disable all react-beautiful-dnd development warnings
(window as any)['__react-beautiful-dnd-disable-dev-warnings'] = true;
});

afterAll(() => {
// enable all react-beautiful-dnd development warnings
(window as any)['__react-beautiful-dnd-disable-dev-warnings'] = false;
});
test('smoke', () => {
setup({ defaultValue: { type: 'grok', fields: { patterns: ['test'] } } });
});
});
Loading