fix(ui): better navbar hiding when idle

Don't destroy navbar since modals are mounted on it + update visibility after transition so animations work as expected
This commit is contained in:
Łukasz Mierzwa
2018-09-23 12:23:29 +01:00
parent f41a302cd5
commit 843118176f
2 changed files with 70 additions and 25 deletions

View File

@@ -22,10 +22,6 @@ import "./index.css";
const DesktopIdleTimeout = 1000 * 60 * 3;
const MobileIdleTimeout = 1000 * 5;
const NavbarOnResize = function(width, height) {
document.body.style["padding-top"] = `${height + 4}px`;
};
const NavBar = observer(
class NavBar extends Component {
static propTypes = {
@@ -34,24 +30,63 @@ const NavBar = observer(
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
};
elementSize = observable(
{
width: 0,
height: 0,
setSize(width, height) {
this.width = width;
this.height = height;
}
},
{ setSize: action }
);
activityStatus = observable(
{
idle: false,
className: "visible",
setIdle() {
this.idle = true;
},
setActive() {
this.idle = false;
},
hide() {
this.className = "invisible";
},
show() {
this.className = "visible";
}
},
{
setIdle: action.bound,
setActive: action.bound
setActive: action.bound,
hide: action.bound,
show: action.bound
}
);
updateBodyPaddingTop = () => {
const paddingTop = this.activityStatus.idle
? 0
: this.elementSize.height + 4;
document.body.style["padding-top"] = `${paddingTop}px`;
};
onHide = () => {
NavbarOnResize(0, 0);
this.activityStatus.hide();
this.updateBodyPaddingTop();
};
onShow = () => {
this.updateBodyPaddingTop();
this.activityStatus.show();
};
onResize = (width, height) => {
this.elementSize.setSize(width, height);
this.updateBodyPaddingTop();
};
render() {
@@ -76,12 +111,12 @@ const NavBar = observer(
<DropdownSlide
in={!this.activityStatus.idle}
appear={false}
onEntered={this.onShow}
onExited={this.onHide}
unmountOnExit
>
<div className="container">
<div className={`container ${this.activityStatus.className}`}>
<nav className="navbar fixed-top navbar-expand navbar-dark p-1 bg-primary-transparent d-inline-block">
<ReactResizeDetector handleHeight onResize={NavbarOnResize} />
<ReactResizeDetector handleHeight onResize={this.onResize} />
<span className="navbar-brand my-0 mx-2 h1 d-none d-sm-block float-left">
{alertStore.info.totalAlerts}
<FetchIndicator alertStore={alertStore} />
@@ -110,4 +145,4 @@ const NavBar = observer(
}
);
export { NavBar, NavbarOnResize };
export { NavBar };

View File

@@ -7,7 +7,7 @@ import moment from "moment";
import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { NavBar, NavbarOnResize } from ".";
import { NavBar } from ".";
let alertStore;
let settingsStore;
@@ -85,18 +85,17 @@ describe("<NavBar />", () => {
it("navbar-nav includes 'flex-column' class with 3 filters", () => {
ValidateNavClass(3, "flex-column");
});
});
describe("NavbarOnResize()", () => {
it("body 'padding-top' style is updated after calling NavbarOnResize()", () => {
NavbarOnResize(0, 10);
const tree = MountedNavbar();
tree.instance().onResize(0, 10);
expect(
window
.getComputedStyle(document.body, null)
.getPropertyValue("padding-top")
).toBe("14px");
NavbarOnResize(0, 36);
tree.instance().onResize(0, 36);
expect(
window
.getComputedStyle(document.body, null)
@@ -110,31 +109,42 @@ describe("<IdleTimer />", () => {
jest.useFakeTimers();
});
it("hides navbar after 4 minutes", () => {
it("hides navbar after 5 seconds on mobile", () => {
global.window.innerWidth = 500;
const tree = MountedNavbar();
expect(tree.find(".navbar")).toHaveLength(1);
jest.runTimersToTime(1000 * 60 * 4);
jest.runTimersToTime(1000 * 6);
tree.update();
expect(tree.find(".navbar")).toHaveLength(0);
expect(tree.find(".container").hasClass("visible")).toBe(false);
expect(tree.find(".container").hasClass("invisible")).toBe(true);
});
it("hides navbar after 3 minutes on desktop", () => {
global.window.innerWidth = 769;
const tree = MountedNavbar();
jest.runTimersToTime(1000 * 60 * 3 + 1000);
tree.update();
expect(tree.find(".container").hasClass("visible")).toBe(false);
expect(tree.find(".container").hasClass("invisible")).toBe(true);
});
it("hidden navbar shows up again after activity", () => {
const tree = MountedNavbar();
const instance = tree.instance();
instance.activityStatus.idle = true;
instance.activityStatus.setIdle();
jest.runOnlyPendingTimers();
tree.update();
expect(tree.find(".navbar")).toHaveLength(0);
expect(tree.find(".container").hasClass("visible")).toBe(false);
expect(tree.find(".container").hasClass("invisible")).toBe(true);
instance.activityStatus.setActive();
jest.runOnlyPendingTimers();
tree.update();
expect(tree.find(".navbar")).toHaveLength(1);
expect(tree.find(".container").hasClass("visible")).toBe(true);
expect(tree.find(".container").hasClass("invisible")).toBe(false);
});
it("body padding-top is 4px when navbar is hidden", () => {
it("body padding-top is 0px when navbar is hidden", () => {
const tree = MountedNavbar();
const instance = tree.instance();
@@ -145,6 +155,6 @@ describe("<IdleTimer />", () => {
window
.getComputedStyle(document.body, null)
.getPropertyValue("padding-top")
).toBe("4px");
).toBe("0px");
});
});