StateTimelinePanel.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import React, { useCallback, useMemo } from 'react';
  2. import { DataFrame, FieldType, PanelProps } from '@grafana/data';
  3. import { TooltipPlugin, useTheme2, ZoomPlugin, usePanelContext } from '@grafana/ui';
  4. import { getLastStreamingDataFramePacket } from 'app/features/live/data/StreamingDataFrame';
  5. import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin';
  6. import { StateTimelineTooltip } from './StateTimelineTooltip';
  7. import { TimelineChart } from './TimelineChart';
  8. import { TimelineMode, TimelineOptions } from './types';
  9. import { prepareTimelineFields, prepareTimelineLegendItems } from './utils';
  10. interface TimelinePanelProps extends PanelProps<TimelineOptions> {}
  11. /**
  12. * @alpha
  13. */
  14. export const StateTimelinePanel: React.FC<TimelinePanelProps> = ({
  15. data,
  16. timeRange,
  17. timeZone,
  18. options,
  19. width,
  20. height,
  21. onChangeTimeRange,
  22. }) => {
  23. const theme = useTheme2();
  24. const { sync } = usePanelContext();
  25. const { frames, warn } = useMemo(
  26. () => prepareTimelineFields(data?.series, options.mergeValues ?? true, timeRange, theme),
  27. [data, options.mergeValues, timeRange, theme]
  28. );
  29. const legendItems = useMemo(
  30. () => prepareTimelineLegendItems(frames, options.legend, theme),
  31. [frames, options.legend, theme]
  32. );
  33. const renderCustomTooltip = useCallback(
  34. (alignedData: DataFrame, seriesIdx: number | null, datapointIdx: number | null) => {
  35. const data = frames ?? [];
  36. // Count value fields in the state-timeline-ready frame
  37. const valueFieldsCount = data.reduce(
  38. (acc, frame) => acc + frame.fields.filter((field) => field.type !== FieldType.time).length,
  39. 0
  40. );
  41. // Not caring about multi mode in StateTimeline
  42. if (seriesIdx === null || datapointIdx === null) {
  43. return null;
  44. }
  45. /**
  46. * There could be a case when the tooltip shows a data from one of a multiple query and the other query finishes first
  47. * from refreshing. This causes data to be out of sync. alignedData - 1 because Time field doesn't count.
  48. * Render nothing in this case to prevent error.
  49. * See https://github.com/grafana/support-escalations/issues/932
  50. */
  51. if (
  52. (!alignedData.meta?.transformations?.length && alignedData.fields.length - 1 !== valueFieldsCount) ||
  53. !alignedData.fields[seriesIdx]
  54. ) {
  55. return null;
  56. }
  57. return (
  58. <StateTimelineTooltip
  59. data={data}
  60. alignedData={alignedData}
  61. seriesIdx={seriesIdx}
  62. datapointIdx={datapointIdx}
  63. timeZone={timeZone}
  64. />
  65. );
  66. },
  67. [timeZone, frames]
  68. );
  69. if (!frames || warn) {
  70. return (
  71. <div className="panel-empty">
  72. <p>{warn ?? 'No data found in response'}</p>
  73. </div>
  74. );
  75. }
  76. if (frames.length === 1) {
  77. const packet = getLastStreamingDataFramePacket(frames[0]);
  78. if (packet) {
  79. // console.log('STREAM Packet', packet);
  80. }
  81. }
  82. return (
  83. <TimelineChart
  84. theme={theme}
  85. frames={frames}
  86. structureRev={data.structureRev}
  87. timeRange={timeRange}
  88. timeZone={timeZone}
  89. width={width}
  90. height={height}
  91. legendItems={legendItems}
  92. {...options}
  93. mode={TimelineMode.Changes}
  94. >
  95. {(config, alignedFrame) => {
  96. return (
  97. <>
  98. <ZoomPlugin config={config} onZoom={onChangeTimeRange} />
  99. <TooltipPlugin
  100. data={alignedFrame}
  101. sync={sync}
  102. config={config}
  103. mode={options.tooltip.mode}
  104. timeZone={timeZone}
  105. renderTooltip={renderCustomTooltip}
  106. />
  107. <OutsideRangePlugin config={config} onChangeTimeRange={onChangeTimeRange} />
  108. </>
  109. );
  110. }}
  111. </TimelineChart>
  112. );
  113. };