mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Add fiscal years and search to time picker (#39073)
* Add search to time picker * implement fiscal datemath Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
@@ -14,6 +14,7 @@ export class User {
|
||||
login: string;
|
||||
orgCount: number;
|
||||
timezone: string;
|
||||
fiscalYearStartMonth: number;
|
||||
helpFlags1: number;
|
||||
lightTheme: boolean;
|
||||
hasEditPermissionInFolders: boolean;
|
||||
@@ -30,6 +31,7 @@ export class User {
|
||||
this.login = '';
|
||||
this.orgCount = 0;
|
||||
this.timezone = '';
|
||||
this.fiscalYearStartMonth = 0;
|
||||
this.helpFlags1 = 0;
|
||||
this.lightTheme = false;
|
||||
this.hasEditPermissionInFolders = false;
|
||||
|
||||
@@ -302,7 +302,7 @@ describe('getTimeRangeFromUrl', () => {
|
||||
it('should parse moment date', () => {
|
||||
// convert date strings to moment object
|
||||
const range = { from: dateTime('2020-10-22T10:44:33.615Z'), to: dateTime('2020-10-22T10:49:33.615Z') };
|
||||
const result = getTimeRangeFromUrl(range, 'browser');
|
||||
const result = getTimeRangeFromUrl(range, 'browser', 0);
|
||||
expect(result.raw).toEqual(range);
|
||||
});
|
||||
|
||||
@@ -311,7 +311,7 @@ describe('getTimeRangeFromUrl', () => {
|
||||
from: dateTime('2020-10-22T10:00:00Z').valueOf().toString(),
|
||||
to: dateTime('2020-10-22T11:00:00Z').valueOf().toString(),
|
||||
};
|
||||
const result = getTimeRangeFromUrl(range, 'browser');
|
||||
const result = getTimeRangeFromUrl(range, 'browser', 0);
|
||||
expect(result.from.valueOf()).toEqual(dateTime('2020-10-22T10:00:00Z').valueOf());
|
||||
expect(result.to.valueOf()).toEqual(dateTime('2020-10-22T11:00:00Z').valueOf());
|
||||
expect(result.raw.from.valueOf()).toEqual(dateTime('2020-10-22T10:00:00Z').valueOf());
|
||||
@@ -323,7 +323,7 @@ describe('getTimeRangeFromUrl', () => {
|
||||
from: dateTime('2020-10-22T10:00:00Z').toISOString(),
|
||||
to: dateTime('2020-10-22T11:00:00Z').toISOString(),
|
||||
};
|
||||
const result = getTimeRangeFromUrl(range, 'browser');
|
||||
const result = getTimeRangeFromUrl(range, 'browser', 0);
|
||||
expect(result.from.valueOf()).toEqual(dateTime('2020-10-22T10:00:00Z').valueOf());
|
||||
expect(result.to.valueOf()).toEqual(dateTime('2020-10-22T11:00:00Z').valueOf());
|
||||
expect(result.raw.from.valueOf()).toEqual(dateTime('2020-10-22T10:00:00Z').valueOf());
|
||||
|
||||
@@ -341,10 +341,10 @@ export const getQueryKeys = (queries: DataQuery[], datasourceInstance?: DataSour
|
||||
return queryKeys;
|
||||
};
|
||||
|
||||
export const getTimeRange = (timeZone: TimeZone, rawRange: RawTimeRange): TimeRange => {
|
||||
export const getTimeRange = (timeZone: TimeZone, rawRange: RawTimeRange, fiscalYearStartMonth: number): TimeRange => {
|
||||
return {
|
||||
from: dateMath.parse(rawRange.from, false, timeZone as any)!,
|
||||
to: dateMath.parse(rawRange.to, true, timeZone as any)!,
|
||||
from: dateMath.parse(rawRange.from, false, timeZone as any, fiscalYearStartMonth)!,
|
||||
to: dateMath.parse(rawRange.to, true, timeZone as any, fiscalYearStartMonth)!,
|
||||
raw: rawRange,
|
||||
};
|
||||
};
|
||||
@@ -387,7 +387,11 @@ const parseRawTime = (value: string | DateTime): TimeFragment | null => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getTimeRangeFromUrl = (range: RawTimeRange, timeZone: TimeZone): TimeRange => {
|
||||
export const getTimeRangeFromUrl = (
|
||||
range: RawTimeRange,
|
||||
timeZone: TimeZone,
|
||||
fiscalYearStartMonth: number
|
||||
): TimeRange => {
|
||||
const raw = {
|
||||
from: parseRawTime(range.from)!,
|
||||
to: parseRawTime(range.to)!,
|
||||
|
||||
@@ -70,6 +70,11 @@ export class DashNavTimeControls extends Component<Props> {
|
||||
this.onRefresh();
|
||||
};
|
||||
|
||||
onChangeFiscalYearStartMonth = (month: number) => {
|
||||
this.props.dashboard.fiscalYearStartMonth = month;
|
||||
this.onRefresh();
|
||||
};
|
||||
|
||||
onZoom = () => {
|
||||
appEvents.publish(new ZoomOutEvent(2));
|
||||
};
|
||||
@@ -81,6 +86,7 @@ export class DashNavTimeControls extends Component<Props> {
|
||||
|
||||
const timePickerValue = getTimeSrv().timeRange();
|
||||
const timeZone = dashboard.getTimezone();
|
||||
const fiscalYearStartMonth = dashboard.fiscalYearStartMonth;
|
||||
const hideIntervalPicker = dashboard.panelInEdit?.isEditing;
|
||||
|
||||
return (
|
||||
@@ -89,10 +95,12 @@ export class DashNavTimeControls extends Component<Props> {
|
||||
value={timePickerValue}
|
||||
onChange={this.onChangeTimePicker}
|
||||
timeZone={timeZone}
|
||||
fiscalYearStartMonth={fiscalYearStartMonth}
|
||||
onMoveBackward={this.onMoveBack}
|
||||
onMoveForward={this.onMoveForward}
|
||||
onZoom={this.onZoom}
|
||||
onChangeTimeZone={this.onChangeTimeZone}
|
||||
onChangeFiscalYearStartMonth={this.onChangeFiscalYearStartMonth}
|
||||
/>
|
||||
<RefreshPicker
|
||||
onIntervalChanged={this.onChangeRefreshInterval}
|
||||
|
||||
@@ -60,7 +60,11 @@ export class TimeSrv {
|
||||
// remember time at load so we can go back to it
|
||||
this.timeAtLoad = cloneDeep(this.time);
|
||||
|
||||
const range = rangeUtil.convertRawToRange(this.time, this.dashboard?.getTimezone());
|
||||
const range = rangeUtil.convertRawToRange(
|
||||
this.time,
|
||||
this.dashboard?.getTimezone(),
|
||||
this.dashboard?.fiscalYearStartMonth
|
||||
);
|
||||
|
||||
if (range.to.isBefore(range.from)) {
|
||||
this.setTime(
|
||||
@@ -327,8 +331,8 @@ export class TimeSrv {
|
||||
const timezone = this.dashboard ? this.dashboard.getTimezone() : undefined;
|
||||
|
||||
return {
|
||||
from: dateMath.parse(raw.from, false, timezone)!,
|
||||
to: dateMath.parse(raw.to, true, timezone)!,
|
||||
from: dateMath.parse(raw.from, false, timezone, this.dashboard?.fiscalYearStartMonth)!,
|
||||
to: dateMath.parse(raw.to, true, timezone, this.dashboard?.fiscalYearStartMonth)!,
|
||||
raw: raw,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ export class DashboardModel {
|
||||
panels: PanelModel[];
|
||||
panelInEdit?: PanelModel;
|
||||
panelInView?: PanelModel;
|
||||
fiscalYearStartMonth?: number;
|
||||
private hasChangesThatAffectsAllPanels: boolean;
|
||||
|
||||
// ------------------
|
||||
@@ -131,26 +132,27 @@ export class DashboardModel {
|
||||
this.id = data.id || null;
|
||||
this.uid = data.uid || null;
|
||||
this.revision = data.revision;
|
||||
this.title = data.title || 'No Title';
|
||||
this.title = data.title ?? 'No Title';
|
||||
this.autoUpdate = data.autoUpdate;
|
||||
this.description = data.description;
|
||||
this.tags = data.tags || [];
|
||||
this.style = data.style || 'dark';
|
||||
this.timezone = data.timezone || '';
|
||||
this.tags = data.tags ?? [];
|
||||
this.style = data.style ?? 'dark';
|
||||
this.timezone = data.timezone ?? '';
|
||||
this.editable = data.editable !== false;
|
||||
this.graphTooltip = data.graphTooltip || 0;
|
||||
this.time = data.time || { from: 'now-6h', to: 'now' };
|
||||
this.timepicker = data.timepicker || {};
|
||||
this.time = data.time ?? { from: 'now-6h', to: 'now' };
|
||||
this.timepicker = data.timepicker ?? {};
|
||||
this.liveNow = Boolean(data.liveNow);
|
||||
this.templating = this.ensureListExist(data.templating);
|
||||
this.annotations = this.ensureListExist(data.annotations);
|
||||
this.refresh = data.refresh;
|
||||
this.snapshot = data.snapshot;
|
||||
this.schemaVersion = data.schemaVersion || 0;
|
||||
this.version = data.version || 0;
|
||||
this.links = data.links || [];
|
||||
this.schemaVersion = data.schemaVersion ?? 0;
|
||||
this.fiscalYearStartMonth = data.fiscalYearStartMonth ?? 0;
|
||||
this.version = data.version ?? 0;
|
||||
this.links = data.links ?? [];
|
||||
this.gnetId = data.gnetId || null;
|
||||
this.panels = map(data.panels || [], (panelData: any) => new PanelModel(panelData));
|
||||
this.panels = map(data.panels ?? [], (panelData: any) => new PanelModel(panelData));
|
||||
this.formatDate = this.formatDate.bind(this);
|
||||
|
||||
this.resetOriginalVariables(true);
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
lastUsedDatasourceKeyForOrgId,
|
||||
parseUrlState,
|
||||
} from 'app/core/utils/explore';
|
||||
import { getTimeZone } from '../profile/state/selectors';
|
||||
import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors';
|
||||
import Explore from './Explore';
|
||||
|
||||
interface OwnProps {
|
||||
@@ -99,13 +99,14 @@ const getTimeRangeFromUrlMemoized = memoizeOne(getTimeRangeFromUrl);
|
||||
function mapStateToProps(state: StoreState, props: OwnProps) {
|
||||
const urlState = parseUrlState(props.urlQuery);
|
||||
const timeZone = getTimeZone(state.user);
|
||||
const fiscalYearStartMonth = getFiscalYearStartMonth(state.user);
|
||||
|
||||
const { datasource, queries, range: urlRange, originPanelId } = (urlState || {}) as ExploreUrlState;
|
||||
const initialDatasource = datasource || store.get(lastUsedDatasourceKeyForOrgId(state.user.orgId));
|
||||
const initialQueries: DataQuery[] = ensureQueriesMemoized(queries);
|
||||
const initialRange = urlRange
|
||||
? getTimeRangeFromUrlMemoized(urlRange, timeZone)
|
||||
: getTimeRange(timeZone, DEFAULT_RANGE);
|
||||
? getTimeRangeFromUrlMemoized(urlRange, timeZone, fiscalYearStartMonth)
|
||||
: getTimeRange(timeZone, DEFAULT_RANGE, fiscalYearStartMonth);
|
||||
|
||||
return {
|
||||
initialized: state.explore[props.exploreId]?.initialized,
|
||||
|
||||
@@ -19,11 +19,13 @@ export interface Props {
|
||||
hideText?: boolean;
|
||||
range: TimeRange;
|
||||
timeZone: TimeZone;
|
||||
fiscalYearStartMonth: number;
|
||||
splitted: boolean;
|
||||
syncedTimes: boolean;
|
||||
onChangeTimeSync: () => void;
|
||||
onChangeTime: (range: RawTimeRange) => void;
|
||||
onChangeTimeZone: (timeZone: TimeZone) => void;
|
||||
onChangeFiscalYearStartMonth: (fiscalYearStartMonth: number) => void;
|
||||
}
|
||||
|
||||
export class ExploreTimeControls extends Component<Props> {
|
||||
@@ -63,11 +65,22 @@ export class ExploreTimeControls extends Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { range, timeZone, splitted, syncedTimes, onChangeTimeSync, hideText, onChangeTimeZone } = this.props;
|
||||
const {
|
||||
range,
|
||||
timeZone,
|
||||
fiscalYearStartMonth,
|
||||
splitted,
|
||||
syncedTimes,
|
||||
onChangeTimeSync,
|
||||
hideText,
|
||||
onChangeTimeZone,
|
||||
onChangeFiscalYearStartMonth,
|
||||
} = this.props;
|
||||
const timeSyncButton = splitted ? <TimeSyncButton onClick={onChangeTimeSync} isSynced={syncedTimes} /> : undefined;
|
||||
const timePickerCommonProps = {
|
||||
value: range,
|
||||
timeZone,
|
||||
fiscalYearStartMonth,
|
||||
onMoveBackward: this.onMoveBack,
|
||||
onMoveForward: this.onMoveForward,
|
||||
onZoom: this.onZoom,
|
||||
@@ -81,6 +94,7 @@ export class ExploreTimeControls extends Component<Props> {
|
||||
isSynced={syncedTimes}
|
||||
onChange={this.onChangeTimePicker}
|
||||
onChangeTimeZone={onChangeTimeZone}
|
||||
onChangeFiscalYearStartMonth={onChangeFiscalYearStartMonth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import { createAndCopyShortLink } from 'app/core/utils/shortLinks';
|
||||
import { changeDatasource } from './state/datasource';
|
||||
import { splitClose, splitOpen } from './state/main';
|
||||
import { syncTimes, changeRefreshInterval } from './state/time';
|
||||
import { getTimeZone } from '../profile/state/selectors';
|
||||
import { updateTimeZoneForSession } from '../profile/state/reducers';
|
||||
import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors';
|
||||
import { updateFiscalYearStartMonthForSession, updateTimeZoneForSession } from '../profile/state/reducers';
|
||||
import { ExploreTimeControls } from './ExploreTimeControls';
|
||||
import { LiveTailButton } from './LiveTailButton';
|
||||
import { RunButton } from './RunButton';
|
||||
@@ -65,6 +65,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
|
||||
loading,
|
||||
range,
|
||||
timeZone,
|
||||
fiscalYearStartMonth,
|
||||
splitted,
|
||||
syncedTimes,
|
||||
refreshInterval,
|
||||
@@ -75,6 +76,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
|
||||
isPaused,
|
||||
containerWidth,
|
||||
onChangeTimeZone,
|
||||
onChangeFiscalYearStartMonth,
|
||||
} = this.props;
|
||||
|
||||
const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false;
|
||||
@@ -158,12 +160,14 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
|
||||
exploreId={exploreId}
|
||||
range={range}
|
||||
timeZone={timeZone}
|
||||
fiscalYearStartMonth={fiscalYearStartMonth}
|
||||
onChangeTime={onChangeTime}
|
||||
splitted={splitted}
|
||||
syncedTimes={syncedTimes}
|
||||
onChangeTimeSync={this.onChangeTimeSync}
|
||||
hideText={showSmallTimePicker}
|
||||
onChangeTimeZone={onChangeTimeZone}
|
||||
onChangeFiscalYearStartMonth={onChangeFiscalYearStartMonth}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -230,6 +234,7 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps) => {
|
||||
loading,
|
||||
range,
|
||||
timeZone: getTimeZone(state.user),
|
||||
fiscalYearStartMonth: getFiscalYearStartMonth(state.user),
|
||||
splitted: isSplit(state),
|
||||
refreshInterval,
|
||||
hasLiveOption,
|
||||
@@ -250,6 +255,7 @@ const mapDispatchToProps = {
|
||||
split: splitOpen,
|
||||
syncTimes,
|
||||
onChangeTimeZone: updateTimeZoneForSession,
|
||||
onChangeFiscalYearStartMonth: updateFiscalYearStartMonthForSession,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { createAction, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { EventBusExtended, DataQuery, ExploreUrlState, TimeRange, HistoryItem, DataSourceApi } from '@grafana/data';
|
||||
// Types
|
||||
import { ThunkResult } from 'app/types';
|
||||
import { getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { getFiscalYearStartMonth, getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { getRichHistory } from '../../../core/utils/richHistory';
|
||||
import { richHistoryUpdatedAction } from './main';
|
||||
@@ -153,7 +153,8 @@ export function refreshExplore(exploreId: ExploreId, newUrlQuery: string): Thunk
|
||||
}
|
||||
|
||||
const timeZone = getTimeZone(getState().user);
|
||||
const range = getTimeRangeFromUrl(urlRange, timeZone);
|
||||
const fiscalYearStartMonth = getFiscalYearStartMonth(getState().user);
|
||||
const range = getTimeRangeFromUrl(urlRange, timeZone, fiscalYearStartMonth);
|
||||
|
||||
// commit changes based on the diff of new url vs old url
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { RefreshPicker } from '@grafana/ui';
|
||||
import { getTimeRange, refreshIntervalToSortOrder, stopQueryState } from 'app/core/utils/explore';
|
||||
import { ExploreItemState, ThunkResult } from 'app/types';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
import { getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { getFiscalYearStartMonth, getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { getTimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import { DashboardModel } from 'app/features/dashboard/state';
|
||||
import { runQueries } from './query';
|
||||
@@ -78,6 +78,7 @@ export const updateTime = (config: {
|
||||
const { exploreId, absoluteRange: absRange, rawRange: actionRange } = config;
|
||||
const itemState = getState().explore[exploreId]!;
|
||||
const timeZone = getTimeZone(getState().user);
|
||||
const fiscalYearStartMonth = getFiscalYearStartMonth(getState().user);
|
||||
const { range: rangeInState } = itemState;
|
||||
let rawRange: RawTimeRange = rangeInState.raw;
|
||||
|
||||
@@ -92,7 +93,7 @@ export const updateTime = (config: {
|
||||
rawRange = actionRange;
|
||||
}
|
||||
|
||||
const range = getTimeRange(timeZone, rawRange);
|
||||
const range = getTimeRange(timeZone, rawRange, fiscalYearStartMonth);
|
||||
const absoluteRange: AbsoluteTimeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
|
||||
|
||||
getTimeSrv().init(
|
||||
|
||||
@@ -9,6 +9,7 @@ import { contextSrv } from 'app/core/core';
|
||||
export interface UserState {
|
||||
orgId: number;
|
||||
timeZone: TimeZone;
|
||||
fiscalYearStartMonth: number;
|
||||
user: UserDTO | null;
|
||||
teams: Team[];
|
||||
orgs: UserOrg[];
|
||||
@@ -22,6 +23,7 @@ export interface UserState {
|
||||
export const initialUserState: UserState = {
|
||||
orgId: config.bootData.user.orgId,
|
||||
timeZone: config.bootData.user.timezone,
|
||||
fiscalYearStartMonth: 0,
|
||||
orgsAreLoading: false,
|
||||
sessionsAreLoading: false,
|
||||
teamsAreLoading: false,
|
||||
@@ -39,6 +41,9 @@ export const slice = createSlice({
|
||||
updateTimeZone: (state, action: PayloadAction<{ timeZone: TimeZone }>) => {
|
||||
state.timeZone = action.payload.timeZone;
|
||||
},
|
||||
updateFiscalYearStartMonth: (state, action: PayloadAction<{ fiscalYearStartMonth: number }>) => {
|
||||
state.fiscalYearStartMonth = action.payload.fiscalYearStartMonth;
|
||||
},
|
||||
setUpdating: (state, action: PayloadAction<{ updating: boolean }>) => {
|
||||
state.isUpdating = action.payload.updating;
|
||||
},
|
||||
@@ -87,6 +92,13 @@ export const slice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const updateFiscalYearStartMonthForSession = (fiscalYearStartMonth: number): ThunkResult<void> => {
|
||||
return async (dispatch) => {
|
||||
set(contextSrv, 'user.fiscalYearStartMonth', fiscalYearStartMonth);
|
||||
dispatch(updateFiscalYearStartMonth({ fiscalYearStartMonth }));
|
||||
};
|
||||
};
|
||||
|
||||
export const updateTimeZoneForSession = (timeZone: TimeZone): ThunkResult<void> => {
|
||||
return async (dispatch) => {
|
||||
if (!isString(timeZone) || isEmpty(timeZone)) {
|
||||
@@ -109,6 +121,7 @@ export const {
|
||||
initLoadSessions,
|
||||
sessionsLoaded,
|
||||
updateTimeZone,
|
||||
updateFiscalYearStartMonth,
|
||||
} = slice.actions;
|
||||
|
||||
export const userReducer = slice.reducer;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UserState } from './reducers';
|
||||
|
||||
export const getTimeZone = (state: UserState) => state.timeZone;
|
||||
export const getFiscalYearStartMonth = (state: UserState) => state.fiscalYearStartMonth;
|
||||
|
||||
Reference in New Issue
Block a user