From 97568c8622c76cc9bc2a899a88edce6297f57c14 Mon Sep 17 00:00:00 2001 From: Romain Baugue Date: Wed, 25 Nov 2020 22:03:53 +0100 Subject: [PATCH] Split the webapp into components files --- web/App.js | 16 ++++++++++------ web/Core.js | 2 ++ web/Core.scss | 7 ++++++- web/QueryLink.js | 7 ++++--- web/Searchbar.js | 47 +++++++++++++++++++++++----------------------- web/Searchbar.scss | 4 ++++ web/Table.js | 5 +++-- web/api.js | 2 +- web/index.scss | 26 +++++-------------------- web/mixins.scss | 23 +++++++++++++++++++++++ web/utils.js | 13 ++++--------- 11 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 web/mixins.scss diff --git a/web/App.js b/web/App.js index 8c14b0c..fdc1198 100644 --- a/web/App.js +++ b/web/App.js @@ -21,18 +21,18 @@ export default function App() { // Initialize the state, essentially updating it to use the query encoded in // the URL, if any. - React.useEffect(function () { + React.useEffect(function initializeQuery() { const raw = new URLSearchParams(window.location.search).get("q"); if (raw === null) { return; } setQuery(api.decodeQuery(raw)); - }); + }, []); // When the query change, we want to run the search query and update // the cores. React.useEffect( - function () { + function runQuery() { api .search(query) .then((res) => { @@ -54,7 +54,7 @@ export default function App() { // (or other similar event). Here we add an event listener when the app is // mounted, and remove it when it's unmounted. The handler is defined in the // effect function so its reference is the same for the full app lifecycle. - React.useEffect(function () { + React.useEffect(function popstateHandler() { function handler() { setQuery(api.decodeQuery(new URLSearchPArams(window.location.search).get("q"))); } @@ -69,7 +69,7 @@ export default function App() { // because the popstate history event already does this, and doing it // again break the forward-history. React.useEffect( - function () { + function updateHash() { const q = api.encodeQuery(query); if (new URLSearchParams(window.location.search).get("q") === q) { return; @@ -79,6 +79,10 @@ export default function App() { [query] ); + function queryHandler(q) { + setQuery(q); + } + function deleteCoreHandler() { if (!window.confirm(`are you sure you want to delete this core?`)) { return; @@ -105,7 +109,7 @@ export default function App() { return (
- + {error !== null && (

Unexpected error

diff --git a/web/Core.js b/web/Core.js index 6ddb415..9e1efc0 100644 --- a/web/Core.js +++ b/web/Core.js @@ -1,6 +1,8 @@ import React from "react"; import styles from "./Core.scss"; import { formatSize, formatDate } from "./utils.js"; +import api from "./api.js"; +import QueryLink from "./QueryLink.js"; // Core is a view of a core's details. export default function Core({ core, onDelete }) { diff --git a/web/Core.scss b/web/Core.scss index d65f492..ecda80c 100644 --- a/web/Core.scss +++ b/web/Core.scss @@ -1,4 +1,5 @@ -@import "./color.scss"; +@import "./colors.scss"; +@import "./mixins.scss"; .Core { ul { @@ -38,3 +39,7 @@ white-space: pre-wrap; } } + +.Button { + @include button; +} diff --git a/web/QueryLink.js b/web/QueryLink.js index 3484890..b90db87 100644 --- a/web/QueryLink.js +++ b/web/QueryLink.js @@ -1,19 +1,20 @@ import React from "react"; +import api from "./api.js"; // QueryLink can be used to make a direct link to a query search. The link is a // standard HTML link with a valid href, but the navigation is intercepted to // be handled by the app. This allow the user to copy-paste the link via his // navigator contextual menu, while making internal navigation easy. -export default function QueryLink({ query, onClick }) { +export default function QueryLink({ children, query, onClick }) { return ( { e.preventDefault(); onClick(); }} > - {props.children} + {children} ); } diff --git a/web/Searchbar.js b/web/Searchbar.js index 294294f..996e6d8 100644 --- a/web/Searchbar.js +++ b/web/Searchbar.js @@ -5,28 +5,29 @@ import { boolattr } from "./utils.js"; // Searchbar is one of the top-level components, tasked with handling the // interface to edit the search query. export default function Searchbar({ query, onSubmit }) { - // The local state is initialized to the current value, and will hold dirty - // values until the user submit the form. We want to update the current state - // when the query change. As the searchbar is never unmounted, this isn't - // done automatically. - const [value, setValue] = React.useState(query); + // The local payload is initialized from the current query, and will hold + // dirty values until the user submit the form. + const [payload, setPayload] = React.useState(query); + const [dirty, setDirty] = React.useState(false); + + // We want to update the current payload when the query change. As the + // searchbar is never unmounted, this isn't done automatically. React.useEffect(resetHandler, [query]); // dirty is used to activate or not the apply and reset buttons when // the state isn't equivalent to the initial query. - const [dirty, setDirty] = React.useState(false); React.useEffect( - function () { - setDirty(Object.keys(query).some((prop) => value[prop] !== query[prop])); + function updateDirty() { + setDirty(Object.keys(query).some((prop) => payload[prop] !== query[prop])); }, - [value] + [payload] ); - // changeHandler is used by form component when their value change to update + // changeHandler is used by form component when their payload change to update // the local state. function changeHandler(e) { - setValue({ - ...value, + setPayload({ + ...payload, [e.target.name]: e.target.value, }); } @@ -35,13 +36,13 @@ export default function Searchbar({ query, onSubmit }) { // propagate the state to the parent component. function submitHandler(e) { e.preventDefault(); - onSubmit(value); + onSubmit(payload); } // resetHandler is used by the reset button when it is clicked so we can - // reset the state to the query value. + // reset the state to the query payload. function resetHandler() { - setValue(query); + setPayload(query); } return ( @@ -50,9 +51,9 @@ export default function Searchbar({ query, onSubmit }) {
{["dumped_at", "hostname"].map((field) => { - const isActive = boolattr(value.sort === field); - const isDirty = boolattr(value.sort === field && value.sort !== query.sort); - const isChecked = value.sort === field; + const isActive = boolattr(payload.sort === field); + const isDirty = boolattr(payload.sort === field && payload.sort !== query.sort); + const isChecked = payload.sort === field; return (