-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[RAM] Implement global alerts page #175143
Changes from 33 commits
b373792
1ca128d
5ca182e
bc455f8
73e4212
b4c2b4c
c943197
56363bd
cf8b85c
428be5e
fed992b
cf338c8
1799015
a0ae54c
f1f4265
3162700
aad3409
cdcdcf9
6348918
b35a559
41c585c
865e958
9359c28
029a525
8090de3
4fe65cd
6cd7612
63660dc
259c2b1
eb7daae
d5a0c1e
bfbaa93
da37428
51c4673
a75223b
876d9ab
a10e890
35b8fce
3bdf33a
2030bce
712a86b
07bea51
d93205e
9afc692
fa603a4
42bbd4a
a6b0fce
cf31c41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
* Side Public License, v 1. | ||
*/ | ||
|
||
import React, { useState, useRef, useEffect, useCallback } from 'react'; | ||
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; | ||
import { isEqual } from 'lodash'; | ||
import { | ||
EuiContextMenuPanelDescriptor, | ||
|
@@ -24,6 +24,7 @@ import { | |
toggleFilterNegated, | ||
pinFilter, | ||
unpinFilter, | ||
compareFilters, | ||
} from '@kbn/es-query'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { METRIC_TYPE } from '@kbn/analytics'; | ||
|
@@ -34,6 +35,8 @@ import { | |
UI_SETTINGS, | ||
} from '@kbn/data-plugin/common'; | ||
import type { SavedQueryService, SavedQuery } from '@kbn/data-plugin/public'; | ||
import { EuiContextMenuPanelItemDescriptor } from '@elastic/eui/src/components/context_menu/context_menu'; | ||
import { isQuickFiltersGroup, QuickFiltersMenuItem } from './quick_filters'; | ||
import type { IUnifiedSearchPluginServices } from '../types'; | ||
import { fromUser } from './from_user'; | ||
import { QueryLanguageSwitcher } from './language_switcher'; | ||
|
@@ -134,10 +137,21 @@ export const strings = { | |
i18n.translate('unifiedSearch.filter.options.filterLanguageLabel', { | ||
defaultMessage: 'Filter language', | ||
}), | ||
getQuickFiltersLabel: () => | ||
i18n.translate('unifiedSearch.filter.options.quickFiltersLabel', { | ||
defaultMessage: 'Quick filters', | ||
}), | ||
}; | ||
|
||
type ContextMenuItem = | ||
| EuiContextMenuPanelDescriptor | ||
| (EuiContextMenuPanelItemDescriptor & { | ||
width?: number; | ||
}); | ||
|
||
export interface QueryBarMenuPanelsProps { | ||
filters?: Filter[]; | ||
quickFilters: QuickFiltersMenuItem[]; | ||
savedQuery?: SavedQuery; | ||
language: string; | ||
dateRangeFrom?: string; | ||
|
@@ -162,6 +176,7 @@ export interface QueryBarMenuPanelsProps { | |
|
||
export function QueryBarMenuPanels({ | ||
filters, | ||
quickFilters, | ||
savedQuery, | ||
language, | ||
dateRangeFrom, | ||
|
@@ -192,6 +207,63 @@ export function QueryBarMenuPanels({ | |
const [hasFiltersOrQuery, setHasFiltersOrQuery] = useState(false); | ||
const [savedQueryHasChanged, setSavedQueryHasChanged] = useState(false); | ||
|
||
const applyQuickFilter = useCallback( | ||
(filter: Filter) => { | ||
if (!filters?.some((f) => compareFilters(f, filter))) { | ||
onFiltersUpdated?.([...(filters ?? []), filter]); | ||
} | ||
closePopover(); | ||
}, | ||
[closePopover, filters, onFiltersUpdated] | ||
); | ||
|
||
const quickFiltersContextMenuData = useMemo(() => { | ||
if (showFilterBar && quickFilters.length > 0) { | ||
// EuiContextMenu expects a flattened panels structure so here we collect all | ||
// the nested panels in a linear list | ||
const panels = [] as EuiContextMenuPanelDescriptor[]; | ||
const quickFiltersItemToContextMenuItem = (qf: QuickFiltersMenuItem) => { | ||
if (isQuickFiltersGroup(qf)) { | ||
const panelId = `quick-filters-panel-${panels.length}`; | ||
panels.push({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Building the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
id: panelId, | ||
title: qf.groupName, | ||
items: qf.items.map( | ||
quickFiltersItemToContextMenuItem | ||
) as EuiContextMenuPanelItemDescriptor[], | ||
'data-test-subj': panelId, | ||
} as EuiContextMenuPanelDescriptor); | ||
return { | ||
name: qf.groupName, | ||
icon: qf.icon ?? 'filterInCircle', | ||
panel: panelId, | ||
'data-test-subj': `quick-filters-item-${qf.groupName}`, | ||
}; | ||
} else { | ||
return { | ||
...qf, | ||
icon: qf.icon ?? 'filterInCircle', | ||
onClick: () => { | ||
applyQuickFilter(qf.filter); | ||
}, | ||
'data-test-subj': `quick-filters-item-${qf.name}`, | ||
}; | ||
} | ||
}; | ||
return { | ||
items: quickFilters.map( | ||
quickFiltersItemToContextMenuItem | ||
) as EuiContextMenuPanelItemDescriptor[], | ||
panels, | ||
}; | ||
} else { | ||
return { | ||
items: [], | ||
panels: [], | ||
}; | ||
} | ||
}, [applyQuickFilter, quickFilters, showFilterBar]); | ||
|
||
useEffect(() => { | ||
const fetchSavedQueries = async () => { | ||
cancelPendingListingRequest.current(); | ||
|
@@ -320,7 +392,7 @@ export function QueryBarMenuPanels({ | |
const luceneLabel = strings.getLuceneLanguageName(); | ||
const kqlLabel = strings.getKqlLanguageName(); | ||
|
||
const filtersRelatedPanels = [ | ||
const filtersRelatedPanels: ContextMenuItem[] = [ | ||
{ | ||
name: strings.getOptionsAddFilterButtonLabel(), | ||
icon: 'plus', | ||
|
@@ -337,7 +409,7 @@ export function QueryBarMenuPanels({ | |
}, | ||
]; | ||
|
||
const queryAndFiltersRelatedPanels = [ | ||
const queryAndFiltersRelatedPanels: ContextMenuItem[] = [ | ||
{ | ||
name: savedQuery | ||
? strings.getLoadOtherFilterSetLabel() | ||
|
@@ -359,7 +431,7 @@ export function QueryBarMenuPanels({ | |
{ isSeparator: true }, | ||
]; | ||
|
||
const items = []; | ||
const items: ContextMenuItem[] = []; | ||
// apply to all actions are only shown when there are filters | ||
if (showFilterBar) { | ||
items.push(...filtersRelatedPanels); | ||
|
@@ -385,6 +457,11 @@ export function QueryBarMenuPanels({ | |
{ isSeparator: true } | ||
); | ||
} | ||
|
||
if (showFilterBar && quickFilters.length > 0) { | ||
items.push(...[...quickFiltersContextMenuData.items, { isSeparator: true } as const]); | ||
} | ||
|
||
// saved queries actions are only shown when the showQueryInput and showFilterBar is true | ||
if (showQueryInput && showFilterBar) { | ||
items.push(...queryAndFiltersRelatedPanels); | ||
|
@@ -513,6 +590,7 @@ export function QueryBarMenuPanels({ | |
width: 400, | ||
content: <div>{manageFilterSetComponent}</div>, | ||
}, | ||
...quickFiltersContextMenuData.panels, | ||
] as EuiContextMenuPanelDescriptor[]; | ||
|
||
if (hiddenPanelOptions && hiddenPanelOptions.length > 0) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { Filter } from '@kbn/es-query'; | ||
import { EuiContextMenuPanelItemDescriptor } from '@elastic/eui/src/components/context_menu/context_menu'; | ||
|
||
type BaseContextMenuItem = Omit<EuiContextMenuPanelItemDescriptor, 'name' | 'title'>; | ||
|
||
export interface QuickFilter extends BaseContextMenuItem { | ||
name: string; | ||
filter: Filter; | ||
} | ||
|
||
export interface QuickFiltersGroup extends BaseContextMenuItem { | ||
groupName: string; | ||
items: QuickFiltersMenuItem[]; | ||
} | ||
|
||
export type QuickFiltersMenuItem = QuickFiltersGroup | QuickFilter; | ||
|
||
export const isQuickFiltersGroup = ( | ||
quickFiltersMenuItem: QuickFiltersMenuItem | ||
): quickFiltersMenuItem is QuickFiltersGroup => 'items' in quickFiltersMenuItem; |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanx a lot for the contribution. It seems as a very nice enhancement but it is very specific for the alerts stack page so I would like to make this more generic in unified search. I suggest:
In that way other consumers can use the same logic to display panel items in than menu without the need to integrating this inside unified search Let me know if you have any questions! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your review @stratoula! 😊 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I think that this
should come from the consumers of unified search. So after the proposed changes it should look
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I mean is that { name: '', filter: {} } render top-level menu items like these: whereas items like { groupName: '', items: [...] } render menu items that link to a panel with sub-items (see The additionalQueryBarMenuItems={[
{ name: '' }, // Top-level
{ title/groupName: '', items: [{ name: '' }] }, // Nested in subpanel
]} or let the consumer handle the association between panels and items: additionalQueryBarMenuItems={[
{ name: '' }, // Top-level
{ name: '', panel: 1 }, // Link to panel
]}
additionalQueryBarMenuPanels={[
{ id: 1, title: '', items: [...] },
{ id: 2, title: '', items: [...] },
]}
// or alternatively
additionalQueryBarMenuItems={{
items: [...],
panels: [...],
}} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry If I was not very clear. I want this additionalQueryBarMenuItemsData to be built outside Unified search. So let's go with this
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This map function isn't reused so I don't think it adds readability to define it as a
const
. Wrapping it withinquickFilters.map
tells me more immediately what this function is being used for.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with you, but in this case the function is recursive so it's actually used more than once:
kibana/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx
Line 232 in 41c585c
kibana/src/plugins/unified_search/public/query_string_input/query_bar_menu_panels.tsx
Line 254 in 41c585c