diff --git a/src/components/piano-roll/PianoGridHeader.test.tsx b/src/components/piano-roll/PianoGridHeader.test.tsx index 35375e5..fb299bd 100644 --- a/src/components/piano-roll/PianoGridHeader.test.tsx +++ b/src/components/piano-roll/PianoGridHeader.test.tsx @@ -28,10 +28,12 @@ function renderHeader({ hasPianoKeys = true, scrollLeft = 0, paddingLeft = hasPianoKeys ? '60px' : '0px', + scrollContainerLeft = 100, }: { hasPianoKeys?: boolean; scrollLeft?: number; paddingLeft?: string; + scrollContainerLeft?: number; } = {}) { const view = render(
@@ -48,18 +50,33 @@ function renderHeader({ writable: true, }); + Object.defineProperty(scrollContainer, 'getBoundingClientRect', { + configurable: true, + value: () => ({ + left: scrollContainerLeft, + top: 0, + right: scrollContainerLeft + 500, + bottom: 20, + width: 500, + height: 20, + x: scrollContainerLeft, + y: 0, + toJSON: () => ({}), + }), + }); + header.style.paddingLeft = paddingLeft; Object.defineProperty(header, 'getBoundingClientRect', { configurable: true, value: () => ({ - left: 100, + left: scrollContainerLeft - scrollLeft, top: 0, - right: 600, + right: scrollContainerLeft - scrollLeft + 500, bottom: 20, width: 500, height: 20, - x: 100, + x: scrollContainerLeft - scrollLeft, y: 0, toJSON: () => ({}), }), @@ -88,7 +105,7 @@ describe('PianoGridHeader', () => { expect(requestMainContentScroll).toHaveBeenCalledWith(3); }); - it('includes the note scroll offset when seeking after horizontal scroll', () => { + it('seeks to the same beat after horizontal scroll when the header rect already shifts with content', () => { const { header } = renderHeader({ scrollLeft: 160 }); fireEvent.click(header, { clientX: 280 }); @@ -118,4 +135,13 @@ describe('PianoGridHeader', () => { expect(setPlayheadPosition).toHaveBeenCalledWith(5); expect(requestMainContentScroll).not.toHaveBeenCalled(); }); + + it('ignores clicks inside the visible piano-key gutter before it fully scrolls out of view', () => { + const { header } = renderHeader({ scrollLeft: 20 }); + + fireEvent.click(header, { clientX: 110 }); + + expect(setPlayheadPosition).not.toHaveBeenCalled(); + expect(requestMainContentScroll).not.toHaveBeenCalled(); + }); }); diff --git a/src/components/piano-roll/PianoGridHeader.tsx b/src/components/piano-roll/PianoGridHeader.tsx index a8f9c4b..8a5c9fa 100644 --- a/src/components/piano-roll/PianoGridHeader.tsx +++ b/src/components/piano-roll/PianoGridHeader.tsx @@ -28,9 +28,9 @@ const PianoGridHeader: React.FC = ({ if (!headerElementRef.current) return null; const headerElement = headerElementRef.current; - const rect = headerElement.getBoundingClientRect(); - const relativeX = clientX - rect.left; const scrollContainer = headerElement.closest('.piano-roll-note-scroll') as HTMLElement | null; + const referenceRect = scrollContainer?.getBoundingClientRect() ?? headerElement.getBoundingClientRect(); + const relativeX = clientX - referenceRect.left; const scrollLeft = scrollContainer?.scrollLeft ?? 0; const leftGutter = parseFloat(getComputedStyle(headerElement).paddingLeft) || 0; const adjustedX = relativeX + scrollLeft - leftGutter;