diff --git a/change/@fluentui-react-motion-f4f8b668-785c-48d9-9d8f-2410472b12f4.json b/change/@fluentui-react-motion-f4f8b668-785c-48d9-9d8f-2410472b12f4.json
new file mode 100644
index 0000000000000..81ed563ea2612
--- /dev/null
+++ b/change/@fluentui-react-motion-f4f8b668-785c-48d9-9d8f-2410472b12f4.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "fix: handle case when Animation.persist() does not exist",
+ "packageName": "@fluentui/react-motion",
+ "email": "seanmonahan@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/react-components/react-motion/library/src/factories/createMotionComponent.test.tsx b/packages/react-components/react-motion/library/src/factories/createMotionComponent.test.tsx
index a2508262d2f20..7aa5ce73c5f76 100644
--- a/packages/react-components/react-motion/library/src/factories/createMotionComponent.test.tsx
+++ b/packages/react-components/react-motion/library/src/factories/createMotionComponent.test.tsx
@@ -36,6 +36,27 @@ function createElementMock() {
}
describe('createMotionComponent', () => {
+ let hasAnimation: boolean;
+ beforeEach(() => {
+ if (!global.Animation) {
+ hasAnimation = false;
+ global.Animation = {
+ // @ts-expect-error mock
+ prototype: {
+ persist: jest.fn(),
+ },
+ };
+ } else {
+ hasAnimation = true;
+ }
+ });
+
+ afterEach(() => {
+ if (!hasAnimation) {
+ // @ts-expect-error mock
+ delete global.Animation;
+ }
+ });
it('creates a motion and plays it', () => {
const TestAtom = createMotionComponent(motion);
const { animateMock, ElementMock } = createElementMock();
@@ -52,6 +73,24 @@ describe('createMotionComponent', () => {
});
});
+ it('creates a motion and plays it (without .persist())', () => {
+ // @ts-expect-error mock
+ delete global.Animation.prototype.persist;
+ const TestAtom = createMotionComponent(motion);
+ const { animateMock, ElementMock } = createElementMock();
+
+ render(
+
+
+ ,
+ );
+
+ expect(animateMock).toHaveBeenCalledWith(motion.keyframes, {
+ duration: 1,
+ fill: 'forwards',
+ });
+ });
+
it('supports functions as motion definitions', () => {
const fnMotion = jest.fn().mockImplementation(() => motion);
const TestAtom = createMotionComponent(fnMotion);
diff --git a/packages/react-components/react-motion/library/src/factories/createPresenceComponent.test.tsx b/packages/react-components/react-motion/library/src/factories/createPresenceComponent.test.tsx
index ad2cedcacd631..a1cd9bb2df262 100644
--- a/packages/react-components/react-motion/library/src/factories/createPresenceComponent.test.tsx
+++ b/packages/react-components/react-motion/library/src/factories/createPresenceComponent.test.tsx
@@ -41,6 +41,28 @@ function createElementMock() {
}
describe('createPresenceComponent', () => {
+ let hasAnimation: boolean;
+ beforeEach(() => {
+ if (!global.Animation) {
+ hasAnimation = false;
+ global.Animation = {
+ // @ts-expect-error mock
+ prototype: {
+ persist: jest.fn(),
+ },
+ };
+ } else {
+ hasAnimation = true;
+ }
+ });
+
+ afterEach(() => {
+ if (!hasAnimation) {
+ // @ts-expect-error mock
+ delete global.Animation;
+ }
+ });
+
describe('appear', () => {
it('does not animate by default', () => {
const TestPresence = createPresenceComponent(motion);
@@ -68,6 +90,24 @@ describe('createPresenceComponent', () => {
expect(animateMock).toHaveBeenCalledWith(enterKeyframes, options);
});
+ it('animates when is "true" (without .persist())', () => {
+ // @ts-expect-error mock
+ delete window.Animation.prototype.persist;
+ const TestPresence = createPresenceComponent(motion);
+ const { animateMock, ElementMock } = createElementMock();
+
+ render(
+
+
+ ,
+ );
+
+ expect(animateMock).toHaveBeenCalledWith(enterKeyframes, {
+ ...options,
+ duration: 1,
+ });
+ });
+
it('finishes motion when wrapped in motion behaviour context with skip behaviour', async () => {
const TestPresence = createPresenceComponent(motion);
const { finishMock, ElementMock } = createElementMock();
diff --git a/packages/react-components/react-motion/library/src/hooks/useAnimateAtoms.ts b/packages/react-components/react-motion/library/src/hooks/useAnimateAtoms.ts
index 66ea0c35e1b6b..6fa1294484363 100644
--- a/packages/react-components/react-motion/library/src/hooks/useAnimateAtoms.ts
+++ b/packages/react-components/react-motion/library/src/hooks/useAnimateAtoms.ts
@@ -2,6 +2,9 @@ import * as React from 'react';
import type { AnimationHandle, AtomMotion } from '../types';
function useAnimateAtomsInSupportedEnvironment() {
+ // eslint-disable-next-line @nx/workspace-no-restricted-globals
+ const SUPPORTS_PERSIST = typeof window !== 'undefined' && typeof window.Animation?.prototype.persist === 'function';
+
return React.useCallback(
(
element: HTMLElement,
@@ -19,10 +22,10 @@ function useAnimateAtomsInSupportedEnvironment() {
fill: 'forwards',
...params,
- ...(isReducedMotion && { duration: 1 }),
+ ...((isReducedMotion || !SUPPORTS_PERSIST) && { duration: 1 }),
});
- animation.persist();
+ SUPPORTS_PERSIST && animation.persist();
return animation;
});