Skip to content

Comments

refactor: extract PanelCard from ChartCard for reusability#23360

Merged
eunjae-lee merged 8 commits intomainfrom
devin/1756215476-panel-card-refactor
Aug 27, 2025
Merged

refactor: extract PanelCard from ChartCard for reusability#23360
eunjae-lee merged 8 commits intomainfrom
devin/1756215476-panel-card-refactor

Conversation

@eunjae-lee
Copy link
Contributor

@eunjae-lee eunjae-lee commented Aug 26, 2025

What does this PR do?

This PR extracts a general-purpose PanelCard component from the existing ChartCard component to enable reusability across the application. The refactoring maintains backward compatibility while creating a flexible base component that can be used in contexts beyond charts.

Motivation: The ChartCard component contains useful general-purpose card functionality (title, subtitle, CTA button, collapsible potential) that could benefit other parts of the application.

Visual Demo (For contributors especially)

Screenshot 2025-08-26 at 16 30 26

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A - I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

Visit /insights and all the charts look the same as before.

devin-ai-integration bot and others added 2 commits August 26, 2025 13:38
- Create base PanelCard and PanelCardItem components in UI package
- Refactor ChartCard to use PanelCard as foundation while keeping legend functionality
- Add headerActions prop to PanelCard for extensibility
- Maintain backward compatibility for existing ChartCard usage
- Export PanelCard and PanelCardItem from UI card components

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Walkthrough

  • Adds a new client-side component PanelCard at packages/ui/components/card/PanelCard.tsx (props: title, subtitle, cta, headerContent, children) and exports it from packages/ui/components/card/index.ts.
  • Refactors packages/features/insights/components/ChartCard.tsx to use PanelCard (passes title, subtitle, cta, and legend as headerContent), removes the inline header markup and direct Button usage, and updates ChartCard prop types to use ReactNode for title and children.

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c004169 and c1f3205.

📒 Files selected for processing (1)
  • packages/ui/components/card/PanelCard.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/components/card/PanelCard.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1756215476-panel-card-refactor

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@keithwillcode keithwillcode added consumer core area: core, team members only labels Aug 26, 2025
- Rename 'actions' prop to 'headerContent' in PanelCard component
- Update ChartCard to use new 'headerContent' prop name
- More accurate naming since content can be text or other elements, not just buttons

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@vercel
Copy link

vercel bot commented Aug 26, 2025

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

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Aug 27, 2025 8:42am
cal-eu Ignored Ignored Aug 27, 2025 8:42am

@eunjae-lee eunjae-lee changed the title feat: extract PanelCard from ChartCard for reusability refactor: extract PanelCard from ChartCard for reusability Aug 26, 2025
@eunjae-lee eunjae-lee marked this pull request as ready for review August 26, 2025 14:31
@eunjae-lee eunjae-lee requested review from a team as code owners August 26, 2025 14:31
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 (6)
packages/ui/components/card/PanelCard.tsx (4)

12-16: Export a shared CTA type to eliminate duplication across consumers

PanelCard defines the CTA shape inline. Exporting a named CTA type improves reuse (e.g., ChartCard) and prevents drift.

Apply:

 export function PanelCard({
@@
-}: {
-  title: string | ReactNode;
-  subtitle?: string;
-  cta?: { label: string; onClick: () => void };
-  headerContent?: ReactNode;
-  children: ReactNode;
-}) {
+}: PanelCardProps) {
   return (
@@
   );
 }
+
+export type PanelCardCTA = { label: string; onClick: () => void };
+export type PanelCardProps = {
+  title: string | ReactNode;
+  subtitle?: string;
+  cta?: PanelCardCTA;
+  headerContent?: ReactNode;
+  children: ReactNode;
+};

18-43: Expose styling hooks for broader reuse

Since PanelCard is meant to be general-purpose, consider className, headerClassName, and contentClassName props to allow consumers to tweak spacing/borders without overriding via deep selectors.

Apply:

-export function PanelCard({
-  title,
-  subtitle,
-  cta,
-  headerContent,
-  children,
-}: PanelCardProps) {
+export function PanelCard({
+  title,
+  subtitle,
+  cta,
+  headerContent,
+  children,
+  className,
+  headerClassName,
+  contentClassName,
+}: PanelCardProps & {
+  className?: string;
+  headerClassName?: string;
+  contentClassName?: string;
+}) {
   return (
-    <div className="bg-muted group relative flex w-full flex-col items-center rounded-2xl px-1 pb-1">
-      <div className="flex h-11 w-full shrink-0 items-center justify-between gap-2 px-4">
+    <div className={classNames("bg-muted group relative flex w-full flex-col items-center rounded-2xl px-1 pb-1", className)}>
+      <div className={classNames("flex h-11 w-full shrink-0 items-center justify-between gap-2 px-4", headerClassName)}>
@@
-      <div className="bg-default border-muted w-full grow gap-3 rounded-xl border">
+      <div className={classNames("bg-default border-muted w-full grow gap-3 rounded-xl border", contentClassName)}>

Note: Add import classNames from "@calcom/ui/classNames"; at the top.


26-33: CTA flexibility: allow disabled/aria/tooltip or a custom node

To keep PanelCard broadly reusable, consider either (a) extending CTA with optional disabled, ariaLabel, and icon, or (b) supporting a ctaNode?: ReactNode for full control (links, menus, popovers).

Example (non-breaking):

-export type PanelCardCTA = { label: string; onClick: () => void };
+export type PanelCardCTA = {
+  label: string;
+  onClick: () => void;
+  disabled?: boolean;
+  ariaLabel?: string;
+};
@@
-          {cta && (
-            <Button className="shrink-0" color="secondary" onClick={cta.onClick}>
+          {cta && (
+            <Button
+              className="shrink-0"
+              color="secondary"
+              onClick={cta.onClick}
+              aria-label={cta.ariaLabel ?? cta.label}
+              disabled={cta.disabled}
+            >
               {cta.label}
             </Button>
           )}

21-25: Heading semantics when title is a custom node

When title is a ReactNode, heading semantics can be lost. Consider an optional headingLevel prop (e.g., 2|3|4) and render the children inside that tag for consistent a11y.

packages/features/insights/components/ChartCard.tsx (2)

31-31: Minor: compute legend inline or memoize if legend is stable

Current approach is fine. If Legend is heavy or legend arrays are stable, consider useMemo to avoid re-renders on unrelated state changes. Otherwise, inline headerContent={legend?.length ? <Legend ... /> : null} is sufficient.


34-36: Prop type reuse from PanelCard for cta

To avoid duplicating CTA shape, consider importing a shared type from PanelCard (e.g., PanelCardCTA) and using cta?: PanelCardCTA here. Keeps API drift-free.

Example:

-import { PanelCard } from "@calcom/ui/components/card";
+import { PanelCard, type PanelCardCTA } from "@calcom/ui/components/card";
@@
-  cta?: { label: string; onClick: () => void };
+  cta?: PanelCardCTA;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6e09489 and c004169.

📒 Files selected for processing (3)
  • packages/features/insights/components/ChartCard.tsx (2 hunks)
  • packages/ui/components/card/PanelCard.tsx (1 hunks)
  • packages/ui/components/card/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/ui/components/card/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/ui/components/card/index.ts
  • packages/ui/components/card/PanelCard.tsx
  • packages/features/insights/components/ChartCard.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/ui/components/card/index.ts
  • packages/ui/components/card/PanelCard.tsx
  • packages/features/insights/components/ChartCard.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/ui/components/card/PanelCard.tsx
  • packages/features/insights/components/ChartCard.tsx
🧬 Code graph analysis (2)
packages/ui/components/card/PanelCard.tsx (1)
packages/ui/components/card/index.ts (1)
  • PanelCard (5-5)
packages/features/insights/components/ChartCard.tsx (3)
packages/features/insights/components/booking/EventTrendsChart.tsx (1)
  • legend (23-30)
packages/features/insights/components/routing/RoutingFunnelContent.tsx (1)
  • legend (25-29)
packages/ui/components/card/PanelCard.tsx (1)
  • PanelCard (5-45)
🔇 Additional comments (6)
packages/ui/components/card/index.ts (1)

5-5: Good addition: named export aligns with our guidelines

Re-exporting PanelCard as a named export via the barrel is correct and consistent with our preference for named exports. No further action needed.

packages/ui/components/card/PanelCard.tsx (1)

36-40: i18n: Verify PanelCard callers localize subtitle and cta.label
PanelCard should remain presentation-only. Confirm every consumer of <PanelCard> passes already-localized strings (e.g. t("…")) for both props:

• For each <PanelCard subtitle={…} />, ensure the value is a translation call (such as t("…")), not a raw string literal.
• For each <PanelCard cta={{ label: … }} />, ensure label is supplied via t("…"), not a hard-coded string.

To audit all usages, you can run something like:

rg -n "PanelCard" -g '*.tsx'

and inspect the subtitle= and cta={{ label: }} props.
If you locate any raw literals, update them to use your i18n function.

packages/features/insights/components/ChartCard.tsx (4)

6-6: Good pivot to reusable layout via PanelCard

Replacing bespoke header markup with PanelCard improves consistency and reuse. Import via the barrel keeps the public API clean.


24-24: Type cleanup: ReactNode alias is preferred

Switching to ReactNode (imported as a type) avoids namespace references and matches our TS style.


29-29: Children’s type narrowed to ReactNode

This aligns with React’s standard typings and is appropriate here.


34-36: No brittle selectors found for ChartCard header

I’ve searched the codebase for any references to the old ChartCard header structure (e.g. direct ChartCard usages in tests or CSS/SCSS selectors targeting the previous h2, flex h-11, or overflow-x-auto classes) and found none. There are no remaining selector or test references that would break due to this header DOM change. Feel free to merge.

Copy link
Contributor

@Udit-takkar Udit-takkar left a comment

Choose a reason for hiding this comment

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

LGTM

@github-actions
Copy link
Contributor

E2E results are ready!

@eunjae-lee eunjae-lee enabled auto-merge (squash) August 27, 2025 09:12
@eunjae-lee eunjae-lee merged commit cdcc04c into main Aug 27, 2025
60 of 62 checks passed
@eunjae-lee eunjae-lee deleted the devin/1756215476-panel-card-refactor branch August 27, 2025 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants