[Console] Fix small UX bugs (#193887)

This commit is contained in:
Ignacio Rivas 2024-09-27 15:09:46 +02:00 committed by GitHub
parent 89f64384ef
commit 8af23349e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 153 additions and 109 deletions

View file

@ -7,17 +7,10 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React from 'react';
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiPopover,
EuiTitle,
EuiText,
EuiFlexGroup,
EuiFlexItem,
EuiButtonIcon,
EuiSpacer,
} from '@elastic/eui';
import { EuiPopover, EuiTitle, EuiText, EuiPanel, EuiSpacer, EuiListGroup } from '@elastic/eui';
import { css } from '@emotion/react';
import { useServicesContext } from '../contexts';
interface HelpPopoverProps {
@ -27,9 +20,81 @@ interface HelpPopoverProps {
resetTour: () => void;
}
const styles = {
// Hide the external svg icon for the link given that we have a custom icon for it.
// Also remove the the hover effect on the action icon since it's a bit distracting.
listItem: css`
.euiListGroupItem__button {
> svg {
display: none;
}
}
.euiButtonIcon:hover {
background: transparent;
}
`,
};
export const HelpPopover = ({ button, isOpen, closePopover, resetTour }: HelpPopoverProps) => {
const { docLinks } = useServicesContext();
const listItems = useMemo(
() => [
{
label: i18n.translate('console.helpPopover.aboutConsoleLabel', {
defaultMessage: 'About Console',
}),
href: docLinks.console.guide,
target: '_blank',
css: styles.listItem,
extraAction: {
iconType: 'popout',
href: docLinks.console.guide,
target: '_blank',
alwaysShow: true,
'aria-label': i18n.translate('console.helpPopover.aboutConsoleButtonAriaLabel', {
defaultMessage: 'About Console link',
}),
},
},
{
label: i18n.translate('console.helpPopover.aboutQueryDSLLabel', {
defaultMessage: 'About Query DSL',
}),
href: docLinks.query.queryDsl,
target: '_blank',
css: styles.listItem,
extraAction: {
iconType: 'popout',
href: docLinks.query.queryDsl,
target: '_blank',
alwaysShow: true,
'aria-label': i18n.translate('console.helpPopover.aboutQueryDSLButtonAriaLabel', {
defaultMessage: 'About QueryDSL link',
}),
},
},
{
label: i18n.translate('console.helpPopover.rerunTourLabel', {
defaultMessage: 'Re-run feature tour',
}),
css: styles.listItem,
onClick: resetTour,
extraAction: {
iconType: 'refresh',
alwaysShow: true,
onClick: resetTour,
'data-test-subj': 'consoleRerunTourButton',
'aria-label': i18n.translate('console.helpPopover.rerunTourButtonAriaLabel', {
defaultMessage: 'Re-run feature tour button',
}),
},
},
],
[docLinks, resetTour]
);
return (
<EuiPopover
button={button}
@ -38,99 +103,31 @@ export const HelpPopover = ({ button, isOpen, closePopover, resetTour }: HelpPop
anchorPosition="downRight"
buffer={4}
ownFocus={false}
panelPaddingSize="none"
data-test-subj="consoleHelpPopover"
>
<EuiTitle size="xs">
<h4>
{i18n.translate('console.helpPopover.title', {
defaultMessage: 'Elastic Console',
})}
</h4>
</EuiTitle>
<EuiPanel paddingSize="m" hasShadow={false} hasBorder={false}>
<EuiTitle size="xs">
<h4>
{i18n.translate('console.helpPopover.title', {
defaultMessage: 'Elastic Console',
})}
</h4>
</EuiTitle>
<EuiSpacer size="s" />
<EuiSpacer size="s" />
<EuiText style={{ width: 300 }} color="subdued" size="s">
<p>
{i18n.translate('console.helpPopover.description', {
defaultMessage:
'Console is an interactive UI for calling Elasticsearch and Kibana APIs and viewing their responses. Search your data, manage settings, and more, using Query DSL and REST API syntax.',
})}
</p>
</EuiText>
<EuiText style={{ width: 300 }} color="subdued" size="s">
<p>
{i18n.translate('console.helpPopover.description', {
defaultMessage:
'Console is an interactive UI for calling Elasticsearch and Kibana APIs and viewing their responses. Search your data, manage settings, and more, using Query DSL and REST API syntax.',
})}
</p>
</EuiText>
</EuiPanel>
<EuiSpacer />
<EuiFlexGroup gutterSize="m" direction="column">
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<p>
{i18n.translate('console.helpPopover.aboutConsoleLabel', {
defaultMessage: 'About Console',
})}
</p>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="popout"
href={docLinks.console.guide}
target="_blank"
color="text"
aria-label={i18n.translate('console.helpPopover.aboutConsoleButtonAriaLabel', {
defaultMessage: 'About Console link',
})}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<p>
{i18n.translate('console.helpPopover.aboutQueryDSLLabel', {
defaultMessage: 'About Query DSL',
})}
</p>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="popout"
href={docLinks.query.queryDsl}
target="_blank"
color="text"
aria-label={i18n.translate('console.helpPopover.aboutQueryDSLButtonAriaLabel', {
defaultMessage: 'About QueryDSL link',
})}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<p>
{i18n.translate('console.helpPopover.rerunTourLabel', {
defaultMessage: 'Re-run feature tour',
})}
</p>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="refresh"
onClick={resetTour}
color="text"
aria-label={i18n.translate('console.helpPopover.rerunTourButtonAriaLabel', {
defaultMessage: 'Re-run feature tour button',
})}
data-test-subj="consoleRerunTourButton"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiListGroup listItems={listItems} color="primary" size="s" />
</EuiPopover>
);
};

View file

@ -62,17 +62,23 @@ export class MonacoEditorActionsProvider {
private setEditorActionsCss: (css: CSSProperties) => void,
private isDevMode: boolean
) {
this.editor.focus();
this.parsedRequestsProvider = getParsedRequestsProvider(this.editor.getModel());
this.highlightedLines = this.editor.createDecorationsCollection();
const debouncedHighlightRequests = debounce(
() => this.highlightRequests(),
async () => {
if (editor.hasTextFocus()) {
await this.highlightRequests();
} else {
this.clearEditorDecorations();
}
},
DEBOUNCE_HIGHLIGHT_WAIT_MS,
{
leading: true,
}
);
debouncedHighlightRequests();
const debouncedTriggerSuggestions = debounce(
() => {
@ -110,6 +116,15 @@ export class MonacoEditorActionsProvider {
});
}
private clearEditorDecorations() {
// remove the highlighted lines
this.highlightedLines.clear();
// hide action buttons
this.setEditorActionsCss({
visibility: 'hidden',
});
}
private updateEditorActions(lineNumber?: number) {
// if no request is currently selected, hide the actions buttons
if (!lineNumber) {

View file

@ -30,30 +30,52 @@ export class MonacoEditorOutputActionsProvider {
private setEditorActionsCss: (css: CSSProperties) => void
) {
this.highlightedLines = this.editor.createDecorationsCollection();
this.editor.focus();
const debouncedHighlightRequests = debounce(
() => this.highlightRequests(),
async () => {
if (editor.hasTextFocus()) {
await this.highlightRequests();
} else {
this.clearEditorDecorations();
}
},
DEBOUNCE_HIGHLIGHT_WAIT_MS,
{
leading: true,
}
);
debouncedHighlightRequests();
// init all listeners
editor.onDidChangeCursorPosition(async (event) => {
editor.onDidChangeCursorPosition(async () => {
await debouncedHighlightRequests();
});
editor.onDidScrollChange(async (event) => {
editor.onDidScrollChange(async () => {
await debouncedHighlightRequests();
});
editor.onDidChangeCursorSelection(async (event) => {
editor.onDidChangeCursorSelection(async () => {
await debouncedHighlightRequests();
});
editor.onDidContentSizeChange(async (event) => {
editor.onDidContentSizeChange(async () => {
await debouncedHighlightRequests();
});
editor.onDidBlurEditorText(() => {
// Since the actions buttons are placed outside of the editor, we need to delay
// the clearing of the editor decorations to ensure that the actions buttons
// are not hidden.
setTimeout(() => {
this.clearEditorDecorations();
}, 100);
});
}
private clearEditorDecorations() {
// remove the highlighted lines
this.highlightedLines.clear();
// hide action buttons
this.setEditorActionsCss({
visibility: 'hidden',
});
}
private updateEditorActions(lineNumber?: number) {

View file

@ -66,7 +66,9 @@ const CheckeableCardLabel = ({ historyItem }: { historyItem: HistoryProps }) =>
<EuiFlexGroup>
<EuiFlexItem>
<EuiText size="s">
<b>{historyItem.endpoint}</b>
<b>
{historyItem.method} {historyItem.endpoint}
</b>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>

View file

@ -47,6 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('should be able to copy the response of a request', async () => {
await sendRequest('GET /_search?pretty');
await PageObjects.console.focusOutputEditor();
await PageObjects.console.clickCopyOutput();
const resultToast = await toasts.getElementByIndex(1);

View file

@ -84,7 +84,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await retry.try(async () => {
const history = await PageObjects.console.getHistoryEntries();
expect(history).to.eql(['/_search?pretty\na few seconds ago']);
expect(history).to.eql(['GET /_search?pretty\na few seconds ago']);
});
await PageObjects.console.clickClearHistory();

View file

@ -51,6 +51,13 @@ export class ConsolePageObject extends FtrService {
await textArea.clearValueWithKeyboard();
}
public async focusOutputEditor() {
const outputEditor = await this.testSubjects.find('consoleMonacoOutput');
// Simply clicking on the output editor doesn't focus it, so we need to click
// on the margin view overlays
await (await outputEditor.findByClassName('margin-view-overlays')).click();
}
public async getOutputText() {
const outputPanel = await this.testSubjects.find('consoleMonacoOutput');
const outputViewDiv = await outputPanel.findByClassName('monaco-scrollable-element');