Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Carousel: Refactor CarouselAnnouncer #32896

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Breaking Preview Change: Set Announcer to drive inner text directly from event, remove state causing unnecessary updates",
"packageName": "@fluentui/react-carousel-preview",
"email": "mifraser@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,7 @@ import { ToggleButtonState } from '@fluentui/react-button';
export const Carousel: ForwardRefComponent<CarouselProps>;

// @public
export const CarouselAnnouncer: ForwardRefComponent<CarouselAnnouncerProps>;

// @public (undocumented)
export const carouselAnnouncerClassNames: SlotClassNames<CarouselAnnouncerSlots>;

// @public
export type CarouselAnnouncerProps = Omit<ComponentProps<Partial<CarouselAnnouncerSlots>>, 'children'> & {
children: AnnouncerIndexRenderFunction;
};

// @public (undocumented)
export type CarouselAnnouncerSlots = {
root: Slot<'div'>;
};

// @public
export type CarouselAnnouncerState = ComponentState<CarouselAnnouncerSlots> & {
renderAnnouncerChild: AnnouncerIndexRenderFunction;
totalSlides: number;
currentIndex: number;
slideGroupList: number[][];
};
export type CarouselAnnouncerFunction = (index: number, totalSlides: number, slideGroupList: number[][]) => string;

// @public
export const CarouselAutoplayButton: ForwardRefComponent<CarouselAutoplayButtonProps>;
Expand Down Expand Up @@ -233,6 +212,7 @@ export type CarouselProps = ComponentProps<CarouselSlots> & {
groupSize?: number | 'auto';
draggable?: boolean;
whitespace?: boolean;
announcement?: CarouselAnnouncerFunction;
};

// @public (undocumented)
Expand Down Expand Up @@ -271,9 +251,6 @@ export type NavButtonRenderFunction = (index: number) => React_2.ReactNode;
// @public
export const renderCarousel_unstable: (state: CarouselState, contextValues: CarouselContextValues) => JSX.Element;

// @public
export const renderCarouselAnnouncer_unstable: (state: CarouselAnnouncerState) => JSX.Element;

// @public
export const renderCarouselAutoplayButton_unstable: (state: CarouselAutoplayButtonState) => JSX.Element;

Expand Down Expand Up @@ -301,12 +278,6 @@ export const renderCarouselSlider_unstable: (state: CarouselSliderState, context
// @public
export function useCarousel_unstable(props: CarouselProps, ref: React_2.Ref<HTMLDivElement>): CarouselState;

// @public
export const useCarouselAnnouncer_unstable: (props: CarouselAnnouncerProps, ref: React_2.Ref<HTMLDivElement>) => CarouselAnnouncerState;

// @public
export const useCarouselAnnouncerStyles_unstable: (state: CarouselAnnouncerState) => CarouselAnnouncerState;

// @public
export const useCarouselAutoplayButton_unstable: (props: CarouselAutoplayButtonProps, ref: React_2.Ref<ARIAButtonElement>) => CarouselAutoplayButtonState;

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export type CarouselSlots = {
root: Slot<'div'>;
};

/**
* Children function replacement, passes through updated context index and carousel information for localization
*/
export type CarouselAnnouncerFunction = (index: number, totalSlides: number, slideGroupList: number[][]) => string;

/**
* Carousel Props
*/
Expand Down Expand Up @@ -51,6 +56,12 @@ export type CarouselProps = ComponentProps<CarouselSlots> & {
* Defaults to: False
*/
whitespace?: boolean;

/**
* Localizes the string used to announce carousel page changes
* Defaults to: undefined
*/
announcement?: CarouselAnnouncerFunction;
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import { getIntrinsicElementProps, slot, useEventCallback, useMergedRefs } from '@fluentui/react-utilities';
import {
getIntrinsicElementProps,
slot,
useEventCallback,
useIsomorphicLayoutEffect,
useMergedRefs,
} from '@fluentui/react-utilities';
import * as React from 'react';

import type { CarouselProps, CarouselState } from './Carousel.types';
import type { CarouselContextValue } from '../CarouselContext.types';
import { useEmblaCarousel } from '../useEmblaCarousel';
import { useAnnounce } from '@fluentui/react-shared-contexts';

/**
* Create the state required to render Carousel.
Expand All @@ -25,6 +32,7 @@ export function useCarousel_unstable(props: CarouselProps, ref: React.Ref<HTMLDi
groupSize = 'auto',
draggable = false,
whitespace = false,
announcement,
} = props;

const { dir } = useFluent();
Expand Down Expand Up @@ -62,6 +70,45 @@ export function useCarousel_unstable(props: CarouselProps, ref: React.Ref<HTMLDi

const mergedRefs = useMergedRefs(ref, containerRef);

// Announce carousel updates
const announcementTextRef = React.useRef<string>('');
const totalNavLength = React.useRef<number>(0);
const navGroupRef = React.useRef<number[][]>([]);

const { announce } = useAnnounce();

const updateAnnouncement = useEventCallback(() => {
if (totalNavLength.current <= 0 || !announcement) {
// Ignore announcements until slides discovered
return;
}

const announcementText = announcement(activeIndex, totalNavLength.current, navGroupRef.current);

if (announcementText !== announcementTextRef.current) {
announcementTextRef.current = announcementText;
announce(announcementText, { polite: true });
}
});

useIsomorphicLayoutEffect(() => {
// Subscribe to any non-index carousel state changes
return subscribeForValues(data => {
if (totalNavLength.current <= 0 && data.navItemsCount > 0 && announcement) {
const announcementText = announcement(data.activeIndex, data.navItemsCount, data.groupIndexList);
// Initialize our string to prevent updateAnnouncement from reading an initial load
announcementTextRef.current = announcementText;
}
totalNavLength.current = data.navItemsCount;
navGroupRef.current = data.groupIndexList;
updateAnnouncement();
});
}, [subscribeForValues, updateAnnouncement, announcement]);

useIsomorphicLayoutEffect(() => {
updateAnnouncement();
}, [activeIndex, updateAnnouncement]);

return {
components: {
root: 'div',
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading