Repeated entry is affected by where an error leaves the operator, not just by click count. Advancing on every Enter can feel quick while letting an invalid quantity slip past.
I built a synthetic order-entry state model. Its row and field identify an intended focus target; they do not move actual browser focus or process an operational order.
Keep values and intended movement together
State holds rows, the current row and field, popup visibility and a message. Input changes the selected value; movement is a separate action. Tests can compare both preserved data and the intended destination rather than infer the next target from an uncontrolled DOM.
This is not a recommendation to intercept every Tab key. A real interface must decide which native keyboard behavior to retain and which repeated-entry shortcuts to add. The model includes no DOM bindings, actual focus movement or screen-reader behavior. A calculated destination is implementation evidence for a rule, not proof that an accessible interface has passed testing.
Closing search should preserve partial input
Search opens the popup only from the item field. Escape closes it without clearing the typed value. Choose accepts a valid synthetic item and targets quantity. Enter while the popup is open does not invent a selection; it returns a choose-or-close message.
A real search widget also needs result navigation, selected-option announcements and a decision about focus restoration. This function fetches no results and does not model arrow-key exploration. Its chosen value is a synthetic string rather than an operational product code. The experiment verifies the distinction between closing and choosing while leaving the widget’s remaining responsibilities explicit.
Keep invalid quantities at their current target
Quantity is an ASCII decimal string representing one through ninety-nine. Zero and an empty value cannot pass Enter or Add. Invalid input retains the current row and field while changing the message, preventing movement into a new blank row from making the unfinished work appear lost.
That rule is not a universal quantity policy. Decimal units, returns and cancellation can require different contracts in an actual ERP. The example deliberately uses bounded synthetic integer input. Its message is a state key such as invalid_quantity, not finished localized copy or an accessible association between an error message and a field.
Add a row only after completing the current one
Add appends a fresh row and targets its item field only when the current item and quantity are valid. Earlier values remain intact. Incomplete input or the experimental twenty-row bound prevents another row. That bound describes this model’s scope rather than a claimed maximum for a production order system.
The entry model does not save or submit anything. Enter at a valid final quantity can report row_complete, but that does not mean the server registered an order. Local completion and confirmed persistence need separate state when connected. The same distinction must remain if this model is combined with the shared save-response contract.
Follow model checks with rendered interaction checks
Tests verify no movement after invalid input, preserved text after cancelling search, quantity targeting after selection, valid row addition and unchanged original state. Recorded output shows a new-row target while the first quantity remains intact. This is not evidence of pressing physical keys or inspecting a mobile layout.
Integration still needs checks for duplicate event binding, Enter during text composition, popup focus restoration, error announcements and native Tab movement. Those are unverified until actually exercised. I share a way to break a speed-of-entry promise into retained input, validation and intended movement, so each responsibility can be tested at the appropriate layer.