Skip to content

Commit

Permalink
feat(ui): Standardise page layout PasscodeLogin (#258)
Browse files Browse the repository at this point in the history
* wip: PasscodeLogin layout

* fix: alert
  • Loading branch information
sdisalvo-crd authored Nov 17, 2023
1 parent 713586e commit f6476d7
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 103 deletions.
52 changes: 39 additions & 13 deletions src/ui/components/Alert/Alert.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@
padding: 0 !important;

h2 {
margin: 0;
margin: 0 auto 0.5rem;
font-size: 1.125rem;
font-style: normal;
font-weight: 500;
line-height: normal;
}

@media screen and (min-height: 300px) and (max-height: 580px) {
h2 {
font-size: 0.9rem;
line-height: 1.2rem;
}
}
}
}
Expand All @@ -37,22 +48,37 @@
}
}

.alert-button {
font-weight: 500 !important;
text-transform: none !important;
border: solid var(--ion-color-medium-grey) 2px !important;
border-radius: 20px !important;
margin: 0;

&:first-child {
border: none !important;
background: var(--ion-color-primary-gradient) !important;
margin-bottom: 0.5rem;
button.alert-button {
&.sc-ion-alert-ios,
&.sc-ion-alert-md {
margin: 0;
font-size: 1rem;
font-style: normal;
font-weight: 500 !important;
line-height: 1.375rem;
text-transform: none !important;
border: solid var(--ion-color-medium-grey) 2px !important;
border-radius: 20px !important;

&:first-child {
border: none !important;
background: var(--ion-color-primary-gradient) !important;
margin-bottom: 0.5rem;
}
}
}

@media screen and (min-height: 300px) and (max-height: 580px) {
button.alert-button {
&.sc-ion-alert-ios,
&.sc-ion-alert-md {
font-size: 0.9rem;
height: 2.5rem;
}
}
}

.custom-alert {
--min-width: calc(100vw - 40px) !important;
&.md {
.alert-sub-title {
text-align: center;
Expand Down
2 changes: 0 additions & 2 deletions src/ui/components/PageFooter/PageFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import "./PageFooter.scss";

const PageFooter = ({
pageId,
dataTestId,
primaryButtonText,
primaryButtonAction,
primaryButtonDisabled,
Expand All @@ -19,7 +18,6 @@ const PageFooter = ({
<IonToolbar
color="light"
className="page-footer"
data-testid={dataTestId}
>
{primaryButtonText && primaryButtonAction && (
<IonButton
Expand Down
1 change: 0 additions & 1 deletion src/ui/components/PageFooter/PageFooter.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
interface PageFooterProps {
pageId?: string;
dataTestId?: string;
primaryButtonText?: string;
primaryButtonAction?: () => void;
primaryButtonDisabled?: boolean;
Expand Down
9 changes: 7 additions & 2 deletions src/ui/pages/PasscodeLogin/PasscodeLogin.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
.passcode-login {
.passcode-module {
padding-top: 2.5rem;
.responsive-page-content {
justify-content: space-evenly;

.passcode-login-title,
.passcode-login-description {
text-align: center;
}
}
}
120 changes: 117 additions & 3 deletions src/ui/pages/PasscodeLogin/PasscodeLogin.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,95 @@
import { MemoryRouter, Route } from "react-router-dom";
import { MemoryRouter, Route, Router } from "react-router-dom";
import { fireEvent, render, waitFor } from "@testing-library/react";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
import { createMemoryHistory } from "history";
import { PasscodeLogin } from "./PasscodeLogin";
import EN_TRANSLATIONS from "../../../locales/en/en.json";
import { SetPasscode } from "../SetPasscode";
import { store } from "../../../store";

import { KeyStoreKeys, SecureStorage } from "../../../core/storage";
import { RoutePath } from "../../../routes";
import { FIFTEEN_WORDS_BIT_LENGTH } from "../../../constants/appConstants";

interface StoreMocked {
stateCache: {
routes: RoutePath[];
authentication: {
loggedIn: boolean;
time: number;
passcodeIsSet: boolean;
seedPhraseIsSet?: boolean;
};
currentOperation: string;
};
seedPhraseCache: {
seedPhrase160: string;
seedPhrase256: string;
selected: number;
};
cryptoAccountsCache: {
cryptoAccounts: never[];
};
}

const mockStore = configureStore();
const dispatchMock = jest.fn();
const history = createMemoryHistory();
const storeMocked = (initialState: StoreMocked) => {
return {
...mockStore(initialState),
dispatch: dispatchMock,
};
};

describe("Passcode Login Page", () => {
beforeAll(() => {
history.push(RoutePath.PASSCODE_LOGIN);
});

const initialStateWithSeedPhrase = {
stateCache: {
routes: [RoutePath.PASSCODE_LOGIN],
authentication: {
loggedIn: false,
time: Date.now(),
passcodeIsSet: true,
seedPhraseIsSet: true,
},
currentOperation: "",
},
seedPhraseCache: {
seedPhrase160:
"example1 example2 example3 example4 example5 example6 example7 example8 example9 example10 example11 example12 example13 example14 example15",
seedPhrase256: "",
selected: FIFTEEN_WORDS_BIT_LENGTH,
},
cryptoAccountsCache: {
cryptoAccounts: [],
},
};

const initialStateWithoutSeedPhrase = {
stateCache: {
routes: [RoutePath.PASSCODE_LOGIN],
authentication: {
loggedIn: false,
time: Date.now(),
passcodeIsSet: true,
seedPhraseIsSet: false,
},
currentOperation: "",
},
seedPhraseCache: {
seedPhrase160: "",
seedPhrase256: "",
selected: FIFTEEN_WORDS_BIT_LENGTH,
},
cryptoAccountsCache: {
cryptoAccounts: [],
},
};

test("Renders Passcode Login page with title and description", () => {
const { getByText } = render(
<Provider store={store}>
Expand Down Expand Up @@ -40,7 +120,7 @@ describe("Passcode Login Page", () => {

test("If no seed phrase was stored and I click on I forgot my passcode, I can start over", async () => {
const { getByText, findByText } = render(
<Provider store={store}>
<Provider store={storeMocked(initialStateWithoutSeedPhrase)}>
<MemoryRouter initialEntries={[RoutePath.PASSCODE_LOGIN]}>
<Route
path={RoutePath.PASSCODE_LOGIN}
Expand Down Expand Up @@ -72,6 +152,40 @@ describe("Passcode Login Page", () => {
).toBeVisible();
});

test("If a seed phrase was stored and I click on I forgot my passcode, I can start over", async () => {
const { getByText, findByText } = render(
<Provider store={storeMocked(initialStateWithSeedPhrase)}>
<MemoryRouter initialEntries={[RoutePath.PASSCODE_LOGIN]}>
<Route
path={RoutePath.PASSCODE_LOGIN}
component={PasscodeLogin}
/>
<Route
path={RoutePath.SET_PASSCODE}
component={SetPasscode}
/>
</MemoryRouter>
</Provider>
);
fireEvent.click(getByText(/1/));
fireEvent.click(getByText(/2/));
fireEvent.click(getByText(/3/));
fireEvent.click(getByText(/4/));
fireEvent.click(getByText(/5/));
fireEvent.click(getByText(/6/));
expect(await findByText(EN_TRANSLATIONS.passcodelogin.error)).toBeVisible();
fireEvent.click(getByText(EN_TRANSLATIONS.passcodelogin.forgotten.button));
expect(
await findByText(EN_TRANSLATIONS.passcodelogin.alert.text.verify)
).toBeVisible();
fireEvent.click(
getByText(EN_TRANSLATIONS.passcodelogin.alert.button.verify)
);
expect(
await findByText(EN_TRANSLATIONS.setpasscode.enterpasscode.title)
).toBeVisible();
});

test("verifies passcode and navigates to next route", async () => {
const storedPass = "storedPass";
const secureStorageGetMock = jest
Expand Down
112 changes: 49 additions & 63 deletions src/ui/pages/PasscodeLogin/PasscodeLogin.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { useEffect, useState } from "react";
import { IonButton, IonCol, IonGrid, IonPage, IonRow } from "@ionic/react";
import { useHistory } from "react-router-dom";
import { i18n } from "../../../i18n";
import { PageLayout } from "../../components/layout/PageLayout";
import {
ErrorMessage,
MESSAGE_MILLISECONDS,
Expand All @@ -22,6 +20,9 @@ import { updateReduxState } from "../../../store/utils";
import "./PasscodeLogin.scss";
import { getBackRoute } from "../../../routes/backRoute";
import { RoutePath } from "../../../routes";
import PageFooter from "../../components/PageFooter/PageFooter";
import { ResponsivePageLayout } from "../../components/layout/ResponsivePageLayout";
import { PageHeader } from "../../components/PageHeader";

const PasscodeLogin = () => {
const history = useHistory();
Expand Down Expand Up @@ -129,67 +130,52 @@ const PasscodeLogin = () => {
};

return (
<IonPage className="page-layout passcode-login safe-area">
<PageLayout currentPath={RoutePath.PASSCODE_LOGIN}>
<IonGrid>
<IonRow>
<IonCol
className="passcode-login-title"
data-testid="passcode-login-title"
>
{i18n.t("passcodelogin.title")}
</IonCol>
</IonRow>
<IonRow>
<IonCol
className="passcode-login-description"
data-testid="passcode-login-description"
>
{i18n.t("passcodelogin.description")}
</IonCol>
</IonRow>
</IonGrid>
<PasscodeModule
error={
<ErrorMessage
message={
passcode.length === 6 && passcodeIncorrect
? `${i18n.t("passcodelogin.error")}`
: undefined
}
timeout={true}
/>
}
passcode={passcode}
handlePinChange={handlePinChange}
handleRemove={handleRemove}
/>
<IonGrid>
<IonRow>
<IonCol className="continue-col">
<IonButton
shape="round"
expand="block"
fill="outline"
className="secondary-button"
onClick={() => setIsOpen(true)}
>
{i18n.t("passcodelogin.forgotten.button")}
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
<Alert
isOpen={isOpen}
setIsOpen={setIsOpen}
dataTestId="alert-forgotten"
headerText={headerText}
confirmButtonText={confirmButtonText}
cancelButtonText={cancelButtonText}
actionConfirm={resetPasscode}
/>
</PageLayout>
</IonPage>
<ResponsivePageLayout
title={"passcode-login"}
header={<PageHeader currentPath={RoutePath.PASSCODE_LOGIN} />}
>
<h2
className="passcode-login-title"
data-testid="passcode-login-title"
>
{i18n.t("passcodelogin.title")}
</h2>
<p
className="passcode-login-description small-hide"
data-testid="passcode-login-description"
>
{i18n.t("passcodelogin.description")}
</p>
<PasscodeModule
error={
<ErrorMessage
message={
passcode.length === 6 && passcodeIncorrect
? `${i18n.t("passcodelogin.error")}`
: undefined
}
timeout={true}
/>
}
passcode={passcode}
handlePinChange={handlePinChange}
handleRemove={handleRemove}
/>
<PageFooter
pageId={"passcode-login"}
secondaryButtonText={`${i18n.t("passcodelogin.forgotten.button")}`}
secondaryButtonAction={() => setIsOpen(true)}
/>
<Alert
isOpen={isOpen}
setIsOpen={setIsOpen}
dataTestId="alert-forgotten"
headerText={headerText}
confirmButtonText={confirmButtonText}
cancelButtonText={cancelButtonText}
actionConfirm={resetPasscode}
/>
</ResponsivePageLayout>
);
};

Expand Down
1 change: 0 additions & 1 deletion src/ui/pages/SetPasscode/SetPasscode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ const SetPasscode = () => {
{originalPassCode !== "" ? (
<PageFooter
pageId={"set-passcode"}
data-testid="forgot-your-passcode-button"
secondaryButtonText={`${i18n.t("setpasscode.startover.label")}`}
secondaryButtonAction={() => handleClearState()}
/>
Expand Down
Loading

0 comments on commit f6476d7

Please sign in to comment.