From 542e45e5ac93dc2cc6605b63e291e5f06a3cdd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Mon, 9 Jul 2018 09:51:26 +0200 Subject: [PATCH] feat(ui): handle outside clicks in dropdowns --- ui/package-lock.json | 5 + ui/package.json | 1 + .../AlertGroup/GroupHeader/GroupMenu.js | 128 ++++++++++++++++++ .../AlertGrid/AlertGroup/GroupHeader/index.js | 43 ++---- .../Grid/AlertGrid/AlertGroup/index.js | 7 +- .../Components/NavBar/FilterInput/History.js | 98 ++++++++------ 6 files changed, 203 insertions(+), 79 deletions(-) create mode 100644 ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.js diff --git a/ui/package-lock.json b/ui/package-lock.json index a0f5956cb..8b0754aaa 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9671,6 +9671,11 @@ "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.7.9.tgz", "integrity": "sha512-JpHQgpB+p3oqZv583xlJakPXgqdIjGBZvngHYcFhlYxTYs0NDZSdoM1FCpU9T2QmqKOEFBVuhhJBMvJzTkF/wQ==" }, + "react-onclickoutside": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz", + "integrity": "sha512-p84kBqGaMoa7VYT0vZ/aOYRfJB+gw34yjpda1Z5KeLflg70HipZOT+MXQenEhdkPAABuE2Astq4zEPdMqUQxcg==" + }, "react-popper": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.0.0.tgz", diff --git a/ui/package.json b/ui/package.json index c9b3f7131..fb1b0e5ee 100644 --- a/ui/package.json +++ b/ui/package.json @@ -27,6 +27,7 @@ "react-linkify": "^0.2.2", "react-masonry-infinite": "^1.2.2", "react-moment": "^0.7.9", + "react-onclickoutside": "^6.7.1", "react-popper": "^1.0.0", "react-resize-detector": "^3.0.1", "react-scripts": "1.1.4", diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.js new file mode 100644 index 000000000..49a0b33bc --- /dev/null +++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.js @@ -0,0 +1,128 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; + +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; + +import { Manager, Reference, Popper } from "react-popper"; +import onClickOutside from "react-onclickoutside"; + +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faEllipsisV } from "@fortawesome/free-solid-svg-icons/faEllipsisV"; +import { faShareSquare } from "@fortawesome/free-solid-svg-icons/faShareSquare"; + +import { FormatAPIFilterQuery } from "Stores/AlertStore"; +import { QueryOperators, StaticLabels, FormatQuery } from "Common/Query"; + +const MenuContent = onClickOutside( + ({ popperPlacement, popperRef, popperStyle, group, afterClick }) => { + let groupFilters = Object.keys(group.labels).map(name => + FormatQuery(name, QueryOperators.Equal, group.labels[name]) + ); + groupFilters.push( + FormatQuery(StaticLabels.Receiver, QueryOperators.Equal, group.receiver) + ); + const groupLink = `?${FormatAPIFilterQuery(groupFilters)}`; + + return ( +
+ + Link to this group + +
+ ); + } +); +MenuContent.propTypes = { + popperPlacement: PropTypes.string, + popperRef: PropTypes.func, + popperStyle: PropTypes.object, + group: PropTypes.object.isRequired, + afterClick: PropTypes.func.isRequired +}; + +const GroupMenu = observer( + class GroupMenu extends Component { + static propTypes = { + group: PropTypes.object.isRequired + }; + + collapse = observable( + { + value: true, + toggle() { + this.value = !this.value; + }, + hide() { + this.value = true; + } + }, + { toggle: action.bound, hide: action.bound }, + { name: "Alert group menu toggle" } + ); + + handleClickOutside = action(event => { + this.collapse.hide(); + }); + + render() { + const { group } = this.props; + + return ( + + + {({ ref }) => ( + + + + )} + + + {this.collapse.value + ? ({ placement, ref, style }) => + : ({ placement, ref, style }) => ( + + )} + + + ); + } + } +); + +export { GroupMenu }; diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.js index e619889fd..f3e13cce7 100644 --- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.js +++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.js @@ -4,60 +4,43 @@ import PropTypes from "prop-types"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faShareSquare } from "@fortawesome/free-solid-svg-icons/faShareSquare"; import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; -import { FormatAPIFilterQuery } from "Stores/AlertStore"; -import { QueryOperators, StaticLabels, FormatQuery } from "Common/Query"; import { FilteringLabel } from "Components/Labels/FilteringLabel"; import { FilteringCounterBadge } from "Components/Labels/FilteringCounterBadge"; +import { GroupMenu } from "./GroupMenu"; const GroupHeader = observer( class GroupHeader extends Component { static propTypes = { collapseStore: PropTypes.object.isRequired, - labels: PropTypes.object.isRequired, - receiver: PropTypes.string.isRequired, - stateCount: PropTypes.object.isRequired + group: PropTypes.object.isRequired }; render() { - const { collapseStore, labels, receiver, stateCount } = this.props; - - let groupFilters = Object.keys(labels).map(name => - FormatQuery(name, QueryOperators.Equal, labels[name]) - ); - groupFilters.push( - FormatQuery(StaticLabels.Receiver, QueryOperators.Equal, receiver) - ); - const groupLink = `?${FormatAPIFilterQuery(groupFilters)}`; + const { collapseStore, group } = this.props; return (
- - - + + + - {Object.keys(labels).map(name => ( - + {Object.keys(group.labels).map(name => ( + ))}
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js index 038f5ec56..653f4f59a 100644 --- a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js +++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js @@ -144,12 +144,7 @@ const AlertGroup = observer(
- + {this.collapse.value ? null : (
    {group.alerts diff --git a/ui/src/Components/NavBar/FilterInput/History.js b/ui/src/Components/NavBar/FilterInput/History.js index 98135c87d..fe6ee961a 100644 --- a/ui/src/Components/NavBar/FilterInput/History.js +++ b/ui/src/Components/NavBar/FilterInput/History.js @@ -6,6 +6,7 @@ import { observer } from "mobx-react"; import { localStored } from "mobx-stored"; import { Manager, Reference, Popper } from "react-popper"; +import onClickOutside from "react-onclickoutside"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown"; @@ -28,49 +29,51 @@ function reduceFilter(filter) { }; } -const HistoryMenu = ({ - popperPlacement, - popperRef, - popperStyle, - filters, - alertStore, - afterClick -}) => { - return ( -
    - {filters.length === 0 ? ( -
    Empty
    - ) : ( - filters.map(historyFilters => ( - - )) - )} -
    - ); -}; +const HistoryMenu = onClickOutside( + ({ + popperPlacement, + popperRef, + popperStyle, + filters, + alertStore, + afterClick + }) => { + return ( +
    + {filters.length === 0 ? ( +
    Empty
    + ) : ( + filters.map(historyFilters => ( + + )) + )} +
    + ); + } +); HistoryMenu.propTypes = { popperPlacement: PropTypes.string, popperRef: PropTypes.func, @@ -140,6 +143,10 @@ const History = observer( this.appendToHistory(); } + handleClickOutside = action(event => { + this.collapse.hide(); + }); + render() { const { alertStore } = this.props; @@ -159,7 +166,7 @@ const History = observer(