Skip to content

Conversation

@tractorss
Copy link
Contributor

@tractorss tractorss commented Dec 11, 2025

PR-Codex overview

This PR focuses on refactoring the Accordion component to improve its props and structure, as well as introducing a new CustomAccordion component that allows for more customization in rendering accordion items.

Detailed summary

  • Renamed Accordion to AccordionComponent in accordion.stories.tsx.
  • Updated prop types for AccordionItem and AccordionProps to include expandButton.
  • Added documentation comments for props in AccordionProps and CustomAccordionProps.
  • Introduced CustomAccordion with customizable expandButton functionality.
  • Improved state management and default values in Accordion and CustomAccordion components.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Customizable expand/collapse button per item or globally; controlled expanded prop and aria-expanded for accessibility; improved spacing and unified transition timing; responsive container width.
  • Refactor

    • Accordion and item types/props exported and public API signatures updated for clearer item shape and prop usage.
  • Tests

    • Storybook stories added/demonstrating per-item and global custom expand buttons and usage scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Dec 11, 2025

Deploy Preview for kleros-v2-ui-storybook ready!

Name Link
🔨 Latest commit 76cb796
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-ui-storybook/deploys/693ab7356f164500086c0e98
😎 Deploy Preview https://deploy-preview-85--kleros-v2-ui-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Exports accordion item types, adds a customizable per-item or global expandButton renderer and controlled expanded prop, moves component signatures from React.FC to function declarations, updates header/body markup and transitions, and adds Storybook stories demonstrating per-item and global expand-button usage.

Changes

Cohort / File(s) Summary
Accordion Item Enhancement
src/lib/accordion/accordion-item.tsx
Exports AccordionItemProps; adds public title, body, expanded, and expandButton props; memoizes ExpandButton via useMemo and uses it for header control; adds aria-expanded, gap spacing in header, and consolidates body reveal into an overflow-hidden container with unified ease-in-out transition.
CustomAccordion API Updates
src/lib/accordion/custom.tsx
Introduces exported CustomAccordionProps; types items as `Pick<AccordionItemProps, "title"
Accordion Core Updates
src/lib/accordion/index.tsx
Exports AccordionItemProps and AccordionProps; updates items typing to AccordionItemProps[]; changes DefaultTitle to accept AccordionItemProps; switches component signature to a function with Readonly<AccordionProps> and updates root container styling to w-full max-w-[1000px].
Story Updates
src/stories/accordion.stories.tsx
Updates imports and metadata to reference AccordionComponent instead of Accordion; renames exported story constant to Accordion.
New CustomAccordion Stories
src/stories/custom-accordion.stories.tsx
Adds new story file with three stories: Accordion (per-item custom expand buttons), GlobalExpandButton (parent-level expandButton), and ItemExpandButton (per-item overrides with parent fallback); provides examples and args demonstrating the new API.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify AccordionItemProps export, expandButton type signature, and expanded controlled behavior.
  • Check useMemo usage for ExpandButton and that memoization dependencies are correct.
  • Confirm per-item vs global expandButton fallback (item.expandButton ?? expandButton) and propagation through CustomAccordion.
  • Review Storybook stories for accurate demonstration and typings.

Possibly related PRs

  • Chore/bug fixes #78 — related changes to accordion expansion handling and defaultExpanded/default state initialization.
  • Fix/bug fixes and updates #80 — prior modifications to accordion-item.tsx (IDs, padding, classes) that may touch the same component surface.

Suggested reviewers

  • alcercu

Poem

🐰 I tweaked a button, gave it a hop and a wink,
Now expanders can sparkle, or use what you think.
Per-item or global, the toggle’s your cue,
I nibble on props and I ship them to you! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main feature: adding custom expand button functionality to the accordion component.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/expand-button-custom-accordion

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/lib/accordion/index.tsx (1)

46-48: Simplify the negated condition.

Per SonarCloud's hint, the negated condition can be inverted for better readability.

  const [expanded, setExpanded] = useState(
-    !isUndefined(defaultExpanded) ? defaultExpanded : -1,
+    isUndefined(defaultExpanded) ? -1 : defaultExpanded,
  );
src/lib/accordion/custom.tsx (1)

58-60: Simplify the negated condition.

Same as in index.tsx, invert the condition for clarity.

  const [expanded, setExpanded] = useState(
-    !isUndefined(defaultExpanded) ? defaultExpanded : -1,
+    isUndefined(defaultExpanded) ? -1 : defaultExpanded,
  );
src/lib/accordion/accordion-item.tsx (1)

60-77: Consider extracting nested ternary for readability.

Per SonarCloud's suggestion, the nested ternary could be refactored into a clearer structure.

  const ExpandButton = useMemo(() => {
+    if (expandButton) {
+      return expandButton({
+        expanded,
+        toggle: () => setExpanded(expanded ? -1 : index),
+      });
+    }
+    const IconComponent = expanded ? Minus : Plus;
+    return (
+      <IconComponent
+        className={cn("fill-klerosUIComponentsPrimaryText size-4 shrink-0")}
+      />
+    );
-    expandButton ? (
-      expandButton({
-        expanded,
-        toggle: () => setExpanded(expanded ? -1 : index),
-      })
-    ) : expanded ? (
-      <Minus
-        className={cn("fill-klerosUIComponentsPrimaryText size-4 shrink-0")}
-      />
-    ) : (
-      <Plus
-        className={cn("fill-klerosUIComponentsPrimaryText size-4 shrink-0")}
-      />
-    ),
-  [expanded, expandButton, index, setExpanded],
+  }, [expanded, expandButton, index, setExpanded]);
  );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0363a9 and 1bd9a4c.

📒 Files selected for processing (5)
  • src/lib/accordion/accordion-item.tsx (1 hunks)
  • src/lib/accordion/custom.tsx (2 hunks)
  • src/lib/accordion/index.tsx (2 hunks)
  • src/stories/accordion.stories.tsx (1 hunks)
  • src/stories/custom-accordion.stories.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: tractorss
Repo: kleros/ui-components-library PR: 80
File: src/lib/accordion/accordion-item.tsx:28-28
Timestamp: 2025-06-06T12:21:11.794Z
Learning: In the AccordionItem component in src/lib/accordion/accordion-item.tsx, the static id="expand-button" on the Button element is intentional and should not be changed to dynamic IDs, even though it creates duplicate IDs across multiple accordion items.
📚 Learning: 2025-06-06T12:21:11.794Z
Learnt from: tractorss
Repo: kleros/ui-components-library PR: 80
File: src/lib/accordion/accordion-item.tsx:28-28
Timestamp: 2025-06-06T12:21:11.794Z
Learning: In the AccordionItem component in src/lib/accordion/accordion-item.tsx, the static id="expand-button" on the Button element is intentional and should not be changed to dynamic IDs, even though it creates duplicate IDs across multiple accordion items.

Applied to files:

  • src/lib/accordion/custom.tsx
  • src/lib/accordion/index.tsx
  • src/lib/accordion/accordion-item.tsx
  • src/stories/accordion.stories.tsx
  • src/stories/custom-accordion.stories.tsx
🧬 Code graph analysis (5)
src/lib/accordion/custom.tsx (3)
src/lib/accordion/accordion-item.tsx (1)
  • AccordionItemProps (9-49)
src/lib/accordion/index.tsx (1)
  • AccordionItemProps (5-10)
src/utils/index.ts (2)
  • isUndefined (8-11)
  • cn (4-6)
src/lib/accordion/index.tsx (2)
src/lib/accordion/accordion-item.tsx (1)
  • AccordionItemProps (9-49)
src/utils/index.ts (2)
  • isUndefined (8-11)
  • cn (4-6)
src/lib/accordion/accordion-item.tsx (3)
src/lib/accordion/index.tsx (1)
  • AccordionItemProps (5-10)
src/hooks/useElementSize.ts (1)
  • useElementSize (10-40)
src/utils/index.ts (1)
  • cn (4-6)
src/stories/accordion.stories.tsx (3)
.storybook/preview.tsx (1)
  • IPreviewArgs (8-11)
src/stories/utils.tsx (1)
  • IPreviewArgs (1-6)
src/stories/custom-accordion.stories.tsx (1)
  • Accordion (20-59)
src/stories/custom-accordion.stories.tsx (1)
src/stories/accordion.stories.tsx (1)
  • Accordion (18-42)
🪛 GitHub Check: SonarCloud Code Analysis
src/lib/accordion/custom.tsx

[warning] 59-59: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=kleros_ui-components-library&issues=AZsNP1qXaKGY6TxDWubZ&open=AZsNP1qXaKGY6TxDWubZ&pullRequest=85

src/lib/accordion/index.tsx

[warning] 47-47: Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=kleros_ui-components-library&issues=AZsNP1qDaKGY6TxDWubY&open=AZsNP1qDaKGY6TxDWubY&pullRequest=85

src/lib/accordion/accordion-item.tsx

[warning] 67-75: Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=kleros_ui-components-library&issues=AZsNP1nkaKGY6TxDWubX&open=AZsNP1nkaKGY6TxDWubX&pullRequest=85

🔇 Additional comments (4)
src/stories/accordion.stories.tsx (1)

6-18: LGTM!

The import alias to AccordionComponent and story renaming to Accordion improve clarity and align with the updated public API structure.

src/lib/accordion/custom.tsx (1)

51-82: LGTM on the expandButton fallback pattern.

The item.expandButton ?? expandButton fallback logic cleanly allows per-item overrides while supporting a global default.

src/lib/accordion/accordion-item.tsx (2)

80-93: Good accessibility improvement with aria-expanded.

Adding aria-expanded={expanded} properly communicates the accordion state to assistive technologies.


9-49: Well-documented props interface.

The JSDoc comments on AccordionItemProps thoroughly explain the expandButton render prop pattern with a clear example.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 11, 2025
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
16.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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

♻️ Duplicate comments (1)
src/stories/custom-accordion.stories.tsx (1)

102-102: Fix typo in story doc comment (“ovverrided” → “overridden”).

The JSDoc above ItemExpandButton still reads “ovverrided”; please correct for clarity:

-/** Parent Expand Button can be ovverrided at Item level if required */
+/** Parent expand button can be overridden at item level if required */
🧹 Nitpick comments (4)
src/lib/accordion/index.tsx (2)

5-10: Avoid having two different AccordionItemProps types in separate modules.

This file defines AccordionItemProps (string title, Icon/icon), while src/lib/accordion/accordion-item.tsx exports another AccordionItemProps (ReactNode title, body, expandButton, expanded, etc.). Even though they live in different modules, the identical name but different shape is easy to mis-import and misunderstand.

Consider either:

  • Renaming this one to something like AccordionItemConfig (and re-exporting the other as the canonical AccordionItemProps), or
  • Re-exporting the implementation-facing AccordionItemProps from accordion-item.tsx here and using a narrower, clearly named type for the public items config if needed.

12-25: Align AccordionProps with the fact that you spread ...props onto the root <div>.

Accordion currently has props { items, defaultExpanded, className } but also does <div {...props}>. This means callers can pass extra attributes (e.g. id, data-*, onClick) that are forwarded at runtime but are not modeled in AccordionProps.

To keep types aligned with behavior, consider:

export interface AccordionProps
  extends React.HTMLAttributes<HTMLDivElement> {
  items: AccordionItemProps[];
  defaultExpanded?: number;
  className?: string;
}

or, if pass-through is not desired, drop {...props} entirely.

Also applies to: 40-45, 50-56

src/lib/accordion/accordion-item.tsx (1)

60-71: You can simplify ExpandButton and drop useMemo for clarity.

useMemo is wrapping a cheap render helper that already depends on expanded and expandButton, so it offers little benefit and adds indirection. You could inline this logic and keep it easier to follow:

-  const ExpandButton = useMemo(() => {
-    if (expandButton) {
-      return expandButton({
-        expanded,
-        toggle: () => setExpanded(expanded ? -1 : index),
-      });
-    }
-    const IconComponent = expanded ? Minus : Plus;
-    return (
-      <IconComponent className="fill-klerosUIComponentsPrimaryText size-4 shrink-0" />
-    );
-  }, [expanded, expandButton, index, setExpanded]);
+  const toggle = () => setExpanded(expanded ? -1 : index);
+
+  const ExpandButton = expandButton
+    ? expandButton({ expanded, toggle })
+    : expanded ? (
+        <Minus className="fill-klerosUIComponentsPrimaryText size-4 shrink-0" />
+      ) : (
+        <Plus className="fill-klerosUIComponentsPrimaryText size-4 shrink-0" />
+      );

This keeps the same behavior with less hook complexity.

Also applies to: 86-87

src/lib/accordion/custom.tsx (1)

5-6: Model passthrough DOM props in CustomAccordionProps or drop {...props}.

Similar to Accordion, CustomAccordion spreads ...props onto the root <div>, but CustomAccordionProps only defines items, className, defaultExpanded, and expandButton. Any extra attributes passed (e.g. id, data-*) work at runtime but are invisible to the type system.

Consider:

export interface CustomAccordionProps
  extends React.HTMLAttributes<HTMLDivElement> {
  items: Pick<AccordionItemProps, "title" | "body" | "expandButton">[];
  className?: string;
  defaultExpanded?: number;
  expandButton?: AccordionItemProps["expandButton"];
}

or, if you don’t want arbitrary attributes forwarded, remove {...props} from the <div>.

Also applies to: 14-24, 51-57, 62-67

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1bd9a4c and 76cb796.

📒 Files selected for processing (4)
  • src/lib/accordion/accordion-item.tsx (1 hunks)
  • src/lib/accordion/custom.tsx (2 hunks)
  • src/lib/accordion/index.tsx (2 hunks)
  • src/stories/custom-accordion.stories.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: tractorss
Repo: kleros/ui-components-library PR: 80
File: src/lib/accordion/accordion-item.tsx:28-28
Timestamp: 2025-06-06T12:21:11.794Z
Learning: In the AccordionItem component in src/lib/accordion/accordion-item.tsx, the static id="expand-button" on the Button element is intentional and should not be changed to dynamic IDs, even though it creates duplicate IDs across multiple accordion items.
📚 Learning: 2025-06-06T12:21:11.794Z
Learnt from: tractorss
Repo: kleros/ui-components-library PR: 80
File: src/lib/accordion/accordion-item.tsx:28-28
Timestamp: 2025-06-06T12:21:11.794Z
Learning: In the AccordionItem component in src/lib/accordion/accordion-item.tsx, the static id="expand-button" on the Button element is intentional and should not be changed to dynamic IDs, even though it creates duplicate IDs across multiple accordion items.

Applied to files:

  • src/lib/accordion/accordion-item.tsx
  • src/lib/accordion/custom.tsx
  • src/stories/custom-accordion.stories.tsx
  • src/lib/accordion/index.tsx
🧬 Code graph analysis (4)
src/lib/accordion/accordion-item.tsx (3)
src/lib/accordion/index.tsx (1)
  • AccordionItemProps (5-10)
src/hooks/useElementSize.ts (1)
  • useElementSize (10-40)
src/utils/index.ts (1)
  • cn (4-6)
src/lib/accordion/custom.tsx (3)
src/lib/accordion/accordion-item.tsx (1)
  • AccordionItemProps (9-49)
src/lib/accordion/index.tsx (1)
  • AccordionItemProps (5-10)
src/utils/index.ts (2)
  • isUndefined (8-11)
  • cn (4-6)
src/stories/custom-accordion.stories.tsx (1)
src/stories/accordion.stories.tsx (1)
  • Accordion (18-42)
src/lib/accordion/index.tsx (2)
src/lib/accordion/accordion-item.tsx (1)
  • AccordionItemProps (9-49)
src/utils/index.ts (2)
  • isUndefined (8-11)
  • cn (4-6)
🔇 Additional comments (4)
src/lib/accordion/index.tsx (1)

12-25: Accordion API and defaultExpanded behavior look good.

The updated docs on items/defaultExpanded, the isUndefined(defaultExpanded) ? -1 : defaultExpanded initialization, and the responsive w-full max-w-[1000px] container together make the Accordion API clearer and more robust while preserving single-item expansion semantics.

Also applies to: 27-32, 46-48, 51-54

src/lib/accordion/accordion-item.tsx (1)

9-49: AccordionItemProps + expandButton API are well-structured.

The public AccordionItemProps with title, body, expanded, and the documented expandButton callback (with { expanded, toggle }) matches the custom/global button stories and keeps the header/body/transition logic coherent. The added aria-expanded and preserved static "expand-button" id also keep behavior predictable and accessible. Based on learnings, keeping the static id is correct.

Also applies to: 73-97

src/lib/accordion/custom.tsx (1)

5-43: CustomAccordionProps and expandButton override behavior look solid.

Using items: Pick<AccordionItemProps, "title" | "body" | "expandButton">[] plus a parent-level expandButton?: AccordionItemProps["expandButton"] neatly models both per-item and global buttons, and item.expandButton ?? expandButton does the right precedence. The defaultExpanded handling matches the base Accordion, keeping the mental model consistent.

Also applies to: 51-60, 69-80

src/stories/custom-accordion.stories.tsx (1)

19-59: Stories accurately exercise per-item and global expandButton behavior.

The three stories cover: per-item buttons, a shared global button, and per-item overrides on top of a global default. All use the correct ({ expanded, toggle }) callback signature and wire onPress={toggle} on the Button, which matches the AccordionItemProps["expandButton"] API.

Also applies to: 61-100, 102-148

@kemuru kemuru self-requested a review December 11, 2025 20:22
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.

3 participants