feat(core): add mute button component (#455)

This commit is contained in:
rahim
2026-02-04 23:10:10 +11:00
committed by GitHub
parent d8f15195e3
commit aa189eec84
18 changed files with 541 additions and 24 deletions
@@ -487,6 +487,39 @@ describe('renderElement', () => {
expect(element?.getAttribute('data-ispaused')).toBe('');
});
it('supports explicit state attribute mapping', () => {
interface VolumeState {
muted: boolean;
volumeLevel: 'low' | 'high';
}
const mapping = {
muted: 'data-muted',
volumeLevel: 'data-volume-level',
} as const;
const VolumeComponent = forwardRef(function VolumeComponent(
props: { muted?: boolean; volumeLevel?: 'low' | 'high' } & renderElement.ComponentProps<VolumeState>,
ref: ForwardedRef<HTMLDivElement>
) {
const { className, style, render: renderProp, muted = false, volumeLevel = 'high', ...elementProps } = props;
const state: VolumeState = { muted, volumeLevel };
return renderElement(
'div',
{ className, style, render: renderProp },
{ state, ref, props: [elementProps], stateAttrMap: mapping }
);
});
const { container } = render(<VolumeComponent muted volumeLevel="low" />);
const element = container.firstElementChild;
expect(element?.getAttribute('data-muted')).toBe('');
expect(element?.getAttribute('data-volume-level')).toBe('low');
expect(element?.hasAttribute('data-volumelevel')).toBe(false);
});
it('state data-* attributes can be overridden by explicit props', () => {
const ComponentWithExplicitDataAttr = forwardRef(function ComponentWithExplicitDataAttr(
props: TestComponentProps,
+4 -3
View File
@@ -1,6 +1,6 @@
'use client';
import { getStateDataAttrs } from '@videojs/core/dom';
import { getStateDataAttrs, type StateAttrMap } from '@videojs/core/dom';
import { isFunction } from '@videojs/utils/predicate';
import type { CSSProperties, ReactElement, Ref } from 'react';
import { cloneElement, createElement, isValidElement } from 'react';
@@ -20,6 +20,7 @@ export interface UseRenderParameters<State, RenderedElementType extends Element>
state: State;
ref?: Ref<RenderedElementType> | Ref<RenderedElementType>[] | undefined;
props?: object | object[] | undefined;
stateAttrMap?: StateAttrMap<State> | undefined;
}
function resolveClassName<State>(
@@ -72,14 +73,14 @@ export function renderElement<
params: UseRenderParameters<State, RenderedElementType>
): ReactElement {
const { className: classNameProp, style: styleProp, render } = componentProps;
const { state, ref, props } = params;
const { state, ref, props, stateAttrMap } = params;
// Resolve className and style if they're functions
const className = resolveClassName(classNameProp, state);
const style = resolveStyle(styleProp, state);
// Generate data attributes from state
const stateDataAttrs = getStateDataAttrs(state);
const stateDataAttrs = getStateDataAttrs(state, stateAttrMap);
// Merge: state data attrs first, then props (so props can override)
const propsArray = Array.isArray(props) ? props : props ? [props] : [];