Skip to content

Commit cf2f477

Browse files
committed
feat(auth): implement useIsSignupComplete hook and refactor onboarding storage
- Introduced the useIsSignupComplete hook to manage and check the signup completion state using local storage. - Refactored local storage management for onboarding progress, consolidating related states into a single structure. - Removed the deprecated useHasCompletedSignup hook and its associated tests to streamline the codebase. - Updated components and tests to utilize the new onboarding storage utilities, ensuring accurate state handling across the application. - Enhanced the onboarding experience by integrating the new hook into relevant components and updating local storage interactions.
1 parent 6c7464a commit cf2f477

File tree

16 files changed

+223
-201
lines changed

16 files changed

+223
-201
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { act, renderHook } from "@testing-library/react";
2+
import { STORAGE_KEYS } from "@web/common/constants/storage.constants";
3+
import { updateOnboardingProgress } from "@web/views/Onboarding/utils/onboarding.storage.util";
4+
import { useIsSignupComplete } from "./isSignupComplete";
5+
6+
describe("useIsSignupComplete", () => {
7+
beforeEach(() => {
8+
localStorage.clear();
9+
});
10+
11+
it("should return false when localStorage is empty", () => {
12+
const { result } = renderHook(() => useIsSignupComplete());
13+
14+
expect(result.current.isSignupComplete).toBe(false);
15+
});
16+
17+
it("should return true when localStorage has completed signup flag", () => {
18+
updateOnboardingProgress({ isSignupComplete: true });
19+
20+
const { result } = renderHook(() => useIsSignupComplete());
21+
22+
expect(result.current.isSignupComplete).toBe(true);
23+
});
24+
25+
it("should return false when localStorage has invalid value", () => {
26+
localStorage.setItem(STORAGE_KEYS.ONBOARDING_PROGRESS, "invalid");
27+
28+
const { result } = renderHook(() => useIsSignupComplete());
29+
30+
expect(result.current.isSignupComplete).toBe(false);
31+
});
32+
33+
it("should update hasCompletedSignup when markSignupCompleted is called", () => {
34+
const { result } = renderHook(() => useIsSignupComplete());
35+
36+
expect(result.current.isSignupComplete).toBe(false);
37+
38+
act(() => {
39+
result.current.markSignupCompleted();
40+
});
41+
42+
expect(result.current.isSignupComplete).toBe(true);
43+
const stored = JSON.parse(
44+
localStorage.getItem(STORAGE_KEYS.ONBOARDING_PROGRESS) ?? "{}",
45+
);
46+
expect(stored.isSignupComplete).toBe(true);
47+
});
48+
49+
it("should handle multiple calls to markSignupCompleted", () => {
50+
const { result } = renderHook(() => useIsSignupComplete());
51+
52+
act(() => {
53+
result.current.markSignupCompleted();
54+
});
55+
56+
expect(result.current.isSignupComplete).toBe(true);
57+
58+
act(() => {
59+
result.current.markSignupCompleted();
60+
});
61+
62+
expect(result.current.isSignupComplete).toBe(true);
63+
const stored = JSON.parse(
64+
localStorage.getItem(STORAGE_KEYS.ONBOARDING_PROGRESS) ?? "{}",
65+
);
66+
expect(stored.isSignupComplete).toBe(true);
67+
});
68+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
import {
3+
getOnboardingProgress,
4+
updateOnboardingProgress,
5+
} from "@web/views/Onboarding/utils/onboarding.storage.util";
6+
7+
export const useIsSignupComplete = () => {
8+
const [isSignupComplete, setIsSignupComplete] = useState<boolean | null>(
9+
null,
10+
);
11+
12+
useEffect(() => {
13+
const checkSignupStatus = () => {
14+
const { isSignupComplete: storedValue } = getOnboardingProgress();
15+
setIsSignupComplete(storedValue);
16+
};
17+
18+
checkSignupStatus();
19+
}, []);
20+
21+
const markSignupCompleted = useCallback(() => {
22+
updateOnboardingProgress({ isSignupComplete: true });
23+
setIsSignupComplete(true);
24+
}, [setIsSignupComplete]);
25+
26+
return {
27+
isSignupComplete,
28+
markSignupCompleted,
29+
};
30+
};

packages/web/src/auth/useHasCompletedSignup.test.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

packages/web/src/auth/useHasCompletedSignup.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

packages/web/src/auth/useSkipOnboarding.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { useCallback, useEffect, useState } from "react";
22
import {
3-
getAuthStorage,
4-
updateAuthStorage,
5-
} from "@web/common/utils/storage/auth.storage.util";
3+
getOnboardingProgress,
4+
updateOnboardingProgress,
5+
} from "@web/views/Onboarding/utils/onboarding.storage.util";
66

77
export const useSkipOnboarding = () => {
88
const [skipOnboarding, setSkipOnboarding] = useState<boolean>(false);
99

1010
useEffect(() => {
1111
const checkOnboardingStatus = () => {
12-
const { skipOnboarding: storedValue } = getAuthStorage();
12+
const { isOnboardingSkipped: storedValue } = getOnboardingProgress();
1313
setSkipOnboarding(storedValue);
1414
};
1515

@@ -18,7 +18,7 @@ export const useSkipOnboarding = () => {
1818

1919
const updateOnboardingStatus = useCallback(
2020
(skip: boolean) => {
21-
updateAuthStorage({ skipOnboarding: skip });
21+
updateOnboardingProgress({ isOnboardingSkipped: skip });
2222

2323
setSkipOnboarding(skip);
2424
},

packages/web/src/common/constants/storage.constants.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@ import { z } from "zod";
22

33
export const StorageKeySchema = z.enum([
44
"compass.reminder",
5-
"compass.auth",
65
"compass.onboarding",
76
]);
87

98
export type StorageKey = z.infer<typeof StorageKeySchema>;
109

1110
export const STORAGE_KEYS: Record<
12-
"REMINDER" | "AUTH" | "ONBOARDING_PROGRESS",
11+
"REMINDER" | "ONBOARDING_PROGRESS",
1312
StorageKey
1413
> = {
1514
REMINDER: "compass.reminder",
16-
AUTH: "compass.auth",
1715
ONBOARDING_PROGRESS: "compass.onboarding",
1816
} as const;

packages/web/src/common/hooks/useGoogleAuth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useNavigate } from "react-router-dom";
2-
import { useHasCompletedSignup } from "@web/auth/useHasCompletedSignup";
2+
import { useIsSignupComplete } from "@web/auth/isSignupComplete";
33
import { useSkipOnboarding } from "@web/auth/useSkipOnboarding";
44
import { AuthApi } from "@web/common/apis/auth.api";
55
import { UserApi } from "@web/common/apis/user.api";
@@ -11,7 +11,7 @@ import { OnboardingStepProps } from "@web/views/Onboarding";
1111
export function useGoogleAuth(props?: Partial<OnboardingStepProps>) {
1212
const navigate = useNavigate();
1313
const { setAuthenticated } = useSession();
14-
const { markSignupCompleted } = useHasCompletedSignup();
14+
const { markSignupCompleted } = useIsSignupComplete();
1515
const { updateOnboardingStatus } = useSkipOnboarding();
1616

1717
const googleLogin = useGoogleLogin({

packages/web/src/common/utils/storage/auth.storage.util.ts

Lines changed: 0 additions & 54 deletions
This file was deleted.

packages/web/src/routers/loaders.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { zYearMonthDayString } from "@core/types/type.utils";
33
import dayjs, { Dayjs } from "@core/util/date/dayjs";
44
import { AUTH_FAILURE_REASONS } from "@web/common/constants/auth.constants";
55
import { ROOT_ROUTES } from "@web/common/constants/routes";
6-
import { getAuthStorage } from "@web/common/utils/storage/auth.storage.util";
6+
import { getOnboardingProgress } from "@web/views/Onboarding/utils/onboarding.storage.util";
77

88
export interface DayLoaderData {
99
dateInView: Dayjs; // in UTC
@@ -19,13 +19,13 @@ export async function loadAuthenticated() {
1919
}
2020

2121
export function loadHasCompletedSignup() {
22-
const { hasCompletedSignup } = getAuthStorage();
22+
const { isSignupComplete: hasCompletedSignup } = getOnboardingProgress();
2323

2424
return { hasCompletedSignup };
2525
}
2626

2727
export function loadOnboardingData() {
28-
const { skipOnboarding } = getAuthStorage();
28+
const { isOnboardingSkipped: skipOnboarding } = getOnboardingProgress();
2929

3030
return { skipOnboarding };
3131
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { act } from "react";
2+
import { createMemoryRouter } from "react-router-dom";
3+
import "@testing-library/jest-dom";
4+
import { screen } from "@testing-library/react";
5+
import { render } from "@web/__tests__/__mocks__/mock.render";
6+
import { getWeekDayLabel } from "@web/common/utils/event/event.util";
7+
import { CalendarView } from "@web/views/Calendar";
8+
9+
const router = createMemoryRouter([{ index: true, Component: CalendarView }], {
10+
initialEntries: ["/"],
11+
});
12+
13+
describe("Scroll", () => {
14+
// separate from other tests to preserve
15+
// '.toHaveBeenCalledTimes' reliability
16+
it("only scrolls once", async () => {
17+
const scrollSpy = jest.spyOn(window.HTMLElement.prototype, "scroll");
18+
19+
await act(() => render(<></>, { router }));
20+
21+
expect(scrollSpy).toHaveBeenCalledTimes(1);
22+
});
23+
});
24+
25+
describe("Calendar: Display without State", () => {
26+
it("displays all the things that a user needs to see", async () => {
27+
await act(() => render(<></>, { router }));
28+
29+
/* week nav arrows */
30+
expect(
31+
screen.getByRole("navigation", {
32+
name: /previous week/i,
33+
}),
34+
).toBeInTheDocument();
35+
expect(
36+
screen.getByRole("navigation", {
37+
name: /next week/i,
38+
}),
39+
).toBeInTheDocument();
40+
41+
/* current week label */
42+
const todayLabel = getWeekDayLabel(new Date());
43+
expect(screen.getByTitle(todayLabel)).toBeInTheDocument();
44+
45+
/* now line */
46+
expect(
47+
screen.getByRole("separator", { name: /now line/i }),
48+
).toBeInTheDocument();
49+
});
50+
});

0 commit comments

Comments
 (0)