feat: replace send button with Send/Cancel buttons on request url (#7675)

* feat: replace request send icon with Send/Cancel buttons

---------

Co-authored-by: naman-bruno <naman@usebruno.com>
This commit is contained in:
gopu-bruno
2026-04-07 13:42:09 +05:30
committed by GitHub
parent 4d6032ba0d
commit 476d30a49e
12 changed files with 125 additions and 85 deletions

View File

@@ -324,7 +324,7 @@ test('should create and execute HTTP request', async ({ page, createTmpDir }) =>
await page.getByRole('button', { name: 'Create' }).click();
// Execute request
await page.locator('#send-request').getByRole('img').nth(2).click();
await page.getByTestId('send-arrow-icon').click();
// Verify response
await expect(page.getByRole('main')).toContainText('200 OK');

View File

@@ -2,9 +2,13 @@ import styled from 'styled-components';
const Wrapper = styled.div`
height: 2.1rem;
.url-input-group {
border: ${(props) => props.theme.requestTabPanel.url.border};
border-radius: ${(props) => props.theme.border.radius.base};
flex: 1;
min-width: 0;
}
.infotip {
position: relative;
@@ -49,6 +53,7 @@ const Wrapper = styled.div`
.shortcut {
font-size: 0.625rem;
}
`;
export default Wrapper;

View File

@@ -16,8 +16,9 @@ import { saveRequest, cancelRequest } from 'providers/ReduxStore/slices/collecti
import { getRequestFromCurlCommand } from 'utils/curl';
import HttpMethodSelector from './HttpMethodSelector';
import { useTheme } from 'providers/Theme';
import { IconDeviceFloppy, IconArrowRight, IconCode, IconSquareRoundedX } from '@tabler/icons';
import { IconDeviceFloppy, IconCode } from '@tabler/icons';
import SingleLineEditor from 'components/SingleLineEditor';
import SendButton from 'components/RequestPane/SendButton';
import { isMacOS } from 'utils/common/platform';
import { hasRequestChanges } from 'utils/collections';
import StyledWrapper from './StyledWrapper';
@@ -384,12 +385,13 @@ const QueryUrl = ({ item, collection, handleRun }) => {
};
return (
<StyledWrapper className="flex items-center w-full">
<div className="flex items-center h-full url-input-group">
<div className="flex items-center h-full min-w-fit">
<HttpMethodSelector method={method} onMethodSelect={onMethodSelect} />
</div>
<div
id="request-url"
className="h-full w-full flex flex-row input-container overflow-auto"
className="h-full w-full flex flex-row items-center input-container overflow-auto"
>
<SingleLineEditor
ref={editorRef}
@@ -405,9 +407,7 @@ const QueryUrl = ({ item, collection, handleRun }) => {
item={item}
showNewlineArrow={true}
/>
</div>
<div className="flex items-center h-full mx-2 gap-3 cursor-pointer" id="send-request" onClick={handleRun}>
<div className="flex items-center h-full mx-2 gap-3" id="request-actions">
<div
title="Generate Code"
className="infotip"
@@ -437,23 +437,15 @@ const QueryUrl = ({ item, collection, handleRun }) => {
Save <span className="shortcut">({saveShortcut})</span>
</span>
</div>
{isLoading || item.response?.stream?.running ? (
<IconSquareRoundedX
color={theme.requestTabPanel.url.iconDanger}
strokeWidth={1.5}
size={20}
data-testid="cancel-request-icon"
onClick={handleCancelRequest}
/>
) : (
<IconArrowRight
color={theme.requestTabPanel.url.icon}
strokeWidth={1.5}
size={20}
data-testid="send-arrow-icon"
/>
)}
</div>
</div>
</div>
<SendButton
isLoading={isLoading || item.response?.stream?.running}
onSend={handleRun}
onCancel={handleCancelRequest}
testId="send-arrow-icon"
/>
{generateCodeItemModalOpen && (
<GenerateCodeItem
collectionUid={collection.uid}

View File

@@ -0,0 +1,20 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
display: flex;
align-self: stretch;
min-width: 4.1rem;
flex-shrink: 0;
> div {
display: flex;
flex: 1;
}
button {
width: 100%;
height: 100%;
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,22 @@
import React from 'react';
import Button from 'ui/Button';
import StyledWrapper from './StyledWrapper';
const SendButton = ({ isLoading = false, onSend, onCancel, testId = 'send-request-btn' }) => {
return (
<StyledWrapper className="ml-2">
<Button
size="sm"
variant={isLoading ? 'outline' : 'filled'}
color="primary"
data-testid={testId}
data-action={isLoading ? 'cancel' : 'send'}
onClick={isLoading ? onCancel : onSend}
>
{isLoading ? 'Cancel' : 'Send'}
</Button>
</StyledWrapper>
);
};
export default SendButton;

View File

@@ -3,12 +3,12 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
height: 2.1rem;
position: relative;
border: ${(props) => props.theme.requestTabPanel.url.border};
border-radius: ${(props) => props.theme.border.radius.base};
.input-container {
background-color: ${(props) => props.theme.requestTabPanel.url.bg};
border: ${(props) => props.theme.requestTabPanel.url.border};
border-radius: ${(props) => props.theme.border.radius.base};
position: relative;
input {
background-color: ${(props) => props.theme.requestTabPanel.url.bg};
@@ -99,6 +99,7 @@ const StyledWrapper = styled.div`
}
}
}
`;
export default StyledWrapper;

View File

@@ -1,4 +1,5 @@
import { IconArrowRight, IconDeviceFloppy, IconPlugConnected, IconPlugConnectedX } from '@tabler/icons';
import { IconDeviceFloppy, IconPlugConnected, IconPlugConnectedX } from '@tabler/icons';
import SendButton from 'components/RequestPane/SendButton';
import classnames from 'classnames';
import SingleLineEditor from 'components/SingleLineEditor/index';
import { requestUrlChanged } from 'providers/ReduxStore/slices/collections';
@@ -123,7 +124,7 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
return (
<StyledWrapper>
<div className="flex items-center h-full">
<div className="flex items-center input-container flex-1 w-full h-full relative">
<div className="flex items-center input-container flex-1 min-w-0 h-full relative">
<div className="flex items-center justify-center px-[10px]">
<span className="text-xs font-medium method-ws">WS</span>
</div>
@@ -187,15 +188,14 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
</div>
</div>
)}
<div data-testid="run-button" className="cursor-pointer" onClick={handleRunClick}>
<IconArrowRight color={theme.requestTabPanel.url.icon} strokeWidth={1.5} size={20} />
</div>
</div>
</div>
</div>
{connectionStatus === CONNECTION_STATUS.CONNECTED && <div className="connection-status-strip"></div>}
</div>
<SendButton
onSend={handleRunClick}
testId="run-button"
/>
</div>
</StyledWrapper>
);
};

View File

@@ -19,13 +19,13 @@ test.describe('Create collection', () => {
// Set the URL
await page.locator('#request-url .CodeMirror').click();
await page.locator('#request-url').locator('textarea').fill('http://localhost:8081');
await page.locator('#send-request').getByTitle('Save Request').click();
await page.locator('#request-actions').getByTitle('Save Request').click();
// Send a request
await page.locator('#request-url .CodeMirror').click();
await page.locator('#request-url').locator('textarea').fill('/ping');
await page.locator('#send-request').getByTitle('Save Request').click();
await page.locator('#send-request').getByRole('img').nth(2).click();
await page.locator('#request-actions').getByTitle('Save Request').click();
await page.getByTestId('send-arrow-icon').click();
// Verify the response
await expect(page.getByRole('main')).toContainText('200 OK');

View File

@@ -22,7 +22,7 @@ test.describe('Multiline Variables - Read Environment Test', () => {
await expect(page.locator('.current-environment').filter({ hasText: /Test/ })).toBeVisible();
// send request
const sendButton = page.locator('#send-request').getByRole('img').nth(2);
const sendButton = page.getByTestId('send-arrow-icon');
await expect(sendButton).toBeVisible();
await sendButton.click();
await expect(page.locator('.response-status-code.text-ok')).toBeVisible();

View File

@@ -77,7 +77,7 @@ test.describe.serial('Create and Delete Response Examples', () => {
});
await test.step('Test form reset', async () => {
await page.locator('#send-request').getByRole('img').nth(2).click();
await page.getByTestId('send-arrow-icon').click();
await clickResponseAction(page, 'response-bookmark-btn');
await page.getByTestId('create-example-name-input').fill('Test Name');

View File

@@ -141,7 +141,7 @@ const createUntitledRequest = async (
if (url) {
await page.locator('#request-url .CodeMirror').click();
await page.locator('#request-url textarea').fill(url);
await page.locator('#send-request').getByTitle('Save Request').click();
await page.locator('#request-actions').getByTitle('Save Request').click();
await page.waitForTimeout(200);
}

View File

@@ -83,7 +83,7 @@ export const buildCommonLocators = (page: Page) => ({
newRequestUrl: () => page.locator('#new-request-url .CodeMirror'),
requestNameInput: () => page.getByPlaceholder('Request Name'),
requestTestId: () => page.getByTestId('request-name'),
generateCodeButton: () => page.locator('#send-request .infotip').first(),
generateCodeButton: () => page.locator('#request-actions .infotip').first(),
bodyModeSelector: () => page.getByTestId('request-body-mode-selector'),
bodyEditor: () => page.getByTestId('request-body-editor')
},