fix(ui): defuse the press-scale click trap on three more composite buttons (#2158)

Same trap the task-list fix hardened: the press animation shrinks the container mid-click and the release lands next to the target. The collections rows, the journey gallery tiles and the shared vacay calendar cards carry the established no-press attribute now.
This commit is contained in:
Maurice
2026-09-02 19:01:03 +02:00
committed by Maurice
parent c026c5b1c9
commit 293dc9e1f1
6 changed files with 45 additions and 2 deletions
@@ -1,4 +1,4 @@
// FE-COMP-COLLIST-001 to FE-COMP-COLLIST-010
// FE-COMP-COLLIST-001 to FE-COMP-COLLIST-011
import { render, screen } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import type { CollectionPlace } from '@trek/shared';
@@ -157,4 +157,14 @@ describe('CollectionList', () => {
expect(screen.getByText('Blue Bottle Coffee').closest('.col-lrow')).toBeInTheDocument();
expect(screen.getByText('Golden Gate Bridge').closest('.col-lrow')).toBeInTheDocument();
});
it('FE-COMP-COLLIST-011: the row opts out of the global press-scale (#2158)', () => {
// jsdom cannot replay the browser mechanics behind #2158: the :active scale on
// the row-wide role="button" shifted the status badge out from under the pointer,
// so the click retargeted onto the row and opened the place instead of cycling the
// status. The data-no-press attribute is the pin.
renderList();
expect(screen.getByText('Blue Bottle Coffee').closest('.col-lrow')).toHaveAttribute('data-no-press');
});
});
@@ -44,6 +44,10 @@ export default function CollectionList({
key={place.id}
ref={active ? selectedRef : undefined}
role="button"
// Opt out of the global :active press-scale: shrinking the row-wide button
// slides the status badge out from under the pointer mid-click, so the click
// retargets onto the row and opens the place instead of cycling (#2158).
data-no-press
tabIndex={0}
onClick={() => (selectMode ? onToggleSelect(place.id) : onOpenPlace(place.id))}
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (selectMode) onToggleSelect(place.id); else onOpenPlace(place.id) } }}
@@ -1,4 +1,4 @@
// FE-JRN-GALLERY-001 to FE-JRN-GALLERY-016
// FE-JRN-GALLERY-001 to FE-JRN-GALLERY-019
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { act, useRef, useState } from 'react'
@@ -373,4 +373,14 @@ describe('GalleryView', () => {
expect(container.querySelectorAll('.aspect-square')).toHaveLength(2)
expect(container.querySelector('.animate-spin')).toBeNull()
})
it('FE-JRN-GALLERY-019: the tile opts out of the global press-scale (#2158)', () => {
// jsdom cannot replay the browser mechanics behind #2158: the :active scale on
// the tile shifted its corner delete button out from under the pointer, so the
// click retargeted onto the tile and opened the lightbox instead of deleting.
// The data-no-press attribute is the pin.
const { container } = mountGallery([buildGalleryPhoto()])
expect(container.querySelector('.aspect-square')).toHaveAttribute('data-no-press')
})
})
@@ -142,6 +142,10 @@ export function GalleryView({ entries, gallery, journeyId, userId, trips, onPhot
<div
key={photo.id}
role="button"
// No press-scale on the tile: shrinking it mid-click slides the corner
// delete button out from under the pointer, so the click retargets onto
// the tile and opens the lightbox instead (#2158).
data-no-press
tabIndex={0}
className="relative aspect-square rounded-lg overflow-hidden cursor-pointer group"
onClick={() => onPhotoClick(allPhotos, i)}
@@ -141,4 +141,15 @@ describe('VacaySharedCalendars', () => {
expect(shareWithMock).toHaveBeenCalledWith(2)
})
it('FE-COMP-VACAYSHARED-008: Incoming row opts out of the global press-scale (#2158)', () => {
// jsdom cannot replay the browser mechanics behind #2158: the :active scale on
// the row shifted the remove X out from under the pointer, so the click retargeted
// onto the row and only toggled visibility. The data-no-press attribute is the pin.
seedShares({ incomingShares: [incoming] })
render(<VacaySharedCalendars />)
expect(screen.getByText('Carol').closest('[role="button"]')).toHaveAttribute('data-no-press')
})
})
@@ -78,6 +78,10 @@ export default function VacaySharedCalendars() {
{incomingShares.map(s => (
<div key={s.id}
role="button"
// No press-scale on the row: shrinking it mid-click slides the remove X
// out from under the pointer, so the click retargets onto the row and
// toggles visibility instead of removing the share (#2158).
data-no-press
tabIndex={0}
onClick={() => handleToggleHidden(s.id, !s.hidden)}
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleToggleHidden(s.id, !s.hidden) } }}