Skip to content

Commit

Permalink
[Console] Progress bar (#56628)
Browse files Browse the repository at this point in the history
* First round of UI updates
 - progress bar loader for in flight request
 - Network request status bar

* Add notification about in flight request

* Use nbsp; and update the EuiCode to use EuiBadge

* Address PR feedback:

- Clean up unused class names
- Move the network request status bar to next to the nav bar with a grey line under it
- Added the request in progress badge in the network request status bar
- Removed logic forcing one request at a time!
- Added notification for when a user is sending a request from a line with no request on it (no more 0 - None status). Also preserve the previuous request in this case

* [NB] Fix for floating tools when request is past bottom of rendered text area!! This can be backported to 7.6 as it causes
when a request is selected at the bottom of a large text buffer and the history viewer is opened.

* Fix types

* Update copy
Remove unused SCSS

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
jloleysens and elasticmachine authored Feb 6, 2020
1 parent 81c1b52 commit d20a7d0
Show file tree
Hide file tree
Showing 14 changed files with 351 additions and 82 deletions.
10 changes: 10 additions & 0 deletions src/legacy/core_plugins/console_legacy/public/styles/_app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
button {
line-height: inherit;
}

position: absolute;
z-index: $euiZLevel1;
top: 0;
Expand Down Expand Up @@ -89,3 +90,12 @@
.conApp__settingsModal {
min-width: 460px;
}

.conApp__requestProgressBarContainer {
position: relative;
z-index: $euiZLevel2;
}

.conApp__tabsExtension {
border-bottom: $euiBorderThin;
}
1 change: 1 addition & 0 deletions src/plugins/console/public/application/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/

export { NetworkRequestStatusBar } from './network_request_status_bar';
export { SomethingWentWrongCallout } from './something_went_wrong_callout';
export { TopNavMenuItem, TopNavMenu } from './top_nav_menu';
export { ConsoleMenu } from './console_menu';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export { NetworkRequestStatusBar } from './network_request_status_bar';
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiText, EuiToolTip } from '@elastic/eui';

export interface Props {
requestInProgress: boolean;
requestResult?: {
// Status code of the request, e.g., 200
statusCode: number;

// Status text of the request, e.g., OK
statusText: string;

// Method of the request, e.g., GET
method: string;

// The path of endpoint that was called, e.g., /_search
endpoint: string;

// The time, in milliseconds, that the last request took
timeElapsedMs: number;
};
}

const mapStatusCodeToBadgeColor = (statusCode: number) => {
if (statusCode <= 199) {
return 'default';
}

if (statusCode <= 299) {
return 'secondary';
}

if (statusCode <= 399) {
return 'primary';
}

if (statusCode <= 499) {
return 'warning';
}

return 'danger';
};

export const NetworkRequestStatusBar: FunctionComponent<Props> = ({
requestInProgress,
requestResult,
}) => {
let content: React.ReactNode = null;

if (requestInProgress) {
content = (
<EuiFlexItem grow={false}>
<EuiBadge color="hollow">
{i18n.translate('console.requestInProgressBadgeText', {
defaultMessage: 'Request in progress',
})}
</EuiBadge>
</EuiFlexItem>
);
} else if (requestResult) {
const { endpoint, method, statusCode, statusText, timeElapsedMs } = requestResult;

content = (
<>
<EuiFlexItem grow={false}>
<EuiToolTip
position="top"
content={
<EuiText size="s">{`${method} ${
endpoint.startsWith('/') ? endpoint : '/' + endpoint
}`}</EuiText>
}
>
<EuiBadge color={mapStatusCodeToBadgeColor(statusCode)}>
{/* Use &nbsp; to ensure that no matter the width we don't allow line breaks */}
{statusCode}&nbsp;-&nbsp;{statusText}
</EuiBadge>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip
position="top"
content={
<EuiText size="s">
{i18n.translate('console.requestTimeElapasedBadgeTooltipContent', {
defaultMessage: 'Time Elapsed',
})}
</EuiText>
}
>
<EuiText size="s">
<EuiBadge color="default">
{timeElapsedMs}&nbsp;{'ms'}
</EuiBadge>
</EuiText>
</EuiToolTip>
</EuiFlexItem>
</>
);
}

return (
<EuiFlexGroup
justifyContent="flexEnd"
alignItems="center"
direction="row"
gutterSize="s"
responsive={false}
>
{content}
</EuiFlexGroup>
);
};
53 changes: 31 additions & 22 deletions src/plugins/console/public/application/containers/editor/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
* under the License.
*/

import React, { useCallback } from 'react';
import React, { useCallback, memo } from 'react';
import { debounce } from 'lodash';
import { EuiProgress } from '@elastic/eui';

import { EditorContentSpinner } from '../../components';
import { Panel, PanelsContainer } from '../../../../../kibana_react/public';
import { Editor as EditorUI, EditorOutput } from './legacy/console_editor';
import { StorageKeys } from '../../../services';
import { useEditorReadContext, useServicesContext } from '../../contexts';
import { useEditorReadContext, useServicesContext, useRequestReadContext } from '../../contexts';

const INITIAL_PANEL_WIDTH = 50;
const PANEL_MIN_WIDTH = '100px';
Expand All @@ -33,12 +34,13 @@ interface Props {
loading: boolean;
}

export const Editor = ({ loading }: Props) => {
export const Editor = memo(({ loading }: Props) => {
const {
services: { storage },
} = useServicesContext();

const { currentTextObject } = useEditorReadContext();
const { requestInFlight } = useRequestReadContext();

const [firstPanelWidth, secondPanelWidth] = storage.get(StorageKeys.WIDTH, [
INITIAL_PANEL_WIDTH,
Expand All @@ -55,23 +57,30 @@ export const Editor = ({ loading }: Props) => {
if (!currentTextObject) return null;

return (
<PanelsContainer onPanelWidthChange={onPanelWidthChange} resizerClassName="conApp__resizer">
<Panel
style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH }}
initialWidth={firstPanelWidth}
>
{loading ? (
<EditorContentSpinner />
) : (
<EditorUI initialTextValue={currentTextObject.text} />
)}
</Panel>
<Panel
style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH }}
initialWidth={secondPanelWidth}
>
{loading ? <EditorContentSpinner /> : <EditorOutput />}
</Panel>
</PanelsContainer>
<>
{requestInFlight ? (
<div className="conApp__requestProgressBarContainer">
<EuiProgress size="xs" color="accent" position="absolute" />
</div>
) : null}
<PanelsContainer onPanelWidthChange={onPanelWidthChange} resizerClassName="conApp__resizer">
<Panel
style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH }}
initialWidth={firstPanelWidth}
>
{loading ? (
<EditorContentSpinner />
) : (
<EditorUI initialTextValue={currentTextObject.text} />
)}
</Panel>
<Panel
style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH }}
initialWidth={secondPanelWidth}
>
{loading ? <EditorContentSpinner /> : <EditorOutput />}
</Panel>
</PanelsContainer>
</>
);
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,14 @@ function EditorUI({ initialTextValue }: EditorProps) {
<EuiFlexItem>
<EuiToolTip
content={i18n.translate('console.sendRequestButtonTooltip', {
defaultMessage: 'click to send request',
defaultMessage: 'Click to send request',
})}
>
<button
onClick={sendCurrentRequestToES}
data-test-subj="sendRequestButton"
aria-label={i18n.translate('console.sendRequestButtonTooltip', {
defaultMessage: 'click to send request',
defaultMessage: 'Click to send request',
})}
className="conApp__editorActionButton conApp__editorActionButton--success"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import React, { useEffect, useRef } from 'react';

import { createReadOnlyAceEditor, CustomAceEditor } from '../../../../models/legacy_core_editor';
import {
useServicesContext,
Expand Down Expand Up @@ -70,8 +71,8 @@ function EditorOutputUI() {
.join('\n')
);
} else if (error) {
editor.session.setMode(modeForContentType(error.contentType));
editor.update(error.value);
editor.session.setMode(modeForContentType(error.response.contentType));
editor.update(error.response.value as string);
} else {
editor.update('');
}
Expand All @@ -82,16 +83,18 @@ function EditorOutputUI() {
}, [readOnlySettings]);

return (
<div ref={editorRef} className="conApp__output" data-test-subj="response-editor">
{/* Axe complains about Ace's textarea element missing a label, which interferes with our
<>
<div ref={editorRef} className="conApp__output" data-test-subj="response-editor">
{/* Axe complains about Ace's textarea element missing a label, which interferes with our
automated a11y tests per #52136. This wrapper does nothing to address a11y but it does
satisfy Axe. */}

{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label className="conApp__textAreaLabelHack">
<div className="conApp__outputContent" id="ConAppOutput" />
</label>
</div>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label className="conApp__textAreaLabelHack">
<div className="conApp__outputContent" id="ConAppOutput" />
</label>
</div>
</>
);
}

Expand Down
Loading

0 comments on commit d20a7d0

Please sign in to comment.