gecko-dev/devtools/client/shared/components/notification-box.js
Dalimil Hajek e32bf05b2b Bug 1297302 - Fix tooltip of messageCloseButton in toolbox-notificationbox. r=Honza
--HG--
extra : rebase_source : f11a0bd549f7e13891f34bf56167c03c17468f9f
2016-08-30 06:14:00 -04:00

264 lines
7.4 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const React = require("devtools/client/shared/vendor/react");
const Immutable = require("devtools/client/shared/vendor/immutable");
const { LocalizationHelper } = require("devtools/shared/l10n");
const l10n = new LocalizationHelper("devtools/locale/components.properties");
// Shortcuts
const { PropTypes, createClass, DOM } = React;
const { div, span, button } = DOM;
// Priority Levels
const PriorityLevels = {
PRIORITY_INFO_LOW: 1,
PRIORITY_INFO_MEDIUM: 2,
PRIORITY_INFO_HIGH: 3,
PRIORITY_WARNING_LOW: 4,
PRIORITY_WARNING_MEDIUM: 5,
PRIORITY_WARNING_HIGH: 6,
PRIORITY_CRITICAL_LOW: 7,
PRIORITY_CRITICAL_MEDIUM: 8,
PRIORITY_CRITICAL_HIGH: 9,
PRIORITY_CRITICAL_BLOCK: 10,
};
/**
* This component represents Notification Box - HTML alternative for
* <xul:notifictionbox> binding.
*
* See also MDN for more info about <xul:notificationbox>:
* https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/notificationbox
*/
var NotificationBox = createClass({
displayName: "NotificationBox",
propTypes: {
// List of notifications appended into the box.
notifications: PropTypes.arrayOf(PropTypes.shape({
// label to appear on the notification.
label: PropTypes.string.isRequired,
// Value used to identify the notification
value: PropTypes.string.isRequired,
// URL of image to appear on the notification. If "" then an icon
// appropriate for the priority level is used.
image: PropTypes.string.isRequired,
// Notification priority; see Priority Levels.
priority: PropTypes.number.isRequired,
// Array of button descriptions to appear on the notification.
buttons: PropTypes.arrayOf(PropTypes.shape({
// Function to be called when the button is activated.
// This function is passed three arguments:
// 1) the NotificationBox component the button is associated with
// 2) the button description as passed to appendNotification.
// 3) the element which was the target of the button press event.
// If the return value from this function is not True, then the
// notification is closed. The notification is also not closed
// if an error is thrown.
callback: PropTypes.func.isRequired,
// The label to appear on the button.
label: PropTypes.string.isRequired,
// The accesskey attribute set on the <button> element.
accesskey: PropTypes.string,
})),
// A function to call to notify you of interesting things that happen
// with the notification box.
eventCallback: PropTypes.func,
})),
// Message that should be shown when hovering over the close button
closeButtonTooltip: PropTypes.string
},
getDefaultProps() {
return {
closeButtonTooltip: l10n.getStr("notificationBox.closeTooltip")
};
},
getInitialState() {
return {
notifications: new Immutable.OrderedMap()
};
},
/**
* Create a new notification and display it. If another notification is
* already present with a higher priority, the new notification will be
* added behind it. See `propTypes` for arguments description.
*/
appendNotification(label, value, image, priority, buttons = [],
eventCallback) {
// Priority level must be within expected interval
// (see priority levels at the top of this file).
if (priority < PriorityLevels.PRIORITY_INFO_LOW ||
priority > PriorityLevels.PRIORITY_CRITICAL_BLOCK) {
throw new Error("Invalid notification priority " + priority);
}
// Custom image URL is not supported yet.
if (image) {
throw new Error("Custom image URL is not supported yet");
}
let type = "warning";
if (priority >= PriorityLevels.PRIORITY_CRITICAL_LOW) {
type = "critical";
} else if (priority <= PriorityLevels.PRIORITY_INFO_HIGH) {
type = "info";
}
let notifications = this.state.notifications.set(value, {
label: label,
value: value,
image: image,
priority: priority,
type: type,
buttons: buttons,
eventCallback: eventCallback,
});
// High priorities must be on top.
notifications = notifications.sortBy((val, key) => {
return -val.priority;
});
this.setState({
notifications: notifications
});
},
/**
* Remove specific notification from the list.
*/
removeNotification(notification) {
this.close(this.state.notifications.get(notification.value));
},
/**
* Returns an object that represents a notification. It can be
* used to close it.
*/
getNotificationWithValue(value) {
let notification = this.state.notifications.get(value);
if (!notification) {
return null;
}
// Return an object that can be used to remove the notification
// later (using `removeNotification` method) or directly close it.
return Object.assign({}, notification, {
close: () => {
this.close(notification);
}
});
},
getCurrentNotification() {
return this.state.notifications.first();
},
/**
* Close specified notification.
*/
close(notification) {
if (!notification) {
return;
}
if (notification.eventCallback) {
notification.eventCallback("removed");
}
this.setState({
notifications: this.state.notifications.remove(notification.value)
});
},
/**
* Render a button. A notification can have a set of custom buttons.
* These are used to execute custom callback.
*/
renderButton(props, notification) {
let onClick = event => {
if (props.callback) {
let result = props.callback(this, props, event.target);
if (!result) {
this.close(notification);
}
event.stopPropagation();
}
};
return (
button({
key: props.label,
className: "notification-button",
accesskey: props.accesskey,
onClick: onClick},
props.label
)
);
},
/**
* Render a notification.
*/
renderNotification(notification) {
return (
div({
key: notification.value,
className: "notification",
"data-type": notification.type},
div({className: "notificationInner"},
div({className: "details"},
div({
className: "messageImage",
"data-type": notification.type}),
span({className: "messageText"},
notification.label
),
notification.buttons.map(props =>
this.renderButton(props, notification)
)
),
div({
className: "messageCloseButton",
title: this.props.closeButtonTooltip,
onClick: this.close.bind(this, notification)}
)
)
)
);
},
/**
* Render the top (highest priority) notification. Only one
* notification is rendered at a time.
*/
render() {
let notification = this.state.notifications.first();
let content = notification ?
this.renderNotification(notification) :
null;
return div({className: "notificationbox"},
content
);
},
});
module.exports.NotificationBox = NotificationBox;
module.exports.PriorityLevels = PriorityLevels;