From 0b069d6fbb7c3705682cacf25bf3ae171a26621b Mon Sep 17 00:00:00 2001 From: Sarah Higley Date: Wed, 12 Feb 2025 13:58:50 -0800 Subject: [PATCH] remove stray console log --- ...-bf44df36-9898-4ce0-b4d5-decf3c170b35.json | 7 +++ .../src/components/Carousel/useCarousel.ts | 1 - .../src/Dialog/DialogDefault.stories.tsx | 48 +++++++++++-------- .../src/Menu/MenuNestedSubmenus.stories.tsx | 8 ++-- .../MenuNestedSubmenusControlled.stories.tsx | 8 ++-- .../Carousel/Carousel.tsx | 47 ++++++++---------- .../Carousel/Carousel.types.ts | 30 +++++++++++- .../TeachingPopoverCarousel.types.ts | 26 ++-------- .../useTeachingPopoverCarousel.ts | 5 +- .../TeachingPopoverCarousel.stories.tsx | 6 ++- yarn.lock | 39 +++------------ 11 files changed, 110 insertions(+), 115 deletions(-) create mode 100644 change/@fluentui-react-carousel-bf44df36-9898-4ce0-b4d5-decf3c170b35.json diff --git a/change/@fluentui-react-carousel-bf44df36-9898-4ce0-b4d5-decf3c170b35.json b/change/@fluentui-react-carousel-bf44df36-9898-4ce0-b4d5-decf3c170b35.json new file mode 100644 index 00000000000000..92ee57d5223b9d --- /dev/null +++ b/change/@fluentui-react-carousel-bf44df36-9898-4ce0-b4d5-decf3c170b35.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: autoplay carousel does not trigger live region announcements", + "packageName": "@fluentui/react-carousel", + "email": "sarah.higley@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-carousel/library/src/components/Carousel/useCarousel.ts b/packages/react-components/react-carousel/library/src/components/Carousel/useCarousel.ts index 4eff7331f04a2b..00439663bca229 100644 --- a/packages/react-components/react-carousel/library/src/components/Carousel/useCarousel.ts +++ b/packages/react-components/react-carousel/library/src/components/Carousel/useCarousel.ts @@ -96,7 +96,6 @@ export function useCarousel_unstable(props: CarouselProps, ref: React.Ref { return ( - - - - - - - Dialog title - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam exercitationem cumque repellendus eaque - est dolor eius expedita nulla ullam? Tenetur reprehenderit aut voluptatum impedit voluptates in natus iure - cumque eaque? - - - - - - - - - - + <> + + + + + + + Dialog title + + Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam exercitationem cumque repellendus eaque + est dolor eius expedita nulla ullam? Tenetur reprehenderit aut voluptatum impedit voluptates in natus iure + cumque eaque? + + + + + + + + + + +
+ + +
+ ); }; diff --git a/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenus.stories.tsx b/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenus.stories.tsx index 49b3af2ca775f7..a36bfc720c3b74 100644 --- a/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenus.stories.tsx +++ b/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenus.stories.tsx @@ -4,7 +4,7 @@ import { Button, Menu, MenuTrigger, MenuList, MenuItem, MenuPopover } from '@flu const EditorLayoutSubMenu = () => { return ( - + Editor Layout @@ -22,7 +22,7 @@ const EditorLayoutSubMenu = () => { const AppearanceSubMenu = () => { return ( - + Appearance @@ -41,7 +41,7 @@ const AppearanceSubMenu = () => { const PreferencesSubMenu = () => { return ( - + Preferences @@ -61,7 +61,7 @@ const PreferencesSubMenu = () => { export const NestedSubmenus = () => { return ( - + diff --git a/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenusControlled.stories.tsx b/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenusControlled.stories.tsx index 95b3d89a73a56c..5be37a87a5aea5 100644 --- a/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenusControlled.stories.tsx +++ b/packages/react-components/react-menu/stories/src/Menu/MenuNestedSubmenusControlled.stories.tsx @@ -10,7 +10,7 @@ const EditorLayoutSubMenu = () => { }; return ( - + Editor Layout @@ -33,7 +33,7 @@ const AppearanceSubMenu = () => { }; return ( - + Appearance @@ -57,7 +57,7 @@ const PreferencesSubMenu = () => { }; return ( - + Preferences @@ -77,7 +77,7 @@ const PreferencesSubMenu = () => { export const NestedSubmenusControlled = () => { return ( - + diff --git a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.tsx b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.tsx index a600408cfe91b9..9c6d1bbd76f2f8 100644 --- a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.tsx +++ b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.tsx @@ -1,33 +1,19 @@ import * as React from 'react'; -import { - isHTMLElement, - useMergedRefs, - useControllableState, - type EventHandler, - useEventCallback, -} from '@fluentui/react-utilities'; -import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; +import { isHTMLElement, useMergedRefs, useControllableState, useEventCallback } from '@fluentui/react-utilities'; +import { useAnnounce, useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { CAROUSEL_ITEM } from './constants'; import { useCarouselWalker_unstable } from './useCarouselWalker'; import { createCarouselStore } from './createCarouselStore'; -import type { CarouselValueChangeData } from './Carousel.types'; +import type { UseCarouselOptions } from './Carousel.types'; import { CarouselContextValue } from './CarouselContext'; -export type UseCarouselOptions = { - defaultValue?: string; - value?: string; - - onValueChange?: EventHandler; - onFinish?: EventHandler; -}; - // TODO: Migrate this into an external @fluentui/carousel component // For now, we won't export this publicly, is only for internal TeachingPopover use until stabilized. export function useCarousel_unstable(options: UseCarouselOptions) { 'use no memo'; - const { onValueChange, onFinish } = options; + const { announcement, onValueChange, onFinish } = options; const { targetDocument } = useFluent(); const win = targetDocument?.defaultView; @@ -41,6 +27,8 @@ export function useCarousel_unstable(options: UseCarouselOptions) { }); const rootRef = React.useRef(null); + const { announce } = useAnnounce(); + if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line react-hooks/rules-of-hooks React.useEffect(() => { @@ -115,6 +103,19 @@ export function useCarousel_unstable(options: UseCarouselOptions) { }; }, [carouselWalker, store, win]); + const updateSlide = useEventCallback( + (event: React.MouseEvent, newValue: string) => { + setValue(newValue); + onValueChange?.(event, { event, type: 'click', value: newValue }); + + const announceText = announcement?.(newValue); + if (announceText) { + console.log('announcing', announceText); + announce(announceText, { polite: true }); + } + }, + ); + const selectPageByDirection: CarouselContextValue['selectPageByDirection'] = useEventCallback((event, direction) => { const active = carouselWalker.active(); @@ -126,25 +127,19 @@ export function useCarousel_unstable(options: UseCarouselOptions) { direction === 'prev' ? carouselWalker.prevPage(active.value) : carouselWalker.nextPage(active.value); if (newPage) { - setValue(newPage?.value); - onValueChange?.(event, { event, type: 'click', value: newPage?.value }); + updateSlide(event, newPage?.value); } else { onFinish?.(event, { event, type: 'click', value: active?.value }); } }); - const selectPageByValue: CarouselContextValue['selectPageByValue'] = useEventCallback((event, _value) => { - setValue(_value); - onValueChange?.(event, { event, type: 'click', value: _value }); - }); - return { carouselRef: useMergedRefs(rootRef, carouselRef), carousel: { store, value, selectPageByDirection, - selectPageByValue, + selectPageByValue: updateSlide, }, }; } diff --git a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.types.ts b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.types.ts index 1b99fabcf06a48..f49a7c4a1198d5 100644 --- a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.types.ts +++ b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/Carousel/Carousel.types.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { EventData } from '@fluentui/react-utilities'; +import { EventData, EventHandler } from '@fluentui/react-utilities'; export type CarouselStore = { clear: () => void; @@ -15,6 +15,34 @@ export type CarouselItem = { value: string | null; }; +export type UseCarouselOptions = { + /** + * Localizes the string used to announce carousel page changes to screen reader users + * Defaults to: undefined + */ + announcement?: (newValue: string) => string; + + /** + * The initial page to display in uncontrolled mode. + */ + defaultValue?: string; + + /** + * The value of the currently active page. + */ + value?: string; + + /** + * Callback to notify a page change. + */ + onValueChange?: EventHandler; + + /** + * Callback to notify when the final button step of a carousel has been activated. + */ + onFinish?: EventHandler; +}; + export type CarouselValueChangeData = EventData<'click', React.MouseEvent> & { /** * The value to be set after event has occurred. diff --git a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/TeachingPopoverCarousel.types.ts b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/TeachingPopoverCarousel.types.ts index 9b2d22514790fc..aa4f9d69f8ddbd 100644 --- a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/TeachingPopoverCarousel.types.ts +++ b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/TeachingPopoverCarousel.types.ts @@ -1,8 +1,8 @@ -import type { ComponentProps, ComponentState, EventHandler, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; import { type PopoverContextValue } from '@fluentui/react-popover'; import { type CarouselContextValue } from './Carousel/CarouselContext'; -import type { CarouselValueChangeData } from './Carousel/Carousel.types'; +import type { UseCarouselOptions } from './Carousel/Carousel.types'; export type TeachingPopoverCarouselSlots = { /** @@ -14,27 +14,7 @@ export type TeachingPopoverCarouselSlots = { /** * TeachingPopoverCarousel Props */ -export type TeachingPopoverCarouselProps = ComponentProps & { - /** - * The initial page to display in uncontrolled mode. - */ - defaultValue?: string; - - /** - * The value of the currently active page. - */ - value?: string; - - /** - * Callback to notify a page change. - */ - onValueChange?: EventHandler; - - /** - * Callback to notify when the final button step of a carousel has been activated. - */ - onFinish?: EventHandler; -}; +export type TeachingPopoverCarouselProps = ComponentProps & UseCarouselOptions; /** * TeachingPopoverCarousel State and Context Hooks diff --git a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/useTeachingPopoverCarousel.ts b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/useTeachingPopoverCarousel.ts index b9c020dd8d0389..14bdb4776a9ec7 100644 --- a/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/useTeachingPopoverCarousel.ts +++ b/packages/react-components/react-teaching-popover/library/src/components/TeachingPopoverCarousel/useTeachingPopoverCarousel.ts @@ -2,19 +2,20 @@ import * as React from 'react'; import { getIntrinsicElementProps, slot, useEventCallback, useMergedRefs } from '@fluentui/react-utilities'; import type { TeachingPopoverCarouselProps, TeachingPopoverCarouselState } from './TeachingPopoverCarousel.types'; import { usePopoverContext_unstable } from '@fluentui/react-popover'; -import { useCarousel_unstable, type UseCarouselOptions } from './Carousel/Carousel'; +import { useCarousel_unstable } from './Carousel/Carousel'; export const useTeachingPopoverCarousel_unstable = ( props: TeachingPopoverCarouselProps, ref: React.Ref, ): TeachingPopoverCarouselState => { const toggleOpen = usePopoverContext_unstable(c => c.toggleOpen); - const handleFinish: UseCarouselOptions['onFinish'] = useEventCallback((event, data) => { + const handleFinish: TeachingPopoverCarouselProps['onFinish'] = useEventCallback((event, data) => { props.onFinish?.(event, data); toggleOpen(event as React.MouseEvent); }); const { carousel, carouselRef } = useCarousel_unstable({ + announcement: props.announcement, defaultValue: props.defaultValue, value: props.value, onValueChange: props.onValueChange, diff --git a/packages/react-components/react-teaching-popover/stories/src/TeachingPopover/TeachingPopoverCarousel.stories.tsx b/packages/react-components/react-teaching-popover/stories/src/TeachingPopover/TeachingPopoverCarousel.stories.tsx index 922bbe8253a05d..b5d56dfb2430e8 100644 --- a/packages/react-components/react-teaching-popover/stories/src/TeachingPopover/TeachingPopoverCarousel.stories.tsx +++ b/packages/react-components/react-teaching-popover/stories/src/TeachingPopover/TeachingPopoverCarousel.stories.tsx @@ -17,6 +17,10 @@ import { const swapImage = 'https://fabricweb.azureedge.net/fabric-website/assets/images/wireframe/image-square.png'; +const getAnnouncement = (newValue: string) => { + return `Carousel slide ${newValue}`; +}; + export const Carousel = () => ( @@ -24,7 +28,7 @@ export const Carousel = () => ( Tips - + }> Teaching Bubble Title diff --git a/yarn.lock b/yarn.lock index 611296686b4ee9..75b363783cfd34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21734,7 +21734,7 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -21769,15 +21769,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -21878,7 +21869,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -21913,13 +21904,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -22231,10 +22215,10 @@ table@^6.0.7: string-width "^4.2.3" strip-ansi "^6.0.1" -tabster@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/tabster/-/tabster-8.2.0.tgz#474b32af394495b0835d4eceeab6594f276486d0" - integrity sha512-Gvplk/Yl/12aVFA6FPOqGcq31Qv8hbPfYO0N+6IxrRgRT6eSLsipT6gkZBYjyOwGsp6BD5XlZAuJgupfG/GHoA== +tabster@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/tabster/-/tabster-8.3.0.tgz#8220197bf0b13ccea2f8dbf8f8af43a28ded0144" + integrity sha512-Y1IKWVe0Xk1P8WLSL+Wj+1jkov69OLST6crAG86ye35WM4mLhr/IeW7vIF+8oQKLNPf0FQ7F1lg5cmXUAQeSdA== dependencies: keyborg "2.6.0" tslib "^2.3.1" @@ -24249,7 +24233,7 @@ workspace-tools@^0.27.0: js-yaml "^4.1.0" micromatch "^4.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -24284,15 +24268,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"