chore(ui): drop react-reveal animations

This library is not maintained anymore and it causes small glitches when rendering a lot of alerts - scrolling groups into view doesn't always trigger animation and they stay hidden.
This commit is contained in:
Łukasz Mierzwa
2020-07-03 17:03:43 +01:00
committed by Łukasz Mierzwa
parent 46d49f2404
commit 2f74561809
40 changed files with 596 additions and 380 deletions
+4 -2
View File
@@ -18,6 +18,10 @@ import {
ReactSelectStyles,
} from "Components/Theme/ReactSelect";
import { config } from "react-transition-group";
config.disabled = true;
const { percyAddon, serializeStories } = createPercyAddon();
setAddon(percyAddon);
@@ -36,7 +40,6 @@ addDecorator((story) => {
value={{
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light),
animations: {
in: true,
duration: 0,
},
}}
@@ -58,7 +61,6 @@ addDecorator((story) => {
value={{
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Dark),
animations: {
in: true,
duration: 0,
},
}}
+18 -8
View File
@@ -12422,6 +12422,11 @@
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
"dev": true
},
"intersection-observer": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.11.0.tgz",
"integrity": "sha512-KZArj2QVnmdud9zTpKf279m2bbGfG+4/kn16UU0NL3pTVl52ZHiJ9IRNSsnn6jaHrL9EGLFM5eWjTx2fz/+zoQ=="
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -18966,6 +18971,14 @@
"prop-types": "^15.5.8"
}
},
"react-intersection-observer": {
"version": "8.26.2",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.26.2.tgz",
"integrity": "sha512-GmSjLNK+oV7kS+BHfrJSaA4wF61ELA33gizKHmN+tk59UT6/aW8kkqvlrFGPwxGoaIzLKS2evfG5fgkw5MIIsg==",
"requires": {
"tiny-invariant": "^1.1.0"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -19061,14 +19074,6 @@
"resize-observer-polyfill": "^1.5.1"
}
},
"react-reveal": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/react-reveal/-/react-reveal-1.2.2.tgz",
"integrity": "sha512-JCv3fAoU6Z+Lcd8U48bwzm4pMZ79qsedSXYwpwt6lJNtj/v5nKJYZZbw3yhaQPPgYePo3Y0NOCoYOq/jcsisuw==",
"requires": {
"prop-types": "^15.5.10"
}
},
"react-scripts": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.1.tgz",
@@ -21682,6 +21687,11 @@
"dev": true,
"optional": true
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tlds": {
"version": "1.207.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.207.0.tgz",
+2 -1
View File
@@ -24,6 +24,7 @@
"date-fns": "2.14.0",
"favico.js": "0.3.10",
"fontfaceobserver": "2.1.0",
"intersection-observer": "0.11.0",
"lodash.debounce": "4.0.8",
"lodash.merge": "4.6.2",
"lodash.throttle": "4.1.1",
@@ -44,13 +45,13 @@
"react-hotkeys-hook": "2.1.4",
"react-idle-timer": "4.2.12",
"react-input-range": "1.3.0",
"react-intersection-observer": "8.26.2",
"react-js-pagination": "3.0.3",
"react-json-pretty": "2.2.0",
"react-linkify": "0.2.2",
"react-media-hook": "0.4.7",
"react-popper": "2.2.3",
"react-resize-detector": "5.0.6",
"react-reveal": "1.2.2",
"react-scripts": "3.4.1",
"react-select": "3.1.0",
"react-transition-group": "4.4.1",
+1 -2
View File
@@ -136,8 +136,7 @@ const App: FunctionComponent<AppProps> = ({ defaultFilters, uiDefaults }) => {
? ReactSelectStyles(ReactSelectColors.Dark)
: ReactSelectStyles(ReactSelectColors.Light),
animations: {
enabled: true,
duration: 1000,
duration: 500,
},
}}
>
@@ -3,10 +3,8 @@
exports[`<CenteredMessage /> matches snapshot 1`] = `
"
<h1 class=\\"display-1 text-placeholder screen-center\\">
<div class=\\"react-reveal\\">
<div>
Foo
</div>
<div>
Foo
</div>
</h1>
"
+14 -9
View File
@@ -1,21 +1,26 @@
import React from "react";
import { Fade } from "react-reveal";
import { CSSTransition } from "react-transition-group";
import { ThemeContext } from "Components/Theme";
const CenteredMessage = ({ children, className }) => {
const theme = React.useContext(ThemeContext);
const context = React.useContext(ThemeContext);
return (
<h1
className={`${
className ? className : "display-1 text-placeholder"
} screen-center`}
<CSSTransition
in={true}
appear={true}
classNames="components-animation-fade"
timeout={context.animations.duration}
>
<Fade in={theme.animations.in} duration={theme.animations.duration}>
<h1
className={`${
className ? className : "display-1 text-placeholder"
} screen-center`}
>
{children}
</Fade>
</h1>
</h1>
</CSSTransition>
);
};
@@ -28,7 +28,7 @@ exports[`<Alert /> matches snapshot when inhibited 1`] = `
</span>
</span>
<span class=\\"Linkify\\">
<span style=\\"opacity: 1;\\">
<span>
some long text
</span>
</span>
@@ -175,7 +175,7 @@ exports[`<Alert /> matches snapshot with showAlertmanagers=false showReceiver=fa
</span>
</span>
<span class=\\"Linkify\\">
<span style=\\"opacity: 1;\\">
<span>
some long text
</span>
</span>
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import Linkify from "react-linkify";
import Flash from "react-reveal/Flash";
import { CSSTransition } from "react-transition-group";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkAlt";
@@ -11,6 +11,7 @@ import { faSearchPlus } from "@fortawesome/free-solid-svg-icons/faSearchPlus";
import { faSearchMinus } from "@fortawesome/free-solid-svg-icons/faSearchMinus";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { useFlashTransition } from "Hooks/useFlashTransition";
const RenderNonLinkAnnotation = memo(
({ name, value, visible, afterUpdate }) => {
@@ -26,40 +27,43 @@ const RenderNonLinkAnnotation = memo(
}
});
const { ref, props } = useFlashTransition(value);
const className =
"mb-1 p-1 bg-light d-inline-block rounded components-grid-annotation text-break mw-100";
if (!isVisible) {
return (
<TooltipWrapper title="Click to show annotation value">
<div
className={`${className} cursor-pointer`}
onClick={() => setIsVisible(!isVisible)}
>
<FontAwesomeIcon icon={faSearchPlus} className="mr-1" />
{name}
</div>
</TooltipWrapper>
);
}
return (
<TooltipWrapper title="Click the icon to hide annotation value">
<div key={name} className={className}>
<span onClick={() => setIsVisible(false)} className="cursor-pointer">
<FontAwesomeIcon icon={faSearchMinus} className="mr-1" />
<span className="text-muted">{name}: </span>
</span>
<Linkify
properties={{
target: "_blank",
rel: "noopener noreferrer",
}}
>
<Flash spy={value}>
<span>{value}</span>
</Flash>
</Linkify>
<TooltipWrapper title="Toggle annotation value">
<div
className={`${className}${isVisible ? "" : " cursor-pointer"}`}
onClick={isVisible ? undefined : () => setIsVisible(!isVisible)}
>
{isVisible ? (
<React.Fragment>
<span
onClick={() => setIsVisible(false)}
className="cursor-pointer"
>
<FontAwesomeIcon icon={faSearchMinus} className="mr-1" />
<span className="text-muted">{name}: </span>
</span>
<Linkify
properties={{
target: "_blank",
rel: "noopener noreferrer",
}}
>
<CSSTransition {...props}>
<span ref={ref}>{value}</span>
</CSSTransition>
</Linkify>
</React.Fragment>
) : (
<React.Fragment>
<FontAwesomeIcon icon={faSearchPlus} className="mr-1" />
{name}
</React.Fragment>
)}
</div>
</TooltipWrapper>
);
@@ -28,7 +28,7 @@ exports[`<GroupFooter /> matches snapshot 1`] = `
</span>
</span>
<span class=\\"Linkify\\">
<span style=\\"opacity: 1;\\">
<span>
This is summary
</span>
</span>
@@ -157,7 +157,7 @@ exports[`<GroupFooter /> mathes snapshot when silence is rendered 1`] = `
</span>
</span>
<span class=\\"Linkify\\">
<span style=\\"opacity: 1;\\">
<span>
This is summary
</span>
</span>
@@ -255,9 +255,7 @@ exports[`<GroupFooter /> mathes snapshot when silence is rendered 1`] = `
link
</a>
<div class=\\"components-grid-alertgrid-alertgroup-shared-silence rounded-0 border-0\\">
<div class=\\"react-reveal card my-1 components-managed-silence\\"
style=\\"opacity: 1;\\"
>
<div class=\\"card my-1 components-managed-silence\\">
<div class=\\"card-header rounded-0 border-bottom-0 px-3\\">
<div class=\\"d-flex flex-row\\">
<div class=\\"flex-shrink-0 flex-grow-0\\">
@@ -1,10 +1,8 @@
import React, { useEffect, useCallback, useRef, useState } from "react";
import React, { useEffect, useCallback, useState } from "react";
import PropTypes from "prop-types";
import { useObserver } from "mobx-react-lite";
import { Fade } from "react-reveal";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus";
import { faMinus } from "@fortawesome/free-solid-svg-icons/faMinus";
@@ -15,7 +13,6 @@ import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { BackgroundClassMap } from "Common/Colors";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { ThemeContext } from "Components/Theme";
import { GroupHeader } from "./GroupHeader";
import { Alert } from "./Alert";
import { GroupFooter } from "./GroupFooter";
@@ -158,20 +155,9 @@ const AlertGroup = ({
}
}
const context = React.useContext(ThemeContext);
const mountRef = useRef(null);
const [fadeDone, setFadeDone] = useState(false);
return useObserver(() => (
<div
ref={mountRef}
className={`components-grid-alertgrid-alertgroup ${
mountRef.current && fadeDone
? "components-animation-fade-appear-done"
: ""
}`}
className="components-grid-alertgrid-alertgroup"
style={{
width: groupWidth,
zIndex: isMenuOpen ? 100 : null,
@@ -180,82 +166,73 @@ const AlertGroup = ({
settingsStore.alertGroupConfig.config.defaultRenderCount
}
>
<Fade
in={context.animations.in}
duration={context.animations.duration}
wait={context.animations.duration}
onReveal={() => setFadeDone(true)}
<div
className={`card ${cardBackgroundClass}`}
data-colortitlebar={settingsStore.alertGroupConfig.config.colorTitleBar}
>
<div
className={`card ${cardBackgroundClass}`}
data-colortitlebar={
settingsStore.alertGroupConfig.config.colorTitleBar
}
>
<GroupHeader
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
<GroupHeader
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
group={group}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
themedCounters={themedCounters}
setIsMenuOpen={setIsMenuOpen}
gridLabelValue={gridLabelValue}
/>
{isCollapsed ? null : (
<div className="card-body px-2 py-1 components-grid-alertgrid-card">
<ul className="list-group">
{group.alerts.slice(0, alertsToRender).map((alert) => (
<Alert
key={alert.id}
group={group}
alert={alert}
showAlertmanagers={
showAlertmanagers && !showAlertmanagersInFooter
}
showReceiver={
alertStore.data.receivers.length > 1 &&
group.alerts.length === 1
}
afterUpdate={afterUpdate}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
setIsMenuOpen={setIsMenuOpen}
/>
))}
{group.alerts.length > defaultRenderCount ? (
<li className="list-group-item border-0 p-0 text-center bg-transparent">
<LoadButton
icon={faMinus}
action={loadLess}
tooltip="Show fewer alerts in this group"
/>
<small className="text-muted mx-2">
{Math.min(alertsToRender, group.alerts.length)}
{" of "}
{group.alerts.length}
</small>
<LoadButton
icon={faPlus}
action={loadMore}
tooltip="Show more alerts in this group"
/>
</li>
) : null}
</ul>
</div>
)}
{isCollapsed === false && group.alerts.length > 1 ? (
<GroupFooter
group={group}
alertmanagers={footerAlertmanagers}
afterUpdate={afterUpdate}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
themedCounters={themedCounters}
setIsMenuOpen={setIsMenuOpen}
gridLabelValue={gridLabelValue}
/>
{isCollapsed ? null : (
<div className="card-body px-2 py-1 components-grid-alertgrid-card">
<ul className="list-group">
{group.alerts.slice(0, alertsToRender).map((alert) => (
<Alert
key={alert.id}
group={group}
alert={alert}
showAlertmanagers={
showAlertmanagers && !showAlertmanagersInFooter
}
showReceiver={
alertStore.data.receivers.length > 1 &&
group.alerts.length === 1
}
afterUpdate={afterUpdate}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
setIsMenuOpen={setIsMenuOpen}
/>
))}
{group.alerts.length > defaultRenderCount ? (
<li className="list-group-item border-0 p-0 text-center bg-transparent">
<LoadButton
icon={faMinus}
action={loadLess}
tooltip="Show fewer alerts in this group"
/>
<small className="text-muted mx-2">
{Math.min(alertsToRender, group.alerts.length)}
{" of "}
{group.alerts.length}
</small>
<LoadButton
icon={faPlus}
action={loadMore}
tooltip="Show more alerts in this group"
/>
</li>
) : null}
</ul>
</div>
)}
{isCollapsed === false && group.alerts.length > 1 ? (
<GroupFooter
group={group}
alertmanagers={footerAlertmanagers}
afterUpdate={afterUpdate}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
) : null}
</div>
</Fade>
) : null}
</div>
</div>
));
};
@@ -102,24 +102,6 @@ describe("<AlertGroup />", () => {
tree.unmount();
});
it("appends components-animation-fade-appear-done class after first mount", () => {
MockAlerts(5);
const tree = MountedAlertGroup(jest.fn(), true);
expect(
tree
.find("div.components-grid-alertgrid-alertgroup")
.hasClass("components-animation-fade-appear-done")
).toBe(false);
tree.setProps({});
act(() => tree.find("RevealBase").at(0).props().onReveal());
tree.update();
expect(
tree
.find("div.components-grid-alertgrid-alertgroup")
.hasClass("components-animation-fade-appear-done")
).toBe(true);
});
it("renders Alertmanager cluster labels in footer if showAlertmanagersInFooter=true", () => {
MockAlerts(2);
const tree = MountedAlertGroup(jest.fn(), true).find("AlertGroup");
+62 -45
View File
@@ -5,7 +5,8 @@ import { useObserver } from "mobx-react-lite";
import debounce from "lodash.debounce";
import { Fade } from "react-reveal";
import TransitionGroup from "react-transition-group/TransitionGroup";
import { CSSTransition } from "react-transition-group";
import FontFaceObserver from "fontfaceobserver";
@@ -31,6 +32,7 @@ const Grid = ({
grid,
outerPadding,
}) => {
const context = React.useContext(ThemeContext);
const { ref, repack } = useGrid(gridSizesConfig);
const debouncedRepack = useCallback(debounce(repack, 10), [repack]);
@@ -93,18 +95,22 @@ const Grid = ({
}
}, [grid.alertGroups.length, groupsToRender]);
const context = React.useContext(ThemeContext);
return useObserver(() => (
<React.Fragment>
{grid.labelName !== "" && (
<CSSTransition
key={grid.labelValue}
in={grid.labelName !== ""}
classNames="components-animation-fade"
timeout={context.animations.duration}
unmountOnExit
>
<Swimlane
alertStore={alertStore}
grid={grid}
isExpanded={isExpanded}
onToggle={onCollapseClick}
/>
)}
</CSSTransition>
<div
className="components-grid"
ref={ref}
@@ -114,49 +120,60 @@ const Grid = ({
paddingRight: outerPadding + "px",
}}
>
{isExpanded || grid.labelName === ""
? grid.alertGroups
.slice(0, groupsToRender)
.map((group) => (
<AlertGroup
<TransitionGroup component={null} appear enter exit>
{isExpanded || grid.labelName === ""
? grid.alertGroups.slice(0, groupsToRender).map((group) => (
<CSSTransition
key={group.id}
group={group}
showAlertmanagers={
Object.keys(alertStore.data.upstreams.clusters).length > 1
}
afterUpdate={debouncedRepack}
alertStore={alertStore}
settingsStore={settingsStore}
silenceFormStore={silenceFormStore}
groupWidth={groupWidth}
gridLabelValue={grid.labelValue}
/>
classNames="components-animation-fade"
timeout={context.animations.duration}
onEntering={repack}
onExited={debouncedRepack}
unmountOnExit
>
<AlertGroup
group={group}
showAlertmanagers={
Object.keys(alertStore.data.upstreams.clusters).length > 1
}
afterUpdate={debouncedRepack}
alertStore={alertStore}
settingsStore={settingsStore}
silenceFormStore={silenceFormStore}
groupWidth={groupWidth}
gridLabelValue={grid.labelValue}
/>
</CSSTransition>
))
: []}
: []}
</TransitionGroup>
</div>
{isExpanded && grid.alertGroups.length > groupsToRender && (
<div className="d-flex flex-row justify-content-between">
<div className="flex-shrink-1 flex-grow-1 text-center">
<Fade
in={context.animations.in}
duration={context.animations.duration}
>
<button
type="button"
className="btn btn-secondary mb-3"
onClick={() =>
setGroupsToRender(
Math.min(groupsToRender + 30, grid.alertGroups.length)
)
}
>
<FontAwesomeIcon className="mr-2" icon={faAngleDoubleDown} />
Load more
</button>
</Fade>
</div>
</div>
)}
<TransitionGroup component={null} enter exit>
{isExpanded && grid.alertGroups.length > groupsToRender && (
<CSSTransition
classNames="components-animation-fade"
timeout={context.animations.duration}
unmountOnExit
>
<div className="d-flex flex-row justify-content-between">
<div className="flex-shrink-1 flex-grow-1 text-center">
<button
type="button"
className="btn btn-secondary mb-3"
onClick={() =>
setGroupsToRender(
Math.min(groupsToRender + 30, grid.alertGroups.length)
)
}
>
<FontAwesomeIcon className="mr-2" icon={faAngleDoubleDown} />
Load more
</button>
</div>
</div>
</CSSTransition>
)}
</TransitionGroup>
</React.Fragment>
));
};
+44 -51
View File
@@ -1,8 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import { Fade } from "react-reveal";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTh } from "@fortawesome/free-solid-svg-icons/faTh";
@@ -12,62 +10,57 @@ import { FilteringLabel } from "Components/Labels/FilteringLabel";
import { FilteringCounterBadge } from "Components/Labels/FilteringCounterBadge";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { ToggleIcon } from "Components/ToggleIcon";
import { ThemeContext } from "Components/Theme";
const Swimlane = ({ alertStore, grid, isExpanded, onToggle }) => {
const context = React.useContext(ThemeContext);
return (
<Fade in={context.animations.in} duration={context.animations.duration}>
<h5 className="components-grid-swimlane d-flex flex-row justify-content-between rounded px-2 py-1 mt-2 mb-0 border border-dark">
<span className="flex-shrink-0 flex-grow-0">
<span className="badge components-label px-0 ml-1 mr-3">
<FontAwesomeIcon icon={faTh} className="text-muted" />
</span>
<h5 className="components-grid-swimlane d-flex flex-row justify-content-between rounded px-2 py-1 mt-2 mb-0 border border-dark">
<span className="flex-shrink-0 flex-grow-0">
<span className="badge components-label px-0 ml-1 mr-3">
<FontAwesomeIcon icon={faTh} className="text-muted" />
</span>
<span className="flex-shrink-1 flex-grow-1" style={{ minWidth: "0px" }}>
{grid.labelName !== "" && grid.labelValue !== "" && (
<FilteringLabel
key={grid.labelValue}
name={grid.labelName}
value={grid.labelValue}
alertStore={alertStore}
/>
)}
</span>
<span className="flex-shrink-0 flex-grow-0 ml-2 mr-0">
<FilteringCounterBadge
name="@state"
value="unprocessed"
counter={grid.stateCount.unprocessed}
themed={true}
</span>
<span className="flex-shrink-1 flex-grow-1" style={{ minWidth: "0px" }}>
{grid.labelName !== "" && grid.labelValue !== "" && (
<FilteringLabel
key={grid.labelValue}
name={grid.labelName}
value={grid.labelValue}
alertStore={alertStore}
/>
<FilteringCounterBadge
name="@state"
value="suppressed"
counter={grid.stateCount.suppressed}
themed={true}
alertStore={alertStore}
/>
<FilteringCounterBadge
name="@state"
value="active"
counter={grid.stateCount.active}
themed={true}
alertStore={alertStore}
/>
<span
className="text-muted cursor-pointer badge px-0 components-label ml-2 mr-1"
onClick={onToggle}
>
<TooltipWrapper title="Click to toggle this grid details or Alt+Click to toggle all grids">
<ToggleIcon isOpen={isExpanded} />
</TooltipWrapper>
</span>
)}
</span>
<span className="flex-shrink-0 flex-grow-0 ml-2 mr-0">
<FilteringCounterBadge
name="@state"
value="unprocessed"
counter={grid.stateCount.unprocessed}
themed={true}
alertStore={alertStore}
/>
<FilteringCounterBadge
name="@state"
value="suppressed"
counter={grid.stateCount.suppressed}
themed={true}
alertStore={alertStore}
/>
<FilteringCounterBadge
name="@state"
value="active"
counter={grid.stateCount.active}
themed={true}
alertStore={alertStore}
/>
<span
className="text-muted cursor-pointer badge px-0 components-label ml-2 mr-1"
onClick={onToggle}
>
<TooltipWrapper title="Click to toggle this grid details or Alt+Click to toggle all grids">
<ToggleIcon isOpen={isExpanded} />
</TooltipWrapper>
</span>
</h5>
</Fade>
</span>
</h5>
);
};
Swimlane.propTypes = {
+12 -4
View File
@@ -157,14 +157,14 @@ const MockGroupList = (count, alertPerGroup) => {
describe("<Grid />", () => {
it("renders only first 50 alert groups", () => {
MockGroupList(60, 5);
MockGroupList(55, 5);
const tree = MountedGrid();
const alertGroups = tree.find("AlertGroup");
expect(alertGroups).toHaveLength(50);
});
it("appends 30 groups after clicking 'Load More' button", () => {
MockGroupList(100, 5);
MockGroupList(85, 5);
const tree = MountedGrid();
tree.find("button").simulate("click");
const alertGroups = tree.find("AlertGroup");
@@ -186,6 +186,8 @@ describe("<Grid />", () => {
});
it("click on the grid toggle toggles all groups", () => {
jest.useFakeTimers();
MockGroupList(10, 3);
const tree = MountedGrid();
const grid = MockGrid();
@@ -201,12 +203,18 @@ describe("<Grid />", () => {
expect(tree.find("AlertGroup")).toHaveLength(10);
tree.find("span.cursor-pointer").at(0).simulate("click");
act(() => jest.runOnlyPendingTimers());
tree.update();
expect(tree.find("AlertGroup")).toHaveLength(0);
expect(tree.find("div.components-grid-alertgrid-alertgroup")).toHaveLength(
0
);
tree.find("span.cursor-pointer").at(0).simulate("click");
act(() => jest.runOnlyPendingTimers());
tree.update();
expect(tree.find("AlertGroup")).toHaveLength(10);
expect(tree.find("div.components-grid-alertgrid-alertgroup")).toHaveLength(
10
);
});
it("renders filter badge for grids with a value", () => {
@@ -3,22 +3,20 @@
exports[`<EmptyGrid /> matches snapshot 1`] = `
"
<h1 class=\\"display-1 text-placeholder screen-center\\">
<div class=\\"react-reveal\\">
<svg aria-hidden=\\"true\\"
focusable=\\"false\\"
data-prefix=\\"fas\\"
data-icon=\\"mug-hot\\"
class=\\"svg-inline--fa fa-mug-hot fa-w-16 screen-center-icon-big text-placeholder\\"
role=\\"img\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
viewbox=\\"0 0 512 512\\"
<svg aria-hidden=\\"true\\"
focusable=\\"false\\"
data-prefix=\\"fas\\"
data-icon=\\"mug-hot\\"
class=\\"svg-inline--fa fa-mug-hot fa-w-16 screen-center-icon-big text-placeholder\\"
role=\\"img\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
viewbox=\\"0 0 512 512\\"
>
<path fill=\\"currentColor\\"
d=\\"M127.1 146.5c1.3 7.7 8 13.5 16 13.5h16.5c9.8 0 17.6-8.5 16.3-18-3.8-28.2-16.4-54.2-36.6-74.7-14.4-14.7-23.6-33.3-26.4-53.5C111.8 5.9 105 0 96.8 0H80.4C70.6 0 63 8.5 64.1 18c3.9 31.9 18 61.3 40.6 84.4 12 12.2 19.7 27.5 22.4 44.1zm112 0c1.3 7.7 8 13.5 16 13.5h16.5c9.8 0 17.6-8.5 16.3-18-3.8-28.2-16.4-54.2-36.6-74.7-14.4-14.7-23.6-33.3-26.4-53.5C223.8 5.9 217 0 208.8 0h-16.4c-9.8 0-17.5 8.5-16.3 18 3.9 31.9 18 61.3 40.6 84.4 12 12.2 19.7 27.5 22.4 44.1zM400 192H32c-17.7 0-32 14.3-32 32v192c0 53 43 96 96 96h192c53 0 96-43 96-96h16c61.8 0 112-50.2 112-112s-50.2-112-112-112zm0 160h-16v-96h16c26.5 0 48 21.5 48 48s-21.5 48-48 48z\\"
>
<path fill=\\"currentColor\\"
d=\\"M127.1 146.5c1.3 7.7 8 13.5 16 13.5h16.5c9.8 0 17.6-8.5 16.3-18-3.8-28.2-16.4-54.2-36.6-74.7-14.4-14.7-23.6-33.3-26.4-53.5C111.8 5.9 105 0 96.8 0H80.4C70.6 0 63 8.5 64.1 18c3.9 31.9 18 61.3 40.6 84.4 12 12.2 19.7 27.5 22.4 44.1zm112 0c1.3 7.7 8 13.5 16 13.5h16.5c9.8 0 17.6-8.5 16.3-18-3.8-28.2-16.4-54.2-36.6-74.7-14.4-14.7-23.6-33.3-26.4-53.5C223.8 5.9 217 0 208.8 0h-16.4c-9.8 0-17.5 8.5-16.3 18 3.9 31.9 18 61.3 40.6 84.4 12 12.2 19.7 27.5 22.4 44.1zM400 192H32c-17.7 0-32 14.3-32 32v192c0 53 43 96 96 96h192c53 0 96-43 96-96h16c61.8 0 112-50.2 112-112s-50.2-112-112-112zm0 160h-16v-96h16c26.5 0 48 21.5 48 48s-21.5 48-48 48z\\"
>
</path>
</svg>
</div>
</path>
</svg>
</h1>
"
`;
@@ -3,7 +3,7 @@
exports[`<FatalError /> matches snapshot 1`] = `
"
<h1 class=\\"display-1 text-placeholder screen-center\\">
<div class=\\"react-reveal container-fluid text-center\\">
<div class=\\"container-fluid text-center\\">
<svg aria-hidden=\\"true\\"
focusable=\\"false\\"
data-prefix=\\"fas\\"
@@ -3,7 +3,7 @@
exports[`<ReloadNeeded /> matches snapshot 1`] = `
"
<h1 class=\\"display-1 text-placeholder screen-center\\">
<div class=\\"react-reveal container-fluid text-center\\">
<div class=\\"container-fluid text-center\\">
<svg aria-hidden=\\"true\\"
focusable=\\"false\\"
data-prefix=\\"fas\\"
@@ -3,7 +3,7 @@
exports[`<UpgradeNeeded /> matches snapshot 1`] = `
"
<h1 class=\\"display-1 text-placeholder screen-center\\">
<div class=\\"react-reveal container-fluid text-center\\">
<div class=\\"container-fluid text-center\\">
<div class=\\"shake-slow shake-constant mb-4\\">
<svg aria-hidden=\\"true\\"
focusable=\\"false\\"
@@ -3,12 +3,13 @@ import PropTypes from "prop-types";
import { observer } from "mobx-react-lite";
import Flash from "react-reveal/Flash";
import { CSSTransition } from "react-transition-group";
import { AlertStore } from "Stores/AlertStore";
import { QueryOperators, FormatQuery } from "Common/Query";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { GetClassAndStyle } from "Components/Labels/Utils";
import { useFlashTransition } from "Hooks/useFlashTransition";
// Same as FilteringLabel but for labels that are counters (usually @state)
// and only renders a pill badge with the counter, it doesn't render anything
@@ -23,6 +24,8 @@ const FilteringCounterBadge = observer(
alwaysVisible,
defaultColor,
}) => {
const { ref, props } = useFlashTransition(counter);
const handleClick = useCallback(
(event) => {
// left click => apply foo=bar filter
@@ -52,8 +55,9 @@ const FilteringCounterBadge = observer(
<TooltipWrapper
title={`Click to only show ${name}=${value} alerts or Alt+Click to hide them`}
>
<Flash spy={counter}>
<CSSTransition {...props}>
<span
ref={ref}
className={
themed
? cs.className
@@ -68,7 +72,7 @@ const FilteringCounterBadge = observer(
>
{counter}
</span>
</Flash>
</CSSTransition>
</TooltipWrapper>
);
}
@@ -35,8 +35,7 @@ const validateStyle = (value, themed) => {
themed={themed}
/>
);
// opacity=>1 is set by react-reveal/Flash
expect(tree.find("span").prop("style")).toEqual({ opacity: 1 });
expect(tree.find("span").prop("style")).toEqual({});
};
const validateOnClick = (value, themed, isNegative) => {
@@ -5,7 +5,7 @@ import parseISO from "date-fns/parseISO";
import copy from "copy-to-clipboard";
import Flash from "react-reveal/Flash";
import { CSSTransition } from "react-transition-group";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEdit } from "@fortawesome/free-solid-svg-icons/faEdit";
@@ -22,14 +22,18 @@ import { QueryOperators } from "Common/Query";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { RenderLinkAnnotation } from "Components/Grid/AlertGrid/AlertGroup/Annotation";
import { DateFromNow } from "Components/DateFromNow";
import { useFlashTransition } from "Hooks/useFlashTransition";
import { DeleteSilence } from "./DeleteSilence";
const SilenceIDCopyButton = ({ id }) => {
const [clickCount, setClickCount] = useState(0);
const { ref, props } = useFlashTransition(clickCount);
return (
<TooltipWrapper title="Copy silence ID to the clipboard">
<Flash spy={clickCount} duration={500}>
<CSSTransition {...props}>
<span
ref={ref}
className="badge badge-secondary px-1 mr-1 components-label cursor-pointer"
onClick={() => {
copy(id);
@@ -38,7 +42,7 @@ const SilenceIDCopyButton = ({ id }) => {
>
<FontAwesomeIcon icon={faCopy} />
</span>
</Flash>
</CSSTransition>
</TooltipWrapper>
);
};
@@ -47,9 +47,7 @@ exports[`<SilenceComment /> Matches snapshot when collapsed 1`] = `
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge-primary badge-pill components-label-with-hover components-label badge\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge-primary badge-pill components-label-with-hover components-label badge\\">
123
</span>
</div>
@@ -124,9 +122,7 @@ exports[`<SilenceComment /> Matches snapshot when collapsed and multiple cluster
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge-primary badge-pill components-label-with-hover components-label badge\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge-primary badge-pill components-label-with-hover components-label badge\\">
123
</span>
</div>
@@ -185,9 +181,7 @@ exports[`<SilenceComment /> Matches snapshot when collapsed and multiple cluster
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge-primary badge-pill components-label-with-hover components-label badge\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge-primary badge-pill components-label-with-hover components-label badge\\">
123
</span>
</div>
@@ -246,9 +240,7 @@ exports[`<SilenceComment /> Matches snapshot when expanded 1`] = `
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge-primary badge-pill components-label-with-hover components-label badge\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge-primary badge-pill components-label-with-hover components-label badge\\">
123
</span>
</div>
@@ -2,9 +2,7 @@
exports[`<ManagedSilence /> matches snapshot when collapsed 1`] = `
"
<div class=\\"react-reveal card my-1 components-managed-silence\\"
style=\\"opacity: 1;\\"
>
<div class=\\"card my-1 components-managed-silence\\">
<div class=\\"card-header rounded-0 border-bottom-0 px-3\\">
<div class=\\"d-flex flex-row\\">
<div class=\\"flex-shrink-0 flex-grow-0\\">
@@ -51,9 +49,7 @@ exports[`<ManagedSilence /> matches snapshot when collapsed 1`] = `
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge-primary badge-pill components-label-with-hover components-label badge\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge-primary badge-pill components-label-with-hover components-label badge\\">
123
</span>
</div>
@@ -82,9 +78,7 @@ exports[`<ManagedSilence /> matches snapshot when collapsed 1`] = `
exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
"
<div class=\\"react-reveal card my-1 components-managed-silence\\"
style=\\"opacity: 1;\\"
>
<div class=\\"card my-1 components-managed-silence\\">
<div class=\\"card-header rounded-0 border-bottom-0 px-3\\">
<div class=\\"d-flex flex-row\\">
<div class=\\"flex-shrink-0 flex-grow-0\\">
@@ -118,9 +112,7 @@ exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge-primary badge-pill components-label-with-hover components-label badge\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge-primary badge-pill components-label-with-hover components-label badge\\">
123
</span>
</div>
@@ -207,9 +199,7 @@ exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
<div style=\\"display: inline-block; max-width: 100%;\\"
class=\\" tooltip-trigger\\"
>
<span class=\\" badge badge-secondary px-1 mr-1 components-label cursor-pointer\\"
style=\\"opacity: 1;\\"
>
<span class=\\"badge badge-secondary px-1 mr-1 components-label cursor-pointer\\">
<svg aria-hidden=\\"true\\"
focusable=\\"false\\"
data-prefix=\\"fas\\"
+21 -29
View File
@@ -1,12 +1,9 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { Fade } from "react-reveal";
import { APISilence } from "Models/API";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore, SilenceTabNames } from "Stores/SilenceFormStore";
import { ThemeContext } from "Components/Theme";
import { SilenceComment } from "./SilenceComment";
import { SilenceDetails } from "./SilenceDetails";
@@ -41,37 +38,32 @@ const ManagedSilence = ({
silenceFormStore.toggle.show();
};
const context = React.useContext(ThemeContext);
return (
<Fade in={context.animations.in} duration={context.animations.duration}>
<div className="card my-1 components-managed-silence">
<div className="card-header rounded-0 border-bottom-0 px-3">
<SilenceComment
alertStore={alertStore}
<div className="card my-1 components-managed-silence">
<div className="card-header rounded-0 border-bottom-0 px-3">
<SilenceComment
alertStore={alertStore}
cluster={cluster}
silence={silence}
alertCount={alertCount}
alertCountAlwaysVisible={alertCountAlwaysVisible}
collapsed={!showDetails}
collapseToggle={() => setShowDetails(!showDetails)}
/>
</div>
{showDetails ? (
<div className="card-body pt-0">
<SilenceDetails
cluster={cluster}
silence={silence}
alertCount={alertCount}
alertCountAlwaysVisible={alertCountAlwaysVisible}
collapsed={!showDetails}
collapseToggle={() => setShowDetails(!showDetails)}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
onEditSilence={onEditSilence}
isUpper={isNested}
/>
</div>
{showDetails ? (
<div className="card-body pt-0">
<SilenceDetails
cluster={cluster}
silence={silence}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
onEditSilence={onEditSilence}
isUpper={isNested}
/>
</div>
) : null}
</div>
</Fade>
) : null}
</div>
);
};
ManagedSilence.propTypes = {
+11 -3
View File
@@ -8,7 +8,7 @@ import ReactResizeDetector from "react-resize-detector";
import IdleTimer from "react-idle-timer";
import { Fade } from "react-reveal";
import { CSSTransition } from "react-transition-group";
import { AlertStore } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
@@ -96,7 +96,15 @@ const NavBar = ({ alertStore, settingsStore, silenceFormStore, fixedTop }) => {
timeout={IsMobile() ? MobileIdleTimeout : DesktopIdleTimeout}
>
<div className={`container p-0 m-0 mw-100 ${containerClass}`}>
<Fade top when={!isIdle}>
<CSSTransition
classNames="components-animation-navbar"
in={!isIdle}
timeout={context.animations.duration}
onEntering={() => {}}
onExited={() => {}}
enter
exit
>
<nav
className={`navbar navbar-expand navbar-dark p-1 bg-primary-transparent d-inline-block ${
fixedTop ? "fixed-top" : "w-100"
@@ -129,7 +137,7 @@ const NavBar = ({ alertStore, settingsStore, silenceFormStore, fixedTop }) => {
settingsStore={settingsStore}
/>
</nav>
</Fade>
</CSSTransition>
</div>
</IdleTimer>
));
+7 -3
View File
@@ -3,12 +3,13 @@ import PropTypes from "prop-types";
import { useObserver } from "mobx-react-lite";
import Flash from "react-reveal/Flash";
import { CSSTransition } from "react-transition-group";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner";
import { AlertStore } from "Stores/AlertStore";
import { useFlashTransition } from "Hooks/useFlashTransition";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { Modal } from "Components/Modal";
@@ -24,11 +25,14 @@ const OverviewModal = ({ alertStore }) => {
const toggle = useCallback(() => setIsVisible(!isVisible), [isVisible]);
const { ref, props } = useFlashTransition(alertStore.info.totalAlerts);
return useObserver(() => (
<React.Fragment>
<TooltipWrapper title="Show alert overview">
<Flash spy={alertStore.info.totalAlerts}>
<CSSTransition {...props}>
<div
ref={ref}
className={`text-center d-inline-block cursor-pointer navbar-brand m-0 components-navbar-button ${
isVisible ? "border-info" : ""
}`}
@@ -36,7 +40,7 @@ const OverviewModal = ({ alertStore }) => {
>
{alertStore.info.totalAlerts}
</div>
</Flash>
</CSSTransition>
</TooltipWrapper>
<Modal size="xl" isOpen={isVisible} toggleOpen={toggle}>
<React.Suspense
@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import { useObserver } from "mobx-react-lite";
import { Fade } from "react-reveal";
import { CSSTransition } from "react-transition-group";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner";
@@ -34,14 +34,19 @@ FetchError.propTypes = {
};
const Placeholder = ({ content }) => {
const theme = React.useContext(ThemeContext);
const context = React.useContext(ThemeContext);
return (
<Fade in={theme.animations.in} duration={theme.animations.duration}>
<CSSTransition
in={true}
appear={true}
classNames="components-animation-fade"
timeout={context.animations.duration}
>
<div className="jumbotron bg-transparent">
<h1 className="display-5 text-placeholder text-center">{content}</h1>
</div>
</Fade>
</CSSTransition>
);
};
Placeholder.propTypes = {
@@ -3,10 +3,10 @@ import PropTypes from "prop-types";
import { useObserver } from "mobx-react-lite";
import Flash from "react-reveal/Flash";
import copy from "copy-to-clipboard";
import { CSSTransition } from "react-transition-group";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus";
import { faUser } from "@fortawesome/free-solid-svg-icons/faUser";
@@ -26,6 +26,7 @@ import {
} from "Stores/SilenceFormStore";
import { Settings } from "Stores/Settings";
import { QueryOperators } from "Common/Query";
import { useFlashTransition } from "Hooks/useFlashTransition";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { ToggleIcon } from "Components/ToggleIcon";
import { AlertManagerInput } from "./AlertManagerInput";
@@ -44,6 +45,8 @@ const ShareButton = ({ silenceFormStore }) => {
window.location.pathname,
].join("");
const { ref, props } = useFlashTransition(clickCount);
return useObserver(() => (
<div className="input-group mb-3">
<div className="input-group-prepend">
@@ -59,7 +62,7 @@ const ShareButton = ({ silenceFormStore }) => {
value={`${baseURL}?m=${silenceFormStore.data.toBase64}`}
onChange={() => {}}
/>
<div className="input-group-append">
<div ref={ref} className="input-group-append">
<span
className="input-group-text cursor-pointer"
onClick={() => {
@@ -68,9 +71,9 @@ const ShareButton = ({ silenceFormStore }) => {
}}
>
<TooltipWrapper title="Copy to clipboard">
<Flash spy={clickCount} duration={500}>
<CSSTransition {...props}>
<FontAwesomeIcon icon={faCopy} />
</Flash>
</CSSTransition>
</TooltipWrapper>
</span>
</div>
+40
View File
@@ -0,0 +1,40 @@
import { useState, useEffect, useRef } from "react";
import { useInView } from "react-intersection-observer";
const defaultProps = {
in: false,
classNames: "components-animation-flash",
timeout: 800,
appear: false,
enter: false,
exit: false,
};
const useFlashTransition = (flashOn) => {
const mountRef = useRef(false);
const [ref, inView] = useInView();
const [isPending, setIsPending] = useState(false);
const [props, setProps] = useState(defaultProps);
useEffect(() => {
if (mountRef.current) {
setIsPending(true);
} else {
mountRef.current = true;
}
}, [flashOn]);
useEffect(() => {
setProps({
...defaultProps,
in: isPending && inView,
enter: isPending && inView,
onEntered: () => setIsPending(false),
});
}, [inView, isPending]);
return { ref, props };
};
export { useFlashTransition, defaultProps };
+97
View File
@@ -0,0 +1,97 @@
import { renderHook, act } from "@testing-library/react-hooks";
import { useInView } from "react-intersection-observer";
import { useFlashTransition, defaultProps } from "./useFlashTransition";
describe("useFlashTransition", () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
useInView.setInView(true);
});
it("does nothing when value changes but element is out of viewport", () => {
useInView.setInView(false);
let value = 0;
const { result, rerender } = renderHook(() => useFlashTransition(value));
expect(result.current.props).toMatchObject(defaultProps);
value = 1;
rerender();
expect(result.current.props).toMatchObject(defaultProps);
});
it("flashes when value changes and element is in viewport", () => {
useInView.setInView(true);
let value = 2;
const { result, rerender } = renderHook(() => useFlashTransition(value));
value = 3;
rerender();
expect(result.current.props).toMatchObject({
...defaultProps,
in: true,
enter: true,
});
});
it("flashes when value changes and element moves into viewport", () => {
useInView.setInView(false);
let value = 2;
const { result, rerender } = renderHook(() => useFlashTransition(value));
value = 3;
rerender();
expect(result.current.props).toMatchObject(defaultProps);
act(() => useInView.setInView(true));
rerender();
expect(result.current.props).toMatchObject({
...defaultProps,
in: true,
enter: true,
});
});
it("stops flashing props.onEntered is called", () => {
useInView.setInView(true);
let value = 2;
const { result, rerender } = renderHook(() => useFlashTransition(value));
value = 3;
rerender();
expect(result.current.props).toMatchObject({
...defaultProps,
in: true,
enter: true,
});
act(() => result.current.props.onEntered());
expect(result.current.props).toMatchObject(defaultProps);
});
it("unmounts cleanly when not flashing", () => {
useInView.setInView(false);
const { unmount } = renderHook(() => useFlashTransition(4));
unmount();
});
it("unmounts cleanly when flashing", () => {
useInView.setInView(false);
let value = 5;
const { rerender, unmount } = renderHook(() => useFlashTransition(value));
value = 6;
rerender();
unmount();
});
});
+7 -9
View File
@@ -1,14 +1,12 @@
.components-grid-alertgrid-alertgroup.components-animation-fade-appear-done {
will-change: transform;
transform: translateZ(0);
transition-property: transform;
transition-duration: 0.4s;
transition-timing-function: ease;
}
.components-grid-alertgrid-alertgroup {
padding: 0.3rem;
&.components-animation-fade-appear-done,
&.components-animation-fade-enter-done {
transition-property: transform;
transition-duration: 0.4s;
transition-timing-function: ease;
}
}
.components-grid-alertgrid-alertgroup-shared-silence {
+19
View File
@@ -0,0 +1,19 @@
$duration: 0.5s;
.components-animation-fade-appear,
.components-animation-fade-enter {
opacity: 0;
}
.components-animation-fade-appear-active,
.components-animation-fade-enter-active {
opacity: 1;
transition: opacity $duration ease-in;
}
.components-animation-fade-exit {
opacity: 1;
}
.components-animation-fade-exit-active {
opacity: 0.01;
transition: opacity $duration ease-out;
}
+26
View File
@@ -0,0 +1,26 @@
$duration: 800ms;
.components-animation-flash-appear,
.components-animation-flash-enter {
animation-name: flash;
animation-duration: $duration;
animation-timing-function: ease-in-out;
}
@keyframes flash {
0% {
opacity: 1;
}
25% {
opacity: 0;
}
50% {
opacity: 1;
}
75% {
opacity: 0;
}
100% {
opacity: 1;
}
}
+23
View File
@@ -0,0 +1,23 @@
$duration: 0.5s;
.components-animation-navbar-appear,
.components-animation-navbar-enter {
opacity: 0.01;
transform: translateY(-100%);
}
.components-animation-navbar-appear-active,
.components-animation-navbar-enter-active {
opacity: 1;
transform: translateY(0);
transition: opacity $duration ease-out, transform $duration ease-out;
}
.components-animation-navbar-exit {
opacity: 1;
transform: translateY(0);
}
.components-animation-navbar-exit-active {
opacity: 0.01;
transform: translateY(-100%);
transition: opacity $duration ease-out, transform $duration ease-out;
}
+3
View File
@@ -98,6 +98,7 @@ $color-default: #708090;
@import "Styles/Components/AlertGroup";
@import "Styles/Components/BaseLabel";
@import "Styles/Components/CenteredMessage";
@import "Styles/Components/Fade";
@import "Styles/Components/FilterInputLabel";
@import "Styles/Components/LabelWithPercent";
@import "Styles/Components/InputRange";
@@ -105,6 +106,7 @@ $color-default: #708090;
@import "Styles/Components/Modal";
@import "Styles/Components/NavBar";
@import "Styles/Components/FilterInput";
@import "Styles/Components/Flash";
@import "Styles/Components/DateTimeSelect";
@import "Styles/Components/MountModal";
@import "Styles/Components/Placeholder";
@@ -113,6 +115,7 @@ $color-default: #708090;
@import "Styles/Components/DropdownSlide";
@import "Styles/Components/History";
@import "Styles/Components/HistoryLabel";
@import "Styles/Components/NavBarSlide";
@import "Styles/Components/SilenceModal";
@import "Styles/Components/Pagination";
@import "Styles/Components/Tooltip";
+3
View File
@@ -80,6 +80,7 @@ $color-default: #708090;
@import "Styles/Components/AlertGroup";
@import "Styles/Components/BaseLabel";
@import "Styles/Components/CenteredMessage";
@import "Styles/Components/Fade";
@import "Styles/Components/FilterInputLabel";
@import "Styles/Components/LabelWithPercent";
@import "Styles/Components/InputRange";
@@ -87,6 +88,7 @@ $color-default: #708090;
@import "Styles/Components/Modal";
@import "Styles/Components/NavBar";
@import "Styles/Components/FilterInput";
@import "Styles/Components/Flash";
@import "Styles/Components/DateTimeSelect";
@import "Styles/Components/MountModal";
@import "Styles/Components/Placeholder";
@@ -95,6 +97,7 @@ $color-default: #708090;
@import "Styles/Components/DropdownSlide";
@import "Styles/Components/History";
@import "Styles/Components/HistoryLabel";
@import "Styles/Components/NavBarSlide";
@import "Styles/Components/SilenceModal";
@import "Styles/Components/Pagination";
@import "Styles/Components/Tooltip";
-1
View File
@@ -5,7 +5,6 @@ import {
const MockThemeContext = {
animations: {
in: undefined,
duration: 500,
},
isDark: false,
+15
View File
@@ -0,0 +1,15 @@
import { useRef } from "react";
const mock = {
value: true,
};
const useInView = () => {
const ref = useRef(null);
return [ref, mock.value];
};
useInView.setInView = (val) => {
mock.value = val;
};
export { useInView };
+3
View File
@@ -4,6 +4,9 @@
import "react-app-polyfill/ie11";
import "react-app-polyfill/stable";
// https://www.npmjs.com/package/react-intersection-observer#polyfill
import "intersection-observer";
import React from "react";
import ReactDOM from "react-dom";
+1 -4
View File
@@ -20,10 +20,7 @@ for (const level of ["error", "warn", "info", "log", "trace"]) {
// https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#new-deprecations
const reactDeprecationWarning = /.*has been renamed, and is not recommended for use.*/;
global.console[level] = (message, ...args) => {
if (
reactDeprecationWarning.test(message) === false &&
message !== "react-reveal - animation failed"
) {
if (reactDeprecationWarning.test(message) === false) {
throw new Error(`message=${message} args=${args}`);
}
};