Skip to content
Snippets Groups Projects
Unverified Commit afa548bc authored by Aleksandr Lesnenko's avatar Aleksandr Lesnenko Committed by GitHub
Browse files

fix reset password flow (#24731)

* fix reset password flow

* review
parent 2531da31
No related branches found
No related tags found
No related merge requests found
......@@ -59,6 +59,7 @@ export const InfoTitle = styled.div`
export const InfoMessage = styled.div`
color: ${color("text-dark")};
text-align: center;
margin-bottom: 1rem;
`;
export const InfoLink = styled(Link)`
......
import React, { useCallback, useMemo, useState } from "react";
import { t } from "ttag";
import Users from "metabase/entities/users";
import Button from "metabase/core/components/Button";
import AuthLayout from "../../containers/AuthLayout";
import { ForgotPasswordData } from "../../types";
import {
......@@ -99,10 +100,7 @@ const ForgotPasswordSuccess = (): JSX.Element => {
<InfoMessage>
{t`Check your email for instructions on how to reset your password.`}
</InfoMessage>
<InfoLink
className="Button Button--primary"
to="/auth/login"
>{t`Back to sign in`}</InfoLink>
<Button primary as="a" href="/auth/login">{t`Back to sign in`}</Button>
</InfoBody>
);
};
......
......@@ -23,20 +23,6 @@ export const InfoBody = styled.div`
align-items: center;
`;
export const InfoIcon = styled(Icon)`
display: block;
color: ${color("brand")};
width: 1.5rem;
height: 1.5rem;
`;
export const InfoIconContainer = styled.div`
padding: 1.25rem;
border-radius: 50%;
background-color: ${color("brand-light")};
margin-bottom: 1.5rem;
`;
export const InfoTitle = styled.div`
color: ${color("text-dark")};
font-size: 1.25rem;
......
......@@ -10,8 +10,6 @@ import {
FormMessage,
FormTitle,
InfoBody,
InfoIcon,
InfoIconContainer,
InfoMessage,
InfoTitle,
} from "./ResetPassword.styled";
......@@ -23,6 +21,8 @@ export interface ResetPasswordProps {
onResetPassword: (token: string, password: string) => void;
onValidatePassword: (password: string) => void;
onValidatePasswordToken: (token: string) => void;
onShowToast: (toast: { message: string }) => void;
onRedirect: (url: string) => void;
}
const ResetPassword = ({
......@@ -30,6 +30,8 @@ const ResetPassword = ({
onResetPassword,
onValidatePassword,
onValidatePasswordToken,
onShowToast,
onRedirect,
}: ResetPasswordProps): JSX.Element | null => {
const [view, setView] = useState<ViewType>("none");
......@@ -57,9 +59,10 @@ const ResetPassword = ({
const handlePasswordSubmit = useCallback(
async ({ password }: ResetPasswordData) => {
await onResetPassword(token, password);
setView("success");
onRedirect("/");
onShowToast({ message: t`You've updated your password.` });
},
[token, onResetPassword],
[onResetPassword, token, onRedirect, onShowToast],
);
useEffect(() => {
......@@ -74,7 +77,6 @@ const ResetPassword = ({
onSubmit={handlePasswordSubmit}
/>
)}
{view === "success" && <ResetPasswordSuccess />}
{view === "expired" && <ResetPasswordExpired />}
</AuthLayout>
);
......@@ -110,22 +112,6 @@ const ResetPasswordForm = ({
);
};
const ResetPasswordSuccess = (): JSX.Element => {
return (
<InfoBody>
<InfoIconContainer>
<InfoIcon name="check" />
</InfoIconContainer>
<InfoTitle>{t`All done!`}</InfoTitle>
<InfoMessage>{t`You've updated your password.`}</InfoMessage>
<Link
className="Button Button--primary"
to="/"
>{t`Sign in with your new password`}</Link>
</InfoBody>
);
};
const ResetPasswordExpired = (): JSX.Element => {
return (
<InfoBody>
......
import React, { ReactNode } from "react";
import { render, screen } from "@testing-library/react";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import ResetPassword, { ResetPasswordProps } from "./ResetPassword";
......@@ -27,18 +27,34 @@ describe("ResetPassword", () => {
});
it("should show a success message when the form is submitted", async () => {
const onShowToast = jest.fn();
const onRedirect = jest.fn();
const props = getProps({
onResetPassword: jest.fn().mockResolvedValue({}),
onValidatePasswordToken: jest.fn().mockResolvedValue({}),
onShowToast,
onRedirect,
});
render(<ResetPassword {...props} />);
render(
<ResetPassword
{...props}
onShowToast={onShowToast}
onRedirect={onRedirect}
/>,
);
const button = await screen.findByText("Save new password");
userEvent.click(button);
const message = await screen.findByText("All done!");
expect(message).toBeInTheDocument();
await waitFor(() => {
expect(onRedirect).toHaveBeenCalledWith("/");
expect(onShowToast).toHaveBeenCalledWith({
message: "You've updated your password.",
});
});
});
});
......@@ -48,6 +64,8 @@ const getProps = (opts?: Partial<ResetPasswordProps>): ResetPasswordProps => {
onResetPassword: jest.fn(),
onValidatePassword: jest.fn(),
onValidatePasswordToken: jest.fn(),
onShowToast: jest.fn(),
onRedirect: jest.fn(),
...opts,
};
};
......
import { connect } from "react-redux";
import { replace } from "react-router-redux";
import ResetPassword from "../../components/ResetPassword";
import { addUndo } from "metabase/redux/undo";
import {
resetPassword,
validatePassword,
......@@ -14,6 +16,8 @@ const mapDispatchToProps = {
onResetPassword: resetPassword,
onValidatePassword: validatePassword,
onValidatePasswordToken: validatePasswordToken,
onShowToast: addUndo,
onRedirect: replace,
};
export default connect(mapStateToProps, mapDispatchToProps)(ResetPassword);
......@@ -37,6 +37,7 @@ interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
as?: ElementType;
className?: string;
to?: string;
href?: string;
icon?: string | ReactNode;
iconSize?: number;
......
......@@ -26,10 +26,7 @@ describe("scenarios > auth > password", () => {
cy.findByLabelText("Confirm your password").type(admin.password);
cy.findByText("Save new password").click();
cy.findByText("All done!");
cy.findByText("Sign in with your new password").click();
cy.findByText(admin.first_name, { exact: false });
cy.findByText("You've updated your password.");
});
});
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment