Math.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { css } from '@emotion/css';
  2. import React, { ChangeEvent, FC } from 'react';
  3. import { useToggle } from 'react-use';
  4. import { GrafanaTheme2 } from '@grafana/data';
  5. import { Stack } from '@grafana/experimental';
  6. import { Button, Icon, InlineField, TextArea, useStyles2 } from '@grafana/ui';
  7. import { ExpressionQuery } from '../types';
  8. interface Props {
  9. labelWidth: number;
  10. query: ExpressionQuery;
  11. onChange: (query: ExpressionQuery) => void;
  12. onRunQuery: () => void;
  13. }
  14. const mathPlaceholder =
  15. 'Math operations on one or more queries. You reference the query by ${refId} ie. $A, $B, $C etc\n' +
  16. 'The sum of two scalar values: $A + $B > 10';
  17. export const Math: FC<Props> = ({ labelWidth, onChange, query, onRunQuery }) => {
  18. const [showHelp, toggleShowHelp] = useToggle(false);
  19. const onExpressionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
  20. onChange({ ...query, expression: event.target.value });
  21. };
  22. const styles = useStyles2((theme) => getStyles(theme, showHelp));
  23. const executeQuery = () => {
  24. if (query.expression) {
  25. onRunQuery();
  26. }
  27. };
  28. return (
  29. <Stack direction="row">
  30. <InlineField
  31. label="Expression"
  32. labelWidth={labelWidth}
  33. grow={true}
  34. shrink={true}
  35. className={css`
  36. align-items: flex-start;
  37. flex: 0.7;
  38. `}
  39. >
  40. <>
  41. <TextArea
  42. value={query.expression}
  43. onChange={onExpressionChange}
  44. rows={5}
  45. placeholder={mathPlaceholder}
  46. onBlur={executeQuery}
  47. />
  48. <Button variant="secondary" size="sm" onClick={toggleShowHelp}>
  49. {showHelp === false ? 'Show' : 'Hide'} help
  50. </Button>
  51. </>
  52. </InlineField>
  53. <div className={styles.documentationContainer}>
  54. <header className={styles.documentationHeader}>
  55. <Icon name="book-open" /> Math operator
  56. </header>
  57. <div>
  58. Run math operations on one or more queries. You reference the query by {'${refId}'} ie. $A, $B, $C etc.
  59. <br />
  60. Example: <code>$A + $B</code>
  61. </div>
  62. <header className={styles.documentationHeader}>Available Math functions</header>
  63. <div className={styles.documentationFunctions}>
  64. <DocumentedFunction
  65. name="abs"
  66. description="returns the absolute value of its argument which can be a number or a series"
  67. />
  68. <DocumentedFunction
  69. name="is_inf"
  70. description="returns 1 for Inf values (negative or positive) and 0 for other values. It's able to operate on series or scalar values."
  71. />
  72. <DocumentedFunction
  73. name="is_nan"
  74. description="returns 1 for NaN values and 0 for other values. It's able to operate on series or scalar values."
  75. />
  76. <DocumentedFunction
  77. name="is_null"
  78. description="returns 1 for null values and 0 for other values. It's able to operate on series or scalar values."
  79. />
  80. <DocumentedFunction
  81. name="is_number"
  82. description="returns 1 for all real number values and 0 for non-number. It's able to operate on series or scalar values."
  83. />
  84. <DocumentedFunction
  85. name="log"
  86. description="returns the natural logarithm of its argument, which can be a number or a series"
  87. />
  88. <DocumentedFunction
  89. name="inf, infn, nan, and null"
  90. description="The inf for infinity positive, infn for infinity negative, nan, and null functions all return a single scalar value that matches its name."
  91. />
  92. <DocumentedFunction
  93. name="round"
  94. description="returns a rounded integer value. It's able to operate on series or escalar values."
  95. />
  96. <DocumentedFunction
  97. name="ceil"
  98. description="rounds the number up to the nearest integer value. It's able to operate on series or escalar values."
  99. />
  100. <DocumentedFunction
  101. name="floor"
  102. description="rounds the number down to the nearest integer value. It's able to operate on series or escalar values."
  103. />
  104. </div>
  105. <div>
  106. See our additional documentation on{' '}
  107. <a
  108. className={styles.documentationLink}
  109. target="_blank"
  110. href="https://grafana.com/docs/grafana/latest/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions/#math"
  111. rel="noreferrer"
  112. >
  113. <Icon size="xs" name="external-link-alt" /> Math expressions
  114. </a>
  115. .
  116. </div>
  117. </div>
  118. </Stack>
  119. );
  120. };
  121. interface DocumentedFunctionProps {
  122. name: string;
  123. description: React.ReactNode;
  124. }
  125. const DocumentedFunction = ({ name, description }: DocumentedFunctionProps) => {
  126. const styles = useStyles2(getDocumentedFunctionStyles);
  127. return (
  128. <>
  129. <span className={styles.name}>{name}</span>
  130. <span className={styles.description}>{description}</span>
  131. </>
  132. );
  133. };
  134. const getStyles = (theme: GrafanaTheme2, showHelp?: boolean) => ({
  135. documentationHeader: css`
  136. font-size: ${theme.typography.h5.fontSize};
  137. font-weight: ${theme.typography.h5.fontWeight};
  138. `,
  139. documentationLink: css`
  140. color: ${theme.colors.text.link};
  141. `,
  142. documentationContainer: css`
  143. display: ${showHelp ? 'flex' : 'none'};
  144. flex: 1;
  145. flex-direction: column;
  146. gap: ${theme.spacing(2)};
  147. `,
  148. documentationFunctions: css`
  149. display: grid;
  150. grid-template-columns: max-content auto;
  151. column-gap: ${theme.spacing(2)};
  152. `,
  153. });
  154. const getDocumentedFunctionStyles = (theme: GrafanaTheme2) => ({
  155. name: css`
  156. font-weight: ${theme.typography.fontWeightBold};
  157. `,
  158. description: css`
  159. font-size: ${theme.typography.bodySmall.fontSize};
  160. color: ${theme.colors.text.disabled};
  161. `,
  162. });