Skip to content

Commit

Permalink
Merge branch 'main' into stefanos/sdk-1775-rootauthloader-rate-limit
Browse files Browse the repository at this point in the history
  • Loading branch information
anagstef authored May 27, 2024
2 parents 77a4177 + f4034ea commit 150f20f
Show file tree
Hide file tree
Showing 18 changed files with 132 additions and 174 deletions.
5 changes: 5 additions & 0 deletions .changeset/good-paws-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/elements': patch
---

Widen optional peerDependency of `next` to include `>=15.0.0-rc`. This way you can use Next.js 15 with Clerk Elements without your package manager complaining. Also allow React 19.
6 changes: 6 additions & 0 deletions .changeset/large-cars-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/clerk-js': patch
---

Bug fix: Use the correct returnBack url when GoogleOneTap remains open across navigations.
Previously it will only use the url that existed in the browser url bar at the time the component was initially rendered.
2 changes: 2 additions & 0 deletions .changeset/serious-turtles-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/tall-fans-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Add `aria-label` and `aria-expanded` in menu trigger to improve accessibility
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ jobs:

strategy:
matrix:
test-name: ['generic', 'nextjs', 'express', 'quickstart', 'ap-flows', 'elements']
test-name: [ 'generic', 'nextjs', 'express', 'quickstart', 'ap-flows', 'elements' ]
test-project: ['chrome']

steps:
Expand Down
2 changes: 1 addition & 1 deletion integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const createLongRunningApps = () => {

return {
getByPattern: (patterns: Array<string | (typeof configs)[number]['id']>) => {
const res = new Set(...patterns.map(pattern => apps.filter(app => idMatchesPattern(app.id, pattern))));
const res = new Set(patterns.map(pattern => apps.filter(app => idMatchesPattern(app.id, pattern))).flat());
if (!res.size) {
const availableIds = configs.map(c => `\n- ${c.id}`).join('');
throw new Error(`Could not find long running app with id ${patterns}. The available ids are: ${availableIds}`);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SignIn } from '@clerk/nextjs';

export default function Page() {
return (
<div>
<SignIn routing={'hash'} />
</div>
);
}
12 changes: 9 additions & 3 deletions integration/testUtils/testAgainstRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { parseEnvOptions } from '../scripts';

type RunningAppsParams = {
withEnv?: EnvironmentConfig | EnvironmentConfig[];
withPattern?: string[];
};

/**
Expand All @@ -20,24 +21,28 @@ type RunningAppsParams = {
*/
const runningApps = (params: RunningAppsParams = {}) => {
const withEnv = [params.withEnv].flat().filter(Boolean);
const withPattern = (params.withPattern || []).flat().filter(Boolean);
const { appIds, appUrl, appPk, appSk, clerkApiUrl } = parseEnvOptions();

if (appIds.length) {
// if appIds are provided, we only return the apps with the given ids
const filter = app => (withEnv.length ? withEnv.includes(app.env) : true);
return appConfigs.longRunningApps.getByPattern(appIds).filter(filter);
return appConfigs.longRunningApps.getByPattern(withPattern.length ? withPattern : appIds).filter(filter);
}

// if no appIds are provided, it means that the user is running an app manually
// so, we return the app with the given env
const env = environmentConfig()
.setId('tempEnv')
.setEnvVariable('private', 'CLERK_SECRET_KEY', appSk)
.setEnvVariable('private', 'CLERK_API_URL', clerkApiUrl)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', appPk);

return [longRunningApplication({ id: 'standalone', env, serverUrl: appUrl, config: applicationConfig() })];
};

export const testAgainstRunningApps =
(runningAppsParams: RunningAppsParams) => (title: string, cb: (p: { app: Application }) => void) => {
export function testAgainstRunningApps(runningAppsParams: RunningAppsParams) {
return (title: string, cb: (p: { app: Application }) => void) => {
test.describe(title, () => {
runningApps(runningAppsParams).forEach(app => {
test.describe(`${app.name}`, () => {
Expand All @@ -46,3 +51,4 @@ export const testAgainstRunningApps =
});
});
};
}
63 changes: 3 additions & 60 deletions integration/tests/navigation.test.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,13 @@
import { test } from '@playwright/test';

import type { Application } from '../models/application';
import { appConfigs } from '../presets';
import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';
import { createTestUtils, testAgainstRunningApps } from '../testUtils';

test.describe('navigation modes @generic', () => {
testAgainstRunningApps({ withPattern: ['next.appRouter.withEmailCodes'] })('navigation modes @generic', ({ app }) => {
test.describe.configure({ mode: 'serial' });
let app: Application;
let fakeUser: FakeUser;

test.beforeAll(async () => {
app = await appConfigs.next.appRouter
.clone()
.addFile(
'src/app/provider.tsx',
() => `'use client'
import { ClerkProvider } from "@clerk/nextjs";
export function Provider({ children }: { children: any }) {
return (
<ClerkProvider>
{children}
</ClerkProvider>
)
}`,
)
.addFile(
'src/app/layout.tsx',
() => `import './globals.css';
import { Inter } from 'next/font/google';
import { Provider } from './provider';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<Provider>
<html lang='en'>
<body className={inter.className}>{children}</body>
</html>
</Provider>
);
}`,
)
.addFile(
'src/app/hash/sign-in/page.tsx',
() => `
import { SignIn } from '@clerk/nextjs';
export default function Page() {
return (
<SignIn routing="hash" />
);
}`,
)
.commit();
await app.setup();
await app.withEnv(appConfigs.envs.withEmailCodes);
await app.dev();

const m = createTestUtils({ app });
fakeUser = m.services.users.createFakeUser();
await m.services.users.createBapiUser(fakeUser);
Expand Down Expand Up @@ -105,7 +48,7 @@ export default function Page() {
await u.po.expect.toBeSignedIn();
});

test('sign in with path routing navigates to previous page', async ({ page, context }) => {
test.skip('sign in with path routing navigates to previous page', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"test:integration:deployment:nextjs": "DEBUG=1 npx playwright test --config integration/playwright.deployments.config.ts",
"test:integration:elements": "E2E_APP_ID=elements.* npm run test:integration:base -- --grep @elements",
"test:integration:express": "E2E_APP_ID=express.* npm run test:integration:base -- --grep @express",
"test:integration:generic": "E2E_APP_ID=react.vite.* npm run test:integration:base -- --grep @generic",
"test:integration:generic": "E2E_APP_ID=react.vite.*,next.appRouter.withEmailCodes npm run test:integration:base -- --grep @generic",
"test:integration:nextjs": "E2E_APP_ID=next.appRouter.* npm run test:integration:base -- --grep @nextjs",
"test:integration:quickstart": "E2E_APP_ID=quickstart.* npm run test:integration:base -- --grep @quickstart",
"test:integration:remix": "echo 'placeholder'",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,14 @@ function _OneTapStart(): JSX.Element | null {
const { navigate } = useRouter();

const ctx = useGoogleOneTapContext();
const {
signInUrl,
signUpUrl,
continueSignUpUrl,
secondFactorUrl,
firstFactorUrl,
signUpForceRedirectUrl,
signInForceRedirectUrl,
} = ctx;

async function oneTapCallback(response: GISCredentialResponse) {
isPromptedRef.current = false;
try {
const res = await clerk.authenticateWithGoogleOneTap({
token: response.credential,
});
await clerk.handleGoogleOneTapCallback(
res,
{
signInUrl,
signUpUrl,
continueSignUpUrl,
secondFactorUrl,
firstFactorUrl,
signUpForceRedirectUrl,
signInForceRedirectUrl,
},
navigate,
);
await clerk.handleGoogleOneTapCallback(res, ctx.generateCallbackUrls(window.location.href), navigate);
} catch (e) {
console.error(e);
}
Expand Down
125 changes: 67 additions & 58 deletions packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useClerk } from '@clerk/shared/react';
import { snakeToCamel } from '@clerk/shared/underscore';
import type { OrganizationResource, UserResource } from '@clerk/types';
import React, { useMemo } from 'react';
import type { HandleOAuthCallbackParams, OrganizationResource, UserResource } from '@clerk/types';
import React, { useCallback, useMemo } from 'react';

import { SIGN_IN_INITIAL_VALUE_KEYS, SIGN_UP_INITIAL_VALUE_KEYS } from '../../core/constants';
import { buildURL, createDynamicParamParser } from '../../utils';
Expand Down Expand Up @@ -506,67 +506,76 @@ export const useGoogleOneTapContext = () => {
throw new Error('Clerk: useGoogleOneTapContext called outside GoogleOneTap.');
}

const redirectUrls = new RedirectUrls(
options,
{
...ctx,
signInFallbackRedirectUrl: window.location.href,
signUpFallbackRedirectUrl: window.location.href,
const generateCallbackUrls = useCallback(
(returnBackUrl: string): HandleOAuthCallbackParams => {
const redirectUrls = new RedirectUrls(
options,
{
...ctx,
signInFallbackRedirectUrl: returnBackUrl,
signUpFallbackRedirectUrl: returnBackUrl,
},
queryParams,
);

let signUpUrl = options.signUpUrl || displayConfig.signUpUrl;
let signInUrl = options.signInUrl || displayConfig.signInUrl;

const preservedParams = redirectUrls.getPreservedSearchParams();
signInUrl = buildURL({ base: signInUrl, hashSearchParams: [queryParams, preservedParams] }, { stringify: true });
signUpUrl = buildURL({ base: signUpUrl, hashSearchParams: [queryParams, preservedParams] }, { stringify: true });

const signInForceRedirectUrl = redirectUrls.getAfterSignInUrl();
const signUpForceRedirectUrl = redirectUrls.getAfterSignUpUrl();

const signUpContinueUrl = buildURL(
{
base: signUpUrl,
hashPath: '/continue',
hashSearch: new URLSearchParams({
sign_up_force_redirect_url: signUpForceRedirectUrl,
}).toString(),
},
{ stringify: true },
);

const firstFactorUrl = buildURL(
{
base: signInUrl,
hashPath: '/factor-one',
hashSearch: new URLSearchParams({
sign_in_force_redirect_url: signInForceRedirectUrl,
}).toString(),
},
{ stringify: true },
);
const secondFactorUrl = buildURL(
{
base: signInUrl,
hashPath: '/factor-two',
hashSearch: new URLSearchParams({
sign_in_force_redirect_url: signInForceRedirectUrl,
}).toString(),
},
{ stringify: true },
);

return {
signInUrl,
signUpUrl,
firstFactorUrl,
secondFactorUrl,
continueSignUpUrl: signUpContinueUrl,
signInForceRedirectUrl,
signUpForceRedirectUrl,
};
},
queryParams,
);

let signUpUrl = options.signUpUrl || displayConfig.signUpUrl;
let signInUrl = options.signInUrl || displayConfig.signInUrl;

const preservedParams = redirectUrls.getPreservedSearchParams();
signInUrl = buildURL({ base: signInUrl, hashSearchParams: [queryParams, preservedParams] }, { stringify: true });
signUpUrl = buildURL({ base: signUpUrl, hashSearchParams: [queryParams, preservedParams] }, { stringify: true });

const signInForceRedirectUrl = redirectUrls.getAfterSignInUrl();
const signUpForceRedirectUrl = redirectUrls.getAfterSignUpUrl();

const signUpContinueUrl = buildURL(
{
base: signUpUrl,
hashPath: '/continue',
hashSearch: new URLSearchParams({
sign_up_force_redirect_url: signUpForceRedirectUrl,
}).toString(),
},
{ stringify: true },
);

const firstFactorUrl = buildURL(
{
base: signInUrl,
hashPath: '/factor-one',
hashSearch: new URLSearchParams({
sign_in_force_redirect_url: signInForceRedirectUrl,
}).toString(),
},
{ stringify: true },
);
const secondFactorUrl = buildURL(
{
base: signInUrl,
hashPath: '/factor-two',
hashSearch: new URLSearchParams({
sign_in_force_redirect_url: signInForceRedirectUrl,
}).toString(),
},
{ stringify: true },
[ctx, displayConfig.signInUrl, displayConfig.signUpUrl, options, queryParams],
);

return {
...ctx,
componentName,
signInUrl,
signUpUrl,
firstFactorUrl,
secondFactorUrl,
continueSignUpUrl: signUpContinueUrl,
signInForceRedirectUrl,
signUpForceRedirectUrl,
generateCallbackUrls,
};
};
10 changes: 7 additions & 3 deletions packages/clerk-js/src/ui/elements/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ export const Menu = withFloatingTree((props: MenuProps) => {
);
});

type MenuTriggerProps = React.PropsWithChildren<Record<never, never>>;
type MenuTriggerProps = React.PropsWithChildren<{ arialLabel?: string | ((open: boolean) => string) }>;

export const MenuTrigger = (props: MenuTriggerProps) => {
const { children } = props;
const { children, arialLabel } = props;
const { popoverCtx, elementId } = useMenuState();
const { reference, toggle } = popoverCtx;
const { reference, toggle, isOpen } = popoverCtx;

const normalizedAriaLabel = typeof arialLabel === 'function' ? arialLabel(isOpen) : arialLabel;

if (!isValidElement(children)) {
return null;
Expand All @@ -60,6 +62,8 @@ export const MenuTrigger = (props: MenuTriggerProps) => {
ref: reference,
elementDescriptor: children.props.elementDescriptor || descriptors.menuButton,
elementId: children.props.elementId || descriptors.menuButton.setId(elementId),
'aria-label': normalizedAriaLabel,
'aria-expanded': isOpen,
onClick: (e: React.MouseEvent) => {
children.props?.onClick?.(e);
toggle();
Expand Down
Loading

0 comments on commit 150f20f

Please sign in to comment.