mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix(packages): handle menu child mutations (#1739)
This commit is contained in:
@@ -252,7 +252,7 @@ export class HlsJsMedia extends HTMLVideoElementHost implements HlsMediaProps {
|
||||
return {
|
||||
...this.config.hlsJs,
|
||||
preferPlayback: this.config.preferPlayback,
|
||||
contentType: this.config.contentType,
|
||||
contentType: this.config.contentType ?? inferContentType(this.#src),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MediaError } from '../../../../core/media/media-error';
|
||||
import type { RemotePlaybackLike } from '../../../../core/media/types';
|
||||
import { addComponent, type Component } from '../../media-host';
|
||||
import { NativeHlsMedia } from '../../native-hls';
|
||||
import { ContentTypes, HlsJsMedia } from '../index';
|
||||
import { ContentTypes, Hls, HlsJsMedia } from '../index';
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
@@ -162,6 +162,26 @@ describe('HlsJsMedia', () => {
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('recreates the engine when inferred content type changes', () => {
|
||||
vi.spyOn(Hls, 'isSupported').mockReturnValue(true);
|
||||
|
||||
const video = document.createElement('video');
|
||||
document.body.appendChild(video);
|
||||
|
||||
const media = new HlsJsMedia();
|
||||
media.attach(video);
|
||||
|
||||
media.src = 'https://example.com/video.mp4';
|
||||
media.load();
|
||||
|
||||
expect(media.engine).toBeNull();
|
||||
|
||||
media.src = 'https://example.com/video.m3u8';
|
||||
media.load();
|
||||
|
||||
expect(media.engine).not.toBeNull();
|
||||
});
|
||||
|
||||
it('resets free-form config when a new object is assigned', () => {
|
||||
const { media } = setup();
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ const MENU_VIEW_ACTIVE_STATE = 'active';
|
||||
const MENU_VIEW_INACTIVE_STATE = 'inactive';
|
||||
const MENU_ROOT_VIEW_ATTR = 'data-menu-root-view';
|
||||
const MENU_VIEWPORT_ATTR = 'data-menu-viewport';
|
||||
const MENU_VIEW_LAYOUT_ATTRS = ['data-availability'];
|
||||
const MENU_WIDTH_VAR = '--media-menu-width';
|
||||
const MENU_HEIGHT_VAR = '--media-menu-height';
|
||||
const MENU_VIEW_MEASURE_STYLE_PROPERTIES = [
|
||||
@@ -273,6 +274,34 @@ export function syncMenuViewRoot(
|
||||
setViewportSize(content, size);
|
||||
}
|
||||
|
||||
export function observeMenuViewContent(content: HTMLElement, onChange: () => void): () => void {
|
||||
if (typeof MutationObserver === 'undefined') return () => {};
|
||||
|
||||
let rafId = 0;
|
||||
|
||||
function scheduleChange(): void {
|
||||
cancelAnimationFrame(rafId);
|
||||
rafId = requestAnimationFrame(() => {
|
||||
rafId = 0;
|
||||
onChange();
|
||||
});
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(scheduleChange);
|
||||
|
||||
observer.observe(content, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: MENU_VIEW_LAYOUT_ATTRS,
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(rafId);
|
||||
observer.disconnect();
|
||||
};
|
||||
}
|
||||
|
||||
export function syncMenuViewTransition(
|
||||
content: HTMLElement | null,
|
||||
view: HTMLElement | null,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
getMenuRootViewAttrs,
|
||||
getMenuViewportAttrs,
|
||||
getMenuViewportElement,
|
||||
observeMenuViewContent,
|
||||
syncMenuViewRoot,
|
||||
syncMenuViewTransition,
|
||||
} from '../menu-viewport-transition';
|
||||
@@ -38,6 +39,15 @@ function createRect(width: number, height: number): DOMRect {
|
||||
} as DOMRect;
|
||||
}
|
||||
|
||||
function nextFrame(): Promise<void> {
|
||||
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
||||
}
|
||||
|
||||
async function waitForMutationFrame(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await nextFrame();
|
||||
}
|
||||
|
||||
function mockMenuViewSize(
|
||||
element: HTMLElement,
|
||||
{
|
||||
@@ -125,6 +135,29 @@ describe('menu-viewport-transition', () => {
|
||||
expect(getMenuViewportElement(content)).toBe(content);
|
||||
});
|
||||
|
||||
it('observes child and availability changes in menu content', async () => {
|
||||
const content = addElement();
|
||||
const item = document.createElement('div');
|
||||
const onChange = vi.fn();
|
||||
const cleanup = observeMenuViewContent(content, onChange);
|
||||
|
||||
content.append(item);
|
||||
await waitForMutationFrame();
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
|
||||
item.setAttribute('data-availability', 'available');
|
||||
await waitForMutationFrame();
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
|
||||
cleanup();
|
||||
item.setAttribute('data-availability', 'unavailable');
|
||||
await waitForMutationFrame();
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('toggles menu viewport data attributes for active and exiting phases', () => {
|
||||
const content = addElement();
|
||||
const rootView = document.createElement('div');
|
||||
|
||||
Reference in New Issue
Block a user