Expressions: adds inline documentation for the Math expression (#48784)

This commit is contained in:
Gilles De Mey 2022-05-16 11:30:27 +02:00 committed by GitHub
parent 483319f1cb
commit 1a7ca3f0de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 147 additions and 17 deletions

View File

@ -18,6 +18,8 @@ export interface Props extends Omit<FieldProps, 'css' | 'horizontal' | 'descript
labelWidth?: number | 'auto';
/** Make the field's child to fill the width of the row. Equivalent to setting `flex-grow:1` on the field */
grow?: boolean;
/** Make the field's child shrink with width of the row. Equivalent to setting `flex-shrink:1` on the field */
shrink?: boolean;
/** Make field's background transparent */
transparent?: boolean;
/** Error message to display */
@ -38,13 +40,14 @@ export const InlineField: FC<Props> = ({
className,
htmlFor,
grow,
shrink,
error,
transparent,
interactive,
...htmlProps
}) => {
const theme = useTheme2();
const styles = getStyles(theme, grow);
const styles = getStyles(theme, grow, shrink);
const inputId = htmlFor ?? getChildId(children);
const labelElement =
@ -79,7 +82,7 @@ export const InlineField: FC<Props> = ({
InlineField.displayName = 'InlineField';
const getStyles = (theme: GrafanaTheme2, grow?: boolean) => {
const getStyles = (theme: GrafanaTheme2, grow?: boolean, shrink?: boolean) => {
return {
container: css`
display: flex;
@ -87,11 +90,11 @@ const getStyles = (theme: GrafanaTheme2, grow?: boolean) => {
align-items: flex-start;
text-align: left;
position: relative;
flex: ${grow ? 1 : 0} 0 auto;
flex: ${grow ? 1 : 0} ${shrink ? 1 : 0} auto;
margin: 0 ${theme.spacing(0.5)} ${theme.spacing(0.5)} 0;
`,
childContainer: css`
flex: ${grow ? 1 : 0} 0 auto;
flex: ${grow ? 1 : 0} ${shrink ? 1 : 0} auto;
`,
fieldValidationWrapper: css`
margin-top: ${theme.spacing(0.5)};

View File

@ -1,7 +1,10 @@
import { css } from '@emotion/css';
import React, { ChangeEvent, FC } from 'react';
import { useToggle } from 'react-use';
import { InlineField, TextArea } from '@grafana/ui';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { Button, Icon, InlineField, TextArea, useStyles2 } from '@grafana/ui';
import { ExpressionQuery } from '../types';
@ -12,24 +15,148 @@ interface Props {
}
const mathPlaceholder =
'Math operations on one more queries, you reference the query by ${refId} ie. $A, $B, $C etc\n' +
'Example: $A + $B\n' +
'Available functions: abs(), log(), is_number(), round(), ceil(), floor(), is_inf(), is_nan(), is_null(), ';
'Math operations on one or more queries. You reference the query by ${refId} ie. $A, $B, $C etc\n' +
'The sum of two scalar values: $A + $B > 10';
export const Math: FC<Props> = ({ labelWidth, onChange, query }) => {
const [showHelp, toggleShowHelp] = useToggle(true);
const onExpressionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
onChange({ ...query, expression: event.target.value });
};
const styles = useStyles2((theme) => getStyles(theme, showHelp));
return (
<InlineField
label="Expression"
labelWidth={labelWidth}
className={css`
align-items: baseline;
`}
>
<TextArea value={query.expression} onChange={onExpressionChange} rows={4} placeholder={mathPlaceholder} />
</InlineField>
<Stack direction="row">
<InlineField
label="Expression"
labelWidth={labelWidth}
grow={true}
shrink={true}
className={css`
align-items: flex-start;
flex: 0.7;
`}
>
<>
<TextArea value={query.expression} onChange={onExpressionChange} rows={5} placeholder={mathPlaceholder} />
<Button color="primary" size="sm" onClick={() => toggleShowHelp()}>
{showHelp === false ? 'Show' : 'Hide'} help
</Button>
</>
</InlineField>
<div className={styles.documentationContainer}>
<header className={styles.documentationHeader}>
<Icon name="book-open" /> Math operator
</header>
<div>
Run math operations on one or more queries. You reference the query by {'${refId}'} ie. $A, $B, $C etc.
<br />
Example: <code>$A + $B</code>
</div>
<header className={styles.documentationHeader}>Available Math functions</header>
<div className={styles.documentationFunctions}>
<DocumentedFunction
name="abs"
description="returns the absolute value of its argument which can be a number or a series"
/>
<DocumentedFunction
name="is_inf"
description="returns 1 for Inf values (negative or positive) and 0 for other values. It's able to operate on series or scalar values."
/>
<DocumentedFunction
name="is_nan"
description="returns 1 for NaN values and 0 for other values. It's able to operate on series or scalar values."
/>
<DocumentedFunction
name="is_null"
description="returns 1 for null values and 0 for other values. It's able to operate on series or scalar values."
/>
<DocumentedFunction
name="is_number"
description="returns 1 for all real number values and 0 for non-number. It's able to operate on series or scalar values."
/>
<DocumentedFunction
name="log"
description="returns the natural logarithm of its argument, which can be a number or a series"
/>
<DocumentedFunction
name="inf, infn, nan, and null"
description="The inf for infinity positive, infn for infinity negative, nan, and null functions all return a single scalar value that matches its name."
/>
<DocumentedFunction
name="round"
description="returns a rounded integer value. It's able to operate on series or escalar values."
/>
<DocumentedFunction
name="ceil"
description="rounds the number up to the nearest integer value. It's able to operate on series or escalar values."
/>
<DocumentedFunction
name="floor"
description="rounds the number down to the nearest integer value. It's able to operate on series or escalar values."
/>
</div>
<div>
See our additional documentation on{' '}
<a
className={styles.documentationLink}
target="_blank"
href="https://grafana.com/docs/grafana/latest/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions/#math"
rel="noreferrer"
>
<Icon size="xs" name="external-link-alt" /> Math expressions
</a>
.
</div>
</div>
</Stack>
);
};
interface DocumentedFunctionProps {
name: string;
description: React.ReactNode;
}
const DocumentedFunction = ({ name, description }: DocumentedFunctionProps) => {
const styles = useStyles2(getDocumentedFunctionStyles);
return (
<>
<span className={styles.name}>{name}</span>
<span className={styles.description}>{description}</span>
</>
);
};
const getStyles = (theme: GrafanaTheme2, showHelp?: boolean) => ({
documentationHeader: css`
font-size: ${theme.typography.h5.fontSize};
font-weight: ${theme.typography.h5.fontWeight};
`,
documentationLink: css`
color: ${theme.colors.text.link};
`,
documentationContainer: css`
display: ${showHelp ? 'flex' : 'none'};
flex: 1;
flex-direction: column;
gap: ${theme.spacing(2)};
`,
documentationFunctions: css`
display: grid;
grid-template-columns: max-content auto;
column-gap: ${theme.spacing(2)};
`,
});
const getDocumentedFunctionStyles = (theme: GrafanaTheme2) => ({
name: css`
font-weight: ${theme.typography.fontWeightBold};
`,
description: css`
font-size: ${theme.typography.bodySmall.fontSize};
color: ${theme.colors.text.disabled};
`,
});