Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,26 @@ exports[`AccountSettingsPage > should render account settings page correctly 1`]
<p
class="bluedot-p not-prose font-semibold mb-2"
>
Password
Account settings
</p>
<button
aria-label="Change password"
class="cta-button flex items-center justify-center transition-all duration-200 w-fit whitespace-nowrap cursor-pointer not-prose disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none text-size-sm bd-md:text-size-xs px-4 py-3 rounded-sm font-semibold cta-button--secondary bg-transparent border border-bluedot-normal text-bluedot-normal hover:bg-bluedot-lighter"
type="button"
<div
class="flex flex-col items-start gap-3"
>
Change Password
</button>
<button
aria-label="Change email"
class="cta-button flex items-center justify-center transition-all duration-200 w-fit whitespace-nowrap cursor-pointer not-prose disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none text-size-sm bd-md:text-size-xs px-4 py-3 rounded-sm font-semibold cta-button--secondary bg-transparent border border-bluedot-normal text-bluedot-normal hover:bg-bluedot-lighter"
type="button"
>
Change email
</button>
<button
aria-label="Change password"
class="cta-button flex items-center justify-center transition-all duration-200 w-fit whitespace-nowrap cursor-pointer not-prose disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none text-size-sm bd-md:text-size-xs px-4 py-3 rounded-sm font-semibold cta-button--secondary bg-transparent border border-bluedot-normal text-bluedot-normal hover:bg-bluedot-lighter"
type="button"
>
Change password
</button>
</div>
</div>
</section>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { TRPCError } from '@trpc/server';
import { describe, expect, test } from 'vitest';
import { server, trpcMsw } from '../../__tests__/trpcMswSetup';
import { TrpcProvider } from '../../__tests__/trpcProvider';
import PasswordSection from './PasswordSection';
import AccountSettingsSection from './AccountSettingsSection';

describe('PasswordSection - User Journeys', () => {
describe('AccountSettingsSection - User Journeys', () => {
// Test data
const validPasswords = {
current: 'MyCurrentPassword123!',
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('PasswordSection - User Journeys', () => {
test('User can successfully change their password', async () => {
server.use(trpcMsw.users.changePassword.mutation(() => ({ message: 'Password updated successfully' })));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

// User clicks "Change Password" button
openPasswordModal();
Expand All @@ -67,7 +67,7 @@ describe('PasswordSection - User Journeys', () => {
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Incorrect password' });
}));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

// User opens modal and fills form
openPasswordModal();
Expand All @@ -93,7 +93,7 @@ describe('PasswordSection - User Journeys', () => {
});

test('User sees validation errors for invalid inputs', async () => {
render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

openPasswordModal();

Expand Down Expand Up @@ -127,7 +127,7 @@ describe('PasswordSection - User Journeys', () => {
});

test('User can cancel password change', async () => {
render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

openPasswordModal();

Expand All @@ -151,7 +151,7 @@ describe('PasswordSection - User Journeys', () => {
test('User can submit form with Enter key', async () => {
server.use(trpcMsw.users.changePassword.mutation(() => ({ message: 'Password updated successfully' })));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

openPasswordModal();
fillPasswordForm(validPasswords.current, validPasswords.new, validPasswords.new);
Expand All @@ -173,7 +173,7 @@ describe('PasswordSection - User Journeys', () => {
});

test('User sees helpful password hint', () => {
render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

openPasswordModal();

Expand All @@ -192,7 +192,7 @@ describe('PasswordSection - User Journeys', () => {

server.use(trpcMsw.users.changePassword.mutation(() => promise));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

openPasswordModal();
fillPasswordForm(validPasswords.current, validPasswords.new, validPasswords.new);
Expand All @@ -217,7 +217,7 @@ describe('PasswordSection - User Journeys', () => {
});

test('User can close modal with Escape key', async () => {
render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

openPasswordModal();

Expand Down Expand Up @@ -248,7 +248,7 @@ describe('PasswordSection - User Journeys', () => {
});
}));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

// User opens modal and fills form
openPasswordModal();
Expand All @@ -273,7 +273,7 @@ describe('PasswordSection - User Journeys', () => {
});
}));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

// User opens modal and fills form
openPasswordModal();
Expand All @@ -290,6 +290,15 @@ describe('PasswordSection - User Journeys', () => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
});

test('User can open the change email modal from the account settings section', () => {
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

fireEvent.click(screen.getByRole('button', { name: /change email/i }));

expect(screen.getByRole('heading', { level: 2, name: 'Change email' })).toBeInTheDocument();
expect(screen.getByLabelText(/new email/i)).toBeInTheDocument();
});

test('User sees fallback error message when backend error has no details', async () => {
server.use(trpcMsw.users.changePassword.mutation(() => {
throw new TRPCError({
Expand All @@ -298,7 +307,7 @@ describe('PasswordSection - User Journeys', () => {
});
}));

render(<PasswordSection />, { wrapper: TrpcProvider });
render(<AccountSettingsSection />, { wrapper: TrpcProvider });

// User opens modal and fills form
openPasswordModal();
Expand Down
Comment thread
Will-Howard marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import { TRPCClientError } from '@trpc/client';
import { useEffect, useRef, useState } from 'react';
import { changePasswordWithConfirmSchema } from '../../lib/schemas/user/changePassword.schema';
import { trpc } from '../../utils/trpc';
import ChangeEmailModal from './ChangeEmailModal';

const PasswordSection = () => {
const AccountSettingsSection = () => {
const [showChangeEmailModal, setShowChangeEmailModal] = useState(false);
const [showChangePasswordModal, setShowChangePasswordModal] = useState(false);
const [passwordUpdateSuccess, setPasswordUpdateSuccess] = useState(false);

Expand All @@ -26,16 +28,25 @@ const PasswordSection = () => {

return (
<>
{/* Password Section */}
{/* Account Settings Section */}
<div className="mb-6">
<P className="font-semibold mb-2">Password</P>
<CTALinkOrButton
variant="secondary"
onClick={() => setShowChangePasswordModal(true)}
aria-label="Change password"
>
Change Password

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Change Password" -> "Change password". From looking at recent changes I think sentence case is the direction we're going in, though it's not completely consistent

</CTALinkOrButton>
<P className="font-semibold mb-2">Account settings</P>
<div className="flex flex-col items-start gap-3">
<CTALinkOrButton
variant="secondary"
onClick={() => setShowChangeEmailModal(true)}
aria-label="Change email"
>
Change email
</CTALinkOrButton>
<CTALinkOrButton
variant="secondary"
onClick={() => setShowChangePasswordModal(true)}
aria-label="Change password"
>
Change password
</CTALinkOrButton>
</div>
{passwordUpdateSuccess && (
<p
className="text-size-sm text-green-600 mt-2"
Expand All @@ -47,6 +58,12 @@ const PasswordSection = () => {
)}
</div>

{/* Change Email Modal */}
<ChangeEmailModal
isOpen={showChangeEmailModal}
setIsOpen={setShowChangeEmailModal}
/>

{/* Change Password Modal */}
<ChangePasswordModal
isOpen={showChangePasswordModal}
Expand Down Expand Up @@ -171,8 +188,9 @@ const ChangePasswordModal = ({
};

return (
<Modal isOpen={isOpen} setIsOpen={setIsOpen} title="Change Password">
<div className="space-y-4">
<Modal isOpen={isOpen} setIsOpen={setIsOpen} title="Change password" bottomDrawerOnMobile>
<div className="w-full max-w-modal space-y-4">
<div className="h-0 w-[600px] max-w-full" />
<div>
<Input
ref={currentPasswordRef}
Expand All @@ -186,7 +204,7 @@ const ChangePasswordModal = ({
}}
onKeyDown={handleKeyDown}
placeholder="Enter current password"
label="Current Password*"
label="Current password*"
aria-label="Current password"
aria-describedby={
errors.current ? 'current-password-error' : undefined
Expand Down Expand Up @@ -218,7 +236,7 @@ const ChangePasswordModal = ({
}}
onKeyDown={handleKeyDown}
placeholder="Enter new password"
label="New Password*"
label="New password*"
aria-label="New password"
aria-describedby={
errors.new ? 'new-password-error' : 'new-password-hint'
Expand All @@ -228,7 +246,7 @@ const ChangePasswordModal = ({
/>
{!errors.new && (
<p
className="text-gray-600 text-size-sm mt-1"
className="text-charcoal-mid text-size-sm mt-1"
Comment thread
Will-Howard marked this conversation as resolved.
id="new-password-hint"
>
Password must be at least 8 characters
Expand Down Expand Up @@ -258,7 +276,7 @@ const ChangePasswordModal = ({
}}
onKeyDown={handleKeyDown}
placeholder="Confirm new password"
label="Confirm New Password*"
label="Confirm new password*"
aria-label="Confirm new password"
aria-describedby={
errors.confirm ? 'confirm-password-error' : undefined
Expand Down Expand Up @@ -299,7 +317,7 @@ const ChangePasswordModal = ({
<span>Updating...</span>
</span>
) : (
'Update Password'
'Update password'
)}
</CTALinkOrButton>
</div>
Expand All @@ -308,4 +326,4 @@ const ChangePasswordModal = ({
);
};

export default PasswordSection;
export default AccountSettingsSection;
Loading