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 { useEffect, useState } from 'react';
import { useBeforeUnload, useUnmount } from 'react-use'; import { useBeforeUnload, useUnmount } from 'react-use';
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { reportInteraction } from '@grafana/runtime'; 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 { Prompt } from 'app/core/components/FormPrompt/Prompt';
import { CORRELATION_EDITOR_POST_CONFIRM_ACTION, ExploreItemState } from 'app/types/explore'; import { CORRELATION_EDITOR_POST_CONFIRM_ACTION, ExploreItemState } from 'app/types/explore';
import { useDispatch, useSelector } from 'app/types/store'; 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]> }) => { export const CorrelationEditorModeBar = ({ panes }: { panes: Array<[string, ExploreItemState]> }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const styles = useStyles2(getStyles);
const correlationDetails = useSelector(selectCorrelationDetails); const correlationDetails = useSelector(selectCorrelationDetails);
const isHelperShowing = useSelector(selectIsHelperShowing); const isHelperShowing = useSelector(selectIsHelperShowing);
const [saveMessage, setSaveMessage] = useState<string | undefined>(undefined); // undefined means do not show 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} message={saveMessage}
/> />
)} )}
<div className={styles.correlationEditorTop}> <Alert title="" severity="info" bottomSpacing={0} topSpacing={0}>
<Stack gap={2} justifyContent="flex-end" alignItems="center"> <Stack width="100%" alignItems="start" justifyContent="space-between">
<Tooltip <Stack gap={1} direction="column">
content={t( <Stack gap={2} alignItems="center" direction="row">
'explore.correlation-editor-mode-bar.content-correlations-editor-explore-experimental-feature', <Text variant="h4">
'Correlations editor in Explore is an experimental feature.' <Trans i18nKey="explore.correlation-editor-mode-bar.edit-mode-title">Correlation Editor Mode</Trans>
)} </Text>
> <Badge
<Icon className={styles.iconColor} name="info-circle" size="xl" /> color="orange"
</Tooltip> text={t('explore.correlation-editor-mode-bar.experimental', 'Experimental')}
<Button tooltip={t(
variant="secondary" 'explore.correlation-editor-mode-bar.content-correlations-editor-explore-experimental-feature',
disabled={!correlationDetails?.canSave} 'Correlations editor in Explore is an experimental feature.'
fill="outline" )}
className={correlationDetails?.canSave ? styles.buttonColor : styles.disabledButtonColor} />
onClick={() => { </Stack>
saveCorrelationPostAction(true); <Stack gap={0} direction="column">
}} <Text variant="bodySmall">
> <Trans i18nKey="explore.correlation-editor-mode-bar.instructions">
<Trans i18nKey="explore.correlation-editor-mode-bar.save">Save</Trans> Step 1: In the left pane, run a query and click a table cell link or a &quot;🔗 Correlate with&quot;
</Button> button.
<Button </Trans>
variant="secondary" </Text>
fill="outline" <Text variant="bodySmall">
className={styles.buttonColor} <Trans i18nKey="explore.correlation-editor-mode-bar.instructions-2">
icon="times" Step 2: In the right pane (Target Query Builder), build and test your correlation query.
onClick={() => { </Trans>
dispatch(changeCorrelationEditorDetails({ isExiting: true })); </Text>
reportInteraction('grafana_explore_correlation_editor_exit_pressed'); <Text variant="bodySmall">
}} <Trans i18nKey="explore.correlation-editor-mode-bar.instructions-3">
> Step 3: Click Save to create the correlation.
<Trans i18nKey="explore.correlation-editor-mode-bar.exit-correlation-editor">Exit correlation editor</Trans> </Trans>
</Button> </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> </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"> <Alert
<Trans className={styles.alertWrapper}
i18nKey="explore.correlation-helper.body-correlation-details" title={t('explore.correlation-helper.title-correlation-details', 'Correlation details')}
values={{ resultField: correlations.resultField }} severity="info"
> >
The correlation link will appear by the <code>{'{{resultField}}'}</code> field. You can use the following <div className={styles.alertContent}>
variables to set up your correlations: <div>
</Trans> <Trans
<pre> i18nKey="explore.correlation-helper.body-correlation-details"
{Object.entries(correlations.vars).map((entry) => { values={{ resultField: correlations.resultField }}
return `\$\{${entry[0]}\} = ${entry[1]}\n`; >
})} When saved, the <code>{'{{resultField}}'}</code> field will have a clickable link that runs your target
</pre> 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 <Collapse
isOpen={isLabelDescOpen} isOpen={isLabelDescOpen}
onToggle={() => { onToggle={() => {
@@ -177,7 +195,9 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => {
}} }}
label={ label={
<Stack gap={1} direction="row" wrap="wrap" alignItems="center"> <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 && ( {!isLabelDescOpen && !loadingLabel && (
<span className={styles.labelCollapseDetails}>{`Label: ${getValues('label') || defaultLabel}`}</span> <span className={styles.labelCollapseDetails}>{`Label: ${getValues('label') || defaultLabel}`}</span>
)} )}
@@ -287,5 +307,69 @@ const getStyles = (theme: GrafanaTheme2) => {
transformationMeta: css({ transformationMeta: css({
alignItems: 'baseline', 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', overflow: 'hidden',
}), }),
correlationsEditorIndicator: css({ 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', overflow: 'scroll',
}), }),
}; };
@@ -16,6 +16,8 @@ import {
ButtonGroup, ButtonGroup,
useStyles2, useStyles2,
Button, Button,
Badge,
Tooltip,
} from '@grafana/ui'; } from '@grafana/ui';
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate'; import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
@@ -57,6 +59,10 @@ const getStyles = (theme: GrafanaTheme2, splitted: Boolean) => ({
marginRight: theme.spacing(0.5), marginRight: theme.spacing(0.5),
width: splitted && theme.spacing(6), width: splitted && theme.spacing(6),
}), }),
badgeWrapper: css({
marginTop: theme.spacing(2),
marginLeft: theme.spacing(2),
}),
}); });
interface Props { interface Props {
@@ -219,10 +225,34 @@ export function ExploreToolbar({ exploreId, onChangeTime, onContentOutlineToogle
<ShortLinkButtonMenu key="share" />, <ShortLinkButtonMenu key="share" />,
]; ];
const showBuilderIndicator = isCorrelationsEditorMode && !isLeftPane;
const showSourceIndicator = isCorrelationsEditorMode && isLeftPane && splitted;
return ( return (
<div> <div>
{refreshInterval && <SetInterval func={onRunQuery} interval={refreshInterval} loading={loading} />} {refreshInterval && <SetInterval func={onRunQuery} interval={refreshInterval} loading={loading} />}
<AppChromeUpdate actions={navBarActions} /> <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 <PageToolbar
aria-label={t('explore.toolbar.aria-label', 'Explore toolbar')} aria-label={t('explore.toolbar.aria-label', 'Explore toolbar')}
leftItems={[ leftItems={[
+2 -2
View File
@@ -7017,10 +7017,10 @@
"add-transformation": "Add transformation", "add-transformation": "Add transformation",
"aria-label-delete-transformation": "Delete transformation", "aria-label-delete-transformation": "Delete transformation",
"aria-label-edit-transformation": "Edit 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>", "expression": "Expression: <1>{{expression}}</1>",
"label-description": "Description", "label-description": "Description",
"label-description-header": "Label / Description", "label-description-header": "Correlation Label / Description",
"label-label": "Label", "label-label": "Label",
"title-correlation-details": "Correlation details", "title-correlation-details": "Correlation details",
"tooltip-transformations": "A transformation extracts one or more variables out of a single field.", "tooltip-transformations": "A transformation extracts one or more variables out of a single field.",