move logic up from index to container, set up storybook

This commit is contained in:
Valerie Pomerleau 2024-11-20 15:45:44 -08:00
Родитель 8267d2b26d
Коммит a853a23ce6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 33A451F0BB2180B4
6 изменённых файлов: 172 добавлений и 161 удалений

Просмотреть файл

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { FtlMsg } from 'fxa-react/lib/utils';
import FormPasswordWithBalloons from '../FormPasswordWithBalloons';
import InputText from '../InputText';

Просмотреть файл

@ -21,7 +21,7 @@ export type FormSetupAccountProps = {
onSubmit: (e?: React.BaseSyntheticEvent) => Promise<void>;
loading: boolean;
isSync: boolean;
offeredSyncEngineConfigs?: typeof syncEngineConfigs;
offeredSyncEngineConfigs?: typeof syncEngineConfigs | undefined;
setDeclinedSyncEngines: React.Dispatch<React.SetStateAction<string[]>>;
isDesktopRelay: boolean;
setSelectedNewsletterSlugs?: React.Dispatch<React.SetStateAction<string[]>>;

Просмотреть файл

@ -2,24 +2,29 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { RouteComponentProps } from '@reach/router';
import React from 'react';
import { RouteComponentProps, useLocation } from '@reach/router';
import SetPassword from '.';
import { currentAccount } from '../../../lib/cache';
import LoadingSpinner from 'fxa-react/components/LoadingSpinner';
import { useNavigateWithQuery as useNavigate } from '../../../lib/hooks/useNavigateWithQuery';
import { useAuthClient, useSensitiveDataClient } from '../../../models';
import { cache } from '../../../lib/cache';
import { useCallback } from 'react';
import { CreatePasswordHandler, SetPasswordIntegration } from './interfaces';
import { HandledError } from '../../../lib/error-utils';
import {
AuthUiErrorNos,
AuthUiErrors,
} from '../../../lib/auth-errors/auth-errors';
isOAuthNativeIntegration,
useAuthClient,
useFtlMsgResolver,
useSensitiveDataClient,
} from '../../../models';
import { cache } from '../../../lib/cache';
import { useState } from 'react';
import { CreatePasswordHandler, SetPasswordIntegration } from './interfaces';
import { getLocalizedErrorMessage } from '../../../lib/error-utils';
import {
AUTH_DATA_KEY,
SensitiveDataClientAuthKeys,
} from '../../../lib/sensitive-data-client';
import useSyncEngines from '../../../lib/hooks/useSyncEngines';
import { getSyncNavigate } from '../../Signin/utils';
import firefox from '../../../lib/channels/firefox';
const SetPasswordContainer = ({
integration,
@ -36,33 +41,19 @@ const SetPasswordContainer = ({
const { keyFetchToken, unwrapBKey } =
(sensitiveData as SensitiveDataClientAuthKeys) || {};
const createPasswordHandler: CreatePasswordHandler = useCallback(
async (email, sessionToken, newPassword) => {
try {
const passwordCreated = await authClient.createPassword(
sessionToken,
email,
newPassword
);
cache.modify({
id: cache.identify({ __typename: 'Account' }),
fields: {
passwordCreated() {
return passwordCreated;
},
},
});
return { error: null };
} catch (error) {
const { errno } = error as HandledError;
if (errno && AuthUiErrorNos[errno]) {
return { error };
}
return { error: AuthUiErrors.UNEXPECTED_ERROR as HandledError };
}
},
[authClient]
);
const ftlMsgResolver = useFtlMsgResolver();
const location = useLocation();
const [createPasswordLoading, setCreatePasswordLoading] =
useState<boolean>(false);
const [bannerErrorText, setBannerErrorText] = useState<string>('');
const {
offeredSyncEngines,
offeredSyncEngineConfigs,
declinedSyncEngines,
setDeclinedSyncEngines,
// TODO some metrics on this page?
} = useSyncEngines(integration);
const isOAuthNative = isOAuthNativeIntegration(integration);
// Users must be already authenticated on this page. This page is currently always
// for the Sync flow so keys are always required.
@ -78,16 +69,87 @@ const SetPasswordContainer = ({
return <LoadingSpinner />;
}
const createPasswordHandler: CreatePasswordHandler = async (newPassword) => {
try {
const passwordCreated = await authClient.createPassword(
sessionToken,
email,
newPassword
);
cache.modify({
id: cache.identify({ __typename: 'Account' }),
fields: {
passwordCreated() {
return passwordCreated;
},
},
});
const syncEngines = {
offeredEngines: offeredSyncEngines,
declinedEngines: declinedSyncEngines,
};
// GleanMetrics.registration.cwts({ sync: { cwts: syncOptions } });
firefox.fxaLogin({
email,
// Do not send these values if OAuth. Mobile doesn't care about this message, and
// sending these values can cause intermittent sync disconnect issues in oauth desktop.
...(!isOAuthNative && {
keyFetchToken,
unwrapBKey,
}),
sessionToken,
uid,
verified: true,
services: {
sync: syncEngines,
},
});
// TODO: call finishOAuthFlowHandler here or needs reauth?
// if (
// isOAuthNative &&
// oauthData
// ) {
// firefox.fxaOAuthLogin({
// action: 'signin',
// code: oauthData.code,
// redirect: oauthData.redirect,
// state: oauthData.state,
// });
// }
// navigate to inline recovery key setup
const { to } = getSyncNavigate(location.search, true);
navigate(to, {
state: {
email,
uid,
sessionToken,
},
});
} catch (error) {
const localizedErrorMessage = getLocalizedErrorMessage(
ftlMsgResolver,
error
);
setBannerErrorText(localizedErrorMessage);
// if the request errored, loading state must be marked as false to reenable submission
setCreatePasswordLoading(false);
}
};
return (
<SetPassword
{...{
email,
sessionToken,
uid,
createPasswordHandler,
keyFetchToken,
unwrapBKey,
integration,
setCreatePasswordLoading,
createPasswordLoading,
bannerErrorText,
offeredSyncEngineConfigs,
setDeclinedSyncEngines,
}}
/>
);

Просмотреть файл

@ -0,0 +1,50 @@
import React from 'react';
import { Meta } from '@storybook/react';
import { SetPassword } from './index';
import { SetPasswordProps } from './interfaces';
import { withLocalization } from 'fxa-react/lib/storybooks';
import { syncEngineConfigs } from '../../../components/ChooseWhatToSync/sync-engines';
export default {
title: 'Pages/PostVerify/SetPassword',
component: SetPassword,
decorators: [
withLocalization,
(Story) => {
const [, setDeclinedSyncEngines] = React.useState<string[]>([]);
return <Story {...{ setDeclinedSyncEngines }} />;
},
],
} as Meta;
const defaultArgs: Omit<SetPasswordProps, 'setDeclinedSyncEngines'> = {
bannerErrorText: '',
email: 'test@example.com',
createPasswordHandler: async (password: string) => {
console.log('Password created:', password);
},
setCreatePasswordLoading: (loading: boolean) => {
console.log('Loading state:', loading);
},
createPasswordLoading: false,
offeredSyncEngineConfigs: syncEngineConfigs,
};
export const Default = ({
setDeclinedSyncEngines,
}: Pick<SetPasswordProps, 'setDeclinedSyncEngines'>) => (
<SetPassword
{...defaultArgs}
setDeclinedSyncEngines={setDeclinedSyncEngines}
/>
);
export const WithError = ({
setDeclinedSyncEngines,
}: Pick<SetPasswordProps, 'setDeclinedSyncEngines'>) => (
<SetPassword
{...defaultArgs}
bannerErrorText="An error occurred"
setDeclinedSyncEngines={setDeclinedSyncEngines}
/>
);

Просмотреть файл

@ -2,125 +2,31 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { FtlMsg } from 'fxa-react/lib/utils';
import AppLayout from '../../../components/AppLayout';
import { FormSetupAccount } from '../../../components/FormSetupAccount';
import { SetPasswordFormData, SetPasswordProps } from './interfaces';
import { useForm } from 'react-hook-form';
import { useCallback, useState } from 'react';
import firefox from '../../../lib/channels/firefox';
import { isOAuthNativeIntegration, useFtlMsgResolver } from '../../../models';
import { getLocalizedErrorMessage } from '../../../lib/error-utils';
import { useCallback } from 'react';
import Banner from '../../../components/Banner';
import { useLocation, useNavigate } from '@reach/router';
import { getSyncNavigate } from '../../Signin/utils';
import useSyncEngines from '../../../lib/hooks/useSyncEngines';
export const SetPassword = ({
bannerErrorText,
email,
sessionToken,
uid,
createPasswordHandler,
keyFetchToken,
unwrapBKey,
integration,
setCreatePasswordLoading,
createPasswordLoading,
offeredSyncEngineConfigs,
setDeclinedSyncEngines,
}: SetPasswordProps) => {
const ftlMsgResolver = useFtlMsgResolver();
const location = useLocation();
const navigate = useNavigate();
const [createPasswordLoading, setCreatePasswordLoading] =
useState<boolean>(false);
const [bannerErrorText, setBannerErrorText] = useState<string>('');
const {
offeredSyncEngines,
offeredSyncEngineConfigs,
declinedSyncEngines,
setDeclinedSyncEngines,
// TODO some metrics on this page?
selectedEngines,
} = useSyncEngines(integration);
const isOAuthNative = isOAuthNativeIntegration(integration);
const onSubmit = useCallback(
async ({ newPassword }: SetPasswordFormData) => {
setCreatePasswordLoading(true);
const { error } = await createPasswordHandler(
email,
sessionToken,
newPassword
);
if (error) {
const localizedErrorMessage = getLocalizedErrorMessage(
ftlMsgResolver,
error
);
setBannerErrorText(localizedErrorMessage);
// if the request errored, loading state must be marked as false to reenable submission
setCreatePasswordLoading(false);
return;
}
const syncEngines = {
offeredEngines: offeredSyncEngines,
declinedEngines: declinedSyncEngines,
};
// GleanMetrics.registration.cwts({ sync: { cwts: syncOptions } });
firefox.fxaLogin({
email,
// Do not send these values if OAuth. Mobile doesn't care about this message, and
// sending these values can cause intermittent sync disconnect issues in oauth desktop.
...(!isOAuthNative && {
keyFetchToken,
unwrapBKey,
}),
sessionToken,
uid,
verified: true,
services: {
sync: syncEngines,
},
});
// TODO: call finishOAuthFlowHandler here or needs reauth?
// if (
// isOAuthNative &&
// oauthData
// ) {
// firefox.fxaOAuthLogin({
// action: 'signin',
// code: oauthData.code,
// redirect: oauthData.redirect,
// state: oauthData.state,
// });
// }
// navigate to inline recovery key setup
const { to } = getSyncNavigate(location.search, true);
navigate(to, {
state: {
email,
uid,
sessionToken,
},
});
await createPasswordHandler(newPassword);
},
[
createPasswordHandler,
email,
ftlMsgResolver,
isOAuthNative,
keyFetchToken,
location.search,
navigate,
sessionToken,
uid,
unwrapBKey,
declinedSyncEngines,
offeredSyncEngines,
]
[createPasswordHandler, setCreatePasswordLoading]
);
const { handleSubmit, register, getValues, errors, formState, trigger } =

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { HandledError } from '../../../lib/error-utils';
import { syncEngineConfigs } from '../../../components/ChooseWhatToSync/sync-engines';
import { OAuthIntegration } from './../../../models/integrations/oauth-native-integration';
export type SetPasswordIntegration = Pick<OAuthIntegration, 'type' | 'isSync'>;
@ -13,22 +13,14 @@ export interface SetPasswordFormData {
confirmPassword: string;
}
export interface CreatePasswordHandlerError {
error: HandledError | null;
}
export type CreatePasswordHandler = (
email: string,
sessionToken: string,
newPassword: string
) => Promise<CreatePasswordHandlerError>;
export type CreatePasswordHandler = (newPassword: string) => Promise<void>;
export interface SetPasswordProps {
email: string;
sessionToken: string;
uid: string;
createPasswordHandler: CreatePasswordHandler;
keyFetchToken: string;
unwrapBKey: string;
integration: SetPasswordIntegration;
setCreatePasswordLoading: (loading: boolean) => void;
createPasswordLoading: boolean;
bannerErrorText: string;
offeredSyncEngineConfigs: typeof syncEngineConfigs | undefined;
setDeclinedSyncEngines: React.Dispatch<React.SetStateAction<string[]>>;
}