diff --git a/packages/grafana-data/src/datetime/rangeutil.test.ts b/packages/grafana-data/src/datetime/rangeutil.test.ts index 63931563880..da1606b6ac7 100644 --- a/packages/grafana-data/src/datetime/rangeutil.test.ts +++ b/packages/grafana-data/src/datetime/rangeutil.test.ts @@ -1,4 +1,4 @@ -import { rangeUtil } from './index'; +import { dateTime, rangeUtil } from './index'; describe('Range Utils', () => { describe('relative time', () => { @@ -40,4 +40,22 @@ describe('Range Utils', () => { expect(() => rangeUtil.describeInterval('xyz')).toThrow(); }); }); + + describe('relativeToTimeRange', () => { + it('should convert seconds to timeRange', () => { + const relativeTimeRange = { from: 600, to: 300 }; + const timeRange = rangeUtil.relativeToTimeRange(relativeTimeRange, dateTime('2021-04-20T15:55:00Z')); + + expect(timeRange.from.valueOf()).toEqual(dateTime('2021-04-20T15:45:00Z').valueOf()); + expect(timeRange.to.valueOf()).toEqual(dateTime('2021-04-20T15:50:00Z').valueOf()); + }); + + it('should convert from now', () => { + const relativeTimeRange = { from: 600, to: 0 }; + const timeRange = rangeUtil.relativeToTimeRange(relativeTimeRange, dateTime('2021-04-20T15:55:00Z')); + + expect(timeRange.from.valueOf()).toEqual(dateTime('2021-04-20T15:45:00Z').valueOf()); + expect(timeRange.to.valueOf()).toEqual(dateTime('2021-04-20T15:55:00Z').valueOf()); + }); + }); }); diff --git a/packages/grafana-data/src/datetime/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts index bc2c01a8c0d..b1ce1432990 100644 --- a/packages/grafana-data/src/datetime/rangeutil.ts +++ b/packages/grafana-data/src/datetime/rangeutil.ts @@ -1,9 +1,9 @@ import { each, groupBy, has } from 'lodash'; -import { RawTimeRange, TimeRange, TimeZone, IntervalValues } from '../types/time'; +import { RawTimeRange, TimeRange, TimeZone, IntervalValues, RelativeTimeRange } from '../types/time'; import * as dateMath from './datemath'; -import { isDateTime, DateTime } from './moment_wrapper'; +import { isDateTime, DateTime, dateTime } from './moment_wrapper'; import { timeZoneAbbrevation, dateTimeFormat, dateTimeFormatTimeAgo } from './formatter'; import { dateTimeParse } from './parser'; @@ -432,3 +432,36 @@ export function roundInterval(interval: number) { return 31536000000; // 1y } } + +/** + * Converts a TimeRange to a RelativeTimeRange that can be used in + * e.g. alerting queries/rules. + * + * @internal + */ +export function timeRangeToRelative(timeRange: TimeRange): RelativeTimeRange { + const now = dateTime().unix(); + const from = (now - timeRange.from.unix()) / 1000; + const to = (now - timeRange.to.unix()) / 1000; + + return { + from, + to, + }; +} + +/** + * Converts a RelativeTimeRange to a TimeRange + * + * @internal + */ +export function relativeToTimeRange(relativeTimeRange: RelativeTimeRange, now: DateTime = dateTime()): TimeRange { + const from = dateTime(now).subtract(relativeTimeRange.from, 's'); + const to = relativeTimeRange.to === 0 ? dateTime(now) : dateTime(now).subtract(relativeTimeRange.to, 's'); + + return { + from, + to, + raw: { from, to }, + }; +} diff --git a/packages/grafana-data/src/types/time.ts b/packages/grafana-data/src/types/time.ts index b417e7dd201..4f7e93f0f89 100644 --- a/packages/grafana-data/src/types/time.ts +++ b/packages/grafana-data/src/types/time.ts @@ -11,6 +11,15 @@ export interface TimeRange { raw: RawTimeRange; } +/** + * Type to describe relative time to now in seconds. + * @internal + */ +export interface RelativeTimeRange { + from: number; + to: number; +} + export interface AbsoluteTimeRange { from: number; to: number; diff --git a/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx b/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx index 3c21fe4fbfe..978a01d4530 100644 --- a/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx +++ b/packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx @@ -43,7 +43,7 @@ export const ButtonSelect = React.memo((props: Props) => { }; return ( - <> +
(props: Props) => {
)} - + ); }); @@ -79,6 +79,10 @@ ButtonSelect.displayName = 'ButtonSelect'; const getStyles = (theme: GrafanaTheme) => { return { + wrapper: css` + position: relative; + display: inline-flex; + `, menuWrapper: css` position: absolute; z-index: ${theme.zIndex.dropdown}; diff --git a/public/app/features/alerting/NextGenAlertingPage.tsx b/public/app/features/alerting/NextGenAlertingPage.tsx index 8bc5cd61e03..d1e13d68d26 100644 --- a/public/app/features/alerting/NextGenAlertingPage.tsx +++ b/public/app/features/alerting/NextGenAlertingPage.tsx @@ -16,19 +16,17 @@ import { evaluateAlertDefinition, evaluateNotSavedAlertDefinition, getAlertDefinition, - onRunQueries, updateAlertDefinition, updateAlertDefinitionOption, updateAlertDefinitionUiState, } from './state/actions'; import { StoreState } from 'app/types'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; +import { GrafanaQuery } from '../../types/unified-alerting-dto'; function mapStateToProps(state: StoreState, props: RouteProps) { return { uiState: state.alertDefinition.uiState, - getQueryOptions: state.alertDefinition.getQueryOptions, - queryRunner: state.alertDefinition.queryRunner, getInstances: state.alertDefinition.getInstances, alertDefinition: state.alertDefinition.alertDefinition, pageId: props.match.params.id as string, @@ -43,7 +41,6 @@ const mapDispatchToProps = { createAlertDefinition, getAlertDefinition, evaluateNotSavedAlertDefinition, - onRunQueries, cleanUpDefinitionState, }; @@ -125,18 +122,9 @@ class NextGenAlertingPageUnconnected extends PureComponent { } render() { - const { - alertDefinition, - uiState, - updateAlertDefinitionUiState, - getQueryOptions, - getInstances, - onRunQueries, - queryRunner, - } = this.props; + const { alertDefinition, uiState, updateAlertDefinitionUiState, getInstances } = this.props; const styles = getStyles(config.theme); - const queryOptions = getQueryOptions(); return (
@@ -146,15 +134,8 @@ class NextGenAlertingPageUnconnected extends PureComponent {
, - , + , + {}} />, ]} uiState={uiState} updateUiState={updateAlertDefinitionUiState} @@ -164,7 +145,6 @@ class NextGenAlertingPageUnconnected extends PureComponent { onChange={this.onChangeAlertOption} onIntervalChange={this.onChangeInterval} onConditionChange={this.onConditionChange} - queryOptions={queryOptions} /> } /> diff --git a/public/app/features/alerting/components/AlertDefinitionOptions.tsx b/public/app/features/alerting/components/AlertDefinitionOptions.tsx index dbc09d64c23..db22fb8d801 100644 --- a/public/app/features/alerting/components/AlertDefinitionOptions.tsx +++ b/public/app/features/alerting/components/AlertDefinitionOptions.tsx @@ -1,8 +1,8 @@ -import React, { FC, FormEvent, useMemo } from 'react'; +import React, { FC, FormEvent } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme, SelectableValue } from '@grafana/data'; import { Field, Input, Select, Tab, TabContent, TabsBar, TextArea, useStyles } from '@grafana/ui'; -import { AlertDefinition, QueryGroupOptions } from 'app/types'; +import { AlertDefinition } from 'app/types'; const intervalOptions: Array> = [ { value: 60, label: '1m' }, @@ -15,20 +15,10 @@ interface Props { onChange: (event: FormEvent) => void; onIntervalChange: (interval: SelectableValue) => void; onConditionChange: (refId: SelectableValue) => void; - queryOptions: QueryGroupOptions; } -export const AlertDefinitionOptions: FC = ({ - alertDefinition, - onChange, - onIntervalChange, - onConditionChange, - queryOptions, -}) => { +export const AlertDefinitionOptions: FC = ({ alertDefinition, onChange, onIntervalChange }) => { const styles = useStyles(getStyles); - const refIds = useMemo(() => queryOptions.queries.map((q) => ({ value: q.refId, label: q.refId })), [ - queryOptions.queries, - ]); return (
@@ -61,14 +51,7 @@ export const AlertDefinitionOptions: FC = ({
-
-