diff --git a/packages/fxa-content-server/app/scripts/lib/glean/index.ts b/packages/fxa-content-server/app/scripts/lib/glean/index.ts index ac947449d5..468f94d2af 100644 --- a/packages/fxa-content-server/app/scripts/lib/glean/index.ts +++ b/packages/fxa-content-server/app/scripts/lib/glean/index.ts @@ -353,6 +353,7 @@ export const GleanMetrics = { // this ensures that events are uploaded. maxEvents: 1, enableAutoPageLoadEvents: true, + enableAutoElementClickEvents: true, }); Glean.setLogPings(config.logPings); if (config.debugViewTag) { diff --git a/packages/fxa-content-server/app/tests/spec/lib/glean.js b/packages/fxa-content-server/app/tests/spec/lib/glean.js index 86fd35fa88..255882bf7d 100644 --- a/packages/fxa-content-server/app/tests/spec/lib/glean.js +++ b/packages/fxa-content-server/app/tests/spec/lib/glean.js @@ -174,6 +174,7 @@ describe('lib/glean', () => { serverEndpoint: mockConfig.serverEndpoint, maxEvents: 1, enableAutoPageLoadEvents: true, + enableAutoElementClickEvents: true, } ); sinon.assert.calledWith(logPingsStub, mockConfig.logPings); diff --git a/packages/fxa-settings/src/components/DataBlock/index.tsx b/packages/fxa-settings/src/components/DataBlock/index.tsx index 3dd57852b8..d8cd360c5a 100644 --- a/packages/fxa-settings/src/components/DataBlock/index.tsx +++ b/packages/fxa-settings/src/components/DataBlock/index.tsx @@ -7,6 +7,7 @@ import GetDataTrio, { DownloadContentType, GetDataCopySingleton, GetDataCopySingletonInline, + GetDataTrioGleanData, } from '../GetDataTrio'; import { Tooltip } from '../Tooltip'; import { FtlMsg } from 'fxa-react/lib/utils'; @@ -30,6 +31,11 @@ export type DataBlockProps = { isIOS?: boolean; isInline?: boolean; email: string; + gleanDataAttrs: { + copy?: GetDataTrioGleanData; + download?: GetDataTrioGleanData; + print?: GetDataTrioGleanData; + }; }; export const DataBlock = ({ @@ -42,6 +48,7 @@ export const DataBlock = ({ isIOS = false, isInline = false, email, + gleanDataAttrs, }: DataBlockProps) => { const valueIsArray = Array.isArray(value); const [performedAction, setPerformedAction] = useState(); @@ -97,13 +104,25 @@ export const DataBlock = ({ )} {isInline && ( )} {isIOS && !isInline && ( )} {!isIOS && !isInline && ( @@ -114,6 +133,7 @@ export const DataBlock = ({ onAction: actionCb, setTooltipVisible, email, + gleanDataAttrs, }} /> )} diff --git a/packages/fxa-settings/src/components/DataBlock/mocks.tsx b/packages/fxa-settings/src/components/DataBlock/mocks.tsx index bd4e943e6f..412cb0ad86 100644 --- a/packages/fxa-settings/src/components/DataBlock/mocks.tsx +++ b/packages/fxa-settings/src/components/DataBlock/mocks.tsx @@ -10,5 +10,5 @@ export const Subject = ({ value = 'ANMD 1S09 7Y2Y 4EES 02CW BJ6Z PYKP H69F', ...props // overrides }: Partial = {}) => ( - + ); diff --git a/packages/fxa-settings/src/components/GetDataTrio/index.stories.tsx b/packages/fxa-settings/src/components/GetDataTrio/index.stories.tsx index 5e97f643f3..82b8fa3f2a 100644 --- a/packages/fxa-settings/src/components/GetDataTrio/index.stories.tsx +++ b/packages/fxa-settings/src/components/GetDataTrio/index.stories.tsx @@ -25,6 +25,7 @@ export const Default = () => { value="Copy that" email={MOCK_EMAIL} {...{ setTooltipVisible }} + gleanDataAttrs={{}} /> ); @@ -38,6 +39,7 @@ export const SingleCopyButton = () => { value="Copy that" email={MOCK_EMAIL} {...{ setTooltipVisible }} + gleanDataAttrs={{}} /> ); @@ -51,6 +53,7 @@ export const SingleCopyButtonInline = () => { value="Copy that" email={MOCK_EMAIL} {...{ setTooltipVisible }} + gleanDataAttrs={{}} /> ); diff --git a/packages/fxa-settings/src/components/GetDataTrio/index.test.tsx b/packages/fxa-settings/src/components/GetDataTrio/index.test.tsx index 059969bd11..7c2c35d70a 100644 --- a/packages/fxa-settings/src/components/GetDataTrio/index.test.tsx +++ b/packages/fxa-settings/src/components/GetDataTrio/index.test.tsx @@ -20,6 +20,7 @@ it('renders Trio as expected', () => { ); expect(screen.getByTestId('databutton-download')).toBeInTheDocument(); @@ -37,6 +38,7 @@ it('renders single Copy button as expected', () => { ); expect(screen.getByTestId('databutton-copy')).toBeInTheDocument(); diff --git a/packages/fxa-settings/src/components/GetDataTrio/index.tsx b/packages/fxa-settings/src/components/GetDataTrio/index.tsx index 63435c407c..36c1334dd5 100644 --- a/packages/fxa-settings/src/components/GetDataTrio/index.tsx +++ b/packages/fxa-settings/src/components/GetDataTrio/index.tsx @@ -23,18 +23,33 @@ const DownloadContentTypeL10nMapping: Record = { 'Firefox account recovery key': 'get-data-trio-title-firefox-recovery-key', }; +export interface GetDataTrioGleanData { + id: + | 'two_step_auth_codes_download' + | 'two_step_auth_codes_copy' + | 'two_step_auth_codes_print' + | 'account_pref_recovery_key_copy'; + type?: 'setup' | 'inline setup' | 'replace'; +} + export type GetDataTrioProps = { value: string | string[]; contentType?: DownloadContentType; onAction?: (type: 'download' | 'copy' | 'print') => void; setTooltipVisible: React.Dispatch>; email: string; + gleanDataAttrs: { + copy?: GetDataTrioGleanData; + download?: GetDataTrioGleanData; + print?: GetDataTrioGleanData; + }; }; export const GetDataCopySingleton = ({ value, onAction, setTooltipVisible, + gleanDataAttrs, }: GetDataTrioProps) => { return ( @@ -50,6 +65,12 @@ export const GetDataCopySingleton = ({ onBlur={() => setTooltipVisible(false)} data-testid="databutton-copy" className="w-12 h-12 relative inline-block text-grey-500 rounded active:text-blue-600 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-blue-500 hover:bg-grey-50" + {...(gleanDataAttrs?.copy && { + 'data-glean-id': gleanDataAttrs.copy.id, + })} + {...(gleanDataAttrs?.copy?.type && { + 'data-glean-type': gleanDataAttrs.copy.type, + })} > { return ( <> @@ -84,6 +106,12 @@ export const GetDataCopySingletonInline = ({ }} onBlur={() => setTooltipVisible(false)} data-testid="databutton-copy" + {...(gleanDataAttrs?.copy && { + 'data-glean-id': gleanDataAttrs.copy.id, + })} + {...(gleanDataAttrs?.copy?.type && { + 'data-glean-type': gleanDataAttrs.copy.type, + })} className="-my-3 -me-4 p-3 rounded text-grey-500 bg-transparent border border-transparent hover:bg-grey-100 active:bg-grey-200 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-blue-500 focus:bg-grey-50" > { const ftlMsgResolver = useFtlMsgResolver(); @@ -161,6 +190,12 @@ export const GetDataTrio = ({ onBlur={() => { setTooltipVisible(false); }} + {...(gleanDataAttrs?.download && { + 'data-glean-id': gleanDataAttrs.download.id, + })} + {...(gleanDataAttrs?.download?.type && { + 'data-glean-type': gleanDataAttrs.download.type, + })} > {/** This only opens the page that is responsible @@ -193,6 +228,12 @@ export const GetDataTrio = ({ onBlur={() => setTooltipVisible(false)} data-testid="databutton-print" className="w-12 h-12 relative inline-block text-grey-500 rounded active:text-blue-600 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-blue-500 hover:bg-grey-50" + {...(gleanDataAttrs?.print && { + 'data-glean-id': gleanDataAttrs.print.id, + })} + {...(gleanDataAttrs?.print?.type && { + 'data-glean-type': gleanDataAttrs.print.type, + })} >
diff --git a/packages/fxa-settings/src/components/Settings/Page2faReplaceRecoveryCodes/index.tsx b/packages/fxa-settings/src/components/Settings/Page2faReplaceRecoveryCodes/index.tsx index 551b89719d..78cd6548f1 100644 --- a/packages/fxa-settings/src/components/Settings/Page2faReplaceRecoveryCodes/index.tsx +++ b/packages/fxa-settings/src/components/Settings/Page2faReplaceRecoveryCodes/index.tsx @@ -148,6 +148,20 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => { onCopy={copyRecoveryCodes} contentType="Backup authentication codes" email={account.email} + gleanDataAttrs={{ + download: { + id: 'two_step_auth_codes_download', + type: 'replace', + }, + copy: { + id: 'two_step_auth_codes_copy', + type: 'replace', + }, + print: { + id: 'two_step_auth_codes_print', + type: 'replace', + }, + }} /> ) : ( diff --git a/packages/fxa-settings/src/components/Settings/PageTwoStepAuthentication/index.tsx b/packages/fxa-settings/src/components/Settings/PageTwoStepAuthentication/index.tsx index 78d5c13c3b..fb34d5602f 100644 --- a/packages/fxa-settings/src/components/Settings/PageTwoStepAuthentication/index.tsx +++ b/packages/fxa-settings/src/components/Settings/PageTwoStepAuthentication/index.tsx @@ -330,6 +330,20 @@ export const PageTwoStepAuthentication = (_: RouteComponentProps) => { onCopy={copyRecoveryCodes} contentType="Backup authentication codes" email={account.email} + gleanDataAttrs={{ + download: { + id: 'two_step_auth_codes_download', + type: 'setup', + }, + copy: { + id: 'two_step_auth_codes_copy', + type: 'setup', + }, + print: { + id: 'two_step_auth_codes_print', + type: 'setup', + }, + }} />
diff --git a/packages/fxa-settings/src/lib/glean/index.test.ts b/packages/fxa-settings/src/lib/glean/index.test.ts index d3f837dee5..e513f75aaf 100644 --- a/packages/fxa-settings/src/lib/glean/index.test.ts +++ b/packages/fxa-settings/src/lib/glean/index.test.ts @@ -76,7 +76,8 @@ describe('lib/glean', () => { setUtmTermStub: SinonStub, setEntrypointExperimentStub: SinonStub, setEntrypointVariationStub: SinonStub, - pageLoadStub: SinonStub; + pageLoadStub: SinonStub, + handleClickEvent: SinonStub; beforeEach(async () => { mockMetricsContext.metricsFlow = { @@ -117,6 +118,10 @@ describe('lib/glean', () => { setEntrypointVariationStub = sandbox.stub(entrypointQuery.variation, 'set'); submitPingStub = sandbox.stub(pings.accountsEvents, 'submit'); pageLoadStub = sandbox.stub(GleanMetricsAPI.default, 'pageLoad'); + handleClickEvent = sandbox.stub( + GleanMetricsAPI.default, + 'handleClickEvent' + ); await testResetGlean('glean-test'); }); @@ -193,6 +198,7 @@ describe('lib/glean', () => { channel: mockConfig.channel, serverEndpoint: mockConfig.serverEndpoint, enableAutoPageLoadEvents: true, + enableAutoElementClickEvents: true, } ); sinon.assert.calledWith(logPingsStub, mockConfig.logPings); @@ -947,4 +953,12 @@ describe('lib/glean', () => { sinon.assert.calledOnce(pageLoadStub); }); }); + describe('handleClickEvent', () => { + it('resolves', async () => { + const fakeEvent = new Event('click'); + GleanMetrics.handleClickEvent(fakeEvent); + sinon.assert.calledOnce(handleClickEvent); + sinon.assert.calledWith(handleClickEvent, fakeEvent); + }); + }); }); diff --git a/packages/fxa-settings/src/lib/glean/index.ts b/packages/fxa-settings/src/lib/glean/index.ts index ed7d881981..2af1845b71 100644 --- a/packages/fxa-settings/src/lib/glean/index.ts +++ b/packages/fxa-settings/src/lib/glean/index.ts @@ -64,6 +64,7 @@ type GleanMetricsT = { getEnabled: () => boolean; isDone: () => Promise; pageLoad: () => void; + handleClickEvent(event: Event): void; } & { [k in EventMapKeys]: { [eventKey in keyof EventsMap[k]]: PingFn }; }; @@ -514,7 +515,12 @@ const createEventFn = export const GleanMetrics: Pick< GleanMetricsT, - 'initialize' | 'setEnabled' | 'getEnabled' | 'isDone' | 'pageLoad' + | 'initialize' + | 'setEnabled' + | 'getEnabled' + | 'isDone' + | 'pageLoad' + | 'handleClickEvent' > = { initialize: (config: GleanMetricsConfig, context: GleanMetricsContext) => { // https://bugzilla.mozilla.org/show_bug.cgi?id=1859629 @@ -527,6 +533,7 @@ export const GleanMetrics: Pick< channel: config.channel, serverEndpoint: config.serverEndpoint, enableAutoPageLoadEvents: true, + enableAutoElementClickEvents: true, }); Glean.setLogPings(config.logPings); if (config.debugViewTag) { @@ -556,6 +563,10 @@ export const GleanMetrics: Pick< GleanMetricsAPI.pageLoad(); }, + handleClickEvent(event: Event) { + GleanMetricsAPI.handleClickEvent(event); + }, + /** * The ping calls are awaited internally for ease of use and that works in * most cases. But in the scenario where we want to wait for the pings to diff --git a/packages/fxa-settings/src/pages/InlineRecoverySetup/index.tsx b/packages/fxa-settings/src/pages/InlineRecoverySetup/index.tsx index c3a05fb0e6..1774c2bd34 100644 --- a/packages/fxa-settings/src/pages/InlineRecoverySetup/index.tsx +++ b/packages/fxa-settings/src/pages/InlineRecoverySetup/index.tsx @@ -202,6 +202,20 @@ const InlineRecoverySetup = ({ onCopy={copyRecoveryCodes} contentType="Backup authentication codes" {...{ email }} + gleanDataAttrs={{ + download: { + id: 'two_step_auth_codes_download', + type: 'inline setup', + }, + copy: { + id: 'two_step_auth_codes_copy', + type: 'inline setup', + }, + print: { + id: 'two_step_auth_codes_print', + type: 'inline setup', + }, + }} />