mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
wip
This commit is contained in:
@@ -7,10 +7,12 @@ import { DataSourceSelectItem } from 'app/types';
|
||||
export interface Props {
|
||||
onChangeDataSource: (ds: DataSourceSelectItem) => void;
|
||||
datasources: DataSourceSelectItem[];
|
||||
current: DataSourceSelectItem;
|
||||
}
|
||||
|
||||
interface State {
|
||||
searchQuery: string;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class DataSourcePicker extends PureComponent<Props, State> {
|
||||
@@ -18,8 +20,10 @@ export class DataSourcePicker extends PureComponent<Props, State> {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,21 +99,39 @@ export class DataSourcePicker extends PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
onOpen = () => {
|
||||
this.setState({ isOpen: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { current } = this.props;
|
||||
const { isOpen } = this.state;
|
||||
|
||||
return (
|
||||
<KeyboardNavigation
|
||||
render={(keyNavProps: KeyboardNavigationProps) => (
|
||||
<>
|
||||
<div className="cta-form__bar">
|
||||
{this.renderFilters(keyNavProps)}
|
||||
<div className="gf-form--grow" />
|
||||
</div>
|
||||
<div className="ds-picker-list">
|
||||
{this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))}
|
||||
</div>
|
||||
</>
|
||||
<div className="ds-picker">
|
||||
{!isOpen && (
|
||||
<div className="toolbar__main" onClick={this.onOpen}>
|
||||
<img className="toolbar__main-image" src={current.meta.info.logos.small} />
|
||||
<div className="toolbar__main-name">{current.name}</div>
|
||||
<i className="fa fa-caret-down" />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
{isOpen && (
|
||||
<KeyboardNavigation
|
||||
render={(keyNavProps: KeyboardNavigationProps) => (
|
||||
<div className="ds-picker-menu">
|
||||
<div className="cta-form__bar">
|
||||
{this.renderFilters(keyNavProps)}
|
||||
<div className="gf-form--grow" />
|
||||
</div>
|
||||
<div className="ds-picker-list">
|
||||
{this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,10 @@ import { FadeIn } from 'app/core/components/Animations/FadeIn';
|
||||
interface Props {
|
||||
children: JSX.Element;
|
||||
heading: string;
|
||||
main?: EditorToolBarView;
|
||||
toolbarItems: EditorToolBarView[];
|
||||
}
|
||||
|
||||
export interface EditorToolBarView {
|
||||
title: string;
|
||||
imgSrc?: string;
|
||||
icon?: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
render: (closeFunction: any) => JSX.Element | JSX.Element[];
|
||||
renderToolbar: () => JSX.Element;
|
||||
}
|
||||
|
||||
interface State {
|
||||
openView?: EditorToolBarView;
|
||||
isOpen: boolean;
|
||||
fadeIn: boolean;
|
||||
}
|
||||
|
||||
@@ -29,9 +17,7 @@ export class EditorTabBody extends PureComponent<Props, State> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
openView: null,
|
||||
fadeIn: false,
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,87 +25,56 @@ export class EditorTabBody extends PureComponent<Props, State> {
|
||||
this.setState({ fadeIn: true });
|
||||
}
|
||||
|
||||
onToggleToolBarView = (item: EditorToolBarView) => {
|
||||
this.setState({
|
||||
openView: item,
|
||||
isOpen: !this.state.isOpen,
|
||||
});
|
||||
};
|
||||
|
||||
onCloseOpenView = () => {
|
||||
this.setState({ isOpen: false });
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (state.openView) {
|
||||
const activeToolbarItem = props.toolbarItems.find(
|
||||
item => item.title === state.openView.title && item.icon === state.openView.icon
|
||||
);
|
||||
if (activeToolbarItem) {
|
||||
return {
|
||||
...state,
|
||||
openView: activeToolbarItem,
|
||||
};
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
renderMainSelection(view: EditorToolBarView) {
|
||||
return (
|
||||
<div className="toolbar__main" onClick={() => this.onToggleToolBarView(view)} key={view.title + view.icon}>
|
||||
<img className="toolbar__main-image" src={view.imgSrc} />
|
||||
<div className="toolbar__main-name">{view.title}</div>
|
||||
<i className="fa fa-caret-down" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButton(view: EditorToolBarView) {
|
||||
const onClick = () => {
|
||||
if (view.onClick) {
|
||||
view.onClick();
|
||||
}
|
||||
this.onToggleToolBarView(view);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="nav-buttons" key={view.title + view.icon}>
|
||||
<button className="btn navbar-button" onClick={onClick} disabled={view.disabled}>
|
||||
{view.icon && <i className={view.icon} />} {view.title}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOpenView(view: EditorToolBarView) {
|
||||
return (
|
||||
<div className="toolbar-subview">
|
||||
<button className="toolbar-subview__close" onClick={this.onCloseOpenView}>
|
||||
<i className="fa fa-chevron-up" />
|
||||
</button>
|
||||
{view.render(this.onCloseOpenView)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// renderMainSelection(view: EditorToolBarView) {
|
||||
// return (
|
||||
// <div className="toolbar__main" onClick={() => this.onToggleToolBarView(view)} key={view.title + view.icon}>
|
||||
// <img className="toolbar__main-image" src={view.imgSrc} />
|
||||
// <div className="toolbar__main-name">{view.title}</div>
|
||||
// <i className="fa fa-caret-down" />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// renderButton(view: EditorToolBarView) {
|
||||
// const onClick = () => {
|
||||
// if (view.onClick) {
|
||||
// view.onClick();
|
||||
// }
|
||||
// this.onToggleToolBarView(view);
|
||||
// };
|
||||
//
|
||||
// return (
|
||||
// <div className="nav-buttons" key={view.title + view.icon}>
|
||||
// <button className="btn navbar-button" onClick={onClick} disabled={view.disabled}>
|
||||
// {view.icon && <i className={view.icon} />} {view.title}
|
||||
// </button>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// renderOpenView(view: EditorToolBarView) {
|
||||
// return (
|
||||
// <div className="toolbar-subview">
|
||||
// <button className="toolbar-subview__close" onClick={this.onCloseOpenView}>
|
||||
// <i className="fa fa-chevron-up" />
|
||||
// </button>
|
||||
// {view.render(this.onCloseOpenView)}
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
render() {
|
||||
const { children, toolbarItems, main, heading } = this.props;
|
||||
const { openView, fadeIn, isOpen } = this.state;
|
||||
const { children, renderToolbar, heading } = this.props;
|
||||
const { fadeIn } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="toolbar">
|
||||
<div className="toolbar__heading">{heading}</div>
|
||||
{main && this.renderMainSelection(main)}
|
||||
<div className="gf-form--grow" />
|
||||
{toolbarItems.map(item => this.renderButton(item))}
|
||||
{renderToolbar && renderToolbar()}
|
||||
</div>
|
||||
<div className="panel-editor__scroll">
|
||||
<CustomScrollbar autoHide={false}>
|
||||
<FadeIn in={isOpen} duration={200} unmountOnExit={true}>
|
||||
<div className="panel-editor__toolbar-view">{openView && this.renderOpenView(openView)}</div>
|
||||
</FadeIn>
|
||||
<div className="panel-editor__content">
|
||||
<FadeIn in={fadeIn} duration={50}>
|
||||
{children}
|
||||
|
||||
@@ -28,15 +28,11 @@ interface Props {
|
||||
dashboard: DashboardModel;
|
||||
}
|
||||
|
||||
interface Help {
|
||||
isLoading: boolean;
|
||||
helpHtml: any;
|
||||
}
|
||||
|
||||
interface State {
|
||||
currentDatasource: DataSourceSelectItem;
|
||||
help: Help;
|
||||
hideTimeOverride: boolean;
|
||||
currentDS: DataSourceSelectItem;
|
||||
helpContent: JSX.Element;
|
||||
isLoadingHelp: string;
|
||||
isPickerOpen: boolean;
|
||||
}
|
||||
|
||||
interface LoadingPlaceholderProps {
|
||||
@@ -56,12 +52,10 @@ export class QueriesTab extends PureComponent<Props, State> {
|
||||
const { panel } = props;
|
||||
|
||||
this.state = {
|
||||
currentDatasource: this.datasources.find(datasource => datasource.value === panel.datasource),
|
||||
help: {
|
||||
isLoading: false,
|
||||
helpHtml: null,
|
||||
},
|
||||
hideTimeOverride: false,
|
||||
currentDS: this.datasources.find(datasource => datasource.value === panel.datasource),
|
||||
isLoadingHelp: false,
|
||||
helpContent: null,
|
||||
isPickerOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,7 +96,7 @@ export class QueriesTab extends PureComponent<Props, State> {
|
||||
|
||||
onChangeDataSource = datasource => {
|
||||
const { panel } = this.props;
|
||||
const { currentDatasource } = this.state;
|
||||
const { currentDS } = this.state;
|
||||
|
||||
// switching to mixed
|
||||
if (datasource.meta.mixed) {
|
||||
@@ -112,13 +106,13 @@ export class QueriesTab extends PureComponent<Props, State> {
|
||||
target.datasource = config.defaultDatasource;
|
||||
}
|
||||
});
|
||||
} else if (currentDatasource) {
|
||||
} else if (currentDS) {
|
||||
// if switching from mixed
|
||||
if (currentDatasource.meta.mixed) {
|
||||
if (currentDS.meta.mixed) {
|
||||
for (const target of panel.targets) {
|
||||
delete target.datasource;
|
||||
}
|
||||
} else if (currentDatasource.meta.id !== datasource.meta.id) {
|
||||
} else if (currentDS.meta.id !== datasource.meta.id) {
|
||||
// we are changing data source type, clear queries
|
||||
panel.targets = [{ refId: 'A' }];
|
||||
}
|
||||
@@ -127,128 +121,118 @@ export class QueriesTab extends PureComponent<Props, State> {
|
||||
panel.datasource = datasource.value;
|
||||
panel.refresh();
|
||||
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
currentDatasource: datasource,
|
||||
}));
|
||||
this.setState({
|
||||
currentDS: datasource,
|
||||
});
|
||||
};
|
||||
|
||||
loadHelp = () => {
|
||||
const { currentDatasource } = this.state;
|
||||
const hasHelp = currentDatasource.meta.hasQueryHelp;
|
||||
// loadHelp = () => {
|
||||
// const { currentDatasource } = this.state;
|
||||
// const hasHelp = currentDatasource.meta.hasQueryHelp;
|
||||
//
|
||||
// if (hasHelp) {
|
||||
// this.setState({
|
||||
// helpContent: <h2>Loading help...</h2>,
|
||||
// isLoadingHelp: true
|
||||
// });
|
||||
//
|
||||
// this.backendSrv
|
||||
// .get(`/api/plugins/${currentDatasource.meta.id}/markdown/query_help`)
|
||||
// .then(res => {
|
||||
// const md = new Remarkable();
|
||||
// const helpHtml = md.render(res); // TODO: Clean out dangerous code? Previous: this.helpHtml = this.$sce.trustAsHtml(md.render(res));
|
||||
// this.setState({
|
||||
// helpContent: <div className="markdown-html" dangerouslySetInnerHTML={{ __html: helpHtml }} />,
|
||||
// isLoadingHelp: false
|
||||
// });
|
||||
// })
|
||||
// .catch(() => {
|
||||
// this.setState({
|
||||
// helpContent: 'Error occured when loading help',
|
||||
// isLoadingHelp: false,
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
if (hasHelp) {
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
help: {
|
||||
helpHtml: <h2>Loading help...</h2>,
|
||||
isLoading: true,
|
||||
},
|
||||
}));
|
||||
|
||||
this.backendSrv
|
||||
.get(`/api/plugins/${currentDatasource.meta.id}/markdown/query_help`)
|
||||
.then(res => {
|
||||
const md = new Remarkable();
|
||||
const helpHtml = md.render(res); // TODO: Clean out dangerous code? Previous: this.helpHtml = this.$sce.trustAsHtml(md.render(res));
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
help: {
|
||||
helpHtml: <div className="markdown-html" dangerouslySetInnerHTML={{ __html: helpHtml }} />,
|
||||
isLoading: false,
|
||||
},
|
||||
}));
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
help: {
|
||||
helpHtml: 'Error occured when loading help',
|
||||
isLoading: false,
|
||||
},
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderOptions = close => {
|
||||
const { currentDatasource } = this.state;
|
||||
const { queryOptions } = currentDatasource.meta;
|
||||
const { panel } = this.props;
|
||||
|
||||
const onChangeFn = (panelKey: string) => {
|
||||
return (value: string | number) => {
|
||||
panel[panelKey] = value;
|
||||
panel.refresh();
|
||||
};
|
||||
};
|
||||
|
||||
const allOptions = {
|
||||
cacheTimeout: {
|
||||
label: 'Cache timeout',
|
||||
placeholder: '60',
|
||||
name: 'cacheTimeout',
|
||||
value: panel.cacheTimeout,
|
||||
tooltipInfo: (
|
||||
<>
|
||||
If your time series store has a query cache this option can override the default cache timeout. Specify a
|
||||
numeric value in seconds.
|
||||
</>
|
||||
),
|
||||
},
|
||||
maxDataPoints: {
|
||||
label: 'Max data points',
|
||||
placeholder: 'auto',
|
||||
name: 'maxDataPoints',
|
||||
value: panel.maxDataPoints,
|
||||
tooltipInfo: (
|
||||
<>
|
||||
The maximum data points the query should return. For graphs this is automatically set to one data point per
|
||||
pixel.
|
||||
</>
|
||||
),
|
||||
},
|
||||
minInterval: {
|
||||
label: 'Min time interval',
|
||||
placeholder: '0',
|
||||
name: 'minInterval',
|
||||
value: panel.interval,
|
||||
panelKey: 'interval',
|
||||
tooltipInfo: (
|
||||
<>
|
||||
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
|
||||
<code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
|
||||
<code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
|
||||
be used in math expressions.
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
const dsOptions = queryOptions
|
||||
? Object.keys(queryOptions).map(key => {
|
||||
const options = allOptions[key];
|
||||
return <DataSourceOption key={key} {...options} onChange={onChangeFn(allOptions[key].panelKey || key)} />;
|
||||
})
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TimeRangeOptions panel={this.props.panel} />
|
||||
{dsOptions}
|
||||
</>
|
||||
);
|
||||
};
|
||||
// renderOptions = close => {
|
||||
// const { currentDatasource } = this.state;
|
||||
// const { queryOptions } = currentDatasource.meta;
|
||||
// const { panel } = this.props;
|
||||
//
|
||||
// const onChangeFn = (panelKey: string) => {
|
||||
// return (value: string | number) => {
|
||||
// panel[panelKey] = value;
|
||||
// panel.refresh();
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// const allOptions = {
|
||||
// cacheTimeout: {
|
||||
// label: 'Cache timeout',
|
||||
// placeholder: '60',
|
||||
// name: 'cacheTimeout',
|
||||
// value: panel.cacheTimeout,
|
||||
// tooltipInfo: (
|
||||
// <>
|
||||
// If your time series store has a query cache this option can override the default cache timeout. Specify a
|
||||
// numeric value in seconds.
|
||||
// </>
|
||||
// ),
|
||||
// },
|
||||
// maxDataPoints: {
|
||||
// label: 'Max data points',
|
||||
// placeholder: 'auto',
|
||||
// name: 'maxDataPoints',
|
||||
// value: panel.maxDataPoints,
|
||||
// tooltipInfo: (
|
||||
// <>
|
||||
// The maximum data points the query should return. For graphs this is automatically set to one data point per
|
||||
// pixel.
|
||||
// </>
|
||||
// ),
|
||||
// },
|
||||
// minInterval: {
|
||||
// label: 'Min time interval',
|
||||
// placeholder: '0',
|
||||
// name: 'minInterval',
|
||||
// value: panel.interval,
|
||||
// panelKey: 'interval',
|
||||
// tooltipInfo: (
|
||||
// <>
|
||||
// A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
|
||||
// <code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
|
||||
// <code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
|
||||
// be used in math expressions.
|
||||
// </>
|
||||
// ),
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// const dsOptions = queryOptions
|
||||
// ? Object.keys(queryOptions).map(key => {
|
||||
// const options = allOptions[key];
|
||||
// return <DataSourceOption key={key} {...options} onChange={onChangeFn(allOptions[key].panelKey || key)} />;
|
||||
// })
|
||||
// : null;
|
||||
//
|
||||
// return (
|
||||
// <>
|
||||
// <TimeRangeOptions panel={this.props.panel} />
|
||||
// {dsOptions}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
renderQueryInspector = () => {
|
||||
const { panel } = this.props;
|
||||
return <QueryInspector panel={panel} LoadingPlaceholder={LoadingPlaceholder} />;
|
||||
};
|
||||
|
||||
renderHelp = () => {
|
||||
const { helpHtml, isLoading } = this.state.help;
|
||||
return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml;
|
||||
};
|
||||
// renderHelp = () => {
|
||||
// const { helpHtml, isLoading } = this.state.help;
|
||||
// return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml;
|
||||
// };
|
||||
|
||||
onAddQuery = (query?: DataQuery) => {
|
||||
this.props.panel.addQuery(query);
|
||||
@@ -280,47 +264,57 @@ export class QueriesTab extends PureComponent<Props, State> {
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { panel } = this.props;
|
||||
const { currentDatasource } = this.state;
|
||||
const { hasQueryHelp } = currentDatasource.meta;
|
||||
|
||||
const dsInformation = {
|
||||
title: currentDatasource.name,
|
||||
imgSrc: currentDatasource.meta.info.logos.small,
|
||||
render: closeOpenView => (
|
||||
<DataSourcePicker
|
||||
datasources={this.datasources}
|
||||
onChangeDataSource={ds => {
|
||||
closeOpenView();
|
||||
this.onChangeDataSource(ds);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const queryInspector = {
|
||||
title: 'Query Inspector',
|
||||
render: this.renderQueryInspector,
|
||||
};
|
||||
|
||||
const dsHelp = {
|
||||
title: '',
|
||||
icon: 'fa fa-question',
|
||||
disabled: !hasQueryHelp,
|
||||
onClick: this.loadHelp,
|
||||
render: this.renderHelp,
|
||||
};
|
||||
|
||||
const options = {
|
||||
title: 'Time Range',
|
||||
icon: '',
|
||||
disabled: false,
|
||||
render: this.renderOptions,
|
||||
};
|
||||
renderToolbar = () => {
|
||||
const { currentDS } = this.state;
|
||||
|
||||
return (
|
||||
<EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
|
||||
<DataSourcePicker
|
||||
datasources={this.datasources}
|
||||
onChangeDataSource={this.onChangeDataSource}
|
||||
current={currentDS}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { panel } = this.props;
|
||||
|
||||
// const dsInformation = {
|
||||
// title: currentDatasource.name,
|
||||
// imgSrc: currentDatasource.meta.info.logos.small,
|
||||
// render: closeOpenView => (
|
||||
// <DataSourcePicker
|
||||
// datasources={this.datasources}
|
||||
// onChangeDataSource={ds => {
|
||||
// closeOpenView();
|
||||
// this.onChangeDataSource(ds);
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
// };
|
||||
//
|
||||
// const queryInspector = {
|
||||
// title: 'Query Inspector',
|
||||
// render: this.renderQueryInspector,
|
||||
// };
|
||||
//
|
||||
// const dsHelp = {
|
||||
// title: '',
|
||||
// icon: 'fa fa-question',
|
||||
// disabled: !hasQueryHelp,
|
||||
// onClick: this.loadHelp,
|
||||
// render: this.renderHelp,
|
||||
// };
|
||||
//
|
||||
// const options = {
|
||||
// title: 'Time Range',
|
||||
// icon: '',
|
||||
// disabled: false,
|
||||
// render: this.renderOptions,
|
||||
// };
|
||||
|
||||
return (
|
||||
<EditorTabBody heading="Queries" renderToolbar={this.renderToolbar}>
|
||||
<div className="query-editor-rows gf-form-group">
|
||||
<div ref={element => (this.element = element)} />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user