Skip to content

Commit 3171863

Browse files
committed
Merge shared-ui and react package
1 parent bcb0a9d commit 3171863

234 files changed

Lines changed: 11168 additions & 1473 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 2123 additions & 1106 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,6 @@
4747
"webpack": "^5.89.0",
4848
"webpack-cli": "^5.1.4",
4949
"webpack-merge": "^5.10.0"
50-
}
50+
},
51+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
5152
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Observe SDK Integration into Complete React SDK — STATUS
2+
3+
All planned phases have been implemented. This document now reflects the final status.
4+
5+
## What was implemented
6+
7+
### Flow-level events (ProcessHandler)
8+
- [x] `loginVisible` — fires when LoginInitBlock first becomes primary
9+
- [x] `loginFinish` — fires in onProcessCompleted when authType === Login
10+
- [x] `signupVisible` — fires when SignupInitBlock first becomes primary
11+
- [x] `signupFinish` — fires in onProcessCompleted when authType === Signup
12+
- [x] `loginReset` — fires when LoginInitBlock becomes primary again (after being away)
13+
14+
### Social login (LoginInitBlock + SignupInitBlock)
15+
- [x] `socialLoginStart` — fires before startSocialVerification API call
16+
- [x] `socialLoginFinish` — fires when finishSocialVerification succeeds
17+
- [x] `socialLoginError` — fires when finishSocialVerification fails
18+
19+
### Identifier tracking (LoginInitBlock + SignupInitBlock + React components)
20+
- [x] `provideIdentifierStartable` — created via tracker in LoginForm.tsx / SignupInit.tsx with DOM ref
21+
- [x] `provideIdentifierSubmitted` — fires in start() / updateUserData() before API call
22+
- [x] `provideIdentifierFinished` — fires on successful API response
23+
- [x] `provideIdentifierError` — fires on failed API response
24+
- [x] Input field instrumentation (auto-detect first character, paste, CUI via PatternDetector)
25+
26+
### Login methods decision (LoginInit + PasskeyError screens)
27+
- [x] `loginMethodsDecisionOffered` — fires in LoginInit.tsx, PasskeyError.tsx, PasskeyErrorLight.tsx
28+
29+
### Email OTP (EmailVerifyBlock)
30+
- [x] `emailOTPStartable` — fires when block is created with verificationMethod === 'email-otp'
31+
- [x] `emailOTPSubmitted` — fires in validateCode() before API call
32+
- [x] `emailOTPFinished` — fires on successful validation
33+
- [x] `emailOTPError` — fires on failed validation
34+
- [x] `emailOTPResent` — fires on successful resend
35+
36+
### Email Link (EmailVerifyBlock)
37+
- [x] `emailLinkStartable` — fires when block is created with verificationMethod === 'email-link'
38+
- [x] `emailLinkSubmitted` — fires in validateEmailLink() before API call
39+
- [x] `emailLinkFinished` — fires on successful link validation
40+
- [x] `emailLinkError` — fires on failed link validation
41+
42+
### web-core changes (B1 fix)
43+
- [x] `loginWithPasskey()` — added `onCeremonyData` callback exposing assertionOptions + assertionResponse
44+
- [x] `loginWithPasskeyChallenge()` — extended `onAuthenticatorCompleted` to pass assertionOptions + assertionResponse
45+
- [x] `appendPasskey()` — added `onCeremonyData` callback exposing attestationOptions + attestationResponse
46+
47+
### Passkey login (PasskeyVerifyBlock + LoginInitBlock conditional UI)
48+
- [x] `passkeyLoginStart` — fires via onCeremonyData callback with assertionOptions
49+
- [x] `passkey.submitted` — fires with assertionResponse
50+
- [x] `passkey.finished` — fires on successful login
51+
- [x] `passkey.clientError` / `passkey.serverErrorUnknown` — fires on failure
52+
- [x] Conditional UI tracking — `conditionalUIStartable`, `conditionalUISubmitted`, `conditionalUIFinished`, `conditionalUIClientError` in LoginInitBlock
53+
54+
### Passkey enrollment (PasskeyAppendBlock)
55+
- [x] `passkeyEnrollmentStartable` — fires when block is initialized
56+
- [x] `passkey_enrollment.started` — fires via onCeremonyData with attestationOptions
57+
- [x] `passkey_enrollment.submitted` — fires with attestationResponse
58+
- [x] `passkey_enrollment.finished` — fires on successful append
59+
- [x] `passkey_enrollment.clientError` — fires on failure
60+
- [x] `passkey_enrollment.skipped` — fires in skipPasskeyAppend()
61+
62+
---
63+
64+
## Architecture
65+
66+
- `ObserveTracker` interface defined in `src/contexts/ObserveContext.ts` (no hard dependency on @corbado/observe)
67+
- Tracker passed via `CorbadoProvider``ObserveContext``FlowHandlerProvider``ProcessHandler` → Block classes
68+
- Flow-level events fire from `ProcessHandler`
69+
- Block-level events fire from Block class methods
70+
- DOM-dependent events (provideIdentifierStartable) bridge via React components setting operations on blocks
71+
72+
## Remaining gaps (future work)
73+
74+
| Gap | Description |
75+
|-----|-------------|
76+
| Phone OTP events | No `phoneOTP*` events in observe SDK |
77+
| Missing fields events | No tracking for the "complete your profile" step |
78+
| Phone identifier instrumentation | `provideIdentifierStartable` assumes text input |
79+
| Password events | No password login events tracked (not present in complete SDK) |

packages/react/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,22 @@
3131
"url": "https://github.com/corbado/javascript/issues"
3232
},
3333
"dependencies": {
34-
"@corbado/shared-ui": "^3.6.0",
34+
"@corbado/observe": "file:../../../../js/packages/observe",
3535
"@corbado/shared-util": "^1.0.12",
3636
"@corbado/web-core": "^3.6.0",
3737
"i18next": "23.5.1",
3838
"i18next-browser-languagedetector": "7.1.0",
3939
"libphonenumber-js": "^1.10.59",
4040
"react-i18next": "13.2.2",
41-
"react-phone-number-input": "^3.3.9"
41+
"react-phone-number-input": "^3.3.9",
42+
"rxjs": "^7.8.1",
43+
"ua-parser-js": "^1.0.37"
4244
},
4345
"devDependencies": {
4446
"@corbado/types": "^3.2.0",
4547
"@types/react": "^18.2.0",
48+
"css-loader": "^6.8.1",
49+
"style-loader": "^3.3.3",
4650
"ts-results": "^3.3.0",
4751
"typescript": "^5.2.2"
4852
},

packages/react/src/components/authentication/AuthFlow.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import type {
88
PasskeyVerifyBlock,
99
PhoneVerifyBlock,
1010
SignupInitBlock,
11-
} from '@corbado/shared-ui';
12-
import { BlockTypes, InitState, ScreenNames } from '@corbado/shared-ui';
11+
} from '../../shared-ui';
12+
import { BlockTypes, InitState, ScreenNames } from '../../shared-ui';
1313
import type { FC } from 'react';
1414
import React, { useEffect, useMemo, useState } from 'react';
1515

packages/react/src/components/authentication/login-init/LastIdentifier.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LoginIdentifierType, type LoginInitBlock } from '@corbado/shared-ui';
1+
import { LoginIdentifierType, type LoginInitBlock } from '../../../shared-ui';
22
import type { FC } from 'react';
33
import React, { useMemo } from 'react';
44
import { useTranslation } from 'react-i18next';
@@ -44,7 +44,7 @@ export const LastIdentifier: FC<LastIdentifierProps> = ({
4444
}
4545

4646
block
47-
.start(lastIdentifier.value, lastIdentifier.type === LoginIdentifierType.Phone)
47+
.start(lastIdentifier.value, lastIdentifier.type === LoginIdentifierType.Phone, true)
4848
.finally(() => setLoading(false));
4949
};
5050

packages/react/src/components/authentication/login-init/LoginForm.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import type { LoginInitBlock, TextFieldWithError } from '@corbado/shared-ui';
1+
import type { LoginInitBlock, TextFieldWithError } from '../../../shared-ui';
22
import type { FC, FormEvent } from 'react';
3-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3+
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
44
import { useTranslation } from 'react-i18next';
55

6+
import { ObserveContext } from '../../../contexts/ObserveContext';
67
import { useTelemetry } from '../../../hooks/useTelemetry';
78
import type { InputFieldProps } from '../../ui';
89
import { InputField, PhoneInputField, PrimaryButton } from '../../ui';
@@ -25,6 +26,7 @@ export const LoginForm: FC<LoginFormProps> = ({
2526
const { t } = useTranslation('translation', { keyPrefix: `login.login-init.login-init` });
2627

2728
const { logMethodCalled } = useTelemetry();
29+
const { tracker } = useContext(ObserveContext);
2830

2931
const [textField, setTextField] = useState<TextFieldWithError | null>(null);
3032
const [usePhone, setUsePhone] = useState<boolean>(
@@ -52,6 +54,19 @@ export const LoginForm: FC<LoginFormProps> = ({
5254
textFieldRef.current.value = block.data.loginIdentifier ? block.data.loginIdentifier : '';
5355
}, [block]);
5456

57+
useEffect(() => {
58+
if (!tracker || !textFieldRef.current) {
59+
return;
60+
}
61+
62+
const op = tracker.provideIdentifierStartable(textFieldRef.current);
63+
block.setProvideIdentifierOp(op);
64+
65+
return () => {
66+
block.destroyProvideIdentifierOp();
67+
};
68+
}, [tracker, block]);
69+
5570
const submitButtonText = useMemo(() => t('button_submit'), [t]);
5671
const IdentifierInputField = useMemo(() => {
5772
const commonProps: Partial<InputFieldProps> & React.RefAttributes<HTMLInputElement> = {
@@ -135,9 +150,9 @@ export const LoginForm: FC<LoginFormProps> = ({
135150
logMethodCalled('loginStart', 'loginInit');
136151

137152
if (usePhone) {
138-
void block.start(phoneInput, true);
153+
void block.start(phoneInput, true, false);
139154
} else {
140-
void block.start(textFieldRef.current?.value ?? '', false);
155+
void block.start(textFieldRef.current?.value ?? '', false, false);
141156
}
142157
},
143158
[block, usePhone, phoneInput],

packages/react/src/components/ui/FreemiumBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { InitState } from '@corbado/shared-ui';
1+
import { InitState } from '../../shared-ui';
22
import type { FC } from 'react';
33
import React from 'react';
44

packages/react/src/components/ui/SocialLoginButtons.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SocialLogin } from '@corbado/shared-ui';
1+
import type { SocialLogin } from '../../shared-ui';
22
import type { SocialProviderType } from '@corbado/web-core';
33
import type { TFunction } from 'i18next';
44
import type { FC, ReactNode } from 'react';

packages/react/src/components/ui/icons/AddIcon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import addSrc from '@corbado/shared-ui/assets/add.svg';
1+
import addSrc from '../../../shared-ui/assets/add.svg';
22
import type { FC } from 'react';
33
import { memo, useRef } from 'react';
44
import React from 'react';

0 commit comments

Comments
 (0)