Skip to content
Open
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"reselect": "^5.1.1",
"screenfull": "^6.0.2",
"tippy.js": "^6.3.7",
"tiptap-extension-code-block-prism": "^0.0.2-e",
"tiptap-extension-code-block-prism": "0.0.2-e",
"typesafe-actions": "^5.1.0",
"web-vitals": "^2.1.4"
},
Expand Down Expand Up @@ -105,7 +105,7 @@
},
"devDependencies": {
"@axe-core/playwright": "^4.11.3",
"@playwright/test": "^1.60.0",
"@playwright/test": "1.61.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Modal } from "@mantine/core";
import { useState, cloneElement, MouseEvent, ReactElement } from "react";
import { cloneElement, MouseEvent, ReactElement, useState } from "react";

import { Exercise } from "@/types/exercises";

Expand Down Expand Up @@ -34,6 +34,7 @@ export const ExercisePreviewModal = ({
size="auto"
centered
title={questionName ? <span className={styles.nameChip}>{questionName}</span> : undefined}
closeButtonProps={{ "aria-label": "Close exercise preview" }}
>
<div className={styles.stage}>
<ExercisePreview htmlsrc={htmlsrc} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const {
} = vi.hoisted(() => ({
hookReturn: {
exercises: [
{ id: 1, name: "Ex One", question_type: "mchoice", qnumber: "Q1" },
{ id: 2, name: "Ex Two", question_type: "activecode", qnumber: "Q2" }
{ id: 1, name: "Ex One", author: "Author One", question_type: "mchoice", qnumber: "Q1" },
{ id: 2, name: "Ex Two", author: "Author Two", question_type: "activecode", qnumber: "Q2" }
],
pagination: { total: 2, page: 0, limit: 20, pages: 1 },
loading: false,
Expand Down Expand Up @@ -157,6 +157,7 @@ describe("SmartSearchExercises", () => {
renderWithMantine(<SmartSearchExercises />);
expect(screen.getByRole("heading", { name: "Search exercises" })).toBeInTheDocument();
expect(screen.getByText("Showing 2 of 2 exercises")).toBeInTheDocument();
expect(screen.queryByText("2 of 2 results shown.")).not.toBeInTheDocument();
});

it("returns to the list view from the toolbar back button", async () => {
Expand Down Expand Up @@ -217,4 +218,11 @@ describe("SmartSearchExercises", () => {
expect(monoCells.length).toBe(2);
expect(monoCells[0].textContent).toBe("Ex One");
});

it("does not duplicate visible Name and Author cell text in title attributes", () => {
renderWithMantine(<SmartSearchExercises />);

expect(screen.getByText("Ex One")).not.toHaveAttribute("title");
expect(screen.getByText("Author One")).not.toHaveAttribute("title");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {
Switch,
TagsInput,
Text,
Tooltip,
VisuallyHidden
Tooltip
} from "@mantine/core";
import { datasetSelectors } from "@store/dataset/dataset.logic";
import {
Expand Down Expand Up @@ -53,11 +52,7 @@ type FilterMetaValue = { value: string | string[] | null };
const MATCH_CONTAINS = "contains";
const MATCH_IN = "in";

const renderEllipsis = (text?: string | null) => (
<div className={styles.ellipsisText} title={text ?? ""}>
{text}
</div>
);
const renderEllipsis = (text?: string | null) => <div className={styles.ellipsisText}>{text}</div>;

export const SmartSearchExercises = ({
setCurrentEditExercise,
Expand Down Expand Up @@ -284,7 +279,7 @@ export const SmartSearchExercises = ({
filter: { variant: "text", placeholder: "Search by name" }
},
cell: ({ row }) => (
<div className={styles.monoName} data-mono-name="" title={row.original.name ?? ""}>
<div className={styles.monoName} data-mono-name="">
{row.original.name}
</div>
)
Expand Down Expand Up @@ -381,7 +376,7 @@ export const SmartSearchExercises = ({
>
<TagsInput
className={styles.search}
label={<VisuallyHidden>Search exercises</VisuallyHidden>}
aria-label="Search exercises"
value={globalTerms}
onChange={(terms) => onGlobalFilterChange(terms.join(" "))}
placeholder="Search terms…"
Expand Down Expand Up @@ -474,6 +469,7 @@ export const SmartSearchExercises = ({
ariaLabel="Search exercises"
fillHeight
scrollSentinelRef={sentinelRef}
showFilterResultsStatus={false}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export interface DataGridProps<T> {
loading?: boolean;
emptyMessage?: React.ReactNode;
filterResultsLabel?: string;
showFilterResultsStatus?: boolean;
filterResultsTotal?: number;
tableFrameClassName?: string;
ariaLabel?: string;
Expand Down Expand Up @@ -190,6 +191,7 @@ export function DataGrid<T>({
initialSorting = [],
loading = false,
emptyMessage = "Nothing here yet",
showFilterResultsStatus = true,
filterResultsLabel = "results",
filterResultsTotal = data.length,
tableFrameClassName,
Expand Down Expand Up @@ -251,7 +253,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 announcesFilterResults =
showFilterResultsStatus && (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
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ describe("course-aware browser history", () => {
}
);

it("does not redirect the Vite development shell without course metadata", () => {
expect(reconcileServerCourse("", "course-2", "/builder/22")).toBeNull();
expect(syncCourseHistoryEntry("", "/builder/22")).toBeNull();
expect(getRestoredCourseDestination(true, "", "/builder/22")).toBeNull();
expect(sessionStorage.getItem("assignment-builder.active-course")).toBeNull();
});

it("redirects a stale grader entry to the grader assignment list", () => {
syncCourseHistoryEntry("course-1", "/grader/22");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ export const reconcileServerCourse = (
serverCourse: string | undefined,
pathname: string
): string | null => {
if (!serverCourse || serverCourse === pageCourse) return null;
// The Vite development shell intentionally has no course metadata. In that
// environment the authenticated API can still report a current course, but
// treating it as a mismatch creates a redirect loop between the production
// route and the Vite client route.
if (!pageCourse || !serverCourse || serverCourse === pageCourse) return null;

markCourseSwitch(serverCourse);
return getCourseSwitchDestination(pathname);
Expand All @@ -93,6 +97,8 @@ export const reconcileServerCourse = (
* from another course is stale and must not restore an assignment-specific URL.
*/
export const syncCourseHistoryEntry = (currentCourse: string, pathname: string): string | null => {
if (!currentCourse) return null;

const entryCourse = getHistoryState()[HISTORY_COURSE_KEY];
const activeCourse = getStoredCourse();

Expand Down Expand Up @@ -137,7 +143,7 @@ export const getRestoredCourseDestination = (
pageCourse: string,
pathname: string
): string | null => {
if (!persisted) return null;
if (!persisted || !pageCourse) return null;

const activeCourse = getStoredCourse();
const activeCourseMismatch = Boolean(activeCourse && activeCourse !== pageCourse);
Expand Down
Loading