Skip to content

feat: add calculator functionality with customizable permissions and … - #398

Merged
saa938 merged 3 commits into
AP-Students:mainfrom
Famousmaster206:desmos-graphing-calculator
Sep 5, 2026
Merged

feat: add calculator functionality with customizable permissions and …#398
saa938 merged 3 commits into
AP-Students:mainfrom
Famousmaster206:desmos-graphing-calculator

Conversation

@Famousmaster206

Copy link
Copy Markdown
Contributor

Description

Adds the Built-in Calculator feature from #333 (the reference-sheet half of that issue is tracked separately). Assessment authors can enable a Desmos calculator (four-function, scientific, or graphing) for MCQ tests and FRQs, set an assessment-wide default, and override it per question — so a test can be all-calculator, all-non-calculator, or a mix (e.g. Part A non-calculator, Part B calculator).

Section/set-level overrides from the issue are intentionally out of scope: neither UnitTest (flat questions[]) nor FRQTemplate (sectionLabel is a display string, not a grouping boundary) has a structural "section" today, so this implements the two levels that actually exist — assessment default, overridden per question — which covers every example in the issue.

Pull request type

Please check the type of change your PR introduces:

  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

A summary of the change, anything else that will help review this PR

Permission model (src/lib/calculator.ts): a pure resolveCalculatorPermission(assessmentDefault, questionOverride) shared by both MCQ and FRQ. Question setting overrides assessment default; "inherit"/absent falls through; an assessment or question with nothing configured resolves to false (safe default for existing assessments).

Calculator overlay (src/components/questions/CalculatorPanel.tsx): a Radix Sheet side panel embedding the resolved Desmos testing-calculator URL in a sandboxed (allow-scripts allow-same-origin allow-forms) iframe, following the same sandboxing pattern already used for the Discord widget. The iframe is only mounted while the panel is open, so closing it — including the automatic close described below — discards any in-progress calculator state.

Auto-close on navigation: both testRenderer.tsx (MCQ) and frq/testRenderer.tsx (FRQ) force-close the panel the moment the resolved permission for the current question becomes false, unmounting the iframe so a student can't carry a live calculator into a question it isn't allowed on, and can't reopen it via a focused-but-blocked button.

Student experience: a Calculator button sits in the same toolbar row as the existing elimination-tool button (MCQ) / next to Exit Test (FRQ). It stays visible but aria-disabled (not disabled, so it keeps an accessible label and can't be silently skipped by assistive tech) on calculator-inactive questions, per the issue's explicit recommendation against hiding it.

Author experience: MCQ test editor and FRQ editor both get a "Default Calculator" select (allowed/not-allowed) plus a "Calculator Type" select when allowed, in the existing assessment-settings area. Per-question override selects (inherit/allowed/not-allowed) are added next to the MCQ Topic field and inside each FRQ QuestionCard, reusing each editor's existing update pattern.

How Has This Been Tested? How can the reviewer test it?

npx tsc --noEmit and npx eslint are clean on every changed/new file (only pre-existing, unrelated pathname-possibly-null errors remain in files this PR didn't touch the affected lines of).

Reviewer steps:

  1. As an admin, open an MCQ test editor. Set "Default Calculator" to Allowed and type to Graphing. On one question, set the per-question override to "Not allowed." Save.
  2. Take that test as a student. Confirm the Calculator button is enabled on every question except the overridden one, where it's visibly grayed out with a tooltip/accessible label explaining why.
  3. Open the calculator on an allowed question, confirm the graphing Desmos iframe loads and is interactive (mouse, touch, keyboard). Close it (X, Escape, outside click) and confirm focus returns to the Calculator button and the question/selected answer is unchanged.
  4. Navigate from that allowed question, with the calculator open, to the overridden question — confirm the panel closes automatically and the button becomes disabled immediately.
  5. Navigate back to an allowed question and confirm the calculator opens fresh (no stale state).
  6. Repeat steps 1–5 for an FRQ template, using the "Scientific" calculator type, confirming an in-progress response is untouched by opening/closing the calculator.
  7. Confirm an existing test/FRQ with no calculator fields set at all shows the button disabled everywhere (safe default), and that timers and submission still work normally with the calculator panel open.

Checklist

  • I have performed a self-review of my own code

Famousmaster206 and others added 3 commits September 2, 2026 20:44
Three fixes to the built-in calculator (AP-Students#333), all reproduced and verified
against the running app.

The student MCQ test page read the test document but never passed
`calculatorDefault`/`calculatorType` to `TestRenderer`. Both props are
optional, so nothing failed to compile — the assessment-level default was
just silently dropped for every student, and the calculator type always fell
back to graphing. Only the admin preview, which does pass them, behaved as
authored. The FRQ path was already correct.

Opening the panel focused its first tabbable node, which is the cross-origin
Desmos iframe. Keydowns inside a cross-origin frame never reach this
document, so Escape stopped dismissing the panel the moment it opened, and
Tab could not get back out either. The panel now takes focus itself on open,
which keeps the dismiss handler reachable, and hands focus back to the
Calculator button on close.

The panel was a modal sheet, so `pointer-events: none` on the body meant a
student had to close the calculator before selecting an answer or moving to
the next question — and the navigate-away auto-close could never fire,
because navigating was impossible while it was open. It is now non-modal
with no backdrop, stops above the test footer so navigation stays clickable,
and ignores outside interactions so answering a question does not dismiss it.

`SheetContent` grows an optional `overlay` prop for this; it defaults to true,
so the navbar and peer-grading sheets are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CTRd1fAGtNecSB13TdCsE6
…culator

Ten files conflicted, all because main now carries the reference-sheet half
of AP-Students#333 (PR AP-Students#397), which adds an assessment-level setting, a per-assessment
toolbar button, and a Radix side panel at exactly the seams the calculator
uses.

Every conflict was two independent features landing on adjacent lines, so
both sides were kept throughout. Three needed more than a concatenation:

- `normalizeFrqTemplate` — both sides re-stated the shared `sectionLabel` /
  `sectionSubtitle` assignments, which would have run twice.
- The admin MCQ editor and the FRQ editor — both sides consumed the same
  settings wrapper `<div>`. The calculator group closes its own, and the
  reference-sheet group gets a fresh one, so the two settings groups sit
  side by side in the toolbar row rather than nesting.
- `frq/testRenderer.tsx` — both features import a panel and a lucide icon
  from the same modules; the imports were combined, and the toolbar now
  carries Reference Sheet, then Calculator, then Exit Test.

Verified after the merge: `tsc --noEmit` clean, `eslint` clean, 70/70 tests,
`next build` successful, and both features present in every merged file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CTRd1fAGtNecSB13TdCsE6
@saa938
saa938 merged commit 318358e into AP-Students:main Sep 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants