Skip to content
Open
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
34 changes: 29 additions & 5 deletions src/ReturnUserExperience/ReturnUserExperience.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'

import styles from './returnUserExperience.module.css'
import RuxInfo from 'src/ReturnUserExperience/RuxInfo'
import { RuxPhoneNumber } from 'src/ReturnUserExperience/RuxPhoneNumber'

import { Stack } from '@mui/material'
import { Icon } from '@mxenabled/mxui'
Expand All @@ -12,6 +13,8 @@ import useAnalyticsEvent from 'src/hooks/useAnalyticsEvent'
import { __ } from 'src/utilities/Intl'
import { AnalyticEvents } from 'src/const/Analytics'
import { RootState } from 'src/redux/Store'
import { ActionTypes } from 'src/redux/actions/Connect'
import { selectInitialConfig } from 'src/redux/reducers/configSlice'
import { ClientLogo } from 'src/components/ClientLogo'

export const RUXViews = {
Expand All @@ -23,30 +26,51 @@ export const RUXViews = {

export const ReturnUserExperience = React.forwardRef(() => {
const [view, setView] = React.useState<(typeof RUXViews)[keyof typeof RUXViews]>(RUXViews.INFO)
const [userEnteredPhone, setUserEnteredPhone] = React.useState('')
const clientGuid = useSelector((state: RootState) => state.profiles.client.guid)
const connectConfig = useSelector(selectInitialConfig)
const dispatch = useDispatch()
const sendAnalyticsEvent = useAnalyticsEvent()

const handleRuxInfoContinue = () => {
// This is currently skipping the backend. See epic/ticket for more details.
sendAnalyticsEvent(AnalyticEvents.RUX_INFO_CONTINUE_CLICKED)
setView(RUXViews.PHONE_NUMBER)
}
const handleContinueWithoutPhone = () =>
dispatch({ type: ActionTypes.RESET_WIDGET_MFA_STEP, payload: connectConfig })

return (
<div className={styles.pageContainer}>
{view !== RUXViews.LIST && (
<Stack className={styles.logoHeaders} direction="row" spacing="8px">
<div className={styles.clientLogo}>
<ClientLogo alt="Client logo" clientGuid={clientGuid} size={64} />
</div>
<Icon name="add" size={20} />
{view === RUXViews.INFO && (
<>
<div className={styles.clientLogo}>
<ClientLogo alt="Client logo" clientGuid={clientGuid} size={64} />
</div>
<Icon name="add" size={20} />
</>
)}
<div className={styles.mxLogo}>
<MXLogoFilledIcon size={64} />
</div>
</Stack>
)}

{view === RUXViews.INFO && <RuxInfo handleRuxContinue={handleRuxInfoContinue} />}

{view === RUXViews.PHONE_NUMBER && (
<RuxPhoneNumber
handleContinueWithoutPhone={handleContinueWithoutPhone}
handleRuxContinue={() => {
// sendAnalyticsEvent(AnalyticEvents.RUX_PHONE_NUMBER_CONTINUE_CLICKED)
setView(RUXViews.OTP)
}}
setUserEnteredPhone={setUserEnteredPhone}
userEnteredPhone={userEnteredPhone}
/>
)}
</div>
)
})
Expand Down
130 changes: 130 additions & 0 deletions src/ReturnUserExperience/RuxPhoneNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React from 'react'

import { useTheme } from '@mui/material'
import InputAdornment from '@mui/material/InputAdornment'
import Stack from '@mui/material/Stack'
import Button from '@mui/material/Button'
import { Text } from '@mxenabled/mxui'
import { Link } from '@mui/material'

import { __ } from 'src/utilities/Intl'
import useAnalyticsPath from 'src/hooks/useAnalyticsPath'
import { TextField } from 'src/privacy/input'
import { PageviewInfo } from 'src/const/Analytics'
import styles from './returnUserExperience.module.css'

export const RuxPhoneNumber = ({
handleContinueWithoutPhone,
handleRuxContinue,
userEnteredPhone,
setUserEnteredPhone,
}: {
handleContinueWithoutPhone: () => void
handleRuxContinue: () => void
userEnteredPhone: string
setUserEnteredPhone: (phone: string) => void
}) => {
useAnalyticsPath(...PageviewInfo.CONNECT_RUX_PHONE_NUMBER)
const { palette } = useTheme()

return (
<>
<Stack className={styles.titleContainer} spacing="6px">
<Text bold={true} className={styles.centerText} truncate={false} variant="h2">
{__('Connect faster with your phone number')}
</Text>
<Text className={styles.centerText} truncate={false} variant="subtitle1">
{__('Login or sign up with MX to securely access your saved accounts. ')}
<Link
className={styles.primaryLink}
href="https://mx.com/learn-more"
rel="noopener noreferrer"
sx={{
color: palette.primary.main,
fontWeight: 'normal',
marginLeft: 0,
textDecoration: 'underline',
}}
target="_blank"
underline="always"
variant="subtitle1"
>
{__('Learn more about MX.')}
</Link>
</Text>
</Stack>

<TextField
InputProps={{
startAdornment: (
<InputAdornment position="start">
<div style={{ display: 'flex', alignItems: 'center' }}>
<Text style={{ paddingLeft: '10px', paddingRight: '16px' }} variant="body1">
Phone
</Text>
<Text sx={{ fontWeight: 400 }} variant="h2">
+1
</Text>
</div>
</InputAdornment>
),
style: {
paddingRight: '14px',
margin: '40px 0',
fontSize: '23px',
fontWeight: '400',
height: 'auto',
maxHeight: '60px',
},
}}
fullWidth={true}
name="phoneNumber"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setUserEnteredPhone(e.target.value.replace(/\D/g, '').slice(0, 10))
}
required={true}
value={formatPhone(userEnteredPhone)}
/>

<Stack spacing="8px">
<Text truncate={false} variant="caption">
{/* --TR: Full string 'By selecting "Get code", you agree to MX's Terms & Conditions' */}
{__('By selecting "Continue", you agree to ')}
<Link
href="https://www.mx.com/terms/"
rel="noopener noreferrer"
sx={{
fontWeight: 'normal',
marginLeft: 0,
marginRight: 0,
textDecoration: 'underline',
}}
target="_blank"
underline="always"
variant="caption"
>
{/* TODO: Do we translate this below? */}
{__("MX's Terms & Conditions")}
</Link>
</Text>
<Button name="continue" onClick={handleRuxContinue} variant="contained">
{__('Continue')}
</Button>
<Button name="continueWithoutPhone" onClick={handleContinueWithoutPhone} variant="text">
{__('Continue without phone number')}
</Button>
</Stack>
</>
)
}

export default RuxPhoneNumber

const formatPhone = (value: string) => {
const digits = value.replace(/\D/g, '').slice(0, 10)

if (digits.length === 0) return digits
if (digits.length <= 3) return `(${digits}`
if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)} - ${digits.slice(6)}`
}
99 changes: 99 additions & 0 deletions src/ReturnUserExperience/__tests__/RuxPhoneNumber-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from 'react'
import { RuxPhoneNumber } from 'src/ReturnUserExperience/RuxPhoneNumber'
import { render, screen } from 'src/utilities/testingLibrary'

describe('RuxPhoneNumber', () => {
it('renders the main heading', () => {
render(
<RuxPhoneNumber
handleContinueWithoutPhone={() => {}}
handleRuxContinue={() => {}}
setUserEnteredPhone={() => {}}
userEnteredPhone=""
/>,
)
const heading = screen.getByText('Connect faster with your phone number')
expect(heading).toBeInTheDocument()
})

it('renders the subtitle with a learn more link', () => {
render(
<RuxPhoneNumber
handleContinueWithoutPhone={() => {}}
handleRuxContinue={() => {}}
setUserEnteredPhone={() => {}}
userEnteredPhone=""
/>,
)
const subtitle = screen.getByText(
/Login or sign up with MX to securely access your saved accounts./i,
)
expect(subtitle).toBeInTheDocument()

const link = screen.getByRole('link', { name: /learn more about mx/i })
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', 'https://mx.com/learn-more')
expect(link).toHaveAttribute('target', '_blank')
expect(link).toHaveAttribute('rel', 'noopener noreferrer')
})

it('renders the phone number input with correct label', () => {
render(
<RuxPhoneNumber
handleContinueWithoutPhone={() => {}}
handleRuxContinue={() => {}}
setUserEnteredPhone={() => {}}
userEnteredPhone=""
/>,
)
const phoneInput = screen.getByRole('textbox')
expect(phoneInput).toBeInTheDocument()
})

it('renders the continue without phone number button', () => {
render(
<RuxPhoneNumber
handleContinueWithoutPhone={() => {}}
handleRuxContinue={() => {}}
setUserEnteredPhone={() => {}}
userEnteredPhone=""
/>,
)
const continueWithoutPhoneButton = screen.getByRole('button', {
name: 'Continue without phone number',
})
expect(continueWithoutPhoneButton).toBeInTheDocument()
})

it('calls handleContinueWithoutPhone when the continue without phone number button is clicked', () => {
const handleContinueWithoutPhoneMock = vi.fn()
render(
<RuxPhoneNumber
handleContinueWithoutPhone={handleContinueWithoutPhoneMock}
handleRuxContinue={() => {}}
setUserEnteredPhone={() => {}}
userEnteredPhone=""
/>,
)
const continueWithoutPhoneButton = screen.getByRole('button', {
name: 'Continue without phone number',
})
continueWithoutPhoneButton.click()
expect(handleContinueWithoutPhoneMock).toHaveBeenCalledTimes(1)
})

it('calls handleRuxContinue when the continue button is clicked', () => {
const handleRuxContinueMock = vi.fn()
render(
<RuxPhoneNumber
handleContinueWithoutPhone={() => {}}
handleRuxContinue={handleRuxContinueMock}
setUserEnteredPhone={() => {}}
userEnteredPhone=""
/>,
)
const continueButton = screen.getByRole('button', { name: 'Continue' })
continueButton.click()
expect(handleRuxContinueMock).toHaveBeenCalledTimes(1)
})
})
4 changes: 2 additions & 2 deletions src/ReturnUserExperience/returnUserExperience.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,6 @@

.titleContainer {
padding-top: 16px;
padding-right: 16px;
padding-left: 16px;
margin-right: -8px;
margin-left: -8px;
}
1 change: 1 addition & 0 deletions src/const/Analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const PageviewInfo = {
CONNECT_OAUTH_ERROR: ['Connect Oauth Error', '/oauth_error'],
CONNECT_NO_ELIGIBLE_ACCOUNTS: ['Connect No Eligible Accounts', '/no_eligible_accounts'],
CONNECT_RUX_INFO: ['Connect RUX Info', '/rux_info'],
CONNECT_RUX_PHONE_NUMBER: ['Connect RUX Phone Number', '/rux_phone_number'],
CONNECT_SEARCH: ['Connect Search', '/search'],
CONNECT_SEARCH_FAILED: ['Connect Search Failed', '/search_failed'],
CONNECT_SEARCH_NO_RESULTS: ['Connect Search No Results', '/no_results'],
Expand Down
5 changes: 5 additions & 0 deletions src/const/language/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,11 @@
"Connect your accounts": "Conecta tus cuentas",
"%1 uses MX to connect your accounts. ": "%1 usa MX para conectar tus cuentas. ",
"Learn more about MX.": "Obtén más información sobre MX.",
"Connect faster with your phone number": "Conéctate más rápido con tu número de teléfono",
"Login or sign up with MX to securely access your saved accounts. ": "Inicia sesión o regístrate con MX para acceder de forma segura a tus cuentas guardadas. ",
"By selecting \"Continue\", you agree to ": "Al seleccionar \"Continuar\", aceptas ",
"MX's Terms & Conditions": "los Términos y Condiciones de MX",
"Continue without phone number": "Continuar sin número de teléfono",
"connect/disclosure/button\u0004Continue": "Continuar",
"connect/disclosure/policy/text\u0004By clicking Continue, you agree to the ": "Al hacer clic en Continuar, tu aceptas la ",
"connect/disclosure/policy/link\u0004MX Privacy Policy.": "Política de privacidad de Money Experience.",
Expand Down
35 changes: 31 additions & 4 deletions src/const/language/es.po
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,11 @@ msgstr "Continuar"
#: src/components/support/GeneralSupport.js
#: src/components/support/SupportSuccess.js
#: src/components/ConnectSuccessSurvey.tsx src/components/FindAccountInfo.js
#: src/components/LeavingNoticeFlat.js src/ReturnUserExperience/RuxInfo.tsx
#: src/views/mfa/DefaultMFA.js src/views/mfa/MFAImages.js
#: src/views/mfa/MFAOptions.js src/views/microdeposits/VerifyDeposits.js
#: src/components/LeavingNoticeFlat.js
#: src/ReturnUserExperience/RuxPhoneNumber.tsx
#: src/ReturnUserExperience/RuxInfo.tsx src/views/mfa/DefaultMFA.js
#: src/views/mfa/MFAImages.js src/views/mfa/MFAOptions.js
#: src/views/microdeposits/VerifyDeposits.js
#: src/views/microdeposits/HowItWorks.js
#: src/views/microdeposits/MicrodepositErrors.js
#: src/views/microdeposits/PersonalInfoForm.js
Expand Down Expand Up @@ -2165,7 +2167,8 @@ msgstr "Privado"

#: src/ReturnUserExperience/RuxInfo.tsx
msgid "We never sell your phone number or use it for marketing."
msgstr "Nunca vendemos tu número de teléfono ni lo usamos con fines de mercadotecnia."
msgstr ""
"Nunca vendemos tu número de teléfono ni lo usamos con fines de mercadotecnia."

#: src/ReturnUserExperience/RuxInfo.tsx
msgid "Connect your accounts"
Expand All @@ -2175,6 +2178,30 @@ msgstr "Conecta tus cuentas"
msgid "%1 uses MX to connect your accounts. "
msgstr "%1 usa MX para conectar tus cuentas. "

#: src/ReturnUserExperience/RuxPhoneNumber.tsx
#: src/ReturnUserExperience/RuxInfo.tsx
msgid "Learn more about MX."
msgstr "Obtén más información sobre MX."

#: src/ReturnUserExperience/RuxPhoneNumber.tsx
msgid "Connect faster with your phone number"
msgstr "Conéctate más rápido con tu número de teléfono"

#: src/ReturnUserExperience/RuxPhoneNumber.tsx
msgid "Login or sign up with MX to securely access your saved accounts. "
msgstr ""
"Inicia sesión o regístrate con MX para acceder de forma segura a tus cuentas "
"guardadas. "

#. TR: Full string 'By selecting "Get code", you agree to MX's Terms & Conditions' */}
#: src/ReturnUserExperience/RuxPhoneNumber.tsx
msgid "By selecting \"Continue\", you agree to "
msgstr "Al seleccionar \"Continuar\", aceptas "

#: src/ReturnUserExperience/RuxPhoneNumber.tsx
msgid "MX's Terms & Conditions"
msgstr "los Términos y Condiciones de MX"

#: src/ReturnUserExperience/RuxPhoneNumber.tsx
msgid "Continue without phone number"
msgstr "Continuar sin número de teléfono"
Loading
Loading