From aee9eebf163754327b1c3febb673b06f4237fed7 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 30 Jun 2026 10:40:18 +0100 Subject: [PATCH 1/5] Fix generator schema tab order --- .gitignore | 2 +- .../generator/functional/schema-edit.spec.js | 29 ++++++++++++ apps/web/styles.css | 37 +++++++++++++++ .../test-data/schema/schema-focus-state.js | 2 +- .../schema/shared-schema-editor-controller.js | 12 ++++- .../schema/shared-schema-editor-ui.js | 14 +++--- .../support/focused-app-test-data-harness.js | 9 +++- .../support/focused-generator-harness.js | 9 +++- .../tests/shared/schema-focus-state.test.js | 47 +++++++++++++++++++ .../shared-schema-editor-controller.test.js | 47 +++++++++++++++++++ .../shared/shared-schema-editor-ui.test.js | 22 +++++++++ 11 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 packages/core-ui/src/tests/shared/schema-focus-state.test.js diff --git a/.gitignore b/.gitignore index 3987d62d..ed1c215b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ testenv/ experiments experiments/* .codex-logs/ -.codex-dev-web.log +.codex-dev-web*.log diff --git a/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js b/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js index f9d93d5a..0c678ea8 100644 --- a/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js +++ b/apps/web/src/tests/browser/generator/functional/schema-edit.spec.js @@ -37,6 +37,35 @@ test.describe('Generator Schema Editing', () => { expectNoPageErrors(pageErrors); }); + test('tabs through schema row fields before row action buttons', async ({ page }) => { + const { generatorPage, pageErrors } = await openGenerator(page); + + await generatorPage.schema.setTextMode(false); + const row = generatorPage.schema.row(0); + const nameInput = row.locator('input[data-field="name"]'); + const sourceTypeSelect = row.locator('select[data-field="sourceType"]'); + const helpLink = row.locator('[data-field="faker-doc-link"]'); + const valueInput = row.locator('input[data-field="value"]'); + const dragButton = row.locator('[data-action="drag"]'); + + await nameInput.focus(); + await expect(nameInput).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(sourceTypeSelect).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(helpLink).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(valueInput).toBeFocused(); + + await page.keyboard.press('Tab'); + await expect(dragButton).toBeFocused(); + + expectNoPageErrors(pageErrors); + }); + test('can edit as schema then edit as text', async ({ page }) => { const { generatorPage, pageErrors } = await openGenerator(page); diff --git a/apps/web/styles.css b/apps/web/styles.css index ea6d4502..5bbc4959 100644 --- a/apps/web/styles.css +++ b/apps/web/styles.css @@ -1985,10 +1985,47 @@ body.theme-dark .generator-status-text[data-severity='info'] { } .shared-schema-row-actions { + grid-column: 1; + grid-row: 1; display: flex; gap: 0.2rem; } +.shared-schema-row > input[data-field='name'] { + grid-column: 2; + grid-row: 1; +} + +.shared-schema-row > select[data-field='sourceType'] { + grid-column: 3; + grid-row: 1; +} + +.shared-schema-row > .shared-schema-command-picker-control { + grid-column: 4; + grid-row: 1; +} + +.shared-schema-row-command > .shared-schema-help-link { + grid-column: 5; + grid-row: 1; +} + +.shared-schema-row-value > .shared-schema-help-link { + grid-column: 4; + grid-row: 1; +} + +.shared-schema-row > .shared-schema-params-control { + grid-column: 6; + grid-row: 1; +} + +.shared-schema-row > input[data-field='value'] { + grid-column: 5; + grid-row: 1; +} + .shared-schema-drag-handle { cursor: grab; } diff --git a/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js b/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js index dcbdc331..9e1bd34b 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js +++ b/packages/core-ui/js/gui_components/shared/test-data/schema/schema-focus-state.js @@ -5,7 +5,7 @@ function captureActiveFieldState(documentObj) { const fieldName = activeElement?.getAttribute?.('data-field'); const actionName = activeElement?.getAttribute?.('data-action'); const rowId = activeElement?.closest?.(SHARED_SCHEMA_ROW_SELECTOR)?.getAttribute?.('data-row-id'); - if (!rowId || (!fieldName && actionName !== 'pick-command')) { + if (!rowId || (!fieldName && !actionName)) { return null; } return { diff --git a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js index 0df93e66..6294cb11 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js +++ b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-controller.js @@ -315,6 +315,16 @@ function createSharedSchemaEditorController({ semanticValidationTimers.set(rowId, timerId); }; + const scheduleFocusSettledSemanticValidationForRow = (rowId) => { + clearSemanticValidationTimer(rowId); + if (typeof timerApi?.setTimeout !== 'function') { + applySemanticValidationForRow(rowId); + return; + } + const timerId = timerApi.setTimeout(() => applySemanticValidationForRow(rowId), 0); + semanticValidationTimers.set(rowId, timerId); + }; + const updateTextElementFromRows = () => { const textElement = getTextElement(); if (textElement) { @@ -785,7 +795,7 @@ function createSharedSchemaEditorController({ if (!rowId) { return; } - scheduleSemanticValidationForRow(rowId, { immediate: true }); + scheduleFocusSettledSemanticValidationForRow(rowId); }; const handleClick = async (event) => { diff --git a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js index 054df237..02ec89d8 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js +++ b/packages/core-ui/js/gui_components/shared/test-data/schema/shared-schema-editor-ui.js @@ -88,13 +88,6 @@ function renderSharedSchemaRows({ rowElem.setAttribute('data-row-id', row.id); rowElem.setAttribute('data-row-index', String(index)); rowElem.innerHTML = ` -
- - - - - -
+ +
invalid row
+
+ + + + + +
+ + `; + } + + test.each(['pick-command', 'drag', 'add', 'remove', 'up', 'down'])( + 'captures and restores focused %s action buttons after a row render', + (actionName) => { + dom = new JSDOM(` + + + + + `); + const { document } = dom.window; + + renderRowWithActions(); + + document.querySelector(`[data-action="${actionName}"]`).focus(); + + const state = captureActiveFieldState(document); + expect(state).toMatchObject({ + rowId: 'row-1', + fieldName: null, + actionName, + }); + + renderRowWithActions(); + const nextActionButton = document.querySelector(`[data-action="${actionName}"]`); + + restoreActiveFieldState(document, state); + + expect(document.activeElement).toBe(nextActionButton); + } + ); + + test('captures and restores focused row fields after a row render', () => { dom = new JSDOM(` -
- +
+ +
invalid row
`); const { document } = dom.window; - document.querySelector('[data-action="drag"]').focus(); + document.querySelector('[data-field="name"]').focus(); const state = captureActiveFieldState(document); expect(state).toMatchObject({ rowId: 'row-1', - fieldName: null, - actionName: 'drag', + fieldName: 'name', + actionName: null, }); document.body.innerHTML = ` -
- +
+ +
invalid row
`; - const nextDragButton = document.querySelector('[data-action="drag"]'); + const nextNameInput = document.querySelector('[data-field="name"]'); restoreActiveFieldState(document, state); - expect(document.activeElement).toBe(nextDragButton); + expect(document.activeElement).toBe(nextNameInput); }); }); From 9c06fd116047e0d7cb0fed37fc49d64c066b82b1 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 1 Jul 2026 00:30:11 +0100 Subject: [PATCH 5/5] Address PR 293 docs and harness feedback --- docs-src/blog/2026-06-30-increments-and-helpers.md | 3 ++- .../tests/interaction/support/focused-app-test-data-harness.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs-src/blog/2026-06-30-increments-and-helpers.md b/docs-src/blog/2026-06-30-increments-and-helpers.md index 0579f933..c51746cf 100644 --- a/docs-src/blog/2026-06-30-increments-and-helpers.md +++ b/docs-src/blog/2026-06-30-increments-and-helpers.md @@ -1,3 +1,4 @@ +--- slug: auto-increments-helpers authors: [alan] tags: [release, feature] @@ -47,7 +48,7 @@ Generated values: ```text filename001.txt -filename0006.txt +filename006.txt filename011.txt ``` diff --git a/packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js b/packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js index ca0b82d0..9f1c7c02 100644 --- a/packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js +++ b/packages/core-ui/src/tests/interaction/support/focused-app-test-data-harness.js @@ -90,6 +90,7 @@ function createFocusedAppTestDataHarness() { } await user.selectOptions(element, value); await user.tab(); + await waitForFocusToLeave(element); } function reset() {