diff --git a/src/components/mui/MuiBaseCustomTheme.js b/src/components/mui/MuiBaseCustomTheme.js
index 240b8ade..68c82cb3 100644
--- a/src/components/mui/MuiBaseCustomTheme.js
+++ b/src/components/mui/MuiBaseCustomTheme.js
@@ -13,6 +13,19 @@
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
+// pagination controls: grey on hover, darker grey on keyboard focus
+const paginationButtonStyles = (theme) => ({
+ transition: theme.transitions.create("background-color", {
+ duration: theme.transitions.duration.shortest
+ }),
+ "&:hover": { backgroundColor: theme.palette.action.hover },
+ "@media (hover: none)": { "&:hover": { backgroundColor: "transparent" } },
+ "&:focus-visible, &:has(:focus-visible)": {
+ backgroundColor: theme.palette.action.focus,
+ outline: "none"
+ }
+});
+
export const MuiBaseCustomTheme = {
// must match the apps' html font-size so MUI's rem values render at their designed px
typography: {
@@ -21,7 +34,7 @@ export const MuiBaseCustomTheme = {
components: {
MuiOutlinedInput: {
styleOverrides: {
- root: {
+ root: ({ theme }) => ({
"--TextField-brandBorderColor": "#000",
"--TextField-brandBorderHoverColor": "#000",
"--TextField-brandBorderFocusedColor": "#000",
@@ -33,8 +46,26 @@ export const MuiBaseCustomTheme = {
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: "var(--TextField-brandBorderFocusedColor)"
+ },
+ // per-page select: keep the border grey on rest, hover and focus
+ "&.pagination-button": {
+ ...paginationButtonStyles(theme),
+ "--TextField-brandBorderColor": theme.palette.divider,
+ "--TextField-brandBorderHoverColor": theme.palette.divider,
+ "--TextField-brandBorderFocusedColor": theme.palette.divider,
+ "&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderWidth: 1 }
}
- }
+ })
+ }
+ },
+ MuiIconButton: {
+ styleOverrides: {
+ root: ({ theme }) => ({ "&.pagination-button": paginationButtonStyles(theme) })
+ }
+ },
+ MuiTypography: {
+ styleOverrides: {
+ root: ({ theme }) => ({ "&.pagination-button": paginationButtonStyles(theme) })
}
},
MuiSelect: {
diff --git a/src/components/mui/__tests__/mui-table-custom-pagination.test.js b/src/components/mui/__tests__/mui-table-custom-pagination.test.js
index 59dccf72..ff75ed3a 100644
--- a/src/components/mui/__tests__/mui-table-custom-pagination.test.js
+++ b/src/components/mui/__tests__/mui-table-custom-pagination.test.js
@@ -17,7 +17,7 @@ jest.mock("i18n-react/dist/i18n-react", () => ({
}));
import React from "react";
-import { render, screen, fireEvent } from "@testing-library/react";
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import CustomTablePagination from "../tables/components/CustomTablePagination";
@@ -84,4 +84,16 @@ describe("CustomTablePagination", () => {
expect(onPageChange).toHaveBeenCalledWith(4);
});
+
+ test("returns keyboard focus to the page button when the slider collapses", async () => {
+ setup({ currentPage: 3, totalRows: 100, perPage: 10 });
+ const pageButton = screen.getByRole("button", { name: "mui_table.page_of" });
+ await userEvent.click(pageButton);
+
+ await userEvent.tab();
+ expect(screen.getByRole("slider")).toHaveFocus();
+ await userEvent.keyboard("{ArrowRight}");
+
+ await waitFor(() => expect(pageButton).toHaveFocus());
+ });
});
diff --git a/src/components/mui/tables/components/CustomTablePagination.js b/src/components/mui/tables/components/CustomTablePagination.js
index 0ea1ce2c..244f3d23 100644
--- a/src/components/mui/tables/components/CustomTablePagination.js
+++ b/src/components/mui/tables/components/CustomTablePagination.js
@@ -86,12 +86,12 @@ const CustomTablePagination = ({
size="small"
value={perPage}
onChange={handleRowsPerPageChange}
+ className="pagination-button"
sx={{
height: 40,
borderRadius: "20px",
minWidth: 80,
bgcolor: "background.paper",
- "& .MuiOutlinedInput-notchedOutline": { borderColor: "divider" },
"& .MuiSelect-select": { color: "text.secondary" }
}}
inputProps={{ "aria-label": T.translate("mui_table.rows_per_page") }}
diff --git a/src/components/mui/tables/components/SliderPagination.js b/src/components/mui/tables/components/SliderPagination.js
index fc80ab61..f00c4418 100644
--- a/src/components/mui/tables/components/SliderPagination.js
+++ b/src/components/mui/tables/components/SliderPagination.js
@@ -12,7 +12,7 @@
* */
import * as React from "react";
-import { useCallback, useRef, useState } from "react";
+import { useCallback, useEffect, useRef, useState } from "react";
import T from "i18n-react/dist/i18n-react";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
@@ -28,6 +28,11 @@ const SliderPagination = ({ currentPage, totalRows, perPage, onPageChange, initi
const [expanded, setExpanded] = useState(initialExpanded);
const [dragValue, setDragValue] = useState(currentPage);
const timeoutRef = useRef(null);
+ const pageButtonRef = useRef(null);
+ const sliderBoxRef = useRef(null);
+
+ // cancel a pending collapse if the component unmounts first
+ useEffect(() => () => clearTimeout(timeoutRef.current), []);
const togglePill = () => {
if (expanded) {
@@ -43,7 +48,11 @@ const SliderPagination = ({ currentPage, totalRows, perPage, onPageChange, initi
const onSliderCommit = useCallback(
(_ev, value) => {
onPageChange(value);
- timeoutRef.current = setTimeout(() => setExpanded(false), 150);
+ timeoutRef.current = setTimeout(() => {
+ // don't leave keyboard focus on the slider once it's hidden
+ if (sliderBoxRef.current?.contains(document.activeElement)) pageButtonRef.current?.focus();
+ setExpanded(false);
+ }, 150);
},
[onPageChange]
);
@@ -79,21 +88,26 @@ const SliderPagination = ({ currentPage, totalRows, perPage, onPageChange, initi
onClick={prev}
disabled={currentPage <= 1}
aria-label={T.translate("mui_table.previous_page")}
+ disableFocusRipple
+ className="pagination-button"
>
{/* explicit px so a host page's root font-size reset can't shrink this */}
@@ -130,6 +148,8 @@ const SliderPagination = ({ currentPage, totalRows, perPage, onPageChange, initi
onClick={next}
disabled={currentPage >= totalPages}
aria-label={T.translate("mui_table.next_page")}
+ disableFocusRipple
+ className="pagination-button"
>