Skip to content

Support placeholder, maxLength, and filter for pxtJsonOptions input - #11723

Open
Dmitriy Antipov (THEb0nny) wants to merge 10 commits into
microsoft:masterfrom
THEb0nny:feature/pxtjson-input-options
Open

Dmitriy Antipov (THEb0nny) wants to merge 10 commits into
microsoft:masterfrom
THEb0nny:feature/pxtjson-input-options

Conversation

@THEb0nny

@THEb0nny Dmitriy Antipov (THEb0nny) commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Description

This PR follows up on #10725 by adding support for placeholder, maxLength, and filter for pxtJsonOptions of type "input" in pxt.json.

This enables targets (such as pxt-ev3 for project folder names) to restrict input values, enforce character limits, and provide meaningful placeholder hints.

Changes:

  • localtypings/pxtarget.d.ts:

    • Converted PxtJsonOption into a discriminated union (PxtJsonCheckboxOption | PxtJsonInputOption), ensuring strict typing and autocomplete for option properties based on type.
    • Added optional placeholder?: string, maxLength?: number, and filter?: string to PxtJsonInputOption.
  • webapp/src/pxtjson.tsx:

    • Forwarded placeholder (localized with pxt.Util.rlf), maxLength, and filter to the <Input /> component.
  • cli/cli.ts:

    • Registered {id:setting}${option.placeholder} into targetStrings during saveThemeJson for Crowdin localization extraction.
  • react-common/components/controls/Input.tsx:

    • Filtering & Limits:
      • Added maxLength?: number prop support.
      • Updated filter to match globally (/g).
      • Added safe code-point boundary truncation (truncateToCodePointBoundary) to prevent splitting UTF-16 surrogate pairs (emoji) at maxLength.
    • Caret & Input Types:
      • Preserved caret position when invalid characters are filtered out during typing.
      • Added safe selection handling (getSelection / setCaret) guarding against DOMException on inputs that do not support selection offsets (e.g. type="number" / type="email").
    • Paste Handling:
      • Stripped line breaks (\r/\n) from pasted text in single-line inputs.
      • Sanitized the full prospective value (prefix + paste + suffix) and derived retained insertion and caret position.
      • Preserved browser native Undo history (Ctrl+Z): allowed native paste when the full prospective text is already valid, and utilized document.execCommand("insertText") for sanitized pastes.
      • Left non-text clipboard contents (images, files) to native handling.
    • IME & State:
      • Handled IME composition events (compositionstart / compositionend) to avoid interrupting multi-step character entry (e.g. CJK), while keeping intermediate callbacks active for unconstrained inputs (SearchInput, etc.).
      • Synchronized lastCommittedValueRef across change, blur, and Enter validation to prevent suppressing legitimate edits and spurious consumer state resets.
      • Ensured safe fallback when filter is undefined.
  • teachertool/src/components/CriteriaInstanceDisplay.tsx:

    • Updated to use filter="[0-9]" alongside maxLength={numeric && !paramOptions ? 2 : undefined} to align with global filter regex matching.

Testing:

  • Builds & Linting:
    • npx gulp cli (passed)
    • npx gulp reactCommon (passed)
    • npx gulp webapp (passed)
    • npx gulp lint (passed, 0 errors across codebase)
  • Local Target Testing (pxt-ev3):
    • Tested on uploadProjectFolder with placeholder: "BrkProg_SAVE", maxLength: 20, and filter: "[a-zA-Z0-9_-]":
      • Placeholder displays correctly.
      • Typing invalid characters is blocked in real-time without resetting the cursor.
      • Pasting (valid, invalid, multi-line, or oversized strings) sanitizes properly, respects maxLength, preserves existing suffixes, and supports Ctrl+Z undo.

Example target configuration (pxtarget.json)

"pxtJsonOptions": [
    {
        "property": "uploadProjectFolder",
        "label": "Custom folder for Bluetooth upload",
        "type": "input",
        "placeholder": "BrkProg_SAVE",
        "maxLength": 32,
        "filter": "[a-zA-Z0-9_]"
    }
]

- Separate PxtJsonOption into discriminated union (checkbox | input)

- Add placeholder, maxLength, and filter to PxtJsonInputOption

- Support maxLength and fix global regex matching for filter in Input component

- Pass placeholder, maxLength, and filter to Input in pxt.json editor
@THEb0nny

Copy link
Copy Markdown
Contributor Author

Richard Knoll (@riknoll) please take a look

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved input-handling regressions remove digit limits, discard valid pasted characters, and can disrupt Chinese IME composition, while new placeholders lack translation registration.

4 open issues
What changed in this PR

This follow-up to #10725 extends target-defined pxtJsonOptions text inputs with placeholder hints, length limits, and character filtering in the project settings editor.

Changes:

  • Split option typings into checkbox and input variants, with optional input-specific properties.
  • Add maxLength support and global regex matching to the shared Input component.
  • Forward placeholder localization lookups, length limits, and filters from project settings.
File Description
webapp/​src/​pxtjson.tsx Passes the new input options to the settings controls.
react-common/​components/​controls/​Input.tsx Adds native length limits and global filter matching.
localtypings/​pxtarget.d.ts Defines separate option variants and the new input properties.

Review effort: Max

Comment thread webapp/src/pxtjson.tsx Resolved
Comment thread webapp/src/pxtjson.tsx Resolved
- Filter complete pasted text before applying maxLength in Input

- Preserve IME composition state during typing in Input

- Register pxtJsonOptions input placeholder in targetStrings in cli

- Limit CriteriaInstanceDisplay numeric input with maxLength and digit filter

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Replacing the public PxtJsonOption interface with a union breaks target extensions and declaration merging, while unresolved filtered-input defects can delete existing characters or save invalid text.

8 open issues
1 resolved since last review

Review effort: Max

- Preserve caret position after sanitizing filtered input in changeHandler and compositionEndHandler

- Handle onPaste to filter clipboard text and limit insertion without truncating existing suffix

- Track lastCommittedValue to skip onChange when composition is cancelled

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Human review is needed to assess input-filtering edge cases affecting caret placement, paste consistency, composition preservation, and regex safety that were omitted from the published review.

1 open issue
4 resolved since last review

Review effort: Max

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Filtered paste can lose cross-boundary regex matches and clear or duplicate existing text, while regression tests for paste behavior and length clamping are missing.

2 open issues
1 resolved since last review

Review effort: Max

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The paste handler bypasses native undo for sanitized text and can delete unselected text after the caret when enforcing maxLength.

2 open issues
1 resolved since last review

Review effort: Max

@THEb0nny

Copy link
Copy Markdown
Contributor Author

Joey Wunderlich (@jwunderl)
This PR is ready for human review.
Along with the core feature and Crowdin localization registration (cli.ts), I've also addressed edge cases in the shared component (caret preservation during filtering, native undo preservation on paste, IME composition, and safe UTF-16 surrogate pair truncation).

Everything has been verified:
gulp lint and gulp webapp pass cleanly with 0 errors.
Tested locally in pxt-ev3 on project folder settings.

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.

3 participants