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 8 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
Expand Up @@ -17,18 +17,26 @@
* under the License.
*/

import { isEqual, get } from 'lodash';
import { useState, useEffect, useRef, useCallback } from 'react';
import { euiDragDropReorder } from '@elastic/eui';

import { FieldConfig, FormData, FieldHook } from '../types';
import { useFormContext } from '../form_context';
import { useField } from '../hooks';
import { unflattenObject } from '../lib';

interface Props {
interface Props<T extends unknown[] = unknown[]> {
path: string;
config?: FieldConfig<FormData, T>;
initialNumberOfItems?: number;
readDefaultValueOnForm?: boolean;
children: (args: {
field: FieldHook<T>;
items: ArrayItem[];
addItem: () => void;
removeItem: (id: number) => void;
moveItem: (sourceIdx: number, destinationIdx: number) => void;
}) => JSX.Element;
}

Expand All @@ -53,35 +61,63 @@ export interface ArrayItem {
*
* Look at the README.md for some examples.
*/
export const UseArray = ({
export const UseArray = <T extends unknown[] = unknown[]>({
path,
config,
initialNumberOfItems,
readDefaultValueOnForm = true,
children,
}: Props) => {
}: Props<T>) => {
const didMountRef = useRef(false);
const form = useFormContext();
const defaultValues = readDefaultValueOnForm && (form.getFieldDefaultValue(path) as any[]);
const uniqueId = useRef(0);

const getInitialItemsFromValues = (values: any[]): ArrayItem[] =>
values.map((_, index) => ({
const form = useFormContext();
const defaultValues = readDefaultValueOnForm
? (form.getFieldDefaultValue(path) as any[])
: undefined;
const fieldConfig: FieldConfig<any, T> & { initialValue?: T } =
config !== undefined
? { ...config }
: ({
...form.__readFieldConfigFromSchema(path),
} as Partial<FieldConfig<any, T>>);

if (defaultValues !== undefined) {
// update the form "defaultValue" ref object so when/if we reset the form we can go back to this value
form.__updateDefaultValueAt(path, defaultValues);

// Use the defaultValue prop as initial value
fieldConfig.initialValue = defaultValues as T;
} else {
if (readDefaultValueOnForm) {
// Read the field initial value from the "defaultValue" object passed to the form
fieldConfig.initialValue = (form.getFieldDefaultValue(path) as T) ?? fieldConfig.defaultValue;
}
}
const field = useField<T>(form, path, fieldConfig);
const { setValue } = field;

const getNewItemAtIndex = useCallback(
(index: number): ArrayItem => ({
id: uniqueId.current++,
path: `${path}[${index}]`,
isNew: false,
}));

const getNewItemAtIndex = (index: number): ArrayItem => ({
id: uniqueId.current++,
path: `${path}[${index}]`,
isNew: true,
});
isNew: true,
}),
[path]
);

const initialState = defaultValues
? getInitialItemsFromValues(defaultValues)
: new Array(initialNumberOfItems).fill('').map((_, i) => getNewItemAtIndex(i));
const [items, setItems] = useState<ArrayItem[]>(() => {
const getInitialItemsFromValues = (values: any[]): ArrayItem[] =>
values.map((_, index) => ({
id: uniqueId.current++,
path: `${path}[${index}]`,
isNew: false,
}));

const [items, setItems] = useState<ArrayItem[]>(initialState);
return defaultValues
? getInitialItemsFromValues(defaultValues)
: new Array(initialNumberOfItems).fill('').map((_, i) => getNewItemAtIndex(i));
});

const updatePaths = useCallback(
(_rows: ArrayItem[]) => {
Expand All @@ -96,19 +132,57 @@ export const UseArray = ({
[path]
);

const addItem = () => {
const addItem = useCallback(() => {
setItems((previousItems) => {
const itemIndex = previousItems.length;
return [...previousItems, getNewItemAtIndex(itemIndex)];
});
};
}, [setItems, getNewItemAtIndex]);

const removeItem = (id: number) => {
setItems((previousItems) => {
const updatedItems = previousItems.filter((item) => item.id !== id);
return updatePaths(updatedItems);
const removeItem = useCallback(
(id: number) => {
setItems((previousItems) => {
const updatedItems = previousItems.filter((item) => item.id !== id);
return updatePaths(updatedItems);
});
},
[setItems, updatePaths]
);

const moveItem = useCallback(
(sourceIdx: number, destinationIdx: number) => {
setItems((previousItems) => {
const reorderedItems = euiDragDropReorder(previousItems, sourceIdx, destinationIdx);
return updatePaths(reorderedItems);
});
},
[setItems, updatePaths]
);

useEffect(() => {
const subscription = form.subscribe((update) => {
const currentValues = update.data.raw[path];
const filteredFlattenedValues = Object.entries(update.data.raw).reduce(
(acc, [key, value]) => {
if (key.startsWith(path + '[')) {
return {
...acc,
[key]: value,
};
}
return acc;
},
{} as Record<string, any>
);
const unflattened = unflattenObject(filteredFlattenedValues);
const nextValues = (get(unflattened, path) as T) ?? [];
const shouldUpdate = !isEqual(nextValues, currentValues);
if (shouldUpdate) {
setValue(nextValues);
}
});
};
return subscription.unsubscribe;
}, [setValue, form, path]);

useEffect(() => {
if (didMountRef.current) {
Expand All @@ -120,5 +194,5 @@ export const UseArray = ({
}
}, [path, updatePaths]);

return children({ items, addItem, removeItem });
return children({ field, items, addItem, removeItem, moveItem });
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.pipelineProcessorsEditor__form__dragAndDropList {
&__panel {
box-shadow: none;
}

&__grabIcon {
margin-right: $euiSizeS;
}

&__removeButton {
margin-left: $euiSizeS;
}

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

import { UseField, ArrayItem } 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;
}

function DragAndDropTextListComponent({
value,
onMove,
onAdd,
onRemove,
addLabel,
}: 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 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}
componentProps={{ euiFieldProps: { compressed: true } }}
Copy link
Contributor

@sebelga sebelga Sep 7, 2020

Choose a reason for hiding this comment

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

As you are manually adding the EuiFieldText below with the props, you don't need to pass euiFieldProps props here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Of course, if you decide to render the validation below the field and use the TextField component then you will have to pass the props here 😊

readDefaultValueOnForm={!item.isNew}
>
{(patternField) => (
<EuiFieldText
value={patternField.value}
onChange={(e) => patternField.setValue(e.target.value)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Here you can simply use

onChange={patternField.onChange}

compressed
fullWidth
/>
)}
</UseField>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
iconType="minusInCircle"
color="danger"
onClick={() => onRemove(item.id)}
/>
</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
Loading