From 90f38a6388e148fd029c6707c0b6ce4860b86a77 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 14 Nov 2022 19:29:03 +0100 Subject: [PATCH 1/3] [Synthetics] Update duration chart legend labels (#145116) --- .../configurations/lens_attributes.test.ts | 4 +-- .../configurations/lens_attributes.ts | 27 ++++++-------- .../monitor_summary/duration_trend.tsx | 35 ++++++++++++++++--- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts index aaef957422172..bce05a998fafe 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts @@ -108,7 +108,7 @@ describe('Lens Attribute', () => { to: 'now', }, dataView: mockDataView, - name: 'ux-series-1', + name: 'Page load time', breakdown: 'percentile', reportDefinitions: {}, selectedMetricField: 'transaction.duration.us', @@ -139,7 +139,7 @@ describe('Lens Attribute', () => { query: 'transaction.type: page-load and processor.event: transaction', }, isBucketed: false, - label: `${rank} percentile of page load time`, + label: 'Page load time', operationType: 'percentile', params: { percentile: Number(rank.slice(0, 2)), diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index e029e8479fde5..c93b6ef2cadd0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -331,7 +331,6 @@ export class LensAttributes { columnType, columnFilter, operationType, - shortLabel, }: { sourceField: string; columnType?: string; @@ -339,7 +338,6 @@ export class LensAttributes { operationType?: SupportedOperations | 'last_value'; label?: string; seriesConfig: SeriesConfig; - shortLabel?: boolean; }) { if (columnType === 'operation' || operationType) { if ( @@ -352,7 +350,6 @@ export class LensAttributes { label, seriesConfig, columnFilter, - shortLabel, }); } if (operationType === 'last_value') { @@ -365,7 +362,7 @@ export class LensAttributes { }); } if (operationType?.includes('th')) { - return this.getPercentileNumberColumn(sourceField, operationType, seriesConfig!); + return this.getPercentileNumberColumn(sourceField, operationType, seriesConfig!, label); } } return this.getNumberRangeColumn(sourceField, seriesConfig!, label); @@ -402,14 +399,12 @@ export class LensAttributes { seriesConfig, operationType, columnFilter, - shortLabel, }: { sourceField: string; operationType: SupportedOperations; label?: string; seriesConfig: SeriesConfig; columnFilter?: ColumnFilter; - shortLabel?: boolean; }): | MinIndexPatternColumn | MaxIndexPatternColumn @@ -469,14 +464,17 @@ export class LensAttributes { getPercentileNumberColumn( sourceField: string, percentileValue: string, - seriesConfig: SeriesConfig + seriesConfig: SeriesConfig, + label?: string ): PercentileIndexPatternColumn { return { ...buildNumberColumn(sourceField), - label: i18n.translate('xpack.observability.expView.columns.label', { - defaultMessage: '{percentileValue} percentile of {sourceField}', - values: { sourceField: seriesConfig.labels[sourceField]?.toLowerCase(), percentileValue }, - }), + label: + label ?? + i18n.translate('xpack.observability.expView.columns.label', { + defaultMessage: '{percentileValue} percentile of {sourceField}', + values: { sourceField: seriesConfig.labels[sourceField]?.toLowerCase(), percentileValue }, + }), operationType: 'percentile', params: getPercentileParam(percentileValue), customLabel: true, @@ -552,7 +550,6 @@ export class LensAttributes { colIndex, layerId, metricOption, - shortLabel, }: { sourceField: string; metricOption?: MetricOption; @@ -561,7 +558,6 @@ export class LensAttributes { layerId: string; layerConfig: LayerConfig; colIndex?: number; - shortLabel?: boolean; }) { const { breakdown, seriesConfig } = layerConfig; const fieldMetaInfo = this.getFieldMeta(sourceField, layerConfig, metricOption); @@ -614,7 +610,8 @@ export class LensAttributes { ...this.getPercentileNumberColumn( fieldName, operationType || PERCENTILE_RANKS[0], - seriesConfig! + seriesConfig!, + label || columnLabel ), filter: colIndex !== undefined ? columnFilters?.[colIndex] : undefined, }; @@ -628,7 +625,6 @@ export class LensAttributes { operationType, label: label || columnLabel, seriesConfig: layerConfig.seriesConfig, - shortLabel, }); } if (operationType === 'unique_count' || fieldType === 'string') { @@ -745,7 +741,6 @@ export class LensAttributes { return this.getColumnBasedOnType({ layerConfig, layerId, - shortLabel: true, label: item.label, sourceField: REPORT_METRIC_FIELD, metricOption: item, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_trend.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_trend.tsx index e104eeb87abdd..000706140d267 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_trend.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/duration_trend.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { i18n } from '@kbn/i18n'; import { ClientPluginsStart } from '../../../../../plugin'; import { useMonitorQueryId } from '../hooks/use_monitor_query_id'; import { useSelectedLocation } from '../hooks/use_selected_location'; @@ -24,8 +25,6 @@ export const MonitorDurationTrend = (props: MonitorDurationTrendProps) => { const monitorId = useMonitorQueryId(); const selectedLocation = useSelectedLocation(); - const metricsToShow = ['min', 'max', 'median', '25th', '75th']; - if (!selectedLocation) { return null; } @@ -34,10 +33,10 @@ export const MonitorDurationTrend = (props: MonitorDurationTrendProps) => { ({ + attributes={Object.keys(metricsToShow).map((metric) => ({ dataType: 'synthetics', time: props, - name: metric + ' Series', + name: metricsToShow[metric], selectedMetricField: 'monitor.duration.us', reportDefinitions: { 'monitor.id': [monitorId], @@ -49,3 +48,31 @@ export const MonitorDurationTrend = (props: MonitorDurationTrendProps) => { /> ); }; + +const MIN_LABEL = i18n.translate('xpack.synthetics.durationTrend.min', { + defaultMessage: 'Min', +}); + +const MAX_LABEL = i18n.translate('xpack.synthetics.durationTrend.max', { + defaultMessage: 'Max', +}); + +const MEDIAN_LABEL = i18n.translate('xpack.synthetics.durationTrend.median', { + defaultMessage: 'Median', +}); + +const PERCENTILE_25_LABEL = i18n.translate('xpack.synthetics.durationTrend.percentile25', { + defaultMessage: '25th', +}); + +const PERCENTILE_75_LABEL = i18n.translate('xpack.synthetics.durationTrend.percentile75', { + defaultMessage: '75th', +}); + +const metricsToShow: Record = { + max: MAX_LABEL, + '75th': PERCENTILE_75_LABEL, + median: MEDIAN_LABEL, + '25th': PERCENTILE_25_LABEL, + min: MIN_LABEL, +}; From bebcd354d36f59464f1899ce80c47af673c024a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 14 Nov 2022 19:42:24 +0100 Subject: [PATCH 2/3] [Uptime] Fix monitor alert label (#145112) ## Summary Closes #143895 Updates the label for the status check labels to reflect what the alert does. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__snapshots__/down_number_select.test.tsx.snap | 4 ++-- .../legacy_uptime/components/overview/alerts/translations.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap index eb417a875de9e..527247ec9f072 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap @@ -16,7 +16,7 @@ exports[`DownNoExpressionSelect component should renders against props 1`] = ` - matching monitors are down > + matching monitors are down >= } data-test-subj="xpack.synthetics.alerts.monitorStatus.numTimesExpression" - description="matching monitors are down >" + description="matching monitors are down >=" id="ping-count" value="5 times" /> diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/translations.ts index 0580528b6b38c..de9da94460338 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/translations.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/translations.ts @@ -117,14 +117,14 @@ export const ENTER_NUMBER_OF_DOWN_COUNTS = i18n.translate( export const MATCHING_MONITORS_DOWN = i18n.translate( 'xpack.synthetics.alerts.monitorStatus.numTimesExpression.matchingMonitors.description', { - defaultMessage: 'matching monitors are down >', + defaultMessage: 'matching monitors are down >=', } ); export const ANY_MONITOR_DOWN = i18n.translate( 'xpack.synthetics.alerts.monitorStatus.numTimesExpression.anyMonitors.description', { - defaultMessage: 'any monitor is down >', + defaultMessage: 'any monitor is down >=', } ); From 20e2fb5e1e3161cc315de842ccba58220d3b4ab6 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Mon, 14 Nov 2022 19:50:07 +0100 Subject: [PATCH 3/3] [Infrastructure UI] Add URL state to Hosts View (#144181) Closes [#141492](https://github.com/elastic/kibana/issues/141492) ## Summary This PR adds `query`, `timeRange` and `filters` parameters to the URL state on the hosts view. URL parameters are updated after search filters are applied (after click on the "update" button. ## Testing Different cases: - Add new search criteria ( filter / time range / query ) and click on "update" - the URL should update - Save a query and reload - Load a saved query - Change an existing query ![image](https://user-images.githubusercontent.com/14139027/199047590-29e375fb-6909-424b-89c4-ef9193a77b10.png) ![image](https://user-images.githubusercontent.com/14139027/199046342-29fbfa76-0314-462b-b593-2c535112be09.png) ![image](https://user-images.githubusercontent.com/14139027/199046201-76ace0fa-8d17-4e1f-b36f-54a2419fb6af.png) - Open the URL in a new browser tab/window - the filters should be added Co-authored-by: Carlos Crespo Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Nathan L Smith --- .../metrics/hosts/components/hosts_table.tsx | 4 +- .../hosts/components/unified_search_bar.tsx | 10 +- .../hosts/hooks/use_hosts_url_state.ts | 169 ++++++++++++++++++ .../metrics/hosts/hooks/use_unified_search.ts | 111 ++++++------ 4 files changed, 234 insertions(+), 60 deletions(-) create mode 100644 x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_url_state.ts diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx index 3d50abeb5c212..14d229b838e56 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table.tsx @@ -29,7 +29,7 @@ const HOST_METRICS: Array<{ type: SnapshotMetricType }> = [ export const HostsTable = () => { const { sourceId } = useSourceContext(); - const { esQuery, dateRangeTimestamp } = useUnifiedSearchContext(); + const { buildQuery, dateRangeTimestamp } = useUnifiedSearchContext(); const timeRange: InfraTimerangeInput = { from: dateRangeTimestamp.from, @@ -38,6 +38,8 @@ export const HostsTable = () => { ignoreLookback: true, }; + const esQuery = buildQuery(); + // Snapshot endpoint internally uses the indices stored in source.configuration.metricAlias. // For the Unified Search, we create a data view, which for now will be built off of source.configuration.metricAlias too // if we introduce data view selection, we'll have to change this hook and the endpoint to accept a new parameter for the indices diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx index f596300092dfe..87715dabe9604 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/unified_search_bar.tsx @@ -24,9 +24,10 @@ export const UnifiedSearchBar = ({ dataView }: Props) => { const { unifiedSearchDateRange, unifiedSearchQuery, - submitFilterChange, + unifiedSearchFilters, + onSubmit, saveQuery, - clearSavedQUery, + clearSavedQuery, } = useUnifiedSearchContext(); const { SearchBar } = unifiedSearch.ui; @@ -40,7 +41,7 @@ export const UnifiedSearchBar = ({ dataView }: Props) => { }; const onClearSavedQuery = () => { - clearSavedQUery(); + clearSavedQuery(); }; const onQuerySave = (savedQuery: SavedQuery) => { @@ -54,7 +55,7 @@ export const UnifiedSearchBar = ({ dataView }: Props) => { payload?: { dateRange: TimeRange; query?: Query }; filters?: Filter[]; }) => { - submitFilterChange(payload?.query, payload?.dateRange, filters); + onSubmit(payload?.query, payload?.dateRange, filters); }; return ( @@ -64,6 +65,7 @@ export const UnifiedSearchBar = ({ dataView }: Props) => { query={unifiedSearchQuery} dateRangeFrom={unifiedSearchDateRange.from} dateRangeTo={unifiedSearchDateRange.to} + filters={unifiedSearchFilters} onQuerySubmit={onQuerySubmit} onSaved={onQuerySave} onSavedQueryUpdated={onQuerySave} diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_url_state.ts new file mode 100644 index 0000000000000..49042fbd9bd7c --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_url_state.ts @@ -0,0 +1,169 @@ +/* + * 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 { useCallback, useEffect, useReducer } from 'react'; +import { TimeRange } from '@kbn/es-query'; +import DateMath from '@kbn/datemath'; +import deepEqual from 'fast-deep-equal'; +import * as rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { constant, identity } from 'fp-ts/lib/function'; +import { enumeration } from '@kbn/securitysolution-io-ts-types'; +import { FilterStateStore } from '@kbn/es-query'; +import { useUrlState } from '../../../../utils/use_url_state'; +import { useKibanaTimefilterTime } from '../../../../hooks/use_kibana_timefilter_time'; + +const DEFAULT_QUERY = { + language: 'kuery', + query: '', +}; +const DEFAULT_FROM_MINUTES_VALUE = 15; +const INITIAL_DATE = new Date(); +export const INITIAL_DATE_RANGE = { from: `now-${DEFAULT_FROM_MINUTES_VALUE}m`, to: 'now' }; +const CALCULATED_DATE_RANGE_FROM = new Date( + INITIAL_DATE.getMinutes() - DEFAULT_FROM_MINUTES_VALUE +).getTime(); +const CALCULATED_DATE_RANGE_TO = INITIAL_DATE.getTime(); + +const INITIAL_HOSTS_STATE: HostsState = { + query: DEFAULT_QUERY, + filters: [], + // for unified search + dateRange: { ...INITIAL_DATE_RANGE }, + // for useSnapshot + dateRangeTimestamp: { + from: CALCULATED_DATE_RANGE_FROM, + to: CALCULATED_DATE_RANGE_TO, + }, +}; + +type Action = + | { + type: 'setQuery'; + payload: rt.TypeOf; + } + | { type: 'setFilter'; payload: rt.TypeOf }; + +const reducer = (state: HostsState, action: Action): HostsState => { + switch (action.type) { + case 'setFilter': + return { ...state, filters: [...action.payload] }; + case 'setQuery': + const { filters, query, ...payload } = action.payload; + const newFilters = !filters ? state.filters : filters; + const newQuery = !query ? state.query : query; + return { + ...state, + ...payload, + filters: [...newFilters], + query: { ...newQuery }, + }; + default: + throw new Error(); + } +}; + +export const useHostsUrlState = () => { + const [urlState, setUrlState] = useUrlState({ + defaultState: INITIAL_HOSTS_STATE, + decodeUrlState, + encodeUrlState, + urlStateKey: '_a', + }); + + const [state, dispatch] = useReducer(reducer, urlState); + + const [getTime] = useKibanaTimefilterTime(INITIAL_DATE_RANGE); + + const getRangeInTimestamp = useCallback(({ from, to }: TimeRange) => { + const fromTS = DateMath.parse(from)?.valueOf() ?? CALCULATED_DATE_RANGE_FROM; + const toTS = DateMath.parse(to)?.valueOf() ?? CALCULATED_DATE_RANGE_TO; + + return { + from: fromTS, + to: toTS, + }; + }, []); + + useEffect(() => { + if (!deepEqual(state, urlState)) { + setUrlState(state); + } + }, [setUrlState, state, urlState]); + + return { + state, + dispatch, + getRangeInTimestamp, + getTime, + }; +}; + +const HostsFilterRT = rt.intersection([ + rt.partial({ + $state: rt.type({ + store: enumeration('FilterStateStore', FilterStateStore), + }), + }), + rt.type({ + meta: rt.partial({ + alias: rt.union([rt.null, rt.string]), + disabled: rt.boolean, + negate: rt.boolean, + controlledBy: rt.string, + group: rt.string, + index: rt.string, + isMultiIndex: rt.boolean, + type: rt.string, + key: rt.string, + params: rt.any, + value: rt.any, + }), + }), + rt.partial({ + query: rt.record(rt.string, rt.any), + }), +]); + +const HostsFiltersRT = rt.array(HostsFilterRT); + +export const HostsQueryStateRT = rt.type({ + language: rt.string, + query: rt.any, +}); + +export const StringDateRangeRT = rt.type({ + from: rt.string, + to: rt.string, +}); + +export const DateRangeRT = rt.type({ + from: rt.number, + to: rt.number, +}); + +export const HostsStateRT = rt.type({ + filters: HostsFiltersRT, + query: HostsQueryStateRT, + dateRange: StringDateRangeRT, + dateRangeTimestamp: DateRangeRT, +}); + +export type HostsState = rt.TypeOf; + +const SetQueryType = rt.partial({ + query: HostsQueryStateRT, + dateRange: StringDateRangeRT, + filters: HostsFiltersRT, + dateRangeTimestamp: DateRangeRT, +}); + +const encodeUrlState = HostsStateRT.encode; +const decodeUrlState = (value: unknown) => { + return pipe(HostsStateRT.decode(value), fold(constant(undefined), identity)); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index 4b3d4e7a47df6..0bd7211cf1dc2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -6,98 +6,99 @@ */ import { useKibana } from '@kbn/kibana-react-plugin/public'; import createContainer from 'constate'; -import { useCallback, useReducer } from 'react'; +import { useCallback } from 'react'; import { buildEsQuery, Filter, Query, TimeRange } from '@kbn/es-query'; -import DateMath from '@kbn/datemath'; import type { SavedQuery } from '@kbn/data-plugin/public'; +import { debounce } from 'lodash'; import type { InfraClientStartDeps } from '../../../../types'; import { useMetricsDataViewContext } from './use_data_view'; -import { useKibanaTimefilterTime } from '../../../../hooks/use_kibana_timefilter_time'; - -const DEFAULT_FROM_MINUTES_VALUE = 15; +import { useSyncKibanaTimeFilterTime } from '../../../../hooks/use_kibana_timefilter_time'; +import { useHostsUrlState, INITIAL_DATE_RANGE } from './use_hosts_url_state'; export const useUnifiedSearch = () => { - const [, forceUpdate] = useReducer((x: number) => x + 1, 0); - + const { state, dispatch, getRangeInTimestamp, getTime } = useHostsUrlState(); const { metricsDataView } = useMetricsDataViewContext(); const { services } = useKibana(); const { data: { query: queryManager }, } = services; - const [getTime, setTime] = useKibanaTimefilterTime({ - from: `now-${DEFAULT_FROM_MINUTES_VALUE}m`, - to: 'now', + useSyncKibanaTimeFilterTime(INITIAL_DATE_RANGE, { + from: state.dateRange.from, + to: state.dateRange.to, }); - const { queryString, filterManager } = queryManager; - - const currentDate = new Date(); - const fromTS = - DateMath.parse(getTime().from)?.valueOf() ?? - new Date(currentDate.getMinutes() - DEFAULT_FROM_MINUTES_VALUE).getTime(); - const toTS = DateMath.parse(getTime().to)?.valueOf() ?? currentDate.getTime(); - const currentTimeRange = { - from: fromTS, - to: toTS, - }; + const { filterManager } = queryManager; - const submitFilterChange = useCallback( + const onSubmit = useCallback( (query?: Query, dateRange?: TimeRange, filters?: Filter[]) => { - if (filters) { - filterManager.setFilters(filters); + if (query || dateRange || filters) { + const newDateRange = dateRange ?? getTime(); + + if (filters) { + filterManager.setFilters(filters); + } + dispatch({ + type: 'setQuery', + payload: { + query, + filters: filters ? filterManager.getFilters() : undefined, + dateRange: newDateRange, + dateRangeTimestamp: getRangeInTimestamp(newDateRange), + }, + }); } - - setTime({ - ...getTime(), - ...dateRange, - }); - - queryString.setQuery({ ...queryString.getQuery(), ...query }); - // Unified search holds the all state, we need to force the hook to rerender so that it can return the most recent values - // This can be removed once we get the state from the URL - forceUpdate(); }, - [filterManager, queryString, getTime, setTime] + [filterManager, getRangeInTimestamp, getTime, dispatch] ); + // This won't prevent onSubmit from being fired twice when `clear filters` is clicked, + // that happens because both onQuerySubmit and onFiltersUpdated are internally triggered on same event by SearchBar. + // This just delays potential duplicate onSubmit calls + // eslint-disable-next-line react-hooks/exhaustive-deps + const debounceOnSubmit = useCallback(debounce(onSubmit, 100), [onSubmit]); + const saveQuery = useCallback( (newSavedQuery: SavedQuery) => { const savedQueryFilters = newSavedQuery.attributes.filters ?? []; const globalFilters = filterManager.getGlobalFilters(); - filterManager.setFilters([...savedQueryFilters, ...globalFilters]); - // Unified search holds the all state, we need to force the hook to rerender so that it can return the most recent values - // This can be removed once we get the state from the URL - forceUpdate(); + const query = newSavedQuery.attributes.query; + + dispatch({ + type: 'setQuery', + payload: { + query, + filters: [...savedQueryFilters, ...globalFilters], + }, + }); }, - [filterManager] + [filterManager, dispatch] ); - const clearSavedQUery = useCallback(() => { - filterManager.setFilters(filterManager.getGlobalFilters()); - - // Unified search holds the all state, we need to force the hook to rerender so that it can return the most recent values - // This can be removed once we get the state from the URL - forceUpdate(); - }, [filterManager]); + const clearSavedQuery = useCallback(() => { + dispatch({ + type: 'setFilter', + payload: filterManager.getGlobalFilters(), + }); + }, [filterManager, dispatch]); const buildQuery = useCallback(() => { if (!metricsDataView) { return null; } - return buildEsQuery(metricsDataView, queryString.getQuery(), filterManager.getFilters()); - }, [filterManager, metricsDataView, queryString]); + return buildEsQuery(metricsDataView, state.query, state.filters); + }, [metricsDataView, state.filters, state.query]); return { - dateRangeTimestamp: currentTimeRange, - esQuery: buildQuery(), - submitFilterChange, + dateRangeTimestamp: state.dateRangeTimestamp, + buildQuery, + onSubmit: debounceOnSubmit, saveQuery, - clearSavedQUery, - unifiedSearchQuery: queryString.getQuery() as Query, + clearSavedQuery, + unifiedSearchQuery: state.query, unifiedSearchDateRange: getTime(), - unifiedSearchFilters: filterManager.getFilters(), + unifiedSearchFilters: state.filters, }; };