mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
SingleTopNav: Handle explore action placement (#94812)
* move query library + share buttons to toolbar * add share text to button * need spacer when not in singleTopNav * fix unit tests * use secondary button styling when singleTopNav is enabled
This commit is contained in:
parent
54c500cd94
commit
65ce173d3f
@ -21,7 +21,7 @@ import { CorrelationEditorModeBar } from './CorrelationEditorModeBar';
|
||||
import { ExploreActions } from './ExploreActions';
|
||||
import { ExploreDrawer } from './ExploreDrawer';
|
||||
import { ExplorePaneContainer } from './ExplorePaneContainer';
|
||||
import { QueriesDrawerContextProvider, useQueriesDrawerContext } from './QueriesDrawer/QueriesDrawerContext';
|
||||
import { useQueriesDrawerContext } from './QueriesDrawer/QueriesDrawerContext';
|
||||
import { QUERY_LIBRARY_LOCAL_STORAGE_KEYS } from './QueryLibrary/QueryLibrary';
|
||||
import { queryLibraryTrackAddFromQueryRow } from './QueryLibrary/QueryLibraryAnalyticsEvents';
|
||||
import { QueryTemplateForm } from './QueryLibrary/QueryTemplateForm';
|
||||
@ -37,11 +37,7 @@ const MIN_PANE_WIDTH = 200;
|
||||
const QUERY_LIBRARY_ACTION_KEY = 'queryLibraryAction';
|
||||
|
||||
export default function ExplorePage(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
|
||||
return (
|
||||
<QueriesDrawerContextProvider>
|
||||
<ExplorePageContent {...props} />
|
||||
</QueriesDrawerContextProvider>
|
||||
);
|
||||
return <ExplorePageContent {...props} />;
|
||||
}
|
||||
|
||||
function ExplorePageContent(props: GrafanaRouteComponentProps<{}, ExploreQueryParams>) {
|
||||
|
@ -4,6 +4,7 @@ import { useMemo } from 'react';
|
||||
import { shallowEqual } from 'react-redux';
|
||||
|
||||
import { DataSourceInstanceSettings, RawTimeRange, GrafanaTheme2 } from '@grafana/data';
|
||||
import { Components } from '@grafana/e2e-selectors';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import {
|
||||
defaultIntervals,
|
||||
@ -26,6 +27,7 @@ import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors
|
||||
|
||||
import { ExploreTimeControls } from './ExploreTimeControls';
|
||||
import { LiveTailButton } from './LiveTailButton';
|
||||
import { useQueriesDrawerContext } from './QueriesDrawer/QueriesDrawerContext';
|
||||
import { QueriesDrawerDropdown } from './QueriesDrawer/QueriesDrawerDropdown';
|
||||
import { ShortLinkButtonMenu } from './ShortLinkButtonMenu';
|
||||
import { ToolbarExtensionPoint } from './extensions/ToolbarExtensionPoint';
|
||||
@ -90,6 +92,7 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
const isCorrelationsEditorMode = correlationDetails?.editorMode || false;
|
||||
const isLeftPane = useSelector(isLeftPaneSelector(exploreId));
|
||||
const isSingleTopNav = config.featureToggles.singleTopNav;
|
||||
const { drawerOpened, setDrawerOpened, queryLibraryAvailable } = useQueriesDrawerContext();
|
||||
|
||||
const shouldRotateSplitIcon = useMemo(
|
||||
() => (isLeftPane && isLargerPane) || (!isLeftPane && !isLargerPane),
|
||||
@ -202,16 +205,32 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
dispatch(changeRefreshInterval({ exploreId, refreshInterval }));
|
||||
};
|
||||
|
||||
const navBarActions = [<ShortLinkButtonMenu key="share" />, <div style={{ flex: 1 }} key="spacer0" />];
|
||||
const navBarActions = [<ShortLinkButtonMenu key="share" />];
|
||||
|
||||
if (isSingleTopNav) {
|
||||
if (queryLibraryAvailable) {
|
||||
navBarActions.unshift(<QueriesDrawerDropdown key="queryLibrary" variant="full" />);
|
||||
} else {
|
||||
navBarActions.unshift(
|
||||
<ToolbarButton
|
||||
variant={drawerOpened ? 'active' : 'canvas'}
|
||||
aria-label={t('explore.secondary-actions.query-history-button-aria-label', 'Query history')}
|
||||
onClick={() => setDrawerOpened(!drawerOpened)}
|
||||
data-testid={Components.QueryTab.queryHistoryButton}
|
||||
icon="history"
|
||||
>
|
||||
<Trans i18nKey="explore.secondary-actions.query-history-button">Query history</Trans>
|
||||
</ToolbarButton>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
navBarActions.push(<div style={{ flex: 1 }} key="spacer0" />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{refreshInterval && <SetInterval func={onRunQuery} interval={refreshInterval} loading={loading} />}
|
||||
{!isSingleTopNav && (
|
||||
<div>
|
||||
<AppChromeUpdate actions={navBarActions} />
|
||||
</div>
|
||||
)}
|
||||
<AppChromeUpdate actions={navBarActions} />
|
||||
<PageToolbar
|
||||
aria-label={t('explore.toolbar.aria-label', 'Explore toolbar')}
|
||||
leftItems={[
|
||||
@ -236,12 +255,11 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
|
||||
hideTextValue={showSmallDataSourcePicker}
|
||||
width={showSmallDataSourcePicker ? 8 : undefined}
|
||||
/>,
|
||||
isSingleTopNav && <ShortLinkButtonMenu key="share" />,
|
||||
].filter(Boolean)}
|
||||
forceShowLeftItems
|
||||
>
|
||||
{[
|
||||
<QueriesDrawerDropdown key="queryLibrary" variant={splitted ? 'compact' : 'full'} />,
|
||||
!isSingleTopNav && <QueriesDrawerDropdown key="queryLibrary" variant={splitted ? 'compact' : 'full'} />,
|
||||
!splitted ? (
|
||||
<ToolbarButton
|
||||
variant="canvas"
|
||||
|
@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Components } from '@grafana/e2e-selectors';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { ToolbarButton, useTheme2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
@ -32,9 +33,10 @@ export function SecondaryActions(props: Props) {
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme);
|
||||
const { drawerOpened, setDrawerOpened, queryLibraryAvailable } = useQueriesDrawerContext();
|
||||
const isSingleTopNav = config.featureToggles.singleTopNav;
|
||||
|
||||
// When queryLibraryAvailable=true we show the button in the toolbar (see QueriesDrawerDropdown)
|
||||
const showHistoryButton = !props.richHistoryRowButtonHidden && !queryLibraryAvailable;
|
||||
const showHistoryButton = !props.richHistoryRowButtonHidden && !queryLibraryAvailable && !isSingleTopNav;
|
||||
|
||||
return (
|
||||
<div className={styles.containerMargin}>
|
||||
|
@ -2,8 +2,8 @@ import { useState } from 'react';
|
||||
|
||||
import { IconName } from '@grafana/data';
|
||||
import { reportInteraction, config } from '@grafana/runtime';
|
||||
import { ToolbarButton, Dropdown, Menu, Stack, ToolbarButtonRow, MenuGroup } from '@grafana/ui';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { ToolbarButton, Dropdown, Menu, MenuGroup, ButtonGroup } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { copyStringToClipboard } from 'app/core/utils/explore';
|
||||
import { createAndCopyShortLink } from 'app/core/utils/shortLinks';
|
||||
import { useSelector } from 'app/types';
|
||||
@ -39,6 +39,7 @@ export function ShortLinkButtonMenu() {
|
||||
const panes = useSelector(selectPanes);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [lastSelected, setLastSelected] = useState(defaultMode);
|
||||
const isSingleTopNav = config.featureToggles.singleTopNav;
|
||||
const onCopyLink = (shorten: boolean, absTime: boolean, url?: string) => {
|
||||
if (shorten) {
|
||||
createAndCopyShortLink(url || global.location.href);
|
||||
@ -131,27 +132,29 @@ export function ShortLinkButtonMenu() {
|
||||
|
||||
// we need the Toolbar button click to be an action separate from opening/closing the menu
|
||||
return (
|
||||
<ToolbarButtonRow>
|
||||
<Stack gap={0} direction="row" alignItems="center" wrap="nowrap">
|
||||
<ButtonGroup>
|
||||
<ToolbarButton
|
||||
tooltip={lastSelected.label}
|
||||
icon={lastSelected.icon}
|
||||
iconOnly={!isSingleTopNav}
|
||||
variant={isSingleTopNav ? 'canvas' : 'default'}
|
||||
narrow={true}
|
||||
onClick={() => {
|
||||
const url = lastSelected.getUrl();
|
||||
onCopyLink(lastSelected.shorten, lastSelected.absTime, url);
|
||||
}}
|
||||
aria-label={t('explore.toolbar.copy-shortened-link', 'Copy shortened URL')}
|
||||
>
|
||||
{isSingleTopNav && <Trans i18nKey="explore.toolbar.copy-shortened-link-label">Share</Trans>}
|
||||
</ToolbarButton>
|
||||
<Dropdown overlay={MenuActions} placement="bottom-end" onVisibleChange={setIsOpen}>
|
||||
<ToolbarButton
|
||||
tooltip={lastSelected.label}
|
||||
icon={lastSelected.icon}
|
||||
iconOnly={true}
|
||||
narrow={true}
|
||||
onClick={() => {
|
||||
const url = lastSelected.getUrl();
|
||||
onCopyLink(lastSelected.shorten, lastSelected.absTime, url);
|
||||
}}
|
||||
aria-label={t('explore.toolbar.copy-shortened-link', 'Copy shortened URL')}
|
||||
variant={isSingleTopNav ? 'canvas' : 'default'}
|
||||
isOpen={isOpen}
|
||||
aria-label={t('explore.toolbar.copy-shortened-link-menu', 'Open copy link options')}
|
||||
/>
|
||||
<Dropdown overlay={MenuActions} placement="bottom-end" onVisibleChange={setIsOpen}>
|
||||
<ToolbarButton
|
||||
narrow={true}
|
||||
isOpen={isOpen}
|
||||
aria-label={t('explore.toolbar.copy-shortened-link-menu', 'Open copy link options')}
|
||||
/>
|
||||
</Dropdown>
|
||||
</Stack>
|
||||
</ToolbarButtonRow>
|
||||
</Dropdown>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import { LokiQuery } from '../../../../plugins/datasource/loki/types';
|
||||
import { ExploreQueryParams } from '../../../../types';
|
||||
import { initialUserState } from '../../../profile/state/reducers';
|
||||
import ExplorePage from '../../ExplorePage';
|
||||
import { QueriesDrawerContextProvider } from '../../QueriesDrawer/QueriesDrawerContext';
|
||||
|
||||
type DatasourceSetup = { settings: DataSourceInstanceSettings; api: DataSourceApi };
|
||||
|
||||
@ -174,11 +175,13 @@ export function setupExplore(options?: SetupOptions): {
|
||||
<Provider store={storeState}>
|
||||
<GrafanaContext.Provider value={contextMock}>
|
||||
<Router history={history}>
|
||||
<Route
|
||||
path="/explore"
|
||||
exact
|
||||
render={(props) => <GrafanaRoute {...props} route={{ component: ExplorePage, path: '/explore' }} />}
|
||||
/>
|
||||
<QueriesDrawerContextProvider>
|
||||
<Route
|
||||
path="/explore"
|
||||
exact
|
||||
render={(props) => <GrafanaRoute {...props} route={{ component: ExplorePage, path: '/explore' }} />}
|
||||
/>
|
||||
</QueriesDrawerContextProvider>
|
||||
</Router>
|
||||
</GrafanaContext.Provider>
|
||||
</Provider>
|
||||
|
@ -12,6 +12,7 @@ import { AppChrome } from '../core/components/AppChrome/AppChrome';
|
||||
import { AppNotificationList } from '../core/components/AppNotifications/AppNotificationList';
|
||||
import { ModalsContextProvider } from '../core/context/ModalsContextProvider';
|
||||
import { useSidecar } from '../core/context/SidecarContext';
|
||||
import { QueriesDrawerContextProvider } from '../features/explore/QueriesDrawer/QueriesDrawerContext';
|
||||
import AppRootPage from '../features/plugins/components/AppRootPage';
|
||||
|
||||
import { createLocationStorageHistory } from './utils';
|
||||
@ -26,22 +27,24 @@ export function RouterWrapper(props: RouterWrapperProps) {
|
||||
<Router history={locationService.getHistory()}>
|
||||
<LocationServiceProvider service={locationService}>
|
||||
<CompatRouter>
|
||||
<ModalsContextProvider>
|
||||
<AppChrome>
|
||||
<AngularRoot />
|
||||
<AppNotificationList />
|
||||
<Stack gap={0} grow={1} direction="column">
|
||||
{props.pageBanners.map((Banner, index) => (
|
||||
<Banner key={index.toString()} />
|
||||
<QueriesDrawerContextProvider>
|
||||
<ModalsContextProvider>
|
||||
<AppChrome>
|
||||
<AngularRoot />
|
||||
<AppNotificationList />
|
||||
<Stack gap={0} grow={1} direction="column">
|
||||
{props.pageBanners.map((Banner, index) => (
|
||||
<Banner key={index.toString()} />
|
||||
))}
|
||||
{props.routes}
|
||||
</Stack>
|
||||
{props.bodyRenderHooks.map((Hook, index) => (
|
||||
<Hook key={index.toString()} />
|
||||
))}
|
||||
{props.routes}
|
||||
</Stack>
|
||||
{props.bodyRenderHooks.map((Hook, index) => (
|
||||
<Hook key={index.toString()} />
|
||||
))}
|
||||
</AppChrome>
|
||||
<ModalRoot />
|
||||
</ModalsContextProvider>
|
||||
</AppChrome>
|
||||
<ModalRoot />
|
||||
</ModalsContextProvider>
|
||||
</QueriesDrawerContextProvider>
|
||||
</CompatRouter>
|
||||
</LocationServiceProvider>
|
||||
</Router>
|
||||
|
@ -1018,6 +1018,7 @@
|
||||
"copy-links-normal-category": "Normal URL links",
|
||||
"copy-shortened-link": "Copy shortened URL",
|
||||
"copy-shortened-link-abs-time": "Copy absolute shortened URL",
|
||||
"copy-shortened-link-label": "Share",
|
||||
"copy-shortened-link-menu": "Open copy link options",
|
||||
"refresh-picker-cancel": "Cancel",
|
||||
"refresh-picker-run": "Run query",
|
||||
|
@ -1018,6 +1018,7 @@
|
||||
"copy-links-normal-category": "Ńőřmäľ ŮŖĿ ľįʼnĸş",
|
||||
"copy-shortened-link": "Cőpy şĥőřŧęʼnęđ ŮŖĿ",
|
||||
"copy-shortened-link-abs-time": "Cőpy äþşőľūŧę şĥőřŧęʼnęđ ŮŖĿ",
|
||||
"copy-shortened-link-label": "Ŝĥäřę",
|
||||
"copy-shortened-link-menu": "Øpęʼn čőpy ľįʼnĸ őpŧįőʼnş",
|
||||
"refresh-picker-cancel": "Cäʼnčęľ",
|
||||
"refresh-picker-run": "Ŗūʼn qūęřy",
|
||||
|
Loading…
Reference in New Issue
Block a user