Repository: soofstad/react-oauth2-pkce
Pull request #198 (2024-10-25 01:14:13)

Popup window to be centred on screen
## What does this pull request change?
Popup window to be centred in the window (or screen)

## Why is this pull request needed?
Popup window is not centred, and would provide better user experience if centred on the page theyre working on that invoked the login

## Issues related to this change
None

sebastianvitterso: @thejurassic, are you still interested in this change? 

Patch:
diff --git a/src/authentication.ts b/src/authentication.ts
index 48e8539..a01f7a1 100644
--- a/src/authentication.ts
+++ b/src/authentication.ts
@@ -1,5 +1,6 @@
 import { postWithXForm } from './httpUtils'
 import { generateCodeChallenge, generateRandomString } from './pkceUtils'
+import { calculatePopupPosition } from './popupUtils'
 import type {
   TInternalConfig,
   TPrimitiveRecord,
@@ -54,7 +55,12 @@ export async function redirectToLogin(
     if (config?.preLogin) config.preLogin()
 
     if (method === 'popup') {
-      const handle: null | WindowProxy = window.open(loginUrl, 'loginPopup', 'popup width=600 height=600')
+      const { width, height, left, top } = calculatePopupPosition(600, 600)
+      const handle: null | WindowProxy = window.open(
+        loginUrl,
+        'loginPopup',
+        `width=${width},height=${height},top=${top},left=${left}`
+      )
       if (handle) return
       console.warn('Popup blocked. Redirecting to login page. Disable popup blocker to use popup login.')
     }
diff --git a/src/popupUtils.ts b/src/popupUtils.ts
new file mode 100644
index 0000000..a8f541c
--- /dev/null
+++ b/src/popupUtils.ts
@@ -0,0 +1,25 @@
+import type { TPopupPosition } from './types'
+
+export function calculatePopupPosition(popupWidth = 600, popupHeight = 600): TPopupPosition {
+  // Calculate the screen dimensions and position the popup at the center
+  const screenLeft = window.screenLeft
+  const screenTop = window.screenTop
+  const screenWidth = window.innerWidth
+  const screenHeight = window.innerHeight
+
+  // Calculate the position to center the popup
+  const defaultLeft = screenLeft + (screenWidth - popupWidth) / 2
+  const defaultTop = screenTop + (screenHeight - popupHeight) / 2
+
+  // Ensure the bottom-right corner does not go off the screen
+  // Adjust the left and top positions if necessary
+  const maxLeft = screenLeft + (screenWidth - popupWidth)
+  const maxTop = screenTop + (screenHeight - popupHeight)
+
+  return {
+    width: Math.min(popupWidth, screenWidth),
+    height: Math.min(popupHeight, screenHeight),
+    left: Math.max(0, Math.min(defaultLeft, maxLeft)),
+    top: Math.max(0, Math.min(defaultTop, maxTop)),
+  }
+}
diff --git a/src/types.ts b/src/types.ts
index c759889..0136e43 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -34,6 +34,13 @@ export type TTokenResponse = {
   id_token?: string
 }
 
+export type TPopupPosition = {
+  left: number
+  top: number
+  width: number
+  height: number
+}
+
 export interface IAuthProvider {
   authConfig: TAuthConfig
   children: ReactNode


Test patch:
diff --git a/tests/login.test.tsx b/tests/login.test.tsx
index 15d189a..d4ef350 100644
--- a/tests/login.test.tsx
+++ b/tests/login.test.tsx
@@ -21,6 +21,9 @@ test('First page visit should redirect to auth provider for login', async () =>
 })
 
 test('First page visit should popup to auth provider for login', async () => {
+  // set window size to 1200x800 to make test predictable in different environments
+  global.innerWidth = 1200
+  global.innerHeight = 800
   render(
     <AuthProvider authConfig={{ ...authConfig, loginMethod: 'popup' }}>
       <AuthConsumer />
@@ -33,7 +36,7 @@ test('First page visit should popup to auth provider for login', async () => {
         /^myAuthEndpoint\?response_type=code&client_id=myClientID&redirect_uri=http%3A%2F%2Flocalhost%2F&code_challenge=.{43}&code_challenge_method=S256&scope=someScope\+openid&state=testState/gm
       ),
       'loginPopup',
-      'popup width=600 height=600'
+      'width=600,height=600,top=100,left=300'
     )
   })
 })