grafana/public/app/features/dashboard/panel_editor/VisualizationTab.tsx
Dominik Prokop bf7579d984
FieldOverrides: Move FieldConfigSource from fieldOptions to PanelModel.fieldConfig (#22600)
* Apply field overrides in PanelChrome

* Move applyFieldOverrides to panel query runner

* Review updates

* Make sure overrides are applied back on souce panel when exiting the new edit mode

* TS ignores in est

* Make field display work in viz repeater

* Review updates

* Review and test updates

* Change the way overrides and trransformations are retrieved in PQR

* Add fieldConfig property to PanelModel

* Dashboard migration v1

* Use field config when exiting new panel edit mode

* Gauge - use fieldConfig from panel model

* FieldDisplayOptions - don's extend FieldConfigSource

* Fix fieldDisplay ts

* StatPanel updated

* Stat panel defaults applied

* Table2 panel options  update

* React graph updates

* BarGauge updated

* PieChart, Gauge, BarGauge and Stat updates

* PieChart - remove field config defaults from options

* FieldDisplayEditor - remove unused methos

* PanelModel - remove debugger

* Remove fieldConfig from field options when migrating dashboard

* Update data links migrations

* Update fieldDisaplay tests to respect new fieldConfig

* Update dashboard schema version in snapshots

* Fix BarGaugePanel test

* Rebase fixes

* Add onFieldConfigChange to PanelProps type

* Update shared single stat migration

* Pass PanelModel instead of options only for panel type change handler [breaking]

* Renames

* Don't mutate panel options

* Migrations update

* Remove obsolete snap

* Minor updates after review

* Fix null checks

* Temporarily (until we decide to switch to new pane edit) bring back old aditors

* Temporarily rename ValueMappingEditor and MappingRow to Legacy*

* Migrations update

* Updae setFieldConfigDefaults API

* Update the way field config defaults are applied

* Use standard field config for gauge, bar gauge and stat panels

* refactoring

* Revert dashboard fieldOptions migrations as those are handled by single stat migrator

* Fix ts in tests

* Strict null fix and some minor fixes

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-03-19 11:50:31 +01:00

233 lines
6.2 KiB
TypeScript

// Libraries
import React, { PureComponent } from 'react';
// Utils & Services
import { connect } from 'react-redux';
import { StoreState } from 'app/types';
import { updateLocation } from 'app/core/actions';
// Components
import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
import { VizTypePicker } from './VizTypePicker';
import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp';
import { FadeIn } from 'app/core/components/Animations/FadeIn';
import { AngularPanelOptions } from './AngularPanelOptions';
// Types
import { PanelModel, DashboardModel } from '../state';
import { VizPickerSearch } from './VizPickerSearch';
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
import { Unsubscribable } from 'rxjs';
import {
PanelPlugin,
PanelPluginMeta,
PanelData,
LoadingState,
DefaultTimeRange,
FieldConfigSource,
} from '@grafana/data';
interface Props {
panel: PanelModel;
dashboard: DashboardModel;
plugin: PanelPlugin;
onPluginTypeChange: (newType: PanelPluginMeta) => void;
updateLocation: typeof updateLocation;
urlOpenVizPicker: boolean;
}
interface State {
isVizPickerOpen: boolean;
searchQuery: string;
scrollTop: number;
hasBeenFocused: boolean;
data: PanelData;
}
export class VisualizationTab extends PureComponent<Props, State> {
element: HTMLElement;
querySubscription: Unsubscribable;
constructor(props: Props) {
super(props);
this.state = {
isVizPickerOpen: this.props.urlOpenVizPicker,
hasBeenFocused: false,
searchQuery: '',
scrollTop: 0,
data: {
state: LoadingState.NotStarted,
series: [],
timeRange: DefaultTimeRange,
},
};
}
getReactPanelOptions = () => {
const { panel } = this.props;
return panel.getOptions();
};
getReactPanelFieldConfig = () => {
const { panel } = this.props;
return panel.getFieldConfig();
};
renderPanelOptions() {
const { plugin, dashboard, panel } = this.props;
if (plugin.angularPanelCtrl) {
return <AngularPanelOptions plugin={plugin} dashboard={dashboard} panel={panel} />;
}
if (plugin.editor) {
return (
<plugin.editor
data={this.state.data}
options={this.getReactPanelOptions()}
onOptionsChange={this.onPanelOptionsChanged}
// TODO[FieldConfig]: Remove when we switch old editor to new
fieldConfig={this.getReactPanelFieldConfig()}
// TODO[FieldConfig]: Remove when we switch old editor to new
onFieldConfigChange={this.onPanelFieldConfigChange}
/>
);
}
return <p>Visualization has no options</p>;
}
componentDidMount() {
const { panel } = this.props;
const queryRunner = panel.getQueryRunner();
this.querySubscription = queryRunner.getData().subscribe({
next: (data: PanelData) => this.setState({ data }),
});
}
componentWillUnmount() {
if (this.querySubscription) {
this.querySubscription.unsubscribe();
}
}
clearQuery = () => {
this.setState({ searchQuery: '' });
};
onPanelOptionsChanged = (options: any, callback?: () => void) => {
this.props.panel.updateOptions(options);
this.forceUpdate(callback);
};
// TODO[FieldConfig]: Remove when we switch old editor to new
onPanelFieldConfigChange = (config: FieldConfigSource, callback?: () => void) => {
this.props.panel.updateFieldConfig(config);
this.forceUpdate(callback);
};
onOpenVizPicker = () => {
this.setState({ isVizPickerOpen: true, scrollTop: 0 });
};
onCloseVizPicker = () => {
if (this.props.urlOpenVizPicker) {
this.props.updateLocation({ query: { openVizPicker: null }, partial: true });
}
this.setState({ isVizPickerOpen: false, hasBeenFocused: false });
};
onSearchQueryChange = (value: string) => {
this.setState({
searchQuery: value,
});
};
renderToolbar = (): JSX.Element => {
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery } = this.state;
const { meta } = plugin;
if (isVizPickerOpen) {
return (
<VizPickerSearch
plugin={meta}
searchQuery={searchQuery}
onChange={this.onSearchQueryChange}
onClose={this.onCloseVizPicker}
/>
);
} else {
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} />
</>
);
}
};
onPluginTypeChange = (plugin: PanelPluginMeta) => {
if (plugin.id === this.props.plugin.meta.id) {
this.setState({ isVizPickerOpen: false });
} else {
this.props.onPluginTypeChange(plugin);
}
};
renderHelp = () => <PluginHelp plugin={this.props.plugin.meta} type="help" />;
setScrollTop = (event: React.MouseEvent<HTMLElement>) => {
const target = event.target as HTMLElement;
this.setState({ scrollTop: target.scrollTop });
};
render() {
const { plugin } = this.props;
const { isVizPickerOpen, searchQuery, scrollTop } = this.state;
const { meta } = plugin;
const pluginHelp: EditorToolbarView = {
heading: 'Help',
icon: 'fa fa-question',
render: this.renderHelp,
};
return (
<EditorTabBody
heading="Visualization"
renderToolbar={this.renderToolbar}
toolbarItems={[pluginHelp]}
scrollTop={scrollTop}
setScrollTop={this.setScrollTop}
>
<>
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}>
<VizTypePicker
current={meta}
onTypeChange={this.onPluginTypeChange}
searchQuery={searchQuery}
onClose={this.onCloseVizPicker}
/>
</FadeIn>
{this.renderPanelOptions()}
</>
</EditorTabBody>
);
}
}
const mapStateToProps = (state: StoreState) => ({
urlOpenVizPicker: !!state.location.query.openVizPicker,
});
const mapDispatchToProps = {
updateLocation,
};
export default connect(mapStateToProps, mapDispatchToProps)(VisualizationTab);