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

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

Merged
merged 7 commits into from
Jan 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions components/notification/__tests__/Notification.placement.test.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
40 changes: 40 additions & 0 deletions components/notification/__tests__/__snapshots__/demo.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,46 @@ exports[`test renders ./components/notification/demo/duration.md correctly 1`] =
</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`] = `
<button
class="ant-btn ant-btn-primary"
Expand Down
49 changes: 49 additions & 0 deletions components/notification/demo/placement.md
Original file line number Diff line number Diff line change
@@ -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.',
Copy link
Member

@afc163 afc163 Jan 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open 这里也支持一下 placement 如何?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的,我也加下。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@afc163
在实现上支持 placement,然后 demo 里的 open 方法内就不用体现 placement 了吧,因为 demo 里是通过 config 方法修改 placement

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那文档里补充一下。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯嗯,好的。

});
};

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);
````
16 changes: 10 additions & 6 deletions components/notification/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ title: Notification
To display a notification message globally.

## 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:

- A notification with complex content.
Expand Down Expand Up @@ -36,22 +36,26 @@ The properties of config are as follows:
| icon | Customized icon | React.Node | _ |
| key | The unique identifier of current notification | String | - |
| 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
will take into account these globally defined options before displaying.

- `notification.config(options)`

```js
notification.config({
top: 100,
placement: 'bottomRight',
bottom: 50,
duration: 3,
});
```

| Property | Description | Type | Default |
|------------|--------------------|----------------------------|--------------|
| top | Offset to top of message | Number | 24px |
| duration | A duration to close notification automatically by default (unit: second) | Number | 4.5 |
| placement | To set the position, which can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight |
| 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 |

74 changes: 64 additions & 10 deletions components/notification/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,47 @@ import React from 'react';
import Notification from 'rc-notification';
import Icon from '../icon';
import assign from 'object-assign';
let defaultTop = 24;
let notificationInstance;
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 {
message: React.ReactNode | string;
Expand All @@ -14,11 +52,14 @@ export interface ArgsProps {
onClose?: () => void;
duration?: number;
icon?: React.ReactNode;
placement?: notificationPlacement;
}

export interface ConfigProps {
top?: number;
bottom?: number;
duration?: number;
placement?: notificationPlacement;
}

function getNotificationInstance(prefixCls) {
Expand All @@ -27,10 +68,8 @@ function getNotificationInstance(prefixCls) {
}
notificationInstance = (Notification as any).newInstance({
prefixCls: prefixCls,
style: {
top: defaultTop,
right: 0,
},
className: `${prefixCls}-${defaultPlacement}`,
style: getPlacementStyle(defaultPlacement),
});
return notificationInstance;
}
Expand All @@ -39,6 +78,11 @@ function notice(args) {
const outerPrefixCls = args.prefixCls || 'ant-notification';
const prefixCls = `${outerPrefixCls}-notice`;

if (args.placement !== undefined) {
defaultPlacement = args.placement;
notificationInstance = null; // delete notificationInstance for new defaultPlacement
}

let duration;
if (args.duration === undefined) {
duration = defaultDuration;
Expand Down Expand Up @@ -113,12 +157,22 @@ const api: {
}
},
config(options: ConfigProps) {
if (options.top !== undefined) {
defaultTop = options.top;
notificationInstance = null; // delete notificationInstance for new defaultTop
const { duration, placement, bottom, top } = options;
if (placement !== undefined) {
defaultPlacement = placement;
}
if (bottom !== undefined) {
defaultBottom = bottom;
}
if (top !== undefined) {
defaultTop = top;
}
// delete notificationInstance
if (placement !== undefined || bottom !== undefined || top !== undefined) {
notificationInstance = null;
}
if (options.duration !== undefined) {
defaultDuration = options.duration;
if (duration !== undefined) {
defaultDuration = duration;
}
},
destroy() {
Expand Down
Loading