Skip to content

Commit 1b8d313

Browse files
Fall back to a generated telemetry event name when creating new widget (#27164)
* fix(telemetry): fall back to a generated event name in CreateNewWidgetModal The dashboard "+" create-widget modal looked up the telemetry event name for a widget type directly in TELEMETRY_EVENT_TYPE.SEARCH_WIDGET_CREATE with no fallback. Since that map only defines 4 of the registered widget types, creating a Custom Aggregation, Events Overview, or Text/Markdown widget (or any plugin-provided widgetCreator not in the map) sent sendTelemetry(undefined, ...). Mirror the fallback already used by the sibling AddWidgetButton component. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(telemetry): update CreateNewWidgetModal test expectations after #27163 useSendTelemetry now prefills app_section internally, so call sites no longer pass it explicitly. Merging master (#27163) into this branch updated CreateNewWidgetModal.tsx accordingly, but left the test's assertions expecting the old explicit app_section argument. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 4ee53ac commit 1b8d313

2 files changed

Lines changed: 103 additions & 2 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (C) 2020 Graylog, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*/
17+
import * as React from 'react';
18+
import { render, screen } from 'wrappedTestingLibrary';
19+
import type { PluginExports } from 'graylog-web-plugin/plugin';
20+
import userEvent from '@testing-library/user-event';
21+
22+
import { asMock } from 'helpers/mocking';
23+
import useViewsDispatch from 'views/stores/useViewsDispatch';
24+
import useView from 'views/hooks/useView';
25+
import { createSearch } from 'fixtures/searches';
26+
import mockDispatch from 'views/test/mockDispatch';
27+
import type { RootState } from 'views/types';
28+
import { usePlugin } from 'views/test/testPlugins';
29+
import Icon from 'components/common/Icon';
30+
import { CreateMessageCount } from 'views/logic/fieldactions/AddMessageCountActionHandler';
31+
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
32+
import WidgetPosition from 'views/logic/widgets/WidgetPosition';
33+
34+
import CreateNewWidgetModal from './CreateNewWidgetModal';
35+
36+
jest.mock('views/stores/useViewsDispatch');
37+
jest.mock('views/hooks/useView');
38+
jest.mock('logic/telemetry/useSendTelemetry');
39+
jest.mock('views/logic/slices/widgetActions', () => ({
40+
addWidget: jest.fn(() => async () => {}),
41+
}));
42+
43+
const bindings: PluginExports = {
44+
widgetCreators: [
45+
{
46+
title: 'Message Count',
47+
func: CreateMessageCount,
48+
icon: () => <Icon name="tag" />,
49+
},
50+
{
51+
title: 'Custom Aggregation',
52+
func: CreateMessageCount,
53+
icon: () => <Icon name="monitoring" />,
54+
},
55+
],
56+
};
57+
58+
const plugin = {
59+
exports: bindings,
60+
metadata: {
61+
name: 'Dummy Plugin for Tests',
62+
},
63+
};
64+
65+
describe('CreateNewWidgetModal', () => {
66+
const sendTelemetry = jest.fn();
67+
const onCancel = jest.fn();
68+
const position = WidgetPosition.builder().col(1).row(1).height(1).width(1).build();
69+
70+
beforeEach(() => {
71+
const view = createSearch();
72+
const dispatch = mockDispatch({ view: { view, activeQuery: 'query-id-1' } } as RootState);
73+
asMock(useViewsDispatch).mockReturnValue(dispatch);
74+
asMock(useView).mockReturnValue(view);
75+
asMock(useSendTelemetry).mockReturnValue(sendTelemetry);
76+
sendTelemetry.mockClear();
77+
});
78+
79+
usePlugin(plugin);
80+
81+
it('sends telemetry with the matching event name for a widget type known to the telemetry constants', async () => {
82+
render(<CreateNewWidgetModal onCancel={onCancel} position={position} />);
83+
84+
const button = await screen.findByRole('button', { name: /Create Message Count Widget/i });
85+
await userEvent.click(button);
86+
87+
expect(sendTelemetry).toHaveBeenCalledWith('Search Widget Message Count Created', {});
88+
});
89+
90+
it('falls back to a generated event name instead of sending an undefined event type for an unmapped widget type', async () => {
91+
render(<CreateNewWidgetModal onCancel={onCancel} position={position} />);
92+
93+
const button = await screen.findByRole('button', { name: /Create Custom Aggregation Widget/i });
94+
await userEvent.click(button);
95+
96+
expect(sendTelemetry).toHaveBeenCalledWith('Search Widget Custom Aggregation Created', {});
97+
});
98+
});

‎graylog2-web-interface/src/views/components/CreateNewWidgetModal.tsx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@ const CreateNewWidgetModal = ({ onCancel, position }: Props) => {
8181
() =>
8282
creators.map(({ title, func, icon: WidgetIcon }) => {
8383
const onClick = async () => {
84-
sendTelemetry(TELEMETRY_EVENT_TYPE.SEARCH_WIDGET_CREATE[upperCase(title).replace(/ /g, '_')], {
85-
});
84+
sendTelemetry(
85+
TELEMETRY_EVENT_TYPE.SEARCH_WIDGET_CREATE[upperCase(title).replace(/ /g, '_')] ??
86+
`Search Widget ${title} Created`,
87+
{},
88+
);
8689

8790
const newId = generateId();
8891
const newWidget = func({ view }).toBuilder().id(newId).build();

0 commit comments

Comments
 (0)