diff --git a/packages/react/src/ui/captions-radio-group/tests/use-captions-options.test.tsx b/packages/react/src/ui/captions-radio-group/tests/use-captions-options.test.tsx index a0a169cd..7457366b 100644 --- a/packages/react/src/ui/captions-radio-group/tests/use-captions-options.test.tsx +++ b/packages/react/src/ui/captions-radio-group/tests/use-captions-options.test.tsx @@ -1,10 +1,11 @@ 'use client'; -import { cleanup, fireEvent, render, screen } from '@testing-library/react'; +import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'; import { CAPTIONS_OFF_VALUE } from '@videojs/core'; import type { ReactNode } from 'react'; import { afterEach, describe, expect, it, vi } from 'vitest'; +import { PlayerContextProvider, type PlayerContextValue } from '../../../player/context'; import { createPlayerWrapper } from '../../../testing/mocks'; import { Menu } from '../../menu'; import { useCaptionsOptions } from '../use-captions-options'; @@ -45,6 +46,42 @@ function renderCaptionsMenu({ return { selectSubtitlesTrack }; } +function createReactiveTextTrackWrapper(initialState: Record) { + const listeners = new Set<() => void>(); + const store = { + state: initialState, + subscribe: (callback: () => void) => { + listeners.add(callback); + return () => listeners.delete(callback); + }, + attach: vi.fn(() => vi.fn()), + destroy: vi.fn(), + }; + + const value: PlayerContextValue = { + store: store as unknown as PlayerContextValue['store'], + media: null, + setMedia: vi.fn(), + container: null, + setContainer: vi.fn(), + }; + + return { + updateState(next: Record) { + store.state = next; + for (const listener of listeners) listener(); + }, + Wrapper({ children }: { children: ReactNode }) { + return {children}; + }, + }; +} + +function CaptionsAvailability(): ReactNode { + const captions = useCaptionsOptions(); + return
{captions?.state.availability ?? 'missing'}
; +} + function CaptionsRadioGroup(): ReactNode { const captions = useCaptionsOptions(); if (!captions?.showMenu) return null; @@ -94,4 +131,37 @@ describe('useCaptionsOptions', () => { expect(selectSubtitlesTrack).toHaveBeenCalledWith(CAPTIONS_OFF_VALUE); }); + + it('updates when caption tracks become available', () => { + const { Wrapper, updateState } = createReactiveTextTrackWrapper({ + chaptersCues: [], + thumbnailCues: [], + thumbnailTrackSrc: null, + textTrackList: [], + subtitlesShowing: false, + selectSubtitlesTrack: vi.fn(), + toggleSubtitles: vi.fn(), + }); + + render(, { wrapper: Wrapper }); + + expect(screen.getByTestId('availability').textContent).toBe('unavailable'); + + act(() => { + updateState({ + chaptersCues: [], + thumbnailCues: [], + thumbnailTrackSrc: null, + textTrackList: [ + { kind: 'subtitles', label: 'English', language: 'en', mode: 'disabled' }, + { kind: 'subtitles', label: 'Spanish', language: 'es', mode: 'showing' }, + ], + subtitlesShowing: true, + selectSubtitlesTrack: vi.fn(), + toggleSubtitles: vi.fn(), + }); + }); + + expect(screen.getByTestId('availability').textContent).toBe('available'); + }); }); diff --git a/packages/react/src/ui/captions-radio-group/use-captions-options.ts b/packages/react/src/ui/captions-radio-group/use-captions-options.ts index 0a7374d4..2ee499b4 100644 --- a/packages/react/src/ui/captions-radio-group/use-captions-options.ts +++ b/packages/react/src/ui/captions-radio-group/use-captions-options.ts @@ -1,10 +1,6 @@ 'use client'; -import { - CAPTIONS_OFF_VALUE, - type CaptionsRadioGroupCore, - CaptionsRadioGroupCore as CaptionsRadioGroupCoreClass, -} from '@videojs/core'; +import { CAPTIONS_OFF_VALUE, CaptionsRadioGroupCore } from '@videojs/core'; import { logMissingFeature, selectTextTrack } from '@videojs/core/dom'; import { useCallback, useState } from 'react'; @@ -28,8 +24,10 @@ export interface CaptionsOptionsResult { } export function useCaptionsOptions(props?: CaptionsOptionsProps): CaptionsOptionsResult | null { + 'use no memo'; + const media = usePlayer(selectTextTrack); - const [core] = useState(() => new CaptionsRadioGroupCoreClass()); + const [core] = useState(() => new CaptionsRadioGroupCore()); core.setProps(props ?? {});