mirror of https://github.com/grafana/grafana
Explore: Show a drawer with tabs for the library and query history (#86279)
* Create basic feature toggle * Rename context to reflect it contains query history and query library * Update icons and variants * Rename hooks * Update tests * Fix mock * Add tracking * Turn button into a toggle * Make dropdown active as well This is required to have better UI and an indication of selected state in split view * Update Query Library icon This is to make it consistent with the toolbar button * Hide query history button when query library is available This is to avoid confusing UX with 2 button triggering the drawer but with slightly different behavior * Make the drawer bigger for query library To avoid confusion for current users and test it internally a bit more it's behind a feature toggle. Bigger drawer may obstruct the view and add more friction in the UX. * Fix tests The test was failing because queryLibraryAvailable was set to true for tests. This change makes it more explicit what use case is being tested * Remove active state underline from the dropdown * Allow closing Query Library drawer from the toolbar * Simplify dropdown designpull/86812/head
parent
de589b98c7
commit
f6e472f879
|
@ -0,0 +1,63 @@ |
|||||||
|
import React, { PropsWithChildren, useState, createContext, useContext, useEffect } from 'react'; |
||||||
|
|
||||||
|
import { config } from '@grafana/runtime'; |
||||||
|
import { useSelector } from 'app/types'; |
||||||
|
|
||||||
|
import { selectRichHistorySettings } from '../state/selectors'; |
||||||
|
|
||||||
|
export enum Tabs { |
||||||
|
QueryLibrary = 'Query library', |
||||||
|
RichHistory = 'Query history', |
||||||
|
Starred = 'Starred', |
||||||
|
Settings = 'Settings', |
||||||
|
} |
||||||
|
|
||||||
|
type QueryLibraryContextType = { |
||||||
|
selectedTab?: Tabs; |
||||||
|
setSelectedTab: (tab: Tabs) => void; |
||||||
|
queryLibraryAvailable: boolean; |
||||||
|
drawerOpened: boolean; |
||||||
|
setDrawerOpened: (value: boolean) => void; |
||||||
|
}; |
||||||
|
|
||||||
|
export const QueriesDrawerContext = createContext<QueryLibraryContextType>({ |
||||||
|
selectedTab: undefined, |
||||||
|
setSelectedTab: () => {}, |
||||||
|
queryLibraryAvailable: false, |
||||||
|
drawerOpened: false, |
||||||
|
setDrawerOpened: () => {}, |
||||||
|
}); |
||||||
|
|
||||||
|
export function useQueriesDrawerContext() { |
||||||
|
return useContext(QueriesDrawerContext); |
||||||
|
} |
||||||
|
|
||||||
|
export function QueriesDrawerContextProvider({ children }: PropsWithChildren) { |
||||||
|
const queryLibraryAvailable = config.featureToggles.queryLibrary === true; |
||||||
|
const [selectedTab, setSelectedTab] = useState<Tabs | undefined>( |
||||||
|
queryLibraryAvailable ? Tabs.QueryLibrary : undefined |
||||||
|
); |
||||||
|
const [drawerOpened, setDrawerOpened] = useState<boolean>(false); |
||||||
|
|
||||||
|
const settings = useSelector(selectRichHistorySettings); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (settings && !queryLibraryAvailable) { |
||||||
|
setSelectedTab(settings.starredTabAsFirstTab ? Tabs.Starred : Tabs.RichHistory); |
||||||
|
} |
||||||
|
}, [settings, setSelectedTab, queryLibraryAvailable]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<QueriesDrawerContext.Provider |
||||||
|
value={{ |
||||||
|
queryLibraryAvailable, |
||||||
|
selectedTab, |
||||||
|
setSelectedTab, |
||||||
|
drawerOpened, |
||||||
|
setDrawerOpened, |
||||||
|
}} |
||||||
|
> |
||||||
|
{children} |
||||||
|
</QueriesDrawerContext.Provider> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
import { css } from '@emotion/css'; |
||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
import { Button, ButtonGroup, Dropdown, Menu, ToolbarButton } from '@grafana/ui'; |
||||||
|
import { useStyles2 } from '@grafana/ui/'; |
||||||
|
|
||||||
|
import { Tabs, useQueriesDrawerContext } from './QueriesDrawerContext'; |
||||||
|
import { i18n } from './utils'; |
||||||
|
|
||||||
|
type Props = { |
||||||
|
variant: 'compact' | 'full'; |
||||||
|
}; |
||||||
|
|
||||||
|
export function QueriesDrawerDropdown({ variant }: Props) { |
||||||
|
const { selectedTab, setSelectedTab, queryLibraryAvailable, drawerOpened, setDrawerOpened } = |
||||||
|
useQueriesDrawerContext(); |
||||||
|
|
||||||
|
const styles = useStyles2(getStyles); |
||||||
|
|
||||||
|
if (!queryLibraryAvailable) { |
||||||
|
return undefined; |
||||||
|
} |
||||||
|
|
||||||
|
function toggle(tab: Tabs) { |
||||||
|
setSelectedTab(tab); |
||||||
|
setDrawerOpened(false); |
||||||
|
setDrawerOpened(true); |
||||||
|
} |
||||||
|
|
||||||
|
const menu = ( |
||||||
|
<Menu> |
||||||
|
<Menu.Item label={i18n.queryLibrary} onClick={() => toggle(Tabs.QueryLibrary)} /> |
||||||
|
<Menu.Item label={i18n.queryHistory} onClick={() => toggle(Tabs.RichHistory)} /> |
||||||
|
</Menu> |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<ButtonGroup> |
||||||
|
<ToolbarButton |
||||||
|
icon="book" |
||||||
|
variant={drawerOpened ? 'active' : 'canvas'} |
||||||
|
onClick={() => setDrawerOpened(!drawerOpened)} |
||||||
|
> |
||||||
|
{variant === 'full' ? selectedTab : undefined} |
||||||
|
</ToolbarButton> |
||||||
|
{drawerOpened ? ( |
||||||
|
<Button |
||||||
|
className={styles.close} |
||||||
|
variant="secondary" |
||||||
|
icon="times" |
||||||
|
onClick={() => setDrawerOpened(false)} |
||||||
|
></Button> |
||||||
|
) : ( |
||||||
|
<Dropdown overlay={menu}> |
||||||
|
<ToolbarButton className={styles.toggle} variant="canvas" icon="angle-down" /> |
||||||
|
</Dropdown> |
||||||
|
)} |
||||||
|
</ButtonGroup> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const getStyles = () => ({ |
||||||
|
toggle: css({ width: '36px' }), |
||||||
|
// tweaking icon position so it's nicely aligned when dropdown turns into a close button
|
||||||
|
close: css({ width: '36px', '> svg': { position: 'relative', left: 2 } }), |
||||||
|
}); |
@ -0,0 +1,30 @@ |
|||||||
|
import React, { PropsWithChildren, useState } from 'react'; |
||||||
|
|
||||||
|
import { QueriesDrawerContext, Tabs } from './QueriesDrawerContext'; |
||||||
|
|
||||||
|
type Props = { |
||||||
|
setDrawerOpened?: (value: boolean) => {}; |
||||||
|
queryLibraryAvailable?: boolean; |
||||||
|
} & PropsWithChildren; |
||||||
|
|
||||||
|
export function QueriesDrawerContextProviderMock(props: Props) { |
||||||
|
const [selectedTab, setSelectedTab] = useState<Tabs>(Tabs.QueryLibrary); |
||||||
|
const [drawerOpened, setDrawerOpened] = useState<boolean>(false); |
||||||
|
|
||||||
|
return ( |
||||||
|
<QueriesDrawerContext.Provider |
||||||
|
value={{ |
||||||
|
queryLibraryAvailable: props.queryLibraryAvailable || false, |
||||||
|
selectedTab, |
||||||
|
setSelectedTab, |
||||||
|
drawerOpened, |
||||||
|
setDrawerOpened: (value) => { |
||||||
|
props.setDrawerOpened?.(value); |
||||||
|
setDrawerOpened(value); |
||||||
|
}, |
||||||
|
}} |
||||||
|
> |
||||||
|
{props.children} |
||||||
|
</QueriesDrawerContext.Provider> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
import { t } from 'app//core/internationalization'; |
||||||
|
|
||||||
|
export const i18n = { |
||||||
|
queryLibrary: t('explore.rich-history.query-library', 'Query library'), |
||||||
|
queryHistory: t('explore.rich-history.query-history', 'Query history'), |
||||||
|
}; |
Loading…
Reference in new issue