diff --git a/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx b/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx index c4c5b58a412..83e1f292643 100644 --- a/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx +++ b/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx @@ -105,7 +105,7 @@ export const PageToolbar: FC = React.memo( )} - {(title || leftItems?.length) && ( + {(title || Boolean(leftItems?.length)) && (
{title && (

diff --git a/packages/grafana-ui/src/components/ToolbarButton/ToolbarButtonRow.tsx b/packages/grafana-ui/src/components/ToolbarButton/ToolbarButtonRow.tsx index 40ea997aee5..10e1cb9f31f 100644 --- a/packages/grafana-ui/src/components/ToolbarButton/ToolbarButtonRow.tsx +++ b/packages/grafana-ui/src/components/ToolbarButton/ToolbarButtonRow.tsx @@ -15,6 +15,8 @@ export interface Props extends HTMLAttributes { alignment?: 'left' | 'right'; } +const OVERFLOW_BUTTON_ID = 'overflow-button'; + export const ToolbarButtonRow = forwardRef( ({ alignment = 'left', className, children, ...rest }, ref) => { const [childVisibility, setChildVisibility] = useState( @@ -53,7 +55,10 @@ export const ToolbarButtonRow = forwardRef( ); if (containerRef.current) { Array.from(containerRef.current.children).forEach((item) => { - intersectionObserver.observe(item); + // don't observe the overflow button + if (item instanceof HTMLElement && item.dataset.testid !== OVERFLOW_BUTTON_ID) { + intersectionObserver.observe(item); + } }); } return () => intersectionObserver.disconnect(); @@ -70,12 +75,11 @@ export const ToolbarButtonRow = forwardRef(

))} {childVisibility.includes(false) && ( - <> +
setShowOverflowItems(!showOverflowItems)} - className={styles.overflowButton} icon="ellipsis-v" iconOnly narrow @@ -87,7 +91,7 @@ export const ToolbarButtonRow = forwardRef(
)} - + )} ); diff --git a/public/app/features/explore/Explore.test.tsx b/public/app/features/explore/Explore.test.tsx index 4a5b6555a58..d7896d34129 100644 --- a/public/app/features/explore/Explore.test.tsx +++ b/public/app/features/explore/Explore.test.tsx @@ -82,6 +82,7 @@ const dummyProps: Props = { showNodeGraph: true, showFlameGraph: true, splitOpen: (() => {}) as any, + splitted: false, changeGraphStyle: () => {}, graphStyle: 'lines', }; diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 93ac4298398..eb0cd59c80c 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -38,6 +38,7 @@ import { TraceViewContainer } from './TraceView/TraceViewContainer'; import { changeSize, changeGraphStyle } from './state/explorePane'; import { splitOpen } from './state/main'; import { addQueryRow, modifyQueries, scanStart, scanStopAction, setQueries } from './state/query'; +import { isSplit } from './state/selectors'; import { makeAbsoluteTime, updateTimeRange } from './state/time'; const getStyles = (theme: GrafanaTheme2) => { @@ -67,6 +68,9 @@ const getStyles = (theme: GrafanaTheme2) => { padding: ${theme.spacing(2)}; padding-top: 0; `, + exploreContainerTopnav: css` + padding-top: ${theme.spacing(2)}; + `, }; }; @@ -349,6 +353,7 @@ export class Explore extends React.PureComponent { showTrace, showNodeGraph, showFlameGraph, + splitted, timeZone, } = this.props; const { openDrawer } = this.state; @@ -377,7 +382,11 @@ export class Explore extends React.PureComponent { {datasourceMissing ? this.renderEmptyState(styles.exploreContainer) : null} {datasourceInstance && ( -
+
{ return ( { ); }; - render() { + renderActions = () => { const { - datasourceMissing, + splitted, + isLive, exploreId, - loading, range, timeZone, fiscalYearStartMonth, - splitted, - syncedTimes, - refreshInterval, onChangeTime, - hasLiveOption, - isLive, - isPaused, - containerWidth, + syncedTimes, onChangeTimeZone, onChangeFiscalYearStartMonth, - topOfViewRef, + refreshInterval, + loading, + isPaused, + hasLiveOption, + containerWidth, } = this.props; - - const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false; const showSmallTimePicker = splitted || containerWidth < 1210; const showExploreToDashboard = contextSrv.hasAccess(AccessControlAction.DashboardsCreate, contextSrv.isEditor) || contextSrv.hasAccess(AccessControlAction.DashboardsWrite, contextSrv.isEditor); - return ( + return [ + !splitted ? ( + + Split + + ) : ( + + Close + + ), + + config.featureToggles.explore2Dashboard && showExploreToDashboard && ( + + + + ), + + !isLive && ( + + ), + + this.renderRefreshPicker(showSmallTimePicker), + + refreshInterval && ( + + ), + + hasLiveOption && ( + + {(c) => { + const controls = { + ...c, + start: () => { + reportInteraction('grafana_explore_logs_live_tailing_clicked', { + datasourceType: this.props.datasourceType, + }); + c.start(); + }, + }; + return ( + + ); + }} + + ), + ].filter(Boolean); + }; + + render() { + const { datasourceMissing, exploreId, splitted, containerWidth, topOfViewRef } = this.props; + + const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false; + const isTopnav = config.featureToggles.topnav; + + const getDashNav = () => ( + + ); + + const getDataSourcePicker = () => + !datasourceMissing && ( + + ); + + const topNavActions = [ + getDashNav(), + !splitted && getDataSourcePicker(), +
, + + {this.renderActions()} + , + ].filter(Boolean); + + const toolbarLeftItems = [exploreId === ExploreId.left && getDashNav(), getDataSourcePicker()].filter(Boolean); + + const toolbarLeftItemsTopNav = [ + exploreId === ExploreId.left && ( + ].filter( + Boolean + )} + /> + ), + getDataSourcePicker(), + ].filter(Boolean); + + return isTopnav && !splitted ? ( +
+ +
+ ) : (
- ), - !datasourceMissing && ( - - ), - ].filter(Boolean)} + title={exploreId === ExploreId.left && !isTopnav ? 'Explore' : undefined} + pageIcon={exploreId === ExploreId.left && !isTopnav ? 'compass' : undefined} + leftItems={isTopnav ? toolbarLeftItemsTopNav : toolbarLeftItems} > - <> - {!splitted ? ( - - Split - - ) : ( - - Close - - )} - - {config.featureToggles.explore2Dashboard && showExploreToDashboard && ( - - - - )} - - {!isLive && ( - - )} - - {this.renderRefreshPicker(showSmallTimePicker)} - - {refreshInterval && } - - {hasLiveOption && ( - - {(c) => { - const controls = { - ...c, - start: () => { - reportInteraction('grafana_explore_logs_live_tailing_clicked', { - datasourceType: this.props.datasourceType, - }); - c.start(); - }, - }; - return ( - - ); - }} - - )} - + {this.renderActions()}
);