fix(site): surface native media properties on element reference pages (#1722)

This commit is contained in:
Darius Cepulis
2026-07-15 09:31:13 -07:00
committed by GitHub
parent a9a09a7e52
commit 0dd84b819e
19 changed files with 1169 additions and 444 deletions
@@ -81,6 +81,7 @@ export function CustomMediaElement(tag: string, Host: any) {
poster: { type: String },
preload: { type: String },
src: { type: String },
streamType: { type: String, attribute: 'stream-type' },
};
}
@@ -0,0 +1,40 @@
/** Mock iframe-backed media host — mirrors VimeoMedia. */
export const embedMediaDefaultProps = {
src: '',
autoplay: false,
};
export class EmbedHost extends EventTarget {
#src = embedMediaDefaultProps.src;
#autoplay = embedMediaDefaultProps.autoplay;
get src(): string {
return this.#src;
}
set src(value: string) {
this.#src = value;
}
get autoplay(): boolean {
return this.#autoplay;
}
set autoplay(value: boolean) {
this.#autoplay = value;
}
/** Start playback through the embedded player. */
play(): Promise<void> {
this.dispatchEvent(new Event('play'));
return Promise.resolve();
}
attach(_target: HTMLIFrameElement): void {
const emit = (type: string) => this.dispatchEvent(new Event(type));
emit('waiting');
for (const type of ['loadedmetadata', 'adapterready']) {
this.dispatchEvent(new Event(type));
}
}
detach(): void {}
destroy(): void {}
}
@@ -4,6 +4,10 @@
* Exercises method extraction: the builder collects public instance methods
* from this class (per media type) for the reference's `methods` field.
* Lifecycle methods (attach/detach/destroy) and accessors are excluded.
*
* Also exercises native-property extraction: getters/setters whose names match
* native HTMLMediaElement members surface in `nativeProperties`; non-native
* accessors (e.g. `streamType`) and re-declared natives (`src`) do not.
*/
export class HTMLMediaElementHost {
// Lifecycle methods — excluded from `methods`.
@@ -14,11 +18,33 @@ export class HTMLMediaElementHost {
// Internal — excluded by the `_` prefix.
_forward(): void {}
// Accessor — excluded (getters/setters are properties, not methods).
// Accessor — excluded from `methods` (it's a property, not a method). Native
// member, but re-declared on engine hosts → deduped out of nativeProperties.
get src(): string {
return '';
}
// Native passthroughs — surface in nativeProperties.
get currentTime(): number {
return 0;
}
set currentTime(_value: number) {}
get volume(): number {
return 1;
}
set volume(_value: number) {}
// Video.js-specific — NOT a native member, so excluded from nativeProperties.
/**
* Current stream type (`'on-demand'`, `'live'`, or `'unknown'`). Consumers can
* set it when the host does not detect the stream type automatically.
*/
get streamType(): string {
return 'unknown';
}
set streamType(_value: string) {}
play(): Promise<void> {
return Promise.resolve();
}
@@ -2,7 +2,9 @@
* Mock video host base — mirrors the real video-host.ts.
*
* Exercises video-specific method extraction: requestFullscreen is added on
* top of the shared media-host methods for video elements only.
* top of the shared media-host methods for video elements only. Also exercises
* video-only native-property extraction (videoWidth) and a non-native helper
* (isFullscreen) that must be filtered out of nativeProperties.
*/
import { HTMLMediaElementHost } from './media-host';
@@ -10,4 +12,14 @@ export class HTMLVideoElementHost extends HTMLMediaElementHost {
requestFullscreen(): Promise<void> {
return Promise.resolve();
}
// Native HTMLVideoElement member — surfaces in nativeProperties (video only).
get videoWidth(): number {
return 0;
}
// Video.js-specific helper — NOT a native member, excluded.
get isFullscreen(): boolean {
return false;
}
}
@@ -0,0 +1,5 @@
import { EmbedVideo } from '../../media/embed-video';
export class EmbedVideoElement extends EmbedVideo {
static readonly tagName = 'embed-video';
}
@@ -0,0 +1,10 @@
import { CustomMediaElement } from '../../../../core/src/dom/media/custom-media-element';
import { EmbedHost } from '../../../../core/src/dom/media/embed';
function MediaAttachMixin(base: any) {
return base;
}
class EmbedCustomMediaElement extends CustomMediaElement('iframe', EmbedHost) {}
export class EmbedVideo extends MediaAttachMixin(EmbedCustomMediaElement) {}
@@ -0,0 +1,22 @@
/**
* Mock React media component.
*
* Exercises the source conventions used by real media components:
* native React video props, a forwarded native-element ref, and a defaults
* object passed to useSyncProps for Video.js-specific props.
*/
import { complexMediaDefaultProps } from '../../../../core/src/dom/media/complex';
interface VideoHTMLAttributes<Element> {
element?: Element;
}
interface ComplexVideoProps extends VideoHTMLAttributes<HTMLVideoElement>, Partial<typeof complexMediaDefaultProps> {}
declare function forwardRef<Ref, Props>(render: (props: Props, ref: Ref) => unknown): unknown;
declare function useSyncProps(target: object, props: object, defaults: object): object;
export const ComplexVideo = forwardRef<HTMLVideoElement, ComplexVideoProps>(function ComplexVideo(props, ref) {
useSyncProps({}, props, complexMediaDefaultProps);
return { ref };
});
@@ -0,0 +1,11 @@
import { embedMediaDefaultProps } from '../../../../core/src/dom/media/embed';
interface EmbedVideoProps extends Partial<typeof embedMediaDefaultProps> {}
declare function forwardRef<Ref, Props>(render: (props: Props, ref: Ref) => unknown): unknown;
declare function useSyncProps(target: object, props: object, defaults: object): object;
export const EmbedVideo = forwardRef<HTMLIFrameElement, EmbedVideoProps>(function EmbedVideo(props, ref) {
useSyncProps({}, props, embedMediaDefaultProps);
return { ref };
});