Files
grafana/public/app/features/dashboard/panel_editor/VisualizationTab.tsx

259 lines
6.7 KiB
TypeScript
Raw Normal View History

// Libraries
import React, { PureComponent } from 'react';
2019-01-04 12:38:50 +01:00
// Utils & Services
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
2019-02-05 17:15:21 +01:00
import { connectWithStore } from 'app/core/utils/connectWithReduxStore';
import { StoreState } from 'app/types';
import { updateLocation } from 'app/core/actions';
2019-01-04 12:38:50 +01:00
// Components
2019-01-04 12:38:50 +01:00
import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
import { VizTypePicker } from './VizTypePicker';
2018-12-19 10:53:14 +01:00
import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp';
2018-12-12 13:59:19 +01:00
import { FadeIn } from 'app/core/components/Animations/FadeIn';
2019-01-04 12:38:50 +01:00
// Types
import { PanelModel } from '../state';
import { DashboardModel } from '../state';
import { VizPickerSearch } from './VizPickerSearch';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
import { PanelPlugin, PanelPluginMeta } from '@grafana/ui';
interface Props {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPlugin;
angularPanel?: AngularComponent;
onTypeChanged: (newType: PanelPluginMeta) => void;
2019-02-05 17:15:21 +01:00
updateLocation: typeof updateLocation;
urlOpenVizPicker: boolean;
}
2018-12-12 09:51:17 +01:00
interface State {
isVizPickerOpen: boolean;
searchQuery: string;
scrollTop: number;
hasBeenFocused: boolean;
2018-12-12 09:51:17 +01:00
}
export class VisualizationTab extends PureComponent<Props, State> {
element: HTMLElement;
angularOptions: AngularComponent;
2018-12-12 09:51:17 +01:00
constructor(props: Props) {
2018-12-12 09:51:17 +01:00
super(props);
this.state = {
2019-02-05 17:15:21 +01:00
isVizPickerOpen: this.props.urlOpenVizPicker,
hasBeenFocused: false,
2018-12-12 11:24:18 +01:00
searchQuery: '',
scrollTop: 0,
2018-12-12 09:51:17 +01:00
};
}
getReactPanelOptions = () => {
const { panel } = this.props;
return panel.getOptions();
2018-12-03 15:02:41 +01:00
};
2018-12-03 13:33:17 +01:00
renderPanelOptions() {
const { plugin, angularPanel } = this.props;
if (angularPanel) {
return <div ref={element => (this.element = element)} />;
}
if (plugin.editor) {
return <plugin.editor options={this.getReactPanelOptions()} onOptionsChange={this.onPanelOptionsChanged} />;
}
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;
panelCtrl.initEditMode();
let template = '';
for (let i = 0; i < panelCtrl.editorTabs.length; i++) {
2018-12-11 10:33:09 +01:00
template +=
`
2019-01-13 12:42:21 +01:00
<div class="panel-options-group" ng-cloak>` +
(i > 0
? `<div class="panel-options-group__header">
<span class="panel-options-group__title">{{ctrl.editorTabs[${i}].title}}
</span>
</div>`
: '') +
2019-01-13 12:42:21 +01:00
`<div class="panel-options-group__body">
2018-11-20 11:06:36 +01:00
<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;
}
}
clearQuery = () => {
this.setState({ searchQuery: '' });
};
onPanelOptionsChanged = (options: any) => {
this.props.panel.updateOptions(options);
this.forceUpdate();
};
2018-12-12 09:51:17 +01:00
onOpenVizPicker = () => {
this.setState({ isVizPickerOpen: true, scrollTop: 0 });
2018-12-12 09:51:17 +01:00
};
2018-12-12 13:59:19 +01:00
onCloseVizPicker = () => {
2019-02-05 17:15:21 +01:00
if (this.props.urlOpenVizPicker) {
this.props.updateLocation({ query: { openVizPicker: null }, partial: true });
}
this.setState({ isVizPickerOpen: false, hasBeenFocused: false });
2018-12-12 13:59:19 +01:00
};
onSearchQueryChange = (value: string) => {
2018-12-12 11:24:18 +01:00
this.setState({
searchQuery: value,
});
};
renderToolbar = (): JSX.Element => {
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery } = this.state;
const { meta } = plugin;
if (isVizPickerOpen) {
2018-12-12 14:44:40 +01:00
return (
<VizPickerSearch
plugin={meta}
searchQuery={searchQuery}
onChange={this.onSearchQueryChange}
onClose={this.onCloseVizPicker}
/>
2018-12-12 14:44:40 +01:00
);
} else {
2018-12-12 09:51:17 +01:00
return (
<>
<div className="toolbar__main" onClick={this.onOpenVizPicker}>
<img className="toolbar__main-image" src={meta.info.logos.small} />
<div className="toolbar__main-name">{meta.name}</div>
<i className="fa fa-caret-down" />
</div>
<PluginStateinfo state={meta.state} />
</>
2018-12-12 09:51:17 +01:00
);
2018-12-12 14:44:40 +01:00
}
2018-12-12 09:51:17 +01:00
};
onTypeChanged = (plugin: PanelPluginMeta) => {
if (plugin.id === this.props.plugin.meta.id) {
2018-12-13 07:44:58 +01:00
this.setState({ isVizPickerOpen: false });
} else {
this.props.onTypeChanged(plugin);
}
2018-12-12 11:24:18 +01:00
};
renderHelp = () => <PluginHelp plugin={this.props.plugin.meta} type="help" />;
2018-12-18 14:40:54 +01:00
setScrollTop = (event: React.MouseEvent<HTMLElement>) => {
const target = event.target as HTMLElement;
this.setState({ scrollTop: target.scrollTop });
};
2018-12-12 09:51:17 +01:00
render() {
2018-12-12 11:24:18 +01:00
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery, scrollTop } = this.state;
const { meta } = plugin;
2018-11-22 11:41:33 +01:00
2018-12-21 11:57:21 +01:00
const pluginHelp: EditorToolbarView = {
2018-12-18 14:40:54 +01:00
heading: 'Help',
icon: 'fa fa-question',
render: this.renderHelp,
};
return (
<EditorTabBody
heading="Visualization"
renderToolbar={this.renderToolbar}
toolbarItems={[pluginHelp]}
scrollTop={scrollTop}
setScrollTop={this.setScrollTop}
>
2018-12-12 12:51:39 +01:00
<>
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}>
2018-12-12 14:44:40 +01:00
<VizTypePicker
current={meta}
2018-12-12 14:44:40 +01:00
onTypeChanged={this.onTypeChanged}
searchQuery={searchQuery}
onClose={this.onCloseVizPicker}
/>
2018-12-12 13:59:19 +01:00
</FadeIn>
2018-12-12 12:51:39 +01:00
{this.renderPanelOptions()}
</>
</EditorTabBody>
);
}
}
2019-02-05 17:15:21 +01:00
const mapStateToProps = (state: StoreState) => ({
urlOpenVizPicker: !!state.location.query.openVizPicker,
2019-02-05 17:15:21 +01:00
});
const mapDispatchToProps = {
updateLocation,
2019-02-05 17:15:21 +01:00
};
export default connectWithStore(VisualizationTab, mapStateToProps, mapDispatchToProps);