Skip to content

Commit 35a0015

Browse files
brkalowLekoArts
andauthored
fix(clerk-react): Fix forceRedirectUrl and fallbackRedirectUrl when passed to button components (#3508)
Co-authored-by: Lennart <lekoarts@gmail.com>
1 parent 94438d4 commit 35a0015

File tree

9 files changed

+203
-14
lines changed

9 files changed

+203
-14
lines changed

.changeset/metal-foxes-raise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-react': patch
3+
---
4+
5+
Update `SignUpButton` and `SignInButton` to respect `forceRedirect` and `fallbackRedirect` props. Previously, these were getting ignored and successful completions of the flows would fallback to the default redirect URL.

.changeset/sixty-ears-rest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Fixed a bug where Clerk components rendered in modals were wrapped with `aria-hidden`.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { SignInButton, SignUpButton } from '@clerk/nextjs';
2+
3+
export default function Home() {
4+
return (
5+
<main>
6+
<SignInButton
7+
mode='modal'
8+
forceRedirectUrl='/protected'
9+
>
10+
Sign in button (force)
11+
</SignInButton>
12+
13+
<SignInButton
14+
mode='modal'
15+
fallbackRedirectUrl='/protected'
16+
>
17+
Sign in button (fallback)
18+
</SignInButton>
19+
20+
<SignUpButton
21+
mode='modal'
22+
forceRedirectUrl='/protected'
23+
>
24+
Sign up button (force)
25+
</SignUpButton>
26+
27+
<SignUpButton
28+
mode='modal'
29+
fallbackRedirectUrl='/protected'
30+
>
31+
Sign up button (fallback)
32+
</SignUpButton>
33+
</main>
34+
);
35+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { test } from '@playwright/test';
2+
3+
import { appConfigs } from '../presets';
4+
import type { FakeUser } from '../testUtils';
5+
import { createTestUtils, testAgainstRunningApps } from '../testUtils';
6+
7+
testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('redirect props @nextjs', ({ app }) => {
8+
test.describe.configure({ mode: 'serial' });
9+
10+
let fakeUser: FakeUser;
11+
12+
test.beforeAll(async () => {
13+
const u = createTestUtils({ app });
14+
fakeUser = u.services.users.createFakeUser({
15+
fictionalEmail: true,
16+
withPhoneNumber: true,
17+
withUsername: true,
18+
});
19+
await u.services.users.createBapiUser(fakeUser);
20+
});
21+
22+
test.afterAll(async () => {
23+
await fakeUser.deleteIfExists();
24+
await app.teardown();
25+
});
26+
27+
test.afterEach(async ({ page, context }) => {
28+
const u = createTestUtils({ app, page, context });
29+
await u.page.signOut();
30+
await u.page.context().clearCookies();
31+
});
32+
33+
test.describe('SignInButton', () => {
34+
test('sign in button respects forceRedirectUrl', async ({ page, context }) => {
35+
const u = createTestUtils({ app, page, context });
36+
37+
await u.page.goToRelative('/buttons');
38+
await u.page.waitForClerkJsLoaded();
39+
await u.po.expect.toBeSignedOut();
40+
41+
await u.page.getByText('Sign in button (force)').click();
42+
43+
await u.po.signIn.waitForMounted();
44+
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
45+
46+
await u.page.waitForAppUrl('/protected');
47+
48+
await u.po.expect.toBeSignedIn();
49+
});
50+
51+
test('sign in button respects fallbackRedirectUrl', async ({ page, context }) => {
52+
const u = createTestUtils({ app, page, context });
53+
54+
await u.page.goToRelative('/buttons');
55+
await u.page.waitForClerkJsLoaded();
56+
await u.po.expect.toBeSignedOut();
57+
58+
await u.page.getByText('Sign in button (fallback)').click();
59+
60+
await u.po.signIn.waitForMounted();
61+
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
62+
63+
await u.page.waitForAppUrl('/protected');
64+
65+
await u.po.expect.toBeSignedIn();
66+
});
67+
});
68+
69+
test.describe('SignUpButton', () => {
70+
test('sign up button respects forceRedirectUrl', async ({ page, context }) => {
71+
const u = createTestUtils({ app, page, context });
72+
const fakeUser = u.services.users.createFakeUser({
73+
fictionalEmail: true,
74+
withPhoneNumber: true,
75+
withUsername: true,
76+
});
77+
78+
await u.page.goToRelative('/buttons');
79+
await u.page.waitForClerkJsLoaded();
80+
81+
await u.page.getByText('Sign up button (force)').click();
82+
83+
// Fill in sign up form
84+
await u.po.signUp.signUpWithEmailAndPassword({
85+
email: fakeUser.email,
86+
password: fakeUser.password,
87+
});
88+
89+
// Verify email
90+
await u.po.signUp.enterTestOtpCode();
91+
92+
await u.page.waitForAppUrl('/protected');
93+
94+
// Check if user is signed in
95+
await u.po.expect.toBeSignedIn();
96+
97+
await fakeUser.deleteIfExists();
98+
});
99+
100+
test('sign up button respects fallbackRedirectUrl', async ({ page, context }) => {
101+
const u = createTestUtils({ app, page, context });
102+
const fakeUser = u.services.users.createFakeUser({
103+
fictionalEmail: true,
104+
withPhoneNumber: true,
105+
withUsername: true,
106+
});
107+
108+
await u.page.goToRelative('/buttons');
109+
await u.page.waitForClerkJsLoaded();
110+
111+
await u.page.getByText('Sign up button (fallback)').click();
112+
113+
// Fill in sign up form
114+
await u.po.signUp.signUpWithEmailAndPassword({
115+
email: fakeUser.email,
116+
password: fakeUser.password,
117+
});
118+
119+
// Verify email
120+
await u.po.signUp.enterTestOtpCode();
121+
122+
await u.page.waitForAppUrl('/protected');
123+
124+
// Check if user is signed in
125+
await u.po.expect.toBeSignedIn();
126+
127+
await fakeUser.deleteIfExists();
128+
});
129+
});
130+
});

packages/clerk-js/src/ui/elements/Modal.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,13 @@ export const Modal = withFloatingTree((props: ModalProps) => {
5050
>
5151
<ModalContext.Provider value={modalCtx}>
5252
<Flex
53-
aria-hidden
5453
ref={overlayRef}
5554
elementDescriptor={descriptors.modalBackdrop}
5655
sx={[
5756
t => ({
5857
animation: `${animations.fadeIn} 150ms ${t.transitionTiming.$common}`,
5958
zIndex: t.zIndices.$modal,
6059
backgroundColor: t.colors.$modalBackdrop,
61-
// ...common.centeredFlex(),
6260
alignItems: 'flex-start',
6361
justifyContent: 'center',
6462
overflow: 'auto',

packages/react/src/components/SignInButton.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { SignInProps } from '@clerk/types';
12
import React from 'react';
23

34
import type { SignInButtonProps, WithClerkProp } from '../types';
@@ -11,21 +12,27 @@ export const SignInButton = withClerk(({ clerk, children, ...props }: WithClerkP
1112
const child = assertSingleChild(children)('SignInButton');
1213

1314
const clickHandler = () => {
14-
const opts = {
15+
const opts: SignInProps = {
16+
forceRedirectUrl,
17+
fallbackRedirectUrl,
1518
signUpFallbackRedirectUrl,
1619
signUpForceRedirectUrl,
17-
signInForceRedirectUrl: forceRedirectUrl,
18-
signInFallbackRedirectUrl: fallbackRedirectUrl,
1920
};
2021

2122
if (mode === 'modal') {
2223
return clerk.openSignIn(opts);
2324
}
24-
return clerk.redirectToSignIn(opts);
25+
return clerk.redirectToSignIn({
26+
...opts,
27+
signInFallbackRedirectUrl: fallbackRedirectUrl,
28+
signInForceRedirectUrl: forceRedirectUrl,
29+
});
2530
};
2631

2732
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
28-
await safeExecute((child as any).props.onClick)(e);
33+
if (child && typeof child === 'object' && 'props' in child) {
34+
await safeExecute(child.props.onClick)(e);
35+
}
2936
return clickHandler();
3037
};
3138

packages/react/src/components/SignUpButton.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { SignUpProps } from '@clerk/types';
12
import React from 'react';
23

34
import type { SignUpButtonProps, WithClerkProp } from '../types';
@@ -19,9 +20,9 @@ export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkP
1920
const child = assertSingleChild(children)('SignUpButton');
2021

2122
const clickHandler = () => {
22-
const opts = {
23-
signUpFallbackRedirectUrl: fallbackRedirectUrl,
24-
signUpForceRedirectUrl: forceRedirectUrl,
23+
const opts: SignUpProps = {
24+
fallbackRedirectUrl,
25+
forceRedirectUrl,
2526
signInFallbackRedirectUrl,
2627
signInForceRedirectUrl,
2728
unsafeMetadata,
@@ -31,11 +32,17 @@ export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkP
3132
return clerk.openSignUp(opts);
3233
}
3334

34-
return clerk.redirectToSignUp(opts);
35+
return clerk.redirectToSignUp({
36+
...opts,
37+
signUpFallbackRedirectUrl: fallbackRedirectUrl,
38+
signUpForceRedirectUrl: forceRedirectUrl,
39+
});
3540
};
3641

3742
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
38-
await safeExecute((child as any).props.onClick)(e);
43+
if (child && typeof child === 'object' && 'props' in child) {
44+
await safeExecute(child.props.onClick)(e);
45+
}
3946
return clickHandler();
4047
};
4148

packages/react/src/components/__tests__/SignInButton.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('<SignInButton/>', () => {
5252
const btn = screen.getByText('Sign in');
5353
await userEvent.click(btn);
5454

55-
expect(mockRedirectToSignIn).toHaveBeenCalledWith({ signInForceRedirectUrl: url });
55+
expect(mockRedirectToSignIn).toHaveBeenCalledWith({ forceRedirectUrl: url, signInForceRedirectUrl: url });
5656
});
5757

5858
it('handles fallbackRedirectUrl prop', async () => {
@@ -62,6 +62,7 @@ describe('<SignInButton/>', () => {
6262
await userEvent.click(btn);
6363

6464
expect(mockRedirectToSignIn).toHaveBeenCalledWith({
65+
fallbackRedirectUrl: url,
6566
signInFallbackRedirectUrl: url,
6667
});
6768
});

packages/react/src/components/__tests__/SignUpButton.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('<SignUpButton/>', () => {
5353
const btn = screen.getByText('Sign up');
5454
userEvent.click(btn);
5555
await waitFor(() => {
56-
expect(mockRedirectToSignUp).toHaveBeenCalledWith({ signUpForceRedirectUrl: url });
56+
expect(mockRedirectToSignUp).toHaveBeenCalledWith({ forceRedirectUrl: url, signUpForceRedirectUrl: url });
5757
});
5858
});
5959

@@ -63,6 +63,7 @@ describe('<SignUpButton/>', () => {
6363
userEvent.click(btn);
6464
await waitFor(() => {
6565
expect(mockRedirectToSignUp).toHaveBeenCalledWith({
66+
fallbackRedirectUrl: url,
6667
signUpFallbackRedirectUrl: url,
6768
});
6869
});

0 commit comments

Comments
 (0)