UNPKG

@carbon/ibm-security

Version:

Carbon for Cloud & Cognitive IBM Security UI components

460 lines (451 loc) 21 kB
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/inherits"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /** * @file Header. * @copyright IBM Security 2019, 2021 */ import { Close20, Notification20, Settings20 } from '@carbon/icons-react'; import classnames from 'classnames'; import PropTypes from 'prop-types'; import React, { Component, Fragment } from 'react'; import theme from '../../globals/theme'; import toggle from '../Component'; import { Accordion, AccordionItem } from '../Accordion'; import Button from '../Button'; import Icon from '../Icon'; import IconButton from '../IconButton'; import { namespace as iconButtonNamespace } from '../IconButton/IconButton'; import Link from '../Link'; import ProfileImage from '../ProfileImage'; import ScrollGradient from '../ScrollGradient'; import Transition from '../Transition'; import HeaderListItem from './HeaderListItem'; import { namespace as headerListItemNamespace } from './HeaderListItem/constants'; import HeaderNotification from './HeaderNotification'; import HeaderPopoverHeader from './HeaderPopoverHeader'; import HeaderPopoverLinkSecondary from './HeaderPopoverLinkSecondary'; import { defaultProps, namespace, propTypes } from './constants'; import { carbonPrefix } from '../../globals/namespace'; var headerButtonNamespace = "".concat(namespace, "__button"); var assistiveTextClass = "".concat(carbonPrefix, "--assistive-text"); var profileButtonAssistiveTextId = "".concat(namespace, "__profile__button--assistive-text"); var getPopoverLabelId = function getPopoverLabelId(string) { return "".concat(namespace, "__popover__label--").concat(string); }; /** * Renders the popover. * @param {HTMLElement} children The containing elements of the popover. * @param {boolean} state Whether the popover is toggled or not. * @returns {HTMLElement} The rendered popover. */ var renderPopover = function renderPopover(children, state) { return /*#__PURE__*/React.createElement(Transition, { className: namespace, component: "span" }, state && /*#__PURE__*/React.createElement("div", { className: "".concat(namespace, "__popover") }, children)); }; var Header = /*#__PURE__*/function (_Component) { function Header() { var _this; _classCallCheck(this, Header); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _callSuper(this, Header, [].concat(args)); _defineProperty(_this, "state", { isActive: { notifications: false, profile: false }, isUserActive: Header.getIsUserActive(_this.props.profile), accountList: Header.getAccountList({ accounts: _this.props.accounts, profile: _this.props.profile }) }); _defineProperty(_this, "toggle", toggle.bind(_this)); return _this; } _inherits(Header, _Component); return _createClass(Header, [{ key: "clearNotification", value: /** * Clears a notification. * @param {string} id The ID of the notification to clear. */ function clearNotification(id) { this.props.onNotificationClear(id); } /** * Clears all notifications. */ }, { key: "clearAllNotifications", value: function clearAllNotifications() { var _this2 = this; if (!this.props.clearAllNotifications) { this.props.notifications.forEach(function (notification) { return _this2.props.onNotificationClear(notification.id); }); } else { this.props.clearAllNotifications(this.props.notifications.map(function (notification) { return notification.id; })); } } /** * Closes the popover when loses focus. */ }, { key: "closePopover", value: function closePopover(target) { var _this3 = this; setTimeout(function () { var activeElement = target.getRootNode().activeElement || document.activeElement; var value = target.getAttribute('value'); var label = activeElement ? activeElement.getAttribute('aria-label') || '' : ''; if (!target.contains(activeElement) && label.indexOf(value) === -1) { _this3.toggle(value); } }, 0); } /** * Renders the profile. * @returns {Function} The rendered profile element. */ }, { key: "renderProfile", value: function renderProfile() { var _this4 = this; var _this$props = this.props, labels = _this$props.labels, links = _this$props.links, profile = _this$props.profile, onAccountClick = _this$props.onAccountClick; var _this$state = this.state, accountList = _this$state.accountList, isActive = _this$state.isActive; if (isActive.profile) { setTimeout(function () { _this4.userProfile.focus(); }, 0); } var hasAccount = profile.account; var accountElement = hasAccount && /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", { className: "".concat(namespace, "__popover__profile__body__label") }, labels.profile.account, ":", ' ', /*#__PURE__*/React.createElement("span", { className: "selectable-text" }, profile.account.id)), /*#__PURE__*/React.createElement("div", { className: "".concat(namespace, "__popover__profile__body__name") }, /*#__PURE__*/React.createElement("span", { className: "".concat(namespace, "__popover__profile__body__name__text") }, hasAccount.name))); var hasAccountList = accountList.length > 0; var popoverLabelId = getPopoverLabelId('profile'); return renderPopover(/*#__PURE__*/React.createElement("div", { ref: function ref(userProfile) { _this4.userProfile = userProfile; }, className: "".concat(namespace, "__popover--focus"), "aria-labelledby": popoverLabelId, onBlur: function onBlur() { return _this4.closePopover(_this4.userProfile); }, role: "tabpanel", tabIndex: "0", value: "profile" }, /*#__PURE__*/React.createElement(HeaderPopoverHeader, { className: "".concat(namespace, "__popover__profile__header") }, /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(ProfileImage, { className: "".concat(namespace, "__popover__profile__header__icon"), profile: profile, large: true }), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("span", { id: popoverLabelId, className: "".concat(namespace, "__popover__profile__header__title") }, profile.name.first_name, " ", profile.name.surname), /*#__PURE__*/React.createElement("span", { className: "".concat(namespace, "__popover__profile__header__email") }, profile.email)))), profile.description && /*#__PURE__*/React.createElement("section", { className: "".concat(namespace, "__popover__profile__description") }, profile.description), hasAccount && /*#__PURE__*/React.createElement("section", { className: classnames("".concat(namespace, "__popover__profile__body"), _defineProperty({}, "".concat(namespace, "__popover__profile__body--account"), !hasAccountList)) }, !hasAccountList ? accountElement : /*#__PURE__*/React.createElement(Accordion, { className: "".concat(namespace, "__popover__profile__body__accordion") }, /*#__PURE__*/React.createElement(AccordionItem, { className: "".concat(namespace, "__popover__profile__body__accordion__item"), title: accountElement }, /*#__PURE__*/React.createElement("ul", { className: "".concat(namespace, "__popover__profile__body__list") }, accountList.map(function (_ref) { var id = _ref.id, name = _ref.name; return /*#__PURE__*/React.createElement("li", { key: id, className: "".concat(namespace, "__popover__profile__body__account") }, /*#__PURE__*/React.createElement("button", { value: id, onClick: onAccountClick, className: "".concat(namespace, "__popover__profile__body__account__button"), type: "button" }, name)); }))))), /*#__PURE__*/React.createElement("section", { className: "".concat(namespace, "__popover__footer") }, this.props.showEditProfile && /*#__PURE__*/React.createElement(HeaderPopoverLinkSecondary, { className: "".concat(namespace, "__popover__profile__footer__edit"), href: links.edit_profile }, labels.profile.edit_profile), /*#__PURE__*/React.createElement(HeaderPopoverLinkSecondary, { className: "".concat(namespace, "__popover__profile__footer__logout"), href: links.sign_out }, labels.profile.sign_out))), isActive.profile); } /** * Renders notifications. * @returns {Function} The rendered notifications element. */ }, { key: "renderNotifications", value: function renderNotifications() { var _this5 = this; var _this$props2 = this.props, labels = _this$props2.labels, links = _this$props2.links, notifications = _this$props2.notifications, totalNotifications = _this$props2.totalNotifications; var isActive = this.state.isActive; var length = notifications.length; if (isActive.notifications) { setTimeout(function () { _this5.notifications.focus(); }, 0); } var popoverLabelId = getPopoverLabelId('notifications'); return renderPopover(/*#__PURE__*/React.createElement("div", { ref: function ref(notifications) { _this5.notifications = notifications; }, className: "".concat(namespace, "__popover--focus"), "aria-labelledby": popoverLabelId, onBlur: function onBlur() { return _this5.closePopover(_this5.notifications); }, role: "tabpanel", tabIndex: "0", value: "notifications" }, /*#__PURE__*/React.createElement(HeaderPopoverHeader, { id: popoverLabelId, title: labels.notifications.title }), length > 0 && /*#__PURE__*/React.createElement("div", { className: "".concat(namespace, "__popover__content") }, /*#__PURE__*/React.createElement("span", { className: "".concat(namespace, "__popover__label") }, labels.notifications.today), /*#__PURE__*/React.createElement("button", { className: "".concat(namespace, "__popover__button"), "aria-label": labels.notifications.clear_all, onClick: function onClick() { return _this5.clearAllNotifications(); }, type: "button" }, /*#__PURE__*/React.createElement(Icon, { renderIcon: Close20 }))), /*#__PURE__*/React.createElement(ScrollGradient, { color: theme.inverse02 }, /*#__PURE__*/React.createElement("ul", { className: "".concat(namespace, "__popover__list") }, /*#__PURE__*/React.createElement(Transition, { className: "".concat(namespace, "__notification"), component: "span" }, notifications.sort(function (a, b) { return new Date(b.datetime).getTime() - new Date(a.datetime).getTime(); }).map(function (_ref2, index) { var datetime = _ref2.datetime, description = _ref2.description, href = _ref2.href, id = _ref2.id, label = _ref2.label, product = _ref2.product; return /*#__PURE__*/React.createElement("li", { key: id, className: "".concat(namespace, "__popover__list-item") }, /*#__PURE__*/React.createElement(HeaderNotification, { clearButtonLabel: "".concat(labels.notifications.clear, " '").concat(description, "'"), dateTime: datetime, description: description, href: href, onClearButtonClick: function onClearButtonClick() { return _this5.clearNotification(id); }, product: product, timeLabel: label, viaLabel: labels.notifications.via, tabIndex: "0", tooltipDirection: index === 0 ? 'bottom' : 'top' })); })))), !length && /*#__PURE__*/React.createElement("span", { className: "".concat(namespace, "__popover__container") }, labels.notifications.success), /*#__PURE__*/React.createElement("div", { className: "".concat(namespace, "__popover__footer") }, links.notifications_view_all && /*#__PURE__*/React.createElement(HeaderPopoverLinkSecondary, { href: links.notifications_view_all }, labels.notifications.link, ' ', totalNotifications > 0 && "(".concat(totalNotifications, ")")), links.notifications_preferences && /*#__PURE__*/React.createElement(Icon, { className: "".concat(namespace, "__popover__icon"), renderIcon: Settings20, title: labels.notifications.preferences || '' }))), isActive.notifications); } /** * Renders whether the user is active or not. * @returns {Fragment} The correctly rendered profile element. */ }, { key: "renderIsUserActive", value: function renderIsUserActive() { var _this6 = this; var _this$props3 = this.props, labels = _this$props3.labels, links = _this$props3.links, profile = _this$props3.profile; var _this$state2 = this.state, isActive = _this$state2.isActive, isUserActive = _this$state2.isUserActive; var notifications = isActive.notifications; var activeClass = '--active'; var headerListItemClass = "".concat(headerListItemNamespace).concat(activeClass); var buttonActiveClass = "".concat(headerButtonNamespace).concat(activeClass); var notificationsButtonClasses = classnames(headerButtonNamespace, _defineProperty(_defineProperty({}, buttonActiveClass, notifications), "".concat(headerButtonNamespace, "--notifications"), this.props.notifications.filter(function (n) { return !n.acknowledged; }).length > 0)); var profileButtonClasses = classnames(iconButtonNamespace, headerButtonNamespace, _defineProperty(_defineProperty({}, buttonActiveClass, isActive.profile), "".concat(iconButtonNamespace).concat(activeClass), isActive.profile)); return isUserActive ? /*#__PURE__*/React.createElement(Fragment, null, this.props.showNotifications && /*#__PURE__*/React.createElement(HeaderListItem, { className: headerListItemClass }, /*#__PURE__*/React.createElement(IconButton, { "aria-expanded": isActive.notifications, "aria-haspopup": isUserActive, "aria-label": labels.notifications.button, className: notificationsButtonClasses, onClick: function onClick() { return _this6.toggle('notifications'); }, renderIcon: Notification20, state: notifications, tooltip: false }, /*#__PURE__*/React.createElement(Icon, { name: "notification" })), this.renderNotifications()), /*#__PURE__*/React.createElement(HeaderListItem, { className: headerListItemClass }, /*#__PURE__*/React.createElement("button", { "aria-expanded": isActive.profile, "aria-haspopup": isUserActive, "aria-describedby": profileButtonAssistiveTextId, className: profileButtonClasses, onClick: function onClick() { return _this6.toggle('profile'); }, type: "button" }, /*#__PURE__*/React.createElement("span", { id: profileButtonAssistiveTextId, className: assistiveTextClass }, labels.profile.button), /*#__PURE__*/React.createElement(ProfileImage, { profile: profile })), this.renderProfile())) : this.props.renderLoginAndSignup(links, labels.profile); } }, { key: "render", value: function render() { var _this$props4 = this.props, className = _this$props4.className, labels = _this$props4.labels, links = _this$props4.links; var brand = labels.brand; var link = links.product; var company = brand.company, domain = brand.domain, product = brand.product; var isUserActive = this.state.isUserActive; var classes = classnames("".concat(namespace, "__container"), className); var domainElement = function domainElement(title) { return /*#__PURE__*/React.createElement("span", { className: "".concat(namespace, "__link__title--domain") }, title); }; return /*#__PURE__*/React.createElement("div", { className: classes }, /*#__PURE__*/React.createElement("header", { className: namespace, role: "banner" }, /*#__PURE__*/React.createElement(Link, { className: "".concat(namespace, "__link"), href: link }, "".concat(company, " "), domain ? /*#__PURE__*/React.createElement(Fragment, null, domainElement("".concat(domain, " ")), /*#__PURE__*/React.createElement("span", { className: "".concat(namespace, "__link__title--product") }, product)) : domainElement(product)), /*#__PURE__*/React.createElement("ul", { className: "".concat(namespace, "__group ").concat(isUserActive && "".concat(namespace, "__group--active")) }, this.renderIsUserActive()))); } }], [{ key: "getDerivedStateFromProps", value: function getDerivedStateFromProps(nextProps) { return { isUserActive: Header.getIsUserActive(nextProps.profile), accountList: Header.getAccountList({ accounts: nextProps.accounts, profile: nextProps.profile }) }; } /** * Returns whether a user is active or not. * @param {Record<string, any>} profile An object list of profile information. * @returns {boolean} Whether the user is active or not. * @static */ }, { key: "getIsUserActive", value: function getIsUserActive(profile) { return profile !== null; } /** * Returns a list of valid user accounts not including their current one. * @param {Record<Array, any>} userAccounts An object list of account information. * @returns {Array} A list of valid accounts. * @static */ }, { key: "getAccountList", value: function getAccountList(userAccounts) { if (userAccounts.profile && userAccounts.profile.account) { return userAccounts.accounts && userAccounts.accounts.filter(function (account) { return account.id !== userAccounts.profile.account.id; }) || []; } return []; } }]); }(Component); _defineProperty(Header, "propTypes", _objectSpread(_objectSpread({}, propTypes), {}, { /** * @type {function(links: Object, labels: Object): React.Element} render custom login and sign up section */ renderLoginAndSignup: PropTypes.func })); _defineProperty(Header, "defaultProps", _objectSpread(_objectSpread({}, defaultProps), {}, { renderLoginAndSignup: function renderLoginAndSignup(links, labels) { return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(HeaderListItem, null, /*#__PURE__*/React.createElement(Button, { href: links.registration }, labels.registration)), /*#__PURE__*/React.createElement(HeaderListItem, null, /*#__PURE__*/React.createElement(Button, { href: links.sign_in, kind: "secondary" }, labels.sign_in))); } })); export { Header as default };