mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
parent
b5102f3659
commit
5ea3a0dba3
7 changed files with 232 additions and 189 deletions
|
@ -1 +1 @@
|
|||
@import 'components/saved_query_management/saved_query_management_component';
|
||||
@import 'components/saved_query_management/index';
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
@import './saved_query_management_component';
|
||||
@import './saved_query_list_item';
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue