-
Notifications
You must be signed in to change notification settings - Fork 1
fix(a11y): expose async button busy states #1352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seonghobae
wants to merge
34
commits into
develop
Choose a base branch
from
fix/aria-busy-clean-scope
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
6508e23
fix(a11y): expose async button busy states
seonghobae a5c8414
docs(a11y): record async busy-state evidence
seonghobae c85a871
ci: repair document action busy identity
seonghobae ffa2f69
ci: verify document action busy identity without new test deps
seonghobae 026ca37
fix(a11y): identify the active document action
github-actions[bot] cdd96f7
chore(ci): revalidate active document action accessibility
seonghobae 0eff0c4
Merge branch 'develop' into fix/aria-busy-clean-scope
opencode-agent[bot] 537bcdb
test(a11y): avoid dynamic regexp in busy-state evidence
seonghobae f969010
Merge branch 'develop' into fix/aria-busy-clean-scope
seonghobae b1a9762
Merge branch 'develop' into fix/aria-busy-clean-scope
seonghobae 87d2d47
Merge branch 'develop' into fix/aria-busy-clean-scope
opencode-agent[bot] 491c52a
Merge remote-tracking branch 'origin/develop' into HEAD
seonghobae 65ab8cb
test: type document action busy-state props
seonghobae 8b7731d
Merge branch 'develop' into fix/aria-busy-clean-scope
opencode-agent[bot] eb8af38
Merge branch 'develop' into fix/aria-busy-clean-scope
seonghobae 1b46593
fix(a11y): distinguish review submission from evidence loading
seonghobae 43be57e
fix(projects): stop claiming unsaved evidence notes are persisted
seonghobae 67fed84
Merge protected develop into aria-busy accessibility repair
seonghobae e11a8e9
fix(session): require explicit authenticated response claims
seonghobae 6abe074
fix(projects): distinguish unavailable sources from empty data
seonghobae 0b28f8e
fix(projects): preserve progress evidence boundaries
seonghobae 517e560
merge: preserve protected attachment repair in project evidence work
seonghobae 9a8c614
test(a11y): distinguish evidence fetch from save busy state
seonghobae aa6b618
test(a11y): hold document action lock through refresh
seonghobae 0809249
fix(a11y): retain document action lock through refresh
seonghobae 40fcc7c
fix(a11y): scope evidence review busy state to save
seonghobae 010facb
docs(a11y): record busy-state lifecycle invariants
seonghobae 15ed98a
test(a11y): preserve active status on rejected reentry
seonghobae c45ed60
fix(a11y): guard document reentry before validation
seonghobae 8c9418d
fix(data): 문서 요청과 목록 갱신 상태를 분리
seonghobae ff6f82c
merge: 원격 접근성 수리와 문서 갱신 보호를 통합
seonghobae 215db67
merge: 원격 재진입 보호 수정을 보존
seonghobae cc30ba6
fix: 동일 품질 화면의 반복 DOM 읽기를 제거
seonghobae 1b8497f
fix: 프로젝트 smoke 응답의 필수 계약을 복원
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Async button busy-state accessibility | ||
|
|
||
| ## Decision | ||
|
|
||
| Naruon exposes `aria-busy=true` only while an action control is actively processing its asynchronous operation. The existing `disabled` behavior remains responsible for preventing duplicate activation; `aria-busy` communicates the processing state to the accessibility API rather than replacing the disabled-state contract. | ||
|
|
||
| The bounded change applies to the project evidence-review action, repository document actions, and duplicate-thread intent action. It does not claim whole-product accessibility conformance or imply that every statically disabled control is busy. | ||
|
|
||
| ## Evidence boundary | ||
|
|
||
| WAI-ARIA defines `aria-busy` as a state indicating that an element is being modified and that assistive technologies can defer exposing intermediate changes until the operation is complete. The attribute is defined for all elements in the base markup and defaults to `false`. This supports binding `aria-busy` to the same boolean state that represents the in-flight asynchronous action, while leaving ordinary unavailable controls unmarked as busy. | ||
|
|
||
| ## Verification | ||
|
|
||
| Merge readiness is determined only from the unchanged current PR head after repository CI, security, coverage, review, and protected-branch requirements pass. The accessibility attribute itself is not a substitute for rendered assistive-technology testing across supported environments. | ||
|
|
||
| ## Reference — APA 7th | ||
|
|
||
| World Wide Web Consortium. (2026, June 4). *Accessible Rich Internet Applications (WAI-ARIA) 1.3* (Working Draft). https://www.w3.org/TR/2026/WD-wai-aria-1.3-20260604/ | ||
|
|
||
| ## Action identity | ||
|
|
||
| A shared loading lock may disable sibling document actions to prevent conflicting writes, but it must not announce every sibling as the operation that is currently processing. Naruon therefore records the initiating document action separately from the shared lock. Only the initiating upload, reparse, embedding-regeneration, HWP-conversion, or WebDAV-materialization button exposes `aria-busy=true`; disabled siblings remain `aria-busy=false`. | ||
|
|
||
| The focused server-rendered regression exercises the real button group and fails if a shared boolean again marks every document action busy. Stable `data-document-action` identifiers exist only to bind rendered accessibility evidence to the initiating operation; they do not authorize or execute an action. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
frontend/src/components/data-layout/DocumentRepositoryTab.busy-state.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import { renderToStaticMarkup } from 'react-dom/server'; | ||
| import { describe, expect, it } from 'vitest'; | ||
|
|
||
| import { DocumentRepositoryTab } from './DocumentRepositoryTab'; | ||
|
|
||
| const workspaceDocument = { | ||
| asset_key: 'workspace-document-1', | ||
| asset_type: 'workspace_document', | ||
| display_name: '운영 계약.md', | ||
| source_label: '로컬 문서', | ||
| state_code: 'ready', | ||
| detail_text: '검토 가능한 문서 근거', | ||
| content_chars: 128, | ||
| captured_at: '2026-08-15T09:00:00+09:00', | ||
| evidence_source: 'workspace_document', | ||
| thread_key: 'thread-1', | ||
| provider_write_executed: false, | ||
| }; | ||
|
|
||
| function renderRepository(activeDocumentAction: string | null) { | ||
| return renderToStaticMarkup( | ||
| <DocumentRepositoryTab | ||
| {...({ | ||
| writebackStatus: 'idle', | ||
| writebackResult: null, | ||
| requestWebdavWritebackIntent: () => undefined, | ||
| isWritebackLoading: false, | ||
| canRequestWebdavWriteback: true, | ||
| selectedWebdavAccount: { | ||
| source_id: 'webdav-1', | ||
| display_label: '업무 문서', | ||
| writeback_enabled: true, | ||
| }, | ||
| isWebdavSourceLoading: false, | ||
| setSelectedWebdavSourceId: () => undefined, | ||
| uniqueThreadStatus: 'idle', | ||
| uniqueThreadResult: null, | ||
| requestUniqueThreadIntent: () => undefined, | ||
| isUniqueThreadLoading: false, | ||
| connectorEvents: [], | ||
| dataSurfaceStatus: 'ready', | ||
| dataQualitySurface: null, | ||
| embeddingStage: null, | ||
| emailRepository: null, | ||
| attachmentRepository: null, | ||
| handleEmailImportFileChange: () => undefined, | ||
| requestEmailFileImport: () => undefined, | ||
| isEmailImportLoading: false, | ||
| emailImportFiles: [], | ||
| emailImportStatus: 'idle', | ||
| emailImportResult: null, | ||
| handleDocumentFileChange: () => undefined, | ||
| requestDocumentUpload: () => undefined, | ||
| isDocumentActionLoading: true, | ||
| activeDocumentAction, | ||
| documentUploadFiles: [], | ||
| documentActionStatus: 'loading', | ||
| documentActionResult: null, | ||
| webdavAccountStatus: 'ready', | ||
| webdavAccounts: [{ | ||
| source_id: 'webdav-1', | ||
| display_label: '업무 문서', | ||
| writeback_enabled: true, | ||
| }], | ||
| webdavAccountMap: new Map(), | ||
| projectFolders: [], | ||
| selectedRepositoryAssetKey: workspaceDocument.asset_key, | ||
| setSelectedRepositoryAssetKey: () => undefined, | ||
| repositoryAssets: [workspaceDocument], | ||
| selectedWorkspaceDocument: workspaceDocument, | ||
| requestDocumentAction: () => undefined, | ||
| } as never)} | ||
| />, | ||
| ); | ||
| } | ||
|
|
||
| function getActionButtonTag(markup: string, action: string) { | ||
| const match = markup.match( | ||
| new RegExp(`<button[^>]*data-document-action="${action}"[^>]*>`), | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| ); | ||
| if (!match) { | ||
| throw new Error(`missing rendered button for ${action}`); | ||
| } | ||
| return match[0]; | ||
| } | ||
|
|
||
| describe('DocumentRepositoryTab action busy identity', () => { | ||
| it('marks only the initiating document action as busy', () => { | ||
| const markup = renderRepository('reparse'); | ||
|
|
||
| expect(getActionButtonTag(markup, 'reparse')).toContain('aria-busy="true"'); | ||
| expect(getActionButtonTag(markup, 'embedding-regeneration-intent')).toContain('aria-busy="false"'); | ||
| expect(getActionButtonTag(markup, 'hwp-conversion-intent')).toContain('aria-busy="false"'); | ||
| expect(getActionButtonTag(markup, 'webdav-materialization-intent')).toContain('aria-busy="false"'); | ||
| expect(getActionButtonTag(markup, 'upload')).toContain('aria-busy="false"'); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.