diff --git a/packages/react/cypress/component/LazyVideo.cy.tsx b/packages/react/cypress/component/LazyVideo.cy.tsx
index d7b5dc5..351f446 100644
--- a/packages/react/cypress/component/LazyVideo.cy.tsx
+++ b/packages/react/cypress/component/LazyVideo.cy.tsx
@@ -84,6 +84,31 @@ describe('Accessibility controls', () => {
cy.get("button").and("have.css", "left");
})
+ it('controls affect playback', () => {
+
+ const onPauseSpy = cy.spy().as("onPauseSpy")
+ const onPlaySpy = cy.spy().as("onPlaySpy");
+
+ cy.mount(
+
+ );
+
+ cy.get("video").isPlaying();
+ cy.get("[aria-label=Pause]").click();
+ cy.get("video").isPaused();
+ cy.get("[aria-label=Play]").click();
+ cy.get("video").isPlaying(); // The second time
+
+ cy.get("@onPauseSpy").should("have.been.calledOnce");
+ cy.get("@onPlaySpy").should("have.been.calledTwice");
+
+ })
+
it("allows a different position to be set", () => {
cy.mount(
{
accessibilityControlsPosition='top right'
/>
);
- cy.get("button").should("have.css", "top")
- cy.get("button").and("have.css", "right");
+ cy.get("[aria-label=Pause]").should("have.css", "top")
+ cy.get("[aria-label=Pause]").and("have.css", "right");
});
it('allows the controls to be hidden', () => {
@@ -104,7 +129,7 @@ describe('Accessibility controls', () => {
hideAccessibilityControls
/>
);
- cy.get("button").should('not.exist')
+ cy.get("[aria-label=Pause]").should("not.exist");
})
it('can have custom icons', () => {
@@ -116,7 +141,7 @@ describe('Accessibility controls', () => {
pauseIcon={() => Pause}
/>
);
- cy.get('button').contains('Pause')
+ cy.get("[aria-label=Pause]").contains("Pause");
})
})
diff --git a/packages/react/src/LazyVideo/AccessibilityControls.tsx b/packages/react/src/LazyVideo/AccessibilityControls.tsx
index ff5966d..0abc4cb 100644
--- a/packages/react/src/LazyVideo/AccessibilityControls.tsx
+++ b/packages/react/src/LazyVideo/AccessibilityControls.tsx
@@ -12,39 +12,39 @@ const minAccessibleBtnSize = 24
// How far from the edge to position the button
const positionGutter = '1em'
-type AccessibilityControlsProps = Pick & {
- play: () => void
- pause: () => void
-}
+type AccessibilityControlsProps = Pick<
+ LazyVideoProps,
+ | "playIcon"
+ | "pauseIcon"
+ | "hideAccessibilityControls"
+ | "accessibilityControlsPosition"
+> & {
+ isVideoPaused: boolean;
+ play: () => void;
+ pause: () => void;
+};
// Adds a simple pause/play UI for accessibility use cases
-export default function AccessibilityControls({ play,
+export default function AccessibilityControls({
+ play,
pause,
- paused,
+ isVideoPaused,
playIcon,
pauseIcon,
hideAccessibilityControls,
- accessibilityControlsPosition
+ accessibilityControlsPosition,
}: AccessibilityControlsProps): ReactElement | null {
-
// If hidden, return nothing
- if (hideAccessibilityControls) return null
+ if (hideAccessibilityControls) return null;
// Determine the icon to display
- const Icon = paused
- ? playIcon || PlayIcon
- : pauseIcon || PauseIcon;
+ const Icon = isVideoPaused ? playIcon || PlayIcon : pauseIcon || PauseIcon;
return (
);
-
}
// Make the styles for positioning the button
diff --git a/packages/react/src/LazyVideo/LazyVideoClient.tsx b/packages/react/src/LazyVideo/LazyVideoClient.tsx
index 7bf2a94..855664f 100644
--- a/packages/react/src/LazyVideo/LazyVideoClient.tsx
+++ b/packages/react/src/LazyVideo/LazyVideoClient.tsx
@@ -3,7 +3,7 @@
import { useInView } from 'react-intersection-observer'
import { useMediaQueries } from '@react-hook/media-query'
-import { useEffect, type ReactElement, useRef, useCallback, type MutableRefObject } from 'react'
+import { useEffect, type ReactElement, useRef, useCallback, type MutableRefObject, useState } from 'react'
import type { LazyVideoProps } from '../types/lazyVideoTypes';
import { fillStyles, transparentGif } from '../lib/styles'
import AccessibilityControls from './AccessibilityControls'
@@ -31,12 +31,18 @@ export default function LazyVideoClient({
position,
priority,
noPoster,
- paused,
+ paused, // Used to control externally
+ onPause,
+ onPlay,
playIcon,
pauseIcon,
hideAccessibilityControls,
accessibilityControlsPosition,
}: LazyVideoClientProps): ReactElement {
+
+ // Track the actual video playback state
+ const [isVideoPaused, setVideoPaused] = useState(true)
+
// Make a ref to the video so it can be controlled
const videoRef = useRef();
@@ -81,6 +87,35 @@ export default function LazyVideoClient({
paused ? pause() : play();
}, [paused]);
+ // Update internal play/pause state
+ useEffect(() => {
+ const videoElement = videoRef.current;
+
+ const handlePlay = () => {
+ setVideoPaused(false);
+ onPlay && onPlay();
+ };
+
+ const handlePause = () => {
+ setVideoPaused(true);
+ onPause && onPause();
+ };
+
+ // Add listeners
+ if (videoElement) {
+ videoElement.addEventListener("play", handlePlay);
+ videoElement.addEventListener("pause", handlePause);
+ }
+
+ // Cleanup
+ return () => {
+ if (videoElement) {
+ videoElement.removeEventListener("play", handlePlay);
+ videoElement.removeEventListener("pause", handlePause);
+ }
+ };
+ }, []);
+
// Simplify logic for whether to load sources
const shouldLoad = priority || inView;
@@ -122,7 +157,7 @@ export default function LazyVideoClient({
{...{
play,
pause,
- paused,
+ isVideoPaused,
playIcon,
pauseIcon,
hideAccessibilityControls,
diff --git a/packages/react/src/ReactVisual.tsx b/packages/react/src/ReactVisual.tsx
index cb02e13..953dca5 100644
--- a/packages/react/src/ReactVisual.tsx
+++ b/packages/react/src/ReactVisual.tsx
@@ -29,6 +29,8 @@ export default function ReactVisual(
sourceTypes,
sourceMedia,
paused,
+ onPause,
+ onPlay,
playIcon,
pauseIcon,
hideAccessibilityControls,
@@ -90,6 +92,8 @@ export default function ReactVisual(
priority,
noPoster: !!image, // Use `image` as poster frame
paused,
+ onPause,
+ onPlay,
playIcon,
pauseIcon,
hideAccessibilityControls,
diff --git a/packages/react/src/types/lazyVideoTypes.ts b/packages/react/src/types/lazyVideoTypes.ts
index f9a01ce..a7e9195 100644
--- a/packages/react/src/types/lazyVideoTypes.ts
+++ b/packages/react/src/types/lazyVideoTypes.ts
@@ -9,6 +9,8 @@ export type LazyVideoProps = Pick void;
+ onPlay?: () => void;
playIcon?: ComponentType;
pauseIcon?: ComponentType;