Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a66393c
Add aria-label to Twitter share button
Mosas2000 May 30, 2026
f8fbda6
Add aria-label to Discord share button
Mosas2000 May 30, 2026
6a3c6e0
Add aria-label to Telegram share button
Mosas2000 May 30, 2026
29f7117
Add aria-label to Reddit share button
Mosas2000 May 30, 2026
d941759
Add aria-label and aria-pressed to snap point buttons
Mosas2000 May 30, 2026
5370b4e
Add aria-label to clear all button
Mosas2000 May 30, 2026
9d8b7b8
Add aria-label to remove drawing button
Mosas2000 May 30, 2026
65ec8cb
Add aria-labels to undo and redo buttons
Mosas2000 May 30, 2026
9f53de8
Add aria-label to close notifications button
Mosas2000 May 30, 2026
d1b3436
Add aria-labels and aria-pressed to filter buttons
Mosas2000 May 30, 2026
f0eff16
Add aria-label to close button in proposal modal
Mosas2000 May 30, 2026
2e01d2a
Add aria-labels to add and remove liquidity buttons
Mosas2000 May 30, 2026
02cc2a6
Add documentation for ARIA label improvements
Mosas2000 May 30, 2026
90fc7a9
Add accessibility tests for ARIA labels
Mosas2000 May 30, 2026
07155cf
Update accessibility checklist with icon button fixes
Mosas2000 May 30, 2026
4683822
chore: add required imports for contract read-only function calls
Mosas2000 May 30, 2026
6bb25a4
refactor: add network parameter to MigrationService constructor
Mosas2000 May 30, 2026
ff3c074
refactor: add helper method for contract address parsing
Mosas2000 May 30, 2026
d47bf62
feat: implement getCurrentVersion method with contract call
Mosas2000 May 30, 2026
eda1f29
feat: implement getMigration method to query migration details
Mosas2000 May 30, 2026
79fe6e1
feat: implement getMigrationData method for data access
Mosas2000 May 30, 2026
adb1e51
feat: implement isMigrationExecuted method for status checking
Mosas2000 May 30, 2026
4033e2b
feat: implement getMigrationCount method to query total migrations
Mosas2000 May 30, 2026
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
23 changes: 21 additions & 2 deletions docs/ACCESSIBILITY_TESTING_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
# Accessibility Testing Checklist - Issue #160
# Accessibility Testing Checklist

## Overview
Use this checklist to verify that aria-describedby attributes are working correctly across all form components.
This checklist covers accessibility testing for form components (Issue #160) and icon-only buttons (Issue #158).

## Issue #158: Icon-Only Buttons ARIA Labels

### Components Fixed
- [x] ShareModal - Social share buttons
- [x] MobileBottomSheet - Snap point buttons
- [x] DrawingToolsPanel - Tool action buttons
- [x] NotificationCenter - Filter and close buttons
- [x] CreateProposalModal - Close button
- [x] PoolPositionRow - Liquidity action buttons

### Testing Requirements
- [ ] All icon-only buttons have descriptive aria-label
- [ ] Toggle buttons have aria-pressed state
- [ ] Decorative icons have aria-hidden="true"
- [ ] Screen readers announce button purpose clearly
- [ ] Button labels are concise and descriptive

## Issue #160: Form Error Announcements

## General Testing

Expand Down
95 changes: 95 additions & 0 deletions frontend/docs/ACCESSIBILITY_ARIA_LABELS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ARIA Labels for Icon-Only Buttons

## Overview

This document outlines the accessibility improvements made to icon-only buttons throughout the application. All icon-only buttons now include descriptive `aria-label` attributes to ensure screen reader users can understand their purpose.

## WCAG Compliance

These changes address WCAG 2.1 Level A compliance, specifically:
- **1.1.1 Non-text Content**: All non-text content has a text alternative that serves the equivalent purpose
- **4.1.2 Name, Role, Value**: User interface components have names that can be programmatically determined

## Components Updated

### ShareModal.tsx
- Twitter share button: `aria-label="Share on Twitter"`
- Discord share button: `aria-label="Share on Discord"`
- Telegram share button: `aria-label="Share on Telegram"`
- Reddit share button: `aria-label="Share on Reddit"`

### MobileBottomSheet.tsx
- Snap point buttons: `aria-label="Snap to position {n}"` with `aria-pressed` state
- Close button: Already had proper `aria-label="Close"`

### DrawingToolsPanel.tsx
- Clear all button: `aria-label="Clear all drawings"`
- Remove drawing button: `aria-label="Remove {type} drawing"`
- Stop drawing button: `aria-label="Stop drawing"`
- Undo button: `aria-label="Undo last action"`
- Redo button: `aria-label="Redo last action"`

### NotificationCenter.tsx
- Close button: `aria-label="Close notifications"`
- Filter buttons:
- All: `aria-label="Show all notifications"` with `aria-pressed`
- Unread: `aria-label="Show unread notifications"` with `aria-pressed`
- Read: `aria-label="Show read notifications"` with `aria-pressed`

### CreateProposalModal.tsx
- Close button: `aria-label="Close modal"`

### PoolPositionRow.tsx
- Add liquidity button: `aria-label="Add liquidity to pool"`
- Remove liquidity button: `aria-label="Remove liquidity from pool"`

## Best Practices

### When to Use aria-label

Use `aria-label` when:
1. A button contains only an icon (no visible text)
2. A button contains only a symbol (×, +, −, etc.)
3. The visible text is not descriptive enough

### When to Use aria-pressed

Use `aria-pressed` for toggle buttons that have an on/off state:
```tsx
<button
aria-label="Show unread notifications"
aria-pressed={filterStatus === 'unread'}
>
Unread
</button>
```

### When to Use aria-hidden

Use `aria-hidden="true"` on decorative icons within buttons that have text labels:
```tsx
<button aria-label="Share on Twitter">
<svg aria-hidden="true">...</svg>
Twitter
</button>
```

## Testing

### Manual Testing
1. Use a screen reader (NVDA, JAWS, VoiceOver) to navigate the application
2. Tab through all interactive elements
3. Verify that each button announces its purpose clearly

### Automated Testing
Run accessibility audits using:
- axe DevTools
- Lighthouse accessibility audit
- WAVE browser extension

## Future Improvements

1. Add keyboard shortcuts for common actions
2. Implement focus management for modals
3. Add live regions for dynamic content updates
4. Ensure all interactive elements have visible focus indicators
1 change: 1 addition & 0 deletions frontend/src/components/CreateProposalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export function CreateProposalModal({
cursor: 'pointer',
padding: '4px',
}}
aria-label="Close modal"
>
×
</button>
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/components/DrawingToolsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function DrawingToolsPanel({
<div className="panel-header">
<h3>Drawing Tools</h3>
{tools.length > 0 && (
<button className="clear-all-btn" onClick={onClearAll}>
<button className="clear-all-btn" onClick={onClearAll} aria-label="Clear all drawings">
Clear All
</button>
)}
Expand Down Expand Up @@ -98,6 +98,7 @@ export function DrawingToolsPanel({
e.stopPropagation();
onRemoveTool(tool.id);
}}
aria-label={`Remove ${tool.type} drawing`}
>
×
</button>
Expand Down Expand Up @@ -175,16 +176,16 @@ export function DrawingToolbar({

{isDrawing && (
<div className="toolbar-actions">
<button onClick={onStopDrawing} className="stop-btn">
<button onClick={onStopDrawing} className="stop-btn" aria-label="Stop drawing">
Stop Drawing
</button>
{onUndo && (
<button onClick={onUndo} className="undo-btn" title="Undo">
<button onClick={onUndo} className="undo-btn" title="Undo" aria-label="Undo last action">
</button>
)}
{onRedo && (
<button onClick={onRedo} className="redo-btn" title="Redo">
<button onClick={onRedo} className="redo-btn" title="Redo" aria-label="Redo last action">
</button>
)}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/MobileBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export function MobileBottomSheet({
? 'bg-blue-500'
: 'bg-neutral-300 dark:bg-neutral-700'
}`}
aria-label={`Snap to position ${index + 1}`}
aria-pressed={currentSnap === index}
/>
))}
</div>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/NotificationCenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export default function NotificationCenter({
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
aria-label="Close notifications"
>
×
</button>
Expand All @@ -100,6 +101,8 @@ export default function NotificationCenter({
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
aria-label="Show all notifications"
aria-pressed={filterStatus === 'all'}
>
All
</button>
Expand All @@ -110,6 +113,8 @@ export default function NotificationCenter({
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
aria-label="Show unread notifications"
aria-pressed={filterStatus === 'unread'}
>
Unread
</button>
Expand All @@ -120,6 +125,8 @@ export default function NotificationCenter({
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
aria-label="Show read notifications"
aria-pressed={filterStatus === 'read'}
>
Read
</button>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/PoolPositionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export function PoolPositionRow({
onClick={onAddLiquidity}
className="px-3 py-1.5 text-xs font-medium text-emerald-400 bg-emerald-500/10 rounded-lg hover:bg-emerald-500/20 transition-colors"
title="Add liquidity"
aria-label="Add liquidity to pool"
>
+
</button>
Expand All @@ -94,6 +95,7 @@ export function PoolPositionRow({
onClick={onRemoveLiquidity}
className="px-3 py-1.5 text-xs font-medium text-red-400 bg-red-500/10 rounded-lg hover:bg-red-500/20 transition-colors"
title="Remove liquidity"
aria-label="Remove liquidity from pool"
>
</button>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/ShareModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export function ShareModal({
<button
onClick={shareToTwitter}
className="flex items-center justify-center gap-2 px-4 py-2 bg-blue-400 hover:bg-blue-500 text-white rounded-lg font-medium transition-colors"
aria-label="Share on Twitter"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
Expand All @@ -123,6 +124,7 @@ export function ShareModal({
<button
onClick={shareToDiscord}
className="flex items-center justify-center gap-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-medium transition-colors"
aria-label="Share on Discord"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515a.074.074 0 00-.079.037c-.211.375-.444.865-.607 1.25a18.27 18.27 0 00-5.487 0c-.163-.385-.396-.875-.607-1.25a.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.08.08 0 00.087-.027c.461-.63.873-1.295 1.226-1.994a.076.076 0 00-.042-.106c-.645-.245-1.26-.561-1.844-.906a.077.077 0 00-.009-.128c.124-.093.248-.189.368-.288a.076.076 0 00.084-.01c3.852 1.755 8.018 1.755 11.829 0a.077.077 0 00.083.01c.12.099.244.195.369.288a.077.077 0 00-.008.129c-.585.345-1.198.66-1.843.906a.077.077 0 00-.041.107c.359.719.77 1.364 1.226 1.994a.076.076 0 00.084.028c2.243-.613 4.438-1.587 5.994-3.03a.076.076 0 00.03-.057c.5-4.718-.838-8.812-3.543-12.46a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-.965-2.157-2.156c0-1.193.964-2.157 2.157-2.157c1.193 0 2.156.964 2.156 2.157c0 1.191-.963 2.156-2.156 2.156zm7.975 0c-1.183 0-2.157-.965-2.157-2.156c0-1.193.964-2.157 2.157-2.157c1.193 0 2.157.964 2.157 2.157c0 1.191-.964 2.156-2.157 2.156z" />
Expand All @@ -133,6 +135,7 @@ export function ShareModal({
<button
onClick={shareToTelegram}
className="flex items-center justify-center gap-2 px-4 py-2 bg-sky-500 hover:bg-sky-600 text-white rounded-lg font-medium transition-colors"
aria-label="Share on Telegram"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a11.955 11.955 0 0 0-.064 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.155.339-.315.612-.495 4.563-3.173 6.071-4.209 7.377-5.427.566-.502 1.06-.886 1.493-1.169.42-.276.922-.42 1.466-.42z" />
Expand All @@ -143,6 +146,7 @@ export function ShareModal({
<button
onClick={shareToReddit}
className="flex items-center justify-center gap-2 px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white rounded-lg font-medium transition-colors"
aria-label="Share on Reddit"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.385 4.859-7.181 4.859-3.796 0-7.182-2.165-7.182-4.859a3.5 3.5 0 0 1 .476-1.84c-.424-.355-.641-.89-.641-1.427 0-.968.786-1.754 1.754-1.754.418 0 .801.134 1.122.357 1.191-.857 2.844-1.416 4.665-1.489l.812-3.827c.207-.026.426-.026.607 0l2.94.625c.321-.586.922-.961 1.6-.961z" />
Expand Down
106 changes: 106 additions & 0 deletions frontend/src/components/__tests__/accessibility.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ShareModal } from '../ShareModal';
import { MobileBottomSheet } from '../MobileBottomSheet';
import { NotificationCenter } from '../NotificationCenter';

describe('Accessibility - ARIA Labels', () => {
describe('ShareModal', () => {
it('should have aria-label on close button', () => {
render(
<ShareModal
isOpen={true}
onClose={() => {}}
marketId={1}
/>
);

const closeButton = screen.getByLabelText('Close');
expect(closeButton).toBeInTheDocument();
});

it('should have aria-labels on social share buttons', () => {
render(
<ShareModal
isOpen={true}
onClose={() => {}}
marketId={1}
/>
);

expect(screen.getByLabelText('Share on Twitter')).toBeInTheDocument();
expect(screen.getByLabelText('Share on Discord')).toBeInTheDocument();
expect(screen.getByLabelText('Share on Telegram')).toBeInTheDocument();
expect(screen.getByLabelText('Share on Reddit')).toBeInTheDocument();
});
});

describe('MobileBottomSheet', () => {
it('should have aria-label on close button', () => {
render(
<MobileBottomSheet
isOpen={true}
onClose={() => {}}
title="Test Sheet"
>
<div>Content</div>
</MobileBottomSheet>
);

const closeButton = screen.getByLabelText('Close');
expect(closeButton).toBeInTheDocument();
});

it('should have aria-labels on snap point buttons', () => {
render(
<MobileBottomSheet
isOpen={true}
onClose={() => {}}
snapPoints={[0.5, 0.9]}
>
<div>Content</div>
</MobileBottomSheet>
);

expect(screen.getByLabelText('Snap to position 1')).toBeInTheDocument();
expect(screen.getByLabelText('Snap to position 2')).toBeInTheDocument();
});
});

describe('NotificationCenter', () => {
it('should have aria-label on close button', () => {
render(
<NotificationCenter
userId="test-user"
onClose={() => {}}
/>
);

const closeButton = screen.getByLabelText('Close notifications');
expect(closeButton).toBeInTheDocument();
});

it('should have aria-labels on filter buttons', () => {
render(
<NotificationCenter
userId="test-user"
/>
);

expect(screen.getByLabelText('Show all notifications')).toBeInTheDocument();
expect(screen.getByLabelText('Show unread notifications')).toBeInTheDocument();
expect(screen.getByLabelText('Show read notifications')).toBeInTheDocument();
});

it('should have aria-pressed on active filter button', () => {
render(
<NotificationCenter
userId="test-user"
/>
);

const allButton = screen.getByLabelText('Show all notifications');
expect(allButton).toHaveAttribute('aria-pressed', 'true');
});
});
});
Loading
Loading