mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
fix(ui): drop react-onclickoutside
use a custom hook instad
This commit is contained in:
committed by
Łukasz Mierzwa
parent
4d2a543e4a
commit
4eacd18cf6
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -118,7 +118,7 @@ const MountedMenuContent = (group) => {
|
||||
return mount(
|
||||
<MenuContent
|
||||
popperPlacement="top"
|
||||
popperRef={null}
|
||||
popperRef={jest.fn()}
|
||||
popperStyle={{}}
|
||||
group={group}
|
||||
alert={alert}
|
||||
|
||||
@@ -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\\"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -120,7 +120,7 @@ const MountedMenuContent = (group) => {
|
||||
return mount(
|
||||
<MenuContent
|
||||
popperPlacement="top"
|
||||
popperRef={null}
|
||||
popperRef={jest.fn()}
|
||||
popperStyle={{}}
|
||||
group={group}
|
||||
afterClick={MockAfterClick}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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={{}}
|
||||
|
||||
6
ui/src/__mocks__/react-onclickoutside.js
vendored
6
ui/src/__mocks__/react-onclickoutside.js
vendored
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user