diff --git a/packages/grafana-runtime/src/utils/useFavoriteDatasources.test.ts b/packages/grafana-runtime/src/utils/useFavoriteDatasources.test.ts index 9f589cf8846..ffe37ddbb2e 100644 --- a/packages/grafana-runtime/src/utils/useFavoriteDatasources.test.ts +++ b/packages/grafana-runtime/src/utils/useFavoriteDatasources.test.ts @@ -11,8 +11,8 @@ const mockSetItem = jest.fn(); jest.mock('./userStorage', () => { return { UserStorage: jest.fn().mockImplementation(() => ({ - getItem: mockGetItem, - setItem: mockSetItem, + getItem: (key: string) => mockGetItem(key), + setItem: (key: string, value: string) => mockSetItem(key, value), })), }; }); diff --git a/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts b/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts index bbb47da67e7..20233bbf1c7 100644 --- a/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts +++ b/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts @@ -18,6 +18,9 @@ export type FavoriteDatasources = { isFavoriteDatasource: (dsUid: string) => boolean; }; +// Initialize user storage as a singleton +const userStorage = new UserStorage('grafana-runtime'); + /** * A hook for managing favorite data sources using user storage. * This hook provides functionality to store and retrieve a list of favorite data source UIDs @@ -45,7 +48,6 @@ export function useFavoriteDatasources(): FavoriteDatasources { }; } - const [userStorage] = useState(() => new UserStorage('grafana-runtime')); const [favoriteDatasources, setFavoriteDatasources] = useState([]); const [initialFavoriteDataSources, setInitialFavoriteDataSources] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -64,18 +66,15 @@ export function useFavoriteDatasources(): FavoriteDatasources { }; loadFavorites(); - }, [userStorage]); + }, []); // Helper function to save favorites to storage - const saveFavorites = useCallback( - async (newFavorites: string[]) => { - setIsLoading(true); - await userStorage.setItem(FAVORITE_DATASOURCES_KEY, JSON.stringify(newFavorites)); - setFavoriteDatasources(newFavorites); - setIsLoading(false); - }, - [userStorage] - ); + const saveFavorites = useCallback(async (newFavorites: string[]) => { + setIsLoading(true); + await userStorage.setItem(FAVORITE_DATASOURCES_KEY, JSON.stringify(newFavorites)); + setFavoriteDatasources(newFavorites); + setIsLoading(false); + }, []); const addFavoriteDatasource = useCallback( (ds: DataSourceInstanceSettings) => { diff --git a/public/app/features/datasources/components/picker/DataSourceList.tsx b/public/app/features/datasources/components/picker/DataSourceList.tsx index 139cff29df4..77e332dbc5f 100644 --- a/public/app/features/datasources/components/picker/DataSourceList.tsx +++ b/public/app/features/datasources/components/picker/DataSourceList.tsx @@ -6,13 +6,14 @@ import { Observable } from 'rxjs'; import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceRef, GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Trans } from '@grafana/i18n'; -import { getTemplateSrv, useFavoriteDatasources } from '@grafana/runtime'; +import { getTemplateSrv, reportInteraction, useFavoriteDatasources } from '@grafana/runtime'; import { useStyles2, useTheme2 } from '@grafana/ui'; import { useDatasources, useKeyboardNavigatableList, useRecentlyUsedDataSources } from '../../hooks'; import { AddNewDataSourceButton } from './AddNewDataSourceButton'; import { DataSourceCard } from './DataSourceCard'; +import { INTERACTION_EVENT_NAME, INTERACTION_ITEM } from './DataSourcePicker'; import { getDataSourceCompareFn, isDataSourceMatch } from './utils'; /** @@ -107,10 +108,15 @@ export function DataSourceList(props: DataSourceListProps) { onChange(ds); }} selected={isDataSourceMatch(ds, current)} - isFavorite={favoriteDataSources.isFavoriteDatasource(ds.uid)} + isFavorite={favoriteDataSources.enabled ? favoriteDataSources.isFavoriteDatasource(ds.uid) : undefined} onToggleFavorite={ favoriteDataSources.enabled ? () => { + reportInteraction(INTERACTION_EVENT_NAME, { + item: INTERACTION_ITEM.TOGGLE_FAVORITE, + ds_type: ds.type, + is_favorite: !favoriteDataSources.isFavoriteDatasource(ds.uid), + }); favoriteDataSources.isFavoriteDatasource(ds.uid) ? favoriteDataSources.removeFavoriteDatasource(ds) : favoriteDataSources.addFavoriteDatasource(ds); diff --git a/public/app/features/datasources/components/picker/DataSourcePicker.tsx b/public/app/features/datasources/components/picker/DataSourcePicker.tsx index 37bdc3079ef..9961af98afc 100644 --- a/public/app/features/datasources/components/picker/DataSourcePicker.tsx +++ b/public/app/features/datasources/components/picker/DataSourcePicker.tsx @@ -11,7 +11,7 @@ import { Observable } from 'rxjs'; import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; -import { reportInteraction } from '@grafana/runtime'; +import { reportInteraction, useFavoriteDatasources } from '@grafana/runtime'; import { DataQuery, DataSourceJsonData, DataSourceRef } from '@grafana/schema'; import { Button, Icon, Input, ModalsController, Portal, ScrollContainer, useStyles2 } from '@grafana/ui'; import config from 'app/core/config'; @@ -25,14 +25,15 @@ import { DataSourceLogo, DataSourceLogoPlaceHolder } from './DataSourceLogo'; import { DataSourceModal } from './DataSourceModal'; import { dataSourceLabel, matchDataSourceWithSearch } from './utils'; -const INTERACTION_EVENT_NAME = 'dashboards_dspicker_clicked'; -const INTERACTION_ITEM = { +export const INTERACTION_EVENT_NAME = 'dashboards_dspicker_clicked'; +export const INTERACTION_ITEM = { SEARCH: 'search', OPEN_DROPDOWN: 'open_dspicker', SELECT_DS: 'select_ds', ADD_FILE: 'add_file', OPEN_ADVANCED_DS_PICKER: 'open_advanced_ds_picker', CONFIG_NEW_DS_EMPTY_STATE: 'config_new_ds_empty_state', + TOGGLE_FAVORITE: 'toggle_favorite', }; export interface DataSourcePickerProps { @@ -114,6 +115,7 @@ export function DataSourcePicker(props: DataSourcePickerProps) { type: props.type, variables: props.variables, }); + const favoriteDataSources = useFavoriteDatasources(); // the order of middleware is important! const middleware = [ @@ -298,6 +300,9 @@ export function DataSourcePicker(props: DataSourcePickerProps) { reportInteraction(INTERACTION_EVENT_NAME, { item: INTERACTION_ITEM.SELECT_DS, ds_type: ds.type, + is_favorite: favoriteDataSources.enabled + ? favoriteDataSources.isFavoriteDatasource(ds.uid) + : undefined, }); } }}