Skip to content

fix: Long secret key in success dialog#3938

Merged
chronark merged 10 commits intomainfrom
eng-2057-creating-long-api-keys-cuts-off-modal-content
Oct 1, 2025
Merged

fix: Long secret key in success dialog#3938
chronark merged 10 commits intomainfrom
eng-2057-creating-long-api-keys-cuts-off-modal-content

Conversation

@MichaelUnkey
Copy link
Collaborator

@MichaelUnkey MichaelUnkey commented Sep 9, 2025

… snippet

What does this PR do?

Fixes # (issue)
ENG-2057

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Dialog looks ok with long key
  • Does not cause issues with success dialog
  • Looks like we think it should

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@linear
Copy link

linear bot commented Sep 9, 2025

@changeset-bot
Copy link

changeset-bot bot commented Sep 9, 2025

⚠️ No Changeset found

Latest commit: c399f2a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

📝 Walkthrough

Summary by CodeRabbit

  • Style
    • Constrained the “Key Created” success dialog to a maximum width for a cleaner, more readable layout.
    • Enabled horizontal scrolling in the secret/code section to prevent content overflow and clipping on smaller screens.
    • Improves overall usability when viewing long secrets or code snippets.

Walkthrough

Refactors KeyCreatedSuccessDialog to a typed FC with a new props interface, makes onClose/onCreateAnother awaitable, updates confirm-close flow to await callbacks, and adjusts KeySecretSection layout by moving visibility/copy controls outside the Code block and adding overflow handling.

Changes

Cohort / File(s) Summary
Dialog props & control-flow
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx
Added KeyCreatedSuccessDialogProps and exported component as FC<KeyCreatedSuccessDialogProps>; made onClose/onCreateAnother accept `void
Key secret rendering & controls
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx
Moved VisibleButton and CopyButton out of Code into an external wrapper; preserved snippet vs masked-key toggle (showKeyInSnippet); added overflow-x-auto and new className props for layout/scrolling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant D as KeyCreatedSuccessDialog
  participant S as KeySecretSection
  participant C as Consumer (caller)

  U->>D: Click "Create another" or "Close"
  alt Create another provided
    D->>C: call onCreateAnother()
    Note right of D#orange: handler is awaited (may be Promise)
    C-->>D: resolves
  end
  D->>C: call onClose()
  Note right of D#lightblue: onClose is awaited (void or Promise)
  C-->>D: resolves
  D->>S: render secret (showKeyInSnippet / masked)
  S-->>D: user toggles visibility / copies
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Bug

Suggested reviewers

  • chronark
  • perkinsjr
  • mcstepp
  • ogzhanolguncu

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "fix: Long secret key in success dialog" clearly describes the main change in this PR. The changeset shows modifications to the key creation success dialog component to handle long secret keys properly, including overflow handling adjustments and UI improvements. The title accurately captures the bug fix nature of the change and specifically identifies what issue is being addressed (long secret keys in the success dialog), making it clear and specific enough for teammates to understand the primary change when scanning history.
Description Check ✅ Passed The pull request description follows the template structure well and includes most required sections. The author has filled out the type of change (bug fix), provided testing instructions describing what to verify, and completed nearly all items in the required checklist including running build/format commands, self-reviewing, and merging latest changes. However, there are a few gaps: the "What does this PR do?" section lacks a detailed summary of the actual changes made (it only references the issue number ENG-2057), one required checklist item about responsiveness is unchecked, and the appreciated items for UI changes (screenshots/recordings) are not provided despite this being a UI-related bug fix.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch eng-2057-creating-long-api-keys-cuts-off-modal-content

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7baf572 and 670938b.

📒 Files selected for processing (2)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (5 hunks)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3156
File: apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/index.tsx:112-0
Timestamp: 2025-04-22T11:49:06.167Z
Learning: In the CreateKeyDialog component of Unkey, section navigation is designed to always allow progression even if validation fails, as visual indicators display the validation state of each section.
📚 Learning: 2025-08-13T19:55:19.828Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3779
File: apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx:39-41
Timestamp: 2025-08-13T19:55:19.828Z
Learning: The Tailwind CSS truncate utility includes white-space: nowrap behavior along with overflow: hidden and text-overflow: ellipsis, making additional nowrap classes redundant.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx
📚 Learning: 2025-08-13T19:55:19.828Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3779
File: apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx:39-41
Timestamp: 2025-08-13T19:55:19.828Z
Learning: The Tailwind CSS truncate utility already includes white-space: nowrap behavior along with overflow: hidden and text-overflow: ellipsis, making additional nowrap classes redundant.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx
🧬 Code graph analysis (1)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1)
internal/ui/src/components/buttons/copy-button.tsx (1)
  • CopyButton (27-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
🔇 Additional comments (6)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (5)

13-21: LGTM! Clean interface with async callback support.

The props interface correctly types both synchronous and asynchronous callbacks, providing flexibility for consumers.


9-9: Good addition of explicit FC typing.

Also applies to: 22-22


63-63: Excellent async callback handling.

The use of Promise.resolve() ensures both sync and async callbacks work correctly, and the try-catch block provides proper error handling.

Also applies to: 73-73, 79-79


122-122: Overflow handling correctly addresses the long key cut-off issue.

Replacing truncate with overflow-auto enables scrolling for long keys, directly fixing ENG-2057.

Verify with a 200+ byte key (as mentioned in PR comments by chronark) that:

  • The dialog displays the full key via horizontal scrolling
  • No content is clipped or cut off
  • Single scrollbar appears (no nested scrolling issues)

Also applies to: 127-127


189-195: Key secret display correctly enables scrolling instead of truncation.

The overflow-x-auto class on secretKeyClassName and the codeClassName prop properly address the original bug where long keys were cut off.

apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1)

63-75: CopyButton leaks the unmasked secret when the UI shows it masked.

Line 74 always copies the full snippet regardless of showKeyInSnippet state. When users mask the key (visibility toggled off), they expect the copy to respect that choice.

Apply this diff to align copy behavior with visibility:

-          <CopyButton value={snippet} className="absolute right-3 md:right-4 top-3 md:top-4" />
+          <CopyButton 
+            value={showKeyInSnippet ? snippet : snippet.replace(keyValue, maskedKey)} 
+            className="absolute right-3 md:right-4 top-3 md:top-4" 
+          />

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 9, 2025

Thank you for following the naming conventions for pull request titles! 🙏

@vercel
Copy link

vercel bot commented Sep 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
dashboard Ready Ready Preview Comment Sep 30, 2025 0:23am
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
engineering Ignored Ignored Preview Sep 30, 2025 0:23am

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (2)

119-121: Modal min-width breaks small screens; use responsive width instead.

min-w-[760px] will overflow/clamp on mobile and can still clip long keys despite inner scrolling. Prefer a responsive width with a max cap.

Apply this diff:

-      <DialogContent
-        className="drop-shadow-2xl border-gray-4 overflow-hidden !rounded-2xl p-0 gap-0 min-w-[760px] max-h-[90vh] overflow-y-auto"
+      <DialogContent
+        className="drop-shadow-2xl border-gray-4 overflow-hidden !rounded-2xl p-0 gap-0 w-[90vw] max-w-[760px] max-h-[90vh] overflow-y-auto"

40-45: beforeunload handler won’t show a prompt in modern browsers.

You need to set e.returnValue to trigger the native confirmation.

Apply this diff:

-    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
-      e.preventDefault();
-    };
+    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
+      e.preventDefault();
+      e.returnValue = "";
+    };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0086690 and 28e8842.

📒 Files selected for processing (1)
  • apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3156
File: apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/index.tsx:112-0
Timestamp: 2025-04-22T11:49:06.167Z
Learning: In the CreateKeyDialog component of Unkey, section navigation is designed to always allow progression even if validation fails, as visual indicators display the validation state of each section.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test

@chronark
Copy link
Collaborator

CleanShot 2025-09-15 at 10 09 22@2x this is what it looks like for me

I tried it with a 200 byte length key

@chronark chronark self-assigned this Sep 15, 2025
Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to clamp the key secret itself, not the entire dialog

@chronark
Copy link
Collaborator

@MichaelUnkey

eng-2057-creating-long-api-keys-cuts-off-modal-content
@coderabbitai coderabbitai bot requested a review from chronark September 29, 2025 16:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (3)

13-20: Props allow async callbacks, but they aren’t awaited → unhandled rejections/ordering hazards

onClose/onCreateAnother can return Promise, yet handleConfirmClose calls them without await, so errors won’t hit the try/catch and sequencing may race.

Make handleConfirmClose async and await callbacks:

-  const handleConfirmClose = () => {
+  const handleConfirmClose = async () => {
     if (!pendingAction) {
       console.error("No pending action when confirming close");
       return;
     }
     setIsConfirmOpen(false);
     try {
-      // Always close the dialog first
-      onClose();
+      // Always close the dialog first
+      await onClose();
       // Then execute the specific action
       switch (pendingAction) {
         case "create-another":
-          if (onCreateAnother) {
-            onCreateAnother();
-          } else {
+          if (onCreateAnother) {
+            await onCreateAnother();
+          } else {
             console.warn("onCreateAnother callback not provided");
           }
           break;

Confirm no downstream code relies on fire-and-forget behavior; if so, we can wrap awaits in void with explicit .catch(toast…) instead.


122-122: Fixed min-width (760px) breaks small screens; use responsive width constraints

A hard min-w-[760px] can overflow mobile/tablet viewports. Prefer a responsive width cap:

-        className="drop-shadow-2xl border-gray-4 !rounded-2xl p-0 gap-0 min-w-[760px] max-h-[90vh] overflow-auto"
+        className="drop-shadow-2xl border-gray-4 !rounded-2xl p-0 gap-0 w-[min(92vw,760px)] max-h-[90vh] overflow-y-auto"

Resize the viewport to <760px and ensure no horizontal scrolling is introduced by the dialog container.


38-52: beforeunload handler won’t reliably warn; set e.returnValue

Browsers require e.returnValue = "" (or a string) to trigger the native unload confirmation.

-    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
-      e.preventDefault();
-    };
+    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
+      e.preventDefault();
+      e.returnValue = "";
+    };

Confirm the native tab-close prompt appears while the dialog is open.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28e8842 and 7baf572.

📒 Files selected for processing (2)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (3 hunks)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3156
File: apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/index.tsx:112-0
Timestamp: 2025-04-22T11:49:06.167Z
Learning: In the CreateKeyDialog component of Unkey, section navigation is designed to always allow progression even if validation fails, as visual indicators display the validation state of each section.
📚 Learning: 2025-07-02T14:13:01.711Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3425
File: apps/engineering/content/design/components/filter/control-cloud.examples.tsx:73-83
Timestamp: 2025-07-02T14:13:01.711Z
Learning: In apps/engineering/content/design/components/, when the `RenderComponentWithSnippet` component does not render code snippets correctly, use the `customCodeSnippet` prop to manually provide the correct JSX code as a string. This manual approach is necessary due to technical limitations in the automatic rendering mechanism.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx
🧬 Code graph analysis (1)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1)
internal/ui/src/components/buttons/copy-button.tsx (1)
  • CopyButton (27-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (1)

174-176: Good use of truncate at the single-line key name

Single-line, bounded text with a tooltip is the right place for truncate. No change needed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1)

71-74: Inner scrolling div likely unnecessary; rely on Code’s pre for padding/scrolling.

Simplify to reduce nested scroll containers and potential clipping. This is addressed in the diff above by moving p-4 to preClassName and removing the inner wrapper.

apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (1)

122-122: Hard min-width can cause horizontal overflow on small screens.

Prefer responsive sizing to keep the dialog usable on mobile while preserving the intended width on larger screens.

-        className="drop-shadow-2xl border-gray-4 !rounded-2xl p-0 gap-0 min-w-[760px] max-h-[90vh] overflow-auto"
+        className="drop-shadow-2xl border-gray-4 !rounded-2xl p-0 gap-0 w-[calc(100vw-2rem)] sm:min-w-[760px] max-w-[min(100vw-2rem,760px)] max-h-[90vh] overflow-auto"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28e8842 and 7baf572.

📒 Files selected for processing (2)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (3 hunks)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3156
File: apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/index.tsx:112-0
Timestamp: 2025-04-22T11:49:06.167Z
Learning: In the CreateKeyDialog component of Unkey, section navigation is designed to always allow progression even if validation fails, as visual indicators display the validation state of each section.
📚 Learning: 2025-07-02T14:13:01.711Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3425
File: apps/engineering/content/design/components/filter/control-cloud.examples.tsx:73-83
Timestamp: 2025-07-02T14:13:01.711Z
Learning: In apps/engineering/content/design/components/, when the `RenderComponentWithSnippet` component does not render code snippets correctly, use the `customCodeSnippet` prop to manually provide the correct JSX code as a string. This manual approach is necessary due to technical limitations in the automatic rendering mechanism.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-secret-section.tsx
🔇 Additional comments (2)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_components/create-key/components/key-created-success-dialog.tsx (2)

9-9: Type‑only import for FC is fine.

Import hygiene looks good; using type FC avoids bundling types at runtime.


189-196: Verify long key UX end‑to‑end.

After applying the above, please validate:

  • 200‑byte key renders fully without ellipsis in SecretKey; horizontal scroll works.
  • Code block scrolls horizontally; overlay buttons stay aligned at top‑right and remain clickable.
  • Toggling visibility updates both display and CopyButton value; copying while masked does not leak the real key.
  • Dialog remains usable on narrow viewports (<760px) without horizontal page scroll.

@chronark chronark merged commit 19ca176 into main Oct 1, 2025
17 checks passed
@chronark chronark deleted the eng-2057-creating-long-api-keys-cuts-off-modal-content branch October 1, 2025 07:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants