init: initial updates

This commit is contained in:
Alex Spencer
2025-11-10 11:32:48 -08:00
parent 90ddd922ad
commit b35a8a6c36
5 changed files with 192 additions and 90 deletions
@@ -1,11 +1,9 @@
import { css } from '@emotion/css';
import { useEffect, useState } from 'react';
import { useBeforeUnload, useUnmount } from 'react-use';
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { reportInteraction } from '@grafana/runtime';
import { Button, Icon, Stack, Tooltip, useStyles2 } from '@grafana/ui';
import { Alert, Badge, Button, Stack, Text } from '@grafana/ui';
import { Prompt } from 'app/core/components/FormPrompt/Prompt';
import { CORRELATION_EDITOR_POST_CONFIRM_ACTION, ExploreItemState } from 'app/types/explore';
import { useDispatch, useSelector } from 'app/types/store';
@@ -21,7 +19,6 @@ import { selectCorrelationDetails, selectIsHelperShowing } from './state/selecto
export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, ExploreItemState]> }) => {
const dispatch = useDispatch();
const styles = useStyles2(getStyles);
const correlationDetails = useSelector(selectCorrelationDetails);
const isHelperShowing = useSelector(selectIsHelperShowing);
const [saveMessage, setSaveMessage] = useState<string | undefined>(undefined); // undefined means do not show
@@ -230,74 +227,68 @@ export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, Expl
message={saveMessage}
/>
)}
<div className={styles.correlationEditorTop}>
<Stack gap={2} justifyContent="flex-end" alignItems="center">
<Tooltip
content={t(
'explore.correlation-editor-mode-bar.content-correlations-editor-explore-experimental-feature',
'Correlations editor in Explore is an experimental feature.'
)}
>
<Icon className={styles.iconColor} name="info-circle" size="xl" />
</Tooltip>
<Button
variant="secondary"
disabled={!correlationDetails?.canSave}
fill="outline"
className={correlationDetails?.canSave ? styles.buttonColor : styles.disabledButtonColor}
onClick={() => {
saveCorrelationPostAction(true);
}}
>
<Trans i18nKey="explore.correlation-editor-mode-bar.save">Save</Trans>
</Button>
<Button
variant="secondary"
fill="outline"
className={styles.buttonColor}
icon="times"
onClick={() => {
dispatch(changeCorrelationEditorDetails({ isExiting: true }));
reportInteraction('grafana_explore_correlation_editor_exit_pressed');
}}
>
<Trans i18nKey="explore.correlation-editor-mode-bar.exit-correlation-editor">Exit correlation editor</Trans>
</Button>
<Alert title="" severity="info" bottomSpacing={0} topSpacing={0}>
<Stack width="100%" alignItems="start" justifyContent="space-between">
<Stack gap={1} direction="column">
<Stack gap={2} alignItems="center" direction="row">
<Text variant="h4">
<Trans i18nKey="explore.correlation-editor-mode-bar.edit-mode-title">Correlation Editor Mode</Trans>
</Text>
<Badge
color="orange"
text={t('explore.correlation-editor-mode-bar.experimental', 'Experimental')}
tooltip={t(
'explore.correlation-editor-mode-bar.content-correlations-editor-explore-experimental-feature',
'Correlations editor in Explore is an experimental feature.'
)}
/>
</Stack>
<Stack gap={0} direction="column">
<Text variant="bodySmall">
<Trans i18nKey="explore.correlation-editor-mode-bar.instructions">
Step 1: In the left pane, run a query and click a table cell link or a &quot;🔗 Correlate with&quot;
button.
</Trans>
</Text>
<Text variant="bodySmall">
<Trans i18nKey="explore.correlation-editor-mode-bar.instructions-2">
Step 2: In the right pane (Target Query Builder), build and test your correlation query.
</Trans>
</Text>
<Text variant="bodySmall">
<Trans i18nKey="explore.correlation-editor-mode-bar.instructions-3">
Step 3: Click Save to create the correlation.
</Trans>
</Text>
</Stack>
</Stack>
<Stack gap={1} alignItems="center">
<Button
size="sm"
disabled={!correlationDetails?.canSave}
variant="secondary"
onClick={() => {
saveCorrelationPostAction(true);
}}
>
<Trans i18nKey="explore.correlation-editor-mode-bar.save">Save</Trans>
</Button>
<Button
size="sm"
icon="times"
variant="secondary"
onClick={() => {
dispatch(changeCorrelationEditorDetails({ isExiting: true }));
reportInteraction('grafana_explore_correlation_editor_exit_pressed');
}}
>
<Trans i18nKey="explore.correlation-editor-mode-bar.exit-correlation-editor">
Exit correlation editor
</Trans>
</Button>
</Stack>
</Stack>
</div>
</Alert>
</>
);
};
const getStyles = (theme: GrafanaTheme2) => {
const contrastColor = theme.colors.getContrastText(theme.colors.primary.main);
const lighterBackgroundColor = colorManipulator.lighten(theme.colors.primary.main, 0.1);
const darkerBackgroundColor = colorManipulator.darken(theme.colors.primary.main, 0.2);
const disabledColor = colorManipulator.darken(contrastColor, 0.2);
return {
correlationEditorTop: css({
backgroundColor: theme.colors.primary.main,
marginTop: '3px',
padding: theme.spacing(1),
}),
iconColor: css({
color: contrastColor,
}),
buttonColor: css({
color: contrastColor,
borderColor: contrastColor,
'&:hover': {
color: contrastColor,
borderColor: contrastColor,
backgroundColor: lighterBackgroundColor,
},
}),
// important needed to override disabled state styling
disabledButtonColor: css({
color: `${disabledColor} !important`,
backgroundColor: `${darkerBackgroundColor} !important`,
}),
};
};
@@ -157,19 +157,37 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
}
/>
)}
<Alert title={t('explore.correlation-helper.title-correlation-details', 'Correlation details')} severity="info">
<Trans
i18nKey="explore.correlation-helper.body-correlation-details"
values={{ resultField: correlations.resultField }}
>
The correlation link will appear by the <code>{'{{resultField}}'}</code> field. You can use the following
variables to set up your correlations:
</Trans>
<pre>
{Object.entries(correlations.vars).map((entry) => {
return `\$\{${entry[0]}\} = ${entry[1]}\n`;
})}
</pre>
<Alert
className={styles.alertWrapper}
title={t('explore.correlation-helper.title-correlation-details', 'Correlation details')}
severity="info"
>
<div className={styles.alertContent}>
<div>
<Trans
i18nKey="explore.correlation-helper.body-correlation-details"
values={{ resultField: correlations.resultField }}
>
When saved, the <code>{'{{resultField}}'}</code> field will have a clickable link that runs your target
query below. Use the below variables in your query. When clicked, they're replaced with values from that
row.
</Trans>
</div>
<div className={styles.variableList}>
{Object.entries(correlations.vars).map(([name, value]) => (
<div key={name} className={styles.variableRow}>
<div className={styles.variableNameCell}>
<code className={styles.variableName}>${`{${name}}`}</code>
</div>
<div className={styles.variableValueCell}>
<Tooltip content={value} placement="auto-start">
<span className={styles.variableValue}>{value}</span>
</Tooltip>
</div>
</div>
))}
</div>
</div>
<Collapse
isOpen={isLabelDescOpen}
onToggle={() => {
@@ -177,7 +195,9 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
}}
label={
<Stack gap={1} direction="row" wrap="wrap" alignItems="center">
<Trans i18nKey="explore.correlation-helper.label-description-header">Label / Description</Trans>
<Trans i18nKey="explore.correlation-helper.label-description-header">
Correlation Label / Description
</Trans>
{!isLabelDescOpen && !loadingLabel && (
<span className={styles.labelCollapseDetails}>{`Label: ${getValues('label') || defaultLabel}`}</span>
)}
@@ -287,5 +307,69 @@ const getStyles = (theme: GrafanaTheme2) => {
transformationMeta: css({
alignItems: 'baseline',
}),
alertWrapper: css({
'& > div': {
minWidth: 0,
maxWidth: '100%',
overflow: 'hidden',
},
}),
alertContent: css({
minWidth: 0,
maxWidth: '100%',
overflow: 'hidden',
width: '100%',
}),
variableList: css({
marginTop: theme.spacing(1.5),
marginBottom: theme.spacing(2),
display: 'table',
width: '100%',
tableLayout: 'auto',
borderCollapse: 'collapse',
}),
variableRow: css({
display: 'table-row',
}),
variableNameCell: css({
display: 'table-cell',
width: '1%',
paddingBottom: theme.spacing(0.5),
paddingRight: theme.spacing(1),
verticalAlign: 'top',
whiteSpace: 'nowrap',
}),
variableName: css({
display: 'inline-block',
backgroundColor: theme.colors.background.secondary,
padding: theme.spacing(0.5, 1),
borderRadius: theme.shape.radius.default,
fontFamily: theme.typography.fontFamilyMonospace,
fontSize: theme.typography.bodySmall.fontSize,
color: theme.colors.primary.text,
fontWeight: theme.typography.fontWeightMedium,
whiteSpace: 'nowrap',
}),
variableValueCell: css({
display: 'table-cell',
width: '100%',
maxWidth: 0,
paddingBottom: theme.spacing(0.5),
verticalAlign: 'top',
}),
variableValue: css({
display: 'inline-block',
backgroundColor: theme.colors.background.secondary,
padding: theme.spacing(0.5, 1),
borderRadius: theme.shape.radius.default,
fontFamily: theme.typography.fontFamilyMonospace,
fontSize: theme.typography.bodySmall.fontSize,
color: theme.colors.text.primary,
maxWidth: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
verticalAlign: 'top',
}),
};
};
@@ -120,9 +120,6 @@ const getStyles = (theme: GrafanaTheme2) => {
overflow: 'hidden',
}),
correlationsEditorIndicator: css({
borderLeft: `4px solid ${theme.colors.primary.main}`,
borderRight: `4px solid ${theme.colors.primary.main}`,
borderBottom: `4px solid ${theme.colors.primary.main}`,
overflow: 'scroll',
}),
};
@@ -16,6 +16,8 @@ import {
ButtonGroup,
useStyles2,
Button,
Badge,
Tooltip,
} from '@grafana/ui';
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
@@ -57,6 +59,10 @@ const getStyles = (theme: GrafanaTheme2, splitted: Boolean) => ({
marginRight: theme.spacing(0.5),
width: splitted && theme.spacing(6),
}),
badgeWrapper: css({
marginTop: theme.spacing(2),
marginLeft: theme.spacing(2),
}),
});
interface Props {
@@ -219,10 +225,34 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
<ShortLinkButtonMenu key="share" />,
];
const showBuilderIndicator = isCorrelationsEditorMode && !isLeftPane;
const showSourceIndicator = isCorrelationsEditorMode && isLeftPane && splitted;
return (
<div>
{refreshInterval && <SetInterval func={onRunQuery} interval={refreshInterval} loading={loading} />}
<AppChromeUpdate actions={navBarActions} />
{showSourceIndicator && (
<div className={styles.badgeWrapper}>
<Tooltip
content={t(
'explore.toolbar.source-correlation-query',
'Run a query and click a correlation link to start building your correlation'
)}
>
<Badge color="blue" icon="arrow-from-right" text={t('explore.toolbar.source-query', 'Source query')} />
</Tooltip>
</div>
)}
{showBuilderIndicator && (
<div className={styles.badgeWrapper}>
<Tooltip
content={t('explore.toolbar.build-correlation-query', 'Build and test your correlation target query here')}
>
<Badge color="orange" icon="crosshair" text={t('explore.toolbar.target-query-builder', 'Target query')} />
</Tooltip>
</div>
)}
<PageToolbar
aria-label={t('explore.toolbar.aria-label', 'Explore toolbar')}
leftItems={[
+2 -2
View File
@@ -7017,10 +7017,10 @@
"add-transformation": "Add transformation",
"aria-label-delete-transformation": "Delete transformation",
"aria-label-edit-transformation": "Edit transformation",
"body-correlation-details": "The correlation link will appear by the <1>{{resultField}}</1> field. You can use the following variables to set up your correlations:",
"body-correlation-details": "When saved, the <1>{{resultField}}</1> field will have a clickable link that runs your target query below. Use the below variables in your query. When clicked, they're replaced with values from that row.",
"expression": "Expression: <1>{{expression}}</1>",
"label-description": "Description",
"label-description-header": "Label / Description",
"label-description-header": "Correlation Label / Description",
"label-label": "Label",
"title-correlation-details": "Correlation details",
"tooltip-transformations": "A transformation extracts one or more variables out of a single field.",