Skip to content

Commit

Permalink
[Discover] Runtime Fields Editor integration: Add + Delete operation (#…
Browse files Browse the repository at this point in the history
…96762)

* [Discover] Updating a functional test

* [Discover] Support for edit operation

* Fix unit tests

* Fix typescript

* Fixing failing functional test

* Fixing wrongly commented line

* Uncomment accidentally commented line

* Reintroducing accidnetally removed unit test

* Trigger data refetch onSave

* Remove refreshAppState variable

* Bundling observers together

* Clean state before refetch

* Update formatting in data grid

* [Discover] Runtime fields editor : add operation

* [Discover] Updating a functional test

* Adding a functional test

* Fixing package.json

* Reset fieldCount after data fetch

* [Discover] Updating a functional test

* Don't allow editing of unmapped fields

* Add functionality

* Fix issues with mobile display

* Allow editing if it's a runtime field

* Add a functional test

* [Discover] Updating a functional test

* Add functional test

* Remove unnecessary debugger statement

* Add more tests

* Add delete functionality

* Include runtimeFields in doc search

* Add another functional test

* [Discover] Updating a functional test

* Fix failing i18n check

* Fix package.json

* Addressing PR comments

* Addressing design input

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
Maja Grubic and kibanamachine authored Apr 15, 2021
1 parent 5686ec9 commit 6501b39
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('Test of <Doc /> helper / hook', () => {
],
},
},
"runtime_mappings": Object {},
"script_fields": Array [],
"stored_fields": Array [],
}
Expand Down Expand Up @@ -79,6 +80,55 @@ describe('Test of <Doc /> helper / hook', () => {
],
},
},
"runtime_mappings": Object {},
"script_fields": Array [],
"stored_fields": Array [],
}
`);
});

test('buildSearchBody with runtime fields', () => {
const indexPattern = {
getComputedFields: () => ({
storedFields: [],
scriptFields: [],
docvalueFields: [],
runtimeFields: {
myRuntimeField: {
type: 'double',
script: {
source: 'emit(10.0)',
},
},
},
}),
} as any;
const actual = buildSearchBody('1', indexPattern, true);
expect(actual).toMatchInlineSnapshot(`
Object {
"_source": false,
"docvalue_fields": Array [],
"fields": Array [
Object {
"field": "*",
"include_unmapped": "true",
},
],
"query": Object {
"ids": Object {
"values": Array [
"1",
],
},
},
"runtime_mappings": Object {
"myRuntimeField": Object {
"script": Object {
"source": "emit(10.0)",
},
"type": "double",
},
},
"script_fields": Array [],
"stored_fields": Array [],
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function buildSearchBody(
useNewFieldsApi: boolean
): Record<string, any> {
const computedFields = indexPattern.getComputedFields();

const runtimeFields = indexPattern.getComputedFields().runtimeFields;
return {
query: {
ids: {
Expand All @@ -42,6 +42,7 @@ export function buildSearchBody(
fields: useNewFieldsApi ? [{ field: '*', include_unmapped: 'true' }] : undefined,
script_fields: computedFields.scriptFields,
docvalue_fields: computedFields.docvalueFields,
runtime_mappings: useNewFieldsApi && runtimeFields ? runtimeFields : {}, // needed for index pattern runtime fields in a single doc view
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,17 @@ export interface DiscoverFieldProps {

multiFields?: Array<{ field: IndexPatternField; isSelected: boolean }>;

/**
* Callback to edit a runtime field from index pattern
* @param fieldName name of the field to edit
*/
onEditField?: (fieldName: string) => void;

/**
* Callback to delete a runtime field from index pattern
* @param fieldName name of the field to delete
*/
onDeleteField?: (fieldName: string) => void;
}

export function DiscoverField({
Expand All @@ -87,6 +97,7 @@ export function DiscoverField({
trackUiMetric,
multiFields,
onEditField,
onDeleteField,
}: DiscoverFieldProps) {
const addLabelAria = i18n.translate('discover.fieldChooser.discoverField.addButtonAriaLabel', {
defaultMessage: 'Add {field} to table',
Expand Down Expand Up @@ -255,6 +266,7 @@ export function DiscoverField({
};

const fieldInfoIcon = getFieldInfoIcon();

const shouldRenderMultiFields = !!multiFields;
const renderMultiFields = () => {
if (!multiFields) {
Expand Down Expand Up @@ -289,13 +301,15 @@ export function DiscoverField({
const isRuntimeField = Boolean(indexPattern.getFieldByName(field.name)?.runtimeField);
const isUnknownField = field.type === 'unknown' || field.type === 'unknown_selected';
const canEditField = onEditField && (!isUnknownField || isRuntimeField);
const displayNameGrow = canEditField ? 9 : 10;
const canDeleteField = onDeleteField && isRuntimeField;
const popoverTitle = (
<EuiPopoverTitle style={{ textTransform: 'none' }} className="eui-textBreakWord">
<EuiFlexGroup responsive={false}>
<EuiFlexItem grow={displayNameGrow}>{field.displayName}</EuiFlexItem>
<EuiFlexGroup responsive={false} gutterSize="s">
<EuiFlexItem grow={true}>
<h5>{field.displayName}</h5>
</EuiFlexItem>
{canEditField && (
<EuiFlexItem grow={1} data-test-subj="discoverFieldListPanelEditItem">
<EuiFlexItem grow={false} data-test-subj="discoverFieldListPanelEditItem">
<EuiButtonIcon
onClick={() => {
if (onEditField) {
Expand All @@ -311,6 +325,29 @@ export function DiscoverField({
/>
</EuiFlexItem>
)}
{canDeleteField && (
<EuiFlexItem grow={false} data-test-subj="discoverFieldListPanelDeleteItem">
<EuiToolTip
content={i18n.translate('discover.fieldChooser.discoverField.deleteFieldLabel', {
defaultMessage: 'Delete index pattern field',
})}
>
<EuiButtonIcon
onClick={() => {
if (onDeleteField) {
onDeleteField(field.name);
}
}}
iconType="trash"
data-test-subj={`discoverFieldListPanelDelete-${field.name}`}
color="danger"
aria-label={i18n.translate('discover.fieldChooser.discoverField.deleteFieldLabel', {
defaultMessage: 'Delete index pattern field',
})}
/>
</EuiToolTip>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiPopoverTitle>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@
opacity: 1; /* 2 */
}
}

.dscSidebar__indexPatternSwitcher {
min-width: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import {
EuiSpacer,
EuiNotificationBadge,
EuiPageSideBar,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiPopover,
EuiButtonIcon,
useResizeObserver,
} from '@elastic/eui';

Expand Down Expand Up @@ -51,7 +55,7 @@ export interface DiscoverSidebarProps extends DiscoverSidebarResponsiveProps {
setFieldFilter: (next: FieldFilterState) => void;

/**
* Callback to close the flyout sidebar rendered in a flyout, close flyout
* Callback to close the flyout if sidebar is rendered in a flyout
*/
closeFlyout?: () => void;

Expand Down Expand Up @@ -88,7 +92,8 @@ export function DiscoverSidebar({
closeFlyout,
}: DiscoverSidebarProps) {
const [fields, setFields] = useState<IndexPatternField[] | null>(null);
const { indexPatternFieldEditor } = services;
const [isAddIndexPatternFieldPopoverOpen, setIsAddIndexPatternFieldPopoverOpen] = useState(false);
const { indexPatternFieldEditor, core } = services;
const indexPatternFieldEditPermission = indexPatternFieldEditor?.userPermissions.editIndexPattern();
const canEditIndexPatternField = !!indexPatternFieldEditPermission && useNewFieldsApi;
const [scrollContainer, setScrollContainer] = useState<Element | null>(null);
Expand Down Expand Up @@ -224,6 +229,37 @@ export function DiscoverSidebar({
return map;
}, [fields, useNewFieldsApi, selectedFields]);

const deleteField = useMemo(
() =>
canEditIndexPatternField && selectedIndexPattern
? async (fieldName: string) => {
const ref = indexPatternFieldEditor.openDeleteModal({
ctx: {
indexPattern: selectedIndexPattern,
},
fieldName,
onDelete: async () => {
onEditRuntimeField();
},
});
if (setFieldEditorRef) {
setFieldEditorRef(ref);
}
if (closeFlyout) {
closeFlyout();
}
}
: undefined,
[
selectedIndexPattern,
canEditIndexPatternField,
setFieldEditorRef,
closeFlyout,
onEditRuntimeField,
indexPatternFieldEditor,
]
);

const getPaginated = useCallback(
(list) => {
return list.slice(0, fieldsToRender);
Expand All @@ -237,7 +273,7 @@ export function DiscoverSidebar({
return null;
}

const editField = (fieldName: string) => {
const editField = (fieldName?: string) => {
if (!canEditIndexPatternField) {
return;
}
Expand All @@ -258,6 +294,10 @@ export function DiscoverSidebar({
}
};

const addField = () => {
editField(undefined);
};

if (useFlyout) {
return (
<section
Expand All @@ -277,6 +317,64 @@ export function DiscoverSidebar({
);
}

const indexPatternActions = (
<EuiPopover
panelPaddingSize="s"
isOpen={isAddIndexPatternFieldPopoverOpen}
closePopover={() => {
setIsAddIndexPatternFieldPopoverOpen(false);
}}
ownFocus
data-test-subj="discover-addRuntimeField-popover"
button={
<EuiButtonIcon
color="text"
iconType="boxesHorizontal"
data-test-subj="discoverIndexPatternActions"
aria-label={i18n.translate('discover.fieldChooser.indexPatterns.actionsPopoverLabel', {
defaultMessage: 'Index pattern settings',
})}
onClick={() => {
setIsAddIndexPatternFieldPopoverOpen(!isAddIndexPatternFieldPopoverOpen);
}}
/>
}
>
<EuiContextMenuPanel
size="s"
items={[
<EuiContextMenuItem
key="add"
icon="indexOpen"
data-test-subj="indexPattern-add-field"
onClick={() => {
setIsAddIndexPatternFieldPopoverOpen(false);
addField();
}}
>
{i18n.translate('discover.fieldChooser.indexPatterns.addFieldButton', {
defaultMessage: 'Add field to index pattern',
})}
</EuiContextMenuItem>,
<EuiContextMenuItem
key="manage"
icon="indexSettings"
onClick={() => {
setIsAddIndexPatternFieldPopoverOpen(false);
core.application.navigateToApp('management', {
path: `/kibana/indexPatterns/patterns/${selectedIndexPattern.id}`,
});
}}
>
{i18n.translate('discover.fieldChooser.indexPatterns.manageFieldButton', {
defaultMessage: 'Manage index pattern fields',
})}
</EuiContextMenuItem>,
]}
/>
</EuiPopover>
);

return (
<EuiPageSideBar
className="dscSidebar"
Expand All @@ -294,14 +392,19 @@ export function DiscoverSidebar({
responsive={false}
>
<EuiFlexItem grow={false}>
<DiscoverIndexPattern
config={config}
selectedIndexPattern={selectedIndexPattern}
indexPatternList={sortBy(indexPatternList, (o) => o.attributes.title)}
indexPatterns={indexPatterns}
state={state}
setAppState={setAppState}
/>
<EuiFlexGroup direction="row" alignItems="center" gutterSize="s">
<EuiFlexItem grow={true} className="dscSidebar__indexPatternSwitcher">
<DiscoverIndexPattern
config={config}
selectedIndexPattern={selectedIndexPattern}
indexPatternList={sortBy(indexPatternList, (o) => o.attributes.title)}
indexPatterns={indexPatterns}
state={state}
setAppState={setAppState}
/>
</EuiFlexItem>
{useNewFieldsApi && <EuiFlexItem grow={false}>{indexPatternActions}</EuiFlexItem>}
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<form>
Expand Down Expand Up @@ -370,6 +473,7 @@ export function DiscoverSidebar({
trackUiMetric={trackUiMetric}
multiFields={multiFields?.get(field.name)}
onEditField={canEditIndexPatternField ? editField : undefined}
onDeleteField={canEditIndexPatternField ? deleteField : undefined}
/>
</li>
);
Expand Down Expand Up @@ -428,6 +532,7 @@ export function DiscoverSidebar({
trackUiMetric={trackUiMetric}
multiFields={multiFields?.get(field.name)}
onEditField={canEditIndexPatternField ? editField : undefined}
onDeleteField={canEditIndexPatternField ? deleteField : undefined}
/>
</li>
);
Expand Down Expand Up @@ -455,6 +560,7 @@ export function DiscoverSidebar({
trackUiMetric={trackUiMetric}
multiFields={multiFields?.get(field.name)}
onEditField={canEditIndexPatternField ? editField : undefined}
onDeleteField={canEditIndexPatternField ? deleteField : undefined}
/>
</li>
);
Expand Down
Loading

0 comments on commit 6501b39

Please sign in to comment.