mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix(react): prevent gesture tap from firing on slider interactions (#1361)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { DATA_ATTRS, SELECTORS } from '../fixtures/selectors';
|
||||
import { DATA_ATTRS } from '../fixtures/selectors';
|
||||
import { PlayerPage } from '../page-objects/player';
|
||||
|
||||
/**
|
||||
@@ -52,6 +52,55 @@ test.describe('Mouse Gestures', () => {
|
||||
await player.playButton.click();
|
||||
await expect(player.playButton).not.toHaveAttribute(DATA_ATTRS.paused, { timeout: 5_000 });
|
||||
});
|
||||
|
||||
test('click on slider does not trigger container gesture', async ({ page }) => {
|
||||
// Start playback so the slider has a seekable range
|
||||
await player.play();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Click the time slider — should seek, not toggle play/pause.
|
||||
// Wait briefly after seek to give any leaked gesture time to fire.
|
||||
await player.seekTo(50);
|
||||
await page.waitForTimeout(300);
|
||||
await expect(player.playButton).not.toHaveAttribute(DATA_ATTRS.paused);
|
||||
});
|
||||
});
|
||||
|
||||
// --- React gestures (verify slider interaction isolation) ---
|
||||
|
||||
test.describe('React Mouse Gestures', () => {
|
||||
let player: PlayerPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
player = new PlayerPage(page);
|
||||
await page.goto('/pages/react-video-mp4.html');
|
||||
await player.waitForMediaReady();
|
||||
});
|
||||
|
||||
test('click center of container toggles play/pause', async ({ page }) => {
|
||||
await expect(player.playButton).toHaveAttribute(DATA_ATTRS.paused, '');
|
||||
|
||||
const { x, y } = await getCenter(player);
|
||||
await page.mouse.click(x, y);
|
||||
await expect(player.playButton).not.toHaveAttribute(DATA_ATTRS.paused, { timeout: 5_000 });
|
||||
});
|
||||
|
||||
test('click on button does not trigger container gesture', async () => {
|
||||
await expect(player.playButton).toHaveAttribute(DATA_ATTRS.paused, '');
|
||||
await player.playButton.click();
|
||||
await expect(player.playButton).not.toHaveAttribute(DATA_ATTRS.paused, { timeout: 5_000 });
|
||||
});
|
||||
|
||||
test('click on slider does not trigger container gesture', async ({ page }) => {
|
||||
await player.play();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Click the time slider — should seek, not toggle play/pause.
|
||||
// Wait briefly after seek to give any leaked gesture time to fire.
|
||||
await player.seekTo(50);
|
||||
await page.waitForTimeout(300);
|
||||
await expect(player.playButton).not.toHaveAttribute(DATA_ATTRS.paused);
|
||||
});
|
||||
});
|
||||
|
||||
// --- Touch gestures (pointer="touch") ---
|
||||
|
||||
@@ -85,6 +85,7 @@ export const controlsFeature = definePlayerFeature({
|
||||
if (event.pointerType === 'touch' && Date.now() - pointerDownTime < TAP_THRESHOLD) {
|
||||
// When a toggleControls touch tap gesture is registered, it handles toggle — skip inline handler.
|
||||
const coordinator = findGestureCoordinator(container as HTMLElement);
|
||||
|
||||
if (
|
||||
coordinator?.bindings.some(
|
||||
(b) => b.type === 'tap' && b.action === 'toggleControls' && (!b.pointer || b.pointer === 'touch')
|
||||
|
||||
@@ -215,10 +215,14 @@ export function createSlider(options: SliderOptions): SliderApi {
|
||||
},
|
||||
|
||||
onPointerUp(event) {
|
||||
if (isNull(capturedPointerId)) return;
|
||||
if (options.isDisabled()) return;
|
||||
|
||||
// The slider fully owns pointer interactions — prevent parent gesture
|
||||
// coordinators from misinterpreting slider taps as surface gestures.
|
||||
event.stopPropagation();
|
||||
|
||||
if (isNull(capturedPointerId)) return;
|
||||
|
||||
const percent = getPercentFromPointerEvent(event, cachedRect!, options.getOrientation(), cachedRTL);
|
||||
|
||||
// Cancel any pending throttled change before the final unthrottled pair.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { resolveEventTarget } from './event';
|
||||
|
||||
export const INTERACTIVE_SELECTOR = 'button,input,select,textarea,a[href],[role="slider"],[role="button"]';
|
||||
export const INTERACTIVE_SELECTOR =
|
||||
'button,input,select,textarea,a[href],[role="slider"],[role="button"],[data-interactive]';
|
||||
|
||||
const EDITABLE_INPUT_TYPES = ['text', 'search', 'url', 'tel', 'email', 'password', 'number'];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user