From d6bb94cbfcb2ebac6198a647e9002c5ddd7e4df4 Mon Sep 17 00:00:00 2001 From: Ruxandra Machedon Date: Mon, 11 Mar 2024 18:22:13 -0400 Subject: [PATCH] feat: standardize event tracking for link sharing add a common analytics event for sharing and reuse it in both copy-link share button usages in the application. add a test for the new common code. --- src/common/components/SocialShare.js | 8 +-- src/monitoring/events.js | 31 +++++++++++ src/monitoring/events.test.js | 52 +++++++++++++++++++ .../components/SharedDataEntryFile.js | 5 +- src/sharedData/sharedDataHooks.js | 4 +- 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 src/monitoring/events.js create mode 100644 src/monitoring/events.test.js diff --git a/src/common/components/SocialShare.js b/src/common/components/SocialShare.js index 8bd4c7b8c..d5bcfa437 100644 --- a/src/common/components/SocialShare.js +++ b/src/common/components/SocialShare.js @@ -36,7 +36,7 @@ import useMediaQuery from '@mui/material/useMediaQuery'; import { useCopy } from 'custom-hooks'; -import { useAnalytics } from 'monitoring/analytics'; +import { useShareEvent } from 'monitoring/events'; import CopyLink from './CopyLink'; @@ -261,7 +261,7 @@ const PostToService = props => { const SocialShare = props => { const { buttonProps } = props; const { t } = useTranslation(); - const { trackEvent } = useAnalytics(); + const { onShare } = useShareEvent(); const { socialShareProps } = useContext(SocialShareContext); const { name } = socialShareProps; const [open, setOpen] = useState(false); @@ -280,10 +280,6 @@ const SocialShare = props => { } }; - const onShare = method => { - trackEvent('share', { props: { url: pageUrl.toString(), method } }); - }; - if (!name) { return null; } diff --git a/src/monitoring/events.js b/src/monitoring/events.js new file mode 100644 index 000000000..23a003bc1 --- /dev/null +++ b/src/monitoring/events.js @@ -0,0 +1,31 @@ +/* + * Copyright © 2021-2024 Technology Matters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import { useMemo } from 'react'; + +import { useAnalytics } from 'monitoring/analytics'; + +export const useShareEvent = () => { + const { trackEvent } = useAnalytics(); + const pageUrl = useMemo(() => window.location, []); + + const onShare = method => { + trackEvent('share', { props: { url: pageUrl.toString(), method } }); + }; + + return { onShare }; +}; diff --git a/src/monitoring/events.test.js b/src/monitoring/events.test.js new file mode 100644 index 000000000..cad6e25e5 --- /dev/null +++ b/src/monitoring/events.test.js @@ -0,0 +1,52 @@ +/* + * Copyright © 2021-2023 Technology Matters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ +import { render } from 'tests/utils'; + +import { useAnalytics } from 'monitoring/analytics'; +import { useShareEvent } from 'monitoring/events'; + +jest.mock('monitoring/analytics', () => ({ + ...jest.requireActual('monitoring/analytics'), + useAnalytics: jest.fn(), +})); + +const Component = () => { + const { onShare } = useShareEvent(); + + onShare('test'); + return
; +}; + +test('Events: onShare', async () => { + const trackEvent = jest.fn(); + useAnalytics.mockReturnValue({ + trackEvent, + }); + + const expectedUrl = window.location.toString(); + + await render(); + + const eventCall = trackEvent.mock.calls[0]; + expect(eventCall[0]).toStrictEqual('share'); + expect(eventCall[1]).toStrictEqual({ + props: { + method: 'test', + url: expectedUrl, + }, + }); +}); diff --git a/src/sharedData/components/SharedDataEntryFile.js b/src/sharedData/components/SharedDataEntryFile.js index 35f69a9c7..4afbc330a 100644 --- a/src/sharedData/components/SharedDataEntryFile.js +++ b/src/sharedData/components/SharedDataEntryFile.js @@ -49,6 +49,7 @@ import { useCollaborationContext } from 'collaboration/collaborationContext'; import CopyLink from 'common/components/CopyLink'; import RouterLink from 'common/components/RouterLink'; import { formatDate } from 'localization/utils'; +import { useShareEvent } from 'monitoring/events'; import { SHARE_ACCESS_ALL, SHARE_ACCESS_TYPES, @@ -162,6 +163,8 @@ const ShareDialog = props => { } }; + const { onShare } = useShareEvent(); + const onChange = useCallback( event => { setShowUpdateSuccess(false); @@ -280,7 +283,7 @@ const ShareDialog = props => { )} - + { const downloadFile = useCallback( file => { trackEvent('dataEntry.file.download', { - props: { [entityType]: owner.slug }, + props: { + [entityType]: owner.slug, + }, }); window.open(file.url, '_blank'); },