mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -06:00
Explore: Fix shared crosshair for logs, logsvolume and graph panels (#57892)
* Explore: enable shared corsshair for logs, logsvolume & graph panel * avoid recreating a scoped bus on every render
This commit is contained in:
parent
3558cadb7e
commit
e6b088fbf5
@ -3897,9 +3897,6 @@ exports[`better eslint`] = {
|
||||
"public/app/features/explore/Explore.test.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/explore/Explore.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/explore/ExploreGraph.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { AutoSizerProps } from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { DataSourceApi, LoadingState, CoreApp, createTheme } from '@grafana/data';
|
||||
import { DataSourceApi, LoadingState, CoreApp, createTheme, EventBusSrv } from '@grafana/data';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
|
||||
@ -86,6 +86,7 @@ const dummyProps: Props = {
|
||||
splitted: false,
|
||||
changeGraphStyle: () => {},
|
||||
graphStyle: 'lines',
|
||||
eventBus: new EventBusSrv(),
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
|
||||
|
@ -4,7 +4,6 @@ import memoizeOne from 'memoize-one';
|
||||
import React, { createRef } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { compose } from 'redux';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
|
||||
import {
|
||||
@ -14,6 +13,7 @@ import {
|
||||
LoadingState,
|
||||
QueryFixAction,
|
||||
RawTimeRange,
|
||||
EventBus,
|
||||
SplitOpenOptions,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -87,6 +87,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
export interface ExploreProps extends Themeable2 {
|
||||
exploreId: ExploreId;
|
||||
theme: GrafanaTheme2;
|
||||
eventBus: EventBus;
|
||||
}
|
||||
|
||||
enum ExploreDrawer {
|
||||
@ -128,12 +129,16 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
scrollElement: HTMLDivElement | undefined;
|
||||
absoluteTimeUnsubsciber: Unsubscribable | undefined;
|
||||
topOfViewRef = createRef<HTMLDivElement>();
|
||||
graphEventBus: EventBus;
|
||||
logsEventBus: EventBus;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
openDrawer: undefined,
|
||||
};
|
||||
this.graphEventBus = props.eventBus.newScopedBus('graph', { onlyLocal: false });
|
||||
this.logsEventBus = props.eventBus.newScopedBus('logs', { onlyLocal: false });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -291,6 +296,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
splitOpenFn={this.onSplitOpen('graph')}
|
||||
loadingState={queryResponse.state}
|
||||
anchorToZero={false}
|
||||
eventBus={this.graphEventBus}
|
||||
/>
|
||||
</Collapse>
|
||||
);
|
||||
@ -324,6 +330,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
onStartScanning={this.onStartScanning}
|
||||
onStopScanning={this.onStopScanning}
|
||||
scrollElement={this.scrollElement}
|
||||
eventBus={this.logsEventBus}
|
||||
splitOpenFn={this.onSplitOpen('logs')}
|
||||
/>
|
||||
);
|
||||
@ -550,4 +557,4 @@ const mapDispatchToProps = {
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
export default compose(connector, withTheme2)(Explore) as React.ComponentType<{ exploreId: ExploreId }>;
|
||||
export default withTheme2(connector(Explore));
|
||||
|
@ -18,6 +18,8 @@ import {
|
||||
LoadingState,
|
||||
SplitOpen,
|
||||
TimeZone,
|
||||
DashboardCursorSync,
|
||||
EventBus,
|
||||
} from '@grafana/data';
|
||||
import { PanelRenderer } from '@grafana/runtime';
|
||||
import { GraphDrawStyle, LegendDisplayMode, TooltipDisplayMode, SortOrder } from '@grafana/schema';
|
||||
@ -29,7 +31,6 @@ import {
|
||||
useStyles2,
|
||||
useTheme2,
|
||||
} from '@grafana/ui';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { defaultGraphConfig, getGraphFieldConfig } from 'app/plugins/panel/timeseries/config';
|
||||
import { TimeSeriesOptions } from 'app/plugins/panel/timeseries/types';
|
||||
|
||||
@ -54,6 +55,7 @@ interface Props {
|
||||
onChangeTime: (timeRange: AbsoluteTimeRange) => void;
|
||||
graphStyle: ExploreGraphStyle;
|
||||
anchorToZero: boolean;
|
||||
eventBus: EventBus;
|
||||
}
|
||||
|
||||
export function ExploreGraph({
|
||||
@ -70,6 +72,7 @@ export function ExploreGraph({
|
||||
graphStyle,
|
||||
tooltipDisplayMode = TooltipDisplayMode.Single,
|
||||
anchorToZero,
|
||||
eventBus,
|
||||
}: Props) {
|
||||
const theme = useTheme2();
|
||||
const [showAllTimeSeries, setShowAllTimeSeries] = useState(false);
|
||||
@ -142,7 +145,8 @@ export function ExploreGraph({
|
||||
const seriesToShow = showAllTimeSeries ? dataWithConfig : dataWithConfig.slice(0, MAX_NUMBER_OF_TIME_SERIES);
|
||||
|
||||
const panelContext: PanelContext = {
|
||||
eventBus: appEvents,
|
||||
eventBus,
|
||||
sync: () => DashboardCursorSync.Crosshair,
|
||||
onSplitOpen: splitOpenFn,
|
||||
onToggleSeriesVisibility(label: string, mode: SeriesVisibilityChangeMode) {
|
||||
setBaseStructureRev((r) => r + 1);
|
||||
|
@ -3,7 +3,7 @@ import memoizeOne from 'memoize-one';
|
||||
import React from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { ExploreUrlState, EventBusExtended, EventBusSrv, GrafanaTheme2 } from '@grafana/data';
|
||||
import { ExploreUrlState, EventBusExtended, EventBusSrv, GrafanaTheme2, EventBus } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Themeable2, withTheme2 } from '@grafana/ui';
|
||||
import { config } from 'app/core/config';
|
||||
@ -50,6 +50,7 @@ interface OwnProps extends Themeable2 {
|
||||
exploreId: ExploreId;
|
||||
urlQuery: string;
|
||||
split: boolean;
|
||||
eventBus: EventBus;
|
||||
}
|
||||
|
||||
interface Props extends OwnProps, ConnectedProps<typeof connector> {}
|
||||
@ -143,12 +144,12 @@ class ExplorePaneContainerUnconnected extends React.PureComponent<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { theme, split, exploreId, initialized } = this.props;
|
||||
const { theme, split, exploreId, initialized, eventBus } = this.props;
|
||||
const styles = getStyles(theme);
|
||||
const exploreClass = cx(styles.explore, split && styles.exploreSplit);
|
||||
return (
|
||||
<div className={exploreClass} ref={this.getRef} data-testid={selectors.pages.Explore.General.container}>
|
||||
{initialized && <Explore exploreId={exploreId} />}
|
||||
{initialized && <Explore exploreId={exploreId} eventBus={eventBus} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { LoadingState, LogLevel, LogRowModel, MutableDataFrame, toUtc } from '@grafana/data';
|
||||
import { LoadingState, LogLevel, LogRowModel, MutableDataFrame, toUtc, EventBusSrv } from '@grafana/data';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { Logs } from './Logs';
|
||||
@ -37,6 +37,7 @@ describe('Logs', () => {
|
||||
getFieldLinks={() => {
|
||||
return [];
|
||||
}}
|
||||
eventBus={new EventBusSrv()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -23,6 +23,9 @@ import {
|
||||
SplitOpen,
|
||||
DataQueryResponse,
|
||||
CoreApp,
|
||||
DataHoverEvent,
|
||||
DataHoverClearEvent,
|
||||
EventBus,
|
||||
} from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import {
|
||||
@ -79,6 +82,7 @@ interface Props extends Themeable2 {
|
||||
getFieldLinks: (field: Field, rowIndex: number) => Array<LinkModel<Field>>;
|
||||
addResultsToCache: () => void;
|
||||
clearCache: () => void;
|
||||
eventBus: EventBus;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -108,6 +112,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
flipOrderTimer?: number;
|
||||
cancelFlippingTimer?: number;
|
||||
topLogsRef = createRef<HTMLDivElement>();
|
||||
logsVolumeEventBus: EventBus;
|
||||
|
||||
state: State = {
|
||||
showLabels: store.getBool(SETTINGS_KEYS.showLabels, false),
|
||||
@ -122,6 +127,11 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
forceEscape: false,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.logsVolumeEventBus = props.eventBus.newScopedBus('logsvolume', { onlyLocal: false });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.flipOrderTimer) {
|
||||
window.clearTimeout(this.flipOrderTimer);
|
||||
@ -132,6 +142,20 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
onLogRowHover = (row?: LogRowModel) => {
|
||||
if (!row) {
|
||||
this.props.eventBus.publish(new DataHoverClearEvent());
|
||||
} else {
|
||||
this.props.eventBus.publish(
|
||||
new DataHoverEvent({
|
||||
point: {
|
||||
time: row.timeEpochMs,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onChangeLogsSortOrder = () => {
|
||||
this.setState({ isFlipping: true });
|
||||
// we are using setTimeout here to make sure that disabled button is rendered before the rendering of reordered logs
|
||||
@ -367,6 +391,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
splitOpen={splitOpen}
|
||||
onLoadLogsVolume={() => loadLogsVolumeData(exploreId)}
|
||||
onHiddenSeriesChanged={this.onToggleLogLevel}
|
||||
eventBus={this.logsVolumeEventBus}
|
||||
/>
|
||||
)}
|
||||
</Collapse>
|
||||
@ -480,6 +505,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
||||
onClickHideDetectedField={this.hideDetectedField}
|
||||
app={CoreApp.Explore}
|
||||
scrollElement={scrollElement}
|
||||
onLogRowHover={this.onLogRowHover}
|
||||
/>
|
||||
</div>
|
||||
<LogsNavigation
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
LoadingState,
|
||||
LogRowModel,
|
||||
RawTimeRange,
|
||||
EventBus,
|
||||
SplitOpen,
|
||||
} from '@grafana/data';
|
||||
import { Collapse } from '@grafana/ui';
|
||||
@ -35,6 +36,7 @@ interface LogsContainerProps extends PropsFromRedux {
|
||||
onClickFilterOutLabel?: (key: string, value: string) => void;
|
||||
onStartScanning: () => void;
|
||||
onStopScanning: () => void;
|
||||
eventBus: EventBus;
|
||||
splitOpenFn: SplitOpen;
|
||||
}
|
||||
|
||||
@ -156,6 +158,7 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
addResultsToCache={() => addResultsToCache(exploreId)}
|
||||
clearCache={() => clearCache(exploreId)}
|
||||
scrollElement={scrollElement}
|
||||
eventBus={this.props.eventBus}
|
||||
/>
|
||||
</LogsCrossFadeTransition>
|
||||
</>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { DataQueryResponse, LoadingState } from '@grafana/data';
|
||||
import { DataQueryResponse, LoadingState, EventBusSrv } from '@grafana/data';
|
||||
|
||||
import { LogsVolumePanel } from './LogsVolumePanel';
|
||||
|
||||
@ -25,6 +25,7 @@ function renderPanel(logsVolumeData?: DataQueryResponse) {
|
||||
logLinesBasedDataVisibleRange={undefined}
|
||||
onLoadLogsVolume={() => {}}
|
||||
onHiddenSeriesChanged={() => null}
|
||||
eventBus={new EventBusSrv()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
LoadingState,
|
||||
SplitOpen,
|
||||
TimeZone,
|
||||
EventBus,
|
||||
} from '@grafana/data';
|
||||
import { Alert, Button, Collapse, InlineField, TooltipDisplayMode, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
@ -25,6 +26,7 @@ type Props = {
|
||||
onUpdateTimeRange: (timeRange: AbsoluteTimeRange) => void;
|
||||
onLoadLogsVolume: () => void;
|
||||
onHiddenSeriesChanged: (hiddenSeries: string[]) => void;
|
||||
eventBus: EventBus;
|
||||
};
|
||||
|
||||
const SHORT_ERROR_MESSAGE_LIMIT = 100;
|
||||
@ -131,6 +133,7 @@ export function LogsVolumePanel(props: Props) {
|
||||
tooltipDisplayMode={TooltipDisplayMode.Multi}
|
||||
onHiddenSeriesChanged={onHiddenSeriesChanged}
|
||||
anchorToZero
|
||||
eventBus={props.eventBus}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { ErrorBoundaryAlert } from '@grafana/ui';
|
||||
import { ErrorBoundaryAlert, usePanelContext } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
||||
@ -38,6 +38,8 @@ function Wrapper(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
|
||||
const navModel = useNavModel('explore');
|
||||
const { get } = useCorrelations();
|
||||
const { warning } = useAppNotification();
|
||||
const panelCtx = usePanelContext();
|
||||
const eventBus = useRef(panelCtx.eventBus.newScopedBus('explore', { onlyLocal: false }));
|
||||
|
||||
useEffect(() => {
|
||||
//This is needed for breadcrumbs and topnav.
|
||||
@ -102,11 +104,21 @@ function Wrapper(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
|
||||
<ExploreActions exploreIdLeft={ExploreId.left} exploreIdRight={ExploreId.right} />
|
||||
<div className={styles.exploreWrapper}>
|
||||
<ErrorBoundaryAlert style="page">
|
||||
<ExplorePaneContainer split={hasSplit} exploreId={ExploreId.left} urlQuery={queryParams.left} />
|
||||
<ExplorePaneContainer
|
||||
split={hasSplit}
|
||||
exploreId={ExploreId.left}
|
||||
urlQuery={queryParams.left}
|
||||
eventBus={eventBus.current}
|
||||
/>
|
||||
</ErrorBoundaryAlert>
|
||||
{hasSplit && (
|
||||
<ErrorBoundaryAlert style="page">
|
||||
<ExplorePaneContainer split={hasSplit} exploreId={ExploreId.right} urlQuery={queryParams.right} />
|
||||
<ExplorePaneContainer
|
||||
split={hasSplit}
|
||||
exploreId={ExploreId.right}
|
||||
urlQuery={queryParams.right}
|
||||
eventBus={eventBus.current}
|
||||
/>
|
||||
</ErrorBoundaryAlert>
|
||||
)}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user