InspectJSONTab.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import React, { PureComponent } from 'react';
  2. import AutoSizer from 'react-virtualized-auto-sizer';
  3. import { AppEvents, DataFrameJSON, dataFrameToJSON, DataTopic, PanelData, SelectableValue } from '@grafana/data';
  4. import { selectors } from '@grafana/e2e-selectors';
  5. import { Button, CodeEditor, Field, Select } from '@grafana/ui';
  6. import { appEvents } from 'app/core/core';
  7. import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
  8. import { getPanelInspectorStyles } from '../inspector/styles';
  9. enum ShowContent {
  10. PanelJSON = 'panel',
  11. PanelData = 'data',
  12. DataFrames = 'frames',
  13. }
  14. const options: Array<SelectableValue<ShowContent>> = [
  15. {
  16. label: 'Panel JSON',
  17. description: 'The model saved in the dashboard JSON that configures how everything works.',
  18. value: ShowContent.PanelJSON,
  19. },
  20. {
  21. label: 'Panel data',
  22. description: 'The raw model passed to the panel visualization',
  23. value: ShowContent.PanelData,
  24. },
  25. {
  26. label: 'DataFrame JSON',
  27. description: 'JSON formatted DataFrames',
  28. value: ShowContent.DataFrames,
  29. },
  30. ];
  31. interface Props {
  32. onClose: () => void;
  33. dashboard?: DashboardModel;
  34. panel?: PanelModel;
  35. data?: PanelData;
  36. }
  37. interface State {
  38. show: ShowContent;
  39. text: string;
  40. }
  41. export class InspectJSONTab extends PureComponent<Props, State> {
  42. hasPanelJSON: boolean;
  43. constructor(props: Props) {
  44. super(props);
  45. this.hasPanelJSON = !!(props.panel && props.dashboard);
  46. // If we are in panel, we want to show PanelJSON, otherwise show DataFrames
  47. this.state = {
  48. show: this.hasPanelJSON ? ShowContent.PanelJSON : ShowContent.DataFrames,
  49. text: this.hasPanelJSON ? getPrettyJSON(props.panel!.getSaveModel()) : getPrettyJSON(props.data),
  50. };
  51. }
  52. onSelectChanged = (item: SelectableValue<ShowContent>) => {
  53. const show = this.getJSONObject(item.value!);
  54. const text = getPrettyJSON(show);
  55. this.setState({ text, show: item.value! });
  56. };
  57. // Called onBlur
  58. onTextChanged = (text: string) => {
  59. this.setState({ text });
  60. };
  61. getJSONObject(show: ShowContent) {
  62. const { data, panel } = this.props;
  63. if (show === ShowContent.PanelData) {
  64. return data;
  65. }
  66. if (show === ShowContent.DataFrames) {
  67. return getPanelDataFrames(data);
  68. }
  69. if (this.hasPanelJSON && show === ShowContent.PanelJSON) {
  70. return panel!.getSaveModel();
  71. }
  72. return { note: `Unknown Object: ${show}` };
  73. }
  74. onApplyPanelModel = () => {
  75. const { panel, dashboard, onClose } = this.props;
  76. if (this.hasPanelJSON) {
  77. try {
  78. if (!dashboard!.meta.canEdit) {
  79. appEvents.emit(AppEvents.alertError, ['Unable to apply']);
  80. } else {
  81. const updates = JSON.parse(this.state.text);
  82. dashboard!.shouldUpdateDashboardPanelFromJSON(updates, panel!);
  83. panel!.restoreModel(updates);
  84. panel!.refresh();
  85. appEvents.emit(AppEvents.alertSuccess, ['Panel model updated']);
  86. }
  87. } catch (err) {
  88. console.error('Error applying updates', err);
  89. appEvents.emit(AppEvents.alertError, ['Invalid JSON text']);
  90. }
  91. onClose();
  92. }
  93. };
  94. render() {
  95. const { dashboard } = this.props;
  96. const { show, text } = this.state;
  97. const jsonOptions = this.hasPanelJSON ? options : options.slice(1, options.length);
  98. const selected = options.find((v) => v.value === show);
  99. const isPanelJSON = show === ShowContent.PanelJSON;
  100. const canEdit = dashboard && dashboard.meta.canEdit;
  101. const styles = getPanelInspectorStyles();
  102. return (
  103. <div className={styles.wrap}>
  104. <div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}>
  105. <Field label="Select source" className="flex-grow-1">
  106. <Select
  107. inputId="select-source-dropdown"
  108. options={jsonOptions}
  109. value={selected}
  110. onChange={this.onSelectChanged}
  111. />
  112. </Field>
  113. {this.hasPanelJSON && isPanelJSON && canEdit && (
  114. <Button className={styles.toolbarItem} onClick={this.onApplyPanelModel}>
  115. Apply
  116. </Button>
  117. )}
  118. </div>
  119. <div className={styles.content}>
  120. <AutoSizer disableWidth>
  121. {({ height }) => (
  122. <CodeEditor
  123. width="100%"
  124. height={height}
  125. language="json"
  126. showLineNumbers={true}
  127. showMiniMap={(text && text.length) > 100}
  128. value={text || ''}
  129. readOnly={!isPanelJSON}
  130. onBlur={this.onTextChanged}
  131. />
  132. )}
  133. </AutoSizer>
  134. </div>
  135. </div>
  136. );
  137. }
  138. }
  139. function getPanelDataFrames(data?: PanelData): DataFrameJSON[] {
  140. const frames: DataFrameJSON[] = [];
  141. if (data?.series) {
  142. for (const f of data.series) {
  143. frames.push(dataFrameToJSON(f));
  144. }
  145. }
  146. if (data?.annotations) {
  147. for (const f of data.annotations) {
  148. const json = dataFrameToJSON(f);
  149. if (!json.schema?.meta) {
  150. json.schema!.meta = {};
  151. }
  152. json.schema!.meta.dataTopic = DataTopic.Annotations;
  153. frames.push(json);
  154. }
  155. }
  156. return frames;
  157. }
  158. function getPrettyJSON(obj: any): string {
  159. return JSON.stringify(obj, null, 2);
  160. }