diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx new file mode 100644 index 00000000000000..7e11a4ee8759a0 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx @@ -0,0 +1,128 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { httpServiceMock, notificationServiceMock } from '@kbn/core/public/mocks'; +import { Filter } from '@kbn/es-query'; +import { useLoadRuleTypesQuery, useAlertsDataView, useRuleAADFields } from '../common/hooks'; +import { AlertsSearchBar } from '.'; + +jest.mock('../common/hooks/use_load_rule_types_query'); +jest.mock('../common/hooks/use_rule_aad_fields'); +jest.mock('../common/hooks/use_alerts_data_view'); + +jest.mocked(useAlertsDataView).mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-*', + fields: [ + { + name: 'event.action', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + }, +}); + +jest.mocked(useLoadRuleTypesQuery).mockReturnValue({ + ruleTypesState: { + isInitialLoad: false, + data: new Map(), + isLoading: false, + error: null, + }, + authorizedToReadAnyRules: false, + hasAnyAuthorizedRuleType: false, + authorizedRuleTypes: [], + authorizedToCreateAnyRules: false, + isSuccess: false, +}); + +jest.mocked(useRuleAADFields).mockReturnValue({ + aadFields: [], + loading: false, +}); + +const mockDataPlugin = dataPluginMock.createStartContract(); +const unifiedSearchBarMock = jest.fn().mockImplementation((props) => ( + +)); +const httpMock = httpServiceMock.createStartContract(); +const toastsMock = notificationServiceMock.createStartContract().toasts; + +describe('AlertsSearchBar', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', () => { + render( + + ); + expect(screen.getByTestId('querySubmitButton')).toBeInTheDocument(); + }); + + it('renders initial filters correctly', () => { + const filters = [ + { + meta: { + negate: false, + alias: null, + disabled: false, + type: 'custom', + key: 'query', + }, + query: { match_phrase: { 'host.name': 'testValue' } }, + $state: { store: 'appState' }, + }, + ] as Filter[]; + + render( + + ); + + expect(mockDataPlugin.query.filterManager.addFilters).toHaveBeenCalledWith(filters); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx index 3cdb3b5b08a0c5..0b8bdab528864c 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx @@ -7,7 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo, useEffect, useState, useRef } from 'react'; +import { cloneDeep } from 'lodash'; import type { Query, TimeRange } from '@kbn/es-query'; import type { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; @@ -26,10 +27,8 @@ export const AlertsSearchBar = ({ featureIds = EMPTY_FEATURE_IDS, ruleTypeId, query, - filters, onQueryChange, onQuerySubmit, - onFiltersUpdated, rangeFrom, rangeTo, showFilterBar = false, @@ -40,14 +39,17 @@ export const AlertsSearchBar = ({ http, toasts, unifiedSearchBar, - dataViewsService, + dataService, + initialFilters, + onFiltersUpdated, }: AlertsSearchBarProps) => { const [queryLanguage, setQueryLanguage] = useState('kuery'); + const isFirstRender = useRef(false); const { dataView } = useAlertsDataView({ featureIds, http, toasts, - dataViewsService, + dataViewsService: dataService.dataViews, }); const { aadFields, loading: fieldsLoading } = useRuleAADFields({ ruleTypeId, @@ -105,6 +107,26 @@ export const AlertsSearchBar = ({ }); }; + useEffect(() => { + if (initialFilters?.length && !isFirstRender.current) { + dataService.query.filterManager.addFilters(cloneDeep(initialFilters)); + isFirstRender.current = true; + } + + const subscription = dataService.query.state$.subscribe((state) => { + if (state.changes.filters) { + onFiltersUpdated?.(state.state.filters ?? []); + } + }); + + return () => { + isFirstRender.current = false; + subscription.unsubscribe(); + dataService.query.filterManager.removeAll(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [initialFilters]); + return unifiedSearchBar({ appName, disableQueryLanguageSwitcher, @@ -112,13 +134,11 @@ export const AlertsSearchBar = ({ indexPatterns: !indexPatterns || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns, placeholder, query: { query: query ?? '', language: queryLanguage }, - filters, dateRangeFrom: rangeFrom, dateRangeTo: rangeTo, displayStyle: 'inPage', showFilterBar, onQuerySubmit: onSearchQuerySubmit, - onFiltersUpdated, onRefresh, showDatePicker, showQueryInput: true, diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts index 8875ef62947a84..0c844989e4a332 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts @@ -11,7 +11,7 @@ import type { Filter } from '@kbn/es-query'; import type { ValidFeatureId } from '@kbn/rule-data-utils'; import type { ToastsStart, HttpStart } from '@kbn/core/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import type { DataViewsContract } from '@kbn/data-views-plugin/common'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; export type QueryLanguageType = 'lucene' | 'kuery'; @@ -22,7 +22,7 @@ export interface AlertsSearchBarProps { rangeFrom?: string; rangeTo?: string; query?: string; - filters?: Filter[]; + initialFilters?: Filter[]; showFilterBar?: boolean; showDatePicker?: boolean; showSubmitButton?: boolean; @@ -41,5 +41,5 @@ export interface AlertsSearchBarProps { http: HttpStart; toasts: ToastsStart; unifiedSearchBar: UnifiedSearchPublicPluginStart['ui']['SearchBar']; - dataViewsService: DataViewsContract; + dataService: DataPublicPluginStart; } diff --git a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts index 399f49f9a4cc09..d0a3fd324ed051 100644 --- a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts @@ -15,7 +15,7 @@ import { createMaintenanceWindow, CreateParams } from '../services/maintenance_w const onErrorWithMessage = (message: string) => i18n.translate('xpack.alerting.maintenanceWindowsCreateFailureWithMessage', { - defaultMessage: 'Failed to create maintenance window: {message}', + defaultMessage: ['Failed to create maintenance window', message].filter(Boolean).join(': '), values: { message }, }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx index 6783289963f6c7..49169f473bf708 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx @@ -85,9 +85,7 @@ export const CreateMaintenanceWindowForm = React.memo(initialValue?.scopedQuery?.kql || ''); - const [filters, setFilters] = useState( - (initialValue?.scopedQuery?.filters as Filter[]) || [] - ); + const [filters, setFilters] = useState(initialValue?.scopedQuery?.filters || []); const [scopedQueryErrors, setScopedQueryErrors] = useState([]); const hasSetInitialCategories = useRef(false); const categoryIdsHistory = useRef([]); @@ -463,7 +461,7 @@ export const CreateMaintenanceWindowForm = React.memo { @@ -63,7 +63,7 @@ describe('MaintenanceWindowScopedQuery', () => { featureIds={['observability', 'management', 'securitySolution'] as AlertConsumers[]} isEnabled={false} query={''} - filters={[]} + initialFilters={[]} onQueryChange={jest.fn()} onFiltersChange={jest.fn()} /> @@ -77,7 +77,7 @@ describe('MaintenanceWindowScopedQuery', () => { featureIds={['observability', 'management', 'securitySolution'] as AlertConsumers[]} isLoading={true} query={''} - filters={[]} + initialFilters={[]} onQueryChange={jest.fn()} onFiltersChange={jest.fn()} /> diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx index d252333fb4a0bf..6aa00dfec02113 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_window_scoped_query.tsx @@ -16,7 +16,7 @@ import { useKibana } from '../../../utils/kibana_react'; export interface MaintenanceWindowScopedQueryProps { featureIds: AlertConsumers[]; query: string; - filters: Filter[]; + initialFilters?: Filter[]; errors?: string[]; isLoading?: boolean; isEnabled?: boolean; @@ -29,7 +29,7 @@ export const MaintenanceWindowScopedQuery = React.memo( const { featureIds, query, - filters, + initialFilters, errors = [], isLoading, isEnabled = true, @@ -79,7 +79,7 @@ export const MaintenanceWindowScopedQuery = React.memo( featureIds={featureIds} disableQueryLanguageSwitcher={true} query={query} - filters={filters} + initialFilters={initialFilters} onQueryChange={onQueryChangeInternal} onQuerySubmit={onQueryChangeInternal} onFiltersUpdated={onFiltersChange} @@ -90,7 +90,7 @@ export const MaintenanceWindowScopedQuery = React.memo( http={http} toasts={toasts} unifiedSearchBar={SearchBar} - dataViewsService={data.dataViews} + dataService={data} /> diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_search_bar/alert_search_bar.tsx b/x-pack/plugins/observability_solution/observability/public/components/alert_search_bar/alert_search_bar.tsx index 863855bbbb3b85..c45d2cdfa08fe2 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_search_bar/alert_search_bar.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_search_bar/alert_search_bar.tsx @@ -165,7 +165,7 @@ export function ObservabilityAlertSearchBar({ rangeFrom={rangeFrom} rangeTo={rangeTo} showFilterBar={showFilterBar} - filters={filters} + initialFilters={filters} onFiltersUpdated={onFilterUpdated} savedQuery={savedQuery} onSavedQueryUpdated={setSavedQuery} diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 5a4660f362987a..068073bc6ac2eb 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -9793,7 +9793,6 @@ "xpack.alerting.maintenanceWindows.upcoming": "À venir", "xpack.alerting.maintenanceWindowsArchiveFailure": "Impossible d'archiver la fenêtre de maintenance.", "xpack.alerting.maintenanceWindowsArchiveSuccess": "Fenêtre de maintenance archivée \"{title}\"", - "xpack.alerting.maintenanceWindowsCreateFailureWithMessage": "Impossible de créer la fenêtre de maintenance : {message}", "xpack.alerting.maintenanceWindowsCreateFailureWithoutMessage": "Impossible de créer la fenêtre de maintenance", "xpack.alerting.maintenanceWindowsCreateSuccess": "Fenêtre de maintenance créée \"{title}\"", "xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure": "Impossible d'annuler et d'archiver la fenêtre de maintenance.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c112b137c2e2b8..4d644aae283bd7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9787,7 +9787,6 @@ "xpack.alerting.maintenanceWindows.upcoming": "予定", "xpack.alerting.maintenanceWindowsArchiveFailure": "保守時間枠をアーカイブできませんでした。", "xpack.alerting.maintenanceWindowsArchiveSuccess": "アーカイブされた保守時間枠''{title}''", - "xpack.alerting.maintenanceWindowsCreateFailureWithMessage": "保守時間枠を作成できませんでした:{message}", "xpack.alerting.maintenanceWindowsCreateFailureWithoutMessage": "保守時間枠を作成できませんでした", "xpack.alerting.maintenanceWindowsCreateSuccess": "作成された保守時間枠''{title}''", "xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure": "保守時間枠をキャンセルしてアーカイブできませんでした。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 30d822480bdff0..41f8a38e1eaa0c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9801,7 +9801,6 @@ "xpack.alerting.maintenanceWindows.upcoming": "即将发生", "xpack.alerting.maintenanceWindowsArchiveFailure": "无法归档维护窗口。", "xpack.alerting.maintenanceWindowsArchiveSuccess": "已归档维护窗口“{title}”", - "xpack.alerting.maintenanceWindowsCreateFailureWithMessage": "无法创建维护窗口:{message}", "xpack.alerting.maintenanceWindowsCreateFailureWithoutMessage": "无法创建维护窗口", "xpack.alerting.maintenanceWindowsCreateSuccess": "已创建维护窗口“{title}”", "xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure": "无法取消并归档维护窗口。", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts index 97c9ba686435a9..df1dce08f684ab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts @@ -11,9 +11,10 @@ import { isValidUrl, getConnectorWithInvalidatedFields, getRuleWithInvalidatedFields, + validateActionFilterQuery, } from './value_validators'; import { v4 as uuidv4 } from 'uuid'; -import { Rule, IErrorObject, UserConfiguredActionConnector } from '../../types'; +import { Rule, IErrorObject, UserConfiguredActionConnector, RuleUiAction } from '../../types'; describe('throwIfAbsent', () => { test('throws if value is absent', () => { @@ -441,3 +442,70 @@ describe('getRuleWithInvalidatedFields', () => { expect((rule.actions[1].params as any).incident.field.name).toEqual('myIncident'); }); }); + +describe('validateActionFilterQuery', () => { + test('does not return an error when kql query exists', () => { + const actionItem: RuleUiAction = { + actionTypeId: 'test', + group: 'qwer', + id: '123', + params: { + incident: { + field: { + name: 'myIncident', + }, + }, + }, + alertsFilter: { query: { kql: 'id: *', filters: [] } }, + }; + + expect(validateActionFilterQuery(actionItem)).toBe(null); + }); + + test('does not return an error when filter query exists', () => { + const actionItem = { + actionTypeId: 'test', + group: 'qwer', + id: '123', + params: { + incident: { + field: { + name: 'myIncident', + }, + }, + }, + alertsFilter: { + query: { + kql: undefined, + filters: [ + { + $state: { store: 'state' }, + meta: { key: 'test' }, + query: { exists: { field: '_id' } }, + }, + ], + }, + }, + }; + + expect(validateActionFilterQuery(actionItem)).toBe(null); + }); + + test('returns an error no kql and no filters', () => { + const actionItem = { + actionTypeId: 'test', + group: 'qwer', + id: '123', + params: { + incident: { + field: { + name: 'myIncident', + }, + }, + }, + alertsFilter: { query: { kql: '', filters: [] } }, + }; + + expect(validateActionFilterQuery(actionItem)).toBe('A custom query is required.'); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts index bb98c2664141d5..6ad7eb14c8270e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.ts @@ -20,7 +20,7 @@ const filterQueryRequiredError = i18n.translate( export const validateActionFilterQuery = (actionItem: RuleUiAction): string | null => { if ('alertsFilter' in actionItem) { const query = actionItem?.alertsFilter?.query; - if (query && !query.kql) { + if (query && !(query.kql || query.filters.length)) { return filterQueryRequiredError; } } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx index 0abcce240b17e0..48ca372eef81dc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx @@ -84,7 +84,7 @@ export const ActionAlertsFilterQuery: React.FC = ( ruleTypeId={ruleTypeId} disableQueryLanguageSwitcher={true} query={query.kql} - filters={query.filters ?? []} + initialFilters={query.filters ?? []} onQueryChange={onQueryChange} onQuerySubmit={onQueryChange} onFiltersUpdated={onFiltersUpdated} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx new file mode 100644 index 00000000000000..f0a818121cac84 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx @@ -0,0 +1,136 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { Filter } from '@kbn/es-query'; +import { NotificationsStart } from '@kbn/core-notifications-browser'; +import { useLoadRuleTypesQuery } from '../../hooks/use_load_rule_types_query'; +import { useRuleAADFields } from '../../hooks/use_rule_aad_fields'; +import { AlertsSearchBar } from './alerts_search_bar'; + +const mockDataPlugin = dataPluginMock.createStartContract(); +jest.mock('../../hooks/use_load_rule_types_query'); +jest.mock('../../hooks/use_rule_aad_fields'); +jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'); +jest.mock('@kbn/kibana-react-plugin/public', () => { + const original = jest.requireActual('@kbn/kibana-react-plugin/public'); + return { + useKibana: () => ({ + services: { + ...original.useKibana().services, + data: mockDataPlugin, + unifiedSearch: { + ui: { + SearchBar: jest.fn().mockImplementation((props) => ( + + )), + }, + }, + notifications: { toasts: { addWarning: jest.fn() } } as unknown as NotificationsStart, + }, + }), + }; +}); +jest.mocked(useAlertsDataView).mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-*', + fields: [ + { + name: 'event.action', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + }, +}); + +jest.mocked(useLoadRuleTypesQuery).mockReturnValue({ + ruleTypesState: { + initialLoad: false, + data: new Map(), + isLoading: false, + error: undefined, + }, + authorizedToReadAnyRules: false, + hasAnyAuthorizedRuleType: false, + authorizedRuleTypes: [], + authorizedToCreateAnyRules: false, + isSuccess: false, +}); + +jest.mocked(useRuleAADFields).mockReturnValue({ + aadFields: [], + loading: false, +}); + +describe('AlertsSearchBar', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', () => { + render( + + ); + expect(screen.getByTestId('querySubmitButton')).toBeInTheDocument(); + }); + + it('renders initial filters correctly', () => { + const filters = [ + { + meta: { + negate: false, + alias: null, + disabled: false, + type: 'custom', + key: 'query', + }, + query: { match_phrase: { 'host.name': 'testValue' } }, + $state: { store: 'appState' }, + }, + ] as Filter[]; + + render( + + ); + + expect(mockDataPlugin.query.filterManager.addFilters).toHaveBeenCalledWith(filters); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 3896e5d0e938a7..0cdda4f35840ad 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -5,13 +5,14 @@ * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { compareFilters, Query, TimeRange } from '@kbn/es-query'; import { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; +import { cloneDeep } from 'lodash'; import { isQuickFiltersGroup, QuickFiltersMenuItem } from './quick_filters'; import { NO_INDEX_PATTERNS } from './constants'; import { SEARCH_BAR_PLACEHOLDER } from './translations'; @@ -31,7 +32,7 @@ export function AlertsSearchBar({ featureIds = EMPTY_FEATURE_IDS, ruleTypeId, query, - filters, + initialFilters, quickFilters = [], onQueryChange, onQuerySubmit, @@ -53,9 +54,11 @@ export function AlertsSearchBar({ unifiedSearch: { ui: { SearchBar }, }, + data: dataService, } = useKibana().services; const [queryLanguage, setQueryLanguage] = useState('kuery'); + const isFirstRender = useRef(false); const { dataView } = useAlertsDataView({ featureIds, http, @@ -146,8 +149,8 @@ export function AlertsSearchBar({ ...menuItem, icon: qf.icon ?? 'filterInCircle', onClick: () => { - if (!filters?.some((f) => compareFilters(f, filter))) { - onFiltersUpdated?.([...(filters ?? []), filter]); + if (!initialFilters?.some((f) => compareFilters(f, filter))) { + onFiltersUpdated?.([...(initialFilters ?? []), filter]); } }, 'data-test-subj': `quick-filters-item-${qf.name}`, @@ -166,7 +169,27 @@ export function AlertsSearchBar({ panels: [], }; } - }, [filters, onFiltersUpdated, quickFilters, showFilterBar]); + }, [initialFilters, onFiltersUpdated, quickFilters, showFilterBar]); + + useEffect(() => { + if (initialFilters?.length && !isFirstRender.current) { + dataService.query.filterManager.addFilters(cloneDeep(initialFilters)); + isFirstRender.current = true; + } + + const subscription = dataService.query.state$.subscribe((state) => { + if (state.changes.filters) { + onFiltersUpdated?.(state.state.filters ?? []); + } + }); + + return () => { + isFirstRender.current = false; + subscription.unsubscribe(); + dataService.query.filterManager.removeAll(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [initialFilters]); return ( , 'query' | 'onQueryChange' | 'onQuerySubmit'> { + extends Omit, 'query' | 'onQueryChange' | 'onQuerySubmit' | 'filters'> { appName: string; disableQueryLanguageSwitcher?: boolean; featureIds: ValidFeatureId[]; rangeFrom?: string; rangeTo?: string; query?: string; - filters?: Filter[]; + initialFilters?: Filter[]; quickFilters?: QuickFiltersMenuItem[]; showFilterBar?: boolean; showDatePicker?: boolean; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx index 71594e3206ffd5..1eb9a3891b2d0d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/url_synced_alerts_search_bar.tsx @@ -138,7 +138,7 @@ export const UrlSyncedAlertsSearchBar = ({ rangeTo={rangeTo} query={kuery} onQuerySubmit={onQueryChange} - filters={filters} + initialFilters={filters} onFiltersUpdated={onFiltersChange} savedQuery={savedQuery} onSavedQueryUpdated={setSavedQuery}