diff --git a/docs/experimental-code.md b/docs/experimental-code.md
index 3c938083869d..30b92124a901 100644
--- a/docs/experimental-code.md
+++ b/docs/experimental-code.md
@@ -38,20 +38,20 @@ const unstable_meta = {
// An unstable component will retain its name, specifically for things like
// the rules of hooks plugin which depend on the correct casing of the name
-function StaticNotification(props) {
+function ComponentName(props) {
// ...
}
// However, when we export the component we will export it with the `unstable_`
// prefix. (Similar to React.unstable_Suspense, React.unstable_Profiler)
-export { default as unstable_StaticNotification } from './components/StaticNotification';
+export { default as unstable_ComponentName } from './components/ComponentName';
```
For teams using these features, they will need to import the functionality by
using the `unstable_` prefix. For example:
```jsx
-import { unstable_StaticNotification as StaticNotification } from '@carbon/react';
+import { unstable_ComponentName as ComponentName } from '@carbon/react';
```
### Documenting components and exports prefixed with `unstable_`
diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
index eeb9f6003532..566ab6e50944 100644
--- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
+++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
@@ -569,6 +569,50 @@ Map {
"2": "bottom",
"3": "left",
},
+ "Callout" => Object {
+ "propTypes": Object {
+ "actionButtonLabel": Object {
+ "type": "string",
+ },
+ "children": Object {
+ "type": "node",
+ },
+ "className": Object {
+ "type": "string",
+ },
+ "kind": Object {
+ "args": Array [
+ Array [
+ "error",
+ "info",
+ "info-square",
+ "success",
+ "warning",
+ "warning-alt",
+ ],
+ ],
+ "type": "oneOf",
+ },
+ "lowContrast": Object {
+ "type": "bool",
+ },
+ "onActionButtonClick": Object {
+ "type": "func",
+ },
+ "statusIconDescription": Object {
+ "type": "string",
+ },
+ "subtitle": Object {
+ "type": "node",
+ },
+ "title": Object {
+ "type": "string",
+ },
+ "titleId": Object {
+ "type": "string",
+ },
+ },
+ },
"Checkbox" => Object {
"$$typeof": Symbol(react.forward_ref),
"propTypes": Object {
@@ -7455,50 +7499,7 @@ Map {
},
"render": [Function],
},
- "StaticNotification" => Object {
- "propTypes": Object {
- "actionButtonLabel": Object {
- "type": "string",
- },
- "children": Object {
- "type": "node",
- },
- "className": Object {
- "type": "string",
- },
- "kind": Object {
- "args": Array [
- Array [
- "error",
- "info",
- "info-square",
- "success",
- "warning",
- "warning-alt",
- ],
- ],
- "type": "oneOf",
- },
- "lowContrast": Object {
- "type": "bool",
- },
- "onActionButtonClick": Object {
- "type": "func",
- },
- "statusIconDescription": Object {
- "type": "string",
- },
- "subtitle": Object {
- "type": "node",
- },
- "title": Object {
- "type": "string",
- },
- "titleId": Object {
- "type": "string",
- },
- },
- },
+ "StaticNotification" => Object {},
"StructuredListBody" => Object {
"propTypes": Object {
"children": Object {
diff --git a/packages/react/src/__tests__/index-test.js b/packages/react/src/__tests__/index-test.js
index f83f5635a36a..3fc8a6767449 100644
--- a/packages/react/src/__tests__/index-test.js
+++ b/packages/react/src/__tests__/index-test.js
@@ -36,6 +36,7 @@ describe('Carbon Components React', () => {
"ButtonSkeleton",
"ButtonTooltipAlignments",
"ButtonTooltipPositions",
+ "Callout",
"Checkbox",
"CheckboxGroup",
"CheckboxSkeleton",
diff --git a/packages/react/src/components/Notification/Notification-test.js b/packages/react/src/components/Notification/Notification-test.js
index 33f482a43a64..f29bdcfbb0e0 100644
--- a/packages/react/src/components/Notification/Notification-test.js
+++ b/packages/react/src/components/Notification/Notification-test.js
@@ -12,7 +12,9 @@ import {
ToastNotification,
InlineNotification,
ActionableNotification,
-} from './Notification';
+ StaticNotification,
+ Callout,
+} from '../Notification';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@@ -337,3 +339,41 @@ describe('ActionableNotification', () => {
});
});
});
+
+// TODO: Remove StaticNotification tests when Callout moves to stable OR in
+// v12, whichever is first. Ensure test parity on Callout.
+describe('StaticNotification', () => {
+ it('logs a deprecation notice when used', () => {
+ const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
+
+ expect(() => {
+ render();
+ }).not.toThrow();
+
+ expect(spy).toHaveBeenCalledWith(
+ 'Warning: `StaticNotification` has been renamed to `Callout`.' +
+ 'Run the following codemod to automatically update usages in your' +
+ 'project: `npx @carbon/upgrade migrate refactor-to-callout --write`'
+ );
+ spy.mockRestore();
+ });
+});
+
+describe('Callout', () => {
+ it('enforces aria-describedby on interactive children elements', () => {
+ const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
+
+ expect(() => {
+ render(
+
+
+
+ );
+ }).not.toThrow();
+
+ expect(spy).not.toHaveBeenCalled();
+ spy.mockRestore();
+ });
+});
diff --git a/packages/react/src/components/Notification/Notification.mdx b/packages/react/src/components/Notification/Notification.mdx
index c44833e75c0f..1e08c67932d6 100644
--- a/packages/react/src/components/Notification/Notification.mdx
+++ b/packages/react/src/components/Notification/Notification.mdx
@@ -22,7 +22,7 @@ import { Canvas, ArgTypes, Meta } from '@storybook/blocks';
There are 4 different types of notification components:
`ActionableNotification`, `InlineNotification`, `ToastNotification`, and
-`unstable__StaticNotification`.
+`unstable__Callout`.
### ActionableNotification
@@ -39,19 +39,18 @@ focus until the action is acted upon or the notification is dismissed.
elements or rich text. These are announced by screenreaders when rendered. They
don't grab focus. Use them to provide the user with an alert, status, or log.
-### unstable\_\_StaticNotification
+### unstable\_\_Callout (previously StaticNotification)
-`unstable__StaticNotification` is non-modal and should only be used inline with
-content on the initial render of the page or modal because it will not be
-announced to screenreader users like the other notification components.
+`unstable__Callout` is non-modal and should only be used inline with content on
+the initial render of the page or modal because it will not be announced to
+screenreader users like the other notification components.
As such, this should not be used for real-time notifications or notifications
responding to user input (unless the page is completely refreshing and bumping
-the users focus back to the first element in the dom/tab order). If a
-StaticNotification is rendered after the initial render, screenreader users'
-focus may have already passed this portion of the DOM and they will not know
-that the notification has been rendered until they circle back to the beginning
-of the page.
+the users focus back to the first element in the dom/tab order). If a Callout is
+rendered after the initial render, screenreader users' focus may have already
+passed this portion of the DOM and they will not know that the notification has
+been rendered until they circle back to the beginning of the page.
This is the most passive notification component and is essentially just a styled
div. If you place actions or interactive elements within this component, place
diff --git a/packages/react/src/components/Notification/Notification.tsx b/packages/react/src/components/Notification/Notification.tsx
index f1b1d25f01e7..0c82454f7608 100644
--- a/packages/react/src/components/Notification/Notification.tsx
+++ b/packages/react/src/components/Notification/Notification.tsx
@@ -42,6 +42,7 @@ import { useId } from '../../internal/useId';
import { noopFn } from '../../internal/noopFn';
import wrapFocus, { wrapFocusWithoutSentinels } from '../../internal/wrapFocus';
import { useFeatureFlag } from '../FeatureFlags';
+import { warning } from '../../internal/warning';
/**
* Conditionally call a callback when the escape key is pressed
@@ -851,7 +852,7 @@ export interface ActionableNotificationProps
/**
* @deprecated This prop will be removed in the next major version, v12.
- * Specify if focus should be moved to the component on render. To meet the spec for alertdialog, this must always be true. If you're setting this to false, explore using StaticNotification instead. https://github.com/carbon-design-system/carbon/pull/15532
+ * Specify if focus should be moved to the component on render. To meet the spec for alertdialog, this must always be true. If you're setting this to false, explore using Callout instead. https://github.com/carbon-design-system/carbon/pull/15532
*/
hasFocus?: boolean;
@@ -1128,10 +1129,14 @@ ActionableNotification.propTypes = {
closeOnEscape: PropTypes.bool,
/**
- * Deprecated, please use StaticNotification once it's available. Issue #15532
* Specify if focus should be moved to the component when the notification contains actions
*/
- hasFocus: deprecate(PropTypes.bool),
+ hasFocus: deprecate(
+ PropTypes.bool,
+ 'hasFocus is deprecated. To conform to accessibility requirements hasFocus ' +
+ 'should always be `true` for ActionableNotification. If you were ' +
+ 'setting this prop to `false`, consider using the Callout component instead.'
+ ),
/**
* Specify the close button should be disabled, or not
@@ -1198,12 +1203,11 @@ ActionableNotification.propTypes = {
};
/**
- * StaticNotification
+ * Callout
* ==================
*/
-export interface StaticNotificationProps
- extends HTMLAttributes {
+export interface CalloutProps extends HTMLAttributes {
/**
* Pass in the action button label that will be rendered within the ActionableNotification.
*/
@@ -1231,7 +1235,7 @@ export interface StaticNotificationProps
| 'warning-alt';
/**
- * Specify whether you are using the low contrast variant of the StaticNotification.
+ * Specify whether you are using the low contrast variant of the Callout.
*/
lowContrast?: boolean;
@@ -1261,7 +1265,7 @@ export interface StaticNotificationProps
titleId?: string;
}
-export function StaticNotification({
+export function Callout({
actionButtonLabel,
children,
onActionButtonClick,
@@ -1273,7 +1277,7 @@ export function StaticNotification({
kind = 'error',
lowContrast,
...rest
-}: StaticNotificationProps) {
+}: CalloutProps) {
const prefix = usePrefix();
const containerClassName = cx(className, {
[`${prefix}--actionable-notification`]: true,
@@ -1329,7 +1333,7 @@ export function StaticNotification({
);
}
-StaticNotification.propTypes = {
+Callout.propTypes = {
/**
* Pass in the action button label that will be rendered within the ActionableNotification.
*/
@@ -1358,7 +1362,7 @@ StaticNotification.propTypes = {
]),
/**
- * Specify whether you are using the low contrast variant of the StaticNotification.
+ * Specify whether you are using the low contrast variant of the Callout.
*/
lowContrast: PropTypes.bool,
@@ -1387,3 +1391,30 @@ StaticNotification.propTypes = {
*/
titleId: PropTypes.string,
};
+
+// In renaming StaticNotification to Callout, the legacy StaticNotification
+// export and it's types should remain usable until Callout is moved to stable.
+// The StaticNotification component below forwards props to Callout and inherits
+// CalloutProps to ensure consumer usage is not impacted, while providing them
+// a deprecation warning.
+// TODO: remove this when Callout moves to stable OR in v12, whichever is first
+/**
+ * @deprecated Use `CalloutProps` instead.
+ */
+export interface StaticNotificationProps extends CalloutProps {}
+let didWarnAboutDeprecation = false;
+export const StaticNotification: React.FC = (
+ props
+) => {
+ if (__DEV__) {
+ warning(
+ didWarnAboutDeprecation,
+ '`StaticNotification` has been renamed to `Callout`.' +
+ 'Run the following codemod to automatically update usages in your' +
+ 'project: `npx @carbon/upgrade migrate refactor-to-callout --write`'
+ );
+ didWarnAboutDeprecation = true;
+ }
+
+ return ;
+};
diff --git a/packages/react/src/components/Notification/index.tsx b/packages/react/src/components/Notification/index.tsx
index a28475e11150..37432616086e 100644
--- a/packages/react/src/components/Notification/index.tsx
+++ b/packages/react/src/components/Notification/index.tsx
@@ -18,4 +18,6 @@ export {
type ActionableNotificationProps,
StaticNotification,
type StaticNotificationProps,
+ Callout,
+ type CalloutProps,
} from './Notification';
diff --git a/packages/react/src/components/Notification/stories/Callout.stories.js b/packages/react/src/components/Notification/stories/Callout.stories.js
new file mode 100644
index 000000000000..530c3f3a3aac
--- /dev/null
+++ b/packages/react/src/components/Notification/stories/Callout.stories.js
@@ -0,0 +1,98 @@
+/**
+ * Copyright IBM Corp. 2016, 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from 'react';
+import { Callout } from '../../Notification';
+import { Link } from '../../Link';
+import mdx from '../Notification.mdx';
+
+export default {
+ title: 'Experimental/unstable__Callout',
+ component: Callout,
+ parameters: {
+ docs: {
+ page: mdx,
+ },
+ },
+ args: {
+ kind: 'error',
+ lowContrast: false,
+ statusIconDescription: 'notification',
+ },
+};
+
+export const Default = () => (
+
+);
+
+export const WithInteractiveElements = () => (
+
+
+ Additional text can describe the notification, or a link to{' '}
+
+ learn more
+
+
+
+ Create
+ {' '}
+ or{' '}
+
+ register
+ {' '}
+ a cluster before creating a Configuration. Some additional info could go
+ here to show that this notification subtitle goes below the title.
+
-
- Create
- {' '}
- or{' '}
-
- register
- {' '}
- a cluster before creating a Configuration. Some additional info could go
- here to show that this notification subtitle goes below the title.
+ <>
+
+
+
+
+ Run the following codemod to automatically update usages in your
+ project:
+