fix(ui): drop react-onclickoutside

use a custom hook instad
This commit is contained in:
Łukasz Mierzwa
2020-05-13 17:07:37 +01:00
committed by Łukasz Mierzwa
parent 4d2a543e4a
commit 4eacd18cf6
9 changed files with 139 additions and 140 deletions

View File

@@ -50,7 +50,6 @@
"react-masonry-infinite": "1.2.2",
"react-media": "1.10.0",
"react-moment": "0.9.7",
"react-onclickoutside": "6.9.0",
"react-popper": "2.2.3",
"react-resize-detector": "4.2.3",
"react-reveal": "1.2.2",

View File

@@ -1,12 +1,9 @@
import React from "react";
import React, { useRef, useEffect } from "react";
import PropTypes from "prop-types";
import { useObserver, useLocalStore } from "mobx-react";
import hash from "object-hash";
import { Manager, Reference, Popper } from "react-popper";
import onClickOutside from "react-onclickoutside";
import Moment from "react-moment";
@@ -20,6 +17,7 @@ import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore, SilenceTabNames } from "Stores/SilenceFormStore";
import { FetchPauser } from "Components/FetchPauser";
import { DropdownSlide } from "Components/Animations/DropdownSlide";
import { useOnClickOutside } from "Hooks/useOnClickOutside";
const onSilenceClick = (alertStore, silenceFormStore, group, alert) => {
silenceFormStore.data.resetProgress();
@@ -32,60 +30,65 @@ const onSilenceClick = (alertStore, silenceFormStore, group, alert) => {
silenceFormStore.toggle.show();
};
const MenuContent = onClickOutside(
({
popperPlacement,
popperRef,
popperStyle,
group,
alert,
afterClick,
alertStore,
silenceFormStore,
}) => {
const isReadOnly =
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0;
const MenuContent = ({
popperPlacement,
popperRef,
popperStyle,
group,
alert,
afterClick,
alertStore,
silenceFormStore,
}) => {
const ref = useRef(null);
useOnClickOutside(ref, afterClick);
return (
<FetchPauser alertStore={alertStore}>
<div
className="dropdown-menu d-block shadow"
ref={popperRef}
style={popperStyle}
data-placement={popperPlacement}
>
<h6 className="dropdown-header">Alert source links:</h6>
{alert.alertmanager.map((am) => (
<a
key={am.name}
className="dropdown-item"
href={am.source}
target="_blank"
rel="noopener noreferrer"
onClick={afterClick}
>
<FontAwesomeIcon className="mr-1" icon={faExternalLinkAlt} />
{am.name}
</a>
))}
<div className="dropdown-divider" />
<div
className={`dropdown-item cursor-pointer ${
isReadOnly && "disabled"
}`}
onClick={() =>
isReadOnly ||
onSilenceClick(alertStore, silenceFormStore, group, alert)
}
useEffect(() => {
popperRef(ref.current);
}, [popperRef]);
return (
<FetchPauser alertStore={alertStore}>
<div
className="dropdown-menu d-block shadow"
ref={ref}
style={popperStyle}
data-placement={popperPlacement}
>
<h6 className="dropdown-header">Alert source links:</h6>
{alert.alertmanager.map((am) => (
<a
key={am.name}
className="dropdown-item"
href={am.source}
target="_blank"
rel="noopener noreferrer"
onClick={afterClick}
>
<FontAwesomeIcon className="mr-1" icon={faBellSlash} />
Silence this alert
</div>
<FontAwesomeIcon className="mr-1" icon={faExternalLinkAlt} />
{am.name}
</a>
))}
<div className="dropdown-divider" />
<div
className={`dropdown-item cursor-pointer ${
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0 &&
"disabled"
}`}
onClick={() => {
if (Object.keys(alertStore.data.clustersWithoutReadOnly).length) {
onSilenceClick(alertStore, silenceFormStore, group, alert);
afterClick();
}
}}
>
<FontAwesomeIcon className="mr-1" icon={faBellSlash} />
Silence this alert
</div>
</FetchPauser>
);
}
);
</div>
</FetchPauser>
);
};
MenuContent.propTypes = {
popperPlacement: PropTypes.string,
popperRef: PropTypes.func,
@@ -114,14 +117,12 @@ const AlertMenu = ({
},
}));
const uniqueClass = `components-grid-alert-${group.id}-${hash(alert.labels)}`;
return useObserver(() => (
<Manager>
<Reference>
{({ ref }) => (
<span
className={`components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer ${uniqueClass}`}
className="components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer"
ref={ref}
onClick={collapse.toggle}
data-toggle="dropdown"
@@ -153,8 +154,6 @@ const AlertMenu = ({
alertStore={alertStore}
silenceFormStore={silenceFormStore}
afterClick={collapse.hide}
handleClickOutside={collapse.hide}
outsideClickIgnoreClass={uniqueClass}
/>
)}
</Popper>

View File

@@ -118,7 +118,7 @@ const MountedMenuContent = (group) => {
return mount(
<MenuContent
popperPlacement="top"
popperRef={null}
popperRef={jest.fn()}
popperStyle={{}}
group={group}
alert={alert}

View File

@@ -62,7 +62,7 @@ exports[`<Alert /> matches snapshot when inhibited 1`] = `
</div>
</div>
</div>
<span class=\\"components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer components-grid-alert-099c5ca6d1c92f615b13056b935d0c8dee70f18c-0988cb349635341c67d91bfe3454d2b3178c443c\\"
<span class=\\"components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer\\"
data-toggle=\\"dropdown\\"
>
<svg aria-hidden=\\"true\\"
@@ -224,7 +224,7 @@ exports[`<Alert /> matches snapshot with showAlertmanagers=false showReceiver=fa
</div>
</div>
</div>
<span class=\\"components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer components-grid-alert-099c5ca6d1c92f615b13056b935d0c8dee70f18c-0988cb349635341c67d91bfe3454d2b3178c443c\\"
<span class=\\"components-label components-label-with-hover px-1 mr-1 badge badge-secondary cursor-pointer\\"
data-toggle=\\"dropdown\\"
>
<svg aria-hidden=\\"true\\"

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useRef } from "react";
import PropTypes from "prop-types";
import { useObserver, useLocalStore } from "mobx-react";
@@ -6,7 +6,6 @@ import { useObserver, useLocalStore } from "mobx-react";
import copy from "copy-to-clipboard";
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";
@@ -20,6 +19,7 @@ import { SilenceFormStore, SilenceTabNames } from "Stores/SilenceFormStore";
import { QueryOperators, StaticLabels, FormatQuery } from "Common/Query";
import { DropdownSlide } from "Components/Animations/DropdownSlide";
import { FetchPauser } from "Components/FetchPauser";
import { useOnClickOutside } from "Hooks/useOnClickOutside";
const onSilenceClick = (alertStore, silenceFormStore, group) => {
silenceFormStore.data.resetProgress();
@@ -31,65 +31,71 @@ const onSilenceClick = (alertStore, silenceFormStore, group) => {
silenceFormStore.toggle.show();
};
const MenuContent = onClickOutside(
({
popperPlacement,
popperRef,
popperStyle,
group,
afterClick,
alertStore,
silenceFormStore,
}) => {
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 baseURL = [
window.location.protocol,
"//",
window.location.host,
window.location.pathname,
].join("");
const groupLink = `${baseURL}?${FormatAlertsQ(groupFilters)}`;
const MenuContent = ({
popperPlacement,
popperRef,
popperStyle,
group,
afterClick,
alertStore,
silenceFormStore,
}) => {
const ref = useRef(null);
useOnClickOutside(ref, afterClick);
const isReadOnly =
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0;
useEffect(() => {
popperRef(ref.current);
}, [popperRef]);
return (
<FetchPauser alertStore={alertStore}>
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 baseURL = [
window.location.protocol,
"//",
window.location.host,
window.location.pathname,
].join("");
const groupLink = `${baseURL}?${FormatAlertsQ(groupFilters)}`;
return (
<FetchPauser alertStore={alertStore}>
<div
className="dropdown-menu d-block shadow"
ref={ref}
style={popperStyle}
data-placement={popperPlacement}
>
<div
className="dropdown-menu d-block shadow"
ref={popperRef}
style={popperStyle}
data-placement={popperPlacement}
className="dropdown-item cursor-pointer"
onClick={() => {
copy(groupLink);
afterClick();
}}
>
<div
className="dropdown-item cursor-pointer"
onClick={() => {
copy(groupLink);
afterClick();
}}
>
<FontAwesomeIcon icon={faShareSquare} /> Copy link to this group
</div>
<div
className={`dropdown-item cursor-pointer ${
isReadOnly && "disabled"
}`}
onClick={() =>
isReadOnly || onSilenceClick(alertStore, silenceFormStore, group)
}
>
<FontAwesomeIcon icon={faBellSlash} /> Silence this group
</div>
<FontAwesomeIcon icon={faShareSquare} /> Copy link to this group
</div>
</FetchPauser>
);
}
);
<div
className={`dropdown-item cursor-pointer ${
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0 &&
"disabled"
}`}
onClick={() => {
if (Object.keys(alertStore.data.clustersWithoutReadOnly).length) {
onSilenceClick(alertStore, silenceFormStore, group);
afterClick();
}
}}
>
<FontAwesomeIcon icon={faBellSlash} /> Silence this group
</div>
</div>
</FetchPauser>
);
};
MenuContent.propTypes = {
popperPlacement: PropTypes.string,
popperRef: PropTypes.func,
@@ -152,8 +158,6 @@ const GroupMenu = ({
alertStore={alertStore}
silenceFormStore={silenceFormStore}
afterClick={collapse.hide}
handleClickOutside={collapse.hide}
outsideClickIgnoreClass={`components-grid-alertgroup-${group.id}`}
/>
)}
</Popper>

View File

@@ -120,7 +120,7 @@ const MountedMenuContent = (group) => {
return mount(
<MenuContent
popperPlacement="top"
popperRef={null}
popperRef={jest.fn()}
popperStyle={{}}
group={group}
afterClick={MockAfterClick}

View File

@@ -7,7 +7,6 @@ import { localStored } from "mobx-stored";
import hash from "object-hash";
import { Manager, Reference, Popper } from "react-popper";
import onClickOutside from "react-onclickoutside";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faHistory } from "@fortawesome/free-solid-svg-icons/faHistory";
@@ -20,6 +19,7 @@ import { AlertStore } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { DropdownSlide } from "Components/Animations/DropdownSlide";
import { HistoryLabel } from "Components/Labels/HistoryLabel";
import { useOnClickOutside } from "Hooks/useOnClickOutside";
// takes a filter object out of alertStore.history.values and creates a new
// object with only those keys that will be stored in history
@@ -52,7 +52,7 @@ ActionButton.propTypes = {
afterClick: PropTypes.func.isRequired,
};
const HistoryMenuContent = ({
const HistoryMenu = ({
popperPlacement,
popperRef,
popperStyle,
@@ -62,10 +62,17 @@ const HistoryMenuContent = ({
afterClick,
onClear,
}) => {
const ref = useRef(null);
useOnClickOutside(ref, afterClick);
useEffect(() => {
popperRef(ref.current);
}, [popperRef]);
return (
<div
className="dropdown-menu d-block shadow components-navbar-historymenu"
ref={popperRef}
ref={ref}
style={popperStyle}
data-placement={popperPlacement}
>
@@ -130,7 +137,7 @@ const HistoryMenuContent = ({
</div>
);
};
HistoryMenuContent.propTypes = {
HistoryMenu.propTypes = {
popperPlacement: PropTypes.string,
popperRef: PropTypes.func,
popperStyle: PropTypes.object,
@@ -141,8 +148,6 @@ HistoryMenuContent.propTypes = {
onClear: PropTypes.func.isRequired,
};
const HistoryMenu = onClickOutside(HistoryMenuContent);
const History = ({ alertStore, settingsStore }) => {
// this will be dumped to local storage via mobx-stored
const history = localStored(
@@ -236,8 +241,6 @@ const History = ({ alertStore, settingsStore }) => {
alertStore={alertStore}
settingsStore={settingsStore}
afterClick={collapse.hide}
handleClickOutside={collapse.hide}
outsideClickIgnoreClass="components-navbar-history"
/>
)}
</Popper>
@@ -250,4 +253,4 @@ History.propTypes = {
settingsStore: PropTypes.instanceOf(Settings).isRequired,
};
export { History, HistoryMenu, HistoryMenuContent, ReduceFilter };
export { History, HistoryMenu, ReduceFilter };

View File

@@ -5,7 +5,7 @@ import { storiesOf } from "@storybook/react";
import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { HistoryMenuContent } from "./FilterInput/History";
import { HistoryMenu } from "./FilterInput/History";
import { NavBar } from ".";
import "Styles/Percy.scss";
@@ -91,7 +91,7 @@ storiesOf("NavBar", module).add("NavBar", () => {
silenceFormStore={silenceFormStore}
fixedTop={false}
/>
<HistoryMenuContent
<HistoryMenu
popperPlacement="top"
popperRef={() => {}}
popperStyle={{}}

View File

@@ -1,6 +0,0 @@
// mock react-onclickoutside so we bypass it due to:
// TypeError: Cannot read property 'isReactComponent' of undefined
export default function onClickOutsideHOC(WrappedComponent, config) {
return WrappedComponent;
}