Skip to content

Commit

Permalink
feat(app): add analytics for localization feature (#17130)
Browse files Browse the repository at this point in the history
  • Loading branch information
smb2268 authored Dec 20, 2024
1 parent e458d06 commit 3162131
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { describe, it, vi, afterEach, beforeEach, expect } from 'vitest'

import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
import {
ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
useTrackEvent,
} from '/app/redux/analytics'
import {
getAppLanguage,
getStoredSystemLanguage,
Expand All @@ -16,6 +20,7 @@ import { SystemLanguagePreferenceModal } from '..'
vi.mock('react-router-dom')
vi.mock('/app/redux/config')
vi.mock('/app/redux/shell')
vi.mock('/app/redux/analytics')

const render = () => {
return renderWithProviders(<SystemLanguagePreferenceModal />, {
Expand All @@ -24,6 +29,7 @@ const render = () => {
}

const mockNavigate = vi.fn()
const mockTrackEvent = vi.fn()

const MOCK_DEFAULT_LANGUAGE = 'en-US'

Expand All @@ -33,6 +39,7 @@ describe('SystemLanguagePreferenceModal', () => {
vi.mocked(getSystemLanguage).mockReturnValue(MOCK_DEFAULT_LANGUAGE)
vi.mocked(getStoredSystemLanguage).mockReturnValue(MOCK_DEFAULT_LANGUAGE)
vi.mocked(useNavigate).mockReturnValue(mockNavigate)
vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent)
})
afterEach(() => {
vi.resetAllMocks()
Expand Down Expand Up @@ -68,6 +75,14 @@ describe('SystemLanguagePreferenceModal', () => {
'language.systemLanguage',
MOCK_DEFAULT_LANGUAGE
)
expect(mockTrackEvent).toBeCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
properties: {
language: MOCK_DEFAULT_LANGUAGE,
systemLanguage: MOCK_DEFAULT_LANGUAGE,
modalType: 'appBootModal',
},
})
})

it('should default to English (US) if system language is unsupported', () => {
Expand All @@ -90,6 +105,14 @@ describe('SystemLanguagePreferenceModal', () => {
MOCK_DEFAULT_LANGUAGE
)
expect(updateConfigValue).toBeCalledWith('language.systemLanguage', 'es-MX')
expect(mockTrackEvent).toBeCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
properties: {
language: MOCK_DEFAULT_LANGUAGE,
systemLanguage: 'es-MX',
modalType: 'appBootModal',
},
})
})

it('should set a supported app language when system language is an unsupported locale of the same language', () => {
Expand All @@ -112,6 +135,14 @@ describe('SystemLanguagePreferenceModal', () => {
MOCK_DEFAULT_LANGUAGE
)
expect(updateConfigValue).toBeCalledWith('language.systemLanguage', 'en-GB')
expect(mockTrackEvent).toBeCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
properties: {
language: MOCK_DEFAULT_LANGUAGE,
systemLanguage: 'en-GB',
modalType: 'appBootModal',
},
})
})

it('should render the correct header, description, and buttons when system language changes', () => {
Expand Down Expand Up @@ -139,6 +170,14 @@ describe('SystemLanguagePreferenceModal', () => {
'language.systemLanguage',
'zh-CN'
)
expect(mockTrackEvent).toBeCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
properties: {
language: 'zh-CN',
systemLanguage: 'zh-CN',
modalType: 'systemLanguageUpdateModal',
},
})
fireEvent.click(secondaryButton)
expect(updateConfigValue).toHaveBeenNthCalledWith(
3,
Expand Down Expand Up @@ -168,6 +207,14 @@ describe('SystemLanguagePreferenceModal', () => {
'language.systemLanguage',
'zh-Hant'
)
expect(mockTrackEvent).toBeCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
properties: {
language: 'zh-CN',
systemLanguage: 'zh-Hant',
modalType: 'systemLanguageUpdateModal',
},
})
fireEvent.click(secondaryButton)
expect(updateConfigValue).toHaveBeenNthCalledWith(
3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import {
} from '@opentrons/components'

import { LANGUAGES } from '/app/i18n'
import {
ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
useTrackEvent,
} from '/app/redux/analytics'
import {
getAppLanguage,
getStoredSystemLanguage,
Expand All @@ -32,7 +36,7 @@ type ArrayElement<

export function SystemLanguagePreferenceModal(): JSX.Element | null {
const { i18n, t } = useTranslation(['app_settings', 'shared', 'branded'])

const trackEvent = useTrackEvent()
const [currentOption, setCurrentOption] = useState<DropdownOption>(
LANGUAGES[0]
)
Expand Down Expand Up @@ -66,6 +70,16 @@ export function SystemLanguagePreferenceModal(): JSX.Element | null {
const handlePrimaryClick = (): void => {
dispatch(updateConfigValue('language.appLanguage', currentOption.value))
dispatch(updateConfigValue('language.systemLanguage', systemLanguage))
trackEvent({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_MODAL,
properties: {
language: currentOption.value,
systemLanguage,
modalType: showUpdateModal
? 'systemLanguageUpdateModal'
: 'appBootModal',
},
})
}

const handleDropdownClick = (value: string): void => {
Expand Down
20 changes: 19 additions & 1 deletion app/src/organisms/ODD/RobotSettingsDashboard/LanguageSetting.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Fragment } from 'react'
import { Fragment, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import styled from 'styled-components'
import uuidv1 from 'uuid/v4'

import {
BORDERS,
Expand All @@ -14,6 +15,8 @@ import {
} from '@opentrons/components'

import { LANGUAGES } from '/app/i18n'
import { ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS } from '/app/redux/analytics'
import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
import { ChildNavigation } from '/app/organisms/ODD/ChildNavigation'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'

Expand Down Expand Up @@ -42,16 +45,31 @@ interface LanguageSettingProps {
setCurrentOption: SetSettingOption
}

const uuid: () => string = uuidv1

export function LanguageSetting({
setCurrentOption,
}: LanguageSettingProps): JSX.Element {
const { t } = useTranslation('app_settings')
const dispatch = useDispatch<Dispatch>()
const { trackEventWithRobotSerial } = useTrackEventWithRobotSerial()

let transactionId = ''
useEffect(() => {
transactionId = uuid()
}, [])

const appLanguage = useSelector(getAppLanguage)

const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
dispatch(updateConfigValue('language.appLanguage', event.target.value))
trackEventWithRobotSerial({
name: ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS,
properties: {
language: event.target.value,
transactionId,
},
})
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ import {
SIMPLIFIED_CHINESE_DISPLAY_NAME,
SIMPLIFIED_CHINESE,
} from '/app/i18n'
import { ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS } from '/app/redux/analytics'
import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
import { renderWithProviders } from '/app/__testing-utils__'

import { LanguageSetting } from '../LanguageSetting'

vi.mock('/app/redux/config')
vi.mock('/app/redux-resources/analytics')

const mockSetCurrentOption = vi.fn()
const mockTrackEvent = vi.fn()

const render = (props: React.ComponentProps<typeof LanguageSetting>) => {
return renderWithProviders(<LanguageSetting {...props} />, {
Expand All @@ -32,6 +36,9 @@ describe('LanguageSetting', () => {
setCurrentOption: mockSetCurrentOption,
}
vi.mocked(getAppLanguage).mockReturnValue(US_ENGLISH)
vi.mocked(useTrackEventWithRobotSerial).mockReturnValue({
trackEventWithRobotSerial: mockTrackEvent,
})
})

it('should render text and buttons', () => {
Expand All @@ -49,6 +56,13 @@ describe('LanguageSetting', () => {
'language.appLanguage',
SIMPLIFIED_CHINESE
)
expect(mockTrackEvent).toHaveBeenCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_ODD_SETTINGS,
properties: {
language: SIMPLIFIED_CHINESE,
transactionId: expect.anything(),
},
})
})

it('should call mock function when tapping back button', () => {
Expand Down
17 changes: 15 additions & 2 deletions app/src/pages/Desktop/AppSettings/GeneralSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// app info card with version and updated
import { useState } from 'react'
import { useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import { useSelector, useDispatch } from 'react-redux'
import uuidv1 from 'uuid/v4'

import {
ALIGN_CENTER,
Expand Down Expand Up @@ -41,6 +42,7 @@ import {
import {
useTrackEvent,
ANALYTICS_APP_UPDATE_NOTIFICATIONS_TOGGLED,
ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
} from '/app/redux/analytics'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
import { UpdateAppModal } from '/app/organisms/Desktop/UpdateAppModal'
Expand All @@ -55,6 +57,7 @@ const GITHUB_LINK =
'https://github.com/Opentrons/opentrons/blob/edge/app-shell/build/release-notes.md'

const ENABLE_APP_UPDATE_NOTIFICATIONS = 'Enable app update notifications'
const uuid: () => string = uuidv1

export function GeneralSettings(): JSX.Element {
const { t } = useTranslation(['app_settings', 'shared', 'branded'])
Expand All @@ -68,9 +71,19 @@ export function GeneralSettings(): JSX.Element {

const appLanguage = useSelector(getAppLanguage)
const currentLanguageOption = LANGUAGES.find(lng => lng.value === appLanguage)

let transactionId = ''
useEffect(() => {
transactionId = uuid()
}, [])
const handleDropdownClick = (value: string): void => {
dispatch(updateConfigValue('language.appLanguage', value))
trackEvent({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
properties: {
language: value,
transactionId,
},
})
}

const [showUpdateBanner, setShowUpdateBanner] = useState<boolean>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
US_ENGLISH_DISPLAY_NAME,
} from '/app/i18n'
import { getAlertIsPermanentlyIgnored } from '/app/redux/alerts'
import {
ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
useTrackEvent,
} from '/app/redux/analytics'
import { getAppLanguage, updateConfigValue } from '/app/redux/config'
import * as Shell from '/app/redux/shell'
import { GeneralSettings } from '../GeneralSettings'
Expand All @@ -32,11 +36,14 @@ const render = (): ReturnType<typeof renderWithProviders> => {
)
}

const mockTrackEvent = vi.fn()

describe('GeneralSettings', () => {
beforeEach(() => {
vi.mocked(Shell.getAvailableShellUpdate).mockReturnValue(null)
vi.mocked(getAlertIsPermanentlyIgnored).mockReturnValue(false)
vi.mocked(getAppLanguage).mockReturnValue(US_ENGLISH)
vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent)
})
afterEach(() => {
vi.resetAllMocks()
Expand Down Expand Up @@ -118,5 +125,12 @@ describe('GeneralSettings', () => {
'language.appLanguage',
SIMPLIFIED_CHINESE
)
expect(mockTrackEvent).toHaveBeenCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_DESKTOP_APP_SETTINGS,
properties: {
language: SIMPLIFIED_CHINESE,
transactionId: expect.anything(),
},
})
})
})
21 changes: 19 additions & 2 deletions app/src/pages/ODD/ChooseLanguage/__tests__/ChooseLanguage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { vi, it, describe, expect } from 'vitest'
import { vi, it, describe, expect, beforeEach } from 'vitest'
import { fireEvent, screen } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'

import { renderWithProviders } from '/app/__testing-utils__'
import { i18n } from '/app/i18n'
import { updateConfigValue } from '/app/redux/config'
import { ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW } from '/app/redux/analytics'
import { useTrackEventWithRobotSerial } from '/app/redux-resources/analytics'
import { updateConfigValue, getAppLanguage } from '/app/redux/config'
import { ChooseLanguage } from '..'

import type { NavigateFunction } from 'react-router-dom'
Expand All @@ -18,6 +20,9 @@ vi.mock('react-router-dom', async importOriginal => {
}
})
vi.mock('/app/redux/config')
vi.mock('/app/redux-resources/analytics')

const mockTrackEvent = vi.fn()

const render = () => {
return renderWithProviders(
Expand All @@ -31,6 +36,12 @@ const render = () => {
}

describe('ChooseLanguage', () => {
beforeEach(() => {
vi.mocked(useTrackEventWithRobotSerial).mockReturnValue({
trackEventWithRobotSerial: mockTrackEvent,
})
vi.mocked(getAppLanguage).mockReturnValue('en-US')
})
it('should render text, language options, and continue button', () => {
render()
screen.getByText('Choose your language')
Expand All @@ -54,6 +65,12 @@ describe('ChooseLanguage', () => {
it('should call mockNavigate when tapping continue', () => {
render()
fireEvent.click(screen.getByRole('button', { name: 'Continue' }))
expect(mockTrackEvent).toHaveBeenCalledWith({
name: ANALYTICS_LANGUAGE_UPDATED_ODD_UNBOXING_FLOW,
properties: {
language: 'en-US',
},
})
expect(mockNavigate).toHaveBeenCalledWith('/welcome')
})
})
Loading

0 comments on commit 3162131

Please sign in to comment.