feat(Response Pane): improve tabs styles (#7765)

* update tabs styling for response panes

* Rename Timeline to Console in e2e tests

* timeline -> console in mock panes

* update e2e
This commit is contained in:
James Gatz 2024-07-26 14:35:26 +02:00 committed by GitHub
parent 41bbb70da6
commit f411eb7f21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 142 additions and 118 deletions

View File

@ -34,7 +34,7 @@ test('can use node-libcurl, httpsnippet, hidden browser window', async ({ app, p
await page.getByLabel('Request Collection').getByTestId('sends request with pre-request script').press('Enter'); await page.getByLabel('Request Collection').getByTestId('sends request with pre-request script').press('Enter');
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
await expect(statusTag).toContainText('200 OK'); await expect(statusTag).toContainText('200 OK');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
}); });
test('can use external modules in scripts ', async ({ app, page }) => { test('can use external modules in scripts ', async ({ app, page }) => {

View File

@ -28,7 +28,7 @@ test.describe('after-response script features tests', async () => {
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify // verify
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responsePane).toContainText('✓ happy tests'); await expect(responsePane).toContainText('✓ happy tests');
await expect(responsePane).toContainText('✕ unhappy tests: AssertionError: expected 199 to deeply equal 200'); await expect(responsePane).toContainText('✕ unhappy tests: AssertionError: expected 199 to deeply equal 200');

View File

@ -45,7 +45,7 @@ test('can send requests', async ({ app, page }) => {
await page.getByLabel('Request Collection').getByTestId('connects to event stream and shows ping response').press('Enter'); await page.getByLabel('Request Collection').getByTestId('connects to event stream and shows ping response').press('Enter');
await page.getByTestId('request-pane').getByRole('button', { name: 'Connect' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Connect' }).click();
await expect(statusTag).toContainText('200 OK'); await expect(statusTag).toContainText('200 OK');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responseBody).toContainText('Connected to 127.0.0.1'); await expect(responseBody).toContainText('Connected to 127.0.0.1');
await page.getByTestId('request-pane').getByRole('button', { name: 'Disconnect' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Disconnect' }).click();
@ -66,7 +66,7 @@ test('can send requests', async ({ app, page }) => {
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
await expect(statusTag).toContainText('200 OK'); await expect(statusTag).toContainText('200 OK');
// TODO(filipe): re-add a check for the preview that is less flaky // TODO(filipe): re-add a check for the preview that is less flaky
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.locator('pre').filter({ hasText: '< Content-Type: application/pdf' }).click(); await page.locator('pre').filter({ hasText: '< Content-Type: application/pdf' }).click();
await page.getByLabel('Request Collection').getByTestId('sends request with basic authentication').press('Enter'); await page.getByLabel('Request Collection').getByTestId('sends request with basic authentication').press('Enter');
@ -77,7 +77,7 @@ test('can send requests', async ({ app, page }) => {
await page.getByLabel('Request Collection').getByTestId('sends request with cookie and get cookie in response').press('Enter'); await page.getByLabel('Request Collection').getByTestId('sends request with cookie and get cookie in response').press('Enter');
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
await expect(statusTag).toContainText('200 OK'); await expect(statusTag).toContainText('200 OK');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responseBody).toContainText('Set-Cookie: insomnia-test-cookie=value123'); await expect(responseBody).toContainText('Set-Cookie: insomnia-test-cookie=value123');
}); });

View File

@ -44,7 +44,7 @@ test.describe('Cookie editor', async () => {
await page.click('[data-testid="request-pane"] button:has-text("Send")'); await page.click('[data-testid="request-pane"] button:has-text("Send")');
// Check in the timeline that the cookie was sent // Check in the timeline that the cookie was sent
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.click('text=foo2=bar2; foo=b123ar'); await page.click('text=foo2=bar2; foo=b123ar');
// Send ws request // Send ws request
@ -53,7 +53,7 @@ test.describe('Cookie editor', async () => {
await page.click('[data-testid="request-pane"] >> text=Connect'); await page.click('[data-testid="request-pane"] >> text=Connect');
// Check in the timeline that the cookie was sent // Check in the timeline that the cookie was sent
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.click('text=foo2=bar2; foo=b123ar;'); await page.click('text=foo2=bar2; foo=b123ar;');
}); });

View File

@ -30,7 +30,7 @@ test.describe('Environment Editor', async () => {
// Send a request check variables defaulted to base env since new env is empty // Send a request check variables defaulted to base env since new env is empty
await page.getByLabel('Request Collection').getByTestId('New Request').press('Enter'); await page.getByLabel('Request Collection').getByTestId('New Request').press('Enter');
await page.getByRole('button', { name: 'Send' }).click(); await page.getByRole('button', { name: 'Send' }).click();
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.getByText('baseenv0').click(); await page.getByText('baseenv0').click();
await page.getByText('baseenv1').click(); await page.getByText('baseenv1').click();
}); });
@ -53,7 +53,7 @@ test.describe('Environment Editor', async () => {
await page.getByLabel('Request Collection').getByTestId('New Request').press('Enter'); await page.getByLabel('Request Collection').getByTestId('New Request').press('Enter');
// await page.waitForTimeout(60000); // await page.waitForTimeout(60000);
await page.getByRole('button', { name: 'Send' }).click(); await page.getByRole('button', { name: 'Send' }).click();
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.getByText('subenvB0').click(); await page.getByText('subenvB0').click();
await page.getByText('subenvB1').click(); await page.getByText('subenvB1').click();
}); });
@ -94,7 +94,7 @@ test.describe('Environment Editor', async () => {
// Check new variables are in the timeline // Check new variables are in the timeline
await page.getByRole('button', { name: 'Send' }).click(); await page.getByRole('button', { name: 'Send' }).click();
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
// FIXME(filipe) - adding variables to request body can be so fast they don't get picked up when sending request // FIXME(filipe) - adding variables to request body can be so fast they don't get picked up when sending request

View File

@ -15,6 +15,6 @@ test('can make a mock route: WARNING: THIS TEST DEPENDS ON mock.insomnia.moe to
await page.getByRole('button', { name: 'Test' }).click(); await page.getByRole('button', { name: 'Test' }).click();
await page.getByText('No body returned for response').click(); await page.getByText('No body returned for response').click();
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.getByText('HTTP/2 200').click(); await page.getByText('HTTP/2 200').click();
}); });

View File

@ -368,7 +368,7 @@ test.describe('pre-request features tests', async () => {
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify // verify
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responsePane).toContainText('localhost:2222'); // original proxy await expect(responsePane).toContainText('localhost:2222'); // original proxy
await expect(responsePane).toContainText('Trying 127.0.0.1:8888'); // updated proxy await expect(responsePane).toContainText('Trying 127.0.0.1:8888'); // updated proxy
}); });
@ -395,7 +395,7 @@ test.describe('pre-request features tests', async () => {
// send // send
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify // verify
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responsePane).toContainText('fixtures/certificates/fake.pfx'); // original proxy await expect(responsePane).toContainText('fixtures/certificates/fake.pfx'); // original proxy
}); });
@ -408,7 +408,7 @@ test.describe('pre-request features tests', async () => {
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click(); await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
// verify // verify
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responsePane).toContainText('✓ happy tests'); await expect(responsePane).toContainText('✓ happy tests');
await expect(responsePane).toContainText('✕ unhappy tests: AssertionError: expected 199 to deeply equal 200'); await expect(responsePane).toContainText('✕ unhappy tests: AssertionError: expected 199 to deeply equal 200');

View File

@ -54,8 +54,8 @@ test.describe('test hidden window handling', async () => {
await page.waitForSelector('[data-testid="response-status-tag"]:visible'); await page.waitForSelector('[data-testid="response-status-tag"]:visible');
expect(await page.locator('.pane-two pre').innerText()).toEqual('Timeout: Running script took too long'); expect(await page.locator('.pane-two pre').innerText()).toEqual('Timeout: Running script took too long');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await page.getByRole('tab', { name: 'Preview' }).click(); await page.getByRole('tab', { name: 'Preview' }).click();
const windows = await app.windows(); const windows = await app.windows();
const hiddenWindow = windows[1]; const hiddenWindow = windows[1];
hiddenWindow.close(); hiddenWindow.close();

View File

@ -29,7 +29,7 @@ test('Check filter responses by environment preference', async ({ app, page }) =
// Send a request // Send a request
await page.getByLabel('Request Collection').getByTestId('example http').press('Enter'); await page.getByLabel('Request Collection').getByTestId('example http').press('Enter');
await page.click('[data-testid="request-pane"] button:has-text("Send")'); await page.click('[data-testid="request-pane"] button:has-text("Send")');
await page.click('text=Timeline'); await page.click('text=Console');
await page.locator('text=HTTP/1.1 200 OK').click(); await page.locator('text=HTTP/1.1 200 OK').click();
// Set filter responses by environment // Set filter responses by environment
@ -40,6 +40,6 @@ test('Check filter responses by environment preference', async ({ app, page }) =
// Re-send the request and check timeline // Re-send the request and check timeline
await page.locator('[data-testid="request-pane"] button:has-text("Send")').click(); await page.locator('[data-testid="request-pane"] button:has-text("Send")').click();
await page.click('text=Timeline'); await page.click('text=Console');
await page.locator('text=HTTP/1.1 200 OK').click(); await page.locator('text=HTTP/1.1 200 OK').click();
}); });

View File

@ -25,7 +25,7 @@ test('can make websocket connection', async ({ app, page }) => {
await expect(page.locator('.app')).toContainText('ws://localhost:4010'); await expect(page.locator('.app')).toContainText('ws://localhost:4010');
await page.click('text=Connect'); await page.click('text=Connect');
await expect(statusTag).toContainText('101 Switching Protocols'); await expect(statusTag).toContainText('101 Switching Protocols');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responseBody).toContainText('WebSocket connection established'); await expect(responseBody).toContainText('WebSocket connection established');
await page.click('text=Disconnect'); await page.click('text=Disconnect');
await expect(responseBody).toContainText('Closing connection with code 1005'); await expect(responseBody).toContainText('Closing connection with code 1005');
@ -35,7 +35,7 @@ test('can make websocket connection', async ({ app, page }) => {
await expect(page.locator('.app')).toContainText('ws://localhost:4010/basic-auth'); await expect(page.locator('.app')).toContainText('ws://localhost:4010/basic-auth');
await page.click('text=Connect'); await page.click('text=Connect');
await expect(statusTag).toContainText('101 Switching Protocols'); await expect(statusTag).toContainText('101 Switching Protocols');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responseBody).toContainText('> authorization: Basic dXNlcjpwYXNzd29yZA=='); await expect(responseBody).toContainText('> authorization: Basic dXNlcjpwYXNzd29yZA==');
// Can connect with Bearer Auth // Can connect with Bearer Auth
@ -43,7 +43,7 @@ test('can make websocket connection', async ({ app, page }) => {
await expect(page.locator('.app')).toContainText('ws://localhost:4010/bearer'); await expect(page.locator('.app')).toContainText('ws://localhost:4010/bearer');
await page.click('text=Connect'); await page.click('text=Connect');
await expect(statusTag).toContainText('101 Switching Protocols'); await expect(statusTag).toContainText('101 Switching Protocols');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responseBody).toContainText('> authorization: Bearer insomnia-cool-token-!!!1112113243111'); await expect(responseBody).toContainText('> authorization: Bearer insomnia-cool-token-!!!1112113243111');
// Can handle redirects // Can handle redirects
@ -51,7 +51,7 @@ test('can make websocket connection', async ({ app, page }) => {
await expect(page.locator('.app')).toContainText('ws://localhost:4010/redirect'); await expect(page.locator('.app')).toContainText('ws://localhost:4010/redirect');
await page.click('text=Connect'); await page.click('text=Connect');
await expect(statusTag).toContainText('101 Switching Protocols'); await expect(statusTag).toContainText('101 Switching Protocols');
await page.getByRole('tab', { name: 'Timeline' }).click(); await page.getByRole('tab', { name: 'Console' }).click();
await expect(responseBody).toContainText('WebSocket connection established'); await expect(responseBody).toContainText('WebSocket connection established');
const webSocketActiveConnections = page.locator('[data-testid="WebSocketSpinner__Connected"]'); const webSocketActiveConnections = page.locator('[data-testid="WebSocketSpinner__Connected"]');

View File

@ -120,7 +120,7 @@ export const MockResponsePane = () => {
<TabItem key="headers" title="Headers"> <TabItem key="headers" title="Headers">
<ResponseHeadersViewer headers={activeResponse?.headers || []} /> <ResponseHeadersViewer headers={activeResponse?.headers || []} />
</TabItem> </TabItem>
<TabItem key="timeline" title="Timeline"> <TabItem key="timeline" title="Console">
<ResponseTimelineViewer <ResponseTimelineViewer
key={activeResponse?._id} key={activeResponse?._id}
timeline={timeline} timeline={timeline}

View File

@ -1,6 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import { extension as mimeExtension } from 'mime-types'; import { extension as mimeExtension } from 'mime-types';
import React, { type FC, useCallback } from 'react'; import React, { type FC, useCallback } from 'react';
import { Tab, TabList, TabPanel, Tabs, Toolbar } from 'react-aria-components';
import { useRouteLoaderData } from 'react-router-dom'; import { useRouteLoaderData } from 'react-router-dom';
import { PREVIEW_MODE_SOURCE } from '../../../common/constants'; import { PREVIEW_MODE_SOURCE } from '../../../common/constants';
@ -12,7 +13,6 @@ import { useExecutionState } from '../../hooks/use-execution-state';
import { useRequestMetaPatcher } from '../../hooks/use-request'; import { useRequestMetaPatcher } from '../../hooks/use-request';
import type { RequestLoaderData } from '../../routes/request'; import type { RequestLoaderData } from '../../routes/request';
import { useRootLoaderData } from '../../routes/root'; import { useRootLoaderData } from '../../routes/root';
import { PanelContainer, TabItem, Tabs } from '../base/tabs';
import { PreviewModeDropdown } from '../dropdowns/preview-mode-dropdown'; import { PreviewModeDropdown } from '../dropdowns/preview-mode-dropdown';
import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown'; import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown';
import { MockResponseExtractor } from '../editors/mock-response-extractor'; import { MockResponseExtractor } from '../editors/mock-response-extractor';
@ -157,16 +157,52 @@ export const ResponsePane: FC<Props> = ({
/> />
</PaneHeader> </PaneHeader>
)} )}
<Tabs aria-label="Response pane tabs"> <Tabs aria-label='Request group tabs' className="flex-1 w-full h-full flex flex-col">
<TabItem <TabList className='w-full flex-shrink-0 overflow-x-auto border-solid scro border-b border-b-[--hl-md] bg-[--color-bg] flex items-center h-[--line-height-sm]' aria-label='Request pane tabs'>
key="preview" <Tab
title={ className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='preview'
>
Preview
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='headers'
>
Headers
{activeResponse.headers.length > 0 && (
<span className="p-2 aspect-square flex items-center justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{activeResponse.headers.length}</span>
)}
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='cookies'
>
Cookies
{cookieHeaders.length > 0 && (
<span className="p-2 aspect-square flex items-center justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{cookieHeaders.length}</span>
)}
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='mock-response'
>
Mock
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='timeline'
>
Console
</Tab>
</TabList>
<TabPanel className='w-full flex-1 flex flex-col overflow-hidden' id='preview'>
<Toolbar className="w-full flex-shrink-0 h-[--line-height-sm] border-b border-solid border-[--hl-md] flex items-center px-2">
<PreviewModeDropdown <PreviewModeDropdown
download={handleDownloadResponseBody} download={handleDownloadResponseBody}
copyToClipboard={handleCopyResponseToClipboard} copyToClipboard={handleCopyResponseToClipboard}
/> />
} </Toolbar>
>
<ResponseViewer <ResponseViewer
key={activeResponse._id} key={activeResponse._id}
bytes={Math.max(activeResponse.bytesContent, activeResponse.bytesRead)} bytes={Math.max(activeResponse.bytesContent, activeResponse.bytesRead)}
@ -184,56 +220,37 @@ export const ResponsePane: FC<Props> = ({
updateFilter={activeResponse.error ? undefined : handleSetFilter} updateFilter={activeResponse.error ? undefined : handleSetFilter}
url={activeResponse.url} url={activeResponse.url}
/> />
</TabItem> </TabPanel>
<TabItem <TabPanel className='w-full flex-1 flex flex-col overflow-y-auto' id='headers'>
key="headers" <ErrorBoundary key={activeResponse._id} errorClassName="font-error pad text-center">
title={ <ResponseHeadersViewer headers={activeResponse.headers} />
<div className='flex items-center gap-2'> </ErrorBoundary>
Headers </TabPanel>
{activeResponse.headers.length > 0 && ( <TabPanel className='w-full flex-1 flex flex-col overflow-y-auto' id='cookies'>
<span className="p-2 aspect-square flex items-center color-inherit justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{activeResponse.headers.length}</span> <ErrorBoundary key={activeResponse._id} errorClassName="font-error pad text-center">
)} <ResponseCookiesViewer
</div> cookiesSent={activeResponse.settingSendCookies}
} cookiesStored={activeResponse.settingStoreCookies}
headers={cookieHeaders}
/>
</ErrorBoundary>
</TabPanel>
<TabPanel
className='w-full flex-1 flex flex-col overflow-y-auto'
id='mock-response'
> >
<PanelContainer className="pad"> <MockResponseExtractor />
<ErrorBoundary key={activeResponse._id} errorClassName="font-error pad text-center"> </TabPanel>
<ResponseHeadersViewer headers={activeResponse.headers} />
</ErrorBoundary> <TabPanel className='w-full flex-1 flex flex-col overflow-y-auto' id='timeline'>
</PanelContainer>
</TabItem>
<TabItem
key="cookies"
title={
<div className='flex items-center gap-2'>
Cookies
{cookieHeaders.length > 0 && (
<span className="p-2 aspect-square flex items-center color-inherit justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{cookieHeaders.length}</span>
)}
</div>
}
>
<PanelContainer className="pad">
<ErrorBoundary key={activeResponse._id} errorClassName="font-error pad text-center">
<ResponseCookiesViewer
cookiesSent={activeResponse.settingSendCookies}
cookiesStored={activeResponse.settingStoreCookies}
headers={cookieHeaders}
/>
</ErrorBoundary>
</PanelContainer>
</TabItem>
<TabItem key="timeline" title="Timeline">
<ErrorBoundary key={activeResponse._id} errorClassName="font-error pad text-center"> <ErrorBoundary key={activeResponse._id} errorClassName="font-error pad text-center">
<ResponseTimelineViewer <ResponseTimelineViewer
key={activeResponse._id} key={activeResponse._id}
timeline={timeline} timeline={timeline}
/> />
</ErrorBoundary> </ErrorBoundary>
</TabItem> </TabPanel>
<TabItem key="mock-response" title="Mock Response">
<MockResponseExtractor />
</TabItem>
</Tabs> </Tabs>
<ErrorBoundary errorClassName="font-error pad text-center"> <ErrorBoundary errorClassName="font-error pad text-center">
{isExecuting && <ResponseTimer {isExecuting && <ResponseTimer

View File

@ -1,6 +1,6 @@
import fs from 'fs'; import fs from 'fs';
import React, { type FC, useEffect, useState } from 'react'; import React, { type FC, useEffect, useState } from 'react';
import { Button, Input, SearchField } from 'react-aria-components'; import { Button, Input, SearchField, Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { useRouteLoaderData } from 'react-router-dom'; import { useRouteLoaderData } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
@ -13,7 +13,6 @@ import type { Response } from '../../../models/response';
import type { WebSocketResponse } from '../../../models/websocket-response'; import type { WebSocketResponse } from '../../../models/websocket-response';
import { useRealtimeConnectionEvents } from '../../hooks/use-realtime-connection-events'; import { useRealtimeConnectionEvents } from '../../hooks/use-realtime-connection-events';
import type { RequestLoaderData, WebSocketRequestLoaderData } from '../../routes/request'; import type { RequestLoaderData, WebSocketRequestLoaderData } from '../../routes/request';
import { PanelContainer, TabItem, Tabs } from '../base/tabs';
import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown'; import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown';
import { ErrorBoundary } from '../error-boundary'; import { ErrorBoundary } from '../error-boundary';
import { Icon } from '../icon'; import { Icon } from '../icon';
@ -135,8 +134,40 @@ const RealtimeActiveResponsePane: FC<{ response: WebSocketResponse | Response }>
activeResponse={response} activeResponse={response}
/> />
</PaneHeader> </PaneHeader>
<Tabs aria-label="Curl response pane tabs"> <Tabs aria-label='Request group tabs' className="flex-1 w-full h-full flex flex-col">
<TabItem key="events" title="Events"> <TabList className='w-full flex-shrink-0 overflow-x-auto border-solid scro border-b border-b-[--hl-md] bg-[--color-bg] flex items-center h-[--line-height-sm]' aria-label='Request pane tabs'>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='events'
>
Events
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='headers'
>
Headers
{response.headers.length > 0 && (
<span className="p-2 aspect-square flex items-center justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{response.headers.length}</span>
)}
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='cookies'
>
Cookies
{cookieHeaders.length > 0 && (
<span className="p-2 aspect-square flex items-center justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{cookieHeaders.length}</span>
)}
</Tab>
<Tab
className='flex-shrink-0 h-full flex items-center justify-between cursor-pointer gap-2 outline-none select-none px-3 py-1 text-[--hl] aria-selected:text-[--color-font] hover:bg-[--hl-sm] hover:text-[--color-font] aria-selected:bg-[--hl-xs] aria-selected:focus:bg-[--hl-sm] aria-selected:hover:bg-[--hl-sm] focus:bg-[--hl-sm] transition-colors duration-300'
id='timeline'
>
Console
</Tab>
</TabList>
<TabPanel className='w-full flex-1 flex flex-col overflow-hidden' id='events'>
<PanelGroup direction='vertical' className='h-full w-full grid grid-rows-[repeat(auto-fit,minmax(0,1fr))]'> <PanelGroup direction='vertical' className='h-full w-full grid grid-rows-[repeat(auto-fit,minmax(0,1fr))]'>
{response.error ? <ResponseErrorViewer url={response.url} error={response.error} /> {response.error ? <ResponseErrorViewer url={response.url} error={response.error} />
: <> : <>
@ -210,52 +241,28 @@ const RealtimeActiveResponsePane: FC<{ response: WebSocketResponse | Response }>
)} )}
</>} </>}
</PanelGroup> </PanelGroup>
</TabItem> </TabPanel>
<TabItem <TabPanel className='w-full flex-1 flex flex-col overflow-y-auto' id='headers'>
key="headers" <ErrorBoundary key={response._id} errorClassName="font-error pad text-center">
title={ <ResponseHeadersViewer headers={response.headers} />
<div className='flex items-center gap-2'> </ErrorBoundary>
Headers </TabPanel>
{response.headers.length > 0 && ( <TabPanel className='w-full flex-1 flex flex-col overflow-y-auto' id='cookies'>
<span className="p-2 aspect-square flex items-center color-inherit justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{response.headers.length}</span> <ErrorBoundary key={response._id} errorClassName="font-error pad text-center">
)} <ResponseCookiesViewer
</div> cookiesSent={response.settingSendCookies}
} cookiesStored={response.settingStoreCookies}
> headers={cookieHeaders}
<PanelContainer className="pad"> />
<ErrorBoundary key={response._id} errorClassName="font-error pad text-center"> </ErrorBoundary>
<ResponseHeadersViewer headers={response.headers} /> </TabPanel>
</ErrorBoundary> <TabPanel className='w-full flex-1 flex flex-col overflow-hidden' id='timeline'>
</PanelContainer>
</TabItem>
<TabItem
key="cookies"
title={
<div className='flex items-center gap-2'>
Cookies
{cookieHeaders.length > 0 && (
<span className="p-2 aspect-square flex items-center color-inherit justify-between border-solid border border-[--hl-md] overflow-hidden rounded-lg text-xs shadow-small">{cookieHeaders.length}</span>
)}
</div>
}
>
<PanelContainer className="pad">
<ErrorBoundary key={response._id} errorClassName="font-error pad text-center">
<ResponseCookiesViewer
cookiesSent={response.settingSendCookies}
cookiesStored={response.settingStoreCookies}
headers={cookieHeaders}
/>
</ErrorBoundary>
</PanelContainer>
</TabItem>
<TabItem key="timeline" title="Timeline">
<ResponseTimelineViewer <ResponseTimelineViewer
key={response._id} key={response._id}
timeline={timeline} timeline={timeline}
pinToBottom={true} pinToBottom={true}
/> />
</TabItem> </TabPanel>
</Tabs> </Tabs>
</ Pane> </ Pane>
); );