mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 13:39:19 -06:00
PluginExtensions: Reports user interactions with UI extensions (#74355)
* Added tracking information to how UI links are being used by users. * Fixed nit.
This commit is contained in:
parent
1a8a19a9ed
commit
05ce7e5789
@ -1,10 +1,18 @@
|
||||
import { PluginExtensionLinkConfig, PluginExtensionTypes } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import { createPluginExtensionRegistry } from './createPluginExtensionRegistry';
|
||||
import { getPluginExtensions } from './getPluginExtensions';
|
||||
import { isReadOnlyProxy } from './utils';
|
||||
import { assertPluginExtensionLink } from './validators';
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
reportInteraction: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('getPluginExtensions()', () => {
|
||||
const extensionPoint1 = 'grafana/dashboard/panel/menu';
|
||||
const extensionPoint2 = 'plugins/myorg-basic-app/start';
|
||||
@ -30,6 +38,7 @@ describe('getPluginExtensions()', () => {
|
||||
};
|
||||
|
||||
global.console.warn = jest.fn();
|
||||
jest.mocked(reportInteraction).mockReset();
|
||||
});
|
||||
|
||||
test('should return the extensions for the given placement', () => {
|
||||
@ -43,7 +52,7 @@ describe('getPluginExtensions()', () => {
|
||||
type: PluginExtensionTypes.link,
|
||||
title: link1.title,
|
||||
description: link1.description,
|
||||
path: link1.path,
|
||||
path: expect.stringContaining(link1.path!),
|
||||
})
|
||||
);
|
||||
});
|
||||
@ -60,7 +69,7 @@ describe('getPluginExtensions()', () => {
|
||||
type: PluginExtensionTypes.link,
|
||||
title: link1.title,
|
||||
description: link1.description,
|
||||
path: link1.path,
|
||||
path: expect.stringContaining(link1.path!),
|
||||
})
|
||||
);
|
||||
});
|
||||
@ -89,7 +98,7 @@ describe('getPluginExtensions()', () => {
|
||||
type: PluginExtensionTypes.link,
|
||||
title: link1.title,
|
||||
description: link1.description,
|
||||
path: link1.path,
|
||||
path: expect.stringContaining(link1.path!),
|
||||
})
|
||||
);
|
||||
});
|
||||
@ -129,11 +138,32 @@ describe('getPluginExtensions()', () => {
|
||||
expect(link2.configure).toHaveBeenCalledTimes(1);
|
||||
expect(extension.title).toBe('Updated title');
|
||||
expect(extension.description).toBe('Updated description');
|
||||
expect(extension.path).toBe(`/a/${pluginId}/updated-path`);
|
||||
expect(extension.path?.startsWith(`/a/${pluginId}/updated-path`)).toBeTruthy();
|
||||
expect(extension.icon).toBe('search');
|
||||
expect(extension.category).toBe('Machine Learning');
|
||||
});
|
||||
|
||||
test('should append link tracking to path when running configure() function', () => {
|
||||
link2.configure = jest.fn().mockImplementation(() => ({
|
||||
title: 'Updated title',
|
||||
description: 'Updated description',
|
||||
path: `/a/${pluginId}/updated-path`,
|
||||
icon: 'search',
|
||||
category: 'Machine Learning',
|
||||
}));
|
||||
|
||||
const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]);
|
||||
const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 });
|
||||
const [extension] = extensions;
|
||||
|
||||
assertPluginExtensionLink(extension);
|
||||
|
||||
expect(link2.configure).toHaveBeenCalledTimes(1);
|
||||
expect(extension.path).toBe(
|
||||
`/a/${pluginId}/updated-path?uel_pid=grafana-basic-app&uel_epid=plugins%2Fmyorg-basic-app%2Fstart`
|
||||
);
|
||||
});
|
||||
|
||||
test('should ignore restricted properties passed via the configure() function', () => {
|
||||
link2.configure = jest.fn().mockImplementation(() => ({
|
||||
// The following props are not allowed to override
|
||||
@ -332,7 +362,7 @@ describe('getPluginExtensions()', () => {
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('should should not make original context read only', () => {
|
||||
test('should not make original context read only', () => {
|
||||
const context = {
|
||||
title: 'New title from the context!',
|
||||
nested: { title: 'title' },
|
||||
@ -348,4 +378,35 @@ describe('getPluginExtensions()', () => {
|
||||
context.array.push('b');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('should report interaction when onClick is triggered', () => {
|
||||
const reportInteractionMock = jest.mocked(reportInteraction);
|
||||
|
||||
const registry = createPluginExtensionRegistry([
|
||||
{
|
||||
pluginId,
|
||||
extensionConfigs: [
|
||||
{
|
||||
...link1,
|
||||
path: undefined,
|
||||
onClick: jest.fn(),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint1 });
|
||||
const [extension] = extensions;
|
||||
|
||||
assertPluginExtensionLink(extension);
|
||||
|
||||
extension.onClick?.();
|
||||
|
||||
expect(reportInteractionMock).toBeCalledTimes(1);
|
||||
expect(reportInteractionMock).toBeCalledWith('ui_extension_link_clicked', {
|
||||
pluginId: extension.pluginId,
|
||||
extensionPointId: extensionPoint1,
|
||||
title: extension.title,
|
||||
category: extension.category,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { isString } from 'lodash';
|
||||
|
||||
import {
|
||||
type PluginExtension,
|
||||
PluginExtensionTypes,
|
||||
type PluginExtensionLink,
|
||||
type PluginExtensionLinkConfig,
|
||||
type PluginExtensionComponent,
|
||||
urlUtil,
|
||||
} from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
|
||||
import type { PluginExtensionRegistry } from './types';
|
||||
import {
|
||||
@ -60,24 +64,25 @@ export const getPluginExtensions: GetExtensions = ({ context, extensionPointId,
|
||||
// LINK
|
||||
if (isPluginExtensionLinkConfig(extensionConfig)) {
|
||||
// Run the configure() function with the current context, and apply the ovverides
|
||||
const overrides = getLinkExtensionOverrides(registryItem.pluginId, extensionConfig, frozenContext);
|
||||
const overrides = getLinkExtensionOverrides(pluginId, extensionConfig, frozenContext);
|
||||
|
||||
// configure() returned an `undefined` -> hide the extension
|
||||
if (extensionConfig.configure && overrides === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const path = overrides?.path || extensionConfig.path;
|
||||
const extension: PluginExtensionLink = {
|
||||
id: generateExtensionId(registryItem.pluginId, extensionConfig),
|
||||
id: generateExtensionId(pluginId, extensionConfig),
|
||||
type: PluginExtensionTypes.link,
|
||||
pluginId: registryItem.pluginId,
|
||||
onClick: getLinkExtensionOnClick(extensionConfig, frozenContext),
|
||||
pluginId: pluginId,
|
||||
onClick: getLinkExtensionOnClick(pluginId, extensionConfig, frozenContext),
|
||||
|
||||
// Configurable properties
|
||||
icon: overrides?.icon || extensionConfig.icon,
|
||||
title: overrides?.title || extensionConfig.title,
|
||||
description: overrides?.description || extensionConfig.description,
|
||||
path: overrides?.path || extensionConfig.path,
|
||||
path: isString(path) ? getLinkExtensionPathWithTracking(pluginId, path, extensionConfig) : undefined,
|
||||
category: overrides?.category || extensionConfig.category,
|
||||
};
|
||||
|
||||
@ -165,6 +170,7 @@ function getLinkExtensionOverrides(pluginId: string, config: PluginExtensionLink
|
||||
}
|
||||
|
||||
function getLinkExtensionOnClick(
|
||||
pluginId: string,
|
||||
config: PluginExtensionLinkConfig,
|
||||
context?: object
|
||||
): ((event?: React.MouseEvent) => void) | undefined {
|
||||
@ -176,6 +182,13 @@ function getLinkExtensionOnClick(
|
||||
|
||||
return function onClickExtensionLink(event?: React.MouseEvent) {
|
||||
try {
|
||||
reportInteraction('ui_extension_link_clicked', {
|
||||
pluginId: pluginId,
|
||||
extensionPointId: config.extensionPointId,
|
||||
title: config.title,
|
||||
category: config.category,
|
||||
});
|
||||
|
||||
const result = onClick(event, getEventHelpers(context));
|
||||
|
||||
if (isPromise(result)) {
|
||||
@ -192,3 +205,13 @@ function getLinkExtensionOnClick(
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getLinkExtensionPathWithTracking(pluginId: string, path: string, config: PluginExtensionLinkConfig): string {
|
||||
return urlUtil.appendQueryToUrl(
|
||||
path,
|
||||
urlUtil.toUrlParams({
|
||||
uel_pid: pluginId,
|
||||
uel_epid: config.extensionPointId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user