change(ui) - fetch details modal refactor and added prev and next navigation
This commit is contained in:
parent
1fd7a848b6
commit
39ddd3d82a
10 changed files with 336 additions and 314 deletions
|
|
@ -67,36 +67,36 @@ export function renderStart(r: any) {
|
|||
);
|
||||
}
|
||||
|
||||
const renderXHRText = () => (
|
||||
<span className="flex items-center">
|
||||
{XHR}
|
||||
<QuestionMarkHint
|
||||
content={
|
||||
<>
|
||||
Use our{' '}
|
||||
<a
|
||||
className="color-teal underline"
|
||||
target="_blank"
|
||||
href="https://docs.openreplay.com/plugins/fetch"
|
||||
>
|
||||
Fetch plugin
|
||||
</a>
|
||||
{' to capture HTTP requests and responses, including status codes and bodies.'} <br />
|
||||
We also provide{' '}
|
||||
<a
|
||||
className="color-teal underline"
|
||||
target="_blank"
|
||||
href="https://docs.openreplay.com/plugins/graphql"
|
||||
>
|
||||
support for GraphQL
|
||||
</a>
|
||||
{' for easy debugging of your queries.'}
|
||||
</>
|
||||
}
|
||||
className="ml-1"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
// const renderXHRText = () => (
|
||||
// <span className="flex items-center">
|
||||
// {XHR}
|
||||
// <QuestionMarkHint
|
||||
// content={
|
||||
// <>
|
||||
// Use our{' '}
|
||||
// <a
|
||||
// className="color-teal underline"
|
||||
// target="_blank"
|
||||
// href="https://docs.openreplay.com/plugins/fetch"
|
||||
// >
|
||||
// Fetch plugin
|
||||
// </a>
|
||||
// {' to capture HTTP requests and responses, including status codes and bodies.'} <br />
|
||||
// We also provide{' '}
|
||||
// <a
|
||||
// className="color-teal underline"
|
||||
// target="_blank"
|
||||
// href="https://docs.openreplay.com/plugins/graphql"
|
||||
// >
|
||||
// support for GraphQL
|
||||
// </a>
|
||||
// {' for easy debugging of your queries.'}
|
||||
// </>
|
||||
// }
|
||||
// className="ml-1"
|
||||
// />
|
||||
// </span>
|
||||
// );
|
||||
|
||||
function renderSize(r: any) {
|
||||
if (r.responseBodySize) return formatBytes(r.responseBodySize);
|
||||
|
|
@ -180,6 +180,7 @@ function NetworkPanel(props: Props) {
|
|||
const [sortAscending, setSortAscending] = useState(true);
|
||||
const [filter, setFilter] = useState('');
|
||||
const [showOnlyErrors, setShowOnlyErrors] = useState(false);
|
||||
const [activeRequest, setActiveRequest] = useState(false )
|
||||
const onTabClick = (activeTab: any) => setActiveTab(activeTab);
|
||||
const onFilterChange = ({ target: { value } }: any) => setFilter(value);
|
||||
const additionalHeight = 0;
|
||||
|
|
@ -237,7 +238,7 @@ function NetworkPanel(props: Props) {
|
|||
}
|
||||
|
||||
const onRowClick = (row: any) => {
|
||||
showModal(<FetchDetailsModal resource={row} fetchPresented={fetchPresented} />, {
|
||||
showModal(<FetchDetailsModal resource={row} rows={filtered} fetchPresented={fetchPresented} />, {
|
||||
right: true,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,257 +0,0 @@
|
|||
import React from 'react';
|
||||
import { JSONTree, NoContent, Button, Tabs, Icon } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import stl from './fetchDetails.module.css';
|
||||
import Headers from './components/Headers';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import { formatBytes } from 'App/utils';
|
||||
import CopyText from 'Shared/CopyText';
|
||||
|
||||
const HEADERS = 'HEADERS';
|
||||
const REQUEST = 'REQUEST';
|
||||
const RESPONSE = 'RESPONSE';
|
||||
|
||||
const TABS = [HEADERS, REQUEST, RESPONSE].map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
export default class FetchDetailsModal extends React.PureComponent {
|
||||
state = { activeTab: REQUEST, tabs: [] };
|
||||
|
||||
onTabClick = (activeTab) => this.setState({ activeTab });
|
||||
|
||||
componentDidMount() {
|
||||
this.checkTabs();
|
||||
}
|
||||
|
||||
renderActiveTab = (tab) => {
|
||||
const {
|
||||
resource: { payload, response = this.props.resource.body },
|
||||
} = this.props;
|
||||
let jsonPayload,
|
||||
jsonResponse,
|
||||
requestHeaders,
|
||||
responseHeaders = undefined;
|
||||
|
||||
try {
|
||||
jsonPayload = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
||||
requestHeaders = jsonPayload.headers;
|
||||
jsonPayload.body =
|
||||
typeof jsonPayload.body === 'string' ? JSON.parse(jsonPayload.body) : jsonPayload.body;
|
||||
delete jsonPayload.headers;
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
jsonResponse = typeof response === 'string' ? JSON.parse(response) : response;
|
||||
responseHeaders = jsonResponse.headers;
|
||||
jsonResponse.body =
|
||||
typeof jsonResponse.body === 'string' ? JSON.parse(jsonResponse.body) : jsonResponse.body;
|
||||
delete jsonResponse.headers;
|
||||
} catch (e) {}
|
||||
|
||||
switch (tab) {
|
||||
case REQUEST:
|
||||
return (
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">Body is Empty.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={!payload}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
{jsonPayload === undefined ? (
|
||||
<div className="ml-3 break-words my-3"> {payload} </div>
|
||||
) : (
|
||||
<JSONTree src={jsonPayload} collapsed={false} enableClipboard />
|
||||
)}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
case RESPONSE:
|
||||
return (
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">Body is Empty.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={!response}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
{jsonResponse === undefined ? (
|
||||
<div className="ml-3 break-words my-3"> {response} </div>
|
||||
) : (
|
||||
<JSONTree src={jsonResponse} collapsed={false} enableClipboard />
|
||||
)}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
case HEADERS:
|
||||
return <Headers requestHeaders={requestHeaders} responseHeaders={responseHeaders} />;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.resource.index === this.props.resource.index) return;
|
||||
|
||||
this.checkTabs();
|
||||
}
|
||||
|
||||
checkTabs() {
|
||||
const {
|
||||
resource: { payload, response, body },
|
||||
isResult,
|
||||
} = this.props;
|
||||
const _tabs = TABS;
|
||||
// const _tabs = TABS.filter(t => {
|
||||
// if (t.key == REQUEST && !!payload) {
|
||||
// return true
|
||||
// }
|
||||
|
||||
// if (t.key == RESPONSE && !!response) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// })
|
||||
this.setState({ tabs: _tabs, activeTab: _tabs.length > 0 ? _tabs[0].key : null });
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
resource,
|
||||
fetchPresented,
|
||||
nextClick,
|
||||
prevClick,
|
||||
first = false,
|
||||
last = false,
|
||||
} = this.props;
|
||||
const { method, url, duration } = resource;
|
||||
const { activeTab, tabs } = this.state;
|
||||
const _duration = parseInt(duration);
|
||||
|
||||
return (
|
||||
<div className="bg-white p-5 h-screen overflow-y-auto" style={{ width: '500px' }}>
|
||||
<h5 className="mb-2 text-2xl">Network Request</h5>
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Name</div>
|
||||
<div className="rounded-lg bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip cursor-pointer">
|
||||
<CopyText content={resource.url}>{resource.name}</CopyText>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Type</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{resource.type}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!!resource.decodedBodySize && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Size</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{formatBytes(resource.decodedBodySize)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{method && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Request Method</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{resource.method}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{resource.status && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Status</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip flex items-center">
|
||||
{resource.status === '200' && (
|
||||
<div className="w-4 h-4 bg-green rounded-full mr-2"></div>
|
||||
)}
|
||||
{resource.status}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!!_duration && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Time</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{_duration} ms
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{resource.type === TYPES.XHR && !fetchPresented && (
|
||||
<div className="bg-active-blue rounded p-3 mt-4">
|
||||
<div className="mb-2 flex items-center">
|
||||
<Icon name="lightbulb" size="18" />
|
||||
<span className="ml-2 font-medium">Get more out of network requests</span>
|
||||
</div>
|
||||
<ul className="list-disc ml-5">
|
||||
<li>
|
||||
Integrate{' '}
|
||||
<a
|
||||
href="https://docs.openreplay.com/plugins/fetch"
|
||||
className="link"
|
||||
target="_blank"
|
||||
>
|
||||
Fetch plugin
|
||||
</a>{' '}
|
||||
to capture fetch payloads.
|
||||
</li>
|
||||
<li>
|
||||
Find a detailed{' '}
|
||||
<a
|
||||
href="https://www.youtube.com/watch?v=YFCKstPZzZg"
|
||||
className="link"
|
||||
target="_blank"
|
||||
>
|
||||
video tutorial
|
||||
</a>{' '}
|
||||
to understand practical example of how to use fetch plugin.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-6">
|
||||
{resource.type === TYPES.XHR && fetchPresented && (
|
||||
<div>
|
||||
<Tabs tabs={tabs} active={activeTab} onClick={this.onTabClick} border={true} />
|
||||
<div style={{ height: 'calc(100vh - 314px)', overflowY: 'auto' }}>
|
||||
{this.renderActiveTab(activeTab)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* <div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">
|
||||
<Button variant="outline" onClick={prevClick} disabled={first}>
|
||||
Prev
|
||||
</Button>
|
||||
<Button variant="outline" onClick={nextClick} disabled={last}>
|
||||
Next
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import FetchBasicDetails from './components/FetchBasicDetails';
|
||||
import { Button } from 'UI';
|
||||
import FetchPluginMessage from './components/FetchPluginMessage';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import FetchTabs from './components/FetchTabs/FetchTabs';
|
||||
|
||||
interface Props {
|
||||
resource: any;
|
||||
rows: any;
|
||||
fetchPresented?: boolean;
|
||||
}
|
||||
function FetchDetailsModal(props: Props) {
|
||||
const { rows, fetchPresented = false } = props;
|
||||
const [resource, setResource] = useState(props.resource);
|
||||
const [first, setFirst] = useState(false);
|
||||
const [last, setLast] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const index = rows.indexOf(resource);
|
||||
const length = rows.length - 1;
|
||||
setFirst(index === 0);
|
||||
setLast(index === length);
|
||||
}, [resource]);
|
||||
|
||||
const prevClick = () => {
|
||||
const index = rows.indexOf(resource);
|
||||
if (index > 0) {
|
||||
setResource(rows[index - 1]);
|
||||
}
|
||||
};
|
||||
|
||||
const nextClick = () => {
|
||||
const index = rows.indexOf(resource);
|
||||
if (index < rows.length - 1) {
|
||||
setResource(rows[index + 1]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-5 h-screen overflow-y-auto" style={{ width: '500px' }}>
|
||||
<h5 className="mb-2 text-2xl">Network Request</h5>
|
||||
<FetchBasicDetails resource={resource} />
|
||||
|
||||
{resource.type === TYPES.XHR && !fetchPresented && <FetchPluginMessage />}
|
||||
|
||||
{resource.type === TYPES.XHR && fetchPresented && <FetchTabs resource={resource} />}
|
||||
|
||||
{rows && rows.length > 0 && (
|
||||
<div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">
|
||||
<Button variant="outline" onClick={prevClick} disabled={first}>
|
||||
Prev
|
||||
</Button>
|
||||
<Button variant="outline" onClick={nextClick} disabled={last}>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FetchDetailsModal;
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import React from 'react';
|
||||
import { Icon } from 'UI';
|
||||
import { formatBytes } from 'App/utils';
|
||||
import CopyText from 'Shared/CopyText';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
|
||||
interface Props {
|
||||
resource: any;
|
||||
}
|
||||
function FetchBasicDetails({ resource }: Props) {
|
||||
const _duration = parseInt(resource.duration);
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Name</div>
|
||||
<div className="rounded-lg bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip cursor-pointer">
|
||||
<CopyText content={resource.url}>{resource.name}</CopyText>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Type</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{resource.type}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!!resource.decodedBodySize && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Size</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{formatBytes(resource.decodedBodySize)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{resource.method && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Request Method</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{resource.method}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{resource.status && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Status</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip flex items-center">
|
||||
{resource.status === '200' && (
|
||||
<div className="w-4 h-4 bg-green rounded-full mr-2"></div>
|
||||
)}
|
||||
{resource.status}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!!_duration && (
|
||||
<div className="flex items-center py-1">
|
||||
<div className="font-medium">Time</div>
|
||||
<div className="rounded bg-active-blue px-2 py-1 ml-2 whitespace-nowrap overflow-hidden text-clip">
|
||||
{_duration} ms
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FetchBasicDetails;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './FetchBasicDetails';
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import { Icon } from 'UI';
|
||||
|
||||
function FetchPluginMessage() {
|
||||
return (
|
||||
<div className="bg-active-blue rounded p-3 mt-4">
|
||||
<div className="mb-2 flex items-center">
|
||||
<Icon name="lightbulb" size="18" />
|
||||
<span className="ml-2 font-medium">Get more out of network requests</span>
|
||||
</div>
|
||||
<ul className="list-disc ml-5">
|
||||
<li>
|
||||
Integrate{' '}
|
||||
<a href="https://docs.openreplay.com/plugins/fetch" className="link" target="_blank">
|
||||
Fetch plugin
|
||||
</a>{' '}
|
||||
to capture fetch payloads.
|
||||
</li>
|
||||
<li>
|
||||
Find a detailed{' '}
|
||||
<a href="https://www.youtube.com/watch?v=YFCKstPZzZg" className="link" target="_blank">
|
||||
video tutorial
|
||||
</a>{' '}
|
||||
to understand practical example of how to use fetch plugin.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FetchPluginMessage;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './FetchPluginMessage';
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Headers from '../Headers';
|
||||
import { JSONTree, Tabs, NoContent } from 'UI';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
||||
const HEADERS = 'HEADERS';
|
||||
const REQUEST = 'REQUEST';
|
||||
const RESPONSE = 'RESPONSE';
|
||||
const TABS = [HEADERS, REQUEST, RESPONSE].map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
interface Props {
|
||||
resource: any;
|
||||
}
|
||||
function FetchTabs(props: Props) {
|
||||
const { resource } = props;
|
||||
const [activeTab, setActiveTab] = useState(HEADERS);
|
||||
const onTabClick = (tab: string) => setActiveTab(tab);
|
||||
const [jsonPayload, setJsonPayload] = useState(null);
|
||||
const [jsonResponse, setJsonResponse] = useState(null);
|
||||
const [requestHeaders, setRequestHeaders] = useState(null);
|
||||
const [responseHeaders, setResponseHeaders] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const { payload, response } = resource;
|
||||
|
||||
try {
|
||||
let jsonPayload = typeof payload === 'string' ? JSON.parse(payload) : payload;
|
||||
let requestHeaders = jsonPayload.headers;
|
||||
jsonPayload.body =
|
||||
typeof jsonPayload.body === 'string' ? JSON.parse(jsonPayload.body) : jsonPayload.body;
|
||||
delete jsonPayload.headers;
|
||||
setJsonPayload(jsonPayload);
|
||||
setRequestHeaders(requestHeaders);
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
let jsonResponse = typeof response === 'string' ? JSON.parse(response) : response;
|
||||
let responseHeaders = jsonResponse.headers;
|
||||
jsonResponse.body =
|
||||
typeof jsonResponse.body === 'string' ? JSON.parse(jsonResponse.body) : jsonResponse.body;
|
||||
delete jsonResponse.headers;
|
||||
setJsonResponse(jsonResponse);
|
||||
setResponseHeaders(responseHeaders);
|
||||
} catch (e) {}
|
||||
}, [resource, activeTab]);
|
||||
|
||||
const renderActiveTab = () => {
|
||||
const { payload, response } = resource;
|
||||
switch (activeTab) {
|
||||
case REQUEST:
|
||||
return (
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">Body is Empty.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={!payload}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
{jsonPayload === undefined ? (
|
||||
<div className="ml-3 break-words my-3"> {payload} </div>
|
||||
) : (
|
||||
<JSONTree src={jsonPayload} collapsed={false} enableClipboard />
|
||||
)}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
case RESPONSE:
|
||||
return (
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_RESULTS} size="170" />
|
||||
<div className="mt-6 text-2xl">Body is Empty.</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={!response}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
{jsonResponse === undefined ? (
|
||||
<div className="ml-3 break-words my-3"> {response} </div>
|
||||
) : (
|
||||
<JSONTree src={jsonResponse} collapsed={false} enableClipboard />
|
||||
)}
|
||||
</div>
|
||||
<div className="divider" />
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
case HEADERS:
|
||||
return <Headers requestHeaders={requestHeaders} responseHeaders={responseHeaders} />;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Tabs tabs={TABS} active={activeTab} onClick={onTabClick} border={true} />
|
||||
<div style={{ height: 'calc(100vh - 314px)', overflowY: 'auto' }}>{renderActiveTab()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FetchTabs;
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
import React from 'react'
|
||||
import { NoContent, TextEllipsis } from 'UI'
|
||||
import stl from './headers.module.css'
|
||||
import React from 'react';
|
||||
import { NoContent, TextEllipsis } from 'UI';
|
||||
import stl from './headers.module.css';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
||||
function Headers(props) {
|
||||
interface Props {
|
||||
requestHeaders: any;
|
||||
responseHeaders: any;
|
||||
}
|
||||
function Headers(props: Props) {
|
||||
return (
|
||||
<div>
|
||||
<NoContent
|
||||
|
|
@ -14,37 +18,33 @@ function Headers(props) {
|
|||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={ !props.requestHeaders && !props.responseHeaders }
|
||||
show={!props.requestHeaders && !props.responseHeaders}
|
||||
// animatedIcon="no-results"
|
||||
>
|
||||
{ props.requestHeaders && (
|
||||
{props.requestHeaders && (
|
||||
<>
|
||||
<div className="mb-4 mt-4">
|
||||
<div className="my-2 font-medium">Request Headers</div>
|
||||
{
|
||||
Object.keys(props.requestHeaders).map(h => (
|
||||
<div className={stl.row}>
|
||||
<span className="mr-2 font-medium">{h}:</span>
|
||||
<span>{props.requestHeaders[h]}</span>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{Object.keys(props.requestHeaders).map((h) => (
|
||||
<div className={stl.row}>
|
||||
<span className="mr-2 font-medium">{h}:</span>
|
||||
<span>{props.requestHeaders[h]}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
</>
|
||||
)}
|
||||
|
||||
{ props.responseHeaders && (
|
||||
|
||||
{props.responseHeaders && (
|
||||
<div className="mt-4">
|
||||
<div className="my-2 font-medium">Response Headers</div>
|
||||
{
|
||||
Object.keys(props.responseHeaders).map(h => (
|
||||
<div className={stl.row}>
|
||||
<span className="mr-2 font-medium">{h}:</span>
|
||||
<span>{props.responseHeaders[h]}</span>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{Object.keys(props.responseHeaders).map((h) => (
|
||||
<div className={stl.row}>
|
||||
<span className="mr-2 font-medium">{h}:</span>
|
||||
<span>{props.responseHeaders[h]}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</NoContent>
|
||||
|
|
@ -52,4 +52,4 @@ function Headers(props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default Headers;
|
||||
export default Headers;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import cn from 'classnames';
|
||||
import stl from './tabs.module.css';
|
||||
|
||||
const Tabs = ({ tabs, active, onClick, border = true, className }) => (
|
||||
const Tabs = ({ tabs, active, onClick, border = true, className = '' }) => (
|
||||
<div className={ cn(stl.tabs, className, { [ stl.bordered ]: border }) } role="tablist" >
|
||||
{ tabs.map(({ key, text, hidden = false, disabled = false }) => (
|
||||
<div
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue