Skip to content

fix(core): allow typing decimals in numeric column filters - #950

Open
marianmoldovan wants to merge 1 commit into
mainfrom
fix/715-numeric-filter-decimals
Open

fix(core): allow typing decimals in numeric column filters#950
marianmoldovan wants to merge 1 commit into
mainfrom
fix/715-numeric-filter-decimals

Conversation

@marianmoldovan

Copy link
Copy Markdown
Collaborator

Fixes #715

The bug

You could not type 0.01 into a numeric column filter. The TextField was controlled from the parsed number (String(internalValue)) while onChange ran parseFloat, so every keystroke round-tripped text → number → text and destroyed intermediate states:

typing 0.01:  "0"→0→"0"   "0."→0→"0"   "00"→0→"0"   "01"→1→"1"   RESULT: 1
typing -0.5:  "-"→undef→"" "0"→0→"0"   "0."→0→"0"   "05"→5→"5"   RESULT: 5

parseFloat("0.") and parseFloat("0.0") are both 0, which renders as "0", so 0.01 was unreachable left-to-right. Pasting worked because the text was already canonical. The form/inline editors were unaffected because TextFieldBinding renders value from form state rather than re-deriving it.

The fix

Raw input text lives in its own state and is rendered directly; a syncedValueRef stops the props-sync effect clobbering in-progress typing; parsing is extracted into a pure filter_text_input.ts helper.

typed "0"    -> updateValue=true  value=0
typed "0."   -> updateValue=false value=undefined   ← text preserved, no value written
typed "0.0"  -> updateValue=true  value=0
typed "0.01" -> updateValue=true  value=0.01        ← previously unreachable

is-null still disables the input; the enumValues Select/MultiSelect branches are untouched.

Tests

13 tests on the pure helper, asserting the value after every keystroke for 0.01, 1., -1; final values for 0.001, -0.5, .5, .01, 1.05, 10.00001, -.5; reverse deletion to empty; and that 0 stays a valid filter value.

@firecms/core: 250/253 passing (was 237/240) — 3 pre-existing failures unchanged.

Two judgement calls to review

  • The clear (X) button condition moved from internalValue != null to inputText !== "". The earlier 0-shows-the-X fix (c570dd02f) is preserved since "0" is non-empty. Side effect: an emptied string filter no longer shows a dangling X on an empty field.
  • Parsing is stricter than parseFloat: "1.2.3", "12abc", "0x10", "1e999" no longer commit bogus values (previously 1.2, 12, 0, Infinity). An improvement, but a behaviour change.

Separate pre-existing quirk found, left alone

Switching the operation away from is-null doesn't work — updateFilter("==", null) re-emits ["==", null] and the sync effect maps it straight back to is-null. Identical on main, unrelated to this issue.

Not verified

Typing behaviour needs a browser. The package's jest testEnvironment is node and the one existing component test already fails to run, so no DOM test was added. Verified instead with a faithful simulation of the new component state machine (both useStates, the ref guard, and the sync effect re-firing after every setValue).

🤖 Generated with Claude Code

The numeric column filter would not let you type a value like `0.01`: as soon as
a `0` was typed after the decimal separator the whole field collapsed to `0`, and
typing `.01` lost the separator entirely.

Root cause: the filter's `TextField` was controlled from the parsed number
(`value={String(internalValue)}`) while `onChange` ran `parseFloat` on the raw
text. Every keystroke round-tripped text -> number -> text, which destroys any
intermediate state that is not a canonical number:

    "0."  -> parseFloat -> 0 -> String -> "0"   (separator gone)
    "0.0" -> parseFloat -> 0 -> String -> "0"

so `0.01` was unreachable when typed left to right, while pasting worked because
the text was already canonical. The form field binding is unaffected because it
renders the value held in form state instead of re-deriving it from a parse.

Fix: hold the raw input text in its own `inputText` state and render that, so
what the user typed is never rewritten. A new pure helper,
`parseFilterTextInput`, converts the text into a filter value and only commits a
number once the text is a complete number, otherwise leaving the current filter
value untouched. Intermediate inputs such as "", "-", "0.", ".0" and "1." are now
preserved in the input without corrupting the filter.

`0` remains a valid filter value; only an empty input clears the filter. The
props sync effect still updates the displayed text when the value is changed
elsewhere, but compares the incoming value against the one the text was built
from so it cannot clobber an in-progress edit. The `is-null` operation and the
`enumValues` select paths are unchanged.

Parsing is also stricter than `parseFloat` was: "1.2.3", "12abc", "0x10" and
"1e999" no longer sneak a bogus number into the filter.

Fixes #715

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes numeric column filter typing by decoupling the rendered input text from the parsed filter value, preventing intermediate keystrokes (e.g. 0., -) from being clobbered during controlled re-renders.

Changes:

  • Update StringNumberFilterField to render from a dedicated inputText state and parse/commit values only when the text is a “complete” number.
  • Add a pure filter_text_input.ts helper to centralize parsing and value→text rendering.
  • Add Jest unit tests covering per-keystroke behavior and edge cases for numeric parsing.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/firecms_core/src/components/SelectableTable/filters/StringNumberFilterField.tsx Switches numeric/string filter input to preserve raw typing while committing parsed values safely.
packages/firecms_core/src/components/SelectableTable/filters/filter_text_input.ts Introduces strict parsing + value-to-text helper for filter inputs.
packages/firecms_core/src/components/SelectableTable/filters/filter_text_input.test.ts Adds unit tests validating parsing and per-keystroke commit behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +176 to 179
endAdornment={inputText !== "" && <IconButton
onClick={(e) => updateFilter(operation, undefined)}>
<CloseIcon />
</IconButton>}
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.

Numeric Control on Column Filter doesn't allow typing 0's after decimal

2 participants