fix(html): slider interaction and edge alignment broken (#721)

This commit is contained in:
rahim
2026-03-04 21:13:37 -08:00
committed by GitHub
parent 7548a8e4a1
commit ff12296355
13 changed files with 126 additions and 25 deletions
+11 -2
View File
@@ -1,5 +1,11 @@
import { SliderCore, SliderDataAttrs } from '@videojs/core';
import { applyStateDataAttrs, createSlider, getSliderCSSVars, type SliderApi } from '@videojs/core/dom';
import {
applyElementProps,
applyStateDataAttrs,
createSlider,
getSliderCSSVars,
type SliderApi,
} from '@videojs/core/dom';
import type { PropertyDeclarationMap, PropertyValues } from '@videojs/element';
import { ContextProvider } from '@videojs/element/context';
import { applyStyles, isRTL } from '@videojs/utils/dom';
@@ -67,8 +73,10 @@ export class SliderElement extends MediaElement {
onDragEnd: () => {
this.dispatchEvent(new CustomEvent('drag-end', { bubbles: true }));
},
adjustPercent: (raw, thumbSize, trackSize) => this.#core.adjustPercentForAlignment(raw, thumbSize, trackSize),
});
applyElementProps(this, this.#slider.rootProps, { signal });
this.#slider.input.subscribe(() => this.requestUpdate(), { signal });
// Prevent default touch gestures and text selection during interaction.
@@ -95,7 +103,8 @@ export class SliderElement extends MediaElement {
this.#core.setInput(this.#slider.input.current);
const state = this.#core.getSliderState(this.value);
const cssVars = getSliderCSSVars(state);
const cssVars = getSliderCSSVars(this.#slider.adjustForAlignment(state));
applyStyles(this, cssVars);
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { SliderBufferElement } from '../slider-buffer-element';
import { SliderElement } from '../slider-element';
import { SliderFillElement } from '../slider-fill-element';
@@ -107,6 +107,27 @@ describe('SliderElement', () => {
expect(slider.style.getPropertyValue('--media-slider-fill')).toBe('75.000%');
});
it('binds rootProps pointer events on connect', async () => {
const slider = createElement(SliderElement);
slider.value = 0;
document.body.appendChild(slider);
await slider.updateComplete;
// Stub setPointerCapture/releasePointerCapture (not available in happy-dom).
slider.setPointerCapture = vi.fn();
slider.releasePointerCapture = vi.fn();
const spy = vi.fn();
slider.addEventListener('value-change', spy);
// Simulate pointerdown on the slider element.
slider.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, pointerId: 1, clientX: 50, clientY: 0 }));
// pointerdown triggers onValueChange via rootProps.
expect(spy).toHaveBeenCalled();
});
it('supports vertical orientation', async () => {
const slider = createElement(SliderElement);
slider.orientation = 'vertical';
@@ -36,6 +36,20 @@ describe('TimeSliderElement', () => {
expect(slider.thumbAlignment).toBe('center');
});
it('binds rootProps pointer events on connect', async () => {
const slider = createElement(TimeSliderElement);
document.body.appendChild(slider);
await slider.updateComplete;
// Without store, slider is disabled — but rootProps should still be bound.
// Verify by dispatching pointermove (which does not guard on disabled).
slider.dispatchEvent(new PointerEvent('pointermove', { bubbles: true, clientX: 50, clientY: 0 }));
// No errors thrown means rootProps were bound correctly.
expect(slider.isConnected).toBe(true);
});
it('sets touch-action and user-select styles on connect', async () => {
const slider = createElement(TimeSliderElement);
@@ -1,5 +1,6 @@
import { TimeSliderCore, TimeSliderDataAttrs } from '@videojs/core';
import {
applyElementProps,
applyStateDataAttrs,
createSlider,
getTimeSliderCSSVars,
@@ -77,8 +78,10 @@ export class TimeSliderElement extends MediaElement {
onDragEnd: () => {
this.dispatchEvent(new CustomEvent('drag-end', { bubbles: true }));
},
adjustPercent: (raw, thumbSize, trackSize) => this.#core.adjustPercentForAlignment(raw, thumbSize, trackSize),
});
applyElementProps(this, this.#slider.rootProps, { signal });
this.#slider.input.subscribe(() => this.requestUpdate(), { signal });
// Prevent default touch gestures and text selection during interaction.
@@ -115,7 +118,8 @@ export class TimeSliderElement extends MediaElement {
const media = { ...time, ...(buffer ?? { buffered: [], seekable: [] }) };
this.#core.setMedia(media);
const state = this.#core.getState();
const cssVars = getTimeSliderCSSVars(state);
const cssVars = getTimeSliderCSSVars(this.#slider.adjustForAlignment(state));
applyStyles(this, cssVars);
@@ -34,6 +34,19 @@ describe('VolumeSliderElement', () => {
expect(slider.thumbAlignment).toBe('center');
});
it('binds rootProps pointer events on connect', async () => {
const slider = createElement(VolumeSliderElement);
document.body.appendChild(slider);
await slider.updateComplete;
// Without store, slider is disabled — but rootProps should still be bound.
slider.dispatchEvent(new PointerEvent('pointermove', { bubbles: true, clientX: 50, clientY: 0 }));
// No errors thrown means rootProps were bound correctly.
expect(slider.isConnected).toBe(true);
});
it('sets touch-action and user-select styles on connect', async () => {
const slider = createElement(VolumeSliderElement);
@@ -1,5 +1,6 @@
import { SliderDataAttrs, VolumeSliderCore } from '@videojs/core';
import {
applyElementProps,
applyStateDataAttrs,
createSlider,
getSliderCSSVars,
@@ -73,8 +74,10 @@ export class VolumeSliderElement extends MediaElement {
onDragEnd: () => {
this.dispatchEvent(new CustomEvent('drag-end', { bubbles: true }));
},
adjustPercent: (raw, thumbSize, trackSize) => this.#core.adjustPercentForAlignment(raw, thumbSize, trackSize),
});
applyElementProps(this, this.#slider.rootProps, { signal });
this.#slider.input.subscribe(() => this.requestUpdate(), { signal });
// Prevent default touch gestures and text selection during interaction.
@@ -109,7 +112,8 @@ export class VolumeSliderElement extends MediaElement {
this.#core.setInput(this.#slider.input.current);
this.#core.setMedia(media);
const state = this.#core.getState();
const cssVars = getSliderCSSVars(state);
const cssVars = getSliderCSSVars(this.#slider.adjustForAlignment(state));
applyStyles(this, cssVars);