mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(core): add mute button component (#455)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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] : [];
|
||||
|
||||
Reference in New Issue
Block a user