From c95e1343e16c8667f6b7f5560d12ec5f36f1acd8 Mon Sep 17 00:00:00 2001 From: rahim Date: Wed, 18 Mar 2026 12:44:12 -0700 Subject: [PATCH] feat(html): add data-availability to volume slider (#1001) --- .../volume-slider/tests/volume-slider-core.test.ts | 14 ++++++++++++++ .../core/ui/volume-slider/volume-slider-core.ts | 7 +++++-- .../ui/volume-slider/volume-slider-data-attrs.ts | 1 + .../src/ui/volume-slider/volume-slider-element.ts | 6 +++--- .../src/ui/volume-slider/volume-slider-root.tsx | 6 +++--- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts b/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts index c9a42ca6..2f0abead 100644 --- a/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts +++ b/packages/core/src/core/ui/volume-slider/tests/volume-slider-core.test.ts @@ -115,6 +115,20 @@ describe('VolumeSliderCore', () => { expect(state.muted).toBe(true); expect(state.fillPercent).toBe(0); }); + + it('projects availability from volumeAvailability', () => { + const core = new VolumeSliderCore(); + core.setInput(createInput()); + core.setMedia(createMediaState({ volumeAvailability: 'unsupported' })); + expect(core.getState().availability).toBe('unsupported'); + }); + + it('reflects available availability', () => { + const core = new VolumeSliderCore(); + core.setInput(createInput()); + core.setMedia(createMediaState({ volumeAvailability: 'available' })); + expect(core.getState().availability).toBe('available'); + }); }); describe('getAttrs', () => { diff --git a/packages/core/src/core/ui/volume-slider/volume-slider-core.ts b/packages/core/src/core/ui/volume-slider/volume-slider-core.ts index 5236a60f..48594a60 100644 --- a/packages/core/src/core/ui/volume-slider/volume-slider-core.ts +++ b/packages/core/src/core/ui/volume-slider/volume-slider-core.ts @@ -1,7 +1,7 @@ import { defaults } from '@videojs/utils/object'; import type { NonNullableObject } from '@videojs/utils/types'; -import type { MediaVolumeState } from '../../media/state'; +import type { MediaFeatureAvailability, MediaVolumeState } from '../../media/state'; import { SliderCore, type SliderProps, type SliderState } from '../slider/slider-core'; export interface VolumeSliderProps extends SliderProps { @@ -13,7 +13,9 @@ export interface VolumeSliderProps extends SliderProps { max?: number | undefined; } -export interface VolumeSliderState extends SliderState, Pick {} +export interface VolumeSliderState extends SliderState, Pick { + availability: MediaFeatureAvailability; +} /** Volume-domain slider: maps media volume/mute state to slider state. */ export class VolumeSliderCore extends SliderCore { @@ -51,6 +53,7 @@ export class VolumeSliderCore extends SliderCore { fillPercent: effectivelyMuted ? 0 : base.fillPercent, volume, muted: effectivelyMuted, + availability: media.volumeAvailability, }; } diff --git a/packages/core/src/core/ui/volume-slider/volume-slider-data-attrs.ts b/packages/core/src/core/ui/volume-slider/volume-slider-data-attrs.ts index 99aea06d..613e4618 100644 --- a/packages/core/src/core/ui/volume-slider/volume-slider-data-attrs.ts +++ b/packages/core/src/core/ui/volume-slider/volume-slider-data-attrs.ts @@ -4,4 +4,5 @@ import type { VolumeSliderState } from './volume-slider-core'; export const VolumeSliderDataAttrs = { ...SliderDataAttrs, + availability: 'data-availability', } as const satisfies StateAttrMap; diff --git a/packages/html/src/ui/volume-slider/volume-slider-element.ts b/packages/html/src/ui/volume-slider/volume-slider-element.ts index f21c363d..9bf4b06e 100644 --- a/packages/html/src/ui/volume-slider/volume-slider-element.ts +++ b/packages/html/src/ui/volume-slider/volume-slider-element.ts @@ -1,4 +1,4 @@ -import { SliderDataAttrs, VolumeSliderCore } from '@videojs/core'; +import { VolumeSliderCore, VolumeSliderDataAttrs } from '@videojs/core'; import { applyElementProps, applyStateDataAttrs, @@ -120,12 +120,12 @@ export class VolumeSliderElement extends MediaElement { applyStyles(this, cssVars); // Apply data attributes to root. - applyStateDataAttrs(this, state, SliderDataAttrs); + applyStateDataAttrs(this, state, VolumeSliderDataAttrs); // Provide context to child elements. this.#provider.setValue({ state, - stateAttrMap: SliderDataAttrs, + stateAttrMap: VolumeSliderDataAttrs, pointerValue: this.#core.valueFromPercent(state.pointerPercent), thumbAttrs: this.#core.getAttrs(state), thumbProps: this.#slider.thumbProps, diff --git a/packages/react/src/ui/volume-slider/volume-slider-root.tsx b/packages/react/src/ui/volume-slider/volume-slider-root.tsx index 19a96324..2c5dd7d8 100644 --- a/packages/react/src/ui/volume-slider/volume-slider-root.tsx +++ b/packages/react/src/ui/volume-slider/volume-slider-root.tsx @@ -1,6 +1,6 @@ 'use client'; -import { SliderDataAttrs, VolumeSliderCore } from '@videojs/core'; +import { VolumeSliderCore, VolumeSliderDataAttrs } from '@videojs/core'; import { getSliderCSSVars, logMissingFeature, selectVolume } from '@videojs/core/dom'; import { forwardRef, useState } from 'react'; @@ -85,7 +85,7 @@ export const VolumeSliderRoot = forwardRef core.getAttrs(sliderState as VolumeSliderCore.State), formatValue: (value) => `${Math.round(value)}%`, }} @@ -95,7 +95,7 @@ export const VolumeSliderRoot = forwardRef