Skip to content

Commit 7e3b942

Browse files
committed
feat(audit): Audit summary/detailed mode and blocker table pagination now support back button, and announce in SR
1 parent 6a6440d commit 7e3b942

4 files changed

Lines changed: 51 additions & 12 deletions

File tree

apps/frontend/src/components/BlockersTable.tsx

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
VisibilityState,
1010
} from "@tanstack/react-table";
1111
import * as API from "aws-amplify/api";
12-
import { useState, useMemo, ChangeEvent, ChangeEventHandler } from "react";
12+
import { useState, useMemo, useEffect, useRef, ChangeEvent, ChangeEventHandler } from "react";
1313
//import { formatDate } from "../utils";
1414
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
1515
import * as ToggleGroup from "@radix-ui/react-toggle-group";
@@ -42,7 +42,7 @@ import style from "./BlockersTable.module.scss";
4242
import { SkeletonBlockersTable } from "./Skeleton";
4343
import { StyledLabeledInput } from "./StyledLabeledInput";
4444
import { useDebouncedCallback } from 'use-debounce';
45-
import { Link } from "react-router-dom";
45+
import { Link, useSearchParams } from "react-router-dom";
4646
import { BlockersTableColumnToggle } from "./BlockersTableColumnToggle";
4747

4848
SyntaxHighlighter.registerLanguage("jsx", jsx);
@@ -101,8 +101,19 @@ declare module '@tanstack/table-core' {
101101

102102
export const BlockersTable = ({ auditId, isShared }: BlockersTableProps) => {
103103
const queryClient = useQueryClient();
104-
const [page, setPage] = useState(0);
105-
const [pageSize, setPageSize] = useState(10);
104+
const [searchParams, setSearchParams] = useSearchParams();
105+
const page = parseInt(searchParams.get("page") ?? "0", 10);
106+
const pageSize = parseInt(searchParams.get("pageSize") ?? "10", 10);
107+
108+
const setPage = (updater: number | ((p: number) => number)) => {
109+
setSearchParams((prev) => {
110+
const next = new URLSearchParams(prev);
111+
const newPage = typeof updater === "function" ? updater(page) : updater;
112+
if (newPage === 0) next.delete("page");
113+
else next.set("page", newPage.toString());
114+
return next;
115+
});
116+
};
106117

107118
const [selectedTags, setSelectedTags] = useState<Option[]>([]);
108119
const [availableTags, setAvailableTags] = useState<Option[]>([]); // Added to prevent content flicker while fetching
@@ -330,6 +341,23 @@ export const BlockersTable = ({ auditId, isShared }: BlockersTableProps) => {
330341
placeholderData: (previousData) => previousData,
331342
});
332343

344+
// Announce pagination changes to screen readers once the newly requested
345+
// page has actually loaded (data still holds the previous page until then).
346+
const announcedPageRef = useRef(page);
347+
const announcedPageSizeRef = useRef(pageSize);
348+
useEffect(() => {
349+
if (!data?.pagination) return;
350+
if (announcedPageRef.current !== page || announcedPageSizeRef.current !== pageSize) {
351+
setAnnounceMessage(
352+
`Showing ${data.blockers?.length ?? 0} of ${data.pagination.totalCount} blockers, page ${page + 1} of ${data.pagination.totalPages}`,
353+
"normal",
354+
true
355+
);
356+
}
357+
announcedPageRef.current = page;
358+
announcedPageSizeRef.current = pageSize;
359+
}, [data]);
360+
333361
const getElementTagFromContent = (content: string) => {
334362
const parser = new DOMParser();
335363
const extractedElementTag = `<${parser.parseFromString(content, "text/html").body?.firstChild?.nodeName.toLowerCase()}>`;
@@ -716,7 +744,13 @@ export const BlockersTable = ({ auditId, isShared }: BlockersTableProps) => {
716744
};
717745

718746
const handlePageSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {
719-
setPageSize(parseInt(e.target.value));
747+
setSearchParams((prev) => {
748+
const next = new URLSearchParams(prev);
749+
const size = e.target.value;
750+
if (size === "10") next.delete("pageSize");
751+
else next.set("pageSize", size);
752+
return next;
753+
});
720754
};
721755

722756
const handleSortByUrl = () => {

apps/frontend/src/components/Navigation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export const Navigation = () => {
136136
const tabIndex = focusEl?.getAttribute("tabindex");
137137
focusEl?.setAttribute("tabindex", tabIndex ?? "-1");
138138
focusEl?.focus();
139-
}, [location]);
139+
}, [location.pathname]);
140140

141141
const isAuthRoute =
142142
location.pathname.startsWith("/login") ||

apps/frontend/src/routes/Audit.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useQuery, useQueryClient } from "@tanstack/react-query";
22
import { formatDate, useGlobalStore, unformatId } from "../utils";
33
import * as API from "aws-amplify/api";
4-
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
4+
import { Link, useLocation, useNavigate, useParams, useSearchParams } from "react-router-dom";
55
const apiClient = API.generateClient();
66
import { useEffect, useState, ChangeEvent } from "react";
77
import {
@@ -99,7 +99,16 @@ export const Audit = () => {
9999
const isShared = location.pathname.startsWith("/shared/");
100100
const isQuickScan = location.pathname.startsWith("/quick-scans/");
101101
const { setAnnounceMessage } = useGlobalStore();
102-
const { blockersTableView, setBlockersTableView } = useGlobalStore();
102+
const [searchParams, setSearchParams] = useSearchParams();
103+
const blockersTableView = searchParams.get("view") === "detailed" ? "detailed" : "summary";
104+
const setBlockersTableView = (value: string) => {
105+
setSearchParams((prev) => {
106+
const next = new URLSearchParams(prev);
107+
if (value === "detailed") next.set("view", "detailed");
108+
else next.delete("view"); // "summary" is the default, keep the URL clean
109+
return next;
110+
});
111+
};
103112

104113
useEffect(() => {
105114
if (emailNotifications)

apps/frontend/src/utils/useGlobalStore.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ interface EqualifyState {
2020
auditsTableCreatedByView: string;
2121
setAuditsTableCreatedByView: (val:string) => void;
2222
// blockers table
23-
blockersTableView: string;
24-
setBlockersTableView: (val: string) => void;
2523
blockerTableColumnVisibility: VisibilityState;
2624
setBlockerTableColumnVisibility: OnChangeFn<VisibilityState>;
2725
// screen reader announcer
@@ -44,8 +42,6 @@ export const useGlobalStore = create<EqualifyState>()(
4442
setAuditsTableView: (val) => set(() => ({ auditsTableView: val })),
4543
auditsTableCreatedByView: "user",
4644
setAuditsTableCreatedByView: (val) => set(() => ({auditsTableCreatedByView : val})),
47-
blockersTableView: "summary",
48-
setBlockersTableView: (val) => set(() => ({ blockersTableView: val })),
4945
authenticated: false,
5046
setAuthenticated: (val) => set(() => ({ authenticated: val })),
5147
ssoAuthenticated: false,

0 commit comments

Comments
 (0)