Skip to content

Commit 231e3c1

Browse files
authored
Merge pull request #44 from adewale/claude/audit-react-doctor-fXieE
fix: React Doctor audit - fix errors and accessibility warnings
2 parents a2b18ba + a92931c commit 231e3c1

File tree

14 files changed

+94
-47
lines changed

14 files changed

+94
-47
lines changed

app/src/components/BottomSheet.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@ export function BottomSheet({ isOpen, onClose, title, children }: BottomSheetPro
4343
if (!isOpen) return null;
4444

4545
return (
46-
<div className="bottom-sheet-backdrop" onClick={handleBackdropClick}>
46+
<div
47+
className="bottom-sheet-backdrop"
48+
onClick={handleBackdropClick}
49+
onKeyDown={(e) => { if (e.key === 'Escape') onClose(); }}
50+
role="dialog"
51+
aria-modal="true"
52+
aria-label={title || 'Bottom sheet'}
53+
>
4754
<div className="bottom-sheet" ref={sheetRef}>
4855
<div className="bottom-sheet-handle" />
4956
{title && <div className="bottom-sheet-title">{title}</div>}

app/src/components/EffectsPanel.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ export function EffectsPanel({
128128
<span className="effect-label">Reverb</span>
129129
<div className="effect-controls">
130130
<div className="effect-param">
131-
<label>Mix</label>
131+
<label htmlFor="fx-reverb-mix">Mix</label>
132132
<input
133+
id="fx-reverb-mix"
133134
type="range"
134135
min="0"
135136
max="1"
@@ -142,8 +143,9 @@ export function EffectsPanel({
142143
<span className="param-value">{Math.round(effects.reverb.wet * 100)}%</span>
143144
</div>
144145
<div className="effect-param">
145-
<label>Decay</label>
146+
<label htmlFor="fx-reverb-decay">Decay</label>
146147
<input
148+
id="fx-reverb-decay"
147149
type="range"
148150
min="0.1"
149151
max="10"
@@ -163,8 +165,9 @@ export function EffectsPanel({
163165
<span className="effect-label">Delay</span>
164166
<div className="effect-controls">
165167
<div className="effect-param">
166-
<label>Mix</label>
168+
<label htmlFor="fx-delay-mix">Mix</label>
167169
<input
170+
id="fx-delay-mix"
168171
type="range"
169172
min="0"
170173
max="1"
@@ -177,8 +180,9 @@ export function EffectsPanel({
177180
<span className="param-value">{Math.round(effects.delay.wet * 100)}%</span>
178181
</div>
179182
<div className="effect-param">
180-
<label>Time</label>
183+
<label htmlFor="fx-delay-time">Time</label>
181184
<select
185+
id="fx-delay-time"
182186
value={effects.delay.time}
183187
onChange={(e) => updateEffect('delay', 'time', e.target.value)}
184188
disabled={disabled}
@@ -190,8 +194,9 @@ export function EffectsPanel({
190194
</select>
191195
</div>
192196
<div className="effect-param">
193-
<label>Feedback</label>
197+
<label htmlFor="fx-delay-feedback">Feedback</label>
194198
<input
199+
id="fx-delay-feedback"
195200
type="range"
196201
min="0"
197202
max="0.95"
@@ -211,8 +216,9 @@ export function EffectsPanel({
211216
<span className="effect-label">Chorus</span>
212217
<div className="effect-controls">
213218
<div className="effect-param">
214-
<label>Mix</label>
219+
<label htmlFor="fx-chorus-mix">Mix</label>
215220
<input
221+
id="fx-chorus-mix"
216222
type="range"
217223
min="0"
218224
max="1"
@@ -225,8 +231,9 @@ export function EffectsPanel({
225231
<span className="param-value">{Math.round(effects.chorus.wet * 100)}%</span>
226232
</div>
227233
<div className="effect-param">
228-
<label>Rate</label>
234+
<label htmlFor="fx-chorus-rate">Rate</label>
229235
<input
236+
id="fx-chorus-rate"
230237
type="range"
231238
min="0.1"
232239
max="10"
@@ -239,8 +246,9 @@ export function EffectsPanel({
239246
<span className="param-value">{effects.chorus.frequency.toFixed(1)}Hz</span>
240247
</div>
241248
<div className="effect-param">
242-
<label>Depth</label>
249+
<label htmlFor="fx-chorus-depth">Depth</label>
243250
<input
251+
id="fx-chorus-depth"
244252
type="range"
245253
min="0"
246254
max="1"
@@ -260,8 +268,9 @@ export function EffectsPanel({
260268
<span className="effect-label">Distortion</span>
261269
<div className="effect-controls">
262270
<div className="effect-param">
263-
<label>Mix</label>
271+
<label htmlFor="fx-distortion-mix">Mix</label>
264272
<input
273+
id="fx-distortion-mix"
265274
type="range"
266275
min="0"
267276
max="1"
@@ -274,8 +283,9 @@ export function EffectsPanel({
274283
<span className="param-value">{Math.round(effects.distortion.wet * 100)}%</span>
275284
</div>
276285
<div className="effect-param">
277-
<label>Drive</label>
286+
<label htmlFor="fx-distortion-drive">Drive</label>
278287
<input
288+
id="fx-distortion-drive"
279289
type="range"
280290
min="0"
281291
max="1"

app/src/components/LandingPage.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ export function LandingPage({ onStartSession }: LandingPageProps) {
146146
if (el) cardsRef.current[i] = el;
147147
}}
148148
onClick={() => handleExampleClick(ex)}
149+
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleExampleClick(ex); }}
150+
role="button"
151+
tabIndex={0}
149152
>
150153
<div className="landing-example-thumb">
151154
{pattern.map((row, ri) => (

app/src/components/MixerPanel.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,9 @@ const MixerChannel = memo(function MixerChannel({
149149
{/* Per-track swing (if handler provided) */}
150150
{onSetSwing && (
151151
<div className="channel-swing">
152-
<label className="swing-label">Swing</label>
152+
<label className="swing-label" htmlFor={`mixer-swing-${track.id}`}>Swing</label>
153153
<input
154+
id={`mixer-swing-${track.id}`}
154155
type="range"
155156
className="swing-slider"
156157
min="0"

app/src/components/PitchOverview.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,9 @@ export const PitchOverview = memo(function PitchOverview({
8484
}, [melodicTracks]);
8585

8686
// Count visible tracks (for header info)
87-
const visibleTrackCount = useMemo(() => {
88-
return melodicTracks.filter(track =>
89-
anySoloed ? track.soloed : !track.muted
90-
).length;
91-
}, [melodicTracks, anySoloed]);
87+
const visibleTrackCount = melodicTracks.filter(track =>
88+
anySoloed ? track.soloed : !track.muted
89+
).length;
9290

9391
// Build per-step pitch data
9492
const stepData = useMemo((): StepPitchData[] => {

app/src/components/QROverlay/QRCode.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@ interface QRCodeProps {
1919
className?: string;
2020
}
2121

22+
interface QRState {
23+
svgString: string;
24+
error: string | null;
25+
}
26+
2227
export function QRCode({
2328
value,
2429
size = 200,
2530
errorCorrection = 'M',
2631
className = '',
2732
}: QRCodeProps) {
28-
const [svgString, setSvgString] = useState<string>('');
29-
const [error, setError] = useState<string | null>(null);
33+
const [state, setState] = useState<QRState>({ svgString: '', error: null });
3034

3135
useEffect(() => {
3236
let cancelled = false;
@@ -45,12 +49,11 @@ export function QRCode({
4549
});
4650

4751
if (!cancelled) {
48-
setSvgString(svg);
49-
setError(null);
52+
setState({ svgString: svg, error: null });
5053
}
5154
} catch (err) {
5255
if (!cancelled) {
53-
setError(err instanceof Error ? err.message : 'Failed to generate QR code');
56+
setState({ svgString: '', error: err instanceof Error ? err.message : 'Failed to generate QR code' });
5457
}
5558
}
5659
}
@@ -62,7 +65,7 @@ export function QRCode({
6265
};
6366
}, [value, size, errorCorrection]);
6467

65-
if (error) {
68+
if (state.error) {
6669
return (
6770
<div
6871
className={`qr-code qr-code-error ${className}`}
@@ -75,7 +78,7 @@ export function QRCode({
7578
);
7679
}
7780

78-
if (!svgString) {
81+
if (!state.svgString) {
7982
return (
8083
<div
8184
className={`qr-code qr-code-loading ${className}`}
@@ -92,7 +95,7 @@ export function QRCode({
9295
style={{ width: size, height: size }}
9396
role="img"
9497
aria-label={`QR code linking to ${value}`}
95-
dangerouslySetInnerHTML={{ __html: svgString }}
98+
dangerouslySetInnerHTML={{ __html: state.svgString }}
9699
/>
97100
);
98101
}

app/src/components/SessionName.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ export function SessionName({ name, sessionId, onRename, disabled = false }: Ses
2020
const [isSaving, setIsSaving] = useState(false);
2121
const inputRef = useRef<HTMLInputElement>(null);
2222

23-
// Sync edit value when name prop changes
24-
useEffect(() => {
25-
if (!isEditing) {
26-
setEditValue(name || '');
27-
}
28-
}, [name, isEditing]);
29-
3023
// Focus input when entering edit mode
3124
useEffect(() => {
3225
if (isEditing && inputRef.current) {
@@ -42,9 +35,10 @@ export function SessionName({ name, sessionId, onRename, disabled = false }: Ses
4235

4336
const handleClick = useCallback(() => {
4437
if (!disabled && !isSaving) {
38+
setEditValue(name || '');
4539
setIsEditing(true);
4640
}
47-
}, [disabled, isSaving]);
41+
}, [disabled, isSaving, name]);
4842

4943
const handleSave = useCallback(async () => {
5044
if (isSaving) return;

app/src/components/StepSequencer.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,7 @@ export function StepSequencer() {
321321
}, [dragState.targetTrackId, state.tracks, dispatch, multiplayer]);
322322

323323
// Phase 31D: Count muted tracks for button display
324-
const mutedTrackCount = useMemo(() => {
325-
return state.tracks.filter(t => t.muted).length;
326-
}, [state.tracks]);
324+
const mutedTrackCount = state.tracks.filter(t => t.muted).length;
327325

328326
// Phase 31F: Selection state and handler
329327
const handleSelectStep = useCallback((trackId: string, step: number, mode: 'toggle' | 'extend') => {

app/src/components/ToastNotification.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ function ToastItem({ toast, onDismiss }: { toast: Toast; onDismiss: (id: string)
8888
<div
8989
className={`toast toast-url ${isExiting ? 'exiting' : ''}`}
9090
onClick={handleUrlTap}
91+
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleUrlTap(); }}
92+
role="button"
93+
tabIndex={0}
9194
>
9295
<div className="toast-url-header">
9396
<span className="toast-message">{toast.message}</span>

app/src/components/TrackRow.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ export const TrackRow = React.memo(function TrackRow({
771771
<div
772772
className={`mobile-edit-panel ${isMenuOpen ? 'expanded' : ''}`}
773773
onClick={() => setIsMenuOpen(!isMenuOpen)}
774+
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') setIsMenuOpen(!isMenuOpen); }}
774775
role="button"
775776
tabIndex={0}
776777
>

0 commit comments

Comments
 (0)