зеркало из https://github.com/mozilla/fxa.git
Merge pull request #17403 from mozilla/FXA-8635
feat(react): Build out Index / email-first component
This commit is contained in:
Коммит
3719aa8c7c
|
@ -0,0 +1,13 @@
|
|||
## Index / home page
|
||||
|
||||
index-header = Enter your email
|
||||
index-sync-header = Continue to your { -product-mozilla-account }
|
||||
index-sync-subheader = Sync your passwords, tabs, and bookmarks everywhere you use { -brand-firefox }.
|
||||
# $serviceName - the service (e.g., Pontoon) that the user is signing into with a Mozilla account
|
||||
index-subheader-with-servicename = Continue to { $serviceName }
|
||||
index-subheader-with-logo = Continue to <span>{ $serviceLogo }</span>
|
||||
index-subheader-default = Continue to account settings
|
||||
index-cta = Sign up or sign in
|
||||
index-account-info = A { -product-mozilla-account } also unlocks access to more privacy-protecting products from { -brand-mozilla }.
|
||||
index-email-input =
|
||||
.label = Enter your email
|
|
@ -0,0 +1,53 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 Index from '.';
|
||||
import { Meta } from '@storybook/react';
|
||||
import { withLocalization } from 'fxa-react/lib/storybooks';
|
||||
import { IndexProps } from './interfaces';
|
||||
import {
|
||||
createMockIndexOAuthIntegration,
|
||||
createMockIndexSyncIntegration,
|
||||
Subject,
|
||||
} from './mocks';
|
||||
import {
|
||||
MONITOR_CLIENTIDS,
|
||||
POCKET_CLIENTIDS,
|
||||
} from '../../models/integrations/client-matching';
|
||||
import { MozServices } from '../../lib/types';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Index',
|
||||
component: Index,
|
||||
decorators: [withLocalization],
|
||||
} as Meta;
|
||||
|
||||
const storyWithProps = ({
|
||||
...props // overrides
|
||||
}: Partial<IndexProps> = {}) => {
|
||||
const story = () => <Subject {...props} />;
|
||||
return story;
|
||||
};
|
||||
|
||||
export const Default = storyWithProps();
|
||||
|
||||
export const Sync = storyWithProps({
|
||||
integration: createMockIndexSyncIntegration(),
|
||||
serviceName: MozServices.FirefoxSync,
|
||||
});
|
||||
|
||||
export const Monitor = storyWithProps({
|
||||
integration: createMockIndexOAuthIntegration({
|
||||
clientId: MONITOR_CLIENTIDS[0],
|
||||
}),
|
||||
serviceName: MozServices.Monitor,
|
||||
});
|
||||
|
||||
export const Pocket = storyWithProps({
|
||||
integration: createMockIndexOAuthIntegration({
|
||||
clientId: POCKET_CLIENTIDS[0],
|
||||
}),
|
||||
serviceName: MozServices.Pocket,
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 { screen } from '@testing-library/react';
|
||||
import {
|
||||
createMockIndexOAuthIntegration,
|
||||
createMockIndexSyncIntegration,
|
||||
Subject,
|
||||
} from './mocks';
|
||||
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
|
||||
import { POCKET_CLIENTIDS } from '../../models/integrations/client-matching';
|
||||
import { MozServices } from '../../lib/types';
|
||||
|
||||
const syncText =
|
||||
'Sync your passwords, tabs, and bookmarks everywhere you use Firefox.';
|
||||
const syncTextSecondary =
|
||||
'A Mozilla account also unlocks access to more privacy-protecting products from Mozilla.';
|
||||
|
||||
function thirdPartyAuthWithSeparatorRendered() {
|
||||
screen.getByText('or');
|
||||
screen.getByRole('button', {
|
||||
name: /Continue with Google/,
|
||||
});
|
||||
screen.getByRole('button', {
|
||||
name: /Continue with Apple/,
|
||||
});
|
||||
}
|
||||
|
||||
describe('Index page', () => {
|
||||
it('renders as expected with web integration', () => {
|
||||
renderWithLocalizationProvider(<Subject />);
|
||||
|
||||
screen.getByRole('heading', { name: 'Enter your email' });
|
||||
screen.getByText('Continue to account settings');
|
||||
screen.getByLabelText('Enter your email');
|
||||
screen.getByRole('button', { name: 'Sign up or sign in' });
|
||||
|
||||
expect(screen.queryByText(syncText)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(syncTextSecondary)).not.toBeInTheDocument();
|
||||
|
||||
thirdPartyAuthWithSeparatorRendered();
|
||||
|
||||
expect(
|
||||
screen.getByRole('link', {
|
||||
name: /Terms of Service/,
|
||||
})
|
||||
).toHaveAttribute('href', '/legal/terms');
|
||||
});
|
||||
it('renders as expected when sync', () => {
|
||||
renderWithLocalizationProvider(
|
||||
<Subject
|
||||
integration={createMockIndexSyncIntegration()}
|
||||
serviceName={MozServices.FirefoxSync}
|
||||
/>
|
||||
);
|
||||
|
||||
screen.getByRole('heading', { name: 'Continue to your Mozilla account' });
|
||||
screen.getByText(syncText);
|
||||
|
||||
screen.getByText(syncTextSecondary);
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /Continue with Google/ })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: /Continue with Apple/ })
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByRole('link', {
|
||||
name: /Terms of Service/,
|
||||
})
|
||||
).toHaveAttribute('href', '/legal/terms');
|
||||
});
|
||||
|
||||
it('renders as expected when client is Pocket', () => {
|
||||
renderWithLocalizationProvider(
|
||||
<Subject
|
||||
integration={createMockIndexOAuthIntegration({
|
||||
clientId: POCKET_CLIENTIDS[0],
|
||||
})}
|
||||
serviceName={MozServices.Pocket}
|
||||
/>
|
||||
);
|
||||
|
||||
screen.getByRole('heading', { name: 'Enter your email' });
|
||||
screen.getByAltText('Pocket');
|
||||
|
||||
thirdPartyAuthWithSeparatorRendered();
|
||||
|
||||
const tosLinks = screen.getAllByRole('link', {
|
||||
name: /Terms of Service/,
|
||||
});
|
||||
|
||||
expect(tosLinks[0]).toHaveAttribute('href', 'https://getpocket.com/tos/');
|
||||
expect(tosLinks[1]).toHaveAttribute('href', '/legal/terms');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 { RouteComponentProps } from '@reach/router';
|
||||
import { IndexProps } from './interfaces';
|
||||
import AppLayout from '../../components/AppLayout';
|
||||
import CardHeader from '../../components/CardHeader';
|
||||
import InputText from '../../components/InputText';
|
||||
import { FtlMsg } from 'fxa-react/lib/utils';
|
||||
import ThirdPartyAuth from '../../components/ThirdPartyAuth';
|
||||
import TermsPrivacyAgreement from '../../components/TermsPrivacyAgreement';
|
||||
import { isOAuthIntegration } from '../../models';
|
||||
import {
|
||||
isClientMonitor,
|
||||
isClientPocket,
|
||||
} from '../../models/integrations/client-matching';
|
||||
|
||||
export const Index = ({
|
||||
integration,
|
||||
serviceName,
|
||||
}: IndexProps & RouteComponentProps) => {
|
||||
const clientId = integration.getService();
|
||||
const isSync = integration.isSync();
|
||||
const isOAuth = isOAuthIntegration(integration);
|
||||
const isPocketClient = isOAuth && isClientPocket(clientId);
|
||||
const isMonitorClient = isOAuth && isClientMonitor(clientId);
|
||||
return (
|
||||
<AppLayout>
|
||||
{isSync ? (
|
||||
<>
|
||||
<h1 className="card-header">
|
||||
<FtlMsg id="index-sync-header">
|
||||
Continue to your Mozilla account
|
||||
</FtlMsg>
|
||||
</h1>
|
||||
<p className="mt-1 mb-9 text-sm">
|
||||
<FtlMsg id="index-sync-subheader">
|
||||
Sync your passwords, tabs, and bookmarks everywhere you use
|
||||
Firefox.
|
||||
</FtlMsg>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<CardHeader
|
||||
headingText="Enter your email"
|
||||
headingTextFtlId="index-header"
|
||||
subheadingWithDefaultServiceFtlId="index-subheader-default"
|
||||
subheadingWithCustomServiceFtlId="index-subheader-with-servicename"
|
||||
subheadingWithLogoFtlId="index-subheader-with-logo"
|
||||
{...{ clientId, serviceName }}
|
||||
/>
|
||||
)}
|
||||
<FtlMsg id="index-email-input" attrs={{ label: true }}>
|
||||
<InputText className="mt-8" type="email" label="Enter your email" />
|
||||
</FtlMsg>
|
||||
<div className="flex mt-5">
|
||||
<button className="cta-primary cta-xl" type="submit">
|
||||
<FtlMsg id="index-cta">Sign up or sign in</FtlMsg>
|
||||
</button>
|
||||
</div>
|
||||
{isSync ? (
|
||||
<p className="mt-5 text-xs text-grey-500">
|
||||
<FtlMsg id="index-account-info">
|
||||
A Mozilla account also unlocks access to more privacy-protecting
|
||||
products from Mozilla.
|
||||
</FtlMsg>
|
||||
</p>
|
||||
) : (
|
||||
<ThirdPartyAuth showSeparator viewName="index" />
|
||||
)}
|
||||
<TermsPrivacyAgreement {...{ isPocketClient, isMonitorClient }} />
|
||||
</AppLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
|
@ -0,0 +1,16 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 { MozServices } from '../../lib/types';
|
||||
import { Integration } from '../../models';
|
||||
|
||||
export type IndexIntegration = Pick<
|
||||
Integration,
|
||||
'type' | 'isSync' | 'getService'
|
||||
>;
|
||||
|
||||
export interface IndexProps {
|
||||
integration: IndexIntegration;
|
||||
serviceName: MozServices;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 { LocationProvider } from '@reach/router';
|
||||
import { MozServices } from '../../lib/types';
|
||||
import { IntegrationType } from '../../models';
|
||||
import { IndexIntegration } from './interfaces';
|
||||
import Index from '.';
|
||||
import { MOCK_CLIENT_ID } from '../mocks';
|
||||
|
||||
export function createMockIndexWebIntegration(): IndexIntegration {
|
||||
return {
|
||||
type: IntegrationType.Web,
|
||||
isSync: () => false,
|
||||
getService: () => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function createMockIndexSyncIntegration(): IndexIntegration {
|
||||
return {
|
||||
type: IntegrationType.OAuth,
|
||||
isSync: () => true,
|
||||
getService: () => MOCK_CLIENT_ID,
|
||||
};
|
||||
}
|
||||
|
||||
export function createMockIndexOAuthIntegration({
|
||||
clientId = MOCK_CLIENT_ID,
|
||||
}): IndexIntegration {
|
||||
return {
|
||||
type: IntegrationType.OAuth,
|
||||
isSync: () => false,
|
||||
getService: () => clientId,
|
||||
};
|
||||
}
|
||||
|
||||
export const Subject = ({
|
||||
integration = createMockIndexWebIntegration(),
|
||||
serviceName = MozServices.Default,
|
||||
}: {
|
||||
integration?: IndexIntegration;
|
||||
serviceName?: MozServices;
|
||||
}) => {
|
||||
return (
|
||||
<LocationProvider>
|
||||
<Index
|
||||
{...{
|
||||
integration,
|
||||
serviceName,
|
||||
}}
|
||||
/>
|
||||
</LocationProvider>
|
||||
);
|
||||
};
|
Загрузка…
Ссылка в новой задаче