Skip to content

Fix generator schema row tab order#293

Open
eviltester wants to merge 3 commits into
masterfrom
codex/271-generator-schema-tab-order
Open

Fix generator schema row tab order#293
eviltester wants to merge 3 commits into
masterfrom
codex/271-generator-schema-tab-order

Conversation

@eviltester

@eviltester eviltester commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the generator schema row keyboard tab order so primary editing controls are reached before row action buttons.

  • Keeps the visual row-action column on the left while moving row actions after editable controls in DOM/tab order.
  • Defers focusout validation until native Tab focus settles, preventing row re-rendering from dropping focus to the page body.
  • Restores focus for schema row action buttons after validation re-renders.
  • Ignores Codex dev-server log variants with .codex-dev-web*.log.

Fixed Issues

Closes #271
Closes #291

Validation

Summary by CodeRabbit

  • Bug Fixes

    • Improved keyboard navigation in schema rows so Tab moves through fields and actions in a more predictable order.
    • Focus is now restored more reliably after schema rows re-render.
    • Validation now waits for focus to settle, reducing premature blur-related updates.
  • Tests

    • Added coverage for tab order, focus restoration, and deferred validation behavior.

Copilot AI review requested due to automatic review settings June 30, 2026 09:51

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 was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@eviltester, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 09855bec-328b-441e-95c7-540cea205c59

📥 Commits

Reviewing files that changed from the base of the PR and between aee9eeb and aa7450b.

📒 Files selected for processing (4)
  • apps/web/styles.css
  • docs-src/blog/2026-06-12-combinatorial-grid-workflows.md
  • docs-src/blog/2026-06-30-increments-and-helpers.md
  • packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js
📝 Walkthrough

Walkthrough

Fixes keyboard tab order in schema editor rows by moving the row action buttons to the end of the DOM template and adding explicit CSS grid column placement for all row controls. Focus-out validation is deferred to the next event-loop tick via setTimeout(0). Test harnesses are updated for async timing, and new unit, UI, and Playwright tests verify the corrected tab sequence.

Changes

Schema Row Tab Order Fix

Layer / File(s) Summary
DOM template reorder and CSS grid placement
packages/core-ui/js/.../shared-schema-editor-ui.js, apps/web/styles.css
Action buttons block moved from before the name input to after the validation message in the row template. CSS grid-column/grid-row rules added to position name input, sourceType select, command picker, help links, params control, value input, and actions in explicit columns.
Focus state guard and deferred blur validation
packages/core-ui/js/.../schema-focus-state.js, packages/core-ui/js/.../shared-schema-editor-controller.js
captureActiveFieldState guard tightened to require rowId plus fieldName or actionName. New scheduleFocusSettledSemanticValidationForRow schedules semantic validation at setTimeout(0) on blur; handleFocusOut now calls this instead of immediate scheduling.
Unit and UI tests
packages/core-ui/src/tests/shared/schema-focus-state.test.js, packages/core-ui/src/tests/shared/shared-schema-editor-controller.test.js, packages/core-ui/src/tests/shared/shared-schema-editor-ui.test.js
New Jest tests verify focus capture/restore with action buttons, deferred timerApi scheduling and cleanup on destroy, and exact tab-focusable control order within a rendered row.
Test harness timing and browser e2e test
packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js, packages/core-ui/src/tests/interaction/support/focused-generator-harness.js, apps/web/src/tests/browser/generator/functional/schema-edit.spec.js
Harnesses switch input-clearing to direct focus+programmatic value+event dispatch with setTimeout(0) yields; fillRow uses async waitFor for the command select. Playwright test asserts tab sequence: name → sourceType → help link → value → drag button.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Tab, tab, tab through the fields in a row,
No more skipping to buttons below!
Grid columns aligned, focus restored,
The keyboard user's journey: no longer ignored.
Each input in order, the rabbit hops right — ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing generator schema row tab order.
Linked Issues check ✅ Passed The PR addresses the reported tab-order bug by moving row actions later in DOM order and adding tests for the expected keyboard sequence.
Out of Scope Changes check ✅ Passed The changes stay within the stated scope, with related focus/validation fixes and the .codex-dev-web log ignore update.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/271-generator-schema-tab-order

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aee9eebf16

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/styles.css
Comment on lines 1987 to +1989
.shared-schema-row-actions {
grid-column: 1;
grid-row: 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reset row placement in tablet layout

At widths 481–980px, the later @media (max-width: 980px) rules only change grid-column for the row actions and name input and do not reset grid-row. Because this new base rule pins the actions to row 1 while the new name-input rule also pins the textbox to row 1, the action buttons and column-name field overlap on tablet-width layouts; add responsive grid-row overrides while keeping the desktop left-action column.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Implemented in aa7450b1.

The tablet layout now explicitly places the shared schema row actions on grid row 1 and the column-name input on grid row 2 inside the max-width: 980px rules, so the desktop left-action-column placement does not carry an overlapping row assignment into tablet widths.

Verified with the targeted generator schema-edit Playwright spec, pnpm run verify:ui, pnpm run verify:local, and the pre-push verification hook.

Comment on lines +53 to +56
element.focus();
element.value = '';
fireEvent.input(element, { target: { value: '' } });
fireEvent.change(element, { target: { value: '' } });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Drive harness inputs through user-like events

AGENTS.md's Browser Test Interaction Rules require UI/browser abstractions to use user-like interactions only and avoid synthetic event dispatch or direct DOM hooks; this helper now focuses the element, mutates .value, and dispatches input/change directly for every clear. Tests using this focused app harness can therefore pass without exercising the real click/clear/type path, which is especially risky for this focusout/tab-order fix; keep the generic path on userEvent and limit direct dispatch to unavoidable jsdom-only exceptions.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Implemented in aa7450b1.

The focused app harness now uses user-event driven interactions for the generic helper path: user.click, user.clear, user.type/user.paste, user.selectOptions, and user.tab. The only special handling is still through user-event: number inputs replace the selected existing value in one type action so the row-count control does not clamp an intermediate empty value before typing.

Verified with the focused app harness Jest suites, the targeted generator schema-edit Playwright spec, pnpm run verify:ui, pnpm run verify:local, and the pre-push verification hook.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
packages/core-ui/src/tests/shared/shared-schema-editor-ui.test.js (1)

92-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a command-row tab-order case.

This only exercises the value-row branch. renderSharedSchemaRows builds command rows through a different path (pick-command, help link, params, edit-params), and the acceptance criteria here explicitly include command parameters. A companion assertion for a faker/domain row would close the main remaining regression gap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core-ui/src/tests/shared/shared-schema-editor-ui.test.js` around
lines 92 - 111, Add a command-row tab-order test to cover the non-value-row path
in `renderSharedSchemaRows`. The current case only verifies the editable
value-row branch, so add a companion assertion for a command row that exercises
`pick-command`, the help link, params, and edit-params controls. Keep the
focus-order check in the same style as the existing `orders focusable row
controls from editable fields to row actions` test, using the row query and
focusable element collection to validate the expected tab sequence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core-ui/src/tests/shared/shared-schema-editor-ui.test.js`:
- Around line 92-111: Add a command-row tab-order test to cover the
non-value-row path in `renderSharedSchemaRows`. The current case only verifies
the editable value-row branch, so add a companion assertion for a command row
that exercises `pick-command`, the help link, params, and edit-params controls.
Keep the focus-order check in the same style as the existing `orders focusable
row controls from editable fields to row actions` test, using the row query and
focusable element collection to validate the expected tab sequence.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7845862a-9232-4ebb-8d3c-15e343309dea

📥 Commits

Reviewing files that changed from the base of the PR and between 7b6a120 and aee9eeb.

📒 Files selected for processing (11)
  • .gitignore
  • apps/web/src/tests/browser/generator/functional/schema-edit.spec.js
  • apps/web/styles.css
  • packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js
  • packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js
  • packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js
  • packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js
  • packages/core-ui/src/tests/interaction/support/focused-generator-harness.js
  • packages/core-ui/src/tests/shared/schema-focus-state.test.js
  • packages/core-ui/src/tests/shared/shared-schema-editor-controller.test.js
  • packages/core-ui/src/tests/shared/shared-schema-editor-ui.test.js

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes keyboard tab order in schema rows by moving the row-action buttons (drag, add, remove, up, down) to the end of the DOM while using CSS grid-column explicit placement to keep them visually in their original left-hand column. It also defers focusout-triggered semantic validation by one microtask tick so that native browser focus can settle before re-rendering, and broadens focus-state capture to all row action buttons (not just pick-command) so focus can be restored after a re-render.

  • shared-schema-editor-ui.js: row actions <div> moved to end of innerHTML template; styles.css adds explicit grid-column/grid-row to all row elements so the visual layout is preserved.
  • shared-schema-editor-controller.js: new scheduleFocusSettledSemanticValidationForRow that schedules with delay 0 (instead of immediate: true) is used by handleFocusOut, giving native Tab focus time to land before triggering row re-render.
  • schema-focus-state.js: broadens capture guard from actionName !== 'pick-command' to !actionName so all action buttons are captured and can be restored after re-render.
  • Test files add unit and browser-level coverage for tab order, deferred validation, and focus restoration.

Confidence Score: 5/5

Safe to merge — the changes are well-scoped, covered by new unit and browser tests, and the approach (DOM reorder + CSS grid + deferred setTimeout(0)) is a standard, low-risk pattern for this class of focus problem.

All three layers of the fix (DOM order, CSS grid placement, deferred validation) have direct test coverage at the unit, DOM/component, and Playwright levels. The timer management reuses the existing clearSemanticValidationTimer mechanism, so cleanup on destroy is already handled. No existing behaviour was removed — the change is additive except for the DOM reorder, which is fully compensated by the explicit grid positions.

No files require special attention.

Important Files Changed

Filename Overview
packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js Row actions div moved to end of DOM; CSS grid explicit placement maintains visual column order — core of the tab-order fix.
packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js Adds scheduleFocusSettledSemanticValidationForRow (setTimeout 0) used by handleFocusOut to prevent premature re-render on Tab; existing timer cancellation logic is preserved.
packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js Guard widened from actionName !== 'pick-command' to !actionName so all row action buttons (drag, add, remove, up, down) can have focus captured and restored after re-render.
apps/web/styles.css Explicit grid-column/grid-row rules added for all schema row child elements; row-actions fixed to column 1 / row 1 to preserve visual position despite moving to DOM end.
packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js Replaces direct fireEvent/element.blur() calls with user-event interactions (selectOptions, type, paste, tab) and adds setTimeout(0) settling waits to align with deferred validation.
packages/core-ui/src/tests/interaction/support/focused-generator-harness.js Adds setTimeout(0) after blur and waitFor loop for command select to handle deferred re-renders from the new validation scheduling.
packages/core-ui/src/tests/shared/schema-focus-state.test.js New unit test verifying capture and restore of focus state for row action buttons (drag) after a re-render.
packages/core-ui/src/tests/shared/shared-schema-editor-controller.test.js New test confirms handleFocusOut schedules validation with setTimeout delay 0 and that destroy() clears the pending timer.
packages/core-ui/src/tests/shared/shared-schema-editor-ui.test.js New test asserts focusable DOM order within a rendered row: editable fields before action buttons.
apps/web/src/tests/browser/generator/functional/schema-edit.spec.js New Playwright test verifies Tab navigates name → sourceType → helpLink → value → drag in the actual browser.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant DOM as Schema Row DOM
    participant Controller
    participant Timer as setTimeout(0)
    participant Renderer

    Note over DOM: Actions div is last in DOM<br/>(CSS grid puts it visually first)

    User->>DOM: Tab through fields (name → sourceType → helpLink → value → drag…)
    User->>DOM: focusout fires on a field (name/command/params/value)
    DOM->>Controller: handleFocusOut(event)
    Controller->>Timer: "scheduleFocusSettledSemanticValidationForRow(rowId, delay=0)"
    Note over Timer: clears any prior debounce timer<br/>schedules validation after focus settles
    Timer-->>Controller: applySemanticValidationForRow(rowId)
    Controller->>Controller: captureActiveFieldState(document)
    Note over Controller: captures fieldName OR actionName<br/>(all action buttons now captured)
    Controller->>Renderer: renderRows()
    Renderer->>DOM: innerHTML rewritten
    Controller->>DOM: restoreActiveFieldState(document, state)
    Note over DOM: focus returned to correct<br/>field or action button
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant DOM as Schema Row DOM
    participant Controller
    participant Timer as setTimeout(0)
    participant Renderer

    Note over DOM: Actions div is last in DOM<br/>(CSS grid puts it visually first)

    User->>DOM: Tab through fields (name → sourceType → helpLink → value → drag…)
    User->>DOM: focusout fires on a field (name/command/params/value)
    DOM->>Controller: handleFocusOut(event)
    Controller->>Timer: "scheduleFocusSettledSemanticValidationForRow(rowId, delay=0)"
    Note over Timer: clears any prior debounce timer<br/>schedules validation after focus settles
    Timer-->>Controller: applySemanticValidationForRow(rowId)
    Controller->>Controller: captureActiveFieldState(document)
    Note over Controller: captures fieldName OR actionName<br/>(all action buttons now captured)
    Controller->>Renderer: renderRows()
    Renderer->>DOM: innerHTML rewritten
    Controller->>DOM: restoreActiveFieldState(document, state)
    Note over DOM: focus returned to correct<br/>field or action button
Loading

Reviews (3): Last reviewed commit: "Address PR 293 review feedback" | Re-trigger Greptile

@eviltester eviltester left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Code Review for PR #293\n\nI've completed the review of this PR. Key observations and suggested fixes follow.

@eviltester

Copy link
Copy Markdown
Owner Author

Code Review Feedback for PR #293

1. Potential Bug: Focus State Capture Logic Regression

File: packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js:10

The original condition explicitly excluded pick-command from being captured:

if (!rowId || (!fieldName && actionName !== 'pick-command')) {

The new condition captures all action buttons including pick-command:

if (!rowId || (!fieldName && !actionName)) {

This changes behavior - pick-command elements will now have their focus state captured and restored, which may not be intended. If pick-command elements shouldn't have focus restored (e.g., they're transient dropdown triggers), this could cause unexpected focus jumps after validation re-renders.

Suggestion: Revert to the explicit exclusion or add a comment explaining why pick-command is now included.


2. Incomplete Test Coverage for Focus Restore on Action Buttons

File: packages/core-ui/src/tests/shared/schema-focus-state.test.js

The new test only covers the drag action button. It doesn't test:

  • add, remove, up, down action buttons
  • Multiple action buttons in the same row
  • Whether focus restoration works when the row has validation errors

Suggestion: Add tests for other action types or parameterize the existing test.


3. Deferred Validation Timer Cleanup on Destroy

File: packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js:324-334

The test verifies cleanup on destroy() (clearTimeout called), but the production code doesn't show a destroy() handler that clears all timers. Need to verify destroy() calls clearSemanticValidationTimer for all rows.

Suggestion: Confirm destroy() iterates semanticValidationTimers and clears each. If not, add it.


4. CSS Grid Ordering - Missing Explicit Column for Drag Handle

File: apps/web/styles.css:1995-2030

The CSS explicitly assigns grid-column for editable fields but not for the drag handle inside .shared-schema-row-actions. The drag handle has no column assignment and relies on implicit grid auto-placement which could be fragile.

Suggestion: Add explicit grid-column: 1 to .shared-schema-drag-handle for clarity and stability.


5. Test Harness Async Handling Could Be Flaky

File: packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js:53-66

Uses setTimeout(resolve, 0) to wait for microtask queue. This is a timing-dependent workaround. Better to use waitFor with an assertion on expected state.

Suggestion: Replace with await waitFor(() => expect(...)) pattern for deterministic async handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants