Skip to content

Commit 6189634

Browse files
committed
♻️ refactoring code under the hood
1 parent e87d594 commit 6189634

File tree

14 files changed

+345
-298
lines changed

14 files changed

+345
-298
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,14 @@
4646
"crel": "^4.2.0",
4747
"crelns": "^1.0.1",
4848
"lodash": "^4.17.15"
49+
},
50+
"jest": {
51+
"moduleFileExtensions": [
52+
"js"
53+
],
54+
"moduleDirectories": [
55+
"node_modules",
56+
"<rootDir>"
57+
]
4958
}
5059
}

src/features/action-bar/action-bar.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import ReloadButton from "../action-bar/reload-button";
2-
import TrayButton from "../action-bar/tray-button";
1+
import ReloadButton from "src/features/action-bar/reload-button";
2+
import TrayButton from "src/features/action-bar/tray-button";
33
import crel from "crel";
44

55
export default function ActionBar({
6+
actions = [],
67
corner,
7-
showReload,
8-
showTray,
98
shouldRefreshCache,
109
trayIsOpen,
1110
onToggleTray = () => {}
@@ -15,7 +14,14 @@ export default function ActionBar({
1514
return crel(
1615
"div",
1716
{ id: "mde-action-bar", "data-corner": corner },
18-
showReload ? ReloadButton({ onClick: () => location.reload(shouldRefreshCache) }) : null,
19-
showTray ? TrayButton({ isActive: trayIsOpen, onClick: onToggleTray }) : null
17+
...actions.map(action => {
18+
if (action === "reload") {
19+
return ReloadButton({ onClick: () => location.reload(shouldRefreshCache) });
20+
}
21+
if (action === "toggle-tray") {
22+
return TrayButton({ isActive: trayIsOpen, onClick: onToggleTray });
23+
}
24+
return null;
25+
})
2026
);
2127
}

src/features/action-bar/action-bar.test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,31 @@ test("ActionBar renders properly", () => {
99
});
1010

1111
test("ActionBar shows reload button", () => {
12-
const actionBar = ActionBar({ showReload: true });
12+
const actionBar = ActionBar({ actions: ["reload"] });
1313

1414
expect(!!actionBar.querySelector("#mde-reload")).toBe(true);
1515
});
1616

1717
test("ActionBar shows tray button", () => {
18-
const actionBar = ActionBar({ showTray: true });
18+
const actionBar = ActionBar({ actions: ["toggle-tray"] });
1919

2020
expect(!!actionBar.querySelector("#mde-toggle-tray")).toBe(true);
2121
});
2222

2323
test("ActionBar tray button displays with `.active` class", () => {
24-
const actionBar = ActionBar({ showTray: true, trayIsOpen: true });
24+
const actionBar = ActionBar({ actions: ["toggle-tray"], trayIsOpen: true });
2525

2626
expect(!!actionBar.querySelector("#mde-toggle-tray.active")).toBe(true);
2727
});
2828

2929
test("ActionBar tray button displays without `.active` class", () => {
30-
const actionBar = ActionBar({ showTray: true });
30+
const actionBar = ActionBar({ actions: ["toggle-tray"] });
3131

3232
expect(!!actionBar.querySelector("#mde-toggle-tray.active")).toBe(false);
3333
});
3434

3535
test("ActionBar reloads page when ReloadButton clicked", () => {
36-
const actionBar = ActionBar({ showReload: true });
36+
const actionBar = ActionBar({ actions: ["reload"] });
3737
delete window.location;
3838
const reloadMock = jest.fn();
3939
window.location = { reload: reloadMock };
@@ -45,7 +45,7 @@ test("ActionBar reloads page when ReloadButton clicked", () => {
4545
});
4646

4747
test("ActionBar reloads page and cache when ReloadButton clicked", () => {
48-
const actionBar = ActionBar({ showReload: true, shouldRefreshCache: true });
48+
const actionBar = ActionBar({ actions: ["reload"], shouldRefreshCache: true });
4949
delete window.location;
5050
const reloadMock = jest.fn();
5151
window.location = { reload: reloadMock };
@@ -58,7 +58,7 @@ test("ActionBar reloads page and cache when ReloadButton clicked", () => {
5858

5959
test("ActionBar calls onToggleTray", () => {
6060
const onToggleTray = jest.fn();
61-
const actionBar = ActionBar({ showTray: true, onToggleTray });
61+
const actionBar = ActionBar({ actions: ["toggle-tray"], onToggleTray });
6262

6363
actionBar.querySelector("#mde-toggle-tray").click();
6464

src/features/action-bar/reload-button.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ReloadIcon from "../icons/reload";
1+
import ReloadIcon from "src/features/icons/reload";
22
import crel from "crel";
33

44
export default function ReloadButton({ onClick = () => {} } = {}) {

src/features/action-bar/tray-button.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import TrayIcon from "../icons/tray";
1+
import TrayIcon from "src/features/icons/tray";
22
import classnames from "classnames";
33
import crel from "crel";
44

src/features/app/app.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import ActionBar from "src/features/action-bar/action-bar";
2+
import Tray from "src/features/tray/tray";
3+
import crel from "crel";
4+
import limitTrayHeight from "src/utils/limit-tray-height";
5+
import stateHandler from "src/utils/state";
6+
import tracer from "src/utils/tracer.js";
7+
8+
export default function App({ root, stateId, actions, actionsCorner, hardReload }) {
9+
const state = stateHandler(stateId);
10+
11+
// Default tray height
12+
let trayHeight = state.getCache("tray-height", window.innerHeight * 0.25);
13+
trayHeight = limitTrayHeight(trayHeight, !!state.get("actions"));
14+
15+
// SET STATE
16+
17+
state.set("log", []);
18+
state.set("actions", actions);
19+
state.set("actions-corner", actionsCorner);
20+
state.set("reload-action-should-refresh-cache", hardReload);
21+
state.setCache("tray-open", state.getCache("tray-open", true));
22+
state.setCache("tray-height", trayHeight);
23+
24+
/**
25+
* ACTIONS
26+
*/
27+
28+
function toggleTray() {
29+
state.setCache("tray-open", !state.getCache("tray-open"));
30+
render();
31+
}
32+
33+
function resizeTray(offset) {
34+
const height = limitTrayHeight(state.getCache("tray-height") + offset, !!state.get("actions"));
35+
36+
if (height !== state.getCache("tray-height")) {
37+
state.setCache("tray-height", height);
38+
render();
39+
}
40+
}
41+
42+
function addLogEntry({ message, filePath, fileName, lineNumber, type } = {}) {
43+
const log = state.get("log");
44+
const entry = { message, filePath, fileName, lineNumber, type, amount: 1 };
45+
const lastEntry = log[log.length - 1];
46+
47+
if (lastEntry && lastEntry.message === message) {
48+
lastEntry.amount = lastEntry.amount + 1;
49+
log[log.length - 1] = lastEntry;
50+
} else {
51+
log.push(entry);
52+
}
53+
54+
state.set("log", log);
55+
render();
56+
}
57+
58+
// In the event the window is resized
59+
window.addEventListener("resize", () => {
60+
resizeTray(state.getCache("tray-height"));
61+
});
62+
63+
// Display logs
64+
const log = console.log;
65+
console.log = message => {
66+
log(message);
67+
const { filePath, fileName, lineNumber } = tracer(new Error());
68+
addLogEntry({ message, filePath, fileName, lineNumber });
69+
};
70+
const error = console.error;
71+
console.error = message => {
72+
error(message);
73+
const { filePath, fileName, lineNumber } = tracer(new Error());
74+
addLogEntry({ message, filePath, fileName, lineNumber, type: "error" });
75+
};
76+
const assert = console.assert;
77+
console.assert = (assertion, message) => {
78+
assert(assertion, message);
79+
80+
if (!assertion) {
81+
message =
82+
"Assertion failed: " + (typeof message !== "undefined" ? message : "console.assert");
83+
const { filePath, fileName, lineNumber } = tracer(new Error());
84+
addLogEntry({ message, filePath, fileName, lineNumber, type: "error" });
85+
}
86+
};
87+
88+
// Display error messages
89+
window.onerror = (message, filePath, lineNumber) => {
90+
addLogEntry({ message, filePath, fileName: "", lineNumber, type: "error" });
91+
};
92+
93+
// render
94+
95+
function render() {
96+
root.innerHTML = "";
97+
98+
// render action bar
99+
crel(
100+
root,
101+
ActionBar({
102+
actions,
103+
corner: state.get("actions-corner"),
104+
shouldRefreshCache: state.get("reload-action-should-refresh-cache"),
105+
trayIsOpen: state.getCache("tray-open"),
106+
onToggleTray: toggleTray
107+
})
108+
);
109+
110+
// render tray
111+
if (state.get("actions").includes("toggle-tray")) {
112+
crel(
113+
root,
114+
Tray({
115+
state,
116+
trayIsOpen: state.getCache("tray-open"),
117+
trayHeight: state.getCache("tray-height"),
118+
log: state.get("log"),
119+
onResizeTray: resizeTray
120+
})
121+
);
122+
}
123+
}
124+
render();
125+
}

src/features/app/app.test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import App from "./app";
2+
import crel from "crel";
3+
4+
beforeEach(() => crel(document.body, crel("div", { id: "mde" })));
5+
afterEach(() => document.getElementById("mde").remove());
6+
7+
const generateApp = options => {
8+
return App({
9+
root: document.getElementById("mde"),
10+
stateId: "global",
11+
actions: ["reload", "toggle-tray"],
12+
actionsCorner: "tr",
13+
hardReload: true,
14+
...options
15+
});
16+
};
17+
18+
test("App renders without crashing", () => {
19+
generateApp();
20+
21+
expect(!!document.getElementById("mde-action-bar")).toBe(true);
22+
expect(!!document.getElementById("mde-tray")).toBe(true);
23+
});
24+
25+
test("MDE console logs to tray", () => {
26+
generateApp();
27+
28+
console.log("works");
29+
30+
expect(document.querySelectorAll(".mde-log[data-type=string]").length).toBe(1);
31+
expect(document.querySelector(".mde-log .mde-log-message").innerHTML).toBe("works");
32+
33+
console.log("works");
34+
35+
expect(document.querySelector(".mde-log[data-type=string] .mde-log-amount").innerHTML).toBe("2");
36+
});
37+
38+
test("MDE console errors to tray", () => {
39+
generateApp();
40+
41+
console.error("works");
42+
43+
expect(document.querySelectorAll(".mde-log[data-type=error]").length).toBe(1);
44+
expect(document.querySelector(".mde-log .mde-log-message").innerHTML).toBe("works");
45+
46+
console.error("works");
47+
48+
expect(document.querySelector(".mde-log[data-type=error] .mde-log-amount").innerHTML).toBe("2");
49+
});
50+
51+
test("MDE console asserts to tray", () => {
52+
generateApp();
53+
54+
console.assert(false, "works");
55+
56+
expect(document.querySelectorAll(".mde-log[data-type=error]").length).toBe(1);
57+
expect(document.querySelector(".mde-log .mde-log-message").innerHTML).toBe(
58+
"Assertion failed: works"
59+
);
60+
61+
console.assert(false);
62+
63+
expect(document.querySelectorAll(".mde-log[data-type=error]").length).toBe(2);
64+
expect(document.querySelectorAll(".mde-log .mde-log-message")[1].innerHTML).toBe(
65+
"Assertion failed: console.assert"
66+
);
67+
68+
console.assert(false);
69+
70+
expect(
71+
document.querySelectorAll(".mde-log[data-type=error]")[1].querySelector(".mde-log-amount")
72+
.innerHTML
73+
).toBe("2");
74+
});

src/index.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { defaultTo, isElement } from "lodash";
2+
3+
import app from "src/features/app/app";
4+
5+
(function() {
6+
"use strict";
7+
8+
function mobileDevEnvironment({ root, stateId, actions, actionsCorner, hardReload } = {}) {
9+
if (!isElement(root)) {
10+
throw "Could not start MDE because MDE requires a `root` element to attach to the DOM.";
11+
}
12+
13+
// DEFAULT VARIABLES
14+
stateId = defaultTo(stateId, "global");
15+
actions = defaultTo(actions, ["reload", "toggle-tray"]);
16+
actionsCorner = defaultTo(actionsCorner, "tr");
17+
hardReload = defaultTo(hardReload, true);
18+
19+
// RUN
20+
21+
app({
22+
root,
23+
stateId,
24+
actions,
25+
actionsCorner,
26+
hardReload
27+
});
28+
}
29+
30+
// Enable usage in the browser
31+
window.mobileDevEnvironment = mobileDevEnvironment;
32+
33+
// Enable usage in Node
34+
if (typeof module === "object" && typeof module.exports === "object") {
35+
module.exports = mobileDevEnvironment;
36+
}
37+
})();
File renamed without changes.

0 commit comments

Comments
 (0)