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

Save filter expression and Create filter set with given label name from add filter modal #7

Merged
37 changes: 36 additions & 1 deletion src/plugins/data/public/ui/filter_bar/filter_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { SavedQueriesItem } from './saved_queries_item';
import { FilterExpressionItem } from './filter_expression_item';

import { UI_SETTINGS } from '../../../common';
import { SavedQueryMeta } from '../saved_query_form';
import { SavedQueryService } from '../..';

interface Props {
filters: Filter[];
Expand All @@ -46,6 +48,9 @@ interface Props {
selectedSavedQueries?: SavedQuery[];
removeSelectedSavedQuery: (savedQuery: SavedQuery) => void;
onMultipleFiltersUpdated?: (filters: Filter[]) => void;
savedQueryService: SavedQueryService;
onFilterSave: (savedQueryMeta: SavedQueryMeta, saveAsNew?: boolean) => Promise<void>;
onFilterBadgeSave: (groupId: number, alias: string) => void;
}

const FilterBarUI = React.memo(function FilterBarUI(props: Props) {
Expand Down Expand Up @@ -98,7 +103,13 @@ const FilterBarUI = React.memo(function FilterBarUI(props: Props) {
}

function renderMultipleFilters() {
const firstDepthGroupedFilters = groupBy(props.multipleFilters, 'groupId');
const groupedByAlias = groupBy(props.multipleFilters, 'meta.alias');
const filtersWithoutLabel = groupedByAlias.null || groupedByAlias.undefined;
const labels = Object.keys(groupedByAlias).filter(
(key) => key !== 'null' && key !== 'undefined'
);

const firstDepthGroupedFilters = groupBy(filtersWithoutLabel, 'groupId');
const GroupBadge: JSX.Element[] = [];
for (const [groupId, groupedFilters] of Object.entries(firstDepthGroupedFilters)) {
const badge = (
Expand All @@ -110,10 +121,34 @@ const FilterBarUI = React.memo(function FilterBarUI(props: Props) {
onRemove={onRemoveFilterGroup}
onUpdate={onUpdateFilterGroup}
filtersGroupsCount={Object.entries(firstDepthGroupedFilters).length}
savedQueryService={props.savedQueryService}
onFilterSave={props.onFilterSave}
onFilterBadgeSave={props.onFilterBadgeSave}
/>
);
GroupBadge.push(badge);
}

let groupId: string;
labels.map((label) => {
// we should have same groupIds on our labeled filters group
groupId = (groupedByAlias[label][0] as any).groupId;
groupedByAlias[label].forEach((filter) => ((filter as any).groupId = groupId));
const labelBadge = (
<FilterExpressionItem
groupId={groupId}
groupedFilters={groupedByAlias[label]}
indexPatterns={props?.indexPatterns}
onClick={() => {}}
onRemove={onRemoveFilterGroup}
onUpdate={onUpdateFilterGroup}
filtersGroupsCount={1}
customLabel={label}
/>
);
GroupBadge.push(labelBadge);
});

return GroupBadge;
}

Expand Down
71 changes: 67 additions & 4 deletions src/plugins/data/public/ui/filter_bar/filter_expression_item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
EuiTextColor,
EuiPopover,
EuiContextMenu,
EuiIcon,
EuiContextMenuPanelDescriptor,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { groupBy } from 'lodash';
Expand All @@ -22,6 +24,8 @@ import { FILTERS } from '../../../common';
import { existsOperator, isOneOfOperator } from './filter_editor/lib/filter_operators';
import { IIndexPattern } from '../..';
import { getDisplayValueFromFilter, getIndexPatternFromFilter } from '../../query';
import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form';
import { SavedQueryService } from '../..';

const FILTER_ITEM_OK = '';
const FILTER_ITEM_WARNING = 'warn';
Expand All @@ -46,6 +50,10 @@ interface Props {
groupId: string;
filtersGroupsCount: number;
onUpdate?: (filters: Filter[], groupId: string, toggleNegate: boolean) => void;
savedQueryService?: SavedQueryService;
onFilterSave?: (savedQueryMeta: SavedQueryMeta, saveAsNew?: boolean) => Promise<void>;
customLabel?: string;
onFilterBadgeSave?: (groupId: number, alias: string) => void;
}

export const FilterExpressionItem: FC<Props> = ({
Expand All @@ -56,8 +64,17 @@ export const FilterExpressionItem: FC<Props> = ({
groupId,
filtersGroupsCount,
onUpdate,
savedQueryService,
onFilterSave,
customLabel,
onFilterBadgeSave,
}: Props) => {
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
const filters: Filter[] = groupedFilters.map((filter: Filter) => ({
$state: filter.$state,
meta: filter.meta,
query: filter.query,
}));
function handleBadgeClick() {
// if (e.shiftKey) {
// onToggleDisabled();
Expand Down Expand Up @@ -149,7 +166,7 @@ export const FilterExpressionItem: FC<Props> = ({
},
];

return [
const panels: EuiContextMenuPanelDescriptor[] = [
{
id: 0,
items: mainPanelItems,
Expand All @@ -172,6 +189,45 @@ export const FilterExpressionItem: FC<Props> = ({
// ),
// },
];

if (!customLabel && savedQueryService && onFilterSave && onFilterBadgeSave) {
const saveAsFilterPanelItem = {
name: i18n.translate('data.filter.filterBar.saveAsFilterButtonLabel', {
defaultMessage: `Save as filter`,
}),
icon: 'save',
panel: 2,
'data-test-subj': 'saveAsFilter',
};

const saveAsFilterPanelContent = {
id: 2,
title: i18n.translate('data.filter.filterBar.saveAsFilterButtonLabel', {
defaultMessage: `Save as filter`,
}),
content: (
<div style={{ padding: 16 }}>
<SaveQueryForm
savedQueryService={savedQueryService}
onSave={(savedQueryMeta) => {
onFilterSave(savedQueryMeta, true);
setIsPopoverOpen(false);
}}
onClose={() => setIsPopoverOpen(false)}
showTimeFilterOption={false}
showFilterOption={false}
filters={filters}
onFilterBadgeSave={(alias: string) => onFilterBadgeSave(Number(groupId), alias)}
/>
</div>
),
};

mainPanelItems.splice(mainPanelItems.length - 1, 0, saveAsFilterPanelItem);
panels.push(saveAsFilterPanelContent);
}

return panels;
}
/**
* Checks if filter field exists in any of the index patterns provided,
Expand Down Expand Up @@ -406,9 +462,16 @@ export const FilterExpressionItem: FC<Props> = ({
onClick={handleBadgeClick}
>
<div ref={ref}>
{filterExpression.map((expression) => {
return <>{expression}</>;
})}
{customLabel ? (
<>
<EuiIcon type="save" />
{customLabel}
</>
) : (
filterExpression.map((expression) => {
return <>{expression}</>;
})
)}
</div>
</EuiBadge>
</EuiFlexItem>
Expand Down
19 changes: 19 additions & 0 deletions src/plugins/data/public/ui/query_string_input/add_filter_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { GenericComboBox } from '../filter_bar/filter_editor/generic_combo_box';
import { PhraseValueInput } from '../filter_bar/filter_editor/phrase_value_input';
import { PhrasesValuesInput } from '../filter_bar/filter_editor/phrases_values_input';
import { RangeValueInput } from '../filter_bar/filter_editor/range_value_input';
import { SavedQueryMeta } from '../saved_query_form';

import { IIndexPattern, IFieldType } from '../..';

Expand Down Expand Up @@ -95,6 +96,7 @@ export function AddFilterModal({
timeRangeForSuggestionsOverride,
savedQueryManagement,
initialAddFilterMode,
saveFilters,
}: {
onSubmit: (filters: Filter[]) => void;
onMultipleFiltersSubmit: (filters: FilterGroup[], buildFilters: Filter[]) => void;
Expand All @@ -105,6 +107,7 @@ export function AddFilterModal({
timeRangeForSuggestionsOverride?: boolean;
savedQueryManagement?: JSX.Element;
initialAddFilterMode?: string;
saveFilters: (savedQueryMeta: SavedQueryMeta) => void;
}) {
const [selectedIndexPattern, setSelectedIndexPattern] = useState(
getIndexPatternFromFilter(filter, indexPatterns)
Expand Down Expand Up @@ -367,6 +370,13 @@ export function AddFilterModal({
$state.store
);
onSubmit([builtCustomFilter]);
saveFilters({
title: customLabel,
description: '',
shouldIncludeFilters: false,
shouldIncludeTimefilter: false,
filters: [builtCustomFilter],
});
} else if (addFilterMode === 'quick_form' && selectedIndexPattern) {
const builtFilters = localFilters.map((localFilter) => {
if (localFilter.field && localFilter.operator) {
Expand All @@ -388,6 +398,15 @@ export function AddFilterModal({
) as Filter[];
// onSubmit(finalFilters);
onMultipleFiltersSubmit(localFilters, finalFilters);
if (alias) {
saveFilters({
title: customLabel,
description: '',
shouldIncludeFilters: false,
shouldIncludeTimefilter: false,
filters: finalFilters,
});
}
}
} else if (addFilterMode === 'saved_filters') {
applySavedQueries();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { NoDataPopover } from './no_data_popover';
import { shallowEqual } from '../../utils/shallow_equal';
import { SavedQuery } from '../..';
import { AddFilterModal, FilterGroup } from './add_filter_modal';
import { SavedQueryMeta } from '../saved_query_form';

const SuperDatePicker = React.memo(
EuiSuperDatePicker as any
Expand All @@ -49,6 +50,7 @@ const QueryStringInput = withKibana(QueryStringInputUI);
// @internal
export interface QueryBarTopRowProps {
filters: Filter[];
multipleFilters: Filter[];
onFiltersUpdated?: (filters: Filter[]) => void;
onMultipleFiltersUpdated?: (filters: Filter[]) => void;
applySelectedSavedQueries?: () => void;
Expand Down Expand Up @@ -85,6 +87,7 @@ export interface QueryBarTopRowProps {
toggleAddFilterModal?: (value: boolean) => void;
isAddFilterModalOpen?: boolean;
addFilterMode?: string;
onNewFiltersSave: (savedQueryMeta: SavedQueryMeta) => void;
}

const SharingMetaFields = React.memo(function SharingMetaFields({
Expand Down Expand Up @@ -394,18 +397,30 @@ export const QueryBarTopRow = React.memo(
}

function onAddMultipleFiltersANDOR(selectedFilters: FilterGroup[], buildFilters: Filter[]) {
const lastFilter: any = props.multipleFilters[props.multipleFilters.length - 1];
const mappedFilters = mapAndFlattenFilters(buildFilters);
if (lastFilter !== undefined) lastFilter.relationship = 'AND';
const mergedFilters = mappedFilters.map((filter, idx) => {
let groupId = selectedFilters[idx].groupId;
let id = selectedFilters[idx].id;
// groupId starts from 1; id starts from 0

if (lastFilter !== undefined) {
groupId += lastFilter.groupId;
id += lastFilter.id + 1;
}

return {
...filter,
groupId: selectedFilters[idx].groupId,
id: selectedFilters[idx].id,
groupId,
id,
relationship: selectedFilters[idx].relationship,
subGroupId: selectedFilters[idx].subGroupId,
};
});
props.toggleAddFilterModal?.(false);
props?.onMultipleFiltersUpdated?.(mergedFilters);
props?.onMultipleFiltersUpdated?.([...props.multipleFilters, ...mergedFilters]);
// props?.onMultipleFiltersUpdated?.(mergedFilters);

const filters = [...props.filters, ...buildFilters];
props?.onFiltersUpdated?.(filters);
Expand Down Expand Up @@ -448,6 +463,7 @@ export const QueryBarTopRow = React.memo(
timeRangeForSuggestionsOverride={props.timeRangeForSuggestionsOverride}
savedQueryManagement={props.savedQueryManagement}
initialAddFilterMode={props.addFilterMode}
saveFilters={props.onNewFiltersSave}
/>
)}
</EuiFlexItem>
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/data/public/ui/saved_query_form/save_query_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React, { useEffect, useState, useCallback } from 'react';
import { EuiButton, EuiForm, EuiFormRow, EuiFieldText, EuiSwitch, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Filter } from '@kbn/es-query';
import { sortBy, isEqual } from 'lodash';
import { SavedQuery, SavedQueryService } from '../..';

Expand All @@ -19,6 +20,8 @@ interface Props {
onClose: () => void;
showFilterOption: boolean | undefined;
showTimeFilterOption: boolean | undefined;
filters?: Filter[];
onFilterBadgeSave?: (alias: string) => void;
}

export interface SavedQueryMeta {
Expand All @@ -27,6 +30,7 @@ export interface SavedQueryMeta {
description: string;
shouldIncludeFilters: boolean;
shouldIncludeTimefilter: boolean;
filters?: Filter[];
}

export function SaveQueryForm({
Expand All @@ -36,6 +40,8 @@ export function SaveQueryForm({
onClose,
showFilterOption = true,
showTimeFilterOption = true,
filters,
onFilterBadgeSave,
}: Props) {
const [title, setTitle] = useState(savedQuery?.attributes.title ?? '');
const [enabledSaveButton, setEnabledSaveButton] = useState(Boolean(savedQuery));
Expand Down Expand Up @@ -111,7 +117,9 @@ export function SaveQueryForm({
description,
shouldIncludeFilters,
shouldIncludeTimefilter,
filters,
});
if (onFilterBadgeSave) onFilterBadgeSave(title);
}
}, [
validate,
Expand All @@ -121,6 +129,8 @@ export function SaveQueryForm({
description,
shouldIncludeFilters,
shouldIncludeTimefilter,
filters,
onFilterBadgeSave,
]);

const onInputChange = useCallback((event) => {
Expand Down
Loading