Skip to content

Refactor: Replace ArchivedBadge with Flexible StatusBadge Component#2444

Merged
kasya merged 8 commits intoOWASP:mainfrom
mrkeshav-05:feature/status-badge
Oct 19, 2025
Merged

Refactor: Replace ArchivedBadge with Flexible StatusBadge Component#2444
kasya merged 8 commits intoOWASP:mainfrom
mrkeshav-05:feature/status-badge

Conversation

@mrkeshav-05
Copy link
Contributor

@mrkeshav-05 mrkeshav-05 commented Oct 18, 2025

Proposed change

This PR refactors the ArchivedBadge component into a more flexible and reusable StatusBadge component that supports multiple status types. This eliminates code duplication and provides a consistent, maintainable solution for displaying status indicators across the application.

Fixed #2418

Status Configurations:

  • Archived (Yellow): bg-yellow-50 with archive icon
  • Inactive (Red): bg-red-50 with ban icon
Dark Mode Light Mode
image image
image image
image image

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 18, 2025

Summary by CodeRabbit

  • Refactor

    • Consolidated status badge functionality: replaced separate ArchivedBadge with a unified StatusBadge component handling both archived and inactive statuses with consistent styling and accessibility features.
  • Tests

    • Updated and expanded test coverage for badge components to ensure proper rendering and behavior.

Walkthrough

Replaces ArchivedBadge with a configurable StatusBadge, updates CardDetailsPage and RepositoriesCard to use StatusBadge, removes ArchivedBadge and its tests, and adds comprehensive StatusBadge tests plus a small test selector update in RepositoriesCard tests.

Changes

Cohort / File(s) Summary
Removed component
frontend/src/components/ArchivedBadge.tsx
Deleted the ArchivedBadge implementation and its default export.
New component
frontend/src/components/StatusBadge.tsx
Adds StatusBadge with StatusType and StatusBadgeProps; supports status configs (archived/inactive), custom text/icon/tooltip, size variants, light/dark styling, and accessibility attributes; exports default component and types.
Updated usages
frontend/src/components/CardDetailsPage.tsx, frontend/src/components/RepositoriesCard.tsx
Replaced imports/usages of ArchivedBadge with StatusBadge; JSX adjusted to use new component only.
Tests removed
frontend/__tests__/unit/components/ArchivedBadge.test.tsx
Deleted unit tests for the legacy ArchivedBadge.
Tests added/updated
frontend/__tests__/unit/components/StatusBadge.test.tsx, frontend/__tests__/unit/components/RepositoriesCard.test.tsx
Added extensive StatusBadge tests (rendering, sizes, icons, styling, accessibility, edge cases); updated RepositoriesCard test selector for status icon presence/absence.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Refactor: Replace ArchivedBadge with Flexible StatusBadge Component" is concise, clear, and accurately summarizes the primary objective of the changeset. It directly describes the main change—removing the ArchivedBadge component and replacing it with a new, more flexible StatusBadge component. The title is specific enough that teammates scanning the history would immediately understand the refactoring effort involved.
Linked Issues Check ✅ Passed The changes comprehensively address all primary coding objectives from issue #2418. The ArchivedBadge component has been renamed to StatusBadge [#2418], the component now supports dynamic statuses via props including text, styles, and icons [#2418], existing usages in RepositoriesCard and CardDetailsPage have been replaced with StatusBadge [#2418], related unit tests in RepositoriesCard.test.tsx have been updated [#2418], and comprehensive test coverage for StatusBadge has been added [#2418]. The implementation supports both archived and inactive status types as specified, with appropriate styling and accessibility features for current and future extensibility.
Out of Scope Changes Check ✅ Passed All changes in the pull request are directly aligned with the objectives outlined in issue #2418. The removal of ArchivedBadge, creation of StatusBadge, updates to component usage in RepositoriesCard and CardDetailsPage, and updates to related tests are all core to the refactoring effort. No extraneous changes unrelated to the badge component refactoring or the migration of its usages have been introduced.
Description Check ✅ Passed The pull request description clearly explains the proposed change, detailing how ArchivedBadge is being refactored into a more flexible StatusBadge component to eliminate code duplication and provide consistent status indicators. It references the fixed issue (#2418), provides specific status configurations with visual examples for both dark and light modes, and demonstrates that local checks passed. The description is directly related to the changeset and provides meaningful context about the refactoring effort.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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

@github-actions
Copy link

Test: This PR must be linked to an issue.

@mrkeshav-05 mrkeshav-05 changed the title Feature/status badge Refactor: Replace ArchivedBadge with Flexible StatusBadge Component Oct 18, 2025
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 (3)
frontend/src/components/StatusBadge.tsx (3)

21-29: Export StatusBadgeProps interface for external use.

The interface should be exported to allow consumers to type-check props when wrapping or using this component in TypeScript.

Apply this diff:

-interface StatusBadgeProps {
+export interface StatusBadgeProps {
   status: StatusType
   className?: string
   showIcon?: boolean
   size?: 'sm' | 'md' | 'lg'
   customText?: string
   customIcon?: IconDefinition
   customTooltip?: string
 }

80-80: Consider refactoring className concatenation for better readability.

The long template literal makes the className assembly hard to read and maintain. Consider using an array with .join(' ') or a utility like clsx for cleaner code.

Example refactor:

+  const classNames = [
+    sizeClasses[size],
+    className,
+    'inline-flex items-center gap-1.5 rounded-full border',
+    config.borderColor,
+    config.bgColor,
+    config.textColor,
+    config.darkBorderColor,
+    config.darkBgColor,
+    config.darkTextColor,
+    'font-medium',
+  ].join(' ')
+
   return (
     <span
-      className={`${sizeClasses[size]} ${className} inline-flex items-center gap-1.5 rounded-full border ${config.borderColor} ${config.bgColor} ${config.textColor} ${config.darkBorderColor} ${config.darkBgColor} ${config.darkTextColor} font-medium`}
+      className={classNames}
       title={displayTooltip}
       aria-label={config.ariaLabel}
     >

70-82: Consider making aria-label consistent with custom tooltip.

When customTooltip is provided, the visible tooltip changes but aria-label remains unchanged. This creates an inconsistency between what sighted users and screen reader users experience.

Consider one of these approaches:

  1. Derive aria-label from the tooltip:
   const displayTooltip = customTooltip ?? config.tooltip
+  const displayAriaLabel = customTooltip ?? config.ariaLabel

   return (
     <span
       className={`...`}
       title={displayTooltip}
-      aria-label={config.ariaLabel}
+      aria-label={displayAriaLabel}
     >
  1. Add a customAriaLabel prop for explicit control:
   customText?: string
   customIcon?: IconDefinition
   customTooltip?: string
+  customAriaLabel?: string
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b842459 and 4ba6a47.

📒 Files selected for processing (7)
  • frontend/__tests__/unit/components/ArchivedBadge.test.tsx (0 hunks)
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/StatusBadge.test.tsx (1 hunks)
  • frontend/src/components/ArchivedBadge.tsx (0 hunks)
  • frontend/src/components/CardDetailsPage.tsx (2 hunks)
  • frontend/src/components/RepositoriesCard.tsx (2 hunks)
  • frontend/src/components/StatusBadge.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • frontend/tests/unit/components/ArchivedBadge.test.tsx
  • frontend/src/components/ArchivedBadge.tsx
🔇 Additional comments (4)
frontend/src/components/StatusBadge.tsx (1)

58-88: LGTM! Well-structured reusable component.

The component provides a clean, type-safe API with good defaults and extensibility through custom props. The separation of configuration from rendering logic is excellent, and the dark mode support is properly implemented.

frontend/src/components/RepositoriesCard.tsx (1)

8-8: LGTM! Clean migration to StatusBadge.

The import and usage have been correctly updated to use the new StatusBadge component with appropriate props (status="archived", size="sm", showIcon={false}), maintaining the same visual appearance as before.

Also applies to: 51-51

frontend/src/components/CardDetailsPage.tsx (1)

35-35: LGTM! Consistent migration to StatusBadge.

Both inactive and archived statuses now use the new StatusBadge component with appropriate props. The size="md" is suitable for the detail page context.

Also applies to: 101-102

frontend/__tests__/unit/components/StatusBadge.test.tsx (1)

1-332: Excellent test coverage!

The test suite is comprehensive and well-organized, covering rendering, status types, size variants, icon display, custom overrides, styling, accessibility, edge cases, and integration scenarios. The tests are well-named and focused.

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 (4)
frontend/src/components/StatusBadge.tsx (4)

21-29: Consider exporting StatusBadgeProps for better reusability.

The interface is not currently exported, which prevents other components from referencing these prop types (e.g., for wrapper components or HOCs). If external code needs to work with StatusBadge props, consider adding the export keyword.

-interface StatusBadgeProps {
+export interface StatusBadgeProps {

72-76: Move sizeClasses outside the component for better performance.

The sizeClasses object is static but currently recreated on every render. Moving it outside the component body will improve performance.

+const sizeClasses = {
+  sm: 'px-2 py-1 text-xs',
+  md: 'text-sm px-3 py-1',
+  lg: 'px-4 py-2 text-base',
+}
+
 const StatusBadge: React.FC<StatusBadgeProps> = ({
   status,
   className = '',
   showIcon = true,
   size = 'md',
   customText,
   customIcon,
   customTooltip,
 }) => {
   const config = statusConfig[status]
   const displayText = customText ?? config.text
   const displayIcon = customIcon ?? config.icon
   const displayTooltip = customTooltip ?? config.tooltip
-
-  const sizeClasses = {
-    sm: 'px-2 py-1 text-xs',
-    md: 'text-sm px-3 py-1',
-    lg: 'px-4 py-2 text-base',
-  }

82-82: Consider allowing customization of aria-label for consistency.

The component allows customTooltip (line 81) but always uses config.ariaLabel (line 82). If a consumer provides a custom tooltip, the aria-label and tooltip will be inconsistent. Consider adding a customAriaLabel prop or deriving aria-label from displayTooltip when customized.

Example approach:

 interface StatusBadgeProps {
   status: StatusType
   className?: string
   showIcon?: boolean
   size?: 'sm' | 'md' | 'lg'
   customText?: string
   customIcon?: IconDefinition
   customTooltip?: string
+  customAriaLabel?: string
 }

Then use:

const displayAriaLabel = customAriaLabel ?? config.ariaLabel

85-85: Consider scaling icon size with badge size.

The icon size is hardcoded as h-3 w-3 regardless of the badge size variant. For better visual consistency, consider scaling the icon based on the size prop (e.g., sm: h-2.5 w-2.5, md: h-3 w-3, lg: h-4 w-4).

+const iconSizeClasses = {
+  sm: 'h-2.5 w-2.5',
+  md: 'h-3 w-3',
+  lg: 'h-4 w-4',
+}
+
 const StatusBadge: React.FC<StatusBadgeProps> = ({
   ...
 }) => {
   ...
   return (
     <span ...>
       {showIcon && (
-        <FontAwesomeIcon icon={displayIcon} className="h-3 w-3" data-testid="status-icon" />
+        <FontAwesomeIcon icon={displayIcon} className={iconSizeClasses[size]} data-testid="status-icon" />
       )}
       {displayText}
     </span>
   )
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ba6a47 and 4b08414.

📒 Files selected for processing (2)
  • frontend/__tests__/unit/components/StatusBadge.test.tsx (1 hunks)
  • frontend/src/components/StatusBadge.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/tests/unit/components/StatusBadge.test.tsx
🔇 Additional comments (3)
frontend/src/components/StatusBadge.tsx (3)

1-4: LGTM!

Imports are well-structured and follow best practices by importing individual icons for optimal bundle size.


31-56: LGTM!

The statusConfig object is well-structured with comprehensive theming for both light and dark modes, proper accessibility labels, and type-safe exhaustiveness via Record<StatusType, StatusConfig>.


92-92: LGTM!

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@mrkeshav-05 This looks great! Thanks for working on it!

Left a couple of tiny requests, but overall - good work! 👍🏼

@sonarqubecloud
Copy link

@mrkeshav-05 mrkeshav-05 requested a review from kasya October 19, 2025 03:22
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)
frontend/src/components/RepositoriesCard.tsx (2)

25-27: Avoid array index as React key.

Use a stable identifier to prevent stale child identity when toggling “Show more”.

-          return <RepositoryItem key={index} details={repository} />
+          return <RepositoryItem key={repository.key} details={repository} />

43-48: Prefer semantic links over buttons for navigation.

Use Next.js Link for accessibility, semantics, and better prefetch.

Add import:

+import Link from 'next/link'

Update markup:

-        <button
-          onClick={handleClick}
-          className="min-w-0 flex-1 text-start font-semibold text-blue-400 hover:underline"
-        >
-          <TruncatedText text={details?.name} />
-        </button>
+        <Link
+          href={`/organizations/${details.organization.login}/repositories/${details.key}`}
+          className="min-w-0 flex-1 text-start font-semibold text-blue-400 hover:underline"
+        >
+          <TruncatedText text={details?.name} />
+        </Link>
frontend/__tests__/unit/components/StatusBadge.test.tsx (2)

274-281: Reduce brittleness in “renders consistently across multiple instances”.

Comparing full className strings is order-sensitive. Prefer asserting required classes via toHaveClass.

-      const firstRender = screen.getByText('Archived').className
-
-      rerender(<StatusBadge status="archived" />)
-      const secondRender = screen.getByText('Archived').className
-      expect(firstRender).toBe(secondRender)
+      const base = ['inline-flex', 'items-center', 'rounded-full', 'border', 'font-medium']
+      const first = screen.getByText('Archived')
+      base.forEach(c => expect(first).toHaveClass(c))
+      rerender(<StatusBadge status="archived" />)
+      const second = screen.getByText('Archived')
+      base.forEach(c => expect(second).toHaveClass(c))

309-320: Avoid counting raw elements.

Span counts can change with harmless markup tweaks. Assert on the rendered statuses instead.

-      expect(container.querySelectorAll('span').length).toBe(2)
+      expect(screen.getByText('Archived')).toBeInTheDocument()
+      expect(screen.getByText('Inactive')).toBeInTheDocument()
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b08414 and 5e35953.

📒 Files selected for processing (7)
  • frontend/__tests__/unit/components/ArchivedBadge.test.tsx (0 hunks)
  • frontend/__tests__/unit/components/RepositoriesCard.test.tsx (1 hunks)
  • frontend/__tests__/unit/components/StatusBadge.test.tsx (1 hunks)
  • frontend/src/components/ArchivedBadge.tsx (0 hunks)
  • frontend/src/components/CardDetailsPage.tsx (2 hunks)
  • frontend/src/components/RepositoriesCard.tsx (2 hunks)
  • frontend/src/components/StatusBadge.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • frontend/src/components/ArchivedBadge.tsx
  • frontend/tests/unit/components/ArchivedBadge.test.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/tests/unit/components/RepositoriesCard.test.tsx
  • frontend/src/components/CardDetailsPage.tsx
  • frontend/src/components/StatusBadge.tsx
🔇 Additional comments (3)
frontend/src/components/RepositoriesCard.tsx (2)

8-8: Good migration to StatusBadge.

Import and usage align with the new reusable component; no issues.


51-51: Confirm icon visibility choice for archived badge.

You’re hiding the icon (showIcon={false}). Verify this matches the new UX across pages (CardDetailsPage, etc.) for consistency.

frontend/__tests__/unit/components/StatusBadge.test.tsx (1)

1-3: No action required — jest-dom is properly configured globally.

Verification confirms jest.setup.ts imports jest-dom at the entry point, and jest.config.ts correctly references it via setupFilesAfterEnv: ['<rootDir>/jest.setup.ts']. All jest-dom matchers (toBeInTheDocument, toHaveClass, toHaveAttribute, toBeVisible, etc.) used in StatusBadge.test.tsx will work correctly through the global setup.

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

Great work! Thanks for working on this! 👍🏼

One suggestion for any future PRs - could you please not force-push updates when changes were requested? This makes it not possible for me as a reviewer to see any changes from last review. I can only see the whole thing, but not incremental changes.
Regular push works just fine in most cases 👌🏼

@kasya kasya added this pull request to the merge queue Oct 19, 2025
Merged via the queue into OWASP:main with commit a1ffbaf Oct 19, 2025
26 checks passed
@mrkeshav-05 mrkeshav-05 deleted the feature/status-badge branch October 27, 2025 16:12
@coderabbitai coderabbitai bot mentioned this pull request Dec 11, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update ArchivedBadge to a dynamic status badge component

2 participants