VariablesUnknownTable.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { css } from '@emotion/css';
  2. import React, { ReactElement, useEffect, useState } from 'react';
  3. import { useAsync } from 'react-use';
  4. import { GrafanaTheme } from '@grafana/data';
  5. import { reportInteraction } from '@grafana/runtime';
  6. import { CollapsableSection, HorizontalGroup, Icon, Spinner, Tooltip, useStyles, VerticalGroup } from '@grafana/ui';
  7. import { DashboardModel } from '../../dashboard/state';
  8. import { VariableModel } from '../types';
  9. import { VariablesUnknownButton } from './VariablesUnknownButton';
  10. import { getUnknownsNetwork, UsagesToNetwork } from './utils';
  11. export const SLOW_VARIABLES_EXPANSION_THRESHOLD = 1000;
  12. export interface VariablesUnknownTableProps {
  13. variables: VariableModel[];
  14. dashboard: DashboardModel | null;
  15. }
  16. export function VariablesUnknownTable({ variables, dashboard }: VariablesUnknownTableProps): ReactElement {
  17. const [open, setOpen] = useState(false);
  18. const [changed, setChanged] = useState(0);
  19. const [usages, setUsages] = useState<UsagesToNetwork[]>([]);
  20. const style = useStyles(getStyles);
  21. useEffect(() => setChanged((prevState) => prevState + 1), [variables, dashboard]);
  22. const { loading } = useAsync(async () => {
  23. if (open && changed > 0) {
  24. // make sure we only fetch when opened and variables or dashboard have changed
  25. const start = Date.now();
  26. const unknownsNetwork = await getUnknownsNetwork(variables, dashboard);
  27. const stop = Date.now();
  28. const elapsed = stop - start;
  29. if (elapsed >= SLOW_VARIABLES_EXPANSION_THRESHOLD) {
  30. reportInteraction('Slow unknown variables expansion', { elapsed });
  31. }
  32. setChanged(0);
  33. setUsages(unknownsNetwork);
  34. return unknownsNetwork;
  35. }
  36. return [];
  37. }, [variables, dashboard, open, changed]);
  38. const onToggle = (isOpen: boolean) => {
  39. if (isOpen) {
  40. reportInteraction('Unknown variables section expanded');
  41. }
  42. setOpen(isOpen);
  43. };
  44. return (
  45. <div className={style.container}>
  46. <CollapsableSection label={<CollapseLabel />} isOpen={open} onToggle={onToggle}>
  47. {loading && (
  48. <VerticalGroup justify="center">
  49. <HorizontalGroup justify="center">
  50. <span>Loading...</span>
  51. <Spinner size={16} />
  52. </HorizontalGroup>
  53. </VerticalGroup>
  54. )}
  55. {!loading && usages && (
  56. <>
  57. {usages.length === 0 && <NoUnknowns />}
  58. {usages.length > 0 && <UnknownTable usages={usages} />}
  59. </>
  60. )}
  61. </CollapsableSection>
  62. </div>
  63. );
  64. }
  65. function CollapseLabel(): ReactElement {
  66. const style = useStyles(getStyles);
  67. return (
  68. <h5>
  69. Renamed or missing variables
  70. <Tooltip content="Click to expand a list with all variable references that have been renamed or are missing from the dashboard.">
  71. <Icon name="info-circle" className={style.infoIcon} />
  72. </Tooltip>
  73. </h5>
  74. );
  75. }
  76. function NoUnknowns(): ReactElement {
  77. return <span>No renamed or missing variables found.</span>;
  78. }
  79. function UnknownTable({ usages }: { usages: UsagesToNetwork[] }): ReactElement {
  80. const style = useStyles(getStyles);
  81. return (
  82. <table className="filter-table filter-table--hover">
  83. <thead>
  84. <tr>
  85. <th>Variable</th>
  86. <th colSpan={5} />
  87. </tr>
  88. </thead>
  89. <tbody>
  90. {usages.map((usage) => {
  91. const { variable } = usage;
  92. const { id, name } = variable;
  93. return (
  94. <tr key={id}>
  95. <td className={style.firstColumn}>
  96. <span>{name}</span>
  97. </td>
  98. <td className={style.defaultColumn} />
  99. <td className={style.defaultColumn} />
  100. <td className={style.defaultColumn} />
  101. <td className={style.lastColumn}>
  102. <VariablesUnknownButton id={variable.id} usages={usages} />
  103. </td>
  104. </tr>
  105. );
  106. })}
  107. </tbody>
  108. </table>
  109. );
  110. }
  111. const getStyles = (theme: GrafanaTheme) => ({
  112. container: css`
  113. margin-top: ${theme.spacing.xl};
  114. padding-top: ${theme.spacing.xl};
  115. `,
  116. infoIcon: css`
  117. margin-left: ${theme.spacing.sm};
  118. `,
  119. defaultColumn: css`
  120. width: 1%;
  121. `,
  122. firstColumn: css`
  123. width: 1%;
  124. vertical-align: top;
  125. color: ${theme.colors.textStrong};
  126. `,
  127. lastColumn: css`
  128. overflow: hidden;
  129. text-overflow: ellipsis;
  130. white-space: nowrap;
  131. width: 100%;
  132. text-align: right;
  133. `,
  134. });