Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,21 +244,23 @@ export const GraderAssignmentsPage: React.FC = () => {
return (
<>
{viewToggle}
<div className={styles.tableWrap} data-tour="grader-assignment-picker">
<DataGrid<AssignmentRow>
data={rows}
columns={columns}
getRowId={(r) => String(r.id)}
onRowClick={(row) => navigate(`/grader/${row.id}`)}
enableColumnFilters
enableSortingRemoval={false}
initialSorting={[{ id: "duedate", desc: false }]}
pagination={pagination}
onPaginationChange={setPagination}
ariaLabel="Assignments"
emptyMessage="No assignments match the current filters."
/>
</div>
<DataGrid<AssignmentRow>
data={rows}
columns={columns}
getRowId={(r) => String(r.id)}
onRowClick={(row) => navigate(`/grader/${row.id}`)}
enableColumnFilters
filterResultsLabel="assignments"
filterResultsTotal={allRows.length}
tableFrameClassName={styles.tableWrap}
enableSortingRemoval={false}
initialSorting={[{ id: "duedate", desc: false }]}
pagination={pagination}
onPaginationChange={setPagination}
ariaLabel="Assignments"
emptyMessage="No assignments match the current filters."
data-tour="grader-assignment-picker"
/>
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@ describe("GraderAssignmentsPage table pagination", () => {
expect(screen.getByText("Assignment 01")).toBeInTheDocument();
});

it("announces the number of assignments shown after filtering", () => {
renderWithMantine(<GraderAssignmentsPage />);

const status = screen.getByRole("status");

expect(status).toBeVisible();
expect(status).toHaveTextContent("30 of 30 assignments shown.");

fireEvent.change(screen.getByRole("textbox", { name: "Filter by Name" }), {
target: { value: "Assignment 01" }
});

expect(status).toHaveTextContent("1 of 30 assignments shown.");
});

it("does not duplicate visible descriptions in title attributes", () => {
const assignments = makeAssignments(1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,42 @@ describe("DataGrid", () => {
expect(screen.getByRole("textbox", { name: "Search assignments" })).toBeInTheDocument();
});

it("announces the number of results after global filtering", async () => {
renderGrid({
enableGlobalFilter: true,
globalFilterPlaceholder: "Search assignments",
filterResultsLabel: "assignments",
filterResultsTotal: 5
});

const status = screen.getByRole("status");

expect(status).toHaveAttribute("aria-live", "polite");
expect(status).toHaveAttribute("aria-atomic", "true");
expect(status).toBeVisible();
expect(status).toHaveTextContent("3 of 5 assignments shown.");

await userEvent.type(screen.getByRole("textbox", { name: "Search assignments" }), "Alp");

expect(status).toHaveTextContent("1 of 5 assignments shown.");
});

it("can render the filter status outside the framed table content", () => {
renderGrid({
enableGlobalFilter: true,
filterResultsLabel: "assignments",
tableFrameClassName: "table-frame"
});

const status = screen.getByRole("status");
const frame = document.querySelector(".table-frame");

expect(frame).toBeInTheDocument();
expect(frame).toContainElement(screen.getByRole("table"));
expect(frame).toContainElement(screen.getByText("Rows per page"));
expect(frame).not.toContainElement(status);
});

it("renders its own scroll area with a sentinel in fillHeight mode", () => {
const sentinelRef = vi.fn();
const { container } = renderGrid({ fillHeight: true, scrollSentinelRef: sentinelRef });
Expand Down Expand Up @@ -341,6 +377,23 @@ describe("DataGrid column filters", () => {
expect(screen.queryByText("Charlie")).not.toBeInTheDocument();
});

it("announces the number of results after column filtering", () => {
renderWithMantine(
<DataGrid<Row>
data={ROWS}
columns={FILTER_COLUMNS}
getRowId={(r) => r.id}
enableColumnFilters
/>
);

const status = screen.getByRole("status");

fireEvent.change(screen.getByPlaceholderText("filter-name"), { target: { value: "Alp" } });

expect(status).toHaveTextContent("1 of 3 results shown.");
});

it("labels filter controls after their column header", () => {
renderWithMantine(
<DataGrid<Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ export interface DataGridProps<T> {
initialSorting?: SortingState;
loading?: boolean;
emptyMessage?: React.ReactNode;
filterResultsLabel?: string;
filterResultsTotal?: number;
tableFrameClassName?: string;
ariaLabel?: string;
minWidth?: number;
fillHeight?: boolean;
Expand Down Expand Up @@ -187,6 +190,9 @@ export function DataGrid<T>({
initialSorting = [],
loading = false,
emptyMessage = "Nothing here yet",
filterResultsLabel = "results",
filterResultsTotal = data.length,
tableFrameClassName,
ariaLabel,
minWidth = 0,
fillHeight = false,
Expand Down Expand Up @@ -244,6 +250,8 @@ export function DataGrid<T>({

const resolvedPageCount = table.getPageCount();
const rows = table.getRowModel().rows;
const filteredRowCount = table.getFilteredRowModel().rows.length;
const announcesFilterResults = enableGlobalFilter || enableColumnFilters;
const tabbableRowId = rows.some((row) => row.id === keyboardRowId) ? keyboardRowId : rows[0]?.id;

// Column filters shrink the table from the inside, so a page index that came
Expand Down Expand Up @@ -422,25 +430,8 @@ export function DataGrid<T>({
</Table>
);

return (
<div
data-tour={rest["data-tour"]}
className={fillHeight ? styles.rootFill : undefined}
aria-busy={loading || undefined}
>
{enableGlobalFilter && (
<Group justify="flex-end" mb="sm">
<TextInput
value={globalFilter}
onChange={(e) => setGlobalFilter(e.currentTarget.value)}
placeholder={globalFilterPlaceholder}
aria-label={globalFilterPlaceholder}
leftSection={<Icon name="search" />}
size="sm"
/>
</Group>
)}

const tableContent = (
<>
{fillHeight ? (
<div className={styles.scrollFill}>
{scrollSentinelRef && (
Expand Down Expand Up @@ -489,6 +480,39 @@ export function DataGrid<T>({
/>
)}
</Group>
</>
);

return (
<div
data-tour={rest["data-tour"]}
className={fillHeight ? styles.rootFill : undefined}
aria-busy={loading || undefined}
>
{enableGlobalFilter && (
<Group justify="flex-end" mb="sm">
<TextInput
value={globalFilter}
onChange={(e) => setGlobalFilter(e.currentTarget.value)}
placeholder={globalFilterPlaceholder}
aria-label={globalFilterPlaceholder}
leftSection={<Icon name="search" />}
size="sm"
/>
</Group>
)}

{announcesFilterResults && !loading && (
<Text size="sm" c="dimmed" mb="xs" role="status" aria-live="polite" aria-atomic="true">
{filteredRowCount} of {filterResultsTotal} {filterResultsLabel} shown.
</Text>
)}

{tableFrameClassName ? (
<div className={tableFrameClassName}>{tableContent}</div>
) : (
tableContent
)}
</div>
);
}
Loading