Skip to content

chore: Move loading component to UI #3266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

MichaelUnkey
Copy link
Collaborator

@MichaelUnkey MichaelUnkey commented May 16, 2025

What does this PR do?

Fixes # (issue)

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?

Loading components still work as intended

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

Summary by CodeRabbit

  • New Features

    • Introduced a new animated loading indicator with customizable size and animation speed, now available across the app interface.
  • Documentation

    • Added comprehensive documentation and interactive examples for the new loading indicator, including usage instructions and customization options.
  • Refactor

    • Consolidated and streamlined import statements for UI components throughout the dashboard for improved code clarity.
    • Migrated loading spinner usage to the new centralized loading component from the external UI library.
  • Style

    • Updated buttons and UI elements to use a unified loading state, removing inline loading spinners in favor of the new loading indicator component.

Copy link

changeset-bot bot commented May 16, 2025

⚠️ No Changeset found

Latest commit: 53d2690

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

Copy link
Contributor

coderabbitai bot commented May 16, 2025

📝 Walkthrough
## Walkthrough

This change removes the locally defined `Loading` component from the dashboard app and replaces its usage with the new `Loading` component from the `@unkey/ui` package. All relevant import statements are consolidated, and button loading states are now handled via the `loading` prop instead of conditional rendering. Documentation and examples for the new `Loading` component are added, and it is exported from the UI package.

## Changes

| File(s)                                                                                       | Change Summary                                                                                                                        |
|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| `apps/dashboard/components/dashboard/loading.tsx`                                             | Deleted the local `Loading` component implementation.                                                                                 |
| `internal/ui/src/components/loading.tsx`, `internal/ui/src/index.ts`                          | Added new `Loading` component and its export to the UI package.                                                                       |
| `apps/engineering/content/design/components/loading.mdx`, `apps/engineering/.../loading.example.tsx` | Added documentation and usage examples for the new `Loading` component.                                                               |
| `apps/dashboard/app/**/page.tsx`, `apps/dashboard/app/**/client.tsx`, `apps/dashboard/app/**/ts(x)?`, `apps/dashboard/components/**/index.tsx`, `apps/dashboard/components/**/table.tsx`, `apps/dashboard/components/navigation/sidebar/team-switcher.tsx`, `apps/dashboard/components/opt-in.tsx` | Replaced local `Loading` imports with `@unkey/ui`'s `Loading`, consolidated UI component imports, and refactored button loading logic to use the `loading` prop. |
| `apps/dashboard/app/(app)/settings/root-keys/new/client.tsx`, `apps/dashboard/app/(app)/settings/root-keys/[keyId]/update-root-key-name.tsx`, `apps/dashboard/app/new/create-api.tsx`, `apps/dashboard/app/new/create-workspace.tsx`, `apps/dashboard/app/new/keys.tsx`, `apps/dashboard/components/dashboard/root-key-table/index.tsx`, `apps/dashboard/components/dashboard/root-key-table/table.tsx`, `apps/dashboard/app/integrations/vercel/callback/client.tsx` | Removed conditional rendering of the loading spinner inside buttons; now always display static label, using the `loading` prop for state. |
| `apps/dashboard/app/(app)/settings/team/client.tsx`, `apps/dashboard/app/(app)/settings/team/invitations.tsx`, `apps/dashboard/app/(app)/settings/team/members.tsx`, `apps/dashboard/app/(app)/settings/team/role-switcher.tsx`, `apps/dashboard/app/(app)/settings/vercel/client.tsx`, `apps/dashboard/app/(app)/settings/vercel/loading.tsx`, `apps/dashboard/app/(app)/settings/vercel/page.tsx`, `apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx`, `apps/dashboard/app/auth/sign-in/email-code.tsx`, `apps/dashboard/app/auth/sign-in/email-signin.tsx`, `apps/dashboard/app/auth/sign-in/email-verify.tsx`, `apps/dashboard/app/auth/sign-in/oauth-signin.tsx`, `apps/dashboard/app/auth/sign-up/[[...sign-up]]/page.tsx`, `apps/dashboard/app/auth/sign-up/email-code.tsx`, `apps/dashboard/app/auth/sign-up/email-signup.tsx`, `apps/dashboard/app/auth/sign-up/oauth-signup.tsx`, `apps/dashboard/app/integrations/vercel/callback/loading.tsx`, `apps/dashboard/app/integrations/vercel/callback/workspace.tsx` | Changed all `Loading` imports to use `@unkey/ui` instead of local dashboard path; no logic changes.                                   |
| `apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/credits-setup.tsx`, `apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/metadata-setup.tsx`, `apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/logs-table.tsx`, `apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx`, `apps/dashboard/app/(app)/authorization/permissions/[permissionId]/settings-client.tsx`, `apps/dashboard/app/(app)/authorization/roles/[roleId]/settings-client.tsx`, `apps/dashboard/app/(app)/identities/[identityId]/page.tsx`, `apps/dashboard/app/(app)/logs/components/table/logs-table.tsx`, `apps/dashboard/app/(app)/ratelimits/[namespaceId]/_components/identifier-dialog.tsx`, `apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx`, `apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx`, `apps/dashboard/app/(app)/settings/billing/client.tsx`, `apps/dashboard/app/(app)/settings/billing/page.tsx`, `apps/dashboard/components/dashboard/feedback-component.tsx` | Consolidated multiple UI component imports from `@unkey/ui` into single import lines; no logic changes.                               |
| `internal/ui/src/components/animated-loading-spinner.tsx`, `internal/ui/src/components/button.tsx` | Extracted `AnimatedLoadingSpinner` to its own module and updated imports accordingly.                                                  |
| `apps/dashboard/app/(app)/apis/[apiId]/_components/rbac-dialog-content.tsx`, `apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx`, `apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx` | Replaced imports of `AnimatedLoadingSpinner` with `Loading` from `@unkey/ui` and updated usage accordingly.                            |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant AppComponent
    participant UI_Library as @unkey/ui
    AppComponent->>UI_Library: import { Loading }
    AppComponent->>AppComponent: <Button loading={isLoading}>Label</Button>
    Note right of AppComponent: Button shows spinner via loading prop (no manual <Loading />)

Possibly related PRs

  • unkeyed/unkey#2737: Initial integration of Button from @unkey/ui and removal of old CSS/component sources; related to UI component migration in this PR.
  • unkeyed/unkey#3192: Introduces TimestampInfo component to @unkey/ui, which is imported in consolidated form here.
  • unkeyed/unkey#2933: Introduces a new version of Button component; this PR consolidates imports of Button accordingly.

Suggested labels

Improvement, Needs Approval

Suggested reviewers

  • perkinsjr
  • mcstepp
  • chronark
  • ogzhanolguncu


</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=unkeyed/unkey&utm_content=3266):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Copy link

vercel bot commented May 16, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
dashboard ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 3, 2025 0:29am
engineering 🛑 Canceled (Inspect) Jun 3, 2025 0:29am

Copy link
Contributor

github-actions bot commented May 16, 2025

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

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

🧹 Nitpick comments (2)
internal/ui/src/components/loading.tsx (1)

1-1: Improve the biome-ignore comment specificity.

The biome-ignore comment could be more specific about which rule is being ignored and why.

-// biome-ignore lint: React in this context is used throughout, so biome will change to types because no APIs are used even though React is needed.
+// biome-ignore lint/correctness/noUnusedImports: React is required for JSX transformation
apps/engineering/content/design/components/loading.mdx (1)

43-49: Consider adding browser compatibility information.

The props table is accurate and well-formatted. Consider adding a note about SVG animation browser support since this component relies on SVG <animate> elements.

Add a browser compatibility section:

+## Browser Compatibility
+
+This component uses SVG animations (`<animate>` elements) which are supported in all modern browsers. For older browser support, consider using CSS animations as a fallback.
+
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51768aa and b06eb54.

📒 Files selected for processing (51)
  • apps/dashboard/app/(app)/[...not-found]/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/credits-setup.tsx (1 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/metadata-setup.tsx (1 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx (1 hunks)
  • apps/dashboard/app/(app)/authorization/permissions/[permissionId]/settings-client.tsx (1 hunks)
  • apps/dashboard/app/(app)/authorization/roles/[roleId]/settings-client.tsx (1 hunks)
  • apps/dashboard/app/(app)/identities/[identityId]/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/logs/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/_components/identifier-dialog.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/billing/client.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/billing/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/root-keys/[keyId]/update-root-key-name.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/root-keys/new/client.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/client.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/invitations.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/invite.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/members.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/role-switcher.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/vercel/client.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/vercel/loading.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/vercel/page.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-in/email-code.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-in/email-signin.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-in/email-verify.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-in/oauth-signin.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-up/[[...sign-up]]/page.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-up/email-code.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-up/email-signup.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-up/oauth-signup.tsx (1 hunks)
  • apps/dashboard/app/integrations/vercel/callback/client.tsx (1 hunks)
  • apps/dashboard/app/integrations/vercel/callback/loading.tsx (1 hunks)
  • apps/dashboard/app/integrations/vercel/callback/workspace.tsx (1 hunks)
  • apps/dashboard/app/new/create-api.tsx (1 hunks)
  • apps/dashboard/app/new/create-workspace.tsx (1 hunks)
  • apps/dashboard/app/new/keys.tsx (1 hunks)
  • apps/dashboard/components/dashboard/api-key-table/index.tsx (1 hunks)
  • apps/dashboard/components/dashboard/api-key-table/table.tsx (1 hunks)
  • apps/dashboard/components/dashboard/feedback-component.tsx (1 hunks)
  • apps/dashboard/components/dashboard/loading.tsx (0 hunks)
  • apps/dashboard/components/dashboard/root-key-table/index.tsx (1 hunks)
  • apps/dashboard/components/dashboard/root-key-table/table.tsx (1 hunks)
  • apps/dashboard/components/navigation/sidebar/team-switcher.tsx (1 hunks)
  • apps/dashboard/components/opt-in.tsx (1 hunks)
  • apps/engineering/content/design/components/loading.mdx (1 hunks)
  • apps/engineering/content/design/components/loading/loading.example.tsx (1 hunks)
  • internal/ui/src/components/loading.tsx (1 hunks)
  • internal/ui/src/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/components/dashboard/loading.tsx
🔇 Additional comments (52)
apps/dashboard/app/(app)/[...not-found]/page.tsx (1)

1-1: Consolidated imports for UI components: Grouping Button and Empty into a single import from @unkey/ui enhances consistency and reduces import clutter.

apps/dashboard/app/auth/sign-in/email-verify.tsx (1)

8-8: Replaced local Loading import with shared UI library: Switching to the centralized Loading component from @unkey/ui ensures uniform styling and behavior across the application.

apps/dashboard/app/auth/sign-in/email-code.tsx (1)

5-5: Switched to centralized Loading component: Importing Loading from @unkey/ui aligns this file with the global UI toolkit and promotes reuse.

apps/dashboard/app/auth/sign-in/[[...sign-in]]/page.tsx (1)

4-4: Standardized Loading import from UI library: Good replacement of the local loader with the shared Loading component from @unkey/ui for consistency.

apps/dashboard/components/navigation/sidebar/team-switcher.tsx (1)

20-20: Replaced local loader with shared UI component: Importing Loading from @unkey/ui maintains a consistent loading indicator across the dashboard.

apps/dashboard/app/auth/sign-in/oauth-signin.tsx (1)

6-6:

✅ Verification successful

Imported centralized Loading component
Replaced the local loading indicator import with the shared Loading from @unkey/ui. Ensure that @unkey/ui is declared as a dependency in apps/dashboard/package.json and that the version you’re using supports the className prop.

Run this script to verify no residual local imports remain:


🏁 Script executed:

#!/bin/bash
# Verify no leftover local loading imports in the dashboard app
rg -n "components/dashboard/loading"

Length of output: 36


🏁 Script executed:

#!/bin/bash
FILE="apps/dashboard/package.json"
if [ -f "$FILE" ]; then
  echo "📦 Checking '@unkey/ui' in $FILE"
  grep -n '"@unkey/ui"' "$FILE" || echo "⚠️ @unkey/ui is not declared in dependencies"
  echo
  echo "🔍 Extracting version for '@unkey/ui':"
  python3 - << 'EOF'
import json, sys
try:
    pj = json.load(open("apps/dashboard/package.json"))
    ver = pj.get("dependencies", {}).get("@unkey/ui")
    print(ver if ver else "Not declared")
except Exception as e:
    print(f"Error reading JSON: {e}", file=sys.stderr)
EOF
else
  echo "❌ File $FILE not found"
fi

Length of output: 412


🏁 Script executed:

#!/bin/bash
echo "🔍 Searching for LoadingProps in UI package sources"
rg -n "LoadingProps" -A3

echo
echo "🔍 Searching for Loading component definitions"
rg -n "function Loading" -A5
rg -n "const Loading" -A5

Length of output: 6057


Review Approved: Centralized Loading Import Is Correct

  • 📦 Confirmed @unkey/ui is declared in apps/dashboard/package.json (line 59) as workspace:^.
  • ✅ Verified Loading in internal/ui/src/components/loading.tsx extends SVGProps<SVGSVGElement>, so it supports the className prop.
  • 🔍 No residual local loading imports found in the dashboard app.
apps/dashboard/app/auth/sign-up/email-signup.tsx (1)

9-9:

❓ Verification inconclusive

Switched to shared Loading component
The local loading component import was replaced by Loading from @unkey/ui. Confirm that the package is installed in apps/dashboard/package.json and that its API matches the usage here.

To catch any forgotten local imports, run:


🏁 Script executed:

#!/bin/bash
rg -n "components/dashboard/loading"

Length of output: 36


🏁 Script executed:

#!/bin/bash
# Verify that @unkey/ui is listed as a dependency in apps/dashboard/package.json
rg -n '"@unkey/ui"' apps/dashboard/package.json

Length of output: 84


Confirmed shared Loading import
@unkey/ui is declared in apps/dashboard/package.json and no local components/dashboard/loading imports remain. Please verify that the Loading component’s API in @unkey/ui (props, styling, loading behavior) aligns with how it’s used below:

• File: apps/dashboard/app/auth/sign-up/email-signup.tsx
– Check that all passed props and expected rendering match @unkey/ui’s Loading implementation.

apps/dashboard/app/integrations/vercel/callback/workspace.tsx (1)

24-24:

✅ Verification successful

Replaced local loading indicator with shared component
Importing Loading from @unkey/ui centralizes the loader. Ensure your apps/dashboard project has the correct @unkey/ui version installed and that the default sizing works as expected here.

Quick scan for any stray local imports:


🏁 Script executed:

#!/bin/bash
rg -n "components/dashboard/loading"

Length of output: 36


Here’s a deeper check to be sure no local loader remains and that @unkey/ui is declared in your dashboard app:


🏁 Script executed:

#!/bin/bash
# 1. Find any Import of Loading outside of @unkey/ui
echo "Scanning for stray Loading imports not from @unkey/ui…"
rg -n "import.*Loading" -g "*.tsx" -g "*.ts" | grep -v "@unkey/ui" || echo "✔️ No stray Loading imports found"

# 2. Locate dashboard package.json(s) and verify @unkey/ui version
echo -e "\nVerifying @unkey/ui in dashboard package.json:"
PACKAGE_FILES=$(fd -t f package.json | grep "apps/dashboard")
for P in $PACKAGE_FILES; do
  echo "$P"
  rg -n '"@unkey/ui"' "$P" || echo "⚠️ @unkey/ui not declared in $P"
done

Length of output: 1165


Let’s narrow the search to only the Loading import and ignore other similarly named components:


🏁 Script executed:

#!/bin/bash
echo "Scanning for any `import { Loading }` not from `@unkey/ui`"
rg -n "import\s*{\s*Loading\s*}" -g "*.tsx" -g "*.ts" | grep -v "@unkey/ui" || echo "✔️ No stray Loading imports found"

Length of output: 580


No issues found with Loading import or dependency
Scans confirm there are no stray local Loading imports and @unkey/ui is declared in apps/dashboard/package.json. Approving these changes.

apps/dashboard/app/auth/sign-up/email-code.tsx (1)

8-8:

❓ Verification inconclusive

Switched import to shared Loading component
The local loading component has been replaced by the one from @unkey/ui. Please verify that @unkey/ui is listed in your apps/dashboard dependencies and that its API supports the given props.

To ensure no local imports remain:


🏁 Script executed:

#!/bin/bash
rg -n "components/dashboard/loading"

Length of output: 36


🏁 Script executed:

#!/bin/bash
# Verify @unkey/ui is listed as a dependency in apps/dashboard/package.json
rg -n '"@unkey/ui"' apps/dashboard/package.json

# Show how Loading is used in the sign-up form to confirm supported props
rg -n 'Loading' -n apps/dashboard/app/auth/sign-up/email-code.tsx

Length of output: 473


Confirm Loading Component API and Dependencies

  • @unkey/ui is declared in apps/dashboard/package.json at the workspace level.
  • email-code.tsx imports and uses <Loading className="w-4 h-4 mr-2 animate-spin" /> on line 127.

Please verify that the Loading component from @unkey/ui supports the className prop (and any other props you’re passing). Also ensure no local loading component remains:

rg -n "components/dashboard/loading"
apps/dashboard/app/auth/sign-up/[[...sign-up]]/page.tsx (1)

3-3: LGTM! Import path correctly updated.

The Loading component import has been successfully moved from the local dashboard path to the @unkey/ui package, aligning with the PR objective of centralizing UI components.

apps/dashboard/app/auth/sign-in/email-signin.tsx (1)

2-2: LGTM! Import successfully migrated to UI package.

The Loading component import has been correctly updated to use the centralized @unkey/ui package. The component usage on line 52 with custom styling appears to be maintained properly.

apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/metadata-setup.tsx (1)

4-4: Good import consolidation!

Consolidating the Button and FormTextarea imports from @unkey/ui into a single import statement improves code organization and readability.

apps/dashboard/app/integrations/vercel/callback/loading.tsx (1)

1-1: LGTM! Loading component import successfully migrated.

The import has been correctly updated to use the centralized Loading component from @unkey/ui. The component usage remains unchanged, maintaining the expected loading page behavior.

apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/credits-setup.tsx (1)

3-10: Excellent import consolidation!

The multiline import statement for @unkey/ui components is well-formatted and improves code organization. This consolidation makes it easier to see all UI dependencies at a glance and follows standard formatting conventions.

apps/dashboard/app/(app)/identities/[identityId]/page.tsx (1)

19-19: Import consolidation looks good
Grouping Button and CopyButton from @unkey/ui matches their usage in this file and aligns with the broader import cleanup.

apps/dashboard/app/(app)/settings/vercel/page.tsx (1)

14-14: Consolidated UI imports are correct
Importing Button and Empty together from @unkey/ui aligns with the refactoring goal and matches all component usages.

apps/dashboard/app/(app)/settings/root-keys/new/client.tsx (1)

5-5: Centralized Loading import is correct
Replacing the local Loading with the one from @unkey/ui, alongside Button and CopyButton, is consistent and covers all usages in this component.

apps/dashboard/app/(app)/settings/billing/client.tsx (1)

6-6: Grouped @unkey/ui imports are accurate
Importing Button, Empty, and SettingCard in one statement from @unkey/ui is consistent with the rest of the codebase and correctly reflects usage.

apps/dashboard/app/(app)/authorization/permissions/[permissionId]/settings-client.tsx (1)

9-9: UI imports consolidated appropriately
Grouping Button, Input, and SettingCard into a single import from @unkey/ui follows the updated pattern and matches component usage.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx (1)

11-11: LGTM! Clean import consolidation.

The consolidation of UI component imports from @unkey/ui into a single statement improves code organization and readability.

apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx (1)

7-7: LGTM! Consistent import consolidation.

The UI component import consolidation aligns with the broader pattern across the codebase and improves code organization.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx (1)

10-10: LGTM! Import consolidation follows established pattern.

The consolidation of UI component imports maintains consistency with the broader refactoring pattern and improves code maintainability.

apps/dashboard/app/(app)/authorization/roles/[roleId]/settings-client.tsx (1)

8-8: LGTM! Clean import consolidation.

The consolidation of UI component imports from @unkey/ui into a single statement improves code organization and readability without affecting functionality.

apps/dashboard/app/(app)/logs/components/table/logs-table.tsx (1)

9-9: LGTM! Import statement consolidation approved.

The consolidation of Button, Empty, and TimestampInfo imports from @unkey/ui into a single statement follows the established pattern and improves code organization.

apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/logs-table.tsx (1)

8-8: LGTM! Consistent import organization.

The import consolidation follows the established pattern across the codebase and maintains consistency in organizing UI component imports from @unkey/ui.

apps/dashboard/app/(app)/settings/team/role-switcher.tsx (1)

7-7: LGTM! Successfully moved Loading component to UI package.

This change achieves the main PR objective by importing the Loading component from the centralized @unkey/ui package instead of the local dashboard components. The usage remains unchanged, ensuring functionality is preserved while improving code organization.

apps/dashboard/app/(app)/settings/billing/page.tsx (1)

6-6: Consolidated imports look good
The grouped import of Button, Empty, Input, and SettingCard from @unkey/ui aligns with the refactor and maintains clarity without duplicating import statements.

apps/dashboard/app/new/keys.tsx (1)

16-16: Updated Loading import from UI package
Replacing the local Loading component import with the one from @unkey/ui centralizes the component usage and ensures consistency across the app.

apps/dashboard/app/new/create-workspace.tsx (1)

18-18: Moved Loading component to UI library
Importing Loading from @unkey/ui instead of a local module aligns with the new shared UI package and preserves the loading indicator behavior.

internal/ui/src/index.ts (1)

9-9: Exported new Loading component
Adding the Loading component to the public exports exposes it for import from @unkey/ui, completing the migration of the loading indicator to the shared UI library.

apps/dashboard/components/dashboard/feedback-component.tsx (1)

14-14: Switched Loading import to shared UI package
Importing Loading from @unkey/ui replaces the local version and enforces a consistent loader across dashboard components.

apps/dashboard/app/new/create-api.tsx (1)

17-17: Consolidated Loading import with UI components
Importing Loading alongside Button from @unkey/ui replaces the local dashboard loading component, aligning with the refactor to centralize UI elements and maintain consistent behavior.

apps/dashboard/app/(app)/settings/team/invitations.tsx (1)

14-14: Centralize UI imports from @unkey/ui
Grouping Button, Empty, and Loading under @unkey/ui removes the local Loading dependency and standardizes imports across the app settings. This matches the PR’s restructuring goal.

apps/dashboard/app/(app)/settings/root-keys/[keyId]/update-root-key-name.tsx (1)

17-17: Move Loading component to UI library
By importing Loading from @unkey/ui (with Button), the file no longer relies on the now-removed local loading component, ensuring consistency with the new centralized UI package.

apps/dashboard/components/dashboard/root-key-table/index.tsx (1)

25-25: Standardize Loading import in root-key-table
Replacing the local Loading import with the one from @unkey/ui consolidates UI dependencies and aligns this component with the broader refactor.

apps/dashboard/components/dashboard/root-key-table/table.tsx (1)

43-43: Consolidated Loading import for data table actions
Importing Loading from @unkey/ui alongside Button ensures that the component uses the centralized loading indicator, removing the legacy local implementation.

apps/dashboard/app/(app)/settings/vercel/loading.tsx (1)

1-1: LGTM! Import source updated correctly.

The import change from local dashboard component to @unkey/ui package aligns perfectly with the PR objective of moving the loading component to the UI package.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/_components/identifier-dialog.tsx (1)

9-17: LGTM! Import consolidation improves code organization.

Consolidating the @unkey/ui imports into a single multiline statement improves readability and follows best practices for import organization.

apps/dashboard/components/dashboard/api-key-table/table.tsx (1)

43-43:

✅ Verification successful

LGTM! Loading import consolidated correctly.

Adding Loading to the existing @unkey/ui import consolidates the component sources and aligns with the refactoring objective. The usage on line 122 should continue to work as expected.

To ensure compatibility, verify that the new Loading component from @unkey/ui has the same API as the previous local component:


🏁 Script executed:

#!/bin/bash
# Verify Loading component usage patterns across the codebase
echo "Checking Loading component usage patterns..."
rg -A 2 -B 2 "<Loading" --type tsx --type ts

Length of output: 173


🏁 Script executed:

#!/bin/bash
echo "Searching usage of <Loading> across the repo..."
rg "<Loading" -n . || true

echo
echo "Searching export definitions of Loading component..."
rg -E "export (const|function) Loading" -n . || true

echo
echo "Locating any loading.tsx or loading.ts files..."
fd -i loading.tsx || true
fd -i loading.ts || true

echo
echo "Finding references to @unkey/ui package..."
rg "@unkey/ui" -n . || true

Length of output: 38521


🏁 Script executed:

#!/bin/bash
echo "Inspecting Loading component export and props in internal/ui/src/components/loading.tsx"
rg -n "export .*Loading" internal/ui/src/components/loading.tsx || true

echo
echo "Inspecting LoadingProps interface:"
rg -n "interface .*LoadingProps" internal/ui/src/components/loading.tsx || true

echo
echo "Printing first 200 lines of Loading component implementation:"
sed -n '1,200p' internal/ui/src/components/loading.tsx || true

Length of output: 2022


LGTM – Loading API Compatibility Confirmed

The Loading component from @unkey/ui exports:

export function Loading({
  width = 24,
  height = 24,
  dur = "0.75s",
  ...props
}: LoadingProps): JSX.Element

where LoadingProps extends SVGProps<SVGSVGElement> (including className). This covers all existing usage patterns (<Loading />, className, width, height, dur, etc.) with no breaking changes.

– No further action required; ready to merge.

apps/dashboard/app/(app)/settings/team/members.tsx (1)

16-16: LGTM! Import consolidation completed successfully.

Consolidating Button, Empty, and Loading imports from @unkey/ui into a single statement improves import organization and aligns with the refactoring objective. All component usages remain functionally identical.

apps/dashboard/components/opt-in.tsx (3)

3-3: Approve import positioning for toast.
The toast import from @/components/ui/toaster is correctly grouped with other local utility imports and follows the established import ordering conventions.


5-5: Approve import of PostHogEvent.
Importing PostHogEvent from @/providers/PostHogProvider here ensures consistent analytics tagging within this component.


7-7: Approve consolidated UI imports including Loading.
Grouping Button, Empty, and the newly externalized Loading under @unkey/ui reduces fragmentation and matches the standardized pattern across the codebase.

apps/dashboard/app/(app)/settings/team/invite.tsx (1)

17-17: Approve consolidated UI imports.
Combining Button, Select, SelectContent, SelectItem, SelectTrigger, and SelectValue into a single import from @unkey/ui improves readability and aligns with the broader import refactor.

apps/dashboard/app/(app)/settings/vercel/client.tsx (1)

24-33: Approve grouped import of UI components.
The import block now correctly pulls Button, Empty, Loading, and all Select variants from @unkey/ui, replacing the old local Loading import and consolidating related imports. This enhances consistency and maintainability.

apps/dashboard/app/(app)/settings/team/client.tsx (1)

6-8: Approve inclusion of core UI components.
Moving Button, Empty, and Loading into the single @unkey/ui import streamlines dependencies and reflects the centralized UI library approach.

apps/dashboard/app/integrations/vercel/callback/client.tsx (1)

13-21: Approve import consolidation from @unkey/ui.
The block importing Button, Loading, and Select-related components from @unkey/ui properly replaces scattered local imports and maintains a clean, unified import structure.

internal/ui/src/components/loading.tsx (3)

5-8: Well-designed interface with proper TypeScript typing.

The LoadingProps interface correctly extends SVGProps<SVGSVGElement>, providing full flexibility for SVG customization while adding the custom dur prop with proper documentation.


14-21: Excellent accessibility and styling implementation.

The component includes proper accessibility with aria-label="Loading" and uses fill-current class for inheriting parent text color. The fixed viewBox "0 0 24 24" with customizable width/height will scale correctly.


22-42:

❓ Verification inconclusive

Verify the complex animation timing coordination.

The animation timing creates a wave effect through carefully orchestrated delays. The timing relationships are:

  • Circle 1: starts immediately, triggers others
  • Circle 2: starts 0.6s before Circle 1 ends (creating overlap)
  • Circle 3: starts 0.45s before Circle 1 ends, then triggers Circle 1 restart

This creates a continuous looping wave effect, but the complexity warrants verification.


🏁 Script executed:

#!/bin/bash
# Description: Search for any existing SVG animation patterns or timing configurations
# to ensure this implementation follows established patterns in the codebase

# Search for other SVG animations in the codebase
rg -A 10 -B 2 '<animate' --type tsx --type ts

# Search for other loading components that might have similar timing
rg -A 5 -B 5 'animate.*begin.*end' --type tsx --type ts

Length of output: 161


Verify complex wave animation timing

The three <animate> elements coordinate via staggered begin offsets and ID-based triggers to create a looping wave effect. Since no other SVG animations with similar patterns were found in the codebase, please:

  • Ensure the sequence (Circle 1: start; Circle 2: starts 0.6 s before Circle 1 ends; Circle 3: starts 0.45 s before Circle 1 ends, then restarts Circle 1) produces a smooth, continuous loop without visual stutter.
  • Test the Loading component interactively (e.g., in Storybook) at different dur values to confirm timing consistency.
  • Consider adding a brief inline comment above the snippet to explain the offset relationships for future maintainers.

File: internal/ui/src/components/loading.tsx
Lines: 22–42

apps/engineering/content/design/components/loading/loading.example.tsx (1)

1-28: Comprehensive examples demonstrating key Loading component features.

The three examples effectively showcase:

  • Default usage with standard 24x24 size and 0.75s duration
  • Custom animation speed (1s duration)
  • Custom sizing (100x100 pixels)

These cover the primary customization scenarios developers would need.

apps/engineering/content/design/components/loading.mdx (1)

52-55: Excellent technical documentation of the animation implementation.

The implementation details clearly explain how the SVG animations work and the accessibility considerations. This helps developers understand the underlying mechanism and customize if needed.

@MichaelUnkey MichaelUnkey marked this pull request as draft May 28, 2025 17:07
auto-merge was automatically disabled May 28, 2025 17:07

Pull request was converted to draft

@MichaelUnkey MichaelUnkey marked this pull request as ready for review May 28, 2025 17:28
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: 1

🧹 Nitpick comments (4)
apps/engineering/content/design/components/loading.mdx (4)

13-19: Clean Up Usage Snippet
The snippet imports Button from @unkey/ui but never uses it. Please remove the unused Button import or update the example to demonstrate <Loading> within a <Button loading> scenario.


21-28: Expand or Prune Examples Section
Only <LoadingExample /> is shown under “## Examples,” yet you’ve also imported two other example components. Consider adding subsections like “### With Custom Duration” and “### Custom SVG Settings” for LoadingWithDurationExample and LoadingCustomExample, or remove their imports if they’re not needed.


33-38: Clarify Prop Naming
The prop name dur is concise but may be unclear to less familiar users. Consider renaming it to duration in both the component API and documentation, or add a brief note explaining that dur stands for “duration.”


43-45: Grammar Tweak
Minor wording improvement: merge the two sentences in the Implementation Details for better flow. For example:

The loading animation uses chained `<animate>` SVG elements to create a smooth wave-like effect and supports styling/accessibility via Tailwind CSS.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~43-~43: Possible missing comma found.
Context: ...ogether with precise timing to create a smooth wave-like effect. The component is bui...

(AI_HYDRA_LEO_MISSING_COMMA)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8432e9d and 791c169.

📒 Files selected for processing (24)
  • apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/billing/client.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/billing/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/root-keys/[keyId]/update-root-key-name.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/root-keys/new/client.tsx (2 hunks)
  • apps/dashboard/app/(app)/settings/team/client.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/invite.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/team/members.tsx (1 hunks)
  • apps/dashboard/app/(app)/settings/vercel/client.tsx (2 hunks)
  • apps/dashboard/app/auth/sign-in/email-signin.tsx (1 hunks)
  • apps/dashboard/app/auth/sign-up/email-signup.tsx (1 hunks)
  • apps/dashboard/app/integrations/vercel/callback/client.tsx (2 hunks)
  • apps/dashboard/app/new/create-api.tsx (1 hunks)
  • apps/dashboard/app/new/create-workspace.tsx (1 hunks)
  • apps/dashboard/app/new/keys.tsx (2 hunks)
  • apps/dashboard/components/dashboard/feedback-component.tsx (1 hunks)
  • apps/dashboard/components/dashboard/root-key-table/index.tsx (1 hunks)
  • apps/dashboard/components/dashboard/root-key-table/table.tsx (1 hunks)
  • apps/dashboard/components/navigation/sidebar/team-switcher.tsx (1 hunks)
  • apps/dashboard/components/opt-in.tsx (2 hunks)
  • apps/engineering/content/design/components/loading.mdx (1 hunks)
  • apps/engineering/content/design/components/loading/loading.example.tsx (1 hunks)
  • internal/ui/src/components/loading.tsx (1 hunks)
  • internal/ui/src/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • apps/dashboard/app/new/create-workspace.tsx
  • apps/dashboard/app/(app)/apis/[apiId]/settings/components/delete-protection.tsx
  • apps/dashboard/components/navigation/sidebar/team-switcher.tsx
  • apps/dashboard/components/dashboard/feedback-component.tsx
  • apps/dashboard/app/new/create-api.tsx
  • apps/dashboard/app/(app)/settings/team/invite.tsx
  • apps/dashboard/app/(app)/settings/team/client.tsx
  • apps/dashboard/app/(app)/settings/root-keys/new/client.tsx
🚧 Files skipped from review as they are similar to previous changes (15)
  • apps/dashboard/app/auth/sign-up/email-signup.tsx
  • apps/dashboard/app/auth/sign-in/email-signin.tsx
  • internal/ui/src/index.ts
  • apps/dashboard/app/new/keys.tsx
  • apps/dashboard/app/integrations/vercel/callback/client.tsx
  • apps/dashboard/components/dashboard/root-key-table/table.tsx
  • apps/dashboard/app/(app)/settings/billing/page.tsx
  • apps/dashboard/app/(app)/settings/billing/client.tsx
  • apps/dashboard/app/(app)/settings/root-keys/[keyId]/update-root-key-name.tsx
  • apps/dashboard/components/dashboard/root-key-table/index.tsx
  • apps/dashboard/app/(app)/settings/vercel/client.tsx
  • apps/dashboard/app/(app)/settings/team/members.tsx
  • apps/dashboard/components/opt-in.tsx
  • internal/ui/src/components/loading.tsx
  • apps/engineering/content/design/components/loading/loading.example.tsx
🧰 Additional context used
🪛 LanguageTool
apps/engineering/content/design/components/loading.mdx

[uncategorized] ~43-~43: Possible missing comma found.
Context: ...ogether with precise timing to create a smooth wave-like effect. The component is bui...

(AI_HYDRA_LEO_MISSING_COMMA)

⏰ Context from checks skipped due to timeout of 90000ms (14)
  • GitHub Check: Test Go API Local / Test (Shard 8/8)
  • GitHub Check: Test Go API Local / Test (Shard 4/8)
  • GitHub Check: Test Go API Local / Test (Shard 7/8)
  • GitHub Check: Test Go API Local / Test (Shard 6/8)
  • GitHub Check: Test Go API Local / Test (Shard 2/8)
  • GitHub Check: Test Go API Local / Test (Shard 3/8)
  • GitHub Check: Test Go API Local / Test (Shard 5/8)
  • GitHub Check: Test Go API Local / Test (Shard 1/8)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/id
  • GitHub Check: Test Packages / Test ./internal/billing
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/engineering/content/design/components/loading.mdx (1)

1-4: Frontmatter Validation
The YAML frontmatter is correctly formatted with title and description fields enclosed by ---.

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: 0

🧹 Nitpick comments (2)
apps/engineering/content/design/components/loading.mdx (2)

13-19: Remove Unused Button Import
The snippet imports Button from @unkey/ui but only demonstrates <Loading />. Consider removing Button to avoid confusion, or adjust the example to showcase the loading prop on Button.


41-45: Implementation Details Clarity
The description of the SVG animation is concise. Optionally, you could link to any ARIA attributes or accessibility notes if the component exposes them.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~43-~43: Possible missing comma found.
Context: ...ogether with precise timing to create a smooth wave-like effect. The component is bui...

(AI_HYDRA_LEO_MISSING_COMMA)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 791c169 and f167c3d.

📒 Files selected for processing (2)
  • apps/engineering/content/design/components/loading.mdx (1 hunks)
  • internal/ui/src/components/loading.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/ui/src/components/loading.tsx
🧰 Additional context used
🪛 LanguageTool
apps/engineering/content/design/components/loading.mdx

[uncategorized] ~43-~43: Possible missing comma found.
Context: ...ogether with precise timing to create a smooth wave-like effect. The component is bui...

(AI_HYDRA_LEO_MISSING_COMMA)

⏰ Context from checks skipped due to timeout of 90000ms (16)
  • GitHub Check: Test Go API Local / Test (Shard 8/8)
  • GitHub Check: Test Go API Local / Test (Shard 7/8)
  • GitHub Check: Test Go API Local / Test (Shard 6/8)
  • GitHub Check: Test Go API Local / Test (Shard 4/8)
  • GitHub Check: Test Go API Local / Test (Shard 5/8)
  • GitHub Check: Test Go API Local / Test (Shard 3/8)
  • GitHub Check: Test Go API Local / Test (Shard 2/8)
  • GitHub Check: Test Go API Local / Test (Shard 1/8)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/keys
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
apps/engineering/content/design/components/loading.mdx (5)

1-4: Valid Front Matter
The YAML front matter correctly defines the title and description metadata for this MDX page.


21-24: Examples Section Usage
Embedding <LoadingExample /> provides a hands-on showcase of the component. Verify that the example covers the default state, custom color, and duration variants as expected.


25-28: Custom Color Explanation
The note on using the fill-current class to inherit text color is clear and accurate.


29-38: Props Table Accuracy
The props table correctly lists width, height, and dur with their types and default values, matching the component implementation.


5-5:

✅ Verification successful

Verify Example Import Path
Ensure that the LoadingExample component exists at apps/engineering/content/design/components/loading/loading.example.tsx so that the import in this MDX does not break.


🏁 Script executed:

#!/bin/bash
# Verify LoadingExample file exists
test -f apps/engineering/content/design/components/loading/loading.example.tsx || echo "ERROR: Missing LoadingExample component"

Length of output: 80


Import Path Verified
The LoadingExample component was found at apps/engineering/content/design/components/loading/loading.example.tsx. No changes are 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: 2

🧹 Nitpick comments (1)
internal/ui/src/components/animated-loading-spinner.tsx (1)

76-90: Consider extracting SVG paths to a constants file.

The hardcoded SVG paths work correctly but could be moved to a separate constants file for better maintainability and potential reuse.

+// In a separate constants file
+export const SPINNER_SEGMENT_PATHS = [
+  "M13.162,3.82c-...",  // segment paths
+  // ... rest of paths
+];
+
// In this file
-function getPathForSegment(index: number) {
-  const paths = [
-    "M13.162,3.82c-...",
-    // ... all paths
-  ];
-  return paths[index];
-}
+import { SPINNER_SEGMENT_PATHS } from './constants';
+
+function getPathForSegment(index: number) {
+  return SPINNER_SEGMENT_PATHS[index];
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f167c3d and d59652a.

📒 Files selected for processing (9)
  • apps/dashboard/app/(app)/apis/[apiId]/_components/rbac-dialog-content.tsx (2 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx (2 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx (2 hunks)
  • apps/engineering/content/design/components/loading.mdx (1 hunks)
  • apps/engineering/content/design/components/loading/loading.example.tsx (1 hunks)
  • internal/ui/src/components/animated-loading-spinner.tsx (1 hunks)
  • internal/ui/src/components/button.tsx (1 hunks)
  • internal/ui/src/components/loading.tsx (1 hunks)
  • internal/ui/src/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/components/override-indicator.tsx
  • apps/dashboard/app/(app)/apis/[apiId]/_components/rbac-dialog-content.tsx
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx
  • internal/ui/src/components/button.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/ui/src/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
apps/engineering/content/design/components/loading/loading.example.tsx (2)
apps/engineering/app/components/render.tsx (1)
  • RenderComponentWithSnippet (12-67)
internal/ui/src/components/loading.tsx (1)
  • Loading (12-16)
internal/ui/src/components/loading.tsx (1)
internal/ui/src/components/animated-loading-spinner.tsx (1)
  • AnimatedLoadingSpinner (11-74)
internal/ui/src/components/animated-loading-spinner.tsx (2)
apps/dashboard/lib/utils.ts (1)
  • cn (5-7)
apps/dashboard/components/navigation/sidebar/app-sidebar/components/nav-items/utils.ts (1)
  • getPathForSegment (13-25)
⏰ Context from checks skipped due to timeout of 90000ms (22)
  • GitHub Check: Test Go API Local / Test (Shard 6/8)
  • GitHub Check: Test Go API Local / Test (Shard 8/8)
  • GitHub Check: Test Go API Local / Test (Shard 5/8)
  • GitHub Check: Test Go API Local / Test (Shard 7/8)
  • GitHub Check: Test Go API Local / Test (Shard 2/8)
  • GitHub Check: Test Go API Local / Test (Shard 4/8)
  • GitHub Check: Test Go API Local / Test (Shard 3/8)
  • GitHub Check: Test Go API Local / Test (Shard 1/8)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./packages/rbac
  • GitHub Check: Test Packages / Test ./internal/encryption
  • GitHub Check: Test Packages / Test ./internal/keys
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Packages / Test ./internal/id
  • GitHub Check: Test Packages / Test ./internal/billing
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
apps/engineering/content/design/components/loading/loading.example.tsx (2)

5-13: LGTM! Clean default example.

The example demonstrates the basic usage of the Loading component with proper centering and spacing.


15-25: LGTM! Good customization example.

The example effectively demonstrates customizing the Loading component with different size and animation duration. The cyan color styling on the parent div will correctly inherit to the spinner via currentColor.

internal/ui/src/components/loading.tsx (2)

6-10: Well-designed interface with proper TypeScript typing.

The LoadingProps interface properly extends SVGProps and adds the specific props needed for the loading spinner customization.


12-16: Clean wrapper implementation with appropriate defaults.

The component effectively wraps AnimatedLoadingSpinner with sensible defaults (size=24, dur=125) and proper prop forwarding.

internal/ui/src/components/animated-loading-spinner.tsx (3)

30-37: Solid animation implementation with proper cleanup.

The useEffect correctly manages the interval timer with proper cleanup and dependency array. The modulo operation ensures smooth cycling through segments.


49-66: Excellent opacity calculation for smooth visual effect.

The opacity calculation creates a nice fading trail effect by using distance from the current segment index. The smooth transition and currentColor usage are well implemented.


3-3:

✅ Verification successful

Verify the import path for the cn utility.

The import path ../lib/utils may not be correct for the UI package structure. Please ensure this path resolves correctly.


🏁 Script executed:

#!/bin/bash
# Check if the utils file exists at the specified path relative to this component
fd -t f utils.ts internal/ui/src/lib/
fd -t f utils.js internal/ui/src/lib/

Length of output: 108


Import path is correct, no changes needed

The import in internal/ui/src/components/animated-loading-spinner.tsx:

import { cn } from "../lib/utils";

correctly resolves to internal/ui/src/lib/utils.ts. No action required.

apps/engineering/content/design/components/loading.mdx (1)

50-56: LGTM! Good accessibility documentation.

The accessibility section covers important considerations including ARIA attributes, visual feedback, and contrast ratios.

MichaelUnkey and others added 4 commits June 3, 2025 08:24
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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: 0

🧹 Nitpick comments (2)
apps/engineering/content/design/components/loading.mdx (2)

15-16: Remove unused import
The usage snippet imports Button but never uses it, which may trigger lint warnings and confuse readers. Consider removing it:

-import { Loading, Button } from "@unkey/ui";
+import { Loading } from "@unkey/ui";

43-48: Clarify optional props in table
It’s clearer to mark optional props with ? and use a more conventional default for className (e.g., an empty string):

-| `className` | `string` | `undefined` | Additional CSS classes to apply |
+| `className?` | `string` | `''`        | Additional CSS classes to apply |
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d59652a and 53d2690.

📒 Files selected for processing (1)
  • apps/engineering/content/design/components/loading.mdx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Test Go API Local / Test (Shard 5/8)
  • GitHub Check: Test Go API Local / Test (Shard 3/8)
  • GitHub Check: Test Go API Local / Test (Shard 1/8)
  • GitHub Check: Test Go API Local / Test (Shard 2/8)
  • GitHub Check: Test Go API Local / Test (Shard 4/8)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
🔇 Additional comments (2)
apps/engineering/content/design/components/loading.mdx (2)

9-10: Accurate component description
The updated description correctly reflects that the loader is an 8-segment spinner instead of three circles.


5-6:

✅ Verification successful

Verify example imports
Confirm that the file apps/engineering/content/design/components/loading/loading.example exports both LoadingExample and CustomSizeAndDuration.


🏁 Script executed:

#!/usr/bin/env bash
# Verify that the example file exports the required components
rg -Hn "export.*LoadingExample|export.*CustomSizeAndDuration" apps/engineering/content/design/components/loading/loading.example*

Length of output: 358


✔️ Example imports are valid
The file apps/engineering/content/design/components/loading/loading.example.tsx exports both LoadingExample and CustomSizeAndDuration, so the import in apps/engineering/content/design/components/loading.mdx is correct.

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.

1 participant