Compare commits

...

3 Commits

Author SHA1 Message Date
Adela Almasan
426e0f6612 back button 2026-01-06 14:06:45 -06:00
Adela Almasan
4906a795e0 fromSuggestions flag 2026-01-06 12:11:40 -06:00
Adela Almasan
cfaf3610a4 interactions for auto-selected, preview, apply 2026-01-06 10:31:11 -06:00
6 changed files with 84 additions and 11 deletions

View File

@@ -71,6 +71,7 @@ export class PanelOptionsPane extends SceneObjectBase<PanelOptionsPaneState> {
reportInteraction(INTERACTION_EVENT_NAME, {
item: INTERACTION_ITEM.SELECT_PANEL_PLUGIN,
plugin_id: pluginId,
from_suggestions: options.fromSuggestions ?? false,
});
// clear custom options
@@ -236,6 +237,7 @@ function PanelOptionsPaneComponent({ model }: SceneComponentProps<PanelOptionsPa
onClose={model.onToggleVizPicker}
data={data}
showBackButton={config.featureToggles.newVizSuggestions ? hasPickedViz || !isNewPanel : true}
isNewPanel={isNewPanel}
/>
)}
</>

View File

@@ -26,6 +26,7 @@ export interface Props {
editPreview: VizPanel;
onChange: (options: VizTypeChangeDetails, panel?: VizPanel) => void;
onClose: () => void;
isNewPanel?: boolean;
}
const getTabs = (): Array<{ label: string; value: VisualizationSelectPaneTab }> => {
@@ -42,7 +43,7 @@ const getTabs = (): Array<{ label: string; value: VisualizationSelectPaneTab }>
: [allVisualizationsTab, suggestionsTab];
};
export function PanelVizTypePicker({ panel, editPreview, data, onChange, onClose, showBackButton }: Props) {
export function PanelVizTypePicker({ panel, editPreview, data, onChange, onClose, showBackButton, isNewPanel }: Props) {
const styles = useStyles2(getStyles);
const panelModel = useMemo(() => new PanelModelCompatibilityWrapper(panel), [panel]);
const filterId = useId();
@@ -83,6 +84,16 @@ export function PanelVizTypePicker({ panel, editPreview, data, onChange, onClose
[setListMode]
);
const handleBackButtonClick = useCallback(() => {
reportInteraction(INTERACTION_EVENT_NAME, {
item: INTERACTION_ITEM.BACK_BUTTON,
tab: VisualizationSelectPaneTab[listMode],
creator_team: 'grafana_plugins_catalog',
schema_version: '1.0.0',
});
onClose();
}, [listMode, onClose]);
return (
<div className={styles.wrapper}>
<TabsBar className={styles.tabs} hideBorder={true}>
@@ -114,7 +125,7 @@ export function PanelVizTypePicker({ panel, editPreview, data, onChange, onClose
variant="secondary"
icon="arrow-left"
data-testid={selectors.components.PanelEditor.toggleVizPicker}
onClick={onClose}
onClick={handleBackButtonClick}
>
<Trans i18nKey="dashboard-scene.panel-viz-type-picker.button.close">Back</Trans>
</Button>
@@ -136,6 +147,7 @@ export function PanelVizTypePicker({ panel, editPreview, data, onChange, onClose
editPreview={editPreview}
data={data}
searchQuery={searchQuery}
isNewPanel={isNewPanel}
/>
)}
{listMode === VisualizationSelectPaneTab.Visualizations && (

View File

@@ -4,4 +4,5 @@ export const INTERACTION_ITEM = {
SELECT_PANEL_PLUGIN: 'select_panel_plugin',
CHANGE_TAB: 'change_tab', // for ref - PanelVizTypePicker
SEARCH: 'search', // for ref - PanelVizTypePicker
BACK_BUTTON: 'back_button', // for ref - PanelVizTypePicker
};

View File

@@ -22,6 +22,7 @@ import { getAllSuggestions } from '../../suggestions/getAllSuggestions';
import { hasData } from '../../suggestions/utils';
import { VisualizationSuggestionCard } from './VisualizationSuggestionCard';
import { VizSuggestionsInteractions, PANEL_STATES, type PanelState } from './interactions';
import { VizTypeChangeDetails } from './types';
export interface Props {
@@ -30,6 +31,7 @@ export interface Props {
data?: PanelData;
panel?: PanelModel;
searchQuery?: string;
isNewPanel?: boolean;
}
const useSuggestions = (data: PanelData | undefined, searchQuery: string | undefined) => {
@@ -62,7 +64,7 @@ const useSuggestions = (data: PanelData | undefined, searchQuery: string | undef
return { value: filteredValue, loading, error, retry };
};
export function VisualizationSuggestions({ onChange, editPreview, data, panel, searchQuery }: Props) {
export function VisualizationSuggestions({ onChange, editPreview, data, panel, searchQuery, isNewPanel }: Props) {
const styles = useStyles2(getStyles);
const { value: result, loading, error, retry } = useSuggestions(data, searchQuery);
@@ -75,6 +77,18 @@ export function VisualizationSuggestions({ onChange, editPreview, data, panel, s
const isNewVizSuggestionsEnabled = config.featureToggles.newVizSuggestions;
const isUnconfiguredPanel = panel?.type === UNCONFIGURED_PANEL_PLUGIN_ID;
const getPanelState = useCallback((): PanelState => {
if (isUnconfiguredPanel) {
return PANEL_STATES.UNCONFIGURED_PANEL;
}
if (isNewPanel) {
return PANEL_STATES.NEW_PANEL;
}
return PANEL_STATES.EXISTING_PANEL;
}, [isUnconfiguredPanel, isNewPanel]);
const suggestionsByVizType = useMemo(() => {
const meta = getAllPanelPluginMeta();
const record: Record<string, PanelPluginMeta> = {};
@@ -96,22 +110,37 @@ export function VisualizationSuggestions({ onChange, editPreview, data, panel, s
}, [suggestions]);
const applySuggestion = useCallback(
(suggestion: PanelPluginVisualizationSuggestion, isPreview?: boolean) => {
(suggestion: PanelPluginVisualizationSuggestion, isPreview: boolean, isAutoSelected = false) => {
const panelState = getPanelState();
if (isPreview) {
VizSuggestionsInteractions.suggestionPreviewed({
pluginId: suggestion.pluginId,
suggestionName: suggestion.name,
panelState,
isAutoSelected,
});
setSuggestionHash(suggestion.hash);
} else {
VizSuggestionsInteractions.suggestionAccepted({
pluginId: suggestion.pluginId,
suggestionName: suggestion.name,
panelState,
});
}
onChange(
{
pluginId: suggestion.pluginId,
options: suggestion.options,
fieldConfig: suggestion.fieldConfig,
withModKey: isPreview,
fromSuggestions: true,
},
isPreview ? editPreview : undefined
);
if (isPreview) {
setSuggestionHash(suggestion.hash);
}
},
[onChange, editPreview]
[onChange, editPreview, getPanelState]
);
useEffect(() => {
@@ -124,7 +153,7 @@ export function VisualizationSuggestions({ onChange, editPreview, data, panel, s
// the previously selected suggestion is no longer present in the list.
const newFirstCardHash = suggestions?.[0]?.hash ?? null;
if (firstCardHash !== newFirstCardHash || suggestions.every((s) => s.hash !== suggestionHash)) {
applySuggestion(suggestions[0], true);
applySuggestion(suggestions[0], true, true);
setFirstCardHash(newFirstCardHash);
return;
}
@@ -243,7 +272,7 @@ export function VisualizationSuggestions({ onChange, editPreview, data, panel, s
suggestion={suggestion}
width={width}
tabIndex={index}
onClick={() => applySuggestion(suggestion)}
onClick={() => applySuggestion(suggestion, false)}
/>
</div>
))}

View File

@@ -0,0 +1,28 @@
import { reportInteraction } from '@grafana/runtime';
export const PANEL_STATES = {
UNCONFIGURED_PANEL: 'unconfigured_panel',
NEW_PANEL: 'new_panel',
EXISTING_PANEL: 'existing_panel',
} as const;
export type PanelState = (typeof PANEL_STATES)[keyof typeof PANEL_STATES];
export const VizSuggestionsInteractions = {
suggestionPreviewed: (properties: {
pluginId: string;
suggestionName: string;
panelState: PanelState;
isAutoSelected?: boolean;
}) => {
reportVizSuggestionsInteraction('suggestion_previewed', properties);
},
suggestionAccepted: (properties: { pluginId: string; suggestionName: string; panelState: PanelState }) => {
reportVizSuggestionsInteraction('suggestion_accepted', properties);
},
};
const reportVizSuggestionsInteraction = (name: string, properties?: Record<string, unknown>) => {
reportInteraction(`grafana_viz_suggestions_${name}`, properties);
};

View File

@@ -5,4 +5,5 @@ export interface VizTypeChangeDetails {
options?: Record<string, unknown>;
fieldConfig?: FieldConfigSource;
withModKey?: boolean;
fromSuggestions?: boolean;
}