diff --git a/src/shared/components/FormSwitch/FormSwitch.tsx b/src/shared/components/FormSwitch/FormSwitch.tsx index 5418e32..1db2cca 100644 --- a/src/shared/components/FormSwitch/FormSwitch.tsx +++ b/src/shared/components/FormSwitch/FormSwitch.tsx @@ -7,17 +7,11 @@ import Typography from "../../design/Typography" export interface FormSwitchProps { label: string - hint?: string value: boolean onChange: (value: boolean) => void } -const FormSwitch: React.FC = ({ - label, - hint, - value, - onChange, -}) => { +const FormSwitch: React.FC = ({ label, value, onChange }) => { const theme = useTheme() const id = useId() @@ -37,7 +31,6 @@ const FormSwitch: React.FC = ({ { - it("renders label", () => { + it("renders label", async () => { const handleChangeMock = jest.fn() render( @@ -14,9 +14,15 @@ describe("FormTextField component", () => { >, ) - expect(screen.getByText(/Hello/)).toBeOnTheScreen() + const user = userEvent.setup() + expect(screen.getByText(/Hello/)).toBeTruthy() - fireEvent.changeText(screen.getByLabelText(/Hello/i), "test") - expect(handleChangeMock).toHaveBeenCalledWith("test") + await user.type(screen.getByLabelText(/Hello/i), "test") + + expect(handleChangeMock).toHaveBeenCalledTimes(4) + expect(handleChangeMock).toHaveBeenNthCalledWith(1, "responset") + expect(handleChangeMock).toHaveBeenNthCalledWith(2, "responsee") + expect(handleChangeMock).toHaveBeenNthCalledWith(3, "responses") + expect(handleChangeMock).toHaveBeenNthCalledWith(4, "responset") }) }) diff --git a/src/shared/components/FormTextField/FormTextField.tsx b/src/shared/components/FormTextField/FormTextField.tsx index bd39b70..fb8c709 100644 --- a/src/shared/components/FormTextField/FormTextField.tsx +++ b/src/shared/components/FormTextField/FormTextField.tsx @@ -8,16 +8,12 @@ import Typography from "../../design/Typography" export interface FormTextFieldProps { type?: "text" label: string - hint?: string - placeholder?: string value: string onChange?: (value: string) => void } const FormTextField: React.FC = ({ label, - hint, - placeholder, value, onChange, }) => { @@ -32,10 +28,8 @@ const FormTextField: React.FC = ({ = ( - { variant = "primary", disabled, children, ...props }, + { + variant = "primary", + margin, + padding, + fontSize = 20, + fontWeight = "400", + disabled, + children, + ...props + }, ref, ) => { const theme = useTheme() @@ -28,19 +41,23 @@ const Button: React.ForwardRefRenderFunction = ( return ( - + {children} - + ) } diff --git a/src/shared/design/Card/Card.tsx b/src/shared/design/Card/Card.tsx index 13d34fc..3017884 100644 --- a/src/shared/design/Card/Card.tsx +++ b/src/shared/design/Card/Card.tsx @@ -33,6 +33,7 @@ function getStyles(theme: Theme): { } { return StyleSheet.create({ container: { + width: "100%", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, diff --git a/src/shared/design/Typography/Typography.tsx b/src/shared/design/Typography/Typography.tsx index 5815566..45ba6c3 100644 --- a/src/shared/design/Typography/Typography.tsx +++ b/src/shared/design/Typography/Typography.tsx @@ -16,7 +16,7 @@ const Typography: React.FC = ({ const styles = getStyles(theme, variant) return ( - + {children} ) diff --git a/src/shared/design/theme/theme.ts b/src/shared/design/theme/theme.ts index 797a0c8..b3bea30 100644 --- a/src/shared/design/theme/theme.ts +++ b/src/shared/design/theme/theme.ts @@ -32,14 +32,10 @@ export interface Theme { export type ThemeMargin = | keyof Theme["spacing"] - | [keyof Theme["spacing"]] | [keyof Theme["spacing"], keyof Theme["spacing"]] - | [keyof Theme["spacing"], keyof Theme["spacing"], keyof Theme["spacing"]] export type ThemePadding = | keyof Theme["spacing"] - | [keyof Theme["spacing"]] | [keyof Theme["spacing"], keyof Theme["spacing"]] - | [keyof Theme["spacing"], keyof Theme["spacing"], keyof Theme["spacing"]] const light: Theme = { palette: { diff --git a/src/shared/services/auth/AuthProvider.tsx b/src/shared/services/auth/AuthProvider.tsx index b3bc272..f6552c1 100644 --- a/src/shared/services/auth/AuthProvider.tsx +++ b/src/shared/services/auth/AuthProvider.tsx @@ -19,35 +19,65 @@ GoogleSignin.configure({ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { + const [error, setError] = useState() + const [isPending, setIsPending] = useState(true) const [userInfo, setUserInfo] = useState() const signIn = useCallback(async () => { try { + setError(undefined) + setIsPending(true) const userInfo = await GoogleSignin.signIn() + setIsPending(false) setUserInfo(userInfo) return userInfo.user } catch (error) { setUserInfo(null) console.error("GoogleSignin.signIn() error", error) + setError(error as Error) + setIsPending(false) + setUserInfo(null) return false } }, []) const signOut = useCallback(async () => { try { + setError(undefined) + setIsPending(true) + await GoogleSignin.signOut() + setIsPending(false) setUserInfo(null) + return true } catch (error) { console.error("GoogleSignin.signOut() error", error) + setError(error as Error) + return false } }, []) useEffect(() => { async function run() { - const userInfo = await GoogleSignin.getCurrentUser() - setUserInfo(userInfo) + try { + setError(undefined) + setIsPending(true) + + const userInfo = await GoogleSignin.getCurrentUser() + + setIsPending(false) + setUserInfo(userInfo || undefined) + } catch (error) { + console.error( + "Call to GoogleSignin.getCurrentUser() failed with error:", + error, + ) + + setError(error as Error) + setIsPending(false) + } } run() @@ -57,7 +87,9 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ () => ({ signIn, signOut, + error, isAuthenticated: userInfo ? true : userInfo === null ? false : undefined, + isPending, user: userInfo?.user, scopes: userInfo?.scopes, idToken: userInfo?.idToken, diff --git a/src/shared/services/auth/context.ts b/src/shared/services/auth/context.ts index 5fa2f09..4a7e5cf 100644 --- a/src/shared/services/auth/context.ts +++ b/src/shared/services/auth/context.ts @@ -6,8 +6,12 @@ export interface AuthContext { signIn: () => Promise /** Log the user out from Google Auth. Return boolean success. */ signOut: () => Promise + /** Error if any Google API returns an error. Undefined if no errors in the last API call. */ + error?: Error | undefined /** Boolean if the user is authenticated or not. Undefined if unknown. */ isAuthenticated?: boolean + /** Boolean if a Google API call is being made. */ + isPending: boolean /** Google Auth User object. Undefined if not signed in. */ user?: UserInfo["user"] /** List of Google Auth scopes. Undefined if not signed in. */ diff --git a/tsconfig.json b/tsconfig.json index 8487fbf..23d909e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "@shared/*": ["./src/shared/*"], "@screens/*": ["./src/screens/*"] }, + "types": ["react-native", "jest", "node"], "module": "ESNext", "strict": true,