Skip to content

Commit

Permalink
✨ make actions extendable
Browse files Browse the repository at this point in the history
  • Loading branch information
francisashley committed Dec 16, 2019
1 parent 5787b47 commit 13e1b32
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 32 deletions.
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,6 @@ window.addEventListener("DOMContentLoaded", event => {
<td valign="top"><code>'tr'</code></td>
<td valign="top"></td>
</tr>
<tr>
<td valign="top"><code>hardReload</code></td>
<td valign="top">Refresh browser cache</td>
<td valign="top"><code>string</code></td>
<td valign="top"><code>true</code></td>
<td valign="top"></td>
</tr>
<tr>
<td valign="top"><code>root</code></td>
<td valign="top">Provide a DOM element for MDE to hook on too.</td>
Expand Down Expand Up @@ -156,6 +149,19 @@ window.addEventListener("DOMContentLoaded", event => {
</tr>
</table>

## Actions



## Packaged actions



## Custom actions





[version-badge]: https://img.shields.io/npm/v/@fa-repo/mobile-dev-environment.svg?style=flat-square
[license-badge]: https://img.shields.io/npm/l/@testing-library/react.svg?style=flat-square
Expand Down
7 changes: 6 additions & 1 deletion example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
<script>
mobileDevEnvironment({
root: document.getElementById("mde"),
actionsCorner: "tr"
actionsCorner: "tr",
actions: [
"reload",
"toggle-tray",
{ action: "custom", content: "Click me", onClick: () => alert("Hiya!") }
]
});

intentional_error;
Expand Down
11 changes: 7 additions & 4 deletions src/features/action-bar/action-bar.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import CustomButton from "src/features/action-bar/custom-button";
import ReloadButton from "src/features/action-bar/reload-button";
import TrayButton from "src/features/action-bar/tray-button";
import crel from "crel";

export default function ActionBar({
actions = [],
corner,
shouldRefreshCache,
trayIsOpen,
onToggleTray = () => {}
} = {}) {
Expand All @@ -15,12 +15,15 @@ export default function ActionBar({
"div",
{ id: "mde-action-bar", "data-corner": corner },
...actions.map(action => {
if (action === "reload") {
return ReloadButton({ onClick: () => location.reload(shouldRefreshCache) });
if (action.action === "reload") {
return ReloadButton({ onClick: () => location.reload(action.refreshCache) });
}
if (action === "toggle-tray") {
if (action.action === "toggle-tray") {
return TrayButton({ isActive: trayIsOpen, onClick: onToggleTray });
}
if (action.action === "custom") {
return CustomButton({ content: action.content, onClick: action.onClick });
}
return null;
})
);
Expand Down
15 changes: 7 additions & 8 deletions src/features/action-bar/action-bar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@ test("ActionBar renders properly", () => {
});

test("ActionBar shows reload button", () => {
const actionBar = ActionBar({ actions: ["reload"] });
const actionBar = ActionBar({ actions: [{ action: "reload" }] });

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

test("ActionBar shows tray button", () => {
const actionBar = ActionBar({ actions: ["toggle-tray"] });
const actionBar = ActionBar({ actions: [{ action: "toggle-tray" }] });

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

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

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

test("ActionBar tray button displays without `.active` class", () => {
const actionBar = ActionBar({ actions: ["toggle-tray"] });
const actionBar = ActionBar({ actions: [{ action: "toggle-tray" }] });

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

test("ActionBar reloads page when ReloadButton clicked", () => {
const actionBar = ActionBar({ actions: ["reload"] });
const actionBar = ActionBar({ actions: [{ action: "reload" }] });
delete window.location;
const reloadMock = jest.fn();
window.location = { reload: reloadMock };
Expand All @@ -45,20 +45,19 @@ test("ActionBar reloads page when ReloadButton clicked", () => {
});

test("ActionBar reloads page and cache when ReloadButton clicked", () => {
const actionBar = ActionBar({ actions: ["reload"], shouldRefreshCache: true });
const actionBar = ActionBar({ actions: [{ action: "reload" }], shouldRefreshCache: true });
delete window.location;
const reloadMock = jest.fn();
window.location = { reload: reloadMock };

actionBar.querySelector("#mde-reload").click();

expect(window.location.reload).toHaveBeenCalled();
expect(reloadMock.mock.calls[0][0]).toBe(true);
});

test("ActionBar calls onToggleTray", () => {
const onToggleTray = jest.fn();
const actionBar = ActionBar({ actions: ["toggle-tray"], onToggleTray });
const actionBar = ActionBar({ actions: [{ action: "toggle-tray" }], onToggleTray });

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

Expand Down
9 changes: 9 additions & 0 deletions src/features/action-bar/custom-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import crel from "crel";

export default function CustomButton({ content, onClick } = {}) {
const Button = crel("button", content);

if (onClick) Button.addEventListener("click", onClick);

return Button;
}
27 changes: 27 additions & 0 deletions src/features/action-bar/custom-button.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import CustomButton from "./custom-button";

test("Custom button renders without crashing", () => {
expect(!!CustomButton()).toBe(true);
});
test("Custom button calls onClick", () => {
const onClick = jest.fn();
const customButton = CustomButton({ onClick });

customButton.click();

expect(onClick).toHaveBeenCalled();
});
test("Custom button accepts string content", () => {
const customButton = CustomButton({ content: "Hiya!" });

expect(customButton.innerHTML).toBe("Hiya!");
});
test("Custom button accepts element content", () => {
const content = document.createElement("i");
content.innerHTML = "Hiya!";
const customButton = CustomButton({ content });

expect(customButton.innerHTML).toBe("<i>Hiya!</i>");
expect(customButton.querySelector("i").tagName).toBe("I");
expect(customButton.querySelector("i").innerHTML).toBe("Hiya!");
});
6 changes: 2 additions & 4 deletions src/features/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import limitTrayHeight from "src/utils/limit-tray-height";
import stateHandler from "src/utils/state";
import tracer from "src/utils/tracer.js";

export default function App({ root, stateId, actions, actionsCorner, hardReload }) {
export default function App({ root, stateId, actions, actionsCorner }) {
const state = stateHandler(stateId);

// Default tray height
Expand All @@ -17,7 +17,6 @@ export default function App({ root, stateId, actions, actionsCorner, hardReload
state.set("log", []);
state.set("actions", actions);
state.set("actions-corner", actionsCorner);
state.set("reload-action-should-refresh-cache", hardReload);
state.setCache("tray-open", state.getCache("tray-open", true));
state.setCache("tray-height", trayHeight);

Expand Down Expand Up @@ -101,14 +100,13 @@ export default function App({ root, stateId, actions, actionsCorner, hardReload
ActionBar({
actions,
corner: state.get("actions-corner"),
shouldRefreshCache: state.get("reload-action-should-refresh-cache"),
trayIsOpen: state.getCache("tray-open"),
onToggleTray: toggleTray
})
);

// render tray
if (state.get("actions").includes("toggle-tray")) {
if (actions.some(action => action.action === "toggle-tray")) {
crel(
root,
Tray({
Expand Down
3 changes: 1 addition & 2 deletions src/features/app/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ const generateApp = options => {
return App({
root: document.getElementById("mde"),
stateId: "global",
actions: ["reload", "toggle-tray"],
actions: [{ action: "reload" }, { action: "toggle-tray" }],
actionsCorner: "tr",
hardReload: true,
...options
});
};
Expand Down
34 changes: 28 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,51 @@
import { defaultTo, isElement } from "lodash";
import { defaultTo, isElement, isObject } from "lodash";

import app from "src/features/app/app";

(function() {
"use strict";

function mobileDevEnvironment({ root, stateId, actions, actionsCorner, hardReload } = {}) {
function mobileDevEnvironment({ root, stateId, actions, actionsCorner } = {}) {
if (!isElement(root)) {
throw "Could not start MDE because MDE requires a `root` element to attach to the DOM.";
}

// DEFAULT VARIABLES
stateId = defaultTo(stateId, "global");
actions = defaultTo(actions, ["reload", "toggle-tray"]);
actionsCorner = defaultTo(actionsCorner, "tr");
hardReload = defaultTo(hardReload, true);
actions = defaultTo(actions, ["reload", "toggle-tray"]);

actions = actions
.map(action => {
if (typeof action === "string" && action === "reload") {
action = { action: "reload" };
} else if (typeof action === "string" && action === "toggle-tray") {
action = { action: "toggle-tray" };
}

if (isObject(action) && action.action === "reload") {
return { action: "reload", refreshCache: defaultTo(action.refreshCache, true) };
} else if (isObject(action) && action.action === "toggle-tray") {
return { action: "toggle-tray" };
} else if (isObject(action) && action.action === "custom") {
return {
action: "custom",
content: defaultTo(action.content, ""),
onClick: defaultTo(action.onClick, () => {})
};
}

return null;
})
.filter(Boolean);

// RUN

app({
root,
stateId,
actions,
actionsCorner,
hardReload
actionsCorner
});
}

Expand Down
26 changes: 26 additions & 0 deletions src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ test("MobileDevEnvironment displays `toggle-tray` and `reload` actions correctly
expect(document.querySelectorAll("#mde-action-bar button")[1].id).toBe("mde-reload");
});

test("MobileDevEnvironment displays `toggle-tray` and `reload` actions in object syntax correctly", () => {
generateMDE({ actions: [{ action: "toggle-tray" }, { action: "reload" }] });

expect(document.querySelectorAll("#mde-action-bar button")[0].id).toBe("mde-toggle-tray");
expect(document.querySelectorAll("#mde-action-bar button")[1].id).toBe("mde-reload");
});

test("MobileDevEnvironment displays custom actions correctly", () => {
const onClick = jest.fn();
generateMDE({
actions: [
{ action: "toggle-tray" },
{ action: "reload" },
{ action: "custom", content: "Click me!", onClick }
]
});

expect(document.querySelectorAll("#mde-action-bar button")[0].id).toBe("mde-toggle-tray");
expect(document.querySelectorAll("#mde-action-bar button")[1].id).toBe("mde-reload");
expect(document.querySelectorAll("#mde-action-bar button")[2].innerHTML).toBe("Click me!");

document.querySelectorAll("#mde-action-bar button")[2].click();

expect(onClick).toHaveBeenCalled();
});

test("MobileDevEnvironment displays empty actions bar correctly", () => {
generateMDE({ actions: [] });

Expand Down

0 comments on commit 13e1b32

Please sign in to comment.