support notification pop up from topLeft or bottomRight or bottomLeft (#4700)

* update snapshots

* update snapshots

* support notification pop up from topLeft or bottomRight or bottomLeft

* update snapshots

* 1. add test
2. update doc
3. support `placement` arguments in `open` method

* update doc

* update doc.
This commit is contained in:
Rex 2017-01-27 17:12:25 +08:00 committed by 偏右
parent fd233cf430
commit aadf623dc6
7 changed files with 315 additions and 21 deletions

View File

@ -0,0 +1,121 @@
import notification from '..';
describe('Notification.placement', () => {
function $$(className) {
return document.body.querySelectorAll(className);
}
function getStyle(el, prop, getComputedStyle, style) {
getComputedStyle = window.getComputedStyle;
style = getComputedStyle ? getComputedStyle(el) : el.currentStyle;
// If a css property's value is `auto`, it will return an empty string.
return prop ? style[prop] : style;
}
function open(args) {
notification.open({
message: 'Notification Title',
description: 'This is the content of the notification.',
...args,
});
}
function config(args) {
notification.config({
...args,
});
open();
}
it('change notification placement by `open` method', () => {
const defaultTop = '24px';
const defaultBottom = '24px';
let style;
// topLeft
open({
placement: 'topLeft',
});
style = getStyle($$('.ant-notification-topLeft')[0]);
expect(style.top).toBe(defaultTop);
expect(style.left).toBe('0px');
expect(style.bottom).toBe('');
// topRight
open({
placement: 'topRight',
});
style = getStyle($$('.ant-notification-topRight')[0]);
expect(style.top).toBe(defaultTop);
expect(style.right).toBe('0px');
expect(style.bottom).toBe('');
// bottomRight
open({
placement: 'bottomRight',
});
style = getStyle($$('.ant-notification-bottomRight')[0]);
expect(style.top).toBe('');
expect(style.right).toBe('0px');
expect(style.bottom).toBe(defaultBottom);
// bottomLeft
open({
placement: 'bottomLeft',
});
style = getStyle($$('.ant-notification-bottomLeft')[0]);
expect(style.top).toBe('');
expect(style.left).toBe('0px');
expect(style.bottom).toBe(defaultBottom);
});
it('change notification placement by `config` method', () => {
let style;
// topLeft
config({
placement: 'topLeft',
top: 50,
bottom: 50,
});
style = getStyle($$('.ant-notification-topLeft')[1]);
expect(style.top).toBe('50px');
expect(style.left).toBe('0px');
expect(style.bottom).toBe('');
// topRight
config({
placement: 'topRight',
top: 100,
bottom: 50,
});
style = getStyle($$('.ant-notification-topRight')[1]);
expect(style.top).toBe('100px');
expect(style.right).toBe('0px');
expect(style.bottom).toBe('');
// bottomRight
config({
placement: 'bottomRight',
top: 50,
bottom: 100,
});
style = getStyle($$('.ant-notification-bottomRight')[1]);
expect(style.top).toBe('');
expect(style.right).toBe('0px');
expect(style.bottom).toBe('100px');
// bottomLeft
config({
placement: 'bottomLeft',
top: 100,
bottom: 50,
});
style = getStyle($$('.ant-notification-bottomLeft')[1]);
expect(style.top).toBe('');
expect(style.left).toBe('0px');
expect(style.bottom).toBe('50px');
});
});

View File

@ -28,6 +28,46 @@ exports[`test renders ./components/notification/demo/duration.md correctly 1`] =
</button> </button>
`; `;
exports[`test renders ./components/notification/demo/placement.md correctly 1`] = `
<div>
<div
class="ant-select ant-select-enabled"
style="width:120px;margin-right:10px;">
<div
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
tabindex="0">
<div
class="ant-select-selection__rendered">
<div
class="ant-select-selection-selected-value"
style="display:block;opacity:1;"
title="topRight">
topRight
</div>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable">
<b />
</span>
</div>
</div>
<button
class="ant-btn ant-btn-primary"
type="button">
<span>
Open the notification box
</span>
</button>
</div>
`;
exports[`test renders ./components/notification/demo/with-btn.md correctly 1`] = ` exports[`test renders ./components/notification/demo/with-btn.md correctly 1`] = `
<button <button
class="ant-btn ant-btn-primary" class="ant-btn ant-btn-primary"

View File

@ -0,0 +1,49 @@
---
order: 5
title:
zh-CN: 位置
en-US: Placement
---
## zh-CN
可以设置通知从右上角、右下角、左下角、左上角弹出。
## en-US
A notification box can pop up from `topRight` or `bottomRight` or `bottomLeft` or `topLeft`.
````__react
import { Button, Select, notification } from 'antd';
const { Option } = Select;
const options = ['topLeft', 'topRight','bottomLeft', 'bottomRight'];
const openNotification = () => {
notification.open({
message: 'Notification Title',
description: 'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
});
};
ReactDOM.render(
<div>
<Select
defaultValue="topRight"
style={{ width: 120, marginRight: 10 }}
onChange={val => {
notification.config({
placement: val,
});
}}
>
{options.map(val => <Option key={val} value={val}>{val}</Option>)}
</Select>
<Button
type="primary"
onClick={openNotification}
>
Open the notification box
</Button>
</div>
, mountNode);
````

View File

@ -8,7 +8,7 @@ title: Notification
To display a notification message globally. To display a notification message globally.
## When To Use ## When To Use
To display a notification message at the top right of the view port. Typically it can be To display a notification message at the four corner of the view port. Typically it can be
used in the following cases: used in the following cases:
- A notification with complex content. - A notification with complex content.
@ -36,22 +36,26 @@ The properties of config are as follows:
| icon | Customized icon | React.Node | _ | | icon | Customized icon | React.Node | _ |
| key | The unique identifier of current notification | String | - | | key | The unique identifier of current notification | String | - |
| onClose | Specify a function that will be called after clicking the default close button | Function | - | | onClose | Specify a function that will be called after clicking the default close button | Function | - |
| duration | A notification box is closed after 4.5s by default. When specifying `duration` to null or 0, it will never be closed automatically | Number | 4.5 | | duration | A notification box is closed after 4.5s by default. When specifying `duration` to null or 0, it will never be closed automatically | number | 4.5 |
| placement | To set the position, which can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
`notification` also provide a global `config()` method that can be used for specifying the default options. Once this method is used, all the notification boxes `notification` also provide a global `config()` method that can be used for specifying the default options. Once this method is used, all the notification boxes
will take into account these globally defined options before displaying. will take into account these globally defined options before displaying.
- `notification.config(options)` - `notification.config(options)`
```js ```js
notification.config({ notification.config({
top: 100, placement: 'bottomRight',
bottom: 50,
duration: 3, duration: 3,
}); });
``` ```
| Property | Description | Type | Default | | Property | Description | Type | Default |
|------------|--------------------|----------------------------|--------------| |------------|--------------------|----------------------------|--------------|
| top | Offset to top of message | Number | 24px | | placement | To set the position, which can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
| duration | A duration to close notification automatically by default (unit: second) | Number | 4.5 | | top | Offset to top, when message pop up from `topRight` or `topLeft` (unit: pixels). | number | 24 |
| bottom | Offset to bottom, when message pop up from `bottomRight` or `bottomLeft` (unit: pixels). | number | 24 |
| duration | A duration to close notification automatically by default (unit: second) | number | 4.5 |

View File

@ -2,9 +2,47 @@ import React from 'react';
import Notification from 'rc-notification'; import Notification from 'rc-notification';
import Icon from '../icon'; import Icon from '../icon';
import assign from 'object-assign'; import assign from 'object-assign';
let defaultTop = 24;
let notificationInstance; let notificationInstance;
let defaultDuration = 4.5; let defaultDuration = 4.5;
let defaultTop = 24;
let defaultBottom = 24;
let defaultPlacement = 'topRight';
export type notificationPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
function getPlacementStyle(placement) {
let style;
switch (placement) {
case 'topLeft':
style = {
left: 0,
top: defaultTop,
bottom: 'auto',
};
break;
case 'bottomLeft':
style = {
left: 0,
top: 'auto',
bottom: defaultBottom,
};
break;
case 'bottomRight':
style = {
right: 0,
top: 'auto',
bottom: defaultBottom,
};
break;
default:
style = {
right: 0,
top: defaultTop,
bottom: 'auto',
};
}
return style;
}
export interface ArgsProps { export interface ArgsProps {
message: React.ReactNode | string; message: React.ReactNode | string;
@ -14,11 +52,14 @@ export interface ArgsProps {
onClose?: () => void; onClose?: () => void;
duration?: number; duration?: number;
icon?: React.ReactNode; icon?: React.ReactNode;
placement?: notificationPlacement;
} }
export interface ConfigProps { export interface ConfigProps {
top?: number; top?: number;
bottom?: number;
duration?: number; duration?: number;
placement?: notificationPlacement;
} }
function getNotificationInstance(prefixCls) { function getNotificationInstance(prefixCls) {
@ -27,10 +68,8 @@ function getNotificationInstance(prefixCls) {
} }
notificationInstance = (Notification as any).newInstance({ notificationInstance = (Notification as any).newInstance({
prefixCls: prefixCls, prefixCls: prefixCls,
style: { className: `${prefixCls}-${defaultPlacement}`,
top: defaultTop, style: getPlacementStyle(defaultPlacement),
right: 0,
},
}); });
return notificationInstance; return notificationInstance;
} }
@ -39,6 +78,11 @@ function notice(args) {
const outerPrefixCls = args.prefixCls || 'ant-notification'; const outerPrefixCls = args.prefixCls || 'ant-notification';
const prefixCls = `${outerPrefixCls}-notice`; const prefixCls = `${outerPrefixCls}-notice`;
if (args.placement !== undefined) {
defaultPlacement = args.placement;
notificationInstance = null; // delete notificationInstance for new defaultPlacement
}
let duration; let duration;
if (args.duration === undefined) { if (args.duration === undefined) {
duration = defaultDuration; duration = defaultDuration;
@ -113,12 +157,22 @@ const api: {
} }
}, },
config(options: ConfigProps) { config(options: ConfigProps) {
if (options.top !== undefined) { const { duration, placement, bottom, top } = options;
defaultTop = options.top; if (placement !== undefined) {
notificationInstance = null; // delete notificationInstance for new defaultTop defaultPlacement = placement;
} }
if (options.duration !== undefined) { if (bottom !== undefined) {
defaultDuration = options.duration; defaultBottom = bottom;
}
if (top !== undefined) {
defaultTop = top;
}
// delete notificationInstance
if (placement !== undefined || bottom !== undefined || top !== undefined) {
notificationInstance = null;
}
if (duration !== undefined) {
defaultDuration = duration;
} }
}, },
destroy() { destroy() {

View File

@ -10,7 +10,7 @@ subtitle: 通知提醒框
## 何时使用 ## 何时使用
在系统右上角显示通知提醒信息。经常用于以下情况: 在系统四个角显示通知提醒信息。经常用于以下情况:
- 较为复杂的通知内容。 - 较为复杂的通知内容。
- 带有交互的通知,给出用户下一步的行动点。 - 带有交互的通知,给出用户下一步的行动点。
@ -36,7 +36,8 @@ config 参数如下:
| icon | 自定义图标 | React.Node | - | | icon | 自定义图标 | React.Node | - |
| key | 当前通知唯一标志 | String | - | | key | 当前通知唯一标志 | String | - |
| onClose | 点击默认关闭按钮时触发的回调函数 | Function | - | | onClose | 点击默认关闭按钮时触发的回调函数 | Function | - |
| duration | 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭 | Number | 4.5 | | duration | 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭 | number | 4.5 |
| placement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
还提供了一个全局配置方法,在调用前提前配置,全局一次生效。 还提供了一个全局配置方法,在调用前提前配置,全局一次生效。
@ -44,12 +45,15 @@ config 参数如下:
```js ```js
notification.config({ notification.config({
top: 100, placement: 'bottomRight',
bottom: 50,
duration: 3, duration: 3,
}); });
``` ```
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
|------------|--------------------|----------------------------|--------------| |------------|--------------------|----------------------------|--------------|
| top | 消息距离顶部的位置 | Number | 24px | | placement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
| duration | 默认自动关闭延时,单位秒 | Number | 4.5 | | top | 消息从顶部弹出时,距离顶部的位置,单位像素。 | number | 24 |
| bottom | 消息从底部弹出时,距离底部的位置,单位像素。 | number | 24 |
| duration | 默认自动关闭延时,单位秒 | number | 4.5 |

View File

@ -12,6 +12,17 @@
width: @notification-width; width: @notification-width;
margin-right: 24px; margin-right: 24px;
&-topLeft,
&-bottomLeft {
margin-left: 24px;
margin-right: 0;
.@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active,
.@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active {
animation-name: NotificationLeftFadeIn;
}
}
&-notice { &-notice {
padding: @notification-padding; padding: @notification-padding;
border-radius: @border-radius-base; border-radius: @border-radius-base;
@ -133,6 +144,17 @@
} }
} }
@keyframes NotificationLeftFadeIn {
0% {
opacity: 0;
right: @notification-width;
}
100% {
right: 0;
opacity: 1;
}
}
@keyframes NotificationFadeOut { @keyframes NotificationFadeOut {
0% { 0% {
opacity: 1; opacity: 1;