diff --git a/src/legacy/core_plugins/console_legacy/public/styles/_app.scss b/src/legacy/core_plugins/console_legacy/public/styles/_app.scss index 3b6297f9cdbff..865a4fc7fafb0 100644 --- a/src/legacy/core_plugins/console_legacy/public/styles/_app.scss +++ b/src/legacy/core_plugins/console_legacy/public/styles/_app.scss @@ -48,6 +48,7 @@ button { line-height: inherit; } + position: absolute; z-index: $euiZLevel1; top: 0; @@ -89,3 +90,12 @@ .conApp__settingsModal { min-width: 460px; } + +.conApp__requestProgressBarContainer { + position: relative; + z-index: $euiZLevel2; +} + +.conApp__tabsExtension { + border-bottom: $euiBorderThin; +} diff --git a/src/plugins/console/public/application/components/index.ts b/src/plugins/console/public/application/components/index.ts index eccde899a2640..d9a8aa9328b73 100644 --- a/src/plugins/console/public/application/components/index.ts +++ b/src/plugins/console/public/application/components/index.ts @@ -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'; diff --git a/src/plugins/console/public/application/components/network_request_status_bar/index.ts b/src/plugins/console/public/application/components/network_request_status_bar/index.ts new file mode 100644 index 0000000000000..ce214c1cdfffa --- /dev/null +++ b/src/plugins/console/public/application/components/network_request_status_bar/index.ts @@ -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'; diff --git a/src/plugins/console/public/application/components/network_request_status_bar/network_request_status_bar.tsx b/src/plugins/console/public/application/components/network_request_status_bar/network_request_status_bar.tsx new file mode 100644 index 0000000000000..6915ff15f374d --- /dev/null +++ b/src/plugins/console/public/application/components/network_request_status_bar/network_request_status_bar.tsx @@ -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 = ({ + requestInProgress, + requestResult, +}) => { + let content: React.ReactNode = null; + + if (requestInProgress) { + content = ( + + + {i18n.translate('console.requestInProgressBadgeText', { + defaultMessage: 'Request in progress', + })} + + + ); + } else if (requestResult) { + const { endpoint, method, statusCode, statusText, timeElapsedMs } = requestResult; + + content = ( + <> + + {`${method} ${ + endpoint.startsWith('/') ? endpoint : '/' + endpoint + }`} + } + > + + {/* Use   to ensure that no matter the width we don't allow line breaks */} + {statusCode} - {statusText} + + + + + + {i18n.translate('console.requestTimeElapasedBadgeTooltipContent', { + defaultMessage: 'Time Elapsed', + })} + + } + > + + + {timeElapsedMs} {'ms'} + + + + + + ); + } + + return ( + + {content} + + ); +}; diff --git a/src/plugins/console/public/application/containers/editor/editor.tsx b/src/plugins/console/public/application/containers/editor/editor.tsx index 5c7fe293651fb..0bfe837f2cd90 100644 --- a/src/plugins/console/public/application/containers/editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/editor.tsx @@ -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'; @@ -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, @@ -55,23 +57,30 @@ export const Editor = ({ loading }: Props) => { if (!currentTextObject) return null; return ( - - - {loading ? ( - - ) : ( - - )} - - - {loading ? : } - - + <> + {requestInFlight ? ( +
+ +
+ ) : null} + + + {loading ? ( + + ) : ( + + )} + + + {loading ? : } + + + ); -}; +}); diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 759e3dbafb39c..b3e966ddffa4c 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -211,14 +211,14 @@ function EditorUI({ initialTextValue }: EditorProps) {