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

feat(notification): rename StaticNotification to Callout #17376

6 changes: 3 additions & 3 deletions docs/experimental-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_`
Expand Down
89 changes: 45 additions & 44 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,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 {
Expand Down Expand Up @@ -7480,50 +7524,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 {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('Carbon Components React', () => {
"ButtonSkeleton",
"ButtonTooltipAlignments",
"ButtonTooltipPositions",
"Callout",
"Checkbox",
"CheckboxGroup",
"CheckboxSkeleton",
Expand Down
40 changes: 40 additions & 0 deletions packages/react/src/components/Notification/Notification-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
ToastNotification,
InlineNotification,
ActionableNotification,
StaticNotification,
Callout,
} from './Notification';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
Expand Down Expand Up @@ -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(<StaticNotification title="Notification title" />);
}).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(
<Callout title="Notification title" titleId="titleId">
<button type="button" aria-describedby="titleId">
Sample button text
</button>
</Callout>
);
}).not.toThrow();

expect(spy).not.toHaveBeenCalled();
spy.mockRestore();
});
});
19 changes: 9 additions & 10 deletions packages/react/src/components/Notification/Notification.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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

Expand All @@ -37,19 +37,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
Expand Down
53 changes: 42 additions & 11 deletions packages/react/src/components/Notification/Notification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -848,7 +849,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;

Expand Down Expand Up @@ -1125,10 +1126,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
Expand Down Expand Up @@ -1195,12 +1200,11 @@ ActionableNotification.propTypes = {
};

/**
* StaticNotification
* Callout
* ==================
*/

export interface StaticNotificationProps
extends HTMLAttributes<HTMLDivElement> {
export interface CalloutProps extends HTMLAttributes<HTMLDivElement> {
/**
* Pass in the action button label that will be rendered within the ActionableNotification.
*/
Expand Down Expand Up @@ -1228,7 +1232,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;

Expand Down Expand Up @@ -1258,7 +1262,7 @@ export interface StaticNotificationProps
titleId?: string;
}

export function StaticNotification({
export function Callout({
actionButtonLabel,
children,
onActionButtonClick,
Expand All @@ -1270,7 +1274,7 @@ export function StaticNotification({
kind = 'error',
lowContrast,
...rest
}: StaticNotificationProps) {
}: CalloutProps) {
const prefix = usePrefix();
const containerClassName = cx(className, {
[`${prefix}--actionable-notification`]: true,
Expand Down Expand Up @@ -1326,7 +1330,7 @@ export function StaticNotification({
);
}

StaticNotification.propTypes = {
Callout.propTypes = {
/**
* Pass in the action button label that will be rendered within the ActionableNotification.
*/
Expand Down Expand Up @@ -1355,7 +1359,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,

Expand Down Expand Up @@ -1384,3 +1388,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<StaticNotificationProps> = (
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 <Callout {...props} />;
};
2 changes: 2 additions & 0 deletions packages/react/src/components/Notification/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ export {
type ActionableNotificationProps,
StaticNotification,
type StaticNotificationProps,
Callout,
type CalloutProps,
} from './Notification';
Loading
Loading