Skip to content

Adds JSON form field support (PP-4438)#224

Open
tdilauro wants to merge 14 commits into
mainfrom
feature/json-formfield
Open

Adds JSON form field support (PP-4438)#224
tdilauro wants to merge 14 commits into
mainfrom
feature/json-formfield

Conversation

@tdilauro
Copy link
Copy Markdown
Contributor

@tdilauro tdilauro commented May 24, 2026

Description

Adds a new JsonField form component for editing JSON-typed protocol settings. The field renders as a textarea with:

  • Live JSON validation with an inline error message;
  • Copy-to-clipboard and clear buttons that appear on hover/focus;
  • One-shot undo (Ctrl-Z / Cmd-Z) after a clear;
  • Accessible ARIA markup.

Note: This field is activated by the new FormFieldType.JSON settings field type introduced on circulation PR #3386.

Motivation and Context

Allows admins to actively validate JSON in place without submitting the form.

[Jira PP-4438]

How Has This Been Tested?

  • Manual testing in local development environment.
  • Additional tests to cover the new functionality.
  • All checks pass locally.
  • CI checks pass.

Checklist:

  • N/A - I have updated the documentation accordingly.
  • All new and existing tests passed.

@claude
Copy link
Copy Markdown

claude Bot commented May 24, 2026

Claude finished @tdilauro's task in 5m 42s —— View job


Code Review

  • Gather context
  • Read changed files
  • Analyze for bugs and issues
  • Post review findings

Summary

This is a well-constructed addition. The render-phase state sync pattern (replacing UNSAFE_componentWillReceiveProps) is correctly implemented, the one-shot undo is a thoughtful UX detail, and the ARIA markup is solid. The useImperativeHandle dep array is correct, timer cleanup is thorough, and the test suite is comprehensive. Two minor concerns below.

Details

Minor: src/components/JsonField.tsx:131-134

The navigator.clipboard.writeText() promise's .then() callbacks (applyCopyResult) call setCopied/setCopyFailed unconditionally. If the component unmounts before the promise settles — e.g., a parent form closes immediately after the user clicks Copy — React 16 emits a "Can't perform a React state update on an unmounted component" warning. The synchronous !navigator.clipboard path at line 127 is fine, but the async path is not guarded. An isMounted ref would prevent this:

const isMounted = useRef(true);
useEffect(() => () => { isMounted.current = false; }, []);
// inside handleCopy's .then():
() => { if (isMounted.current) applyCopyResult(true); },
() => { if (isMounted.current) applyCopyResult(false); }

https://github.com/ThePalaceProject/circulation-admin/blob/11ae46079f272cc9abbedc6c95756ebc235f34d7/src/components/JsonField.tsx#L131-L134

Minor: src/components/JsonField.tsx:244-252

The useTimeoutCleanup helper has a 9-line JSDoc block. CLAUDE.md specifies "Never write multi-paragraph docstrings or multi-line comment blocks — one short line max." The parameter names and types are already self-documenting; the comment explaining that the effect body is intentionally empty (returning only a cleanup function) is the one non-obvious part and could be kept as a single trailing line.
https://github.com/ThePalaceProject/circulation-admin/blob/11ae46079f272cc9abbedc6c95756ebc235f34d7/src/components/JsonField.tsx#L244-L252

@tdilauro
Copy link
Copy Markdown
Contributor Author

Claude finished @tdilauro's task in 4m 34s —— View job

...

Details

src/components/JsonField.tsx:135

handleCopy calls navigator.clipboard.writeText(text) directly without first checking whether navigator.clipboard exists. The Clipboard API is only available in secure contexts (HTTPS); in an HTTP dev environment, navigator.clipboard is undefined and this line throws a TypeError that escapes the React event handler, since React 16 does not route event-handler exceptions through error boundaries. A guard prevents the crash:

function handleCopy() {
  if (!navigator.clipboard) return;
  if (copyTimeoutId.current) clearTimeout(copyTimeoutId.current);
  navigator.clipboard.writeText(text).then(...);
}

navigator.clipboard.writeText(text).then(

...

Fair point. However, the suggested change will not work. Or, more to the point, it won't produce the desired result, which would be to display the "Copy failed" message so that the user knows that something went wrong. It will just fail silently.

I fixed this and actually made the code a little more readable in the process. Also addressed the other minor / nit issues raised in the initial AI review.

This is ready for human review now.

Copy link
Copy Markdown
Member

@jonathangreen jonathangreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @tdilauro!

tdilauro added a commit to ThePalaceProject/circulation that referenced this pull request May 26, 2026
## Description

Adds a `json` form field type for integration settings. Fields annotated
with `FormFieldType.JSON` display a textarea in the admin UI that
accepts a JSON value. Two-way handling is wired up automatically:
- **Form rendering**: the field's default value is serialized to a JSON
string so the UI displays it correctly.
- **Form submission**: the incoming string is parsed back to a Python
value before Pydantic validation runs, with a clear error message if the
input is not valid JSON.

**Note**: The ability to edit this field type depends on new support
introduced on [circulation-admin PR
#224](ThePalaceProject/circulation-admin#224).

## Motivation and Context

Integration settings that need to store structured data (objects,
arrays) previously had no native form type. This adds a first-class JSON
field type that makes the round-trip between the admin UI and the
settings model transparent.

[Jira PP-4438]

## How Has This Been Tested?

- Manual testing in local development environment admin UI.
- New/updated tests for the new functionality.
- All tests and checks pass locally.
- [CI
tests](https://github.com/ThePalaceProject/circulation/actions/runs/26350026007)
and checks pass.

## Checklist

- N/A - I have updated the documentation accordingly.
- [x] All new and existing tests 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