Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

## 2026-08-10 - Support focus management when using aria-disabled on form buttons
**Learning:** Replacing native `disabled` attributes with `aria-disabled="true"` on form submission buttons (like "Save") preserves focusability for screen readers, but browsers will still allow native form submission because the button isn't truly disabled.
**Action:** When applying `aria-disabled` to a submit button inside a `<form>`, manually intercept the form's `submit` event listener to check for the `aria-disabled` attribute on the button, and call `event.preventDefault()` if present to block submission.
13 changes: 12 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('์ž…๋ ฅ๊ฐ’์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜์ •ํ•ด์•ผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.');
return;
}

renderDraftValidation.flush();
saveEditor();
});
Expand Down Expand Up @@ -1068,7 +1075,11 @@ function renderEditorValidation() {

const saveButton = form.querySelector('button[type="submit"]');
if (saveButton) {
saveButton.disabled = errors.length > 0;
if (errors.length > 0) {
saveButton.setAttribute('aria-disabled', 'true');
} else {
saveButton.removeAttribute('aria-disabled');
}
saveButton.title = errors.length > 0 ? '์ž…๋ ฅ๊ฐ’์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜์ •ํ•ด์•ผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.' : '์ €์žฅ (Enter)';
}

Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"dependencies": {
"@hono/node-server": "^2.0.12",
"hono": "^4.12.32"
"hono": "^4.13.1"
},
"devDependencies": {
"@playwright/test": "1.61.1",
Expand Down
Loading