Commit d9985271 by Torkel Ödegaard

wip

parent 45fde57e
......@@ -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;
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}
</>
);
};
// 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,
// });
// });
// }
// };
// 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);
}}
/>
),
};
renderToolbar = () => {
const { currentDS } = this.state;
const queryInspector = {
title: 'Query Inspector',
render: this.renderQueryInspector,
};
return (
<DataSourcePicker
datasources={this.datasources}
onChangeDataSource={this.onChangeDataSource}
current={currentDS}
/>
);
};
const dsHelp = {
title: '',
icon: 'fa fa-question',
disabled: !hasQueryHelp,
onClick: this.loadHelp,
render: this.renderHelp,
};
render() {
const { panel } = this.props;
const options = {
title: 'Time Range',
icon: '',
disabled: false,
render: this.renderOptions,
};
// 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" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
<EditorTabBody heading="Queries" renderToolbar={this.renderToolbar}>
<div className="query-editor-rows gf-form-group">
<div ref={element => (this.element = element)} />
......
......@@ -273,6 +273,19 @@
}
}
.ds-picker {
position: relative;
}
.ds-picker-menu {
min-width: 400px;
max-width: 500px;
position: absolute;
background: $panel-editor-toolbar-view-bg;
padding: 5px;
overflow: auto;
}
.ds-picker-list__name {
text-overflow: ellipsis;
overflow: hidden;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment