diff --git a/src/components/MainContent.test.tsx b/src/components/MainContent.test.tsx index c374e5e..5d24208 100644 --- a/src/components/MainContent.test.tsx +++ b/src/components/MainContent.test.tsx @@ -3,13 +3,20 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { fireEvent, render, screen } from '@testing-library/react'; import MainContent from './MainContent'; import { KGMidiRegion } from '../core/region/KGMidiRegion'; +import { KGAudioRegion } from '../core/region/KGAudioRegion'; +import { KGAudioTrack } from '../core/track/KGAudioTrack'; import { createMockMidiTrack } from '../test/utils/mock-data'; const midiRegion = new KGMidiRegion('region-1', '1', 0, 'Region 1', 0, 4); -const track = createMockMidiTrack({ id: 1, regions: [midiRegion] }); +const anotherMidiRegion = new KGMidiRegion('region-2', '1', 0, 'Region 2', 8, 4); +const audioRegion = new KGAudioRegion('audio-1', '2', 1, 'Audio 1', 4, 4); +const midiTrack = createMockMidiTrack({ id: 1, regions: [midiRegion, anotherMidiRegion] }); +const audioTrack = new KGAudioTrack('Audio Track', 2); +audioTrack.setTrackIndex(1); +audioTrack.setRegions([audioRegion]); const storeState = { - tracks: [track], + tracks: [midiTrack, audioTrack], maxBars: 8, barWidthMultiplier: 1, reorderTracks: vi.fn(), @@ -71,13 +78,16 @@ vi.mock('../stores/projectStore', () => ({ vi.mock('../core/KGCore', () => ({ KGCore: { instance: () => ({ - addSelectedItems: (items: KGMidiRegion[]) => { + addSelectedItems: (items: Array<{ getId(): string }>) => { storeState.selectedRegionIds = items.map(item => item.getId()); }, clearSelectedItems: () => { storeState.selectedRegionIds = []; }, executeCommand: vi.fn(), + getCurrentProject: () => ({ + getTracks: () => storeState.tracks, + }), }), }, })); @@ -94,14 +104,26 @@ vi.mock('./track/TrackInfoPanel', () => ({ vi.mock('./track/TrackGridPanel', () => ({ default: ({ onRegionClick }: { onRegionClick?: RegionClickHandler }) => ( - + <> + + + + ), })); vi.mock('./piano-roll/PianoRoll', () => ({ - default: () =>
, + default: ({ onClose }: { onClose?: () => void }) => ( +
+ +
+ ), })); describe('MainContent', () => { @@ -120,11 +142,65 @@ describe('MainContent', () => { it('updates activeRegionId when selecting a region with piano roll closed', () => { render(); - fireEvent.click(screen.getByRole('button', { name: 'select-region' })); + fireEvent.click(screen.getByRole('button', { name: 'select-midi-region' })); expect(storeState.activeRegionId).toBe('region-1'); expect(storeState.setActiveRegionId).toHaveBeenCalledWith('region-1'); expect(storeState.showPianoRoll).toBe(false); expect(storeState.openMidiPianoRoll).not.toHaveBeenCalled(); }); + + it('keeps piano roll open and preserves activeRegionId when deselecting all regions', () => { + storeState.showPianoRoll = true; + storeState.activeRegionId = 'region-1'; + storeState.selectedRegionIds = ['region-1']; + + const { container } = render(); + + fireEvent.click(container.firstChild as HTMLElement); + + expect(storeState.selectedRegionIds).toEqual([]); + expect(storeState.showPianoRoll).toBe(true); + expect(storeState.activeRegionId).toBe('region-1'); + expect(storeState.setShowPianoRoll).not.toHaveBeenCalledWith(false); + expect(storeState.setActiveRegionId).not.toHaveBeenCalledWith(null); + }); + + it('auto-switches the open piano roll when selecting another MIDI region', () => { + storeState.showPianoRoll = true; + storeState.activeRegionId = 'region-1'; + + render(); + + fireEvent.click(screen.getByRole('button', { name: 'select-second-midi-region' })); + + expect(storeState.activeRegionId).toBe('region-2'); + expect(storeState.openMidiPianoRoll).toHaveBeenCalledWith('region-2'); + }); + + it('preserves current cross-type behavior when selecting an audio region with the editor open', () => { + storeState.showPianoRoll = true; + storeState.activeRegionId = 'region-1'; + + render(); + + fireEvent.click(screen.getByRole('button', { name: 'select-audio-region' })); + + expect(storeState.activeRegionId).toBe('audio-1'); + expect(storeState.openSpectrogramViewer).toHaveBeenCalledWith('audio-1'); + }); + + it('explicit close still clears piano roll visibility and active region', () => { + storeState.showPianoRoll = true; + storeState.activeRegionId = 'region-1'; + + render(); + + fireEvent.click(screen.getByRole('button', { name: 'close-piano-roll' })); + + expect(storeState.showPianoRoll).toBe(false); + expect(storeState.activeRegionId).toBeNull(); + expect(storeState.setShowPianoRoll).toHaveBeenCalledWith(false); + expect(storeState.setActiveRegionId).toHaveBeenCalledWith(null); + }); }); diff --git a/src/components/MainContent.tsx b/src/components/MainContent.tsx index ace8c85..ab677ec 100644 --- a/src/components/MainContent.tsx +++ b/src/components/MainContent.tsx @@ -336,6 +336,21 @@ const MainContent: React.FC = ({ setRegions(updatedRegions); }, [tracks, timeSignature]); + useEffect(() => { + if (!showPianoRoll || !activeRegionId) { + return; + } + + const activeRegionStillExists = tracks.some(track => + track.getRegions().some(region => region.getId() === activeRegionId) + ); + + if (!activeRegionStillExists) { + setShowPianoRoll(false); + setActiveRegionId(null); + } + }, [tracks, showPianoRoll, activeRegionId, setShowPianoRoll, setActiveRegionId]); + // Apply auto-selection for newly created/imported regions after the regions state commits. useEffect(() => { const pendingRegionId = pendingAutoSelectionRegionIdRef.current; @@ -589,7 +604,9 @@ const MainContent: React.FC = ({ : null; setSelectedRegionId(lastSelectedRegionId); - setActiveRegionId(lastSelectedRegionId); + if (lastSelectedRegionId) { + setActiveRegionId(lastSelectedRegionId); + } if (DEBUG_MODE.MAIN_CONTENT) { console.log(`Selected regions: ${selectedRegions.map(selectedRegion => selectedRegion.getId()).join(', ')}`); @@ -600,7 +617,6 @@ const MainContent: React.FC = ({ } if (!lastSelectedRegionId) { - setShowPianoRoll(false); return; }