summaryrefslogtreecommitdiffstats
path: root/src/components/RequestList/RequestList.tsx
diff options
context:
space:
mode:
authorGravatar Tom van der Lee <tom@vanderlee.io>2022-02-12 10:59:19 +0100
committerGravatar Tom van der Lee <tom@vanderlee.io>2022-02-12 11:07:50 +0100
commit7c48533571e9f9d3731a59433a56cc8d6e008123 (patch)
treef86868dfea251fa3b198ffcf3e158a3ed354ff9d /src/components/RequestList/RequestList.tsx
parent5dbf880ce3cdb227a85dbb0015609c210557c60b (diff)
downloadclient-7c48533571e9f9d3731a59433a56cc8d6e008123.tar.gz
client-7c48533571e9f9d3731a59433a56cc8d6e008123.tar.bz2
client-7c48533571e9f9d3731a59433a56cc8d6e008123.zip
Added search and filter options
Diffstat (limited to 'src/components/RequestList/RequestList.tsx')
-rw-r--r--src/components/RequestList/RequestList.tsx203
1 files changed, 203 insertions, 0 deletions
diff --git a/src/components/RequestList/RequestList.tsx b/src/components/RequestList/RequestList.tsx
new file mode 100644
index 0000000..95c5b75
--- /dev/null
+++ b/src/components/RequestList/RequestList.tsx
@@ -0,0 +1,203 @@
1import {
2 Button,
3 Dropdown,
4 DropdownButton,
5 Form,
6 InputGroup,
7 ListGroup,
8 Offcanvas,
9} from "react-bootstrap";
10import classNames from "classnames";
11import styles from "./RequestList.module.scss";
12import RequestSummary from "../RequestSummary/RequestSummary";
13import * as React from "react";
14import {useCallback, useContext, useMemo, useState} from "react";
15import {Method, RequestResponse} from "../../hooks/useRequests";
16import {DarkModeContext} from "../../contexts/DarkMode";
17import {Filter} from "../Icons/Filter";
18
19interface ListProps {
20 requests: RequestResponse[],
21 selectedRequestIndex: number | null
22 setSelectedRequestIndex: (index: number) => void
23}
24
25type EnabledMethods = {
26 [method in Method]: boolean
27}
28
29
30export default function RequestList({ requests, selectedRequestIndex, setSelectedRequestIndex }: ListProps) {
31 const {darkMode} = useContext(DarkModeContext)
32 const [showFilterOptions, setShowFilterOptions] = useState(false);
33 const [search, setSearch] = useState('');
34 const [enableRegex, setEnableRegex] = useState(false);
35
36 const [methods, setMethods] = useState<EnabledMethods>({
37 GET: true,
38 POST: true,
39 PUT: true,
40 PATCH: true,
41 DELETE: true,
42 });
43
44 const toggleMethods = useCallback((method: Method | null) => {
45 const enabled = method == null;
46 const methods = {
47 GET: enabled,
48 POST: enabled,
49 PUT: enabled,
50 PATCH: enabled,
51 DELETE: enabled,
52 }
53
54 if (!enabled) {
55 methods[method] = true;
56 }
57
58 setMethods(methods);
59 }, []);
60
61 const enabledMethods: Method[] = useMemo(() => (Object
62 .entries(methods)
63 .filter(([method, enabled]) => enabled)
64 .map(([method]) => method)
65 ), [methods]);
66
67 const methodLabel = useMemo(() => {
68 if (enabledMethods.length == 1) {
69 return enabledMethods[0]
70 } else if (enabledMethods.length === 5) {
71 return 'ANY'
72 } else {
73 return 'CUSTOM'
74 }
75 }, [enabledMethods]);
76
77
78
79 const filteredRequests = useMemo(() => {
80 let searchRegex = new RegExp('');
81 try {
82 searchRegex = new RegExp(
83 enableRegex
84 ? search
85 : search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
86 'i');
87 } catch {
88
89 }
90
91 return (
92 (
93 requests
94 .map((request, index) => [index, request])
95 .reverse() as [number, RequestResponse][]
96 )
97 .filter(([index, request]) => (
98 enabledMethods.length > 0 === null || enabledMethods.includes(request.request.method)
99 ))
100 .filter(([index, request]) => (
101 search === '' || searchRegex.test(`${request.request.method} ${request.request.path}`)
102 ))
103 )
104 }, [requests, search, enabledMethods, enableRegex])
105
106 return (
107 <div className={classNames(styles.listContainer, 'd-flex')}>
108 <ListGroup variant="flush"
109 className={classNames('flex-grow-0', 'border-bottom')}>
110 <ListGroup.Item>
111 <InputGroup>
112 <DropdownButton variant="primary" title={methodLabel}>
113 <Dropdown.Item
114 onClick={() => toggleMethods(null)}>ANY</Dropdown.Item>
115 <Dropdown.Divider/>
116 <Dropdown.Item
117 onClick={() => toggleMethods('GET')}>GET</Dropdown.Item>
118 <Dropdown.Item
119 onClick={() => toggleMethods('POST')}>POST</Dropdown.Item>
120 <Dropdown.Item
121 onClick={() => toggleMethods('PUT')}>PUT</Dropdown.Item>
122 <Dropdown.Item
123 onClick={() => toggleMethods('PATCH')}>PATCH</Dropdown.Item>
124 <Dropdown.Item
125 onClick={() => toggleMethods('DELETE')}>DELETE</Dropdown.Item>
126 </DropdownButton>
127 <Form.Control type="text" className='border-secondary'
128 value={search} placeholder="Search..."
129 onChange={({target}) => setSearch(target.value)}/>
130 <Button
131 variant={showFilterOptions ? 'secondary' : 'outline-secondary'}
132 onClick={() => setShowFilterOptions(true)}>
133 <Filter/>
134 </Button>
135 </InputGroup>
136 </ListGroup.Item>
137 </ListGroup>
138 <ListGroup
139 variant='flush'
140 as="ul"
141 className={classNames(styles.list, 'flex-grow-1')}
142 >
143 {
144 filteredRequests.length > 0
145 ? (
146 filteredRequests.map(([index, requestResponse]) => {
147 const selected = selectedRequestIndex === index;
148 return (
149 <ListGroup.Item
150 as="li"
151 onClick={() => setSelectedRequestIndex(index)}
152 key={`request-${index}`}
153 className={classNames({
154 'bg-primary': selected,
155 'text-light': selected,
156 'border-bottom': true
157 })}
158 >
159 <RequestSummary
160 requestResponse={requestResponse}
161 selected={selected}
162 />
163 </ListGroup.Item>
164 )
165 })
166 )
167 : (
168 <div className={styles.noRequest}>
169 <p>No requests</p>
170 </div>
171 )
172 }
173 </ListGroup>
174 <Offcanvas className={
175 classNames({
176 'bg-dark': darkMode
177 })
178 }
179 show={showFilterOptions}
180 onHide={() => setShowFilterOptions(false)}>
181 <Offcanvas.Header closeButton>
182 <Offcanvas.Title>
183 Filter Options
184 </Offcanvas.Title>
185 </Offcanvas.Header>
186 <Offcanvas.Body>
187 <Form.Group className="mb-4">
188 <Form.Label className="fw-bold">Search</Form.Label>
189 <Form.Control type="text" value={search} placeholder="Search..."
190 onChange={({target}) => setSearch(target.value)}/>
191 <Form.Check className="mt-2" type='switch' label='Regex search' checked={enableRegex} onChange={() => setEnableRegex(!enableRegex)} />
192 </Form.Group>
193 <Form.Group>
194 <Form.Label className="fw-bold">Method</Form.Label>
195 {Object.entries(methods).map(([method, enabled]) => (
196 <Form.Check type='switch' label={method} checked={enabled} onChange={() => setMethods({...methods, [method]: !enabled})} />
197 ))}
198 </Form.Group>
199 </Offcanvas.Body>
200 </Offcanvas>
201 </div>
202 )
203}