grafana/public/app/features/explore/ExplorePage.tsx
Torkel Ödegaard 9cdcbab2fc
Explore: Remove unnessary scroll on split pane (#61064)
* Explore: Remove unnessary scroll on split pane

* set overflow to auto

Co-authored-by: Giordano Ricci <me@giordanoricci.com>
2023-01-09 19:55:38 +01:00

176 lines
6.5 KiB
TypeScript

import { css } from '@emotion/css';
import { inRange } from 'lodash';
import React, { useEffect, useRef, useState } from 'react';
import { useWindowSize } from 'react-use';
import { isTruthy } from '@grafana/data';
import { locationService } from '@grafana/runtime';
import { ErrorBoundaryAlert, usePanelContext } from '@grafana/ui';
import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper';
import { useGrafana } from 'app/core/context/GrafanaContext';
import { useAppNotification } from 'app/core/copy/appNotification';
import { useNavModel } from 'app/core/hooks/useNavModel';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { useDispatch, useSelector } from 'app/types';
import { ExploreId, ExploreQueryParams } from 'app/types/explore';
import { Branding } from '../../core/components/Branding/Branding';
import { useCorrelations } from '../correlations/useCorrelations';
import { ExploreActions } from './ExploreActions';
import { ExplorePaneContainer } from './ExplorePaneContainer';
import { lastSavedUrl, saveCorrelationsAction, resetExploreAction, splitSizeUpdateAction } from './state/main';
const styles = {
pageScrollbarWrapper: css`
width: 100%;
flex-grow: 1;
min-height: 0;
height: 100%;
position: relative;
`,
};
export function ExplorePage(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
useExplorePageTitle();
const dispatch = useDispatch();
const queryParams = props.queryParams;
const { keybindings, chrome, config } = useGrafana();
const navModel = useNavModel('explore');
const { get } = useCorrelations();
const { warning } = useAppNotification();
const panelCtx = usePanelContext();
const eventBus = useRef(panelCtx.eventBus.newScopedBus('explore', { onlyLocal: false }));
const [rightPaneWidthRatio, setRightPaneWidthRatio] = useState(0.5);
const { width: windowWidth } = useWindowSize();
const minWidth = 200;
const exploreState = useSelector((state) => state.explore);
useEffect(() => {
//This is needed for breadcrumbs and topnav.
//We should probably abstract this out at some point
chrome.update({ sectionNav: navModel.node });
}, [chrome, navModel]);
useEffect(() => {
keybindings.setupTimeRangeBindings(false);
}, [keybindings]);
useEffect(() => {
if (!config.featureToggles.correlations) {
dispatch(saveCorrelationsAction([]));
} else {
get.execute();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (get.value) {
dispatch(saveCorrelationsAction(get.value));
} else if (get.error) {
dispatch(saveCorrelationsAction([]));
warning(
'Could not load correlations.',
'Correlations data could not be loaded, DataLinks may have partial data.'
);
}
}, [get.value, get.error, dispatch, warning]);
useEffect(() => {
lastSavedUrl.left = undefined;
lastSavedUrl.right = undefined;
// timeSrv (which is used internally) on init reads `from` and `to` param from the URL and updates itself
// using those value regardless of what is passed to the init method.
// The updated value is then used by Explore to get the range for each pane.
// This means that if `from` and `to` parameters are present in the URL,
// it would be impossible to change the time range in Explore.
// We are only doing this on mount for 2 reasons:
// 1: Doing it on update means we'll enter a render loop.
// 2: when parsing time in Explore (before feeding it to timeSrv) we make sure `from` is before `to` inside
// each pane state in order to not trigger un URL update from timeSrv.
const searchParams = locationService.getSearchObject();
if (searchParams.from || searchParams.to) {
locationService.partial({ from: undefined, to: undefined }, true);
}
return () => {
// Cleaning up Explore state so that when navigating back to Explore it starts from a blank state
dispatch(resetExploreAction());
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- dispatch is stable, doesn't need to be in the deps array
}, []);
const updateSplitSize = (size: number) => {
const evenSplitWidth = windowWidth / 2;
const areBothSimilar = inRange(size, evenSplitWidth - 100, evenSplitWidth + 100);
if (areBothSimilar) {
dispatch(splitSizeUpdateAction({ largerExploreId: undefined }));
} else {
dispatch(
splitSizeUpdateAction({
largerExploreId: size > evenSplitWidth ? ExploreId.right : ExploreId.left,
})
);
}
setRightPaneWidthRatio(size / windowWidth);
};
const hasSplit = Boolean(queryParams.left) && Boolean(queryParams.right);
let widthCalc = 0;
if (hasSplit) {
if (!exploreState.evenSplitPanes && exploreState.maxedExploreId) {
widthCalc = exploreState.maxedExploreId === ExploreId.right ? windowWidth - minWidth : minWidth;
} else if (exploreState.evenSplitPanes) {
widthCalc = Math.floor(windowWidth / 2);
} else if (rightPaneWidthRatio !== undefined) {
widthCalc = windowWidth * rightPaneWidthRatio;
}
}
return (
<div className={styles.pageScrollbarWrapper}>
<ExploreActions exploreIdLeft={ExploreId.left} exploreIdRight={ExploreId.right} />
<SplitPaneWrapper
splitOrientation="vertical"
paneSize={widthCalc}
minSize={minWidth}
maxSize={minWidth * -1}
primary="second"
splitVisible={hasSplit}
paneStyle={{ overflow: 'auto', display: 'flex', flexDirection: 'column' }}
onDragFinished={(size) => {
if (size) {
updateSplitSize(size);
}
}}
>
<ErrorBoundaryAlert style="page">
<ExplorePaneContainer exploreId={ExploreId.left} urlQuery={queryParams.left} eventBus={eventBus.current} />
</ErrorBoundaryAlert>
{hasSplit && (
<ErrorBoundaryAlert style="page">
<ExplorePaneContainer
exploreId={ExploreId.right}
urlQuery={queryParams.right}
eventBus={eventBus.current}
/>
</ErrorBoundaryAlert>
)}
</SplitPaneWrapper>
</div>
);
}
const useExplorePageTitle = () => {
const navModel = useNavModel('explore');
const datasources = useSelector((state) =>
[state.explore.left.datasourceInstance?.name, state.explore.right?.datasourceInstance?.name].filter(isTruthy)
);
document.title = `${navModel.main.text} - ${datasources.join(' | ')} - ${Branding.AppTitle}`;
};