[INS-1979] Add disconnect dropdown (#5236)

* add disconnect dropdown

* remove console

* add style fix for ux

* add e2e tests

* rename

* Simplify the smoke websocket smoke tests

Co-authored-by: Filipe Freire <livrofubia@gmail.com>
This commit is contained in:
Mark Kim
2022-10-04 09:49:41 -04:00
committed by GitHub
parent b90b5c993e
commit edad158d65
6 changed files with 127 additions and 5 deletions

View File

@@ -49,4 +49,13 @@ test('can make websocket connection', async ({ app, page }) => {
await page.click('[data-testid="response-pane"] >> [role="tab"]:has-text("Timeline")');
await expect(responseBody).toContainText('WebSocket connection established');
const webSocketActiveConnections = page.locator('[data-testid="WebSocketSpinner__Connected"]');
// Basic auth, Bearer auth, and Redirect connections are displayed as open
await expect(webSocketActiveConnections).toHaveCount(3);
// Can disconnect from all connections
await page.locator('button[name="DisconnectDropdown__DropdownButton"]').click();
await page.locator('text=Disconnect all requests').click();
await expect(webSocketActiveConnections).toHaveCount(0);
});

View File

@@ -1,6 +1,6 @@
import { autoBindMethodsForReact } from 'class-autobind-decorator';
import classnames from 'classnames';
import React, { createElement, PureComponent, ReactNode } from 'react';
import React, { ButtonHTMLAttributes, createElement, PureComponent, ReactNode } from 'react';
import { AUTOBIND_CFG } from '../../../../common/constants';
@@ -15,6 +15,7 @@ interface Props {
children: ReactNode;
className?: string;
color?: string;
unsetStyles?: boolean;
}
@autoBindMethodsForReact(AUTOBIND_CFG)
@@ -44,6 +45,7 @@ export class DropdownItem extends PureComponent<Props> {
className,
color,
onClick,
unsetStyles,
// eslint-disable-line @typescript-eslint/no-unused-vars
stayOpenAfterClick,
// eslint-disable-line @typescript-eslint/no-unused-vars
@@ -61,11 +63,16 @@ export class DropdownItem extends PureComponent<Props> {
</div>
</div>
);
const buttonProps = {
const buttonProps: ButtonHTMLAttributes<HTMLButtonElement> = {
type: 'button',
onClick: this._handleClick,
...props,
};
if (unsetStyles) {
buttonProps.className = 'dropdown__item-button-unset';
}
return createElement(buttonClass || 'button', buttonProps, inner);
}
}

View File

@@ -337,5 +337,5 @@ export const SidebarRequestRow = DropTarget('SIDEBAR_REQUEST_ROW', dragTarget, t
const WebSocketSpinner = ({ requestId }: { requestId: string }) => {
const readyState = useWSReadyState(requestId);
return readyState === ReadyState.OPEN ? <ConnectionCircle /> : null;
return readyState === ReadyState.OPEN ? <ConnectionCircle data-testid="WebSocketSpinner__Connected" /> : null;
};

View File

@@ -10,6 +10,7 @@ import { OneLineEditor } from '../codemirror/one-line-editor';
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
import { showAlert, showModal } from '../modals';
import { RequestRenderErrorModal } from '../modals/request-render-error-modal';
import { DisconnectButton } from './disconnect-button';
const Button = styled.button<{ warning?: boolean }>(({ warning }) => ({
paddingRight: 'var(--padding-md)',
@@ -161,7 +162,7 @@ export const WebSocketActionBar: FC<ActionBarProps> = ({ request, workspaceId, e
</StyledUrlBar>
{isConnectingOrClosed
? <Button type="submit">Connect</Button>
: <Button type="submit" warning>Disconnect</Button>}
: <DisconnectButton requestId={request._id} />}
</Form>
</>
);

View File

@@ -0,0 +1,100 @@
import React, { FC, useRef } from 'react';
import styled from 'styled-components';
import { Dropdown as OriginalDropdown, DropdownHandle } from '../base/dropdown/dropdown';
import { DROPDOWN_BUTTON_DISPLAY_NAME, DropdownButton as OriginalDropdownButton } from '../base/dropdown/dropdown-button';
import { DropdownItem } from '../base/dropdown/dropdown-item';
const SplitButton = styled.div({
display: 'flex',
color: 'var(--color-font-surprise)',
});
const Dropdown = styled(OriginalDropdown)({
display: 'flex',
textAlign: 'center',
borderLeft: '1px solid var(--hl-md)',
background: 'var(--color-danger)',
':hover': {
opacity: 0.9,
},
});
const DropdownButton = styled(OriginalDropdownButton)({
paddingRight: 'var(--padding-xs)',
paddingLeft: 'var(--padding-xs)',
});
DropdownButton.displayName = DROPDOWN_BUTTON_DISPLAY_NAME;
const ActionButton = styled.button({
paddingRight: 'var(--padding-md)',
paddingLeft: 'var(--padding-md)',
background: 'var(--color-danger)',
':hover': {
opacity: 0.9,
},
});
const Connections = styled.div({
display: 'flex',
justifyContent: 'space-evenly',
width: 25,
});
const Connection = styled.div<{ size?: number }>(({ size = 10 }) => ({
borderRadius: '50%',
width: size,
height: size,
background: 'var(--color-success)',
}));
const TextWrapper = styled.div({
textAlign: 'left',
width: '100%',
paddingLeft: 'var(--padding-xs)',
});
export const DisconnectButton: FC<{ requestId: string }> = ({ requestId }) => {
const dropdownRef = useRef<DropdownHandle>();
const handleCloseThisRequest = () => {
window.main.webSocket.close({ requestId });
};
const handleCloseAllRequests = () => {
window.main.webSocket.closeAll();
};
return (
<SplitButton>
<ActionButton
type="button"
onClick={handleCloseThisRequest}
>
Disconnect
</ActionButton>
<Dropdown
key="dropdown"
className="tall"
right
data-testid="DisconnectDropdown__Dropdown"
>
<DropdownButton
name="DisconnectDropdown__DropdownButton"
onClick={() => dropdownRef.current?.show()}
>
<i className="fa fa-caret-down" />
</DropdownButton>
<DropdownItem unsetStyles onClick={handleCloseThisRequest}>
<Connections>
<Connection />
</Connections>
<TextWrapper>
Disconnect this request
</TextWrapper>
</DropdownItem>
<DropdownItem unsetStyles onClick={handleCloseAllRequests}>
<Connections>
<Connection size={5} />
<Connection size={5} />
<Connection size={5} />
</Connections>
<TextWrapper>
Disconnect all requests
</TextWrapper>
</DropdownItem>
</Dropdown>
</SplitButton>
);
};

View File

@@ -126,7 +126,12 @@
text-align: center;
}
}
li > .dropdown__item-button-unset {
min-width: unset;
.dropdown__text > * {
margin-left: unset;
}
}
li > button:hover:not(:disabled),
li.active > button:not(:disabled) {
background: var(--hl-sm);