Incorrect TextArea behavior when additional params are specified in t…#4564
Open
ashklianko wants to merge 1 commit into
Open
Incorrect TextArea behavior when additional params are specified in t…#4564ashklianko wants to merge 1 commit into
ashklianko wants to merge 1 commit into
Conversation
Member
Author
|
Found a problem, hold on |
5b04f31 to
5589f51
Compare
5589f51 to
d38b1ed
Compare
d38b1ed to
d188f67
Compare
Member
Author
|
Can be checked, @edloidas |
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.
…he content type #4544
Problem
Two related regressions in the
form2leaf input components, both rooted in how the form layer reconciles the parsedValuewith what the user actually typed.The first regression affected
DateInput,DateTimeInput,TimeInput,InstantInput,LongInput,DoubleInput,GeoPointInput, andTextLineInput. Typing fast into any of them could intermittently wipe what was just typed. Each of those components carried a localuseState(rawInput)that mirrored the input text, plus auseRef(isLocalChange)flag whose only purpose was to suppress one re-render from the parent right afteronChange. That flag was one-shot: a single re-render consumed it, and any subsequent re-render — Strict Mode double-effects, a parent re-render emitting a fresh nullValueinstance, validation cycles re-emitting the same value — would skip the suppression and overwriterawInputwith the empty string returned byvalueToString(null). The faster the user typed, the more chances those extra re-renders had to slip in.The second regression affected
TextAreaInput. Pasting a string longer than the configured maximum length — or typing past the limit whenshowCounteris enabled, which removes the browser's own length enforcement — wiped the textarea contents and showed no error message.TextAreaInputwas fully controlled byvaluewith no local raw-input fallback. WhenInputField.handleChangerandescriptor.validateand saw an error, it storednewNullValue()inPropertyArray; the next render set the textarea'svalueto the empty string and the DOM was cleared. The localized "Text is too long" message did not appear either, becauseTextAreaDescriptor.validateshort-circuited onvalue.isNull()without ever looking atrawValue, and used a hardcoded English string instead ofi18n('field.value.breaks.maxlength', …)— the same localization helper thatTextLineDescriptoralready used for the equivalent error.Both symptoms share the same architectural root: leaf inputs cannot reconstruct user-typed text from
valuealone once the form layer has decided to nullify invalid input.Solution
Promote raw user input to a first-class property on the form/leaf boundary and make every leaf input stateless with respect to display.
OccurrenceManageralready stored arawValuesarray privately alongsidevalues. The publicOccurrenceManagerStatenow exposesrawValues: (string | undefined)[]as a read-only field.InputTypeComponentPropsgets a newrawValue?: stringproperty.InputField(single-occurrence mode) andOccurrenceList(single + list-item paths) threadstate.rawValues[index]through to each leaf. TheonChangecontract is unchanged — leaves callonChange(parsedValue, rawText?), whichInputField.handleChangealready forwarded tomanager.set(index, value, rawValue).A new utility module
form2/utils/displayValue.tsresolves what each input renders: whenrawValueis present it wins; otherwisetoDisplay(value)is used for non-null values; otherwise the empty string.Picker confirmation handlers in
DateInput,DateTimeInput,TimeInput, andInstantInputpass their formatted display string asrawValuealongside the parsedValue, so the picker's user-readable form (for example2025-06-15 14:30without seconds) survives storage normalisation to2025-06-15T14:30:00.All eight affected components lose their
useState(rawInput), theiruseRef(isLocalChange), and their value-syncuseEffect.TextAreaInputadopts the samedisplayValuepattern and now passesinputValueasrawValueon every change. That is what makes the maximum-length bug go away: even whendescriptor.validateflags the input andInputFieldstoresnullinPropertyArray, the user's text is preserved instate.rawValues[index]and reaches the textarea through display.TextAreaDescriptor.validatewas aligned withTextLineDescriptor.validate: it now acceptsrawValue?: string, falls back torawValuewhenvalue.isNull(), and emits the localized messagei18n('field.value.breaks.maxlength', config.maxLength)instead of the hardcoded English string. Behaviour and error wording are now exactly symmetric withTextLine.InputField's value-sync effect previously calledsync(values)with a closure snapshot of thevaluesarray. Fast typing could schedule multiple effect runs whose closures still pointed to older snapshots.OccurrenceManager.setValuescompares incoming values againstthis.valuesby reference, so a stale snapshot fed into it clearedrawValuesat the affected index — making typed text disappear whenever the parsed value was null. The effect now reads the current values fresh viapropertyArray.getProperties().map(p => p.getValue())and uses the closure'svaluesonly as a dependency trigger.Closes #4544