1import React, { PureComponent } from 'react'; 2import { cloneDeep } from 'lodash'; 3import { 4 DataQuery, 5 DataSourceApi, 6 DataSourceJsonData, 7 DataSourcePlugin, 8 DataSourcePluginMeta, 9 DataSourceSettings, 10} from '@grafana/data'; 11import { AngularComponent, getAngularLoader } from '@grafana/runtime'; 12 13export type GenericDataSourcePlugin = DataSourcePlugin<DataSourceApi<DataQuery, DataSourceJsonData>>; 14 15export interface Props { 16 plugin: GenericDataSourcePlugin; 17 dataSource: DataSourceSettings; 18 dataSourceMeta: DataSourcePluginMeta; 19 onModelChange: (dataSource: DataSourceSettings) => void; 20} 21 22export class PluginSettings extends PureComponent<Props> { 23 element: HTMLDivElement | null = null; 24 component?: AngularComponent; 25 scopeProps: { 26 ctrl: { datasourceMeta: DataSourcePluginMeta; current: DataSourceSettings }; 27 onModelChanged: (dataSource: DataSourceSettings) => void; 28 }; 29 30 constructor(props: Props) { 31 super(props); 32 33 this.scopeProps = { 34 ctrl: { datasourceMeta: props.dataSourceMeta, current: cloneDeep(props.dataSource) }, 35 onModelChanged: this.onModelChanged, 36 }; 37 this.onModelChanged = this.onModelChanged.bind(this); 38 } 39 40 componentDidMount() { 41 const { plugin } = this.props; 42 43 if (!this.element) { 44 return; 45 } 46 47 if (!plugin.components.ConfigEditor) { 48 // React editor is not specified, let's render angular editor 49 // How to approach this better? Introduce ReactDataSourcePlugin interface and typeguard it here? 50 const loader = getAngularLoader(); 51 const template = '<plugin-component type="datasource-config-ctrl" />'; 52 53 this.component = loader.load(this.element, this.scopeProps, template); 54 } 55 } 56 57 componentDidUpdate(prevProps: Props) { 58 const { plugin } = this.props; 59 if (!plugin.components.ConfigEditor && this.props.dataSource !== prevProps.dataSource) { 60 this.scopeProps.ctrl.current = cloneDeep(this.props.dataSource); 61 62 this.component?.digest(); 63 } 64 } 65 66 componentWillUnmount() { 67 if (this.component) { 68 this.component.destroy(); 69 } 70 } 71 72 onModelChanged = (dataSource: DataSourceSettings) => { 73 this.props.onModelChange(dataSource); 74 }; 75 76 render() { 77 const { plugin, dataSource } = this.props; 78 79 if (!plugin) { 80 return null; 81 } 82 83 return ( 84 <div ref={(element) => (this.element = element)}> 85 {plugin.components.ConfigEditor && 86 React.createElement(plugin.components.ConfigEditor, { 87 options: dataSource, 88 onOptionsChange: this.onModelChanged, 89 })} 90 </div> 91 ); 92 } 93} 94 95export default PluginSettings; 96