Skip to content

Commit

Permalink
improvement(user-account-screen): Consolidate input change handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
binh-dam-ibigroup committed Oct 16, 2023
1 parent 760539b commit fafbe96
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 41 deletions.
40 changes: 5 additions & 35 deletions lib/components/user/existing-account-display.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { connect } from 'react-redux'
import { FormattedMessage, useIntl } from 'react-intl'
import { FormikProps } from 'formik'
import React, { useCallback } from 'react'
import React from 'react'

import { AppReduxState } from '../../util/state-types'
import { toastSuccess } from '../util/toasts'
import { TransitModeConfig } from '../../util/config-types'
import PageTitle from '../util/page-title'

Expand All @@ -24,44 +23,15 @@ interface Props extends FormikProps<User> {
/**
* This component handles the existing account display.
*/
const ExistingAccountDisplay = (parentProps: Props) => {
const ExistingAccountDisplay = (props: Props) => {
// The props include Formik props that provide access to the current user data
// and to its own blur/change/submit event handlers that automate the state.
// We forward the props to each pane so that their individual controls
// can be wired to be managed by Formik.
const { handleChange, submitForm, wheelchairEnabled } = parentProps

const { wheelchairEnabled } = props
const intl = useIntl()
const props = {
...parentProps,
handleChange: useCallback(
async (e) => {
// Apply changes and submit the form right away to update the user profile.
handleChange(e)
try {
// Disable input during submission
e.target.disabled = true
await submitForm()
// Re-enable input during submission
e.target.disabled = false
// Display a toast notification on success.
toastSuccess(
intl.formatMessage({
// Use a summary text for the field, if defined (e.g. to replace long labels),
// otherwise, fall back on the first label of the input.
defaultMessage: e.target.labels[0]?.innerText,
id: `components.ExistingAccountDisplay.fields.${e.target.name}`
}),
intl.formatMessage({
id: 'components.ExistingAccountDisplay.fieldUpdated'
})
)
} catch {
alert('Error updating profile')
}
},
[intl, handleChange, submitForm]
)
}

const panes = [
{
pane: FavoritePlaceList,
Expand Down
11 changes: 11 additions & 0 deletions lib/components/user/new-account-wizard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Form, FormikProps } from 'formik'

Check failure on line 1 in lib/components/user/new-account-wizard.tsx

View workflow job for this annotation

GitHub Actions / test-build-release

Property 'onCancel' does not exist on type 'Props'.
import { FormattedMessage, useIntl } from 'react-intl'
import React, { useCallback } from 'react'
import toast from 'react-hot-toast'

import PageTitle from '../util/page-title'

Expand Down Expand Up @@ -28,6 +29,7 @@ interface Props extends FormikUserProps {
*/
const NewAccountWizard = ({
activePaneId,
onCancel, // provided by UserAccountScreen
onCreate, // provided by UserAccountScreen
...formikProps // provided by Formik
}: Props): JSX.Element => {
Expand All @@ -41,6 +43,14 @@ const NewAccountWizard = ({
}
}, [onCreate, userData])

const handleFinish = useCallback(() => {
// Display a toast to acknowledge saved changes
// (although in reality, changes quietly took effect in previous screens).
toast.success(intl.formatMessage({ id: 'actions.user.preferencesSaved' }))

onCancel && onCancel()
}, [intl, onCancel])

if (activePaneId === 'verify') {
const verifyEmail = intl.formatMessage({
id: 'components.NewAccountWizard.verify'
Expand Down Expand Up @@ -97,6 +107,7 @@ const NewAccountWizard = ({
<PageTitle title={createNewAccount} />
<SequentialPaneDisplay
activePaneId={activePaneId}
onFinish={handleFinish}
paneProps={formikProps}
panes={paneSequence}
/>
Expand Down
7 changes: 5 additions & 2 deletions lib/components/user/sequential-pane-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface PaneProps {

interface OwnProps {
activePaneId: string
onFinish?: () => void
panes: PaneProps[]
}

Expand Down Expand Up @@ -57,7 +58,7 @@ class SequentialPaneDisplay<T> extends Component<Props<T>> {
}

_handleToNextPane = async (e: MouseEvent<Button>) => {
const { activePane, activePaneIndex, panes } = this.props
const { activePane, activePaneIndex, onFinish, panes } = this.props
const { invalid, invalidMessage } = activePane

if (activePaneIndex < panes.length - 1) {
Expand All @@ -75,8 +76,10 @@ class SequentialPaneDisplay<T> extends Component<Props<T>> {
}
this._routeTo(nextId)
}
this._focusHeader()
} else if (onFinish) {
onFinish()
}
this._focusHeader()
}

_handleToPrevPane = () => {
Expand Down
45 changes: 41 additions & 4 deletions lib/components/user/user-account-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
withAuthenticationRequired
} from '@auth0/auth0-react'
import { connect } from 'react-redux'
import { Formik } from 'formik'
import { Formik, FormikProps } from 'formik'
import { injectIntl, IntlShape } from 'react-intl'
import { RouteComponentProps } from 'react-router'
import clone from 'clone'
Expand All @@ -14,6 +14,7 @@ import * as uiActions from '../../actions/ui'
import * as userActions from '../../actions/user'
import { CREATE_ACCOUNT_PATH } from '../../util/constants'
import { RETURN_TO_CURRENT_ROUTE } from '../../util/ui'
import { toastSuccess } from '../util/toasts'

import { User } from './types'
import AccountPage from './account-page'
Expand Down Expand Up @@ -114,6 +115,42 @@ class UserAccountScreen extends Component<Props> {
this._handleExit()
}

_handleInputChange = (formikProps: FormikProps) => async (e: FormEvent) => {
const { intl, isCreating } = this.props
const { handleChange, submitForm, values: userData } = formikProps
handleChange(e)
const shouldNotSubmit =
e.target.name === 'hasConsentedToTerms' ||
(e.target.name === 'storeTripHistory' && !userData.id)
if (!shouldNotSubmit) {
// Submit the form right away after applying changes to update the user profile.
try {
// Disable input during submission
e.target.disabled = true
await submitForm()
// Re-enable input and refocus after submission
e.target.disabled = false
e.target.focus()
// For existing accounts, display a toast notification on success.
if (!isCreating) {
toastSuccess(
intl.formatMessage({
// Use a summary text for the field, if defined (e.g. to replace long labels),
// otherwise, fall back on the first label of the input.
defaultMessage: e.target.labels[0]?.innerText,
id: `components.ExistingAccountDisplay.fields.${e.target.name}`
}),
intl.formatMessage({
id: 'components.ExistingAccountDisplay.fieldUpdated'
})
)
}
} catch {
alert('Error updating profile')
}
}
}

/**
* Persist changes immediately (for existing account display)
* @param {*} userData The user edited state to be saved, provided by Formik.
Expand Down Expand Up @@ -151,9 +188,7 @@ class UserAccountScreen extends Component<Props> {
// Force Formik to reload initialValues when we update them (e.g. user gets assigned an id).
enableReinitialize
initialValues={loggedInUserWithNotificationArray}
onSubmit={
isCreating ? this._handleSaveAndExit : this._handleFieldChange
}
onSubmit={this._handleFieldChange}
>
{
// Formik props provide access to the current user data state and errors,
Expand All @@ -167,6 +202,8 @@ class UserAccountScreen extends Component<Props> {
activePaneId={itemId}
// @ts-expect-error emailVerified prop used by only one of the DisplayComponent.
emailVerified={auth0.user?.email_verified}
// Use our own handlChange handler that wraps around Formik's.
handleChange={this._handleInputChange(formikProps)}
loggedInUser={loggedInUser}
onCancel={this._handleExit}
onCreate={this._handleCreateNewUser}
Expand Down

0 comments on commit fafbe96

Please sign in to comment.