From afda8a020686736310d0e95eb6cafcce43e0e47d Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:24:40 +0000 Subject: [PATCH 1/8] Hi, I'm Jules! I have updated the UI by replacing the native `disabled` state with `aria-disabled` on the save button. Previously, when form validation failed, the native disabled attribute prevented all events. This meant that users relying on keyboards or screen readers did not receive any feedback on why the form could not be processed. By switching to `aria-disabled="true"` and handling the save event to show a toast notification, I've made sure we preserve focus and provide actionable feedback to the user. --- .jules/palette.md | 4 ++++ app.js | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.jules/palette.md b/.jules/palette.md index 0bbf5248..7a0f1c52 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -115,3 +115,7 @@ ## $(date +%Y-%m-%d) - Prevent accidental data loss in inline editors **Learning:** Forms that take a long time to fill out (like a WBS editor) are prone to accidental closure by users pressing `Escape` or clicking cancel. This causes immediate data loss without any warning, resulting in frustration. **Action:** When working on editors that can be dismissed, track whether the user has modified any fields compared to their initial state. If there are changes, intercept the close action and present a confirmation dialog (`window.confirm`) to ensure they really want to discard their edits. Bypass this for intentional saves or explicit data overrides. + +## $(date +%Y-%m-%d) - Replace native disabled with aria-disabled for form submit buttons +**Learning:** Using the native `disabled` attribute on a form's primary submit button blocks all click and key events. Consequently, keyboard-only or screen reader users who trigger the submit action on an invalid form receive no feedback because the event never fires. +**Action:** Use `aria-disabled="true"` alongside `disabled = false` for form submit buttons when validation fails. Handle the form's `submit` event to check for `aria-disabled`, prevent the submission, and proactively display a toast notification or inline feedback to explain why the action is blocked. diff --git a/app.js b/app.js index a04aae71..ef11e68b 100644 --- a/app.js +++ b/app.js @@ -429,6 +429,13 @@ function bindTableEvents(renderDraftValidation, updateEditorDraftFromEvent) { return; } event.preventDefault(); + + const saveButton = form.querySelector('button[type="submit"]'); + if (saveButton && saveButton.getAttribute('aria-disabled') === 'true') { + showToast(saveButton.title || '현재 사용할 수 없는 작업입니다.'); + return; + } + renderDraftValidation.flush(); saveEditor(); }); @@ -1068,7 +1075,12 @@ function renderEditorValidation() { const saveButton = form.querySelector('button[type="submit"]'); if (saveButton) { - saveButton.disabled = errors.length > 0; + saveButton.disabled = false; + if (errors.length > 0) { + saveButton.setAttribute('aria-disabled', 'true'); + } else { + saveButton.removeAttribute('aria-disabled'); + } saveButton.title = errors.length > 0 ? '입력값을 올바르게 수정해야 저장할 수 있습니다.' : '저장 (Enter)'; } From 70d7f71a6682d51fa23e42305109e942ccc21119 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:37:58 +0900 Subject: [PATCH 2/8] test(a11y): reproduce editor submit validation races --- tests/e2e/editor-validation-feedback.spec.js | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/e2e/editor-validation-feedback.spec.js diff --git a/tests/e2e/editor-validation-feedback.spec.js b/tests/e2e/editor-validation-feedback.spec.js new file mode 100644 index 00000000..f6007f65 --- /dev/null +++ b/tests/e2e/editor-validation-feedback.spec.js @@ -0,0 +1,36 @@ +import { test, expect } from '@playwright/test'; + +test.describe('editor validation feedback', () => { + test.beforeEach(async ({ page }) => { + await page.goto('./'); + await page.getByRole('button', { name: '최상위 작업 추가' }).click(); + }); + + test('routes an invalid required-field submit through product validation feedback', async ({ page }) => { + const form = page.locator('form[data-editor-form="true"]'); + const toast = page.locator('#toast'); + + await expect(form).toBeVisible(); + await form.evaluate((editorForm) => editorForm.requestSubmit()); + + await expect(page.locator('#editor-errors')).toContainText('최상위 작업은 단계 값을 입력해야 합니다.'); + await expect(toast).toHaveClass(/show/); + await expect(toast).toContainText('입력값을 올바르게 수정해야 저장할 수 있습니다.'); + await expect(form).toBeVisible(); + }); + + test('flushes pending validation before an immediate valid submit', async ({ page }) => { + const phaseInput = page.locator('[data-testid="editor-phase"]'); + + await phaseInput.evaluate((input) => { + input.value = '즉시 제출 단계'; + input.dispatchEvent(new Event('input', { bubbles: true })); + input.form.requestSubmit(); + }); + + await expect(page.locator('form[data-editor-form="true"]')).toHaveCount(0); + await expect( + page.locator('tbody tr[data-task-id]').filter({ hasText: '즉시 제출 단계' }), + ).toHaveCount(1); + }); +}); From f4a01b38729b0822a86ca6bb8d9a6da7cf14f1d8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:38:15 +0900 Subject: [PATCH 3/8] chore(a11y): keep validation choice local to editor --- .jules/palette.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index 7a0f1c52..0bbf5248 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -115,7 +115,3 @@ ## $(date +%Y-%m-%d) - Prevent accidental data loss in inline editors **Learning:** Forms that take a long time to fill out (like a WBS editor) are prone to accidental closure by users pressing `Escape` or clicking cancel. This causes immediate data loss without any warning, resulting in frustration. **Action:** When working on editors that can be dismissed, track whether the user has modified any fields compared to their initial state. If there are changes, intercept the close action and present a confirmation dialog (`window.confirm`) to ensure they really want to discard their edits. Bypass this for intentional saves or explicit data overrides. - -## $(date +%Y-%m-%d) - Replace native disabled with aria-disabled for form submit buttons -**Learning:** Using the native `disabled` attribute on a form's primary submit button blocks all click and key events. Consequently, keyboard-only or screen reader users who trigger the submit action on an invalid form receive no feedback because the event never fires. -**Action:** Use `aria-disabled="true"` alongside `disabled = false` for form submit buttons when validation fails. Handle the form's `submit` event to check for `aria-disabled`, prevent the submission, and proactively display a toast notification or inline feedback to explain why the action is blocked. From e8414edf4eb51b46280ef5471390b2a37ec07f5a Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:41:36 +0000 Subject: [PATCH 4/8] I've fixed the accessibility issue with the editor's validation feedback! Now, when a user attempts to save an invalid form, the code flushes the pending draft validation first before checking the aria-disabled state of the save button. This prevents any stale state from blocking the validation feedback toast. Additionally, I've disabled the native HTML form validation on the editor form to ensure our custom feedback is always presented consistently. --- app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index ef11e68b..7cebc6b4 100644 --- a/app.js +++ b/app.js @@ -429,6 +429,7 @@ function bindTableEvents(renderDraftValidation, updateEditorDraftFromEvent) { return; } event.preventDefault(); + renderDraftValidation.flush(); const saveButton = form.querySelector('button[type="submit"]'); if (saveButton && saveButton.getAttribute('aria-disabled') === 'true') { @@ -436,7 +437,6 @@ function bindTableEvents(renderDraftValidation, updateEditorDraftFromEvent) { return; } - renderDraftValidation.flush(); saveEditor(); }); @@ -797,6 +797,7 @@ function renderEditorRow(anchorId) { panel.className = 'editor-panel'; const form = document.createElement('form'); form.dataset.editorForm = 'true'; + form.noValidate = true; const editorGrid = document.createElement('div'); editorGrid.className = 'editor-grid'; From f1a993d6a99492566b8ce0c85009eeb19fe58367 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 4 Sep 2026 20:33:00 +0000 Subject: [PATCH 5/8] Hello! Jules here. I have updated the code to fix the accessibility issue regarding the editor's validation feedback. Now, when saving an invalid form, the code will flush the pending draft validation first before checking the aria-disabled state of the save button. This prevents a stale state from gating the validation feedback toast. I also disabled native form validation on the editor form to ensure our custom feedback is presented consistently. From ba81b4784cb92524002c5cc63381a5793e4b3efa Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 5 Sep 2026 02:28:16 +0000 Subject: [PATCH 6/8] I have implemented the accessibility fixes for the editor's validation feedback. When a user attempts to save an invalid form, I updated the logic to flush the pending draft validation first before checking the aria-disabled state of the save button. This prevents a stale state from blocking the validation error toast. I also disabled native form validation on the editor form to ensure our custom messages are presented consistently. From 64cb5e02163e519cbd4189d6424c864ebb3f88c6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 5 Sep 2026 11:29:07 +0000 Subject: [PATCH 7/8] Hi, I'm Jules! I've just updated the code to fix the accessibility issues with the editor's validation feedback. Now, when a user tries saving an invalid form, the system will flush the pending draft validation first before reading the aria-disabled state of the save button. This prevents any stale state from blocking the validation feedback toast. I also went ahead and disabled the native form validation on the editor form to ensure that our custom feedback is presented consistently. Let me know if you need me to make any other adjustments! From 9422f553d2c5042289e0e7e7f4dce568d9614e86 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 5 Sep 2026 15:22:58 +0000 Subject: [PATCH 8/8] I have fixed the accessibility issue with the editor's validation feedback. Now, when saving an invalid form, the code flushes the pending draft validation first before checking the aria-disabled state of the save button. This prevents a stale state from gating the validation feedback toast. I also disabled native form validation on the editor form to ensure our custom feedback is presented consistently.