Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app): add analytics for localization feature #17130

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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(),
},
})
})
})
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
Loading