Analytics: send dashboard_list_viewed events to rudderstack (#44589)

* #44449: report interactions with showPreviews toggle

* #44449: hookify showPreviews

* #44449: report dashboard_list_viewed event rather than enabled/disabled_dashboard_previews
This commit is contained in:
Artur Wierzbicki 2022-01-31 22:17:05 +04:00 committed by GitHub
parent e39d43db0a
commit 6e1615b42a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 72 additions and 28 deletions

View File

@ -1,6 +1,6 @@
import React from 'react';
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { locationService } from '@grafana/runtime';
import { locationService, setEchoSrv } from '@grafana/runtime';
import { selectOptionInTest } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
@ -9,6 +9,7 @@ import * as MockSearchSrv from 'app/core/services/__mocks__/search_srv';
import { DashboardSearch, Props } from './DashboardSearch';
import { searchResults } from '../testData';
import { SearchLayout } from '../types';
import { Echo } from 'app/core/services/echo/Echo';
jest.mock('app/core/services/search_srv');
// Typecast the mock search so the mock import is correctly recognised by TS
@ -38,6 +39,10 @@ const setup = (testProps?: Partial<Props>) => {
* calls inside useDebounce hook
*/
describe('DashboardSearch', () => {
beforeAll(() => {
setEchoSrv(new Echo());
});
it('should call search api with default query when initialised', async () => {
locationService.push('/');
setup();

View File

@ -1,15 +1,12 @@
import React, { FC, memo } from 'react';
import { useLocalStorage } from 'react-use';
import { css } from '@emotion/css';
import { useTheme2, CustomScrollbar, stylesFactory, IconButton } from '@grafana/ui';
import { CustomScrollbar, IconButton, stylesFactory, useTheme2 } from '@grafana/ui';
import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useSearchQuery } from '../hooks/useSearchQuery';
import { useDashboardSearch } from '../hooks/useDashboardSearch';
import { SearchField } from './SearchField';
import { SearchResults } from './SearchResults';
import { ActionRow } from './ActionRow';
import { PREVIEWS_LOCAL_STORAGE_KEY } from '../constants';
export interface Props {
onCloseSearch: () => void;
@ -17,14 +14,12 @@ export interface Props {
export const DashboardSearch: FC<Props> = memo(({ onCloseSearch }) => {
const { query, onQueryChange, onTagFilterChange, onTagAdd, onSortChange, onLayoutChange } = useSearchQuery({});
const { results, loading, onToggleSection, onKeyDown } = useDashboardSearch(query, onCloseSearch);
const { results, loading, onToggleSection, onKeyDown, showPreviews, onShowPreviewsChange } = useDashboardSearch(
query,
onCloseSearch
);
const theme = useTheme2();
const styles = getStyles(theme);
const previewsEnabled = config.featureToggles.dashboardPreviews;
const [showPreviews, setShowPreviews] = useLocalStorage<boolean>(PREVIEWS_LOCAL_STORAGE_KEY, previewsEnabled);
const onShowPreviewsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setShowPreviews(event.target.checked);
};
return (
<div tabIndex={0} className={styles.overlay}>
@ -39,7 +34,7 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch }) => {
<ActionRow
{...{
onLayoutChange,
onShowPreviewsChange,
onShowPreviewsChange: (ev) => onShowPreviewsChange(ev.target.checked),
onSortChange,
onTagFilterChange,
query,
@ -54,7 +49,7 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch }) => {
editable={false}
onToggleSection={onToggleSection}
layout={query.layout}
showPreviews={previewsEnabled && showPreviews}
showPreviews={showPreviews}
/>
</CustomScrollbar>
</div>

View File

@ -1,9 +1,7 @@
import React, { FC, memo, useState } from 'react';
import { useLocalStorage } from 'react-use';
import { css } from '@emotion/css';
import { stylesFactory, useTheme, Spinner, FilterInput } from '@grafana/ui';
import { FilterInput, Spinner, stylesFactory, useTheme } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { config } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import { FolderDTO } from 'app/types';
@ -15,7 +13,6 @@ import { useSearchQuery } from '../hooks/useSearchQuery';
import { SearchResultsFilter } from './SearchResultsFilter';
import { SearchResults } from './SearchResults';
import { DashboardActions } from './DashboardActions';
import { PREVIEWS_LOCAL_STORAGE_KEY } from '../constants';
export interface Props {
folder?: FolderDTO;
@ -24,11 +21,6 @@ export interface Props {
const { isEditor } = contextSrv;
export const ManageDashboards: FC<Props> = memo(({ folder }) => {
const previewsEnabled = Boolean(config.featureToggles.dashboardPreviews);
const [showPreviews, setShowPreviews] = useLocalStorage<boolean>(PREVIEWS_LOCAL_STORAGE_KEY, true);
const onShowPreviewsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setShowPreviews(event.target.checked);
};
const folderId = folder?.id;
const folderUid = folder?.uid;
const theme = useTheme();
@ -69,6 +61,8 @@ export const ManageDashboards: FC<Props> = memo(({ folder }) => {
onDeleteItems,
onMoveItems,
noFolders,
showPreviews,
onShowPreviewsChange,
} = useManageDashboards(query, {}, folder);
const onMoveTo = () => {
@ -114,13 +108,13 @@ export const ManageDashboards: FC<Props> = memo(({ folder }) => {
canMove={hasEditPermissionInFolders && canMove}
deleteItem={onItemDelete}
moveTo={onMoveTo}
onShowPreviewsChange={onShowPreviewsChange}
onShowPreviewsChange={(ev) => onShowPreviewsChange(ev.target.checked)}
onToggleAllChecked={onToggleAllChecked}
onStarredFilterChange={onStarredFilterChange}
onSortChange={onSortChange}
onTagFilterChange={onTagFilterChange}
query={query}
showPreviews={previewsEnabled && showPreviews}
showPreviews={showPreviews}
hideLayout={!!folderUid}
onLayoutChange={onLayoutChange}
editable={hasEditPermissionInFolders}
@ -133,7 +127,7 @@ export const ManageDashboards: FC<Props> = memo(({ folder }) => {
onToggleSection={onToggleSection}
onToggleChecked={onToggleChecked}
layout={query.layout}
showPreviews={previewsEnabled && showPreviews}
showPreviews={showPreviews}
/>
</div>
<ConfirmDeleteModal

View File

@ -1,4 +1,4 @@
import { KeyboardEvent, useReducer } from 'react';
import { KeyboardEvent, useEffect, useReducer } from 'react';
import { getLocationSrv } from '@grafana/runtime';
import { DashboardQuery, DashboardSearchItemType, DashboardSection } from '../types';
import { MOVE_SELECTION_DOWN, MOVE_SELECTION_UP } from '../reducers/actionTypes';
@ -6,15 +6,22 @@ import { dashboardsSearchState, DashboardsSearchState, searchReducer } from '../
import { findSelected } from '../utils';
import { useSearch } from './useSearch';
import { locationUtil } from '@grafana/data';
import { useShowDashboardPreviews } from './useShowDashboardPreviews';
import { reportDashboardListViewed } from './useManageDashboards';
export const useDashboardSearch = (query: DashboardQuery, onCloseSearch: () => void) => {
const reducer = useReducer(searchReducer, dashboardsSearchState);
const { showPreviews, onShowPreviewsChange, previewFeatureEnabled } = useShowDashboardPreviews();
const {
state: { results, loading },
onToggleSection,
dispatch,
} = useSearch<DashboardsSearchState>(query, reducer, { queryParsing: true });
useEffect(() => {
reportDashboardListViewed('dashboard_search', showPreviews, previewFeatureEnabled, query.layout);
}, [showPreviews, previewFeatureEnabled, query.layout]);
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
switch (event.key) {
case 'Escape':
@ -47,5 +54,7 @@ export const useDashboardSearch = (query: DashboardQuery, onCloseSearch: () => v
loading,
onToggleSection,
onKeyDown,
showPreviews,
onShowPreviewsChange,
};
};

View File

@ -6,11 +6,17 @@ import { DashboardQuery, DashboardSearchItemType, DashboardSection, SearchAction
import { ManageDashboardsState } from '../reducers/manageDashboards';
import { useManageDashboards } from './useManageDashboards';
import { GENERAL_FOLDER_ID } from '../constants';
import { setEchoSrv } from '@grafana/runtime/src';
import { Echo } from 'app/core/services/echo/Echo';
describe('useManageDashboards', () => {
const useSearchMock = jest.spyOn(useSearch, 'useSearch');
const toggle = async (section: DashboardSection) => section;
beforeAll(() => {
setEchoSrv(new Echo());
});
function setupTestContext({ results = [] }: { results?: DashboardSection[] } = {}) {
jest.clearAllMocks();

View File

@ -1,16 +1,31 @@
import { useCallback, useMemo, useReducer } from 'react';
import { useCallback, useEffect, useMemo, useReducer } from 'react';
import { FolderDTO } from 'app/types';
import { contextSrv } from 'app/core/services/context_srv';
import { DashboardQuery, DashboardSection, OnDeleteItems, OnMoveItems, OnToggleChecked } from '../types';
import { DashboardQuery, DashboardSection, OnDeleteItems, OnMoveItems, OnToggleChecked, SearchLayout } from '../types';
import { DELETE_ITEMS, MOVE_ITEMS, TOGGLE_ALL_CHECKED, TOGGLE_CHECKED } from '../reducers/actionTypes';
import { manageDashboardsReducer, manageDashboardsState, ManageDashboardsState } from '../reducers/manageDashboards';
import { useSearch } from './useSearch';
import { GENERAL_FOLDER_ID } from '../constants';
import { useShowDashboardPreviews } from './useShowDashboardPreviews';
import { reportInteraction } from '@grafana/runtime/src';
const hasChecked = (section: DashboardSection) => {
return section.checked || section.items.some((item) => item.checked);
};
export const reportDashboardListViewed = (
dashboardListType: 'manage_dashboards' | 'dashboard_search',
showPreviews: boolean,
previewsEnabled: boolean,
searchLayout: SearchLayout
) => {
const previews = previewsEnabled ? (showPreviews ? 'on' : 'off') : 'feature_disabled';
reportInteraction(`${dashboardListType}_viewed`, {
previews,
layout: searchLayout,
});
};
export const useManageDashboards = (
query: DashboardQuery,
state: Partial<ManageDashboardsState> = {},
@ -21,6 +36,11 @@ export const useManageDashboards = (
...state,
});
const { showPreviews, onShowPreviewsChange, previewFeatureEnabled } = useShowDashboardPreviews();
useEffect(() => {
reportDashboardListViewed('manage_dashboards', showPreviews, previewFeatureEnabled, query.layout);
}, [showPreviews, previewFeatureEnabled, query.layout]);
const {
state: { results, loading, initialLoading, allChecked },
onToggleSection,
@ -73,5 +93,7 @@ export const useManageDashboards = (
onDeleteItems,
onMoveItems,
noFolders,
showPreviews,
onShowPreviewsChange,
};
};

View File

@ -0,0 +1,13 @@
import { PREVIEWS_LOCAL_STORAGE_KEY } from '../constants';
import { config } from '@grafana/runtime/src';
import { useLocalStorage } from 'react-use';
export const useShowDashboardPreviews = () => {
const previewFeatureEnabled = Boolean(config.featureToggles.dashboardPreviews);
const [showPreviews, setShowPreviews] = useLocalStorage<boolean>(PREVIEWS_LOCAL_STORAGE_KEY, previewFeatureEnabled);
const onShowPreviewsChange = (showPreviews: boolean) => {
setShowPreviews(showPreviews);
};
return { showPreviews: Boolean(showPreviews && previewFeatureEnabled), previewFeatureEnabled, onShowPreviewsChange };
};