diff --git a/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md b/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md
index 4b38d3f78ee27b..beb5a9bb592f52 100644
--- a/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md
+++ b/packages/react-components/react-message-bar-preview/etc/react-message-bar-preview.api.md
@@ -6,6 +6,7 @@
///
+import type { ButtonContextValue } from '@fluentui/react-button';
import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
@@ -59,7 +60,9 @@ export const MessageBarContextProvider: React_2.Provider;
+ bodyRef: React_2.MutableRefObject;
};
// @public
@@ -87,8 +90,9 @@ export type MessageBarGroupState = ComponentState & Pick & Pick & {
+export type MessageBarProps = ComponentProps & Pick, 'layout'> & {
intent?: 'info' | 'success' | 'warning' | 'error';
+ politeness?: 'assertive' | 'polite';
};
// @public (undocumented)
@@ -100,6 +104,8 @@ export type MessageBarSlots = {
// @public
export type MessageBarState = ComponentState & Required> & {
transitionClassName: string;
+ actionsRef: React_2.MutableRefObject;
+ bodyRef: React_2.MutableRefObject;
};
// @public
@@ -123,7 +129,7 @@ export type MessageBarTitleState = ComponentState;
export const renderMessageBar_unstable: (state: MessageBarState, contexts: MessageBarContextValues) => JSX.Element;
// @public
-export const renderMessageBarActions_unstable: (state: MessageBarActionsState) => JSX.Element;
+export const renderMessageBarActions_unstable: (state: MessageBarActionsState, contexts: MessageBarActionsContextValues) => JSX.Element;
// @public
export const renderMessageBarBody_unstable: (state: MessageBarBodyState) => JSX.Element;
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx
index 6a6badfa9ec047..54012eec2b44dc 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.test.tsx
@@ -2,6 +2,10 @@ import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../testing/isConformant';
import { MessageBar } from './MessageBar';
+import { AnnounceProvider_unstable } from '@fluentui/react-shared-contexts';
+import { MessageBarBody } from '../MessageBarBody/MessageBarBody';
+import { MessageBarTitle } from '../MessageBarTitle/MessageBarTitle';
+import { MessageBarActions } from '../MessageBarActions/MessageBarActions';
describe('MessageBar', () => {
beforeAll(() => {
@@ -33,10 +37,52 @@ describe('MessageBar', () => {
},
});
- // TODO add more tests here, and create visual regression tests in /apps/vr-tests
-
it('renders a default state', () => {
const result = render(Default MessageBar);
expect(result.container).toMatchSnapshot();
});
+
+ it.each([
+ ['assertive', 'error'] as const,
+ ['assertive', 'warning'] as const,
+ ['assertive', 'success'] as const,
+ ['polite', 'info'] as const,
+ ])('should announce %s with %s intent', (politeness, intent) => {
+ const announce = jest.fn();
+ render(
+
+
+
+ TitleBody
+
+
+ ,
+ );
+
+ expect(announce).toHaveBeenCalledTimes(1);
+ expect(announce).toHaveBeenCalledWith('TitleBody', {
+ alert: politeness === 'assertive',
+ polite: politeness === 'polite',
+ });
+ });
+
+ it('should announce actions', () => {
+ const announce = jest.fn();
+ render(
+
+
+
+ TitleBody
+
+ Container action}>
+
+
+
+
+ ,
+ );
+
+ expect(announce).toHaveBeenCalledTimes(1);
+ expect(announce).toHaveBeenCalledWith('TitleBody,Action 1Action 2', expect.anything());
+ });
});
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts
index 9de1424f0a04f4..383262e1199e48 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/MessageBar.types.ts
@@ -1,5 +1,6 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
import type { MessageBarContextValue } from '../../contexts/messageBarContext';
+import * as React from 'react';
export type MessageBarSlots = {
root: Slot<'div'>;
@@ -14,8 +15,9 @@ export type MessageBarContextValues = {
* MessageBar Props
*/
export type MessageBarProps = ComponentProps &
- Pick & {
+ Pick, 'layout'> & {
intent?: 'info' | 'success' | 'warning' | 'error';
+ politeness?: 'assertive' | 'polite';
};
/**
@@ -24,4 +26,6 @@ export type MessageBarProps = ComponentProps &
export type MessageBarState = ComponentState &
Required> & {
transitionClassName: string;
+ actionsRef: React.MutableRefObject;
+ bodyRef: React.MutableRefObject;
};
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts
index c296460ad70060..c5c384369237b3 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBar/useMessageBar.ts
@@ -1,5 +1,6 @@
import * as React from 'react';
import { getNativeElementProps, slot, useMergedRefs } from '@fluentui/react-utilities';
+import { useAnnounce_unstable } from '@fluentui/react-shared-contexts';
import type { MessageBarProps, MessageBarState } from './MessageBar.types';
import { getIntentIcon } from './getIntentIcon';
import { useMessageBarReflow } from './useMessageBarReflow';
@@ -15,11 +16,23 @@ import { useMessageBarTransitionContext } from '../../contexts/messageBarTransit
* @param ref - reference to root HTMLElement of MessageBar
*/
export const useMessageBar_unstable = (props: MessageBarProps, ref: React.Ref): MessageBarState => {
- const { layout = 'auto', intent = 'info' } = props;
+ const { layout = 'auto', intent = 'info', politeness } = props;
+ const computedPolitness = politeness ?? intent === 'info' ? 'polite' : 'assertive';
const autoReflow = layout === 'auto';
const { ref: reflowRef, reflowing } = useMessageBarReflow(autoReflow);
const computedLayout = autoReflow ? (reflowing ? 'multiline' : 'singleline') : layout;
const { className: transitionClassName, nodeRef } = useMessageBarTransitionContext();
+ const actionsRef = React.useRef(null);
+ const bodyRef = React.useRef(null);
+ const { announce } = useAnnounce_unstable();
+
+ React.useEffect(() => {
+ const bodyMessage = bodyRef.current?.textContent;
+ const actionsMessage = actionsRef.current?.textContent;
+
+ const message = [bodyMessage, actionsMessage].filter(Boolean).join(',');
+ announce(message, { polite: computedPolitness === 'polite', alert: computedPolitness === 'assertive' });
+ }, [bodyRef, actionsRef, announce, computedPolitness]);
return {
components: {
@@ -42,5 +55,7 @@ export const useMessageBar_unstable = (props: MessageBarProps, ref: React.Ref ({
layout,
+ actionsRef,
+ bodyRef,
}),
- [layout],
+ [layout, actionsRef, bodyRef],
);
return {
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx
index 1e20b5f1f8e83c..75694b0b150cd4 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.tsx
@@ -4,6 +4,7 @@ import { useMessageBarActions_unstable } from './useMessageBarActions';
import { renderMessageBarActions_unstable } from './renderMessageBarActions';
import { useMessageBarActionsStyles_unstable } from './useMessageBarActionsStyles.styles';
import type { MessageBarActionsProps } from './MessageBarActions.types';
+import { useMessageBarActionsContextValue_unstable } from './useMessageBarActionsContextValues';
/**
* MessageBarActions component
@@ -12,7 +13,7 @@ export const MessageBarActions: ForwardRefComponent = Re
const state = useMessageBarActions_unstable(props, ref);
useMessageBarActionsStyles_unstable(state);
- return renderMessageBarActions_unstable(state);
+ return renderMessageBarActions_unstable(state, useMessageBarActionsContextValue_unstable());
});
MessageBarActions.displayName = 'MessageBarActions';
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts
index c8c04ee199908c..112c3a27e5f191 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/MessageBarActions.types.ts
@@ -1,11 +1,16 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
-import { MessageBarContextValue } from '../../contexts/messageBarContext';
+import type { ButtonContextValue } from '@fluentui/react-button';
+import type { MessageBarContextValue } from '../../contexts/messageBarContext';
export type MessageBarActionsSlots = {
root: Slot<'div'>;
containerAction?: Slot<'div'>;
};
+export type MessageBarActionsContextValues = {
+ button: ButtonContextValue;
+};
+
/**
* MessageBarActions Props
*/
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx
index 15ddc4bf084e33..ec0fad640d8b69 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/renderMessageBarActions.tsx
@@ -2,17 +2,24 @@
/** @jsxImportSource @fluentui/react-jsx-runtime */
import { assertSlots } from '@fluentui/react-utilities';
-import type { MessageBarActionsState, MessageBarActionsSlots } from './MessageBarActions.types';
+import type {
+ MessageBarActionsState,
+ MessageBarActionsSlots,
+ MessageBarActionsContextValues,
+} from './MessageBarActions.types';
import { ButtonContextProvider } from '@fluentui/react-button';
/**
* Render the final JSX of MessageBarActions
*/
-export const renderMessageBarActions_unstable = (state: MessageBarActionsState) => {
+export const renderMessageBarActions_unstable = (
+ state: MessageBarActionsState,
+ contexts: MessageBarActionsContextValues,
+) => {
assertSlots(state);
if (state.layout === 'multiline') {
return (
-
+
{state.containerAction && }
@@ -20,7 +27,7 @@ export const renderMessageBarActions_unstable = (state: MessageBarActionsState)
}
return (
-
+
{state.containerAction && }
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts
index 5ccf97a36a7188..eedcb87dd7ab74 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActions.ts
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { getNativeElementProps, slot } from '@fluentui/react-utilities';
+import { getNativeElementProps, slot, useMergedRefs } from '@fluentui/react-utilities';
import type { MessageBarActionsProps, MessageBarActionsState } from './MessageBarActions.types';
import { useMessageBarContext } from '../../contexts/messageBarContext';
@@ -16,7 +16,7 @@ export const useMessageBarActions_unstable = (
props: MessageBarActionsProps,
ref: React.Ref,
): MessageBarActionsState => {
- const { layout = 'singleline' } = useMessageBarContext();
+ const { layout = 'singleline', actionsRef } = useMessageBarContext();
return {
components: {
root: 'div',
@@ -25,7 +25,7 @@ export const useMessageBarActions_unstable = (
containerAction: slot.optional(props.containerAction, { renderByDefault: false, elementType: 'div' }),
root: slot.always(
getNativeElementProps('div', {
- ref,
+ ref: useMergedRefs(ref, actionsRef),
...props,
}),
{ elementType: 'div' },
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActionsContextValues.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActionsContextValues.ts
new file mode 100644
index 00000000000000..94f37f0a57d24a
--- /dev/null
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarActions/useMessageBarActionsContextValues.ts
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import { MessageBarActionsContextValues } from './MessageBarActions.types';
+
+export function useMessageBarActionsContextValue_unstable(): MessageBarActionsContextValues {
+ const buttonContext = React.useMemo(
+ () => ({
+ size: 'small' as const,
+ }),
+ [],
+ );
+
+ return {
+ button: buttonContext,
+ };
+}
diff --git a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts
index ed99cf2f18a9bb..cb91a13aebe212 100644
--- a/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts
+++ b/packages/react-components/react-message-bar-preview/src/components/MessageBarBody/useMessageBarBody.ts
@@ -1,6 +1,7 @@
import * as React from 'react';
-import { getNativeElementProps, slot } from '@fluentui/react-utilities';
+import { getNativeElementProps, slot, useMergedRefs } from '@fluentui/react-utilities';
import type { MessageBarBodyProps, MessageBarBodyState } from './MessageBarBody.types';
+import { useMessageBarContext } from '../../contexts/messageBarContext';
/**
* Create the state required to render MessageBarBody.
@@ -15,13 +16,14 @@ export const useMessageBarBody_unstable = (
props: MessageBarBodyProps,
ref: React.Ref,
): MessageBarBodyState => {
+ const { bodyRef } = useMessageBarContext();
return {
components: {
root: 'div',
},
root: slot.always(
getNativeElementProps('div', {
- ref,
+ ref: useMergedRefs(ref, bodyRef),
...props,
}),
{ elementType: 'div' },
diff --git a/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts b/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts
index 59c72989b360d1..f2bb40b5a84905 100644
--- a/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts
+++ b/packages/react-components/react-message-bar-preview/src/contexts/messageBarContext.ts
@@ -1,12 +1,16 @@
import * as React from 'react';
export type MessageBarContextValue = {
- layout?: 'multiline' | 'singleline' | 'auto';
+ layout: 'multiline' | 'singleline' | 'auto';
+ actionsRef: React.MutableRefObject;
+ bodyRef: React.MutableRefObject;
};
const messageBarContext = React.createContext(undefined);
export const messageBarContextDefaultValue: MessageBarContextValue = {
layout: 'singleline',
+ actionsRef: React.createRef(),
+ bodyRef: React.createRef(),
};
export const MessageBarContextProvider = messageBarContext.Provider;