-
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
[Actionable Observability] Expose ObservabilityAlertSearchBar from Observability plugin #146401
Changes from 5 commits
4e96f35
b693b11
f937182
5489109
6b2b4c1
7275cc6
307af94
a86b076
66d173d
a52525b
03598cc
cbc898e
5aa3496
d785a1b
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,16 +6,15 @@ | |
*/ | ||
|
||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; | ||
|
||
import React, { useCallback, useEffect } from 'react'; | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
import { Query } from '@kbn/es-query'; | ||
import { useKibana } from '../../../utils/kibana_react'; | ||
import { observabilityAlertFeatureIds } from '../../../config'; | ||
import { ObservabilityAppServices } from '../../../application/types'; | ||
import { useServices } from './services'; | ||
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 benefit does it have to introduce a new 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. Since this component will be used in another plugin, we don't know if they have all the related dependencies defined in their plugin as this shared component needs. |
||
import { AlertsStatusFilter } from './components'; | ||
import { observabilityAlertFeatureIds } from '../../../config'; | ||
import { ALERT_STATUS_QUERY, DEFAULT_QUERIES, DEFAULT_QUERY_STRING } from './constants'; | ||
import { AlertSearchBarProps } from './types'; | ||
import { ObservabilityAlertSearchBarProps } from './types'; | ||
import { buildEsQuery } from '../../../utils/build_es_query'; | ||
import { AlertStatus } from '../../../../common/typings'; | ||
|
||
|
@@ -27,84 +26,79 @@ const getAlertStatusQuery = (status: string): Query[] => { | |
|
||
export function ObservabilityAlertSearchBar({ | ||
appName, | ||
defaultSearchQueries = DEFAULT_QUERIES, | ||
onEsQueryChange, | ||
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. Renamed for readability |
||
onKueryChange, | ||
onRangeFromChange, | ||
onRangeToChange, | ||
onStatusChange, | ||
kuery, | ||
rangeFrom, | ||
setRangeFrom, | ||
rangeTo, | ||
setRangeTo, | ||
kuery, | ||
setKuery, | ||
status, | ||
setStatus, | ||
setEsQuery, | ||
queries = DEFAULT_QUERIES, | ||
}: AlertSearchBarProps) { | ||
const { | ||
data: { | ||
query: { | ||
timefilter: { timefilter: timeFilterService }, | ||
}, | ||
}, | ||
notifications: { toasts }, | ||
triggersActionsUi: { getAlertsSearchBar: AlertsSearchBar }, | ||
} = useKibana<ObservabilityAppServices>().services; | ||
}: ObservabilityAlertSearchBarProps) { | ||
const { AlertsSearchBar, errorToast, timeFilterService } = useServices(); | ||
|
||
const onStatusChange = useCallback( | ||
const onAlertStatusChange = useCallback( | ||
(alertStatus: AlertStatus) => { | ||
setEsQuery( | ||
onEsQueryChange( | ||
buildEsQuery( | ||
{ | ||
to: rangeTo, | ||
from: rangeFrom, | ||
}, | ||
kuery, | ||
[...getAlertStatusQuery(alertStatus), ...queries] | ||
[...getAlertStatusQuery(alertStatus), ...defaultSearchQueries] | ||
) | ||
); | ||
}, | ||
[kuery, queries, rangeFrom, rangeTo, setEsQuery] | ||
[kuery, defaultSearchQueries, rangeFrom, rangeTo, onEsQueryChange] | ||
); | ||
|
||
useEffect(() => { | ||
onStatusChange(status); | ||
}, [onStatusChange, status]); | ||
onAlertStatusChange(status); | ||
}, [onAlertStatusChange, status]); | ||
|
||
const onSearchBarParamsChange = useCallback( | ||
const onSearchBarParamsChange = useCallback< | ||
(query: { | ||
dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; | ||
query: string; | ||
}) => void | ||
>( | ||
({ dateRange, query }) => { | ||
try { | ||
// First try to create es query to make sure query is valid, then save it in state | ||
const esQuery = buildEsQuery( | ||
{ | ||
to: rangeTo, | ||
from: rangeFrom, | ||
to: dateRange.to, | ||
from: dateRange.from, | ||
}, | ||
query, | ||
[...getAlertStatusQuery(status), ...queries] | ||
[...getAlertStatusQuery(status), ...defaultSearchQueries] | ||
); | ||
setKuery(query); | ||
onKueryChange(query); | ||
timeFilterService.setTime(dateRange); | ||
setRangeFrom(dateRange.from); | ||
setRangeTo(dateRange.to); | ||
setEsQuery(esQuery); | ||
onRangeFromChange(dateRange.from); | ||
onRangeToChange(dateRange.to); | ||
maryam-saeidi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
onEsQueryChange(esQuery); | ||
} catch (error) { | ||
toasts.addError(error, { | ||
errorToast(error, { | ||
title: i18n.translate('xpack.observability.alerts.searchBar.invalidQueryTitle', { | ||
defaultMessage: 'Invalid query string', | ||
}), | ||
}); | ||
setKuery(DEFAULT_QUERY_STRING); | ||
onKueryChange(DEFAULT_QUERY_STRING); | ||
clintandrewhall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}, | ||
[ | ||
defaultSearchQueries, | ||
timeFilterService, | ||
setRangeFrom, | ||
setRangeTo, | ||
setKuery, | ||
setEsQuery, | ||
rangeTo, | ||
rangeFrom, | ||
onRangeFromChange, | ||
onRangeToChange, | ||
onKueryChange, | ||
onEsQueryChange, | ||
status, | ||
queries, | ||
toasts, | ||
errorToast, | ||
] | ||
); | ||
|
||
|
@@ -126,13 +120,14 @@ export function ObservabilityAlertSearchBar({ | |
<EuiFlexItem grow={false}> | ||
<AlertsStatusFilter | ||
status={status} | ||
onChange={(id) => { | ||
setStatus(id as AlertStatus); | ||
}} | ||
onChange={(id) => onStatusChange(id as AlertStatus)} | ||
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. always avoid inline creation of functions and objects... they will trigger a re-render due to reference inequality. 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. 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. I've read different articles about this topic and there are opposite views about it, such as this one: 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. This is indeed a widely contested point. AFAIK the performance hit is negligible (defining functions is considered cheap) @maryam-saeidi if you want to know for sure, you could add 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. Even if the hit is "negligible", I haven't encountered an argument that defining inline is preferable, other than that's how it was written when the PR was created. I personally pull them out into 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. @maryam-saeidi I just looked at the article you referenced... it's from 2017, and reflects class components, (e.g. AFAIK, the strict equality check and re-render is always an issue with inline objects and props. @CoenWarmer it's not about defining functions, it's about the function causing a sub-component to re-render... which you can't really know how a re-render will affect children. The rule of thumb still applies here. I don't see an argument where defining inline gains anything, where I see plenty of arguments why not to-- along with the cheapness of it, namely creating a 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, since we were talking about the concepts (the article that you shared was from 2018 and also related to PureComponents) I didn't look for a newer article. When I read react documents, I don't see a focus on avoiding re-rendering as much as possible but rather on using tools to improve performance when it is needed. Actually, I saw more focus on making sure our components work as expected even if it is slow and it re-render unnecessarily and then optimizing it. 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. @clintandrewhall Using inline functions is considered an acceptable practice both in the old and the new React docs. In this case, the component that was passed the inline function is not memoized, so unsure as to what value not passing an arrow function would bring apart from perhaps aesthetic reasons. I'm however not against defining functions in a |
||
/> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
); | ||
} | ||
|
||
// eslint-disable-next-line import/no-default-export | ||
export default ObservabilityAlertSearchBar; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,21 @@ import { | |
useAlertSearchBarStateContainer, | ||
} from './containers'; | ||
import { ObservabilityAlertSearchBar } from './alert_search_bar'; | ||
import { ObservabilityAlertSearchBarProvider } from './services'; | ||
import { AlertSearchBarWithUrlSyncProps } from './types'; | ||
import { useKibana } from '../../../utils/kibana_react'; | ||
import { ObservabilityAppServices } from '../../../application/types'; | ||
|
||
function AlertSearchbarWithUrlSync(props: AlertSearchBarWithUrlSyncProps) { | ||
const { urlStorageKey, ...searchBarProps } = props; | ||
const stateProps = useAlertSearchBarStateContainer(urlStorageKey); | ||
const services = useKibana<ObservabilityAppServices>().services; | ||
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. Take every opportunity to eliminate 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. Wondering about the reasoning for it, 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. To make a component shareable and portable, we need to prioritize decoupling it from Kibana. The In short: you have no idea if This is why creating a provider for the shared component and putting it in the root is so helpful-- it allows us to provide and enforce dependency injection. 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.
Since this component is meant to be used inside of the
How do we do that at the root level? |
||
|
||
return <ObservabilityAlertSearchBar {...stateProps} {...searchBarProps} />; | ||
return ( | ||
<ObservabilityAlertSearchBarProvider {...services}> | ||
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. I would move this up to your React root. 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 is the benefit of that? 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. see: #146401 (comment) |
||
<ObservabilityAlertSearchBar {...stateProps} {...searchBarProps} /> | ||
</ObservabilityAlertSearchBarProvider> | ||
); | ||
} | ||
|
||
export function ObservabilityAlertSearchbarWithUrlSync(props: AlertSearchBarWithUrlSyncProps) { | ||
|
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.
I added
getAlertsSearchBar
to the TriggerActionsUI mock over here. So you might not need to reintroduce this here.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.
But this one is mocking
useServices
notuseKibana
, and I also want to check the related props, so I defined the mock here.