Skip to content

[table] Improve a11y for the pagination buttons - #49112

Merged
silviuaavram merged 9 commits into
mui:masterfrom
silviuaavram:fix/table-actions-a11y-improvements
Sep 14, 2026
Merged

[table] Improve a11y for the pagination buttons#49112
silviuaavram merged 9 commits into
mui:masterfrom
silviuaavram:fix/table-actions-a11y-improvements

Conversation

@silviuaavram

@silviuaavram silviuaavram commented Sep 8, 2026

Copy link
Copy Markdown
Member

Wrap buttons in Tooltip

Replace native title hints on TablePaginationActions with Tooltip so action labels appear on hover and keyboard focus.

Restore focus when buttons become disabled

When a focused action becomes disabled, move focus to the nearest enabled action instead of losing it to the document body. For example, navigating to the last page moves focus from Next to Previous. The search also handles actions disabled independently through slotProps and respects focus moved elsewhere by the application.

Preserves accessible labels, custom titles, button slots, refs, and RTL behavior.

@silviuaavram
silviuaavram requested review from ZeeshanTamboli and siriwatknp and a lite review from Copilot September 8, 2026 08:40
@silviuaavram silviuaavram added accessibility a11y scope: table Changes related to the table. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. labels Sep 8, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Sep 8, 2026

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/material 🔺+901B(+0.17%) 🔺+309B(+0.20%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

Copilot AI left a comment

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.

🔵 Needs a closer look

It changes focus-management behavior in a core component and should get final human validation across real-world browser/AT scenarios despite the added tests.

Pull request overview

This PR updates TablePaginationActions to replace native title attributes with MUI Tooltip for action labels and adds focus management so keyboard focus moves to the nearest enabled pagination action when the currently focused action becomes disabled.

Changes:

  • Wrap pagination action buttons with Tooltip while preserving aria-label behavior, RTL slot mapping, and slot customization.
  • Add layout-effect-based focus recovery when a focused action becomes disabled (including via slotProps).
  • Expand unit/browser-only coverage for tooltip visibility, slot/title preservation, and focus management scenarios.
File summaries
File Description
packages/mui-material/src/TablePaginationActions/TablePaginationActions.js Replace native titles with Tooltip, add focus tracking + focus relocation when a focused action becomes disabled, and preserve slot/ref behavior.
packages/mui-material/src/TablePaginationActions/TablePaginationActions.test.js Add tests for tooltip behavior (hover/focus), custom slot/title preservation (LTR/RTL), and non-jsdom focus management behaviors.
Review details

Suppressed comments (1)

packages/mui-material/src/TablePaginationActions/TablePaginationActions.js:162

  • getItemAriaLabel('last', page) is currently evaluated in the slotProps destructuring even when showLastButton is false, which does extra work for a button that won’t render. Guarding the default initializer avoids calling getItemAriaLabel when the last button is hidden.
  const { title: lastButtonTitle = getItemAriaLabel('last', page), ...lastButtonSlotProps } =
    (isRtl ? slotProps.firstButton : slotProps.lastButton) ?? {};
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/mui-material/src/TablePaginationActions/TablePaginationActions.js Outdated
@silviuaavram
silviuaavram force-pushed the fix/table-actions-a11y-improvements branch from 59d49d8 to fba526f Compare September 8, 2026 13:41
@silviuaavram
silviuaavram requested a balanced review from Copilot September 9, 2026 06:45

Copilot AI left a comment

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.

🟡 Changes recommended

Custom function slots without forwarded refs break tooltip display and focus restoration on supported React versions.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread packages/mui-material/src/TablePaginationActions/TablePaginationActions.js Outdated

@mnajdova mnajdova left a comment

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.

Image

Can we just show one Tooltip at a time? If one button is focused and the other hovered they overlap. I know in Base UI we have this feature about tooltip provider, I don't remember if we have something similar in Material UI, or if it is easy to achieve. If not, let's add comment to fix this in the next major once we migrate the Tooltip to Base UI.

import useForkRef from '../utils/useForkRef';
import { getTablePaginationActionsUtilityClass } from './tablePaginationActionsClasses';

function isDisabled(button) {

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.

I can see this logic in other places too, could be useful to have it extracted in @mui/material/utils so potentially it can be reused.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I couldn't find other places, found a couple which partially use some of it, but that's it.

other.onBlur?.(event);
};

useEnhancedEffect(() => {

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.

Let's add a comment what the effect is about.

Comment thread packages/mui-material/src/TablePaginationActions/TablePaginationActions.js Outdated

Copilot AI left a comment

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.

🟡 Changes recommended

The generated PropTypes additions need corresponding TypeScript declarations and regeneration.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

@silviuaavram
silviuaavram force-pushed the fix/table-actions-a11y-improvements branch from 410002f to 7ef866a Compare September 10, 2026 08:28
@silviuaavram
silviuaavram requested review from mnajdova and a balanced review from Copilot September 10, 2026 08:28

Copilot AI left a comment

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.

🟡 Changes recommended

A nullish custom aria-label can now leave an action without an accessible name.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

}

// Prefer following actions, then fall back to preceding actions in reverse order.
const nextButton = [...buttons.slice(index + 1), ...buttons.slice(0, index).reverse()].find(

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.

Much simpler 👌

@mnajdova mnajdova left a comment

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.

Logic looks good, I left few comments on improving the docs. Please resolve them before merging.

### Custom pagination actions

The `ActionsComponent` prop of the `TablePagination` component allows the implementation of custom actions.
A replacement action component is responsible for its own tooltips and focus management, unless it composes the built-in `TablePaginationActions` component.

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.

Suggested change
A replacement action component is responsible for its own tooltips and focus management, unless it composes the built-in `TablePaginationActions` component.
The replacement action component is responsible for its own tooltips and focus management, unless it composes the built-in `TablePaginationActions` component.

The default buttons include tooltips.
When replacing `firstButton`, `previousButton`, `nextButton`, or `lastButton` through `slots.actions`, the custom component receives a `title` prop and is responsible for rendering its own tooltip.

When an update to pagination props or action slot props disables the focused action, `TablePaginationActions` moves focus to the next enabled action in DOM order, or searches backward if none follows.

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.

Suggested change
When an update to pagination props or action slot props disables the focused action, `TablePaginationActions` moves focus to the next enabled action in DOM order, or searches backward if none follows.
When an update to pagination props or action slot props disables the focused action, `TablePaginationActions` moves focus to the nearest enabled action in the DOM order.

Actions with `tabIndex={-1}` are skipped.
Focus stays where it is if no action is available or your application has already moved focus elsewhere.

Custom button slots participate in focus restoration by forwarding the supplied `data-mui-pagination-action` prop to their focusable element.

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.

Suggested change
Custom button slots participate in focus restoration by forwarding the supplied `data-mui-pagination-action` prop to their focusable element.
In order for custom button slots to participate in focus restoration you need to pass the `data-mui-pagination-action` prop to their focusable element.

Maybe also list the available values for it, as they are not documented anywhere at this moment.

@silviuaavram
silviuaavram force-pushed the fix/table-actions-a11y-improvements branch from ecff5e7 to d15266c Compare September 11, 2026 13:19
@silviuaavram
silviuaavram force-pushed the fix/table-actions-a11y-improvements branch from d15266c to f089ea2 Compare September 14, 2026 09:35
@silviuaavram
silviuaavram merged commit 7b06515 into mui:master Sep 14, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accessibility a11y scope: table Changes related to the table. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants