diff --git a/docs/data/material/customization/focus-visible/demos/full/FullFocusVisibleDemo.tsx b/docs/data/material/customization/focus-visible/demos/full/FullFocusVisibleDemo.tsx index f7103078aa0607..272830eb828cc9 100644 --- a/docs/data/material/customization/focus-visible/demos/full/FullFocusVisibleDemo.tsx +++ b/docs/data/material/customization/focus-visible/demos/full/FullFocusVisibleDemo.tsx @@ -33,6 +33,7 @@ import TableBody from '@mui/material/TableBody'; import TableRow from '@mui/material/TableRow'; import TableCell from '@mui/material/TableCell'; import TableSortLabel from '@mui/material/TableSortLabel'; +import TablePagination from '@mui/material/TablePagination'; import Slider from '@mui/material/Slider'; import Link from '@mui/material/Link'; import Breadcrumbs from '@mui/material/Breadcrumbs'; @@ -429,6 +430,18 @@ export default function FullFocusVisibleDemo() { )} /> + + + diff --git a/packages/mui-material/src/TablePagination/TablePagination.js b/packages/mui-material/src/TablePagination/TablePagination.js index 69c5797a865b51..a00e57f1154e25 100644 --- a/packages/mui-material/src/TablePagination/TablePagination.js +++ b/packages/mui-material/src/TablePagination/TablePagination.js @@ -8,6 +8,8 @@ import composeClasses from '@mui/utils/composeClasses'; import { styled } from '../zero-styled'; import memoTheme from '../utils/memoTheme'; import { useDefaultProps } from '../DefaultPropsProvider'; +import { applyInsetFocusVisible } from '../styles/focusVisible'; +import { buttonBaseClasses } from '../ButtonBase'; import InputBase from '../InputBase'; import MenuItem from '../MenuItem'; import Select from '../Select'; @@ -57,6 +59,9 @@ const TablePaginationToolbar = styled(Toolbar, { [`& .${tablePaginationClasses.actions}`]: { flexShrink: 0, marginLeft: 20, + ...(theme.focusVisible && { + [`& .${buttonBaseClasses.root}`]: applyInsetFocusVisible(1), + }), }, })), ); @@ -104,11 +109,17 @@ const TablePaginationSelect = styled(Select, { }); const TablePaginationInputBase = styled(InputBase)( - memoTheme(({ theme }) => ({ - [`& .${tablePaginationClasses.select}:focus`]: { - backgroundColor: (theme.vars || theme).palette.action.focus, - }, - })), + memoTheme(({ theme }) => + theme.focusVisible + ? { + [`& .${tablePaginationClasses.select}:focus-visible`]: theme.focusVisible, + } + : { + [`& .${tablePaginationClasses.select}:focus`]: { + backgroundColor: (theme.vars || theme).palette.action.focus, + }, + }, + ), ); const TablePaginationMenuItem = styled(MenuItem, { @@ -276,7 +287,9 @@ const TablePagination = React.forwardRef(function TablePagination(inProps, ref) {rowsPerPageOptions.length > 1 && ( })} + {...(!selectProps.variant && { + input: , + })} value={rowsPerPage} onChange={onRowsPerPageChange} id={selectId} diff --git a/packages/mui-material/src/TablePagination/TablePagination.test.js b/packages/mui-material/src/TablePagination/TablePagination.test.js index 7572f41258bdc6..4cc42d040d94f5 100644 --- a/packages/mui-material/src/TablePagination/TablePagination.test.js +++ b/packages/mui-material/src/TablePagination/TablePagination.test.js @@ -2,7 +2,15 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as React from 'react'; import { spy } from 'sinon'; import PropTypes from 'prop-types'; -import { fireEvent, createRenderer, isJsdom, screen } from '@mui/internal-test-utils'; +import { + act, + fireEvent, + createRenderer, + focusVisible, + isJsdom, + screen, + simulatePointerDevice, +} from '@mui/internal-test-utils'; import TableFooter from '@mui/material/TableFooter'; import TableCell from '@mui/material/TableCell'; import TableRow from '@mui/material/TableRow'; @@ -11,6 +19,7 @@ import { inputClasses } from '@mui/material/Input'; import { outlinedInputClasses } from '@mui/material/OutlinedInput'; import { filledInputClasses } from '@mui/material/FilledInput'; import { createTheme, ThemeProvider } from '@mui/material/styles'; +import { inputBaseClasses } from '@mui/material/InputBase'; import IconButton, { iconButtonClasses } from '@mui/material/IconButton'; import { svgIconClasses } from '@mui/material/SvgIcon'; import { createSvgIcon } from '@mui/material/utils'; @@ -566,6 +575,88 @@ describe('', () => { }); }); + describe('theme: focusVisible', () => { + function setup(theme) { + render( + + + + + + + +
+
, + ); + + return screen.getByRole('combobox'); + } + + it.skipIf(isJsdom())('keeps the focus tint on the select without the theme ring', () => { + const combobox = setup(createTheme()); + + act(() => combobox.focus()); + + expect(combobox).toHaveComputedStyle({ backgroundColor: 'rgba(0, 0, 0, 0.12)' }); + }); + + it.skipIf(isJsdom())('drops the focus tint on the select with the theme ring', () => { + const combobox = setup(createTheme({ focusVisible: true })); + + act(() => combobox.focus()); + + expect(combobox).toHaveComputedStyle({ backgroundColor: 'rgba(0, 0, 0, 0)' }); + }); + + it.skipIf(isJsdom())('rings the select on keyboard focus instead of the InputBase root', () => { + const combobox = setup(createTheme({ focusVisible: true })); + + focusVisible(combobox); + + expect(combobox).toHaveComputedStyle({ outlineStyle: 'solid', outlineWidth: '2px' }); + expect(combobox.closest(`.${inputBaseClasses.root}`)).toHaveComputedStyle({ + outlineStyle: 'none', + }); + }); + + it.skipIf(isJsdom())('insets the action button ring so the root cannot clip it', () => { + render( + + + + + + + +
+
, + ); + const nextButton = screen.getByRole('button', { name: 'Go to next page' }); + + simulatePointerDevice(); + focusVisible(nextButton); + + expect(nextButton).toHaveComputedStyle({ outlineStyle: 'solid', outlineOffset: '-2px' }); + }); + }); + describe('prop: rowsPerPage', () => { it('should display max number of rows text when prop is -1', () => { const { container } = render(