DashboardPanel.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import React from 'react';
  2. import config from 'app/core/config';
  3. import { PanelModel } from '../panel_model';
  4. import { DashboardModel } from '../dashboard_model';
  5. import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader';
  6. import { DashboardRow } from './DashboardRow';
  7. import { AddPanelPanel } from './AddPanelPanel';
  8. import { importPluginModule } from 'app/features/plugins/plugin_loader';
  9. import { PluginExports, PanelPlugin } from 'app/types/plugins';
  10. import { PanelChrome } from './PanelChrome';
  11. import { PanelEditor } from './PanelEditor';
  12. export interface Props {
  13. panelType: string;
  14. panel: PanelModel;
  15. dashboard: DashboardModel;
  16. }
  17. export interface State {
  18. pluginExports: PluginExports;
  19. }
  20. export class DashboardPanel extends React.Component<Props, State> {
  21. element: any;
  22. angularPanel: AngularComponent;
  23. pluginInfo: any;
  24. specialPanels = {};
  25. constructor(props) {
  26. super(props);
  27. this.state = {
  28. pluginExports: null,
  29. };
  30. this.specialPanels['row'] = this.renderRow.bind(this);
  31. this.specialPanels['add-panel'] = this.renderAddPanel.bind(this);
  32. }
  33. isSpecial() {
  34. return this.specialPanels[this.props.panel.type];
  35. }
  36. renderRow() {
  37. return <DashboardRow panel={this.props.panel} dashboard={this.props.dashboard} />;
  38. }
  39. renderAddPanel() {
  40. return <AddPanelPanel panel={this.props.panel} dashboard={this.props.dashboard} />;
  41. }
  42. onPluginTypeChanged = (plugin: PanelPlugin) => {
  43. this.props.panel.changeType(plugin.id);
  44. this.loadPlugin();
  45. };
  46. onAngularPluginTypeChanged = () => {
  47. this.loadPlugin();
  48. };
  49. loadPlugin() {
  50. if (this.isSpecial()) {
  51. return;
  52. }
  53. // handle plugin loading & changing of plugin type
  54. if (!this.pluginInfo || this.pluginInfo.id !== this.props.panel.type) {
  55. this.pluginInfo = config.panels[this.props.panel.type];
  56. if (this.pluginInfo.exports) {
  57. this.cleanUpAngularPanel();
  58. this.setState({ pluginExports: this.pluginInfo.exports });
  59. } else {
  60. importPluginModule(this.pluginInfo.module).then(pluginExports => {
  61. this.cleanUpAngularPanel();
  62. // cache plugin exports (saves a promise async cycle next time)
  63. this.pluginInfo.exports = pluginExports;
  64. // update panel state
  65. this.setState({ pluginExports: pluginExports });
  66. });
  67. }
  68. }
  69. }
  70. componentDidMount() {
  71. this.loadPlugin();
  72. }
  73. componentDidUpdate() {
  74. this.loadPlugin();
  75. // handle angular plugin loading
  76. if (!this.element || this.angularPanel) {
  77. return;
  78. }
  79. const loader = getAngularLoader();
  80. const template = '<plugin-component type="panel" class="panel-height-helper"></plugin-component>';
  81. const scopeProps = { panel: this.props.panel, dashboard: this.props.dashboard };
  82. this.angularPanel = loader.load(this.element, scopeProps, template);
  83. }
  84. cleanUpAngularPanel() {
  85. if (this.angularPanel) {
  86. this.angularPanel.destroy();
  87. this.angularPanel = null;
  88. }
  89. }
  90. componentWillUnmount() {
  91. this.cleanUpAngularPanel();
  92. }
  93. renderReactPanel() {
  94. const { pluginExports } = this.state;
  95. const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
  96. const panelWrapperClass = this.props.panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
  97. // this might look strange with these classes that change when edit, but
  98. // I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel
  99. return (
  100. <div className={containerClass}>
  101. <div className={panelWrapperClass}>
  102. <PanelChrome
  103. component={pluginExports.PanelComponent}
  104. panel={this.props.panel}
  105. dashboard={this.props.dashboard}
  106. />
  107. </div>
  108. {this.props.panel.isEditing && (
  109. <div className="panel-editor-container__editor">
  110. <PanelEditor
  111. panel={this.props.panel}
  112. panelType={this.props.panel.type}
  113. dashboard={this.props.dashboard}
  114. onTypeChanged={this.onPluginTypeChanged}
  115. pluginExports={pluginExports}
  116. />
  117. </div>
  118. )}
  119. </div>
  120. );
  121. }
  122. render() {
  123. if (this.isSpecial()) {
  124. return this.specialPanels[this.props.panel.type]();
  125. }
  126. if (!this.state.pluginExports) {
  127. return null;
  128. }
  129. if (this.state.pluginExports.PanelComponent) {
  130. return this.renderReactPanel();
  131. }
  132. // legacy angular rendering
  133. return <div ref={element => (this.element = element)} className="panel-height-helper" />;
  134. }
  135. }