AnnotationResultMapper.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import React, { PureComponent } from 'react';
  2. import {
  3. SelectableValue,
  4. getFieldDisplayName,
  5. AnnotationEvent,
  6. AnnotationEventMappings,
  7. AnnotationEventFieldMapping,
  8. formattedValueToString,
  9. AnnotationEventFieldSource,
  10. getValueFormat,
  11. } from '@grafana/data';
  12. import { Select, Tooltip, Icon } from '@grafana/ui';
  13. import { annotationEventNames, AnnotationFieldInfo } from '../standardAnnotationSupport';
  14. import { AnnotationQueryResponse } from '../types';
  15. // const valueOptions: Array<SelectableValue<AnnotationEventFieldSource>> = [
  16. // { value: AnnotationEventFieldSource.Field, label: 'Field', description: 'Set the field value from a response field' },
  17. // { value: AnnotationEventFieldSource.Text, label: 'Text', description: 'Enter direct text for the value' },
  18. // { value: AnnotationEventFieldSource.Skip, label: 'Skip', description: 'Hide this field' },
  19. // ];
  20. interface Props {
  21. response?: AnnotationQueryResponse;
  22. mappings?: AnnotationEventMappings;
  23. change: (mappings?: AnnotationEventMappings) => void;
  24. }
  25. interface State {
  26. fieldNames: Array<SelectableValue<string>>;
  27. }
  28. export class AnnotationFieldMapper extends PureComponent<Props, State> {
  29. constructor(props: Props) {
  30. super(props);
  31. this.state = {
  32. fieldNames: [],
  33. };
  34. }
  35. updateFields = () => {
  36. const frame = this.props.response?.panelData?.series[0];
  37. if (frame && frame.fields) {
  38. const fieldNames = frame.fields.map((f) => {
  39. const name = getFieldDisplayName(f, frame);
  40. let description = '';
  41. for (let i = 0; i < frame.length; i++) {
  42. if (i > 0) {
  43. description += ', ';
  44. }
  45. if (i > 2) {
  46. description += '...';
  47. break;
  48. }
  49. description += f.values.get(i);
  50. }
  51. if (description.length > 50) {
  52. description = description.substring(0, 50) + '...';
  53. }
  54. return {
  55. label: `${name} (${f.type})`,
  56. value: name,
  57. description,
  58. };
  59. });
  60. this.setState({ fieldNames });
  61. }
  62. };
  63. componentDidMount() {
  64. this.updateFields();
  65. }
  66. componentDidUpdate(oldProps: Props) {
  67. if (oldProps.response !== this.props.response) {
  68. this.updateFields();
  69. }
  70. }
  71. onFieldSourceChange = (k: keyof AnnotationEvent, v: SelectableValue<AnnotationEventFieldSource>) => {
  72. const mappings = this.props.mappings || {};
  73. const mapping = mappings[k] || {};
  74. this.props.change({
  75. ...mappings,
  76. [k]: {
  77. ...mapping,
  78. source: v.value || AnnotationEventFieldSource.Field,
  79. },
  80. });
  81. };
  82. onFieldNameChange = (k: keyof AnnotationEvent, v: SelectableValue<string>) => {
  83. const mappings = this.props.mappings || {};
  84. const mapping = mappings[k] || {};
  85. this.props.change({
  86. ...mappings,
  87. [k]: {
  88. ...mapping,
  89. value: v.value,
  90. source: AnnotationEventFieldSource.Field,
  91. },
  92. });
  93. };
  94. renderRow(row: AnnotationFieldInfo, mapping: AnnotationEventFieldMapping, first?: AnnotationEvent) {
  95. const { fieldNames } = this.state;
  96. let picker = fieldNames;
  97. const current = mapping.value;
  98. let currentValue = fieldNames.find((f) => current === f.value);
  99. if (current) {
  100. picker = [...fieldNames];
  101. if (!currentValue) {
  102. picker.push({
  103. label: current,
  104. value: current,
  105. });
  106. }
  107. }
  108. let value = first ? first[row.key] : '';
  109. if (value && row.key.startsWith('time')) {
  110. const fmt = getValueFormat('dateTimeAsIso');
  111. value = formattedValueToString(fmt(value as number));
  112. }
  113. if (value === null || value === undefined) {
  114. value = ''; // empty string
  115. }
  116. return (
  117. <tr key={row.key}>
  118. <td>
  119. {row.key}{' '}
  120. {row.help && (
  121. <Tooltip content={row.help}>
  122. <Icon name="info-circle" />
  123. </Tooltip>
  124. )}
  125. </td>
  126. {/* <td>
  127. <Select
  128. value={valueOptions.find(v => v.value === mapping.source) || valueOptions[0]}
  129. options={valueOptions}
  130. onChange={(v: SelectableValue<AnnotationEventFieldSource>) => {
  131. this.onFieldSourceChange(row.key, v);
  132. }}
  133. />
  134. </td> */}
  135. <td>
  136. <Select
  137. value={currentValue}
  138. options={picker}
  139. placeholder={row.placeholder || row.key}
  140. onChange={(v: SelectableValue<string>) => {
  141. this.onFieldNameChange(row.key, v);
  142. }}
  143. noOptionsMessage="Unknown field names"
  144. allowCustomValue={true}
  145. />
  146. </td>
  147. <td>{`${value}`}</td>
  148. </tr>
  149. );
  150. }
  151. render() {
  152. const first = this.props.response?.events?.[0];
  153. const mappings = this.props.mappings || {};
  154. return (
  155. <table className="filter-table">
  156. <thead>
  157. <tr>
  158. <th>Annotation</th>
  159. <th>From</th>
  160. <th>First Value</th>
  161. </tr>
  162. </thead>
  163. <tbody>
  164. {annotationEventNames.map((row) => {
  165. return this.renderRow(row, mappings[row.key] || {}, first);
  166. })}
  167. </tbody>
  168. </table>
  169. );
  170. }
  171. }