mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: adding a time picker for selecting relative time. (#33689)
* adding placeholder for relative time range. * fixed story. * added basic structure to handle open/close of time range picker. * removed section from TimeOptions since it isn't used any where. * adding mapper and tests * move relativetimepicker to its own dir * added some simple tests. * changed test. * use relativetimerangeinput * redo state management * refactored the tests. * replace timerange with relativetimerange * wip * wip * did some refactoring. * refactored time option formatting. * added proper formatting and display of time range. * add relative time description, slight refactor of height * fixed incorrect import. * added validator and changed formatting. * removed unused dep. * reverted back to internal function. * fixed display of relative time range picker. * fixed failing tests. * fixed parsing issue. * fixed position of time range picker. * some more refactorings. * fixed validation of really big values. * added another test. Co-authored-by: Peter Holmberg <peter.hlmbrg@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { PureComponent, ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { has, cloneDeep } from 'lodash';
|
||||
// Utils & Services
|
||||
@@ -35,10 +35,9 @@ interface Props {
|
||||
queries: DataQuery[];
|
||||
id: string;
|
||||
index: number;
|
||||
timeRange?: TimeRange;
|
||||
dataSource: DataSourceInstanceSettings;
|
||||
onChangeDataSource?: (dsSettings: DataSourceInstanceSettings) => void;
|
||||
onChangeTimeRange?: (timeRange: TimeRange) => void;
|
||||
renderHeaderExtras?: () => ReactNode;
|
||||
onAddQuery: (query: DataQuery) => void;
|
||||
onRemoveQuery: (query: DataQuery) => void;
|
||||
onChange: (query: DataQuery) => void;
|
||||
@@ -304,20 +303,19 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
renderHeader = (props: QueryOperationRowRenderProps) => {
|
||||
const { query, dataSource, onChangeDataSource, onChange, queries, onChangeTimeRange, timeRange } = this.props;
|
||||
const { query, dataSource, onChangeDataSource, onChange, queries, renderHeaderExtras } = this.props;
|
||||
|
||||
return (
|
||||
<QueryEditorRowHeader
|
||||
query={query}
|
||||
queries={queries}
|
||||
onChangeTimeRange={onChangeTimeRange}
|
||||
timeRange={timeRange}
|
||||
onChangeDataSource={onChangeDataSource}
|
||||
dataSource={dataSource}
|
||||
disabled={query.hide}
|
||||
onClick={(e) => this.onToggleEditMode(e, props)}
|
||||
onChange={onChange}
|
||||
collapsedText={!props.isOpen ? this.renderCollapsedText() : null}
|
||||
renderExtras={renderHeaderExtras}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { Props, QueryEditorRowHeader } from './QueryEditorRowHeader';
|
||||
import { DataSourceInstanceSettings, dateTime } from '@grafana/data';
|
||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
jest.mock('@grafana/runtime/src/services/dataSourceSrv', () => {
|
||||
@@ -58,41 +58,6 @@ describe('QueryEditorRowHeader', () => {
|
||||
|
||||
expect(screen.queryByLabelText(selectors.components.DataSourcePicker.container)).toBeNull();
|
||||
});
|
||||
|
||||
it('should show time range picker when callback and value is passed', async () => {
|
||||
renderScenario({
|
||||
onChangeTimeRange: () => {},
|
||||
timeRange: {
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now', to: 'now' },
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.queryByLabelText(selectors.components.TimePicker.openButton)).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not show time range picker when no value is passed', async () => {
|
||||
renderScenario({
|
||||
onChangeTimeRange: () => {},
|
||||
timeRange: undefined,
|
||||
});
|
||||
|
||||
expect(screen.queryByLabelText(selectors.components.DataSourcePicker.container)).toBeNull();
|
||||
});
|
||||
|
||||
it('should not show time range picker when no callback is passed', async () => {
|
||||
renderScenario({
|
||||
onChangeTimeRange: undefined,
|
||||
timeRange: {
|
||||
from: dateTime(),
|
||||
to: dateTime(),
|
||||
raw: { from: 'now', to: 'now' },
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.queryByLabelText(selectors.components.DataSourcePicker.container)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
function renderScenario(overrides: Partial<Props>) {
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { DataQuery, DataSourceInstanceSettings, GrafanaTheme, TimeRange } from '@grafana/data';
|
||||
import { DataQuery, DataSourceInstanceSettings, GrafanaTheme } from '@grafana/data';
|
||||
import { DataSourcePicker } from '@grafana/runtime';
|
||||
import { Icon, Input, FieldValidationMessage, TimeRangeInput, useStyles } from '@grafana/ui';
|
||||
import { Icon, Input, FieldValidationMessage, useStyles } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { ExpressionDatasourceUID } from '../../expressions/ExpressionDatasource';
|
||||
|
||||
export interface Props {
|
||||
query: DataQuery;
|
||||
queries: DataQuery[];
|
||||
disabled?: boolean;
|
||||
timeRange?: TimeRange;
|
||||
dataSource: DataSourceInstanceSettings;
|
||||
renderExtras?: () => ReactNode;
|
||||
onChangeDataSource?: (settings: DataSourceInstanceSettings) => void;
|
||||
onChangeTimeRange?: (timeRange: TimeRange) => void;
|
||||
onChange: (query: DataQuery) => void;
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
collapsedText: string | null;
|
||||
}
|
||||
|
||||
export const QueryEditorRowHeader: React.FC<Props> = (props) => {
|
||||
const { dataSource, onChangeDataSource, disabled, query, queries, onClick, onChange, collapsedText } = props;
|
||||
const { query, queries, onClick, onChange, collapsedText, renderExtras, disabled } = props;
|
||||
|
||||
const styles = useStyles(getStyles);
|
||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||
@@ -112,9 +110,9 @@ export const QueryEditorRowHeader: React.FC<Props> = (props) => {
|
||||
{validationError && <FieldValidationMessage horizontal>{validationError}</FieldValidationMessage>}
|
||||
</>
|
||||
)}
|
||||
<PickerRenderer {...props} />
|
||||
{dataSource && !onChangeDataSource && <em className={styles.contextInfo}> ({dataSource.name})</em>}
|
||||
{disabled && <em className={styles.contextInfo}> Disabled</em>}
|
||||
{renderDataSource(props, styles)}
|
||||
{renderExtras && <div className={styles.itemWrapper}>{renderExtras()}</div>}
|
||||
{disabled && <em className={styles.contextInfo}>Disabled</em>}
|
||||
|
||||
{collapsedText && (
|
||||
<div className={styles.collapsedText} onClick={onClick}>
|
||||
@@ -125,22 +123,16 @@ export const QueryEditorRowHeader: React.FC<Props> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const PickerRenderer: React.FC<Props> = (props) => {
|
||||
const { onChangeTimeRange, timeRange, onChangeDataSource, dataSource } = props;
|
||||
const styles = useStyles(getStyles);
|
||||
const renderDataSource = (props: Props, styles: ReturnType<typeof getStyles>): ReactNode => {
|
||||
const { dataSource, onChangeDataSource } = props;
|
||||
|
||||
if (!onChangeTimeRange && !onChangeDataSource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dataSource.uid === ExpressionDatasourceUID) {
|
||||
return null;
|
||||
if (!onChangeDataSource) {
|
||||
return <em className={styles.contextInfo}>({dataSource.name})</em>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.pickerWrapper}>
|
||||
{onChangeDataSource && <DataSourcePicker current={dataSource.name} onChange={onChangeDataSource} />}
|
||||
{onChangeTimeRange && timeRange && <TimeRangeInput onChange={onChangeTimeRange} value={timeRange} />}
|
||||
<div className={styles.itemWrapper}>
|
||||
<DataSourcePicker current={dataSource.name} onChange={onChangeDataSource} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -222,9 +214,9 @@ const getStyles = (theme: GrafanaTheme) => {
|
||||
color: ${theme.colors.textWeak};
|
||||
padding-left: 10px;
|
||||
`,
|
||||
pickerWrapper: css`
|
||||
itemWrapper: css`
|
||||
display: flex;
|
||||
margin-left: 8px;
|
||||
margin-left: 4px;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user