Skip to content

Commit

Permalink
feat(filter): Implement filter feature (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyp3rflow authored Mar 23, 2022
1 parent e9052a6 commit 8a194ae
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 49 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"packageManager": "[email protected]",
"scripts": {
"dev": "next dev",
"build": "BASE_PATH=/next next build && next export -o dist/next && rsync -a template/ dist",
"build": "BASE_PATH=/next next build && next export -o dist/next && rsync -a template/ dist && rsync -a public/ dist",
"zip": "cd dist && zip -r ../dist.zip ."
},
"dependencies": {
Expand Down
2 changes: 2 additions & 0 deletions public/largeIcons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/Checkbox/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
align-items: center;
line-height: normal;
column-gap: 4px;

> input {
width: auto;
}
}
3 changes: 2 additions & 1 deletion src/components/Checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface CheckboxProps {
isChecked: boolean;
onChange(value: boolean): void;
children?: React.ReactNode;
className?: string;
}
const Checkbox: React.FC<CheckboxProps> = ({
isChecked,
Expand All @@ -13,7 +14,7 @@ const Checkbox: React.FC<CheckboxProps> = ({
...props
}) => {
return (
<label className={styles.checkbox} {...props}>
<label {...props} className={`${styles.checkbox} ${props.className ?? ""}`}>
<input
type="checkbox"
checked={isChecked}
Expand Down
35 changes: 35 additions & 0 deletions src/components/IconButton/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.icon-button {
flex-shrink: 0;
line-height: 0;
border: none;
background: none;
cursor: pointer;

.icon {
display: inline-block;
width: 28px;
height: 24px;
-webkit-mask-image: url(/largeIcons.svg);
background-color: var(--pb-fg);

&:hover {
background-color: var(--pb-fg-hover);
}
}

.filter {
-webkit-mask-position: -56px 120px;

&.active {
background-color: var(--oc-red-3);
}
}

.search {
-webkit-mask-position: -196px 96px;
}

.clear {
-webkit-mask-position: 0px 144px;
}
}
30 changes: 30 additions & 0 deletions src/components/IconButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import styles from "./index.module.scss";

type Icon = "filter" | "search" | "clear";

export interface IconButtonProps
extends Omit<React.HTMLAttributes<HTMLButtonElement>, "children"> {
icon: Icon;
isActive?: boolean;
}

const IconButton: React.FC<IconButtonProps> = ({
icon,
isActive,
...props
}) => {
return (
<button
{...props}
className={`${styles["icon-button"]} ${props.className ?? ""}`}
>
<span
className={`${styles.icon} ${styles[icon]} ${
isActive ? styles.active : ""
}`}
></span>
</button>
);
};

export default IconButton;
35 changes: 35 additions & 0 deletions src/pages/_app.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
:root {
height: 100%;
overflow: hidden;

--pb-bg: var(--oc-white);
--pb-fg: var(--oc-black);
--pb-sep: var(--oc-gray-6);
Expand Down Expand Up @@ -26,3 +29,35 @@ body {
color: var(--pb-fg);
max-height: 100vh;
}

button {
padding: 0;
}

input {
border: 0;
color: var(--oc-gray-5);
background-color: var(--oc-gray-9);
padding: 0.25em;
width: 100%;
}

// For compatibility with the Chromium devTools environment.
body {
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
margin: 0;
cursor: default;
font-family: system-ui, sans-serif;
font-size: 12px;
tab-size: 4;
user-select: none;
}

* {
box-sizing: border-box;
min-width: 0;
min-height: 0;
}
16 changes: 10 additions & 6 deletions src/pages/index.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ import RequestDetail from "./index/RequestDetail";
import RequestList from "./index/RequestList";
import { preserveLogAtom } from "./index/atoms/setting";
import Settings from "./index/Settings";
import Experimental from "./index/Experimental";

const Page: NextPage = () => {
useDevtoolsCommunicationLogic();
return (
<div className={style.page}>
<div className={style.sidebar}>
<Settings />
<RequestList />
<>
<div className={style.page}>
<div className={style.sidebar}>
<Settings />
{process.env.NODE_ENV === "development" && <Experimental />}
<RequestList />
</div>
<RequestDetail />
</div>
<RequestDetail />
</div>
</>
);
};

Expand Down
12 changes: 12 additions & 0 deletions src/pages/index/Experimental/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.settings {
padding: 4px;
display: flex;
flex-direction: column;

border-bottom: 1px solid var(--pb-sep);
}

.search-section {
display: flex;
align-items: center;
}
16 changes: 16 additions & 0 deletions src/pages/index/Experimental/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import Button from "../../../components/Button";
import styles from "./index.module.scss";
import { useAddMockRequests } from "../mocks/requests";

interface ExperimentalProps {}
const Experimental: React.FC<ExperimentalProps> = () => {
const addMockRequests = useAddMockRequests();
return (
<div className={styles.settings}>
<Button onClick={() => addMockRequests()}>Add mock reqs</Button>
</div>
);
};

export default React.memo(Experimental);
66 changes: 41 additions & 25 deletions src/pages/index/RequestList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { memo } from "react";
import { useMemo, memo } from "react";
import { atom, useAtom } from "jotai";
import { requestsAtom } from "../atoms/request";
import { filterAtom, searchValueAtom } from "../atoms/setting";
import { selectedRequestKeyAtom } from "../atoms/ui";
import Button from "../../../components/Button";
import style from "./index.module.scss";
Expand All @@ -11,30 +12,40 @@ const RequestList: React.FC<RequestListProps> = () => {
const [selectedRequestKey, setSelectedRequestKey] = useAtom(
selectedRequestKeyAtom
);
const [isFilterActive] = useAtom(filterAtom);
const [searchValue] = useAtom(searchValueAtom);
const memoizedRequestList = useMemo(() => {
if (searchValue.length === 0 || !isFilterActive) return requestList;
return requestList.filter(({ servicePath, rpcName }) => {
return servicePath.includes(searchValue) || rpcName.includes(searchValue);
});
}, [isFilterActive, requestList, searchValue]);
return (
<div className={style["request-list"]}>
{requestList.map(
({ key, servicePath, rpcName, responsePayloads, responseError }) => (
<Button
key={key}
className={style["request-list-item"]}
data-selected={key === selectedRequestKey}
onClick={() => setSelectedRequestKey(key)}
>
<div className={style["list-main"]}>
<div className={style["service-path"]}>{servicePath}</div>
<div className={style["rpc-name"]}>{rpcName}</div>
</div>
<div className={style["list-status"]}>
{responsePayloads.length > 0 && (
<div className={style["payload-circle"]}>
{responsePayloads.length}
</div>
)}
{responseError && <div className={style["error-circle"]} />}
</div>
</Button>
)
{memoizedRequestList.map(
({ key, servicePath, rpcName, responsePayloads, responseError }) => {
return (
<Button
key={key}
className={style["request-list-item"]}
data-selected={key === selectedRequestKey}
onClick={() => setSelectedRequestKey(key)}
>
<div className={style["list-main"]}>
<div className={style["service-path"]}>{servicePath}</div>
<div className={style["rpc-name"]}>{rpcName}</div>
</div>
<div className={style["list-status"]}>
{responsePayloads.length > 0 && (
<div className={style["payload-circle"]}>
{responsePayloads.length}
</div>
)}
{responseError && <div className={style["error-circle"]} />}
</div>
</Button>
);
}
)}
</div>
);
Expand All @@ -47,7 +58,12 @@ const requestListAtom = atom((get) => {
const { servicePath, rpcName, responsePayloadsAtom, responseError } = get(
requests[key]
);
const responsePayloads = get(responsePayloadsAtom);
return { key, servicePath, rpcName, responsePayloads, responseError };
return {
key,
servicePath,
rpcName,
responsePayloads: get(responsePayloadsAtom),
responseError,
};
});
});
29 changes: 29 additions & 0 deletions src/pages/index/Settings/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,33 @@
padding: 4px;
display: flex;
flex-direction: column;

border-bottom: 1px solid var(--pb-sep);

> * {
padding: 4px 0;

&:first-child {
padding-top: 0;
}

&:not(:first-child) {
border-top: 1px solid var(--pb-sep);
}

&:last-child {
padding-bottom: 0;
}
}
}

.search-section {
display: flex;
align-items: center;
}

.vertical-sep {
height: 100%;
border-right: 1px solid var(--pb-sep);
margin-right: 0.625em;
}
39 changes: 23 additions & 16 deletions src/pages/index/Settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
import React from "react";
import { useAtom } from "jotai";
import { useUpdateAtom } from "jotai/utils";
import Button from "../../../components/Button";
import Checkbox from "../../../components/Checkbox";
import IconButton from "../../../components/IconButton";
import { resetRequestsAtom } from "../atoms/ui";
import { preserveLogAtom } from "../atoms/setting";
import { filterAtom, preserveLogAtom, searchValueAtom } from "../atoms/setting";
import styles from "./index.module.scss";
import { useAddMockRequests } from "../mocks/requests";

interface SettingsProps {}
const Settings: React.FC<SettingsProps> = () => {
const resetRequests = useUpdateAtom(resetRequestsAtom);
const addMockRequests = useAddMockRequests();
const [preserveLog, setPreserveLog] = useAtom(preserveLogAtom);
const [searchValue, setSearchValue] = useAtom(searchValueAtom);
const [isFilterActive, setFilterActive] = useAtom(filterAtom);
return (
<div className={styles.settings}>
<Button onClick={resetRequests}>Remove cache</Button>
{process.env.NODE_ENV === "development" && (
<Button onClick={() => addMockRequests()}>Add mock reqs</Button>
)}
<Checkbox
isChecked={preserveLog}
onChange={(v) => {
setPreserveLog(v);
}}
>
Preserve log
</Checkbox>
<div className={styles["search-section"]}>
<IconButton icon="clear" onClick={resetRequests} />
<div className={styles["vertical-sep"]} />
<Checkbox isChecked={preserveLog} onChange={setPreserveLog}>
Preserve log
</Checkbox>
</div>
<div className={styles["search-section"]}>
<IconButton
icon="filter"
isActive={isFilterActive}
onClick={() => setFilterActive((prev) => !prev)}
/>
<input
placeholder="Filter (rpc, service path)"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
</div>
</div>
);
};
Expand Down
4 changes: 4 additions & 0 deletions src/pages/index/atoms/setting.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { atom } from "jotai";

export const preserveLogAtom = atom<boolean>(false);

export const searchValueAtom = atom<string>("");

export const filterAtom = atom<boolean>(true);

0 comments on commit 8a194ae

Please sign in to comment.