diff --git a/components/app/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/app/__tests__/__snapshots__/demo-extend.test.ts.snap
index dfcd5a9b10..3aa9b9e8b8 100644
--- a/components/app/__tests__/__snapshots__/demo-extend.test.ts.snap
+++ b/components/app/__tests__/__snapshots__/demo-extend.test.ts.snap
@@ -48,3 +48,39 @@ exports[`renders components/app/demo/basic.tsx extend context correctly 1`] = `
`;
+
+exports[`renders components/app/demo/config.tsx extend context correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/components/app/__tests__/__snapshots__/demo.test.ts.snap b/components/app/__tests__/__snapshots__/demo.test.ts.snap
index dea888a49b..833887ea86 100644
--- a/components/app/__tests__/__snapshots__/demo.test.ts.snap
+++ b/components/app/__tests__/__snapshots__/demo.test.ts.snap
@@ -48,3 +48,39 @@ exports[`renders components/app/demo/basic.tsx correctly 1`] = `
`;
+
+exports[`renders components/app/demo/config.tsx correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/components/app/__tests__/index.test.tsx b/components/app/__tests__/index.test.tsx
index c738a60a20..7cfccbdc93 100644
--- a/components/app/__tests__/index.test.tsx
+++ b/components/app/__tests__/index.test.tsx
@@ -1,4 +1,5 @@
import React, { useEffect } from 'react';
+import type { NotificationConfig } from 'antd/es/notification/interface';
import App from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@@ -123,6 +124,46 @@ describe('App', () => {
expect(config?.notification).toStrictEqual({ maxCount: 30, bottom: 41 });
});
+ it('should respect notification placement config from props in priority', async () => {
+ let consumedConfig: AppConfig | undefined;
+
+ const Consumer = () => {
+ const { notification } = App.useApp();
+ consumedConfig = React.useContext(AppConfigContext);
+
+ useEffect(() => {
+ notification.success({ message: 'Notification 1' });
+ notification.success({ message: 'Notification 2' });
+ notification.success({ message: 'Notification 3' });
+ }, [notification]);
+
+ return ;
+ };
+
+ const config: NotificationConfig = {
+ placement: 'bottomLeft',
+ top: 100,
+ bottom: 50,
+ };
+
+ const Wrapper = () => (
+
+
+
+ );
+
+ render();
+ await waitFakeTimer();
+
+ expect(consumedConfig?.notification).toStrictEqual(config);
+ expect(document.querySelector('.ant-notification-topRight')).not.toBeInTheDocument();
+ expect(document.querySelector('.ant-notification-bottomLeft')).toHaveStyle({
+ top: '',
+ left: '0px',
+ bottom: '50px',
+ });
+ });
+
it('support className', () => {
const { container } = render(
diff --git a/components/app/demo/basic.md b/components/app/demo/basic.md
index f8f1d48607..9976b036fd 100644
--- a/components/app/demo/basic.md
+++ b/components/app/demo/basic.md
@@ -1,7 +1,7 @@
## zh-CN
-获取 `message`、`notification`、`modal` 静态方法。
+获取 `message`、`notification`、`modal` 实例。
## en-US
-Static method for `message`, `notification`, `modal`.
+Get instance for `message`, `notification`, `modal`.
diff --git a/components/app/demo/config.md b/components/app/demo/config.md
new file mode 100644
index 0000000000..200dc53e95
--- /dev/null
+++ b/components/app/demo/config.md
@@ -0,0 +1,7 @@
+## zh-CN
+
+对 `message`、`notification` 进行配置。
+
+## en-US
+
+Config for `message`, `notification`.
diff --git a/components/app/demo/config.tsx b/components/app/demo/config.tsx
new file mode 100644
index 0000000000..fdc25a6e70
--- /dev/null
+++ b/components/app/demo/config.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { App, Button, Space } from 'antd';
+
+// Sub page
+const MyPage = () => {
+ const { message, notification } = App.useApp();
+
+ const showMessage = () => {
+ message.success('Success!');
+ };
+
+ const showNotification = () => {
+ notification.info({
+ message: `Notification`,
+ description: 'Hello, Ant Design!!',
+ });
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+// Entry component
+export default () => (
+
+
+
+);
diff --git a/components/app/index.en-US.md b/components/app/index.en-US.md
index eda1014f72..ddc515ed09 100644
--- a/components/app/index.en-US.md
+++ b/components/app/index.en-US.md
@@ -18,7 +18,8 @@ Application wrapper for some global usages.
## Examples
-basic
+Basic
+Hooks config
## How to use
diff --git a/components/app/index.zh-CN.md b/components/app/index.zh-CN.md
index 97793b5d1e..1d73396dff 100644
--- a/components/app/index.zh-CN.md
+++ b/components/app/index.zh-CN.md
@@ -20,6 +20,7 @@ demo:
基本用法
+Hooks 配置
## 如何使用
diff --git a/components/notification/useNotification.tsx b/components/notification/useNotification.tsx
index 2d4e8c3319..505109b04e 100644
--- a/components/notification/useNotification.tsx
+++ b/components/notification/useNotification.tsx
@@ -1,22 +1,23 @@
+import * as React from 'react';
import classNames from 'classnames';
import { useNotification as useRcNotification } from 'rc-notification';
import type { NotificationAPI } from 'rc-notification/lib';
-import * as React from 'react';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { ComponentStyleConfig } from '../config-provider/context';
-import { PureContent, getCloseIcon } from './PurePanel';
import type {
ArgsProps,
NotificationConfig,
NotificationInstance,
NotificationPlacement,
} from './interface';
+import { getCloseIcon, PureContent } from './PurePanel';
import useStyle from './style';
import { getMotion, getPlacementStyle } from './util';
const DEFAULT_OFFSET = 24;
const DEFAULT_DURATION = 4.5;
+const DEFAULT_PLACEMENT: NotificationPlacement = 'topRight';
// ==============================================================================
// == Holder ==
@@ -125,7 +126,8 @@ export function useInternalNotification(
const realCloseIcon = getCloseIcon(noticePrefixCls, closeIcon);
return originOpen({
- placement: 'topRight',
+ // use placement from props instead of hard-coding "topRight"
+ placement: notificationConfig?.placement ?? DEFAULT_PLACEMENT,
...restConfig,
content: (