= useRef(null);
+
+ useEffect(() => {
+ if (isOpen) messageRef.current?.focus();
+ });
+
+ return (
+
+ {!isOpen && }
+ setIsOpen(false)}
+ ref={messageRef}
+ {...props}
+ >
+ Some custom message
+
+
+ );
+};
diff --git a/src/components/message/message.component.tsx b/src/components/message/message.component.tsx
index 7a6cae73fb..064032b4ad 100644
--- a/src/components/message/message.component.tsx
+++ b/src/components/message/message.component.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useRef } from "react";
import { MarginProps } from "styled-system";
import MessageStyle from "./message.style";
@@ -39,52 +39,65 @@ export interface MessageProps extends MarginProps {
variant?: MessageVariant;
}
-export const Message = ({
- open = true,
- transparent = false,
- title,
- variant = "info",
- children,
- onDismiss,
- id,
- className,
- closeButtonAriaLabel,
- showCloseIcon = true,
- ...props
-}: MessageProps) => {
- const marginProps = filterStyledSystemMarginProps(props);
- const l = useLocale();
- const renderCloseIcon = () => {
- if (!showCloseIcon || !onDismiss) return null;
+export const Message = React.forwardRef(
+ (
+ {
+ open = true,
+ transparent = false,
+ title,
+ variant = "info",
+ children,
+ onDismiss,
+ id,
+ className,
+ closeButtonAriaLabel,
+ showCloseIcon = true,
+ ...props
+ }: MessageProps,
+ ref
+ ) => {
+ const messageRef = useRef(null);
+ const refToPass = ref || messageRef;
- return (
- {
+ if (!showCloseIcon || !onDismiss) return null;
+
+ return (
+
+
+
+ );
+ };
+
+ return open ? (
+
-
-
- );
- };
+
+
+ {children}
+
+ {renderCloseIcon()}
+
+ ) : null;
+ }
+);
- return open ? (
-
-
-
- {children}
-
- {renderCloseIcon()}
-
- ) : null;
-};
+Message.displayName = "Message";
export default Message;
diff --git a/src/components/message/message.spec.tsx b/src/components/message/message.spec.tsx
index f5017e80c3..852fbef85e 100644
--- a/src/components/message/message.spec.tsx
+++ b/src/components/message/message.spec.tsx
@@ -201,4 +201,36 @@ describe("Message", () => {
describe("styled-system", () => {
testStyledSystemMargin((props) => );
});
+
+ describe("ref", () => {
+ it("passes ref to component", () => {
+ const ref = { current: null };
+
+ const wrapper = mount(
+ {}} ref={ref}>
+ foobar
+
+ );
+
+ const message = wrapper.find(MessageStyle);
+
+ expect(ref.current).toBe(message.getDOMNode());
+ });
+
+ describe("callback ref", () => {
+ it("allows a callback ref is be passed to component", () => {
+ const testCallbackRef = jest.fn();
+
+ const wrapper = mount(
+ {}} ref={testCallbackRef}>
+ foobar
+
+ );
+
+ const message = wrapper.find(MessageStyle);
+
+ expect(testCallbackRef).toHaveBeenCalledWith(message.getDOMNode());
+ });
+ });
+ });
});
diff --git a/src/components/message/message.stories.mdx b/src/components/message/message.stories.mdx
index be382e8492..b082cf9726 100644
--- a/src/components/message/message.stories.mdx
+++ b/src/components/message/message.stories.mdx
@@ -100,6 +100,14 @@ To see a full list of available margin props, please visit the props table at th
+### With focus
+
+Since it is possible to pass a ref to this component, it is also possible to programmatically focus the component.
+
+
+
## Props
### Message
diff --git a/src/components/message/message.stories.tsx b/src/components/message/message.stories.tsx
index d5e4dcfda9..50896ce109 100644
--- a/src/components/message/message.stories.tsx
+++ b/src/components/message/message.stories.tsx
@@ -1,5 +1,5 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
-import React, { useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
import { ComponentStory } from "@storybook/react";
import Message from ".";
@@ -107,33 +107,82 @@ export const WithRichContent: ComponentStory = () => {
};
export const WithMargin: ComponentStory = () => {
- const [isOpen, setIsOpen] = useState(true);
+ const [isOpen, setIsOpen] = useState({
+ MessageOne: true,
+ MessageTwo: true,
+ MessageThree: true,
+ });
+
+ const displayButton = Object.values({ ...isOpen }).every(
+ (value) => value === false
+ );
return (
<>
- {!isOpen && }
+ {displayButton && (
+
+ )}
setIsOpen(false)}
+ open={isOpen.MessageOne}
+ onDismiss={() => setIsOpen({ ...isOpen, MessageOne: false })}
variant="warning"
m={1}
>
- Some custom message
+ This is message one.
setIsOpen(false)}
+ open={isOpen.MessageTwo}
+ onDismiss={() => setIsOpen({ ...isOpen, MessageTwo: false })}
variant="warning"
m={3}
>
- Some custom message
+ This is message two.
setIsOpen(false)}
+ open={isOpen.MessageThree}
+ onDismiss={() => setIsOpen({ ...isOpen, MessageThree: false })}
variant="warning"
m="16px"
>
- Some custom message
+ This is message three.
+
+ >
+ );
+};
+
+export const WithFocus: ComponentStory = () => {
+ const [isMessageOpen, setIsMessageOpen] = useState(true);
+
+ const messageRef: React.Ref = useRef(null);
+
+ useEffect(() => {
+ if (isMessageOpen) {
+ messageRef.current?.focus();
+ }
+ }, [isMessageOpen]);
+
+ return (
+ <>
+ {!isMessageOpen && (
+
+ )}
+ setIsMessageOpen(false)}
+ variant="error"
+ mb={1}
+ ref={messageRef}
+ >
+ This is message one.
>
);
diff --git a/src/components/message/message.style.ts b/src/components/message/message.style.ts
index 4d04349c9d..8f289a5ce6 100644
--- a/src/components/message/message.style.ts
+++ b/src/components/message/message.style.ts
@@ -28,6 +28,10 @@ const MessageStyle = styled.div`
background-color: var(--colorsUtilityYang100);
min-height: 38px;
+ :focus {
+ outline: none;
+ }
+
${({ transparent }) =>
transparent &&
css`