mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Hotkeys: Make time range permanent (#43802)
Typing `t a` in Explore or Dashboards will turn a relative time like "Last 1 hour" into an absolute range to make the URL permanent, so that when sharing it others will see the same data. - registered `t a` in key service - new `AbsoluteTimeEvent` dispatch via global event bus - dashboard times handled in TimeSrv - Explore times handled in Explore.tsx and Explore's time reducer I could not find an easy way to combine time handling for Exlore and Dashboard in one place.
This commit is contained in:
parent
79d10c6903
commit
a08e0581de
@ -40,6 +40,10 @@ const shortcuts = {
|
||||
keys: ['t', '→'],
|
||||
description: 'Move time range forward',
|
||||
},
|
||||
{
|
||||
keys: ['t', 'a'],
|
||||
description: 'Make time range absolute/permanent',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
ShiftTimeEventPayload,
|
||||
ShowModalReactEvent,
|
||||
ZoomOutEvent,
|
||||
AbsoluteTimeEvent,
|
||||
} from '../../types/events';
|
||||
import { contextSrv } from '../core';
|
||||
import { getDatasourceSrv } from '../../features/plugins/datasource_srv';
|
||||
@ -34,6 +35,7 @@ export class KeybindingSrv {
|
||||
this.bind('g a', this.openAlerting);
|
||||
this.bind('g p', this.goToProfile);
|
||||
this.bind('s o', this.openSearch);
|
||||
this.bind('t a', this.makeAbsoluteTime);
|
||||
this.bind('f', this.openSearch);
|
||||
this.bind('esc', this.exit);
|
||||
this.bindGlobal('esc', this.globalEsc);
|
||||
@ -89,6 +91,10 @@ export class KeybindingSrv {
|
||||
locationService.push('/profile');
|
||||
}
|
||||
|
||||
private makeAbsoluteTime() {
|
||||
appEvents.publish(new AbsoluteTimeEvent());
|
||||
}
|
||||
|
||||
private showHelpModal() {
|
||||
appEvents.publish(new ShowModalReactEvent({ component: HelpModal }));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import { getShiftedTimeRange, getZoomedTimeRange } from 'app/core/utils/timePick
|
||||
import { config } from 'app/core/config';
|
||||
import { getRefreshFromUrl } from '../utils/getRefreshFromUrl';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { ShiftTimeEvent, ShiftTimeEventPayload, ZoomOutEvent } from '../../../types/events';
|
||||
import { AbsoluteTimeEvent, ShiftTimeEvent, ShiftTimeEventPayload, ZoomOutEvent } from '../../../types/events';
|
||||
import { contextSrv, ContextSrv } from 'app/core/services/context_srv';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
@ -41,6 +41,10 @@ export class TimeSrv {
|
||||
this.shiftTime(e.payload);
|
||||
});
|
||||
|
||||
appEvents.subscribe(AbsoluteTimeEvent, () => {
|
||||
this.makeAbsoluteTime();
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (this.autoRefreshBlocked && document.visibilityState === 'visible') {
|
||||
this.autoRefreshBlocked = false;
|
||||
@ -348,6 +352,16 @@ export class TimeSrv {
|
||||
});
|
||||
}
|
||||
|
||||
makeAbsoluteTime() {
|
||||
const params = locationService.getSearch();
|
||||
if (params.get('left')) {
|
||||
return; // explore handles this;
|
||||
}
|
||||
|
||||
const { from, to } = this.timeRange();
|
||||
this.setTime({ from, to });
|
||||
}
|
||||
|
||||
// isRefreshOutsideThreshold function calculates the difference between last refresh and now
|
||||
// if the difference is outside 5% of the current set time range then the function will return true
|
||||
// if the difference is within 5% of the current set time range then the function will return false
|
||||
|
@ -37,6 +37,7 @@ const dummyProps: Props = {
|
||||
isLive: false,
|
||||
syncedTimes: false,
|
||||
updateTimeRange: jest.fn(),
|
||||
makeAbsoluteTime: jest.fn(),
|
||||
graphResult: [],
|
||||
absoluteRange: {
|
||||
from: 0,
|
||||
|
@ -15,7 +15,7 @@ import RichHistoryContainer from './RichHistory/RichHistoryContainer';
|
||||
import ExploreQueryInspector from './ExploreQueryInspector';
|
||||
import { splitOpen } from './state/main';
|
||||
import { changeSize, changeGraphStyle } from './state/explorePane';
|
||||
import { updateTimeRange } from './state/time';
|
||||
import { makeAbsoluteTime, updateTimeRange } from './state/time';
|
||||
import { addQueryRow, loadLogsVolumeData, modifyQueries, scanStart, scanStopAction, setQueries } from './state/query';
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
import { StoreState } from 'app/types';
|
||||
@ -31,6 +31,9 @@ import { ExploreGraph } from './ExploreGraph';
|
||||
import { LogsVolumePanel } from './LogsVolumePanel';
|
||||
import { ExploreGraphLabel } from './ExploreGraphLabel';
|
||||
import { ExploreGraphStyle } from 'app/core/utils/explore';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { AbsoluteTimeEvent } from 'app/types/events';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
@ -97,6 +100,7 @@ export type Props = ExploreProps & ConnectedProps<typeof connector>;
|
||||
*/
|
||||
export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
scrollElement: HTMLDivElement | undefined;
|
||||
absoluteTimeUnsubsciber: Unsubscribable | undefined;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@ -105,6 +109,14 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.absoluteTimeUnsubsciber = appEvents.subscribe(AbsoluteTimeEvent, this.onMakeAbsoluteTime);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.absoluteTimeUnsubsciber?.unsubscribe();
|
||||
}
|
||||
|
||||
onChangeTime = (rawRange: RawTimeRange) => {
|
||||
const { updateTimeRange, exploreId } = this.props;
|
||||
updateTimeRange({ exploreId, rawRange });
|
||||
@ -139,6 +151,11 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
this.props.addQueryRow(exploreId, queryKeys.length);
|
||||
};
|
||||
|
||||
onMakeAbsoluteTime = () => {
|
||||
const { makeAbsoluteTime } = this.props;
|
||||
makeAbsoluteTime();
|
||||
};
|
||||
|
||||
onModifyQueries = (action: any, index?: number) => {
|
||||
const { datasourceInstance } = this.props;
|
||||
if (datasourceInstance?.modifyQuery) {
|
||||
@ -447,6 +464,7 @@ const mapDispatchToProps = {
|
||||
scanStopAction,
|
||||
setQueries,
|
||||
updateTimeRange,
|
||||
makeAbsoluteTime,
|
||||
loadLogsVolumeData,
|
||||
addQueryRow,
|
||||
splitOpen,
|
||||
|
@ -127,6 +127,29 @@ export function syncTimes(exploreId: ExploreId): ThunkResult<void> {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the timepicker's time into absolute time.
|
||||
* The conversion is applied to all Explore panes.
|
||||
* Useful to produce a bookmarkable URL that points to the same data.
|
||||
*/
|
||||
export function makeAbsoluteTime(): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
const timeZone = getTimeZone(getState().user);
|
||||
const fiscalYearStartMonth = getFiscalYearStartMonth(getState().user);
|
||||
const leftState = getState().explore.left;
|
||||
const leftRange = getTimeRange(timeZone, leftState.range.raw, fiscalYearStartMonth);
|
||||
const leftAbsoluteRange: AbsoluteTimeRange = { from: leftRange.from.valueOf(), to: leftRange.to.valueOf() };
|
||||
dispatch(updateTime({ exploreId: ExploreId.left, absoluteRange: leftAbsoluteRange }));
|
||||
const rightState = getState().explore.right!;
|
||||
if (rightState) {
|
||||
const rightRange = getTimeRange(timeZone, rightState.range.raw, fiscalYearStartMonth);
|
||||
const rightAbsoluteRange: AbsoluteTimeRange = { from: rightRange.from.valueOf(), to: rightRange.to.valueOf() };
|
||||
dispatch(updateTime({ exploreId: ExploreId.right, absoluteRange: rightAbsoluteRange }));
|
||||
}
|
||||
dispatch(stateSave());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reducer for an Explore area, to be used by the global Explore reducer.
|
||||
*/
|
||||
|
@ -144,6 +144,10 @@ export class ShiftTimeEvent extends BusEventWithPayload<ShiftTimeEventPayload> {
|
||||
static type = 'shift-time';
|
||||
}
|
||||
|
||||
export class AbsoluteTimeEvent extends BusEventBase {
|
||||
static type = 'absolute-time';
|
||||
}
|
||||
|
||||
export class RemovePanelEvent extends BusEventWithPayload<number> {
|
||||
static type = 'remove-panel';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user