diff --git a/CHANGELOG.md b/CHANGELOG.md index 88bd8f3d8d..d6df59e6e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -400,6 +400,7 @@ Breaking changes in this release: - Fixed virtual keyboard should show up on tap after being suppressed, in iOS 26.2, by [@compulim](https://github.com/compulim) in PR [#5678](https://github.com/microsoft/BotFramework-WebChat/pull/5678) - Fixed compatibility with `create-react-app` by adding file extension to `core-js` imports, by [@compulim](https://github.com/compulim) in PR [#5680](https://github.com/microsoft/BotFramework-WebChat/pull/5680) - Fixed virtual keyboard should be collapsed after being suppressed, in iOS 26.3, by [@compulim](https://github.com/compulim) in PR [#5757](https://github.com/microsoft/BotFramework-WebChat/pull/5757) +- Fixed Fluent/Copilot typing indicator animation background color, in PR [#5770](https://github.com/microsoft/BotFramework-WebChat/pull/5770), by [@OEvgeny](https://github.com/OEvgeny) ## [4.18.0] - 2024-07-10 diff --git a/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html b/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html new file mode 100644 index 0000000000..4c0c8168c6 --- /dev/null +++ b/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html.snap-1.png b/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html.snap-1.png new file mode 100644 index 0000000000..ac6d2f67e3 Binary files /dev/null and b/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html.snap-1.png differ diff --git a/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html.snap-2.png b/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html.snap-2.png new file mode 100644 index 0000000000..ba4df54f75 Binary files /dev/null and b/__tests__/html2/typing/typingIndicator.scroll.copilot.dark.html.snap-2.png differ diff --git a/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html b/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html new file mode 100644 index 0000000000..a5d955a6bb --- /dev/null +++ b/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html.snap-1.png b/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html.snap-1.png new file mode 100644 index 0000000000..22facf3088 Binary files /dev/null and b/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html.snap-1.png differ diff --git a/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html.snap-2.png b/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html.snap-2.png new file mode 100644 index 0000000000..d212fa83d4 Binary files /dev/null and b/__tests__/html2/typing/typingIndicator.scroll.fluent.dark.html.snap-2.png differ diff --git a/__tests__/html2/typing/typingIndicator.scroll.html b/__tests__/html2/typing/typingIndicator.scroll.html index 1004ddfcc7..8dc93a51ec 100644 --- a/__tests__/html2/typing/typingIndicator.scroll.html +++ b/__tests__/html2/typing/typingIndicator.scroll.html @@ -1,127 +1,170 @@ - - - - - - - - - - - -
- - + + + + + + + + +
+ - - + + // THEN: Should hide typing indicator. + await waitFor(() => expect(pageElements.typingIndicator()).toBeFalsy()); + + // THEN: Should match snapshot. + await host.snapshot('local'); + }); + + + + \ No newline at end of file diff --git a/packages/fluent-theme/src/components/assets/AssetComposer.tsx b/packages/fluent-theme/src/components/assets/AssetComposer.tsx index 32ab9f02a2..6d64a67d5e 100644 --- a/packages/fluent-theme/src/components/assets/AssetComposer.tsx +++ b/packages/fluent-theme/src/components/assets/AssetComposer.tsx @@ -10,6 +10,11 @@ type AssetComposerProps = Readonly<{ const SLIDING_DOTS_SVG_STRING = ''; +const SLIDING_DOTS_REDUCED_MOTION_SVG_STRING = SLIDING_DOTS_SVG_STRING.replaceAll( + 'repeatCount="indefinite"', + 'repeatCount="0.01" fill="freeze"' +); + const AssetComposer = memo(({ children }: AssetComposerProps) => { const slidingDotsURL = useMemo( // Content is hardcoded. @@ -18,14 +23,25 @@ const AssetComposer = memo(({ children }: AssetComposerProps) => { [] ); + const slidingDotsReducedMotionURL = useMemo( + // Content is hardcoded. + // eslint-disable-next-line no-restricted-properties + () => URL.createObjectURL(new Blob([SLIDING_DOTS_REDUCED_MOTION_SVG_STRING], { type: 'image/svg+xml' })), + [] + ); + useEffect(() => () => URL.revokeObjectURL(slidingDotsURL), [slidingDotsURL]); + useEffect(() => () => URL.revokeObjectURL(slidingDotsReducedMotionURL), [slidingDotsReducedMotionURL]); const context = useMemo( () => Object.freeze({ - urlStateMap: new Map([['sliding dots', Object.freeze([new URL(slidingDotsURL)])]]) + urlStateMap: new Map([ + ['sliding dots', Object.freeze([new URL(slidingDotsURL)])], + ['sliding dots reduced-motion', Object.freeze([new URL(slidingDotsReducedMotionURL)])] + ]) }), - [slidingDotsURL] + [slidingDotsURL, slidingDotsReducedMotionURL] ); return {children}; diff --git a/packages/fluent-theme/src/components/assets/AssetName.ts b/packages/fluent-theme/src/components/assets/AssetName.ts index ced8915681..33e782f35f 100644 --- a/packages/fluent-theme/src/components/assets/AssetName.ts +++ b/packages/fluent-theme/src/components/assets/AssetName.ts @@ -1 +1 @@ -export type AssetName = 'sliding dots'; +export type AssetName = 'sliding dots' | 'sliding dots reduced-motion'; diff --git a/packages/fluent-theme/src/components/assets/SlidingDots.tsx b/packages/fluent-theme/src/components/assets/SlidingDots.tsx index aabc45727c..ed5d5e6e1b 100644 --- a/packages/fluent-theme/src/components/assets/SlidingDots.tsx +++ b/packages/fluent-theme/src/components/assets/SlidingDots.tsx @@ -1,6 +1,5 @@ import { hooks } from 'botframework-webchat'; -import React, { memo, useCallback, useEffect, useRef } from 'react'; -import { useRefFrom } from 'use-ref-from'; +import React, { memo } from 'react'; import useAssetURL from './private/useAssetURL'; @@ -10,50 +9,12 @@ type SlidingDotsProps = Readonly<{ className: string }>; const SlidingDots = ({ className }: SlidingDotsProps) => { const [shouldReduceMotion] = useShouldReduceMotion(); - const [url] = useAssetURL('sliding dots'); + const [url] = useAssetURL(shouldReduceMotion ? 'sliding dots reduced-motion' : 'sliding dots'); const localize = useLocalizer(); - const objectElementRef = useRef(null); const altText = localize('TYPING_INDICATOR_ALT'); - const shouldReduceMotionRef = useRefFrom(shouldReduceMotion); - - const pauseAnimations = useCallback(() => { - const contentDocument = objectElementRef.current?.contentDocument; - const svgElement = contentDocument?.documentElement; - const { SVGSVGElement } = contentDocument?.defaultView || {}; - - SVGSVGElement && svgElement instanceof SVGSVGElement && svgElement.pauseAnimations(); - }, [objectElementRef]); - - const unpauseAnimations = useCallback(() => { - const contentDocument = objectElementRef.current?.contentDocument; - const svgElement = contentDocument?.documentElement; - const { SVGSVGElement } = contentDocument?.defaultView || {}; - - SVGSVGElement && svgElement instanceof SVGSVGElement && svgElement.unpauseAnimations(); - }, [objectElementRef]); - - const pauseOrUnpauseAnimations = useCallback( - () => (shouldReduceMotionRef.current ? pauseAnimations() : unpauseAnimations()), - [pauseAnimations, shouldReduceMotionRef, unpauseAnimations] - ); - - useEffect(pauseOrUnpauseAnimations, [ - pauseOrUnpauseAnimations, - // Call "pauseOrUnpauseAnimations()" when "shouldReduceMotion" change. - shouldReduceMotion - ]); - - return ( - - ); + + return {altText}; }; SlidingDots.displayName = 'SlidingDots'; diff --git a/packages/test/test-assets/entry/@fluentui/tokens.js b/packages/test/test-assets/entry/@fluentui/tokens.js index a0df7ac1e5..0a7de684ab 100644 --- a/packages/test/test-assets/entry/@fluentui/tokens.js +++ b/packages/test/test-assets/entry/@fluentui/tokens.js @@ -1 +1 @@ -export { createDarkTheme, webLightTheme } from '@fluentui/tokens'; +export { createDarkTheme, webDarkTheme, webLightTheme } from '@fluentui/tokens';