Repository navigation
Add plain-language check explanations and accessible result tooltips - #582
Merged
Merged
Conversation
CheckExplanation had its own copy of the hover, focus and viewport handling in Tooltip, so fixes would have to be made twice. Tooltip now owns that behavior, including a real button trigger, an associated tooltip element and Escape to dismiss, which every tooltip gains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tooltips were positioned inside their trigger, so the published results section, which scrolls on its own, cut them off and gained a scrollbar. Fixed positioning takes them out of that section, and they now track the trigger while the page or a section scrolls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Escape only worked while the trigger had focus, so a tooltip opened by hover stayed up, and a tap on iOS, which doesn't focus a button, left touch users with no way to close it. Escape now works from anywhere while a tooltip is open, and a second tap or a tap elsewhere closes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explanations can run to 2,000 characters, which pushed tooltips past the viewport with no way to reach the rest, and long URLs overflowed the box. Tooltips now open upward when that gives them more room, limit their height to the space available, scroll when needed, focusably so keyboard users can too, and break long words. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reload shared a promise with the save, so if it failed after the save succeeded, the author was told the save had failed and the card kept the old text. It also cost a full read and rebuilt every check row for one string we already knew. The saved value now goes straight into the store, as next steps does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modal doesn't restore focus, so closing it left keyboard users at the top of the page. Now that saving keeps the row in place, the button that opened the modal is still there to go back to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Authors write explanations in a multi-line field and the tooltip kept their line breaks, but Requirements not met, its print summary and the editor card ran the lines together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its alias and parameters siblings declare @consumes, so a non-JSON request is turned away at routing. This one didn't. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
unmetRequirements was still described as the failed checks' display names, but it now carries the explanation when a check has one, and check_results gained an undocumented field. Hosts matching on those names would have stopped matching once an author added an explanation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The alias-or-name expression was repeated for the visible label and the tooltip button's accessible name in both results views, so a naming change could have left "About ..." out of step with the text beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The endpoint updated only the first match, while the client store and the alias and parameter endpoints update them all. With duplicate IDs, the second row showed the new text until a reload and then lost it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every check endpoint read the whole benefit and wrote all of it back, so a concurrent edit could be reverted. A parameter update also spent seconds generating an alias between its read and write, which could erase an explanation the client had been told was saved. Each endpoint now reads and writes only the checks in one transaction. Aliases are generated first, because a transaction must stay short and can rerun, and an alias written by hand meanwhile is kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An in-flight refetch could put the old explanation back in the store, and the next save from the reopened modal could then overwrite the new text. A refetch also gave every check a new reference, which remounted its row and closed any open modal. Saved explanations now win over a refetch that started first, the way next steps do, and reconciling by check ID keeps rows in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Focus stayed on the button behind the backdrop, so Tab walked the obscured page before reaching the dialog, which is portaled to the end of the body, and a screen reader announced nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging a text selection past the dialog edge fires a click on the backdrop, which closed the modal and discarded up to 2,000 characters without asking. With unsaved edits only Cancel closes it now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every failure said to try again, even a 404 for a check removed in another tab, so an author could retry forever with nothing logged. Rejected requests now show the server's reason, and every failure is logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each evaluation produced new objects, so For remounted every row. A keyboard user who tabbed to an explanation while results were updating lost the open tooltip, and focus dropped to the body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fixed tooltip kept tracking a trigger clipped by its scrolling ancestor, so it floated over other sections with nothing to point at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each scroll event measured the page and set a new position object, including scrolls of the tooltip's own content, which don't move the trigger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An alias was title-cased in results but raw in Requirements not met, CRM unmetRequirements and the explanation modal's label. CRM hosts now receive the same display name the crm-integrations docs describe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Title-casing an alias is meant for identifiers such as PrimaryResidenceRequirement, but it also rewrote prose: "Age 18-64" became "Age 18 64" in results, Next steps and CRM unmetRequirements, and an alias of only punctuation became empty, which dropped the failed check from Requirements not met. The editor's card now names a check the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows now stay mounted across evaluations, so a tooltip can outlive a change that moves its trigger or replaces its text. Neither fires a scroll or resize, which left it pointing at the wrong row with a stale height. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published results already kept their rows, but preview unmounted them whenever an evaluation started and rebuilt them from new objects, which closed an open explanation and dropped focus to the body. Both views now share the reconciled store. Preview's loading message sits beside the heading once there are rows, so they don't shift on every evaluation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving disabled the textarea and the focused Save button, so a keyboard user was left on the body when the failure said their edits were still there. The textarea is now read-only during a save, which lets it take focus back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The check only looked at ancestors with auto or scroll overflow, so it missed the window, which is what scrolls on a phone, and ancestors that clip with hidden or clip overflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
updateCustomBenefitChecks returns false for either a missing benefit or a missing check, so removing or editing a check on a deleted benefit said "Check not found in benefit", which the explanation modal shows to the author. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Alias and explanation updates matched checks null-safely, but parameter updates, removals and alias generation called equals on the stored ID, so one config without an ID made them return 500. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Analysts can explain each configured eligibility check in plain language. Published and preview results show the text in an accessible tooltip; failed checks use it in Requirements not met, including print summaries and CRM Next steps. Checks without explanations keep their alias/name fallback.
Closes #561. Follow-up to #552 after #580.
CheckConfig.explanationper configured check, separate from technical descriptions and aliases. Saves and imports enforce a 2,000-character limit; blank/null clears it. Benefit storage, copying, transfer, publication, and evaluation preserve it.Validation on the reviewed branch:
git diff --checkpassed. All six GitHub Actions checks passed on reviewed head0e168ac8before merge, including the full end-to-end suite.