Skip to content

bug: Replace disabled attr with aria-disabled in Button#32325

Closed
WioletaKolodziej wants to merge 4 commits into
storybookjs:nextfrom
WioletaKolodziej:fix/31678-aria-disabled-button
Closed

bug: Replace disabled attr with aria-disabled in Button#32325
WioletaKolodziej wants to merge 4 commits into
storybookjs:nextfrom
WioletaKolodziej:fix/31678-aria-disabled-button

Conversation

@WioletaKolodziej
Copy link
Copy Markdown
Contributor

@WioletaKolodziej WioletaKolodziej commented Aug 23, 2025

Closes #31678

What I did

Replaced native disabled with aria-disabled for accessibility.
This keeps the button focusable and prevents conflicts with native behavior while preserving styling and click handling.

Changes made:

Replaced native disabled attribute with aria-disabled to preserve focusability and accessibility.

Updated handleClick to prevent clicks when disabled is true, instead of conditionally removing the onClick handler.

Styled button still uses disabled for cursor and opacity styling.

Reasoning:

Using native disabled removes the button from keyboard navigation and makes it invisible to screen readers.
By using aria-disabled, the button remains focusable and perceivable by assistive technologies, while still preventing interactions.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook canary-release-pr.yml --field pr=<PR_NUMBER>

Greptile Summary

Updated On: 2025-09-10 14:51:07 UTC

This PR implements accessibility improvements by replacing the native HTML disabled attribute with aria-disabled in the Button component. The change addresses the issue where disabled buttons are removed from the accessibility tree and cannot be navigated via keyboard, making them invisible to screen readers.

The PR updates the Button component to use aria-disabled="true" while maintaining visual styling through CSS that still references the disabled prop. The implementation adds click prevention logic in the handleClick method to prevent interactions when disabled is true, rather than relying on the native HTML disabled behavior. This approach keeps buttons focusable and perceivable by assistive technologies while still preventing user interactions.

Corresponding test files have been updated across the codebase to check for aria-disabled attributes instead of using DOM testing library matchers like toBeDisabled(). The changes affect E2E tests for addon-toolbars, addon-viewport, component testing, and IntentSurvey stories. The Button stories have been enhanced with proper TypeScript types and a custom render function for the disabled state demonstration.

Confidence score: 1/5

  • This PR contains critical syntax errors that will cause immediate compilation failures in production
  • Score reflects severe code quality issues including duplicate property declarations and malformed function signatures in the core Button component
  • Multiple files still contain both disabled and aria-disabled attributes simultaneously, creating conflicting implementations

Summary by CodeRabbit

  • Accessibility

    • Disabled controls now expose their state via aria-disabled and block clicks, improving assistive tech behavior.
  • Bug Fixes

    • Prevented interactions with disabled buttons and toolbar items to avoid unintended actions.
  • Tests

    • Test assertions updated to check aria-disabled instead of the native disabled state; minor expectation adjustments to match the new behavior.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Bot Settings | Greptile

Comment thread code/core/src/components/components/Button/Button.tsx Outdated
@Sidnioulz Sidnioulz changed the title fix(a11y): replace native disabled with isDisabled and aria-disabled in Button - #31678 A11y: Replace disabled attr with aria-disabled in Button Aug 29, 2025
@Sidnioulz Sidnioulz self-assigned this Aug 29, 2025
@Sidnioulz Sidnioulz self-requested a review August 29, 2025 07:41
Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution, @WioletaKolodziej!

Let's first start with addressing the feedback from Greptile, and with fixing the PR description. Please copy the PR template that you deleted and please fill it in carefully. It helps us speed up reviews.

You'll also need to address the following:

  • Keep the Button API stable, it's a public component and there's no need for a breaking change here; map the disabled prop to aria-disabled without changing the public API
  • Update Button.stories.tsx to ensure a disabled story exists; in that story, add a play function that proves the button has the aria-disabled attribute and that clicking it does not trigger onClick
  • Add a story where the button is rendered with two sibling buttons, and add a play function showing that pressing Tab after focusing the previous button moves focus to the aria-disabled button (which is what we want to fix here)
  • Check if any existing test or story uses toBeDisabled for this button, and if so, switch to toHaveAttribute('aria-disabled', true)

PR template

Closes #

<!-- If your PR is related to an issue, provide the number(s) above; if it resolves multiple issues, be sure to break them up (e.g. "closes #1000, closes #1001"). -->

<!--

Thank you for contributing to Storybook! Please submit all PRs to the `next` branch unless they are specific to the current release. Storybook maintainers cherry-pick bug and documentation fixes into the `main` branch as part of the release process, so you shouldn't need to worry about this. For additional guidance: https://storybook.js.org/docs/contribute

-->

## What I did

<!-- Briefly describe what your PR does -->

## Checklist for Contributors

### Testing

<!-- Please check (put an "x" inside the "[ ]") the applicable items below to communicate how to test your changes -->

#### The changes in this PR are covered in the following automated tests:

- [ ] stories
- [ ] unit tests
- [ ] integration tests
- [ ] end-to-end tests

#### Manual testing

_This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!_

<!-- Please include the steps to test your changes here. For example:

1. Run a sandbox for template, e.g. `yarn task --task sandbox --start-from auto --template react-vite/default-ts`
2. Open Storybook in your browser
3. Access X story

-->

### Documentation

<!-- Please check (put an "x" inside the "[ ]") the applicable items below to indicate which documentation has been updated. -->

- [ ] Add or update documentation reflecting your changes
- [ ] If you are deprecating/removing a feature, make sure to update
      [MIGRATION.MD](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md)

## Checklist for Maintainers

- [ ] When this PR is ready for testing, make sure to add `ci:normal`, `ci:merged` or `ci:daily` GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in `code/lib/cli-storybook/src/sandbox-templates.ts`
- [ ] Make sure this PR contains **one** of the labels below:
   <details>
     <summary>Available labels</summary>

  - `bug`: Internal changes that fixes incorrect behavior.
  - `maintenance`: User-facing maintenance tasks.
  - `dependencies`: Upgrading (sometimes downgrading) dependencies.
  - `build`: Internal-facing build tooling & test updates. Will not show up in release changelog.
  - `cleanup`: Minor cleanup style change. Will not show up in release changelog.
  - `documentation`: Documentation **only** changes. Will not show up in release changelog.
  - `feature request`: Introducing a new feature.
  - `BREAKING CHANGE`: Changes that break compatibility in some way with current major version.
  - `other`: Changes that don't fit in the above categories.

   </details>

### 🦋 Canary release

<!-- CANARY_RELEASE_SECTION -->

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the `@storybookjs/core` team here.

_core team members can create a canary release [here](https://github.com/storybookjs/storybook/actions/workflows/canary-release-pr.yml) or locally with `gh workflow run --repo storybookjs/storybook canary-release-pr.yml --field pr=<PR_NUMBER>`_

<!-- CANARY_RELEASE_SECTION -->

<!-- BENCHMARK_SECTION -->
<!-- BENCHMARK_SECTION -->

Thanks!

@WioletaKolodziej WioletaKolodziej force-pushed the fix/31678-aria-disabled-button branch from b2b6297 to 9b937f8 Compare September 3, 2025 13:27
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Sep 3, 2025

Package Benchmarks

Commit: 268d83f, ran on 14 October 2025 at 11:17:44 UTC

The following packages have significant changes to their size or dependencies:

@storybook/addon-docs

Before After Difference
Dependency count 18 18 0
Self size 2.07 MB 2.01 MB 🎉 -61 KB 🎉
Dependency size 10.21 MB 10.20 MB 🎉 -10 KB 🎉
Bundle Size Analyzer Link Link

@storybook/builder-vite

Before After Difference
Dependency count 11 11 0
Self size 330 KB 374 KB 🚨 +44 KB 🚨
Dependency size 1.30 MB 1.30 MB 🎉 -27 B 🎉
Bundle Size Analyzer Link Link

storybook

Before After Difference
Dependency count 43 48 🚨 +5 🚨
Self size 30.19 MB 30.62 MB 🚨 +432 KB 🚨
Dependency size 17.31 MB 17.65 MB 🚨 +346 KB 🚨
Bundle Size Analyzer Link Link

@storybook/html-vite

Before After Difference
Dependency count 14 14 0
Self size 23 KB 23 KB 🎉 -2 B 🎉
Dependency size 1.67 MB 1.71 MB 🚨 +44 KB 🚨
Bundle Size Analyzer Link Link

@storybook/nextjs

Before After Difference
Dependency count 532 532 0
Self size 950 KB 928 KB 🎉 -22 KB 🎉
Dependency size 58.55 MB 58.52 MB 🎉 -28 KB 🎉
Bundle Size Analyzer Link Link

@storybook/nextjs-vite

Before After Difference
Dependency count 124 130 🚨 +6 🚨
Self size 4.10 MB 4.00 MB 🎉 -96 KB 🎉
Dependency size 21.63 MB 21.68 MB 🚨 +47 KB 🚨
Bundle Size Analyzer Link Link

@storybook/preact-vite

Before After Difference
Dependency count 14 14 0
Self size 14 KB 14 KB 🚨 +15 B 🚨
Dependency size 1.65 MB 1.70 MB 🚨 +44 KB 🚨
Bundle Size Analyzer Link Link

@storybook/react-native-web-vite

Before After Difference
Dependency count 157 163 🚨 +6 🚨
Self size 31 KB 31 KB 🎉 -14 B 🎉
Dependency size 23.02 MB 23.07 MB 🚨 +47 KB 🚨
Bundle Size Analyzer Link Link

@storybook/react-vite

Before After Difference
Dependency count 114 120 🚨 +6 🚨
Self size 37 KB 36 KB 🎉 -919 B 🎉
Dependency size 19.57 MB 19.62 MB 🚨 +48 KB 🚨
Bundle Size Analyzer Link Link

@storybook/react-webpack5

Before After Difference
Dependency count 272 272 0
Self size 25 KB 25 KB 🎉 -3 B 🎉
Dependency size 43.51 MB 43.48 MB 🎉 -28 KB 🎉
Bundle Size Analyzer Link Link

@storybook/svelte-vite

Before After Difference
Dependency count 19 19 0
Self size 59 KB 58 KB 🎉 -1 KB 🎉
Dependency size 26.79 MB 26.83 MB 🚨 +45 KB 🚨
Bundle Size Analyzer Link Link

@storybook/sveltekit

Before After Difference
Dependency count 20 20 0
Self size 58 KB 48 KB 🎉 -10 KB 🎉
Dependency size 26.85 MB 26.89 MB 🚨 +44 KB 🚨
Bundle Size Analyzer Link Link

@storybook/vue3-vite

Before After Difference
Dependency count 109 110 🚨 +1 🚨
Self size 38 KB 38 KB 🎉 -290 B 🎉
Dependency size 43.78 MB 43.83 MB 🚨 +53 KB 🚨
Bundle Size Analyzer Link Link

@storybook/web-components-vite

Before After Difference
Dependency count 15 15 0
Self size 20 KB 20 KB 🚨 +14 B 🚨
Dependency size 1.70 MB 1.74 MB 🚨 +44 KB 🚨
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 187 220 🚨 +33 🚨
Self size 921 KB 879 KB 🎉 -43 KB 🎉
Dependency size 79.87 MB 82.22 MB 🚨 +2.35 MB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 169 189 🚨 +20 🚨
Self size 35 KB 35 KB 🎉 -14 B 🎉
Dependency size 76.30 MB 77.29 MB 🚨 +997 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 44 49 🚨 +5 🚨
Self size 1.55 MB 1.52 MB 🎉 -29 KB 🎉
Dependency size 47.50 MB 48.27 MB 🚨 +778 KB 🚨
Bundle Size Analyzer node node

@storybook/react-dom-shim

Before After Difference
Dependency count 0 0 0
Self size 22 KB 12 KB 🎉 -10 KB 🎉
Dependency size 788 B 785 B 🎉 -3 B 🎉
Bundle Size Analyzer Link Link

@storybook/preset-create-react-app

Before After Difference
Dependency count 68 68 0
Self size 36 KB 26 KB 🎉 -10 KB 🎉
Dependency size 5.98 MB 5.98 MB 🎉 -3 B 🎉
Bundle Size Analyzer Link Link

@storybook/preset-react-webpack

Before After Difference
Dependency count 170 170 0
Self size 21 KB 26 KB 🚨 +6 KB 🚨
Dependency size 31.00 MB 31.00 MB 🎉 -9 B 🎉
Bundle Size Analyzer Link Link

@storybook/react

Before After Difference
Dependency count 2 2 0
Self size 894 KB 872 KB 🎉 -23 KB 🎉
Dependency size 28 KB 18 KB 🎉 -10 KB 🎉
Bundle Size Analyzer Link Link

@WioletaKolodziej
Copy link
Copy Markdown
Contributor Author

WioletaKolodziej commented Sep 3, 2025

@Sidnioulz It’s important to note that even if our Button Interface handles disabled, this does not guarantee that the native HTML disabled attribute won’t end up in the DOM. Since disabled is a standard HTML attribute, React will always forward it – whether through ...props, styled-components, or simply if someone uses a plain button somewhere in the code. As a result, our Button.tsx is not resilient to such mistakes, because disabled can still leak directly into the DOM. Approach is to rely on aria-disabled, but instead of exposing disabled in the API, we should consider a custom prop name like isDisabled (or similar) that prevents the native attribute from being accidentally passed through anyway.

@Sidnioulz
Copy link
Copy Markdown
Member

@Sidnioulz It’s important to note that even if our Button Interface handles disabled, this does not guarantee that the native HTML disabled attribute won’t end up in the DOM. Since disabled is a standard HTML attribute, React will always forward it – whether through ...props, styled-components, or simply if someone uses a plain button somewhere in the code. As a result, our Button.tsx is not resilient to such mistakes, because disabled can still leak directly into the DOM. Approach is to rely on aria-disabled, but instead of exposing disabled in the API, we should consider a custom prop name like isDisabled (or similar) that prevents the native attribute from being accidentally passed through anyway.

That's a legitimate concern, but I don't share your analysis. The way I see it, you're arguing in favour of the disabled prop name.

Button with isDisabled prop receives a disabled attribute?
It'll be forwarded as part of {...props} if not actively identified and removed.

Button with disabled prop receives a disabled attribute?
It will be destructured as a prop and has no chance of leaking into the underlying HTML button element.

I'm not a fan of disabled as a name, and also prefer is* naming for stateful boolean props. But in that specific instance:

  • It's public API so we don't want to change it unless necessary
  • It actually helps us avoid the issue you described

@WioletaKolodziej WioletaKolodziej force-pushed the fix/31678-aria-disabled-button branch from 9b937f8 to 2824a75 Compare September 10, 2025 14:16
@WioletaKolodziej WioletaKolodziej deleted the fix/31678-aria-disabled-button branch September 10, 2025 14:17
@WioletaKolodziej WioletaKolodziej restored the fix/31678-aria-disabled-button branch September 10, 2025 14:24
@WioletaKolodziej WioletaKolodziej deleted the fix/31678-aria-disabled-button branch September 10, 2025 14:24
@WioletaKolodziej WioletaKolodziej restored the fix/31678-aria-disabled-button branch September 10, 2025 14:37
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Reviewing changes made in this pull request

Comment thread code/e2e-tests/addon-viewport.spec.ts Outdated
Comment thread code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.stories.tsx Outdated
Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

Thanks for revisiting this, @WioletaKolodziej! I've added a few comments for you. Please let me know if you need any help or clarification!

Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/e2e-tests/addon-viewport.spec.ts Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
@github-actions github-actions Bot added the Stale label Sep 29, 2025
@valentinpalkovic
Copy link
Copy Markdown
Contributor

Hey @Sidnioulz,

Can you verify whether any of these changes have already been made on the a11y-consolidation branch? And if so, can we close this PR?

@Sidnioulz
Copy link
Copy Markdown
Member

Hey @Sidnioulz,

Can you verify whether any of these changes have already been made on the a11y-consolidation branch? And if so, can we close this PR?

This is a separate issue that we haven't been addressing on the a11y branch :) It should stay open.

WioletaKolodziej and others added 2 commits October 14, 2025 12:37
Co-authored-by: Steve Dodier-Lazaro <Sidnioulz@users.noreply.github.com>
Co-authored-by: Steve Dodier-Lazaro <Sidnioulz@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 14, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Tests now assert aria-disabled instead of native disabled checks. The Button component omits native disabled from its public props, uses aria-disabled when disabled, and prevents clicks when disabled. Stories updated to import and use the exported ButtonProps type.

Changes

Cohort / File(s) Summary
Button component API & behavior
code/core/src/components/components/Button/Button.tsx
ButtonProps now Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled'>. Component forwards ...restProps, sets aria-disabled when disabled, and guards clicks (preventDefault + stopPropagation) when disabled.
Button stories / typing
code/core/src/components/components/Button/Button.stories.tsx
Imports ButtonProps type, uses satisfies ButtonProps for story args, and adds an onClick in the Disabled story.
Tests: switch to ARIA assertions
code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx, code/e2e-tests/addon-toolbars.spec.ts, code/e2e-tests/addon-viewport.spec.ts, test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
Replace .toBeDisabled() / .not.toBeDisabled() checks with `.toHaveAttribute('aria-disabled', 'true'

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Button as Button (component)
  participant Handler as onClick handler

  User->>Button: Click
  alt aria-disabled == "true"
    rect #f8f3e6
    Note right of Button: accessibility state set\npreventDefault + stopPropagation
    Button->>Button: preventDefault & stopPropagation
    Note right of Button: handler NOT invoked
    end
  else aria-disabled == "false"
    Button->>Handler: invoke onClick(event)
    Handler-->>Button: handler completes
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0b5902 and 31368c1.

📒 Files selected for processing (6)
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1 hunks)
  • code/core/src/components/components/Button/Button.stories.tsx (2 hunks)
  • code/core/src/components/components/Button/Button.tsx (5 hunks)
  • code/e2e-tests/addon-toolbars.spec.ts (1 hunks)
  • code/e2e-tests/addon-viewport.spec.ts (2 hunks)
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
test-storybooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain test Storybook configurations under test-storybooks/ for E2E and visual testing scenarios

Files:

  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
🧬 Code graph analysis (2)
code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1)
code/core/src/test/testing-library.ts (1)
  • userEvent (119-122)
code/core/src/components/components/Button/Button.stories.tsx (2)
code/core/src/components/components/Button/Button.tsx (2)
  • ButtonProps (8-17)
  • Button (19-85)
code/core/src/components/components/Modal/Modal.styled.tsx (1)
  • Row (80-84)
🪛 Biome (2.1.2)
code/core/src/components/components/Button/Button.tsx

[error] 28-28: Duplicate parameter name.

The parameter overrides a preceding parameter by using the same name.

(lint/suspicious/noDuplicateParameters)


[error] 28-28: Shouldn't redeclare 'disabled'. Consider to delete it or rename it.

'disabled' is defined here:

(lint/suspicious/noRedeclare)


[error] 96-97: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '>'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (4)
code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1)

34-34: LGTM - Correctly checking for absence of aria-disabled.

The assertion correctly checks that the aria-disabled attribute is not present when the button becomes enabled. This aligns with the previous review feedback and accessibility best practices.

code/e2e-tests/addon-viewport.spec.ts (1)

57-57: LGTM - Correct aria-disabled assertion.

The assertion correctly checks the aria-disabled attribute with the string value "true".

code/core/src/components/components/Button/Button.stories.tsx (1)

161-169: Consider following previous review feedback on story structure.

The previous review comment suggested:

  • Removing the render function
  • Adding a play function to demonstrate the button is not clickable but focusable
  • Creating a separate DisabledVariants story with all variants

The current implementation includes a render override and an onClick handler. While the type constraint using satisfies ButtonProps is good for type safety, consider whether the custom render is necessary or if the suggestions from the previous review would better demonstrate the disabled state behavior.

Based on past review feedback, would you like to:

  1. Remove the render function and add a play function instead?
  2. Create a DisabledVariants story showing all button variants in disabled state?
code/core/src/components/components/Button/Button.tsx (1)

44-48: LGTM - Click handler correctly prevents interaction when disabled.

The click handler properly guards against the disabled state by preventing default behavior and stopping propagation, which prevents the button from being clicked when disabled while still allowing it to be focusable.

Comment thread code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/e2e-tests/addon-toolbars.spec.ts
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: 3

♻️ Duplicate comments (4)
code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (3)

26-26: Use string value for aria-disabled attribute.

The aria-disabled attribute is a string, so you should pass 'true' as a string rather than the boolean true.

Apply this diff:

-    await expect(button).toHaveAttribute('aria-disabled', true);
+    await expect(button).toHaveAttribute('aria-disabled', 'true');

31-31: Use string value for aria-disabled attribute.

The aria-disabled attribute is a string, so you should pass 'true' as a string rather than the boolean true.

Apply this diff:

-    await expect(button).toHaveAttribute('aria-disabled', true);
+    await expect(button).toHaveAttribute('aria-disabled', 'true');

39-39: Use string value for aria-disabled attribute.

The aria-disabled attribute is a string, so you should pass 'true' as a string rather than the boolean true.

Apply this diff:

-      await expect(button).toHaveAttribute('aria-disabled', true);
+      await expect(button).toHaveAttribute('aria-disabled', 'true');
code/core/src/components/components/Button/Button.tsx (1)

75-76: Remove native disabled attribute to preserve focusability.

The PR objectives state that buttons should remain focusable and in the accessibility tree. However, line 76 still passes disabled={disabled} to the underlying button element, which sets the native disabled attribute. This prevents keyboard navigation and contradicts the PR goals.

Apply this diff:

         aria-disabled={disabled}
-        disabled={disabled}
         active={active}
🧹 Nitpick comments (1)
code/e2e-tests/addon-toolbars.spec.ts (1)

35-35: Check aria-disabled attribute value for precision.

The current assertion only checks for the presence of the aria-disabled attribute. For better precision, verify that the attribute value is 'true' to confirm the button is actually disabled.

Apply this diff:

-    await expect(button).toHaveAttribute('aria-disabled');
+    await expect(button).toHaveAttribute('aria-disabled', 'true');
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0b5902 and 31368c1.

📒 Files selected for processing (6)
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1 hunks)
  • code/core/src/components/components/Button/Button.stories.tsx (2 hunks)
  • code/core/src/components/components/Button/Button.tsx (5 hunks)
  • code/e2e-tests/addon-toolbars.spec.ts (1 hunks)
  • code/e2e-tests/addon-viewport.spec.ts (2 hunks)
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
test-storybooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain test Storybook configurations under test-storybooks/ for E2E and visual testing scenarios

Files:

  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
🧬 Code graph analysis (2)
code/core/src/components/components/Button/Button.stories.tsx (2)
code/core/src/components/components/Button/Button.tsx (2)
  • ButtonProps (8-17)
  • Button (19-85)
code/core/src/components/components/Modal/Modal.styled.tsx (1)
  • Row (80-84)
code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1)
code/core/src/test/testing-library.ts (1)
  • userEvent (119-122)
🪛 Biome (2.1.2)
code/core/src/components/components/Button/Button.tsx

[error] 28-28: Duplicate parameter name.

The parameter overrides a preceding parameter by using the same name.

(lint/suspicious/noDuplicateParameters)


[error] 28-28: Shouldn't redeclare 'disabled'. Consider to delete it or rename it.

'disabled' is defined here:

(lint/suspicious/noRedeclare)


[error] 96-97: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '>'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (6)
test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts (1)

217-217: Verify aria-disabled attribute value type.

Playwright's toHaveAttribute expects a string value. Since aria-disabled is a string attribute, you should pass 'true' as a string rather than the boolean true.

Apply this diff:

-      expect(watchModeButton).toHaveAttribute("aria-disabled", true),
+      expect(watchModeButton).toHaveAttribute("aria-disabled", "true"),
code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1)

34-34: Verify disabled button does not have aria-disabled='true'.

The current assertion checks that aria-disabled does not have the value true. Based on the Button implementation in code/core/src/components/components/Button/Button.tsx, when disabled is false, the button will have aria-disabled='false'. Consider verifying the attribute has value 'false' instead of just checking it doesn't have value true.

Apply this diff to explicitly check for the 'false' value:

-    await expect(button).not.toHaveAttribute('aria-disabled', true);
+    await expect(button).toHaveAttribute('aria-disabled', 'false');
code/e2e-tests/addon-viewport.spec.ts (2)

33-33: LGTM: Synchronous assertions for width checks.

The change from await expect to synchronous expect is correct for the toBeDefined() assertions on the bounding box width properties.

Also applies to: 39-39, 53-53


57-57: LGTM: Correct aria-disabled attribute check.

The assertion correctly checks for the aria-disabled attribute with string value 'true' to verify the disabled state.

code/core/src/components/components/Button/Button.stories.tsx (1)

163-169: Consider aligning with previous feedback on render function.

A past review comment suggested either removing the render function for a single variant or creating a DisabledVariants story showing all variants (solid, outline, ghost) side by side. The current implementation still uses render for a single variant.

Consider one of these approaches:

  1. Remove the render function to show the disabled state with the default variant
  2. Create a comprehensive DisabledVariants story showing all three variants in their disabled state

Based on learnings

code/core/src/components/components/Button/Button.tsx (1)

44-48: LGTM: Disabled guard in click handler.

The click handler correctly prevents interaction when the button is disabled by stopping propagation and preventing default behavior.

Comment thread code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
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: 4

♻️ Duplicate comments (2)
code/core/src/components/components/Button/Button.stories.tsx (1)

157-170: Consider addressing previous reviewer's feedback on story structure.

A past reviewer questioned the use of render here and suggested either:

  1. Remove the render function and add a play function to demonstrate the button is focusable but not clickable (the key accessibility improvement)
  2. Create a DisabledVariants story showing all variants and pseudo states in disabled state

The current render function wraps a single variant without showcasing the key behavioral change (focusability while disabled) that this PR introduces.

Based on past review comments.

code/core/src/components/components/Button/Button.tsx (1)

75-76: Remove the native disabled attribute to achieve the PR's accessibility goal.

Setting both aria-disabled and the native disabled attribute defeats the purpose of this PR. The native disabled attribute removes the button from keyboard navigation and the accessibility tree, which is precisely what the PR aims to fix by using aria-disabled instead.

A past reviewer noted: "If you don't remove the disabled attr, then keyboard navigation is still impossible."

Apply this diff:

     aria-disabled={disabled}
-    disabled={disabled}
     active={active}
🧹 Nitpick comments (2)
code/e2e-tests/addon-toolbars.spec.ts (1)

35-35: Specify the expected attribute value for more precise assertion.

The assertion only checks for the presence of aria-disabled but doesn't validate its value. Consider checking that the value is 'true' to ensure the button is actually in a disabled state.

Apply this diff:

-    await expect(button).toHaveAttribute('aria-disabled');
+    await expect(button).toHaveAttribute('aria-disabled', 'true');
code/core/src/components/components/Button/Button.tsx (1)

8-17: Clarify the intent of omitting then re-adding disabled in ButtonProps.

The type extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled'> to remove the native disabled property, but then immediately re-declares disabled?: boolean on line 14. This pattern can be confusing.

If the intent is to prevent the native HTML disabled attribute from being forwarded while still accepting a disabled prop for internal handling, this structure achieves that goal. However, consider adding a JSDoc comment explaining this design decision to help future maintainers understand why disabled is both omitted and re-added.

Example documentation:

/**
 * Button props. Omits the native HTML disabled attribute to prevent it from
 * being forwarded to the DOM (which would break keyboard navigation).
 * Instead, uses aria-disabled for accessibility while handling the disabled
 * state internally via event handlers and styling.
 */
export interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled'> {
  // ... existing props including disabled
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0b5902 and 31368c1.

📒 Files selected for processing (6)
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1 hunks)
  • code/core/src/components/components/Button/Button.stories.tsx (2 hunks)
  • code/core/src/components/components/Button/Button.tsx (5 hunks)
  • code/e2e-tests/addon-toolbars.spec.ts (1 hunks)
  • code/e2e-tests/addon-viewport.spec.ts (2 hunks)
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
test-storybooks/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain test Storybook configurations under test-storybooks/ for E2E and visual testing scenarios

Files:

  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
🧬 Code graph analysis (1)
code/core/src/components/components/Button/Button.stories.tsx (2)
code/core/src/components/components/Button/Button.tsx (2)
  • ButtonProps (8-17)
  • Button (19-85)
code/core/src/components/components/Modal/Modal.styled.tsx (1)
  • Row (80-84)
🪛 Biome (2.1.2)
code/core/src/components/components/Button/Button.tsx

[error] 28-28: Duplicate parameter name.

The parameter overrides a preceding parameter by using the same name.

(lint/suspicious/noDuplicateParameters)


[error] 28-28: Shouldn't redeclare 'disabled'. Consider to delete it or rename it.

'disabled' is defined here:

(lint/suspicious/noRedeclare)


[error] 96-97: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '>'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (2)
code/e2e-tests/addon-viewport.spec.ts (1)

57-57: LGTM!

The assertion correctly checks both the presence and value of the aria-disabled attribute using string 'true', which aligns with ARIA specifications.

code/core/src/components/components/Button/Button.stories.tsx (1)

8-8: LGTM!

Importing and exposing ButtonProps as a public type is excellent for type safety and enables consumers to properly type Button usage in their own code.

Comment thread code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
Comment thread code/core/src/components/components/Button/Button.tsx Outdated
@WioletaKolodziej WioletaKolodziej force-pushed the fix/31678-aria-disabled-button branch from 31368c1 to f9f5edd Compare October 14, 2025 10:57
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: 0

🧹 Nitpick comments (1)
code/core/src/components/components/Button/Button.tsx (1)

74-74: Consider omitting aria-disabled when not disabled.

Setting aria-disabled="false" explicitly is valid but not idiomatic. Best practice is to set the attribute to 'true' when disabled or omit it entirely (using undefined) when not disabled.

Apply this diff to follow the more common pattern:

-        aria-disabled={disabled ? 'true' : 'false'}
+        aria-disabled={disabled ? 'true' : undefined}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31368c1 and a20fc11.

📒 Files selected for processing (5)
  • code/core/src/components/components/Button/Button.stories.tsx (2 hunks)
  • code/core/src/components/components/Button/Button.tsx (4 hunks)
  • code/e2e-tests/addon-toolbars.spec.ts (1 hunks)
  • code/e2e-tests/addon-viewport.spec.ts (2 hunks)
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
🧰 Additional context used
📓 Path-based instructions (6)
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.tsx
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/e2e-tests/addon-toolbars.spec.ts
  • code/core/src/components/components/Button/Button.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (2)
code/core/src/components/components/Button/Button.tsx (2)

8-17: LGTM: ButtonProps correctly omits native disabled.

The interface properly excludes the native HTML disabled attribute via Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled'> and defines its own disabled?: boolean. This prevents the native attribute from being forwarded while maintaining the API surface.


43-47: LGTM: Disabled guard correctly prevents interaction.

The handleClick guard properly blocks user interaction when disabled is true by calling preventDefault() and stopPropagation() before returning. This replaces the behavior of the native disabled attribute while keeping the button focusable.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31368c1 and a20fc11.

📒 Files selected for processing (5)
  • code/core/src/components/components/Button/Button.stories.tsx (2 hunks)
  • code/core/src/components/components/Button/Button.tsx (4 hunks)
  • code/e2e-tests/addon-toolbars.spec.ts (1 hunks)
  • code/e2e-tests/addon-viewport.spec.ts (2 hunks)
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/core/src/components/components/Button/Button.tsx
  • test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/e2e-tests/addon-viewport.spec.ts
  • code/e2e-tests/addon-toolbars.spec.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-toolbars.spec.ts
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/e2e-tests/addon-viewport.spec.ts
  • code/e2e-tests/addon-toolbars.spec.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-toolbars.spec.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-toolbars.spec.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/e2e-tests/addon-viewport.spec.ts
  • code/core/src/components/components/Button/Button.stories.tsx
  • code/e2e-tests/addon-toolbars.spec.ts
🧬 Code graph analysis (1)
code/core/src/components/components/Button/Button.stories.tsx (1)
code/core/src/components/components/Button/Button.tsx (1)
  • ButtonProps (8-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (4)
code/core/src/components/components/Button/Button.stories.tsx (2)

8-8: LGTM: Import aligns with type-safe story args.

The import of ButtonProps type enables type-safe story args via the satisfies assertion on line 162.


161-162: Add play function to demonstrate disabled behavior.

The onClick handler and type assertion are good additions, but the previous review comment requested a play function to demonstrate that the button is focusable but not clickable when disabled. Consider adding a play function that:

  • Focuses the button (to show it's focusable)
  • Attempts to click it (to show clicks are blocked)
  • Verifies onClick was not called

Additionally, consider creating a DisabledVariants story that shows all variants in the disabled state.

code/e2e-tests/addon-viewport.spec.ts (1)

57-57: LGTM: Correctly verifies aria-disabled attribute.

The assertion properly checks for aria-disabled='true', which aligns with the PR's accessibility changes to replace native disabled with aria-disabled.

code/e2e-tests/addon-toolbars.spec.ts (1)

35-35: LGTM: Correctly verifies aria-disabled attribute value.

The assertion properly checks both the presence and value of aria-disabled='true', addressing the previous review comment and aligning with the PR's accessibility changes.

Comment thread code/e2e-tests/addon-viewport.spec.ts
@WioletaKolodziej WioletaKolodziej force-pushed the fix/31678-aria-disabled-button branch from a20fc11 to 64fee36 Compare October 14, 2025 11:05
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@WioletaKolodziej WioletaKolodziej force-pushed the fix/31678-aria-disabled-button branch from 64fee36 to 268d83f Compare October 14, 2025 11:07
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: 0

🧹 Nitpick comments (1)
code/core/src/components/components/Button/Button.tsx (1)

74-74: Consider omitting aria-disabled when false.

While setting aria-disabled="false" is valid, the ARIA specification recommends omitting the attribute entirely when the element is not disabled. The absence of aria-disabled is semantically equivalent to aria-disabled="false".

Apply this diff to omit the attribute when not disabled:

-        aria-disabled={disabled ? 'true' : 'false'}
+        aria-disabled={disabled ? 'true' : undefined}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a20fc11 and 268d83f.

📒 Files selected for processing (4)
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx (1 hunks)
  • code/core/src/components/components/Button/Button.stories.tsx (2 hunks)
  • code/core/src/components/components/Button/Button.tsx (4 hunks)
  • code/e2e-tests/addon-viewport.spec.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/addons/onboarding/src/features/IntentSurvey/IntentSurvey.stories.tsx
  • code/core/src/components/components/Button/Button.stories.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/core/src/components/components/Button/Button.tsx
  • code/e2e-tests/addon-viewport.spec.ts
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/core/src/components/components/Button/Button.tsx
  • code/e2e-tests/addon-viewport.spec.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/core/src/components/components/Button/Button.tsx
  • code/e2e-tests/addon-viewport.spec.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/components/components/Button/Button.tsx
  • code/e2e-tests/addon-viewport.spec.ts
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/e2e-tests/addon-viewport.spec.ts
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/e2e-tests/addon-viewport.spec.ts
🧬 Code graph analysis (1)
code/e2e-tests/addon-viewport.spec.ts (1)
code/core/src/test/expect.ts (1)
  • expect (148-148)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (5)
code/e2e-tests/addon-viewport.spec.ts (2)

33-33: Correctly removed unnecessary await from synchronous assertions.

These assertions check plain JavaScript values (width properties) from the already-resolved boundingBox() results, not Playwright locators. The assertions are synchronous and do not require await. The previous review incorrectly flagged these as missing await.

Also applies to: 39-39, 42-42, 53-53


57-57: LGTM! Correctly checks ARIA-disabled state.

The assertion correctly verifies the aria-disabled attribute with the expected string value 'true', aligning with the PR's goal of using ARIA-based disabled state. The await is appropriately retained for this Playwright locator assertion.

code/core/src/components/components/Button/Button.tsx (3)

8-8: Excellent approach to prevent native disabled attribute leakage.

Omitting disabled from the extended ButtonHTMLAttributes prevents the native HTML disabled attribute from being accidentally forwarded while still allowing the component to accept and control disabled as its own prop. This aligns perfectly with the PR objective to replace native disabled with aria-disabled.


30-30: Correctly prevents disabled from being forwarded to DOM.

By destructuring disabled explicitly and using restProps for spreading, the implementation ensures the native disabled attribute is never forwarded to the button element. This maintains keyboard focusability as intended by the PR.

Also applies to: 79-79


43-47: LGTM! Correctly prevents interaction for disabled buttons.

The early return with preventDefault() and stopPropagation() is the correct pattern for aria-disabled buttons. This prevents user interaction while keeping the button in the accessibility tree and keyboard navigation flow.

@WioletaKolodziej WioletaKolodziej changed the title A11y: Replace disabled attr with aria-disabled in Button bug: Replace disabled attr with aria-disabled in Button Oct 14, 2025
@github-actions github-actions Bot added the Stale label Nov 1, 2025
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Nov 3, 2025

View your CI Pipeline Execution ↗ for commit 268d83f

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 46s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-03 09:22:08 UTC

Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

@WioletaKolodziej thanks for updating the branch! As we were busy with the 10.0 release, it took me longer than it should to redo a review.

You're almost there! Just one unit test to add and one E2E test to fix and we can merge this :)

Comment thread code/core/src/components/components/Button/Button.stories.tsx
Comment thread code/core/src/components/components/Button/Button.tsx
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As we are changing the behaviour of Button, we want to make sure that disabled buttons do not call their event handlers. Could you please add a play function to the disabled story, where

  1. you click on the button
  2. you focus on the button and press Enter
  3. you check that onClick was never called

@Sidnioulz
Copy link
Copy Markdown
Member

@WioletaKolodziej I've pushed an updated version of your branch at #34166 (still crediting you for your contribution ;-)).

I'll make sure it gets merged soon. Closing this older PR as it has conflicts.

@Sidnioulz Sidnioulz closed this Mar 16, 2026
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.

[Bug]: Buttons should be aria-disabled instead of disabled

3 participants