0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(ui): update social bind account page content (#2968)

This commit is contained in:
simeng-li 2023-01-18 14:26:17 +08:00 committed by GitHub
parent ff2abdceeb
commit 03f03fe354
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 291 additions and 34 deletions

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: 'Sign in with verification code', // UNTRANSLATED
sign_in_via_password: 'Sign in with password', // UNTRANSLATED
change: 'Change {{method}}', // UNTRANSLATED
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: 'Email',
@ -58,11 +61,13 @@ const translation = {
'Das Konto mit {{type}} {{value}} existiert nicht, möchtest du ein neues Konto erstellen?',
sign_in_id_does_not_exist_alert: 'The account with {{type}} {{value}} does not exist.', // UNTRANSLATED
create_account_id_exists_alert: 'The account with {{type}} {{value}} already exists', // UNTRANSLATED
forgot_password_id_does_not_exits: 'Das Konto mit {{type}} {{value}} existiert nicht.',
bind_account_title: 'Konto verknüpfen',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: 'Link or create account', // UNTRANSLATED
social_create_account: 'Kein Konto? Du kannst ein neues Konto erstellen und es verknüpfen.',
social_bind_account:
'Besitzt du schon ein Konto? Melde dich an, um die Identität zu verknüpfen.',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing: 'Wir haben ein Konto gefunden, das du verknüpfen kannst.',
reset_password: 'Passwort zurücksetzen',
reset_password_description_email:

View file

@ -33,6 +33,9 @@ const translation = {
sign_in_via_passcode: 'Sign in with verification code',
sign_in_via_password: 'Sign in with password',
change: 'Change {{method}}',
link_another_email: 'Link another email',
link_another_phone: 'Link another phone',
link_another_email_or_phone: 'Link another email or phone',
},
description: {
email: 'email',
@ -56,9 +59,13 @@ const translation = {
'The account with {{type}} {{value}} does not exist, would you like to create a new account?',
sign_in_id_does_not_exist_alert: 'The account with {{type}} {{value}} does not exist.',
create_account_id_exists_alert: 'The account with {{type}} {{value}} already exists',
bind_account_title: 'Link account',
social_create_account: 'No account? You can create a new account and link.',
social_bind_account: 'Already had an account? Sign in to link it with your social identity.',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}',
bind_account_title: 'Link or create account',
social_create_account: 'You can create a new account and link.',
social_link_email: 'You can link another email',
social_link_phone: 'You can link another phone',
social_link_email_or_phone: 'You can link another email or phone',
social_bind_with_existing: 'We find a related account, you can link it directly.',
reset_password: 'Reset password',
reset_password_description_email:

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: 'Sign in with verification code', // UNTRANSLATED
sign_in_via_password: 'Sign in with password', // UNTRANSLATED
change: 'Change {{method}}', // UNTRANSLATED
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: 'email',
@ -58,10 +61,13 @@ const translation = {
"Le compte avec {{type}} {{value}} n'existe pas, voulez-vous créer un nouveau compte ?",
sign_in_id_does_not_exist_alert: 'The account with {{type}} {{value}} does not exist.', // UNTRANSLATED
create_account_id_exists_alert: 'The account with {{type}} {{value}} already exists', // UNTRANSLATED
bind_account_title: 'Lier le compte',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: 'Link or create account', // UNTRANSLATED
social_create_account: 'Pas de compte ? Vous pouvez créer un nouveau compte et un lien.',
social_bind_account:
'Vous avez déjà un compte ? Connectez-vous pour le relier à votre identité sociale.',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing:
'Nous trouvons un compte connexe, vous pouvez le relier directement.',
reset_password: 'Réinitialiser le mot de passe',

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: '인증번호로 로그인',
sign_in_via_password: '비밀번호로 로그인',
change: 'Change {{change}}', // UNTRANSLATED,
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: '이메일',
@ -56,9 +59,13 @@ const translation = {
sign_in_id_does_not_exist: '{type}} {{value}} 계정이 존재하지 않아요. 새로 만드시겠어요?',
sign_in_id_does_not_exist_alert: '{{type}} {{value}} 계정이 존재하지 않아요.',
create_account_id_exists_alert: '{{type}} {{value}} 이미 존재해요.',
bind_account_title: '계정 연동',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: 'Link or create account', // UNTRANSLATED
social_create_account: '계정이 없으신가요? 새로운 계정을 만들고 연동해보세요.',
social_bind_account: '계정이 이미 있으신가요? 로그인하여 다른 계정과 연동해보세요.',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing: '관련된 계정을 찾았어요. 해당 계정과 연동할 수 있습니다.',
reset_password: '암호를 재설정',
reset_password_description_email:

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: 'Entrar com código de verificação',
sign_in_via_password: 'Entrar com senha',
change: 'Change {{change}}', // UNTRANSLATED,
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: 'e-mail',
@ -57,9 +60,13 @@ const translation = {
'A conta com {{type}} {{value}} não existe, gostaria de criar uma nova conta?',
sign_in_id_does_not_exist_alert: 'A conta com {{type}} {{value}} não existe.',
create_account_id_exists_alert: 'A conta com {{type}} {{value}} já existe',
bind_account_title: 'Link da conta',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: 'Link or create account', // UNTRANSLATED
social_create_account: 'Sem conta? Você pode criar uma nova conta e link.',
social_bind_account: 'Já tinha uma conta? Faça login para vinculá-lo à sua identidade social.',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing:
'Encontramos uma conta relacionada, você pode vinculá-la diretamente.',
reset_password: 'Redefinir senha',

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: 'Sign in with verification code', // UNTRANSLATED
sign_in_via_password: 'Sign in with password', // UNTRANSLATED
change: 'Change {{method}}', // UNTRANSLATED
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: 'email',
@ -56,9 +59,13 @@ const translation = {
sign_in_id_does_not_exist: 'A conta com {{type}} {{value}} não existe, gostaria de criar uma?',
sign_in_id_does_not_exist_alert: 'The account with {{type}} {{value}} does not exist.', // UNTRANSLATED
create_account_id_exists_alert: 'The account with {{type}} {{value}} already exists', // UNTRANSLATED
bind_account_title: 'Agregar conta',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: 'Link or create account', // UNTRANSLATED
social_create_account: 'Sem conta? Pode criar uma nova e agregar.',
social_bind_account: 'Já tem uma conta? Faça login para agregar a sua identidade social.',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing: 'Encontramos uma conta relacionada, pode agrega-la diretamente.',
reset_password: 'Redefinir Password',
reset_password_description_email:

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: 'Sign in with verification code', // UNTRANSLATED
sign_in_via_password: 'Sign in with password', // UNTRANSLATED
change: 'Change {{method}}', // UNTRANSLATED
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: 'e-posta adresi',
@ -57,9 +60,13 @@ const translation = {
'{{type}} {{value}} ile hesap mevcut değil, yeni bir hesap oluşturmak ister misiniz?',
sign_in_id_does_not_exist_alert: 'The account with {{type}} {{value}} does not exist.', // UNTRANSLATED
create_account_id_exists_alert: 'The account with {{type}} {{value}} already exists', // UNTRANSLATED
bind_account_title: 'Hesap bağla',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: 'Link or create account', // UNTRANSLATED
social_create_account: 'Hesabınız yok mu? Yeni bir hesap ve bağlantı oluşturabilirsiniz.',
social_bind_account: 'Hesabınız zaten var mı? Hesabınıza bağlanmak için giriş yapınız.',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing: 'İlgili bir hesap bulduk, hemen bağlayabilirsiniz.',
reset_password: 'Şifre yenile',
reset_password_description_email:

View file

@ -35,6 +35,9 @@ const translation = {
sign_in_via_passcode: '用验证码登录',
sign_in_via_password: '密码登录',
change: '更改{{method}}',
link_another_email: 'Link another email', // UNTRANSLATED
link_another_phone: 'Link another phone', // UNTRANSLATED
link_another_email_or_phone: 'Link another email or phone', // UNTRANSLATED
},
description: {
email: '邮箱',
@ -56,9 +59,13 @@ const translation = {
sign_in_id_does_not_exist: '{{ type }}为 {{ value }} 的帐号不存在,你要创建一个新帐号吗?',
sign_in_id_does_not_exist_alert: '{{ type }}为 {{ value }} 的帐号不存在。',
create_account_id_exists_alert: '{{ type }}为 {{ value }} 的帐号已存在',
bind_account_title: '绑定帐号',
social_identity_exist:
'The {{type}} {{value}} is linked to another account. Please try another {{type}}', // UNTRANSLATED
bind_account_title: '绑定或创建帐号',
social_create_account: '没有帐号?你可以创建一个帐号并绑定。',
social_bind_account: '已有帐号?登录以绑定社交身份。',
social_link_email: 'You can link another email', // UNTRANSLATED,
social_link_phone: 'You can link another phone', // UNTRANSLATED,
social_link_email_or_phone: 'You can link another email or phone', // UNTRANSLATED,
social_bind_with_existing: '找到了一个匹配的帐号,你可以直接绑定。',
reset_password: '重设密码',
reset_password_description_email: '输入邮件地址,领取验证码以重设密码。',

View file

@ -1,6 +1,6 @@
import type { I18nKey } from '@logto/phrases-ui';
import classNames from 'classnames';
import type { HTMLProps } from 'react';
import type { TFuncKey } from 'react-i18next';
import { useTranslation } from 'react-i18next';
import * as styles from './index.module.scss';
@ -17,7 +17,7 @@ type BaseProps = Omit<HTMLProps<HTMLButtonElement>, 'type' | 'size' | 'title'> &
};
type Props = BaseProps & {
title: I18nKey;
title: TFuncKey;
i18nProps?: Record<string, string>;
};

View file

@ -1,6 +1,9 @@
import { SignInIdentifier } from '@logto/schemas';
import { fireEvent, waitFor } from '@testing-library/react';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import { mockSignInExperienceSettings } from '@/__mocks__/logto';
import { registerWithVerifiedSocial, bindSocialRelatedUser } from '@/apis/interaction';
import SocialLinkAccount from '.';
@ -26,7 +29,9 @@ describe('SocialLinkAccount', () => {
it('should render bindUser Button', async () => {
const { getByText } = renderWithPageContext(
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
<SettingsProvider>
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
</SettingsProvider>
);
const bindButton = getByText('action.bind');
@ -40,9 +45,71 @@ describe('SocialLinkAccount', () => {
});
});
it('should render link email with email signUp identifier', () => {
const { queryByText } = renderWithPageContext(
<SettingsProvider
settings={{
...mockSignInExperienceSettings,
signUp: {
identifiers: [SignInIdentifier.Email],
verify: true,
password: true,
},
}}
>
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
</SettingsProvider>
);
expect(queryByText('description.social_link_email')).not.toBeNull();
expect(queryByText('action.link_another_email')).not.toBeNull();
});
it('should render link phone with phone signUp identifier', () => {
const { queryByText } = renderWithPageContext(
<SettingsProvider
settings={{
...mockSignInExperienceSettings,
signUp: {
identifiers: [SignInIdentifier.Phone],
verify: true,
password: true,
},
}}
>
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
</SettingsProvider>
);
expect(queryByText('description.social_link_phone')).not.toBeNull();
expect(queryByText('action.link_another_phone')).not.toBeNull();
});
it('should render link phone or email with phone and email signUp identifiers', () => {
const { queryByText } = renderWithPageContext(
<SettingsProvider
settings={{
...mockSignInExperienceSettings,
signUp: {
identifiers: [SignInIdentifier.Email, SignInIdentifier.Phone],
verify: true,
password: true,
},
}}
>
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
</SettingsProvider>
);
expect(queryByText('description.social_link_email_or_phone')).not.toBeNull();
expect(queryByText('action.link_another_email_or_phone')).not.toBeNull();
});
it('should call registerWithVerifiedSocial when click create button', async () => {
const { getByText } = renderWithPageContext(
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
<SettingsProvider>
<SocialLinkAccount connectorId="github" relatedUser={relatedUser} />
</SettingsProvider>
);
const createButton = getByText('action.create');

View file

@ -1,8 +1,11 @@
import { SignInIdentifier } from '@logto/schemas';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import type { TFuncKey } from 'react-i18next';
import Button from '@/components/Button';
import Divider from '@/components/Divider';
import { useSieMethods } from '@/hooks/use-sie';
import useBindSocialRelatedUser from '@/hooks/use-social-link-related-user';
import useSocialRegister from '@/hooks/use-social-register';
import type { SocialRelatedUserInfo } from '@/types/guard';
@ -16,12 +19,48 @@ type Props = {
relatedUser: SocialRelatedUserInfo;
};
const getCreateAccountContent = (
signUpMethods: string[]
): { desc: TFuncKey; buttonText: TFuncKey } => {
if (
signUpMethods.includes(SignInIdentifier.Email) &&
signUpMethods.includes(SignInIdentifier.Phone)
) {
return {
desc: 'description.social_link_email_or_phone',
buttonText: 'action.link_another_email_or_phone',
};
}
if (signUpMethods.includes(SignInIdentifier.Email)) {
return {
desc: 'description.social_link_email',
buttonText: 'action.link_another_email',
};
}
if (signUpMethods.includes(SignInIdentifier.Phone)) {
return {
desc: 'description.social_link_phone',
buttonText: 'action.link_another_phone',
};
}
return {
desc: 'description.social_create_account',
buttonText: 'action.create',
};
};
const SocialLinkAccount = ({ connectorId, className, relatedUser }: Props) => {
const { t } = useTranslation();
const { signUpMethods } = useSieMethods();
const bindSocialRelatedUser = useBindSocialRelatedUser();
const registerWithSocial = useSocialRegister(connectorId);
const content = getCreateAccountContent(signUpMethods);
const { type, value } = relatedUser;
return (
@ -41,10 +80,10 @@ const SocialLinkAccount = ({ connectorId, className, relatedUser }: Props) => {
<Divider label="description.or" className={styles.divider} />
<div className={styles.desc}>{t('description.social_create_account')}</div>
<div className={styles.desc}>{t(content.desc)}</div>
<Button
title="action.create"
title={content.buttonText}
type="secondary"
onClick={() => {
void registerWithSocial(connectorId);

View file

@ -1,6 +1,10 @@
import { render } from '@testing-library/react';
import { SignInIdentifier } from '@logto/schemas';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import { mockSignInExperienceSettings } from '@/__mocks__/logto';
import SocialRegister from '.';
jest.mock('react-router-dom', () => ({
@ -12,14 +16,85 @@ jest.mock('react-router-dom', () => ({
describe('SocialRegister', () => {
it('render', () => {
const { queryByText } = render(
<MemoryRouter initialEntries={['/social/link/github']}>
<Routes>
<Route path="/social/link/:connector" element={<SocialRegister />} />
</Routes>
</MemoryRouter>
const { queryByText } = renderWithPageContext(
<SettingsProvider>
<MemoryRouter initialEntries={['/social/link/github']}>
<Routes>
<Route path="/social/link/:connector" element={<SocialRegister />} />
</Routes>
</MemoryRouter>
</SettingsProvider>
);
expect(queryByText('description.bind_account_title')).not.toBeNull();
expect(queryByText('description.social_create_account')).not.toBeNull();
});
it('render link email', () => {
const { queryByText } = renderWithPageContext(
<SettingsProvider
settings={{
...mockSignInExperienceSettings,
signUp: {
identifiers: [SignInIdentifier.Email],
verify: true,
password: true,
},
}}
>
<MemoryRouter initialEntries={['/social/link/github']}>
<Routes>
<Route path="/social/link/:connector" element={<SocialRegister />} />
</Routes>
</MemoryRouter>
</SettingsProvider>
);
expect(queryByText('description.link_email')).not.toBeNull();
expect(queryByText('description.social_link_email')).not.toBeNull();
});
it('render link phone', () => {
const { queryByText } = renderWithPageContext(
<SettingsProvider
settings={{
...mockSignInExperienceSettings,
signUp: {
identifiers: [SignInIdentifier.Phone],
verify: true,
password: true,
},
}}
>
<MemoryRouter initialEntries={['/social/link/github']}>
<Routes>
<Route path="/social/link/:connector" element={<SocialRegister />} />
</Routes>
</MemoryRouter>
</SettingsProvider>
);
expect(queryByText('description.link_phone')).not.toBeNull();
expect(queryByText('description.social_link_phone')).not.toBeNull();
});
it('render link email or phone', () => {
const { queryByText } = renderWithPageContext(
<SettingsProvider
settings={{
...mockSignInExperienceSettings,
signUp: {
identifiers: [SignInIdentifier.Phone, SignInIdentifier.Email],
verify: true,
password: true,
},
}}
>
<MemoryRouter initialEntries={['/social/link/github']}>
<Routes>
<Route path="/social/link/:connector" element={<SocialRegister />} />
</Routes>
</MemoryRouter>
</SettingsProvider>
);
expect(queryByText('description.link_email_or_phone')).not.toBeNull();
expect(queryByText('description.social_link_email_or_phone')).not.toBeNull();
});
});

View file

@ -1,8 +1,11 @@
import { SignInIdentifier } from '@logto/schemas';
import type { TFuncKey } from 'react-i18next';
import { useParams, useLocation } from 'react-router-dom';
import { is } from 'superstruct';
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
import SocialLinkAccountContainer from '@/containers/SocialLinkAccount';
import { useSieMethods } from '@/hooks/use-sie';
import ErrorPage from '@/pages/ErrorPage';
import { socialAccountNotExistErrorDataGuard } from '@/types/guard';
@ -10,9 +13,29 @@ type Parameters = {
connector: string;
};
const getPageTitle = (signUpMethods: SignInIdentifier[]): TFuncKey => {
if (
signUpMethods.includes(SignInIdentifier.Email) &&
signUpMethods.includes(SignInIdentifier.Phone)
) {
return 'description.link_email_or_phone';
}
if (signUpMethods.includes(SignInIdentifier.Email)) {
return 'description.link_email';
}
if (signUpMethods.includes(SignInIdentifier.Phone)) {
return 'description.link_phone';
}
return 'description.bind_account_title';
};
const SocialLinkAccount = () => {
const { connector } = useParams<Parameters>();
const { state } = useLocation();
const { signUpMethods } = useSieMethods();
if (!is(state, socialAccountNotExistErrorDataGuard)) {
return <ErrorPage rawMessage="Missing relate account info" />;
@ -25,7 +48,7 @@ const SocialLinkAccount = () => {
const { relatedUser } = state;
return (
<SecondaryPageWrapper title="description.bind_account_title">
<SecondaryPageWrapper title={getPageTitle(signUpMethods)}>
<SocialLinkAccountContainer connectorId={connector} relatedUser={relatedUser} />
</SecondaryPageWrapper>
);