Saved queries design cleanup (#44191) (#44380)

This commit is contained in:
Caroline Horn 2019-08-29 10:58:26 -04:00 committed by Tim Roes
parent b5102f3659
commit 5ea3a0dba3
7 changed files with 232 additions and 189 deletions

View file

@ -1 +1 @@
@import 'components/saved_query_management/saved_query_management_component';
@import 'components/saved_query_management/index';

View file

@ -0,0 +1,2 @@
@import './saved_query_management_component';
@import './saved_query_list_item';

View file

@ -0,0 +1,21 @@
.kbnSavedQueryListItem {
margin-top: 0;
color: $euiLinkColor;
}
// Can't actually target the button with classes, but styles to override
// are just user agent styles
.kbnSavedQueryListItem-selected button {
font-weight: $euiFontWeightBold;
}
// This will ensure the info icon and tooltip shows even if the label gets truncated
.kbnSavedQueryListItem__label {
display: flex;
align-items: center;
}
.kbnSavedQueryListItem__labelText {
@include euiTextTruncate;
margin-right: $euiSizeXS;
}

View file

@ -1,14 +1,28 @@
.saved-query-management-popover {
width: 400px;
}
.saved-query-list {
@include euiYScrollWithShadows;
}
.saved-query-list-wrapper {
height: 20vh;
overflow-y:hidden;
}
.saved-query-list li:first-child .saved-query-list-item-text {
font-weight: $euiFontWeightBold;
@import '@elastic/eui/src/components/form/variables';
.kbnSavedQueryManagement__popover {
max-width: $euiFormMaxWidth;
}
.kbnSavedQueryManagement__listWrapper {
// Addition height will ensure one item is "cutoff" to indicate more below the scroll
max-height: $euiFormMaxWidth + $euiSize;
overflow-y: hidden;
}
.kbnSavedQueryManagement__pagination {
justify-content: center;
padding: ($euiSizeM / 2) $euiSizeM $euiSizeM;
}
.kbnSavedQueryManagement__text {
padding: $euiSizeM $euiSizeM ($euiSizeM / 2) $euiSizeM;
}
.kbnSavedQueryManagement__list {
@include euiYScrollWithShadows;
max-height: inherit; // Fixes overflow for applied max-height
// Left/Right padding is calculated to match the left alignment of the
// popover text and buttons
padding: ($euiSizeM / 2) $euiSizeXS !important; // Override flush
}

View file

@ -17,17 +17,10 @@
* under the License.
*/
import {
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiConfirmModal,
EuiOverlayMask,
EuiIconTip,
EuiToolTip,
} from '@elastic/eui';
import { EuiListGroupItem, EuiConfirmModal, EuiOverlayMask, EuiIconTip } from '@elastic/eui';
import React, { Fragment, useState } from 'react';
import classNames from 'classnames';
import { i18n } from '@kbn/i18n';
import { SavedQuery } from '../../index';
@ -66,91 +59,76 @@ export const SavedQueryListItem = ({
? `load-saved-query-${savedQuery.attributes.title}-button saved-query-list-item-selected`
: `load-saved-query-${savedQuery.attributes.title}-button`;
const classes = classNames('kbnSavedQueryListItem', {
'kbnSavedQueryListItem-selected': isSelected,
});
const label = (
<span className="kbnSavedQueryListItem__label">
<span className="kbnSavedQueryListItem__labelText">{savedQuery.attributes.title}</span>{' '}
{savedQuery.attributes.description && (
<EuiIconTip
type="iInCircle"
content={savedQuery.attributes.description}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel',
{
defaultMessage: '{savedQueryName} description',
values: { savedQueryName: savedQuery.attributes.title },
}
)}
/>
)}
</span>
);
return (
<Fragment>
<li
<EuiListGroupItem
className={classes}
key={savedQuery.id}
data-test-subj={`saved-query-list-item ${
data-test-subj={`saved-query-list-item ${selectButtonDataTestSubj} ${
isSelected ? 'saved-query-list-item-selected' : ''
}`}
>
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="none">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={() => {
onSelect(savedQuery);
}}
flush="left"
data-test-subj={selectButtonDataTestSubj}
textProps={isSelected ? { className: 'saved-query-list-item-text' } : undefined}
aria-label={selectButtonAriaLabelText}
>
{savedQuery.attributes.title}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="none" justifyContent="flexEnd" alignItems="center">
<EuiFlexItem>
{savedQuery.attributes.description && (
<EuiIconTip
type="iInCircle"
content={savedQuery.attributes.description}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel',
{
defaultMessage: '{savedQueryName} description',
values: { savedQueryName: savedQuery.attributes.title },
}
)}
/>
)}
</EuiFlexItem>
<EuiFlexItem>
{showWriteOperations && (
<Fragment>
<EuiToolTip
position="top"
content={
<p>
{i18n.translate(
'data.search.searchBar.savedQueryPopoverDeleteButtonTooltip',
{
defaultMessage: 'Delete saved query',
}
)}
</p>
}
>
<EuiButtonEmpty
onClick={() => {
setShowDeletionConfirmationModal(true);
}}
iconType="trash"
color="danger"
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel',
{
defaultMessage: 'Delete saved query {savedQueryName}',
values: { savedQueryName: savedQuery.attributes.title },
}
)}
data-test-subj={`delete-saved-query-${savedQuery.attributes.title}-button`}
/>
</EuiToolTip>
</Fragment>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</li>
isActive={isSelected}
onClick={() => {
onSelect(savedQuery);
}}
aria-label={selectButtonAriaLabelText}
label={label}
iconType={isSelected ? 'check' : undefined}
extraAction={
showWriteOperations
? {
color: 'danger',
onClick: () => setShowDeletionConfirmationModal(true),
iconType: 'trash',
iconSize: 's',
'aria-label': i18n.translate(
'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel',
{
defaultMessage: 'Delete saved query {savedQueryName}',
values: { savedQueryName: savedQuery.attributes.title },
}
),
title: i18n.translate(
'data.search.searchBar.savedQueryPopoverDeleteButtonAriaLabel',
{
defaultMessage: 'Delete saved query {savedQueryName}',
values: { savedQueryName: savedQuery.attributes.title },
}
),
'data-test-subj': `delete-saved-query-${savedQuery.attributes.title}-button`,
}
: undefined
}
/>
{showDeletionConfirmationModal && (
<EuiOverlayMask>
<EuiConfirmModal
title={i18n.translate('data.search.searchBar.savedQueryPopoverConfirmDeletionTitle', {
defaultMessage: 'Delete {savedQueryName}?',
defaultMessage: 'Delete "{savedQueryName}"?',
values: {
savedQueryName: savedQuery.attributes.title,
},
@ -171,6 +149,7 @@ export const SavedQueryListItem = ({
onDelete(savedQuery);
setShowDeletionConfirmationModal(false);
}}
buttonColor="danger"
onCancel={() => {
setShowDeletionConfirmationModal(false);
}}

View file

@ -20,12 +20,15 @@
import {
EuiPopover,
EuiPopoverTitle,
EuiPopoverFooter,
EuiButtonEmpty,
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiListGroup,
EuiPagination,
EuiText,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@ -119,6 +122,9 @@ export const SavedQueryManagementComponent: FunctionComponent<Props> = ({
aria-label={i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', {
defaultMessage: 'See saved queries',
})}
title={i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', {
defaultMessage: 'See saved queries',
})}
data-test-subj="saved-query-management-popover-button"
>
#
@ -164,10 +170,11 @@ export const SavedQueryManagementComponent: FunctionComponent<Props> = ({
setIsOpen(false);
}}
anchorPosition="downLeft"
panelPaddingSize="none"
ownFocus
>
<div
className="saved-query-management-popover"
className="kbnSavedQueryManagement__popover"
data-test-subj="saved-query-management-popover"
>
<EuiPopoverTitle id={'savedQueryManagementPopoverTitle'}>
@ -175,60 +182,47 @@ export const SavedQueryManagementComponent: FunctionComponent<Props> = ({
</EuiPopoverTitle>
{savedQueries.length > 0 ? (
<Fragment>
<EuiFlexGroup wrap>
<EuiFlexItem>
<EuiText>{savedQueryDescriptionText}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem className="saved-query-list-wrapper">
<ul
className="saved-query-list"
aria-labelledby={'savedQueryManagementPopoverTitle'}
>
{savedQueryRows()}
</ul>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiPagination
pageCount={Math.ceil(savedQueries.length / pageCount)}
activePage={activePage}
onPageClick={goToPage}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiText size="s" color="subdued" className="kbnSavedQueryManagement__text">
<p>{savedQueryDescriptionText}</p>
</EuiText>
<div className="kbnSavedQueryManagement__listWrapper">
<EuiListGroup
flush={true}
className="kbnSavedQueryManagement__list"
aria-labelledby={'savedQueryManagementPopoverTitle'}
>
{savedQueryRows()}
</EuiListGroup>
</div>
<EuiPagination
className="kbnSavedQueryManagement__pagination"
pageCount={Math.ceil(savedQueries.length / pageCount)}
activePage={activePage}
onPageClick={goToPage}
/>
</Fragment>
) : (
<EuiText grow={false}>{noSavedQueriesDescriptionText}</EuiText>
<Fragment>
<EuiText size="s" color="subdued" className="kbnSavedQueryManagement__text">
<p>{noSavedQueriesDescriptionText}</p>
</EuiText>
<EuiSpacer size="s" />
</Fragment>
)}
<EuiFlexGroup direction="rowReverse" alignItems="center" justifyContent="flexEnd">
{showSaveQuery && loadedSavedQuery && (
<EuiFlexItem grow={false}>
<EuiFlexGroup>
<EuiPopoverFooter>
<EuiFlexGroup
direction="rowReverse"
gutterSize="s"
alignItems="center"
justifyContent="flexEnd"
responsive={false}
wrap={true}
>
{showSaveQuery && loadedSavedQuery && (
<Fragment>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => onSaveAsNew()}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel',
{
defaultMessage: 'Save as a new saved query',
}
)}
data-test-subj="saved-query-management-save-as-new-button"
>
{i18n.translate(
'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText',
{
defaultMessage: 'Save as new',
}
)}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton
size="s"
fill
onClick={() => onSave()}
aria-label={i18n.translate(
@ -248,44 +242,67 @@ export const SavedQueryManagementComponent: FunctionComponent<Props> = ({
)}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
)}
{showSaveQuery && !loadedSavedQuery && (
<EuiFlexItem grow={false}>
<EuiButton
fill
onClick={() => onSave()}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverSaveButtonAriaLabel',
{ defaultMessage: 'Save a new saved query' }
)}
data-test-subj="saved-query-management-save-button"
>
{i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', {
defaultMessage: 'Save',
})}
</EuiButton>
</EuiFlexItem>
)}
<EuiFlexItem />
<EuiFlexItem grow={false}>
{loadedSavedQuery && (
<EuiButtonEmpty
onClick={() => onClearSavedQuery()}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel',
{ defaultMessage: 'Clear current saved query' }
)}
data-test-subj="saved-query-management-clear-button"
>
{i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', {
defaultMessage: 'Clear',
})}
</EuiButtonEmpty>
<EuiFlexItem grow={false}>
<EuiButton
size="s"
onClick={() => onSaveAsNew()}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel',
{
defaultMessage: 'Save as a new saved query',
}
)}
data-test-subj="saved-query-management-save-as-new-button"
>
{i18n.translate(
'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText',
{
defaultMessage: 'Save as new',
}
)}
</EuiButton>
</EuiFlexItem>
</Fragment>
)}
</EuiFlexItem>
</EuiFlexGroup>
{showSaveQuery && !loadedSavedQuery && (
<EuiFlexItem grow={false}>
<EuiButton
size="s"
fill
onClick={() => onSave()}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverSaveButtonAriaLabel',
{ defaultMessage: 'Save a new saved query' }
)}
data-test-subj="saved-query-management-save-button"
>
{i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', {
defaultMessage: 'Save current query',
})}
</EuiButton>
</EuiFlexItem>
)}
<EuiFlexItem />
<EuiFlexItem grow={false}>
{loadedSavedQuery && (
<EuiButtonEmpty
size="s"
flush="left"
onClick={() => onClearSavedQuery()}
aria-label={i18n.translate(
'data.search.searchBar.savedQueryPopoverClearButtonAriaLabel',
{ defaultMessage: 'Clear current saved query' }
)}
data-test-subj="saved-query-management-clear-button"
>
{i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', {
defaultMessage: 'Clear',
})}
</EuiButtonEmpty>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopoverFooter>
</div>
</EuiPopover>
</Fragment>

View file

@ -63,13 +63,18 @@ export default function ({ getService, getPageObjects }) {
it('should show the saved query management component when there are no saved queries', async () => {
await savedQueryManagementComponent.openSavedQueryManagementComponent();
const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover');
expect(descriptionText)
.to
.eql('SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.\nSave');
expect(descriptionText).to.eql(
'SAVED QUERIES\nThere are no saved queries. Save query text and filters that you want to use again.\nSave current query'
);
});
it('should allow a query to be saved via the saved objects management component', async () => {
await savedQueryManagementComponent.saveNewQuery('OkResponse', '200 responses for .jpg over 24 hours', true, true);
await savedQueryManagementComponent.saveNewQuery(
'OkResponse',
'200 responses for .jpg over 24 hours',
true,
true
);
await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse');
});
@ -101,7 +106,12 @@ export default function ({ getService, getPageObjects }) {
});
it('allows saving the currently loaded query as a new query', async () => {
await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery('OkResponseCopy', '200 responses', false, false);
await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery(
'OkResponseCopy',
'200 responses',
false,
false
);
await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseCopy');
});