From c3f02ce3885c3800a7e844172498b357e3e1bc0f Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 5 Sep 2026 13:52:19 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=EC=A0=91=EA=B7=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EB=B2=84=ED=8A=BC=20=ED=8F=AC=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=91=EA=B7=BC=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/palette.md | 3 +++ frontend/src/components/modals/ExportModal.test.tsx | 10 ++++++++-- frontend/src/components/modals/ExportModal.tsx | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index bd0f73248..c2446fdd5 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -57,3 +57,6 @@ ## 2026-07-30 - Add window.confirm for destructive actions **Learning:** Destructive actions like deleting groups and edge relationships previously occurred immediately without user confirmation. **Action:** Always wrap delete operations with window.confirm() dialogs and ensure corresponding tests successfully mock window.confirm. +## 2026-09-05 - Use aria-disabled for focusable disabled buttons +**Learning:** Using the native disabled attribute prevents focus, making descriptive hints (like aria-describedby) undiscoverable by keyboard users. +**Action:** Use aria-disabled={true} and preventDefault with inline styles for disabled buttons that require explanation. diff --git a/frontend/src/components/modals/ExportModal.test.tsx b/frontend/src/components/modals/ExportModal.test.tsx index bd89c5384..2c61ce003 100644 --- a/frontend/src/components/modals/ExportModal.test.tsx +++ b/frontend/src/components/modals/ExportModal.test.tsx @@ -1,5 +1,6 @@ import '@testing-library/jest-dom/vitest'; import { cleanup, fireEvent, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { ExportModal } from './ExportModal'; @@ -171,13 +172,18 @@ describe('ExportModal', () => { expect(screen.getByRole('button', { name: '데이터 사전 Markdown 내보내기' })).toBeDisabled(); }); - it('exposes access-control guidance for disabled button', () => { + it('exposes access-control guidance for disabled button', async () => { + const user = userEvent.setup(); render(); expect(screen.getByText('접근 권한 관리는 프로젝트 권한 설정에서 처리합니다.')).toBeInTheDocument(); const accessManagementButton = screen.getByRole('button', { name: '접근 관리' }); - expect(accessManagementButton).toBeDisabled(); + expect(accessManagementButton).toHaveAttribute('aria-disabled', 'true'); expect(accessManagementButton).toHaveAttribute('aria-describedby', 'share-export-access-hint'); expect(accessManagementButton).not.toHaveAttribute('title'); + + accessManagementButton.focus(); + expect(accessManagementButton).toHaveFocus(); + await user.keyboard('{Enter}'); }); }); diff --git a/frontend/src/components/modals/ExportModal.tsx b/frontend/src/components/modals/ExportModal.tsx index 995393ceb..d53787740 100644 --- a/frontend/src/components/modals/ExportModal.tsx +++ b/frontend/src/components/modals/ExportModal.tsx @@ -202,7 +202,9 @@ export function ExportModal({ )}