Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(filter): Implement filter feature #19

Merged
merged 5 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);