mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 16:45:43 -06:00
* feat: Add new picker to DashNavTimeControls * chore: noImplicitAny limit reached * chore: noImplicityAny fix * chore: Add momentUtc helper to avoid the isUtc conditionals * chore: Move getRaw from Explore's time picker to grafana/ui utils and rename to getRawRange * feat: Use helper functions to convert utc to browser time * fix: Dont Select current value when pressing tab when using Time Picker * fix: Add tabIndex to time range inputs so tab works smoothly and prevent mouseDown event to propagate to react-select * fix: Add spacing to custom range labels * fix: Updated snapshot * fix: Re-adding getRaw() temporary to fix the build * fix: Disable scroll event in Popper when we're using the TimePicker so the popup wont "follow" the menu * fix: Move all "Last xxxx" quick ranges to the menu and show a "UTC" text when applicable * fix: Add zoom functionality * feat: Add logic to mark selected option as active * fix: Add tooltip to zoom button * fix: lint fix after rebase * chore: Remove old time picker from DashNav * TimePicker: minor design update * chore: Move all time picker quick ranges to the menu * fix: Remove the popover border-right, since the quick ranges are gone * chore: Remove function not in use * Fix: Close time picker on resize event * Fix: Remove border bottom * Fix: Use fa icons on prev/next arrows * Fix: Pass ref from TimePicker to TimePickerOptionGroup so the popover will align as it should * Fix: time picker ui adjustments to get better touch area on buttons * Fix: Dont increase line height on large screens * TimePicker: style updates * Fix: Add more prominent colors for selected dates and fade out dates in previous/next month * TimePicker: style updates2 * TimePicker: Big refactorings and style changes * Removed use of Popper not sure we need that here? * Made active selected item in the list have the "selected" checkmark * Changed design of popover * Changed design of and implementation of the Custom selection in the dropdown it did not feel like a item you could select like the rest now the list is just a normal list * TimePicker: Refactoring & style changes * TimePicker: use same date format everywhere * TimePicker: Calendar style updates * TimePicker: fixed unit test * fixed unit test * TimeZone: refactoring time zone type * TimePicker: refactoring * TimePicker: finally to UTC to work * TimePicker: better way to handle calendar utc dates * TimePicker: Fixed tooltip issues * Updated snapshot * TimePicker: moved tooltip from DashNavControls into TimePicker
336 lines
10 KiB
TypeScript
336 lines
10 KiB
TypeScript
import React, { PureComponent } from 'react';
|
|
import { connect } from 'react-redux';
|
|
import { hot } from 'react-hot-loader';
|
|
|
|
import { ExploreId, ExploreMode } from 'app/types/explore';
|
|
import {
|
|
DataSourceSelectItem,
|
|
RawTimeRange,
|
|
ClickOutsideWrapper,
|
|
TimeZone,
|
|
TimeRange,
|
|
SelectOptionItem,
|
|
LoadingState,
|
|
} from '@grafana/ui';
|
|
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
|
|
import { StoreState } from 'app/types/store';
|
|
import {
|
|
changeDatasource,
|
|
clearQueries,
|
|
splitClose,
|
|
runQueries,
|
|
splitOpen,
|
|
changeRefreshInterval,
|
|
changeMode,
|
|
} from './state/actions';
|
|
import TimePicker from './TimePicker';
|
|
import { getTimeZone } from '../profile/state/selectors';
|
|
import { RefreshPicker, SetInterval } from '@grafana/ui';
|
|
import ToggleButtonGroup, { ToggleButton } from 'app/core/components/ToggleButtonGroup/ToggleButtonGroup';
|
|
|
|
enum IconSide {
|
|
left = 'left',
|
|
right = 'right',
|
|
}
|
|
|
|
const createResponsiveButton = (options: {
|
|
splitted: boolean;
|
|
title: string;
|
|
onClick: () => void;
|
|
buttonClassName?: string;
|
|
iconClassName?: string;
|
|
iconSide?: IconSide;
|
|
disabled?: boolean;
|
|
}) => {
|
|
const defaultOptions = {
|
|
iconSide: IconSide.left,
|
|
};
|
|
const props = { ...options, defaultOptions };
|
|
const { title, onClick, buttonClassName, iconClassName, splitted, iconSide, disabled } = props;
|
|
|
|
return (
|
|
<button
|
|
className={`btn navbar-button ${buttonClassName ? buttonClassName : ''}`}
|
|
onClick={onClick}
|
|
disabled={disabled || false}
|
|
>
|
|
{iconClassName && iconSide === IconSide.left ? <i className={`${iconClassName}`} /> : null}
|
|
<span className="btn-title">{!splitted ? title : ''}</span>
|
|
{iconClassName && iconSide === IconSide.right ? <i className={`${iconClassName}`} /> : null}
|
|
</button>
|
|
);
|
|
};
|
|
|
|
interface OwnProps {
|
|
exploreId: ExploreId;
|
|
timepickerRef: React.RefObject<TimePicker>;
|
|
onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void;
|
|
}
|
|
|
|
interface StateProps {
|
|
datasourceMissing: boolean;
|
|
exploreDatasources: DataSourceSelectItem[];
|
|
loading: boolean;
|
|
range: TimeRange;
|
|
timeZone: TimeZone;
|
|
selectedDatasource: DataSourceSelectItem;
|
|
splitted: boolean;
|
|
refreshInterval: string;
|
|
supportedModeOptions: Array<SelectOptionItem<ExploreMode>>;
|
|
selectedModeOption: SelectOptionItem<ExploreMode>;
|
|
hasLiveOption: boolean;
|
|
isLive: boolean;
|
|
}
|
|
|
|
interface DispatchProps {
|
|
changeDatasource: typeof changeDatasource;
|
|
clearAll: typeof clearQueries;
|
|
runQueries: typeof runQueries;
|
|
closeSplit: typeof splitClose;
|
|
split: typeof splitOpen;
|
|
changeRefreshInterval: typeof changeRefreshInterval;
|
|
changeMode: typeof changeMode;
|
|
}
|
|
|
|
type Props = StateProps & DispatchProps & OwnProps;
|
|
|
|
export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
}
|
|
|
|
onChangeDatasource = async option => {
|
|
this.props.changeDatasource(this.props.exploreId, option.value);
|
|
};
|
|
|
|
onClearAll = () => {
|
|
this.props.clearAll(this.props.exploreId);
|
|
};
|
|
|
|
onRunQuery = () => {
|
|
return this.props.runQueries(this.props.exploreId);
|
|
};
|
|
|
|
onCloseTimePicker = () => {
|
|
this.props.timepickerRef.current.setState({ isOpen: false });
|
|
};
|
|
|
|
onChangeRefreshInterval = (item: string) => {
|
|
const { changeRefreshInterval, exploreId } = this.props;
|
|
changeRefreshInterval(exploreId, item);
|
|
};
|
|
|
|
onModeChange = (mode: ExploreMode) => {
|
|
const { changeMode, exploreId } = this.props;
|
|
changeMode(exploreId, mode);
|
|
};
|
|
|
|
render() {
|
|
const {
|
|
datasourceMissing,
|
|
exploreDatasources,
|
|
closeSplit,
|
|
exploreId,
|
|
loading,
|
|
range,
|
|
timeZone,
|
|
selectedDatasource,
|
|
splitted,
|
|
timepickerRef,
|
|
refreshInterval,
|
|
onChangeTime,
|
|
split,
|
|
supportedModeOptions,
|
|
selectedModeOption,
|
|
hasLiveOption,
|
|
isLive,
|
|
} = this.props;
|
|
|
|
return (
|
|
<div className={splitted ? 'explore-toolbar splitted' : 'explore-toolbar'}>
|
|
<div className="explore-toolbar-item">
|
|
<div className="explore-toolbar-header">
|
|
<div className="explore-toolbar-header-title">
|
|
{exploreId === 'left' && (
|
|
<span className="navbar-page-btn">
|
|
<i className="gicon gicon-explore" />
|
|
Explore
|
|
</span>
|
|
)}
|
|
</div>
|
|
{splitted && (
|
|
<a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}>
|
|
<i className="fa fa-times fa-fw" />
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="explore-toolbar-item">
|
|
<div className="explore-toolbar-content">
|
|
{!datasourceMissing ? (
|
|
<div className="explore-toolbar-content-item">
|
|
<div className="datasource-picker">
|
|
<DataSourcePicker
|
|
onChange={this.onChangeDatasource}
|
|
datasources={exploreDatasources}
|
|
current={selectedDatasource}
|
|
/>
|
|
</div>
|
|
{supportedModeOptions.length > 1 ? (
|
|
<div className="query-type-toggle">
|
|
<ToggleButtonGroup label="" transparent={true}>
|
|
<ToggleButton
|
|
key={ExploreMode.Metrics}
|
|
value={ExploreMode.Metrics}
|
|
onChange={this.onModeChange}
|
|
selected={selectedModeOption.value === ExploreMode.Metrics}
|
|
>
|
|
{'Metrics'}
|
|
</ToggleButton>
|
|
<ToggleButton
|
|
key={ExploreMode.Logs}
|
|
value={ExploreMode.Logs}
|
|
onChange={this.onModeChange}
|
|
selected={selectedModeOption.value === ExploreMode.Logs}
|
|
>
|
|
{'Logs'}
|
|
</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
|
|
{exploreId === 'left' && !splitted ? (
|
|
<div className="explore-toolbar-content-item">
|
|
{createResponsiveButton({
|
|
splitted,
|
|
title: 'Split',
|
|
onClick: split,
|
|
iconClassName: 'fa fa-fw fa-columns icon-margin-right',
|
|
iconSide: IconSide.left,
|
|
disabled: isLive,
|
|
})}
|
|
</div>
|
|
) : null}
|
|
<div className="explore-toolbar-content-item timepicker">
|
|
{!isLive && (
|
|
<ClickOutsideWrapper onClick={this.onCloseTimePicker}>
|
|
<TimePicker ref={timepickerRef} range={range} timeZone={timeZone} onChangeTime={onChangeTime} />
|
|
</ClickOutsideWrapper>
|
|
)}
|
|
|
|
<RefreshPicker
|
|
onIntervalChanged={this.onChangeRefreshInterval}
|
|
onRefresh={this.onRunQuery}
|
|
value={refreshInterval}
|
|
tooltip="Refresh"
|
|
hasLiveOption={hasLiveOption}
|
|
/>
|
|
{refreshInterval && <SetInterval func={this.onRunQuery} interval={refreshInterval} loading={loading} />}
|
|
</div>
|
|
|
|
<div className="explore-toolbar-content-item">
|
|
<button className="btn navbar-button" onClick={this.onClearAll}>
|
|
Clear All
|
|
</button>
|
|
</div>
|
|
<div className="explore-toolbar-content-item">
|
|
{createResponsiveButton({
|
|
splitted,
|
|
title: 'Run Query',
|
|
onClick: this.onRunQuery,
|
|
buttonClassName: 'navbar-button--secondary',
|
|
iconClassName:
|
|
loading && !isLive ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon',
|
|
iconSide: IconSide.right,
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps => {
|
|
const splitted = state.explore.split;
|
|
const exploreItem = state.explore[exploreId];
|
|
const {
|
|
datasourceInstance,
|
|
datasourceMissing,
|
|
exploreDatasources,
|
|
range,
|
|
refreshInterval,
|
|
loadingState,
|
|
supportedModes,
|
|
mode,
|
|
isLive,
|
|
} = exploreItem;
|
|
const selectedDatasource = datasourceInstance
|
|
? exploreDatasources.find(datasource => datasource.name === datasourceInstance.name)
|
|
: undefined;
|
|
const loading = loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming;
|
|
const hasLiveOption =
|
|
datasourceInstance && datasourceInstance.meta && datasourceInstance.meta.streaming ? true : false;
|
|
|
|
const supportedModeOptions: Array<SelectOptionItem<ExploreMode>> = [];
|
|
let selectedModeOption = null;
|
|
for (const supportedMode of supportedModes) {
|
|
switch (supportedMode) {
|
|
case ExploreMode.Metrics:
|
|
const option1 = {
|
|
value: ExploreMode.Metrics,
|
|
label: ExploreMode.Metrics,
|
|
};
|
|
supportedModeOptions.push(option1);
|
|
if (mode === ExploreMode.Metrics) {
|
|
selectedModeOption = option1;
|
|
}
|
|
break;
|
|
case ExploreMode.Logs:
|
|
const option2 = {
|
|
value: ExploreMode.Logs,
|
|
label: ExploreMode.Logs,
|
|
};
|
|
supportedModeOptions.push(option2);
|
|
if (mode === ExploreMode.Logs) {
|
|
selectedModeOption = option2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
datasourceMissing,
|
|
exploreDatasources,
|
|
loading,
|
|
range,
|
|
timeZone: getTimeZone(state.user),
|
|
selectedDatasource,
|
|
splitted,
|
|
refreshInterval,
|
|
supportedModeOptions,
|
|
selectedModeOption,
|
|
hasLiveOption,
|
|
isLive,
|
|
};
|
|
};
|
|
|
|
const mapDispatchToProps: DispatchProps = {
|
|
changeDatasource,
|
|
changeRefreshInterval,
|
|
clearAll: clearQueries,
|
|
runQueries,
|
|
closeSplit: splitClose,
|
|
split: splitOpen,
|
|
changeMode: changeMode,
|
|
};
|
|
|
|
export const ExploreToolbar = hot(module)(
|
|
connect(
|
|
mapStateToProps,
|
|
mapDispatchToProps
|
|
)(UnConnectedExploreToolbar)
|
|
);
|