The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/features/dashboard/dashgrid/VisualizationTab.tsx

212 lines
5.4 KiB

// Libraries
import React, { PureComponent } from 'react';
// Utils & Services
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
// Components
import { EditorTabBody } from './EditorTabBody';
import { VizTypePicker } from './VizTypePicker';
import { FadeIn } from 'app/core/components/Animations/FadeIn';
// Types
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { PanelPlugin } from 'app/types/plugins';
interface Props {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPlugin) => void;
}
7 years ago
interface State {
isVizPickerOpen: boolean;
searchQuery: string;
}
export class VisualizationTab extends PureComponent<Props, State> {
element: HTMLElement;
angularOptions: AngularComponent;
7 years ago
searchInput: HTMLElement;
constructor(props) {
super(props);
this.state = {
isVizPickerOpen: false,
7 years ago
searchQuery: '',
7 years ago
};
}
7 years ago
getPanelDefaultOptions = () => {
const { panel, plugin } = this.props;
if (plugin.exports.PanelDefaults) {
return panel.getOptions(plugin.exports.PanelDefaults.options);
}
return panel.getOptions(plugin.exports.PanelDefaults);
7 years ago
};
renderPanelOptions() {
const { plugin, angularPanel } = this.props;
const { PanelOptions } = plugin.exports;
if (angularPanel) {
return <div ref={element => (this.element = element)} />;
}
if (PanelOptions) {
return <PanelOptions options={this.getPanelDefaultOptions()} onChange={this.onPanelOptionsChanged} />;
} else {
return <p>Visualization has no options</p>;
}
}
componentDidMount() {
if (this.shouldLoadAngularOptions()) {
this.loadAngularOptions();
}
}
componentDidUpdate(prevProps: Props) {
if (this.props.plugin !== prevProps.plugin) {
this.cleanUpAngularOptions();
}
if (this.shouldLoadAngularOptions()) {
this.loadAngularOptions();
}
}
shouldLoadAngularOptions() {
return this.props.angularPanel && this.element && !this.angularOptions;
}
loadAngularOptions() {
const { angularPanel } = this.props;
const scope = angularPanel.getScope();
// When full page reloading in edit mode the angular panel has on fully compiled & instantiated yet
if (!scope.$$childHead) {
setTimeout(() => {
this.forceUpdate();
});
return;
}
const panelCtrl = scope.$$childHead.ctrl;
let template = '';
for (let i = 0; i < panelCtrl.editorTabs.length; i++) {
template +=
`
<div class="form-section" ng-cloak>` +
7 years ago
(i > 0 ? `<div class="form-section__header">{{ctrl.editorTabs[${i}].title}}</div>` : '') +
`<div class="form-section__body">
<panel-editor-tab editor-tab="ctrl.editorTabs[${i}]" ctrl="ctrl"></panel-editor-tab>
</div>
</div>
`;
}
const loader = getAngularLoader();
const scopeProps = { ctrl: panelCtrl };
this.angularOptions = loader.load(this.element, scopeProps, template);
}
componentWillUnmount() {
this.cleanUpAngularOptions();
}
cleanUpAngularOptions() {
if (this.angularOptions) {
this.angularOptions.destroy();
this.angularOptions = null;
}
}
onPanelOptionsChanged = (options: any) => {
this.props.panel.updateOptions(options);
this.forceUpdate();
};
7 years ago
onOpenVizPicker = () => {
this.setState({ isVizPickerOpen: true });
};
onCloseVizPicker = () => {
this.setState({ isVizPickerOpen: false });
};
7 years ago
onSearchQueryChange = evt => {
const value = evt.target.value;
this.setState({
searchQuery: value,
});
};
renderToolbar = (): JSX.Element => {
const { plugin } = this.props;
7 years ago
const { searchQuery } = this.state;
7 years ago
// if (this.state.isVizPickerOpen) {
// return (
// <>
// <label className="gf-form--has-input-icon">
// <input
// type="text"
// className="gf-form-input width-13"
// placeholder=""
// onChange={this.onSearchQueryChange}
// value={searchQuery}
// ref={elem => elem && elem.focus()}
// />
// <i className="gf-form-input-icon fa fa-search" />
// </label>
// </>
// );
// } else {
7 years ago
return (
<div className="toolbar__main" onClick={this.onOpenVizPicker}>
<img className="toolbar__main-image" src={plugin.info.logos.small} />
<div className="toolbar__main-name">{plugin.name}</div>
<i className="fa fa-caret-down" />
</div>
);
7 years ago
// }
7 years ago
};
7 years ago
onTypeChanged = (plugin: PanelPlugin) => {
// this.setState({ isVizPickerOpen: false });
this.props.onTypeChanged(plugin);
};
7 years ago
render() {
7 years ago
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery } = this.state;
7 years ago
const panelHelp = {
title: '',
icon: 'fa fa-question',
render: () => <h2>Help</h2>,
};
return (
7 years ago
<EditorTabBody heading="Visualization" renderToolbar={this.renderToolbar} toolbarItems={[panelHelp]}>
7 years ago
<>
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true}>
7 years ago
<VizTypePicker current={plugin} onTypeChanged={this.onTypeChanged} searchQuery={searchQuery} onClose={this.onCloseVizPicker} />
</FadeIn>
7 years ago
{this.renderPanelOptions()}
</>
</EditorTabBody>
);
}
}