fix(a11y): announce empty mail and search states without wrapping actions - #1576
fix(a11y): announce empty mail and search states without wrapping actions#1576seonghobae wants to merge 22 commits into
Conversation
…서 빈 상태(Empty State)를 표시하는 요소에 role="status"와 aria-live="polite" 속성을 추가하여 스크린 리더 접근성을 개선했습니다.
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughEmailList now separates inbox and search empty states and ignores stale responses. SearchLayout adds live-region semantics for empty results, empty relationships, and capture failures. Unit and Playwright tests validate these behaviors. ChangesEmail search state and ordering
SearchLayout accessibility semantics
End-to-end accessibility validation
Estimated code review effort: 3 (Moderate) | ~25 minutes Merge Risk: ⚪ Minimal · up to This change distinguishes inbox and search empty states, prevents stale search responses from replacing newer inbox data, and improves screen-reader announcements for empty and error states. No current merge-blocking risk is evidenced. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review Please review exact head |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Checkout identity =='
git rev-parse HEAD
git status --short
git show -s --format='commit=%H%nparents=%P%nsubject=%s' HEAD
echo '== Effective diff paths against protected base =='
git diff --name-status 042b0c70531b229af3acbd0421a2f23098d848b3..HEAD
echo '== Effective diff =='
git diff --unified=80 042b0c70531b229af3acbd0421a2f23098d848b3..HEAD -- \
'*EmailList.tsx' '*SearchLayout.tsx' '*TasksLayout.tsx' '*DocumentRepositoryTab.tsx' '*SearchLayout.live-region.test.tsx'
echo '== Candidate files =='
fd -t f -a '^(EmailList|SearchLayout|TasksLayout|DocumentRepositoryTab)(\.live-region\.test)?\.tsx$' . \
-E node_modules -E dist -E build
echo '== Component and test outlines =='
for f in $(fd -t f -a '^(EmailList|SearchLayout|TasksLayout|DocumentRepositoryTab)(\.live-region\.test)?\.tsx$' . -E node_modules -E dist -E build); do
echo "--- $f"
ast-grep outline "$f" --items all || true
done
echo '== Live roles, focus APIs, and interactive controls in changed components =='
rg -n -C 8 --glob '*EmailList.tsx' --glob '*SearchLayout.tsx' --glob '*TasksLayout.tsx' --glob '*DocumentRepositoryTab.tsx' \
'role=(["'\''](?:status|alert)|\{)|aria-live=|aria-atomic=|autoFocus|\.focus\s*\(|<button\b|<Button\b|<a\b|<input\b|<select\b|<textarea\b' . \
-g '!node_modules/**' -g '!dist/**' -g '!build/**'
echo '== Focused test =='
test_file=$(fd -t f -a '^SearchLayout\.live-region\.test\.tsx$' . -E node_modules -E dist -E build | head -n 1 || true)
if [ -n "$test_file" ]; then
cat -n "$test_file"
fi
echo '== Test runner references =='
rg -n -C 3 --glob 'package.json' --glob 'vitest.config.*' --glob 'vite.config.*' --glob 'jest.config.*' \
'vitest|jest|test' . -g '!node_modules/**' -g '!dist/**' -g '!build/**'Length of output: 173402 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Tasks state lifecycle and changed render branches =='
sed -n '160,290p' frontend/src/components/TasksLayout.tsx
sed -n '300,410p' frontend/src/components/TasksLayout.tsx
sed -n '480,705p' frontend/src/components/TasksLayout.tsx
echo '== Document repository changed render branches =='
sed -n '70,125p' frontend/src/components/data-layout/DocumentRepositoryTab.tsx
sed -n '270,315p' frontend/src/components/data-layout/DocumentRepositoryTab.tsx
sed -n '565,615p' frontend/src/components/data-layout/DocumentRepositoryTab.tsx
echo '== Search lifecycle and Sender DAG state derivation =='
sed -n '340,610p' frontend/src/components/SearchLayout.tsx
sed -n '780,970p' frontend/src/components/SearchLayout.tsx
echo '== Related accessibility and component tests =='
rg -n -C 3 --glob '*.{test,spec}.{ts,tsx}' \
'role=.status.|aria-live|role=.alert.|SearchLayout|TasksLayout|DocumentRepositoryTab|EmailList' \
frontend/src -g '!node_modules/**'
echo '== Test setup =='
cat -n frontend/src/test/setup.ts
cat -n frontend/vitest.config.ts
echo '== Current PR metadata, reviews, and checks =='
gh pr view 1576 --repo ContextualWisdomLab/naruon \
--json headRefOid,isDraft,mergeStateStatus,reviewDecision,reviews,statusCheckRollup,urlLength of output: 84547
I found two accessibility issues.
The Sender DAG repair is correct in source. The empty relationship message is a standalone polite status. The capture button is outside that status. The capture failure uses
I could not execute frontend tests in this environment. The exact-head hosted checks are queued, and browser or assistive-technology evidence is still incomplete. Keep ✅ Action performedReview finished.
|
Acknowledged. However, there is no command directed to me in this comment, and it seems to be an automated trigger for another bot (@coderabbitai). No action required from me. |
…서 빈 상태(Empty State)를 표시하는 요소에 role="status"와 aria-live="polite" 속성을 추가하여 스크린 리더 접근성을 개선했습니다.
…서 빈 상태(Empty State)를 표시하는 요소에 role="status"와 aria-live="polite" 속성을 추가하여 스크린 리더 접근성을 개선했습니다.
|
@coderabbitai review Please review the unchanged exact head |
Acknowledged. However, there is no command directed to me in this comment, and it seems to be an automated trigger for another bot (@coderabbitai). No action required from me. |
…서 빈 상태(Empty State)를 표시하는 요소에 role="status"와 aria-live="polite" 속성을 추가하여 스크린 리더 접근성을 개선했습니다.
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
expected_head='270acbc60b637e42ac9aaa9a5f0e8a987e3ea9fd'
actual_head="$(git rev-parse HEAD)"
printf 'HEAD=%s\n' "$actual_head"
if [ "$actual_head" != "$expected_head" ]; then
echo "ERROR: checkout does not match requested exact head" >&2
exit 2
fi
echo '== Commit identity =='
git show -s --format='commit=%H%nparents=%P%nsubject=%s' HEAD
git status --short
echo '== Merge-base and effective changed files =='
base="$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master 2>/dev/null || true)"
printf 'base=%s\n' "$base"
if [ -n "$base" ]; then
git diff --name-status "$base"..HEAD
echo '== Effective diff: requested surfaces =='
git diff --unified=60 "$base"..HEAD -- \
'frontend/src/components/EmailList.tsx' \
'frontend/src/components/SearchLayout.tsx' \
'frontend/src/components/EmailList.test.tsx' \
'frontend/src/components/EmailList.request-ordering.test.tsx' \
'frontend/src/components/SearchLayout.live-region.test.tsx' \
'frontend/tests/e2e/mail-search-accessibility.spec.ts'
fi
echo '== Candidate file existence =='
for f in \
frontend/src/components/EmailList.tsx \
frontend/src/components/SearchLayout.tsx \
frontend/src/components/EmailList.test.tsx \
frontend/src/components/EmailList.request-ordering.test.tsx \
frontend/src/components/SearchLayout.live-region.test.tsx \
frontend/tests/e2e/mail-search-accessibility.spec.ts \
frontend/src/components/TasksLayout.tsx \
frontend/src/components/data-layout/DocumentRepositoryTab.tsx; do
test -f "$f" && echo "$f"
done
echo '== Component outlines =='
ast-grep outline frontend/src/components/EmailList.tsx --items all
ast-grep outline frontend/src/components/SearchLayout.tsx --items all
echo '== EmailList: state, effects, fetch and empty-state render =='
rg -n -C 14 --glob 'EmailList.tsx' \
'request|fetchEmails|emptyStateMode|isSearching|useEffect|role=|aria-live|emptyCopy|search' \
frontend/src/components
echo '== SearchLayout: status, alert, buttons, focus APIs and SenderDagPanel =='
rg -n -C 12 --glob 'SearchLayout.tsx' \
'SenderDagPanel|role=|aria-live|aria-atomic|capture|relationships|\.focus\s*\(|autoFocus|<button\b|<Button\b|<input\b|<a\b' \
frontend/src/components
echo '== Tasks and document duplicate-status surfaces at this head =='
rg -n -C 8 --glob 'TasksLayout.tsx' --glob 'DocumentRepositoryTab.tsx' \
'role=|aria-live|status|empty|없습니다' \
frontend/src/components
echo '== Focused unit tests =='
cat -n frontend/src/components/EmailList.test.tsx
cat -n frontend/src/components/EmailList.request-ordering.test.tsx
cat -n frontend/src/components/SearchLayout.live-region.test.tsx
echo '== Playwright contract and configuration references =='
cat -n frontend/tests/e2e/mail-search-accessibility.spec.ts
rg -n -C 5 --glob 'playwright.config.*' --glob 'package.json' --glob '*e2e*' \
'testDir|projects|desktop|baseURL|webServer|mail-search-accessibility|playwright' \
frontend
echo '== Hosted exact-head metadata and checks =='
gh pr view 1576 --repo ContextualWisdomLab/naruon \
--json headRefOid,isDraft,mergeStateStatus,reviewDecision,statusCheckRollup,urlLength of output: 3586 🏁 Script executed: #!/bin/bash
set -euo pipefail
repo='ContextualWisdomLab/naruon'
head='270acbc60b637e42ac9aaa9a5f0e8a987e3ea9fd'
echo '== Local requested-object availability =='
if git cat-file -e "${head}^{commit}" 2>/dev/null; then
echo 'local_commit=yes'
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
echo '== Local effective base candidates =='
for ref in origin/main origin/master; do
if git show-ref --verify --quiet "refs/remotes/$ref"; then
base="$(git merge-base "$head" "$ref")"
printf '%s=%s\n' "$ref" "$base"
git diff --name-status "$base" "$head"
fi
done
echo '== Exact-head files and diff =='
git show --name-status --format='' "$head"
git show --unified=45 "$head" -- \
frontend/src/components/EmailList.tsx \
frontend/src/components/SearchLayout.tsx \
frontend/src/components/EmailList.test.tsx \
frontend/src/components/EmailList.request-ordering.test.tsx \
frontend/src/components/SearchLayout.live-region.test.tsx \
frontend/tests/e2e/mail-search-accessibility.spec.ts
echo '== Exact-head live status surfaces outside effective delta =='
for f in frontend/src/components/TasksLayout.tsx frontend/src/components/data-layout/DocumentRepositoryTab.tsx; do
echo "--- $f"
git show "${head}:${f}" 2>/dev/null | nl -ba | grep -E -C 8 'role=|aria-live|status|empty|없습니다' || true
done
else
echo 'local_commit=no'
fi
echo '== GitHub requested commit =='
gh api "repos/${repo}/commits/${head}" \
--jq '{sha,parents:[.parents[].sha],message:.commit.message,files:[.files[]|{filename,status,additions,deletions,patch}]}'
echo '== Pull request metadata and exact-head checks =='
gh pr view 1576 --repo "$repo" \
--json headRefOid,isDraft,mergeStateStatus,reviewDecision,statusCheckRollup,urlLength of output: 14848 🏁 Script executed: #!/bin/bash
set -euo pipefail
repo='ContextualWisdomLab/naruon'
head='270acbc60b637e42ac9aaa9a5f0e8a987e3ea9fd'
echo '== Requested-head ancestry =='
git log --format='%H %P %s' --max-count=35 "$head"
echo '== Requested-head tree: six review files =='
for f in \
frontend/src/components/EmailList.tsx \
frontend/src/components/SearchLayout.tsx \
frontend/src/components/EmailList.test.tsx \
frontend/src/components/EmailList.request-ordering.test.tsx \
frontend/src/components/SearchLayout.live-region.test.tsx \
frontend/tests/e2e/mail-search-accessibility.spec.ts; do
echo "--- $f"
git show "${head}:${f}" | cat -n
done
echo '== Requested-head tree: simultaneous-empty and focus surfaces =='
for f in \
frontend/src/components/TasksLayout.tsx \
frontend/src/components/data-layout/DocumentRepositoryTab.tsx; do
echo "--- $f"
git show "${head}:${f}" | grep -n -E -C 10 \
'role=|aria-live|aria-atomic|\.focus\s*\(|autoFocus|onKeyDown|empty|없습니다' || true
done
echo '== GitHub compare requested head against default branch =='
default_branch="$(gh api "repos/${repo}" --jq '.default_branch')"
printf 'default_branch=%s\n' "$default_branch"
gh api "repos/${repo}/compare/${default_branch}...${head}" \
--jq '{status,ahead_by,behind_by,merge_base_commit:.merge_base_commit.sha,files:[.files[]|{filename,status,additions,deletions,patch}]}'
echo '== Relationship between requested commit and current PR head =='
current_head="$(gh pr view 1576 --repo "$repo" --json headRefOid --jq '.headRefOid')"
printf 'pr_head=%s\n' "$current_head"
gh api "repos/${repo}/compare/${head}...${current_head}" \
--jq '{status,ahead_by,behind_by,merge_base_commit:.merge_base_commit.sha}'Length of output: 157163 🏁 Script executed: #!/bin/bash
set -euo pipefail
head='270acbc60b637e42ac9aaa9a5f0e8a987e3ea9fd'
current='58df9fb7b7d09688c4dc13c8ce2a312bfa6679ad'
echo '== Exact-head E2E helper candidates =='
fd -t f -a '^(helpers|playwright\.config|package)\.(ts|tsx|js|json)$' frontend
for f in $(fd -t f -a '^(helpers|playwright\.config|package)\.(ts|tsx|js|json)$' frontend); do
echo "--- $f"
git show "${head}:${f}" 2>/dev/null | cat -n || true
done
echo '== Exact-head browser-route and desktop workspace references =='
git grep -n -E \
'mockDashboardApi|데스크톱 메일 작업공간|메일함 바로가기|function.*Desktop|Desktop.*Mail|EmailList' \
"$head" -- frontend/tests/e2e frontend/src 2>/dev/null || true
echo '== Exact-head accessibility-test registration =='
git grep -n -E \
'mail-search-accessibility|testDir|projects:|name: .desktop.|desktop' \
"$head" -- frontend 2>/dev/null || true
echo '== Only commits after the requested head =='
git log --format='%H %P %s' "${head}..${current}"
git diff --name-status "$head..$current"
git diff --unified=20 "$head..$current" -- frontend/tests/e2e/mail-search-accessibility.spec.tsLength of output: 118488 🏁 Script executed: #!/bin/bash
set -euo pipefail
head='270acbc60b637e42ac9aaa9a5f0e8a987e3ea9fd'
echo '== Playwright configuration at requested head =='
git show "${head}:frontend/playwright.config.ts" | cat -n
echo '== mockDashboardApi implementation at requested head =='
git show "${head}:frontend/tests/e2e/helpers.ts" | sed -n '780,940p' | cat -n
echo '== Helper search and ontology mock branches =='
git show "${head}:frontend/tests/e2e/helpers.ts" \
| grep -n -E -C 12 \
"api/search|ontology/relationships|Q2 출시 계획|launch|route\(" || true
echo '== Desktop workspace render behavior at requested head =='
git show "${head}:frontend/src/components/WorkspaceHome.tsx" \
| sed -n '740,885p' | cat -n
echo '== Current-head comparison identity only =='
git diff --name-status "$head..58df9fb7b7d09688c4dc13c8ce2a312bfa6679ad"Length of output: 21832
I found no blocking issues in the requested scope.
The Playwright tests validate browser-rendered roles, The current You are interacting with an AI system. |
Current authority
develop@042b0c70531b229af3acbd0421a2f23098d848b3palette/dynamic-empty-state-a11y-590398598580493568758df9fb7b7d09688c4dc13c8ce2a312bfa6679ad030675b6f42ebf8493284727d989bee0b944f080EmailList.tsx,EmailList.test.tsx,EmailList.request-ordering.test.tsx,SearchLayout.tsx,SearchLayout.live-region.test.tsx,frontend/tests/e2e/mail-search-accessibility.spec.tsReady is review admission only. No predecessor check or review evidence is transferred to this new commit SHA.
Product contracts retained
The effective tree preserves the verified causal repairs developed earlier in this PR:
받은 메일이 없습니다), while an actually submitted empty search uses맥락 검색 결과가 없습니다; editing a query alone does not change result-set authority and clearing returns to inbox authority;This browser semantics contract does not by itself prove what VoiceOver/NVDA/JAWS spoke. Do not claim screen-reader speech verification without actual AT evidence.
Concurrent child audit and non-force repair
After
270acbc60b637e42ac9aaa9a5f0e8a987e3ea9fdhad completed repository-owned CI, direct child33f1e14555663dcc9a6c1a1510a280bc33a201bfarrived normally. It was read and preserved in ancestry rather than treated as a race.That child removed the dedicated request-ordering regression and browser E2E, regressed the initial inbox assertion back to search-empty copy, duplicated the SearchLayout empty-state test into the broad product-events suite, removed the request-sequence/source-state repair, and reintroduced repository-root helper
patch_email_list_test.js. Those changes discarded already-validated buyer-visible behavior and test authority; they were not a new independent product requirement.Ordinary child
58df9fb7b7d09688c4dc13c8ce2a312bfa6679adtherefore adopts the exact reviewed tree from270acbc...while keeping33f1e145...in ancestry. The branch ref was advanced withforce=false; there was no destructive rebase or force-push.Evidence boundary
The exact tree now equals the previously exercised
270acbc...tree, but exact-head governance is SHA-bound. The following are predecessor evidence only and are not claimed as current-head GREEN for58df9fb...:270acbc...Application CI34022935311succeeded, including frontend tests/lint/build, Playwright Chromium andRun full product smoke, plus backend Python 3.14 lint/tests;34022935317, Semgrep34022935287, Bandit34022935385, and Docker34022935414succeeded;34022935340failed in the canonical central dispatch/verdict path;270acbc...with no actionable comments on the current six-file delta, but that review is not transferred to58df9fb...;CHANGES_REQUESTEDreview is on olda0c544b...; its only inline thread was resolved after the scope was narrowed.A fresh CodeRabbit request after Ready hit the included-review limit while trying to process the now-superseded
33f1e145...; do not manufacture a source commit to bypass that external review-capacity limit. Current-head OpenCode/CodeRabbit evidence may arrive through normal governance without changing the product tree.Merge boundary
Do not merge until the unchanged
58df9fb...head has all applicable required checks terminal-success, no valid unresolved current-head finding, and qualifying current-head robot-review evidence underdocs/development/merge-gate-policy.md. CodeQL remains a canonical.githubowner-path blocker; do not widen authorization or add a Naruon-local bypass. Actual assistive-technology speech evidence remains required before making any stronger screen-reader-announcement claim.The product Gap ledger remains owned by #1557; this PR does not edit
docs/product-technical-gap-baseline.mdin parallel. Protected AGENTS/CLAUDE routing-language drift remains tracked by #1548.UI Delivery Gate: FAIL (current-head evidence incomplete). The intended source/test/browser contract is restored, but the new exact head still needs its own hosted gate and robot-review evidence. No self-approval, force-push, destructive rebase, dummy/no-op requeue, predecessor-evidence transfer, gate weakening, or unsupported accessibility/performance claim is used.