mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
POC: Unified History (#94318)
This commit is contained in:
parent
f5d44ff51d
commit
e0af98dec8
@ -10,9 +10,10 @@ import { isShallowEqual } from 'app/core/utils/isShallowEqual';
|
|||||||
import { KioskMode } from 'app/types';
|
import { KioskMode } from 'app/types';
|
||||||
|
|
||||||
import { RouteDescriptor } from '../../navigation/types';
|
import { RouteDescriptor } from '../../navigation/types';
|
||||||
|
import { buildBreadcrumbs } from '../Breadcrumbs/utils';
|
||||||
|
|
||||||
import { ReturnToPreviousProps } from './ReturnToPrevious/ReturnToPrevious';
|
import { ReturnToPreviousProps } from './ReturnToPrevious/ReturnToPrevious';
|
||||||
import { TOP_BAR_LEVEL_HEIGHT } from './types';
|
import { HistoryEntry, TOP_BAR_LEVEL_HEIGHT } from './types';
|
||||||
|
|
||||||
export interface AppChromeState {
|
export interface AppChromeState {
|
||||||
chromeless?: boolean;
|
chromeless?: boolean;
|
||||||
@ -31,6 +32,7 @@ export interface AppChromeState {
|
|||||||
|
|
||||||
export const DOCKED_LOCAL_STORAGE_KEY = 'grafana.navigation.docked';
|
export const DOCKED_LOCAL_STORAGE_KEY = 'grafana.navigation.docked';
|
||||||
export const DOCKED_MENU_OPEN_LOCAL_STORAGE_KEY = 'grafana.navigation.open';
|
export const DOCKED_MENU_OPEN_LOCAL_STORAGE_KEY = 'grafana.navigation.open';
|
||||||
|
export const HISTORY_LOCAL_STORAGE_KEY = 'grafana.navigation.history';
|
||||||
|
|
||||||
export class AppChromeService {
|
export class AppChromeService {
|
||||||
searchBarStorageKey = 'SearchBar_Hidden';
|
searchBarStorageKey = 'SearchBar_Hidden';
|
||||||
@ -99,6 +101,8 @@ export class AppChromeService {
|
|||||||
newState.chromeless = newState.kioskMode === KioskMode.Full || this.currentRoute?.chromeless;
|
newState.chromeless = newState.kioskMode === KioskMode.Full || this.currentRoute?.chromeless;
|
||||||
|
|
||||||
if (!this.ignoreStateUpdate(newState, current)) {
|
if (!this.ignoreStateUpdate(newState, current)) {
|
||||||
|
config.featureToggles.unifiedHistory &&
|
||||||
|
store.setObject(HISTORY_LOCAL_STORAGE_KEY, this.getUpdatedHistory(newState));
|
||||||
this.state.next(newState);
|
this.state.next(newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +131,29 @@ export class AppChromeService {
|
|||||||
window.sessionStorage.removeItem('returnToPrevious');
|
window.sessionStorage.removeItem('returnToPrevious');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private getUpdatedHistory(newState: AppChromeState): HistoryEntry[] {
|
||||||
|
const breadcrumbs = buildBreadcrumbs(newState.sectionNav.node, newState.pageNav, { text: 'Home', url: '/' }, true);
|
||||||
|
const newPageNav = newState.pageNav || newState.sectionNav.node;
|
||||||
|
|
||||||
|
let entries = store.getObject<HistoryEntry[]>(HISTORY_LOCAL_STORAGE_KEY, []);
|
||||||
|
const clickedHistory = store.getObject<boolean>('CLICKING_HISTORY');
|
||||||
|
if (clickedHistory) {
|
||||||
|
store.setObject('CLICKING_HISTORY', false);
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
if (!newPageNav) {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastEntry = entries[0];
|
||||||
|
if (!lastEntry || lastEntry.name !== newPageNav.text) {
|
||||||
|
lastEntry = { name: newPageNav.text, views: [], breadcrumbs, time: Date.now(), url: window.location.href };
|
||||||
|
}
|
||||||
|
if (lastEntry !== entries[0]) {
|
||||||
|
entries = [lastEntry, ...entries];
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
private ignoreStateUpdate(newState: AppChromeState, current: AppChromeState) {
|
private ignoreStateUpdate(newState: AppChromeState, current: AppChromeState) {
|
||||||
if (isShallowEqual(newState, current)) {
|
if (isShallowEqual(newState, current)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useToggle } from 'react-use';
|
||||||
|
|
||||||
|
import { GrafanaTheme2, store } from '@grafana/data';
|
||||||
|
import { Drawer, ToolbarButton, useStyles2 } from '@grafana/ui';
|
||||||
|
import { appEvents } from 'app/core/app_events';
|
||||||
|
import { t } from 'app/core/internationalization';
|
||||||
|
import { RecordHistoryEntryEvent } from 'app/types/events';
|
||||||
|
|
||||||
|
import { HISTORY_LOCAL_STORAGE_KEY } from '../AppChromeService';
|
||||||
|
import { NavToolbarSeparator } from '../NavToolbar/NavToolbarSeparator';
|
||||||
|
import { HistoryEntry } from '../types';
|
||||||
|
|
||||||
|
import { HistoryWrapper } from './HistoryWrapper';
|
||||||
|
|
||||||
|
export function HistoryContainer() {
|
||||||
|
const [showHistoryDrawer, onToggleShowHistoryDrawer] = useToggle(false);
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sub = appEvents.subscribe(RecordHistoryEntryEvent, (ev) => {
|
||||||
|
const clickedHistory = store.getObject<boolean>('CLICKING_HISTORY');
|
||||||
|
if (clickedHistory) {
|
||||||
|
store.setObject('CLICKING_HISTORY', false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const history = store.getObject<HistoryEntry[]>(HISTORY_LOCAL_STORAGE_KEY, []);
|
||||||
|
let lastEntry = history[0];
|
||||||
|
const newUrl = ev.payload.url;
|
||||||
|
const lastUrl = lastEntry.views[0]?.url;
|
||||||
|
if (lastUrl !== newUrl) {
|
||||||
|
lastEntry.views = [
|
||||||
|
{
|
||||||
|
name: ev.payload.name,
|
||||||
|
description: ev.payload.description,
|
||||||
|
url: newUrl,
|
||||||
|
time: Date.now(),
|
||||||
|
},
|
||||||
|
...lastEntry.views,
|
||||||
|
];
|
||||||
|
store.setObject(HISTORY_LOCAL_STORAGE_KEY, [...history]);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
sub.unsubscribe();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ToolbarButton
|
||||||
|
onClick={onToggleShowHistoryDrawer}
|
||||||
|
iconOnly
|
||||||
|
icon="history"
|
||||||
|
aria-label={t('nav.history-container.drawer-tittle', 'History')}
|
||||||
|
/>
|
||||||
|
<NavToolbarSeparator className={styles.separator} />
|
||||||
|
{showHistoryDrawer && (
|
||||||
|
<Drawer
|
||||||
|
title={t('nav.history-container.drawer-tittle', 'History')}
|
||||||
|
onClose={onToggleShowHistoryDrawer}
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<HistoryWrapper onClose={() => onToggleShowHistoryDrawer(false)} />
|
||||||
|
</Drawer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
|
return {
|
||||||
|
separator: css({
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
204
public/app/core/components/AppChrome/History/HistoryWrapper.tsx
Normal file
204
public/app/core/components/AppChrome/History/HistoryWrapper.tsx
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { FieldType, GrafanaTheme2, store } from '@grafana/data';
|
||||||
|
import { Button, Card, IconButton, Space, Stack, Text, useStyles2, Box, Sparkline, useTheme2 } from '@grafana/ui';
|
||||||
|
import { t } from 'app/core/internationalization';
|
||||||
|
|
||||||
|
import { HISTORY_LOCAL_STORAGE_KEY } from '../AppChromeService';
|
||||||
|
import { HistoryEntry } from '../types';
|
||||||
|
|
||||||
|
export function HistoryWrapper({ onClose }: { onClose: () => void }) {
|
||||||
|
const history = store.getObject<HistoryEntry[]>(HISTORY_LOCAL_STORAGE_KEY, []).filter((entry) => {
|
||||||
|
return moment(entry.time).isAfter(moment().subtract(2, 'day').startOf('day'));
|
||||||
|
});
|
||||||
|
const [numItemsToShow, setNumItemsToShow] = useState(5);
|
||||||
|
|
||||||
|
const selectedTime = history.find((entry) => {
|
||||||
|
return entry.url === window.location.href || entry.views.some((view) => view.url === window.location.href);
|
||||||
|
})?.time;
|
||||||
|
|
||||||
|
const hist = history.slice(0, numItemsToShow).reduce((acc: { [key: string]: HistoryEntry[] }, entry) => {
|
||||||
|
const date = moment(entry.time);
|
||||||
|
let key = '';
|
||||||
|
if (date.isSame(moment(), 'day')) {
|
||||||
|
key = t('nav.history-wrapper.today', 'Today');
|
||||||
|
} else if (date.isSame(moment().subtract(1, 'day'), 'day')) {
|
||||||
|
key = t('nav.history-wrapper.yesterday', 'Yesterday');
|
||||||
|
} else {
|
||||||
|
key = date.format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
acc[key] = [...(acc[key] || []), entry];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack direction="column" alignItems="flex-start">
|
||||||
|
<Box width="100%">
|
||||||
|
{Object.keys(hist).map((entries, date) => {
|
||||||
|
return (
|
||||||
|
<Stack key={date} direction="column" gap={1}>
|
||||||
|
<Text color="secondary" variant="bodySmall">
|
||||||
|
{entries}
|
||||||
|
</Text>
|
||||||
|
{hist[entries].map((entry, index) => {
|
||||||
|
return (
|
||||||
|
<HistoryEntryAppView
|
||||||
|
key={index}
|
||||||
|
entry={entry}
|
||||||
|
isSelected={entry.time === selectedTime}
|
||||||
|
onClick={() => onClose()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
{history.length > numItemsToShow && (
|
||||||
|
<Button variant="secondary" fill="text" onClick={() => setNumItemsToShow(numItemsToShow + 5)}>
|
||||||
|
{t('nav.history-wrapper.show-more', 'Show more')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
interface ItemProps {
|
||||||
|
entry: HistoryEntry;
|
||||||
|
isSelected: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function HistoryEntryAppView({ entry, isSelected, onClick }: ItemProps) {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
const theme = useTheme2();
|
||||||
|
const [isExpanded, setIsExpanded] = useState(isSelected && entry.views.length > 0);
|
||||||
|
const { breadcrumbs, views, time, url, sparklineData } = entry;
|
||||||
|
const expandedLabel = isExpanded
|
||||||
|
? t('nav.history-wrapper.collapse', 'Collapse')
|
||||||
|
: t('nav.history-wrapper.expand', 'Expand');
|
||||||
|
|
||||||
|
const selectedViewTime =
|
||||||
|
isSelected &&
|
||||||
|
entry.views.find((entry) => {
|
||||||
|
return entry.url === window.location.href;
|
||||||
|
})?.time;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack direction="column" gap={1}>
|
||||||
|
<Stack>
|
||||||
|
{views.length > 0 ? (
|
||||||
|
<IconButton
|
||||||
|
name={isExpanded ? 'angle-down' : 'angle-right'}
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
aria-label={expandedLabel}
|
||||||
|
className={styles.iconButton}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Space h={2} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Card
|
||||||
|
onClick={() => {
|
||||||
|
store.setObject('CLICKING_HISTORY', true);
|
||||||
|
onClick();
|
||||||
|
}}
|
||||||
|
href={url}
|
||||||
|
isCompact={true}
|
||||||
|
className={isSelected ? undefined : styles.card}
|
||||||
|
>
|
||||||
|
<Stack direction="column">
|
||||||
|
<div>
|
||||||
|
{breadcrumbs.map((breadcrumb, index) => (
|
||||||
|
<Text key={index}>
|
||||||
|
{breadcrumb.text} {index !== breadcrumbs.length - 1 ? '> ' : ''}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Text color="secondary">{moment(time).format('h:mm A')}</Text>
|
||||||
|
{sparklineData && (
|
||||||
|
<Sparkline
|
||||||
|
theme={theme}
|
||||||
|
width={240}
|
||||||
|
height={40}
|
||||||
|
config={{
|
||||||
|
custom: {
|
||||||
|
fillColor: 'rgba(130, 181, 216, 0.1)',
|
||||||
|
lineColor: '#82B5D8',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sparkline={{
|
||||||
|
y: {
|
||||||
|
type: FieldType.number,
|
||||||
|
name: 'test',
|
||||||
|
config: {},
|
||||||
|
values: sparklineData.values,
|
||||||
|
state: {
|
||||||
|
range: {
|
||||||
|
...sparklineData.range,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
|
{isExpanded && (
|
||||||
|
<div className={styles.expanded}>
|
||||||
|
{views.map((view, index) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
key={index}
|
||||||
|
href={view.url}
|
||||||
|
onClick={() => {
|
||||||
|
store.setObject('CLICKING_HISTORY', true);
|
||||||
|
onClick();
|
||||||
|
}}
|
||||||
|
isCompact={true}
|
||||||
|
className={view.time === selectedViewTime ? undefined : styles.card}
|
||||||
|
>
|
||||||
|
<Stack direction="column" gap={0}>
|
||||||
|
<Text variant="bodySmall">{view.name}</Text>
|
||||||
|
{view.description && (
|
||||||
|
<Text color="secondary" variant="bodySmall">
|
||||||
|
{view.description}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
|
return {
|
||||||
|
card: css({
|
||||||
|
background: 'none',
|
||||||
|
}),
|
||||||
|
iconButton: css({
|
||||||
|
margin: 0,
|
||||||
|
}),
|
||||||
|
expanded: css({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
marginLeft: theme.spacing(5),
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
position: 'relative',
|
||||||
|
'&:before': {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
left: theme.spacing(-2),
|
||||||
|
top: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: '1px',
|
||||||
|
background: theme.colors.border.weak,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
@ -14,6 +14,7 @@ import { useSelector } from 'app/types';
|
|||||||
import { Branding } from '../../Branding/Branding';
|
import { Branding } from '../../Branding/Branding';
|
||||||
import { Breadcrumbs } from '../../Breadcrumbs/Breadcrumbs';
|
import { Breadcrumbs } from '../../Breadcrumbs/Breadcrumbs';
|
||||||
import { buildBreadcrumbs } from '../../Breadcrumbs/utils';
|
import { buildBreadcrumbs } from '../../Breadcrumbs/utils';
|
||||||
|
import { HistoryContainer } from '../History/HistoryContainer';
|
||||||
import { enrichHelpItem } from '../MegaMenu/utils';
|
import { enrichHelpItem } from '../MegaMenu/utils';
|
||||||
import { NewsContainer } from '../News/NewsContainer';
|
import { NewsContainer } from '../News/NewsContainer';
|
||||||
import { QuickAdd } from '../QuickAdd/QuickAdd';
|
import { QuickAdd } from '../QuickAdd/QuickAdd';
|
||||||
@ -49,6 +50,7 @@ export const SingleTopBar = memo(function SingleTopBar({
|
|||||||
const profileNode = navIndex['profile'];
|
const profileNode = navIndex['profile'];
|
||||||
const homeNav = useSelector((state) => state.navIndex)[HOME_NAV_ID];
|
const homeNav = useSelector((state) => state.navIndex)[HOME_NAV_ID];
|
||||||
const breadcrumbs = buildBreadcrumbs(sectionNav, pageNav, homeNav);
|
const breadcrumbs = buildBreadcrumbs(sectionNav, pageNav, homeNav);
|
||||||
|
const unifiedHistoryEnabled = config.featureToggles.unifiedHistory;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.layout}>
|
<div className={styles.layout}>
|
||||||
@ -72,6 +74,7 @@ export const SingleTopBar = memo(function SingleTopBar({
|
|||||||
<Stack gap={0.5} alignItems="center">
|
<Stack gap={0.5} alignItems="center">
|
||||||
<TopSearchBarCommandPaletteTrigger />
|
<TopSearchBarCommandPaletteTrigger />
|
||||||
<QuickAdd />
|
<QuickAdd />
|
||||||
|
{unifiedHistoryEnabled && <HistoryContainer />}
|
||||||
{enrichedHelpNode && (
|
{enrichedHelpNode && (
|
||||||
<Dropdown overlay={() => <TopNavBarMenu node={enrichedHelpNode} />} placement="bottom-end">
|
<Dropdown overlay={() => <TopNavBarMenu node={enrichedHelpNode} />} placement="bottom-end">
|
||||||
<ToolbarButton iconOnly icon="question-circle" aria-label="Help" />
|
<ToolbarButton iconOnly icon="question-circle" aria-label="Help" />
|
||||||
|
@ -5,3 +5,28 @@ export interface ToolbarUpdateProps {
|
|||||||
pageNav?: NavModelItem;
|
pageNav?: NavModelItem;
|
||||||
actions?: React.ReactNode;
|
actions?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HistoryEntryView {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
time: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HistoryEntrySparkline {
|
||||||
|
values: number[];
|
||||||
|
range: {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
delta: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HistoryEntry {
|
||||||
|
name: string;
|
||||||
|
time: number;
|
||||||
|
breadcrumbs: NavModelItem[];
|
||||||
|
url: string;
|
||||||
|
views: HistoryEntryView[];
|
||||||
|
sparklineData?: HistoryEntrySparkline;
|
||||||
|
}
|
||||||
|
@ -2,7 +2,12 @@ import { NavModelItem } from '@grafana/data';
|
|||||||
|
|
||||||
import { Breadcrumb } from './types';
|
import { Breadcrumb } from './types';
|
||||||
|
|
||||||
export function buildBreadcrumbs(sectionNav: NavModelItem, pageNav?: NavModelItem, homeNav?: NavModelItem) {
|
export function buildBreadcrumbs(
|
||||||
|
sectionNav: NavModelItem,
|
||||||
|
pageNav?: NavModelItem,
|
||||||
|
homeNav?: NavModelItem,
|
||||||
|
skipHome?: boolean
|
||||||
|
) {
|
||||||
const crumbs: Breadcrumb[] = [];
|
const crumbs: Breadcrumb[] = [];
|
||||||
let foundHome = false;
|
let foundHome = false;
|
||||||
let lastPath: string | undefined = undefined;
|
let lastPath: string | undefined = undefined;
|
||||||
@ -22,7 +27,9 @@ export function buildBreadcrumbs(sectionNav: NavModelItem, pageNav?: NavModelIte
|
|||||||
|
|
||||||
// Check if we found home/root if if so return early
|
// Check if we found home/root if if so return early
|
||||||
if (homeNav && urlToMatch === homeNav.url) {
|
if (homeNav && urlToMatch === homeNav.url) {
|
||||||
crumbs.unshift({ text: homeNav.text, href: node.url ?? '' });
|
if (!skipHome) {
|
||||||
|
crumbs.unshift({ text: homeNav.text, href: node.url ?? '' });
|
||||||
|
}
|
||||||
foundHome = true;
|
foundHome = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import { getTimeZoneInfo, GrafanaTheme2, InternalTimeZones, TIME_FORMAT } from '@grafana/data';
|
import { getTimeZoneInfo, GrafanaTheme2, InternalTimeZones, TIME_FORMAT } from '@grafana/data';
|
||||||
import { convertRawToRange } from '@grafana/data/src/datetime/rangeutil';
|
import { convertRawToRange } from '@grafana/data/src/datetime/rangeutil';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
SceneComponentProps,
|
SceneComponentProps,
|
||||||
SceneObjectBase,
|
SceneObjectBase,
|
||||||
@ -15,6 +16,8 @@ import {
|
|||||||
SceneVariableValueChangedEvent,
|
SceneVariableValueChangedEvent,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { Stack, Tooltip, useStyles2 } from '@grafana/ui';
|
import { Stack, Tooltip, useStyles2 } from '@grafana/ui';
|
||||||
|
import { appEvents } from 'app/core/app_events';
|
||||||
|
import { RecordHistoryEntryEvent } from 'app/types/events';
|
||||||
|
|
||||||
import { DataTrail, DataTrailState, getTopSceneFor } from './DataTrail';
|
import { DataTrail, DataTrailState, getTopSceneFor } from './DataTrail';
|
||||||
import { SerializedTrailHistory } from './TrailStore/TrailStore';
|
import { SerializedTrailHistory } from './TrailStore/TrailStore';
|
||||||
@ -148,15 +151,24 @@ export class DataTrailHistory extends SceneObjectBase<DataTrailsHistoryState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addTrailStep(
|
const tooltip = parseTimeTooltip({
|
||||||
trail,
|
from: newState.from,
|
||||||
'time',
|
to: newState.to,
|
||||||
parseTimeTooltip({
|
timeZone: newState.timeZone,
|
||||||
from: newState.from,
|
});
|
||||||
to: newState.to,
|
|
||||||
timeZone: newState.timeZone,
|
this.addTrailStep(trail, 'time', tooltip);
|
||||||
})
|
|
||||||
);
|
if (config.featureToggles.unifiedHistory) {
|
||||||
|
appEvents.publish(
|
||||||
|
new RecordHistoryEntryEvent({
|
||||||
|
name: 'Time range changed',
|
||||||
|
description: tooltip,
|
||||||
|
url: window.location.href,
|
||||||
|
time: Date.now(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { AnnotationQuery, BusEventBase, BusEventWithPayload, eventFactory } from '@grafana/data';
|
import { AnnotationQuery, BusEventBase, BusEventWithPayload, eventFactory } from '@grafana/data';
|
||||||
import { IconName, ButtonVariant } from '@grafana/ui';
|
import { IconName, ButtonVariant } from '@grafana/ui';
|
||||||
|
import { HistoryEntryView } from 'app/core/components/AppChrome/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event Payloads
|
* Event Payloads
|
||||||
@ -209,3 +210,7 @@ export class PanelEditEnteredEvent extends BusEventWithPayload<number> {
|
|||||||
export class PanelEditExitedEvent extends BusEventWithPayload<number> {
|
export class PanelEditExitedEvent extends BusEventWithPayload<number> {
|
||||||
static type = 'panel-edit-finished';
|
static type = 'panel-edit-finished';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RecordHistoryEntryEvent extends BusEventWithPayload<HistoryEntryView> {
|
||||||
|
static type = 'record-history-entry';
|
||||||
|
}
|
||||||
|
@ -2176,6 +2176,16 @@
|
|||||||
"help/documentation": "Documentation",
|
"help/documentation": "Documentation",
|
||||||
"help/keyboard-shortcuts": "Keyboard shortcuts",
|
"help/keyboard-shortcuts": "Keyboard shortcuts",
|
||||||
"help/support": "Support",
|
"help/support": "Support",
|
||||||
|
"history-container": {
|
||||||
|
"drawer-tittle": "History"
|
||||||
|
},
|
||||||
|
"history-wrapper": {
|
||||||
|
"collapse": "Collapse",
|
||||||
|
"expand": "Expand",
|
||||||
|
"show-more": "Show more",
|
||||||
|
"today": "Today",
|
||||||
|
"yesterday": "Yesterday"
|
||||||
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"title": "Home"
|
"title": "Home"
|
||||||
},
|
},
|
||||||
|
@ -2176,6 +2176,16 @@
|
|||||||
"help/documentation": "Đőčūmęʼnŧäŧįőʼn",
|
"help/documentation": "Đőčūmęʼnŧäŧįőʼn",
|
||||||
"help/keyboard-shortcuts": "Ķęyþőäřđ şĥőřŧčūŧş",
|
"help/keyboard-shortcuts": "Ķęyþőäřđ şĥőřŧčūŧş",
|
||||||
"help/support": "Ŝūppőřŧ",
|
"help/support": "Ŝūppőřŧ",
|
||||||
|
"history-container": {
|
||||||
|
"drawer-tittle": "Ħįşŧőřy"
|
||||||
|
},
|
||||||
|
"history-wrapper": {
|
||||||
|
"collapse": "Cőľľäpşę",
|
||||||
|
"expand": "Ēχpäʼnđ",
|
||||||
|
"show-more": "Ŝĥőŵ mőřę",
|
||||||
|
"today": "Ŧőđäy",
|
||||||
|
"yesterday": "Ÿęşŧęřđäy"
|
||||||
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"title": "Ħőmę"
|
"title": "Ħőmę"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user