Skip to content

feat: Accordion improvements#1725

Merged
kotAPI merged 3 commits intomainfrom
kotapi/accordion-imptovements
Apr 3, 2026
Merged

feat: Accordion improvements#1725
kotAPI merged 3 commits intomainfrom
kotapi/accordion-imptovements

Conversation

@kotAPI
Copy link
Copy Markdown
Collaborator

@kotAPI kotAPI commented Apr 3, 2026

Summary by CodeRabbit

  • New Features

    • Added root-level disabled prop to disable all accordion items at once
    • Introduced data-orientation attributes throughout accordion for consistent styling hooks
  • Tests

    • Expanded accessibility test coverage for accordion functionality
  • Chores

    • Migrated ESLint configuration and updated linting toolchain
    • Updated lint scripts and dependencies

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 3, 2026

🦋 Changeset detected

Latest commit: 5a393d4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@radui/ui Patch

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

This PR introduces a root-level disabled prop to disable all accordion items and propagates orientation through the Accordion context to all fragments for consistent data attributes. It simultaneously migrates ESLint configuration to flat config format and updates dependencies and tooling scripts.

Changes

Cohort / File(s) Summary
Accordion Feature Enhancement
src/components/ui/Accordion/contexts/AccordionContext.tsx, src/components/ui/Accordion/fragments/AccordionRoot.tsx, src/components/ui/Accordion/fragments/AccordionItem.tsx, src/components/ui/Accordion/fragments/AccordionTrigger.tsx, src/components/ui/Accordion/fragments/AccordionHeader.tsx, src/components/ui/Accordion/fragments/AccordionContent.tsx
Added disabled boolean and orientation fields to AccordionContext; propagated both through root prop and context to all fragments; added data-orientation attributes across trigger, header, content, and item elements; implemented combined disabled state logic in AccordionItem.
ESLint Configuration Migration
.eslintrc.cjs, eslint.config.mjs, .eslintignore
Removed legacy .eslintrc.cjs and .eslintignore; added new eslint.config.mjs flat configuration with equivalent rules, overrides for test/story files, Babel parser setup, and Storybook integration.
Build & Tooling Configuration
package.json, .husky/pre-commit, .storybook/preview.js
Updated lint script to simplify eslint invocation; extended lint-staged to run jest tests; migrated eslint dependencies to @eslint/* packages with version ^9.39.4; added eslint-config-standard override; removed npm test from pre-commit hook; reformatted Storybook preview object structure.
Accessibility & Metadata
.changeset/accordion-orientation-disabled.md, src/components/ui/Accordion/tests/Accordion.a11y.test.tsx
Added Changeset entry documenting disabled root prop and orientation propagation; added two accessibility tests verifying data-orientation presence across components and disabled root aria-disabled behavior with toggle prevention.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AccordionRoot
    participant AccordionContext
    participant AccordionItem
    participant AccordionTrigger

    User->>AccordionRoot: Render with disabled=true & orientation="horizontal"
    AccordionRoot->>AccordionContext: Provide {disabled: true, orientation: "horizontal"}
    AccordionRoot->>AccordionRoot: Set data-orientation="horizontal"
    
    AccordionContext->>AccordionItem: Read {disabled: true, orientation: "horizontal"}
    AccordionItem->>AccordionItem: Calculate effectiveDisabled = true
    AccordionItem->>AccordionItem: Set data-disabled=true, data-orientation="horizontal"
    
    AccordionContext->>AccordionTrigger: Read {disabled: true, orientation: "horizontal"}
    AccordionTrigger->>AccordionTrigger: Set data-orientation="horizontal"
    
    User->>AccordionTrigger: Click to toggle
    AccordionTrigger->>AccordionItem: Attempt onOpenChange
    AccordionItem->>AccordionItem: Block transition (effectiveDisabled=true)
    AccordionItem->>User: No state change
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰 Hop, hop—the accordion now knows its place,
Disabled roots and orientations grace each face,
Data trails through triggers, headers, and more,
While ESLint hops into flat config lore!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: Accordion improvements' is vague and generic, using non-descriptive language that doesn't convey the specific changes made (disabled prop, orientation propagation, and accessibility tests). Consider a more descriptive title like 'feat: Add disabled prop and data-orientation to Accordion component' to clearly communicate the main changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kotapi/accordion-imptovements

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Coverage

This report compares the PR with the base branch. "Δ" shows how the PR affects each metric.

Metric PR Δ
Statements 80.95% +0.01%
Branches 64.38% +0.06%
Functions 69.05% +0.00%
Lines 82.27% +0.01%

Coverage improved or stayed the same. Great job!

Run npm run coverage locally for detailed reports and target untested areas to raise these numbers.

Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (1)
package.json (1)

210-221: ⚠️ Potential issue | 🟡 Minor

Overlapping lint-staged patterns may run ESLint twice on some files.

Files in src/, e2e/, scripts/, test-utils/, or .storybook/ directories matching *.{js,jsx,ts,tsx} will match both patterns (lines 211 and 215), causing ESLint to run twice on the same files. Consider consolidating:

♻️ Suggested consolidation
   "lint-staged": {
     "*.{js,jsx,ts,tsx}": [
       "eslint --fix",
       "jest --findRelatedTests --passWithNoTests"
     ],
-    "{src,e2e,scripts,test-utils,.storybook}/**/*.{js,jsx,ts,tsx}": [
-      "eslint --fix"
-    ],
     "*.{css,scss,sass}": [
       "stylelint --fix"
     ]
   },

Alternatively, if the directory-specific pattern exists for a reason (e.g., different options), exclude those directories from the first pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 210 - 221, The lint-staged config currently runs
ESLint twice because the global pattern "*.{js,jsx,ts,tsx}" overlaps with the
directory-specific pattern
"{src,e2e,scripts,test-utils,.storybook}/**/*.{js,jsx,ts,tsx}"; fix this by
consolidating or excluding: either remove the global pattern and keep only the
directory-specific entry, or keep the global pattern and change it to exclude
those directories (e.g., "!src/**", "!e2e/**", "!scripts/**", "!test-utils/**",
"!.storybook/**") so ESLint only executes once; edit the "lint-staged" block and
adjust the two patterns accordingly.
🧹 Nitpick comments (3)
src/components/ui/Accordion/tests/Accordion.a11y.test.tsx (1)

76-94: Good coverage for orientation propagation, but DOM traversal is fragile.

The test at lines 86-87 uses trigger.closest('h3') and header?.parentElement to find the item element. This assumes a specific DOM structure and may break silently if the component hierarchy changes.

Consider using data-testid attributes on the item/header for more robust element selection, or at minimum add a comment explaining the expected DOM structure.

💡 Suggested improvement for robustness
-            const header = trigger.closest('h3');
-            const item = header?.parentElement;
+            // DOM structure: Item > Header(h3) > Trigger(button)
+            const header = trigger.closest('h3');
+            const item = header?.closest('[data-orientation]');

Or consider adding data-testid="accordion-item" to AccordionItem for more reliable selection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Accordion/tests/Accordion.a11y.test.tsx` around lines 76 -
94, The test's DOM traversal is fragile because it uses trigger.closest('h3')
and header?.parentElement to find the item; update the component and test to use
a stable test id instead: add a data-testid (e.g. data-testid="accordion-item")
to the AccordionItem (or TestAccordion's rendered item) and then replace the
fragile traversal in the test with const item =
screen.getByTestId('accordion-item') (keep assertions on
header/trigger/content), or alternatively add a brief comment above the
traversal explaining the required DOM shape if you prefer not to add test ids;
reference TestAccordion, AccordionItem, trigger and header when making the
change.
src/components/ui/Accordion/fragments/AccordionHeader.tsx (1)

18-21: Consumer-provided data-orientation on child element will override accordion's value.

Based on the mergeProps implementation (see src/core/utils/mergeProps.ts:49), childProps are spread last, giving them precedence. Unlike className (which is concatenated) and event handlers (which are composed), data-orientation has no special handling—so a consumer's value silently wins.

This is consistent with the "child wins" philosophy of mergeProps, but consider whether this is the intended behavior for the accordion's orientation attribute. If the accordion's orientation should always take precedence, you'd need to either:

  1. Add special handling in mergeProps for data attributes, or
  2. Spread the data-orientation after {...merged} in the cloneElement call.

If the current behavior (consumer override wins) is acceptable, no change is needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Accordion/fragments/AccordionHeader.tsx` around lines 18 -
21, The current mergeProps usage in AccordionHeader (variable merged created by
mergeProps({ className: mergedClass, 'data-orientation': orientation },
child.props)) allows a consumer-provided data-orientation in child.props to
override the accordion's orientation; to ensure the accordion's orientation
always wins, either update mergeProps to special-case data attributes (so
mergeProps({ 'data-orientation': ... }, child.props) preserves the first value)
or, more locally and simply, change the cloneElement call that uses merged to
spread the accordion's data-orientation after the merged props (i.e., apply {
...merged, 'data-orientation': orientation } when calling React.cloneElement) so
the AccordionHeader's orientation takes precedence over child.props.
eslint.config.mjs (1)

67-71: Use 'detect' for React version.

Hardcoding '18.2.0' requires manual updates when React is upgraded. Using 'detect' auto-reads the version from package.json.

♻️ Proposed fix
         settings: {
             react: {
-                version: '18.2.0',
+                version: 'detect',
             },
         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eslint.config.mjs` around lines 67 - 71, The React version in the ESLint
config is hardcoded (settings.react.version = '18.2.0'); change it to 'detect'
so the linter automatically reads the project React version from
package.json—update the settings.react.version entry to use the string 'detect'
(refer to the settings.react.version key in the config).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@eslint.config.mjs`:
- Line 112: Remove the deprecated eslint rule 'jsx-a11y/accessible-emoji' from
the config: locate the rule entry "'jsx-a11y/accessible-emoji': 'warn'," in
eslint.config.mjs (the rule list) and delete that line so the linter stops
referencing a rule removed in eslint-plugin-jsx-a11y v6.4.0; no replacement is
needed because the rule is deprecated and has no effect with ^6.10.2.

---

Outside diff comments:
In `@package.json`:
- Around line 210-221: The lint-staged config currently runs ESLint twice
because the global pattern "*.{js,jsx,ts,tsx}" overlaps with the
directory-specific pattern
"{src,e2e,scripts,test-utils,.storybook}/**/*.{js,jsx,ts,tsx}"; fix this by
consolidating or excluding: either remove the global pattern and keep only the
directory-specific entry, or keep the global pattern and change it to exclude
those directories (e.g., "!src/**", "!e2e/**", "!scripts/**", "!test-utils/**",
"!.storybook/**") so ESLint only executes once; edit the "lint-staged" block and
adjust the two patterns accordingly.

---

Nitpick comments:
In `@eslint.config.mjs`:
- Around line 67-71: The React version in the ESLint config is hardcoded
(settings.react.version = '18.2.0'); change it to 'detect' so the linter
automatically reads the project React version from package.json—update the
settings.react.version entry to use the string 'detect' (refer to the
settings.react.version key in the config).

In `@src/components/ui/Accordion/fragments/AccordionHeader.tsx`:
- Around line 18-21: The current mergeProps usage in AccordionHeader (variable
merged created by mergeProps({ className: mergedClass, 'data-orientation':
orientation }, child.props)) allows a consumer-provided data-orientation in
child.props to override the accordion's orientation; to ensure the accordion's
orientation always wins, either update mergeProps to special-case data
attributes (so mergeProps({ 'data-orientation': ... }, child.props) preserves
the first value) or, more locally and simply, change the cloneElement call that
uses merged to spread the accordion's data-orientation after the merged props
(i.e., apply { ...merged, 'data-orientation': orientation } when calling
React.cloneElement) so the AccordionHeader's orientation takes precedence over
child.props.

In `@src/components/ui/Accordion/tests/Accordion.a11y.test.tsx`:
- Around line 76-94: The test's DOM traversal is fragile because it uses
trigger.closest('h3') and header?.parentElement to find the item; update the
component and test to use a stable test id instead: add a data-testid (e.g.
data-testid="accordion-item") to the AccordionItem (or TestAccordion's rendered
item) and then replace the fragile traversal in the test with const item =
screen.getByTestId('accordion-item') (keep assertions on
header/trigger/content), or alternatively add a brief comment above the
traversal explaining the required DOM shape if you prefer not to add test ids;
reference TestAccordion, AccordionItem, trigger and header when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 76e7130e-e5ac-4da9-b2ee-08fd0c5fd3e2

📥 Commits

Reviewing files that changed from the base of the PR and between 70ef81d and 5a393d4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • .changeset/accordion-orientation-disabled.md
  • .eslintignore
  • .eslintrc.cjs
  • .husky/pre-commit
  • .storybook/preview.js
  • eslint.config.mjs
  • package.json
  • src/components/ui/Accordion/contexts/AccordionContext.tsx
  • src/components/ui/Accordion/fragments/AccordionContent.tsx
  • src/components/ui/Accordion/fragments/AccordionHeader.tsx
  • src/components/ui/Accordion/fragments/AccordionItem.tsx
  • src/components/ui/Accordion/fragments/AccordionRoot.tsx
  • src/components/ui/Accordion/fragments/AccordionTrigger.tsx
  • src/components/ui/Accordion/tests/Accordion.a11y.test.tsx
💤 Files with no reviewable changes (3)
  • .husky/pre-commit
  • .eslintignore
  • .eslintrc.cjs

Comment thread eslint.config.mjs
semi: ['warn', 'always'],
'jsx-a11y/alt-text': 'warn',
'jsx-a11y/anchor-is-valid': 'warn',
'jsx-a11y/accessible-emoji': 'warn',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

When was jsx-a11y/accessible-emoji rule deprecated in eslint-plugin-jsx-a11y?

💡 Result:

The jsx-a11y/accessible-emoji rule was first deprecated in eslint-plugin-jsx-a11y version 6.4.0, released on 2020-10-26. It was marked as deprecated via commit dac6864 ("Deprecate the accessible-emoji rule"), included in v6.4.0. It was fully deprecated (removed from recommended/strict configs and README updated) in PR #782, merged on 2021-04-06.

Citations:


Remove deprecated jsx-a11y/accessible-emoji rule.

This rule was deprecated in eslint-plugin-jsx-a11y v6.4.0 (2020-10-26) since emoji are now well-supported by screen readers. With ^6.10.2 installed, this rule has no effect.

🧹 Proposed fix
             'jsx-a11y/alt-text': 'warn',
             'jsx-a11y/anchor-is-valid': 'warn',
-            'jsx-a11y/accessible-emoji': 'warn',
             'jsx-a11y/anchor-has-content': 'warn',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'jsx-a11y/accessible-emoji': 'warn',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eslint.config.mjs` at line 112, Remove the deprecated eslint rule
'jsx-a11y/accessible-emoji' from the config: locate the rule entry
"'jsx-a11y/accessible-emoji': 'warn'," in eslint.config.mjs (the rule list) and
delete that line so the linter stops referencing a rule removed in
eslint-plugin-jsx-a11y v6.4.0; no replacement is needed because the rule is
deprecated and has no effect with ^6.10.2.

@kotAPI kotAPI merged commit 008342f into main Apr 3, 2026
10 checks passed
@kotAPI kotAPI deleted the kotapi/accordion-imptovements branch April 3, 2026 05:26
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