Skip to content

Commit

Permalink
[Discover] Dismiss flyouts when opening another one (#193865)
Browse files Browse the repository at this point in the history
- Closes #193452

## Summary

This PR makes sure that only one flyout is open at a time and
automatically dismisses all others.


### Checklist


- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
  • Loading branch information
jughosta authored Sep 27, 2024
1 parent 54659e8 commit 6fc017a
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 11 deletions.
3 changes: 3 additions & 0 deletions packages/kbn-discover-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ export {
getFieldValue,
getVisibleColumns,
canPrependTimeFieldColumn,
DiscoverFlyouts,
dismissAllFlyoutsExceptFor,
dismissFlyouts,
} from './src';

export type { LogsContextService } from './src';
Expand Down
48 changes: 48 additions & 0 deletions packages/kbn-discover-utils/src/utils/dismiss_flyouts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export enum DiscoverFlyouts {
lensEdit = 'lensEdit',
docViewer = 'docViewer',
esqlDocs = 'esqlDocs',
}

const AllDiscoverFlyouts = Object.values(DiscoverFlyouts);

const getFlyoutCloseButton = (flyout: DiscoverFlyouts): HTMLElement | null => {
switch (flyout) {
case DiscoverFlyouts.lensEdit:
return document.getElementById('lnsCancelEditOnFlyFlyout');
case DiscoverFlyouts.docViewer:
return document.querySelector('[data-test-subj="docViewerFlyoutCloseButton"]');
case DiscoverFlyouts.esqlDocs:
return document.querySelector(
'[data-test-subj="esqlInlineDocumentationFlyout"] [data-test-subj="euiFlyoutCloseButton"]'
);
}
};

export const dismissFlyouts = (
selectedFlyouts: DiscoverFlyouts[] = AllDiscoverFlyouts,
excludedFlyout?: DiscoverFlyouts
) => {
selectedFlyouts.forEach((flyout) => {
if (flyout === excludedFlyout) {
return;
}
const closeButton = getFlyoutCloseButton(flyout);
if (closeButton) {
closeButton.click?.();
}
});
};

export const dismissAllFlyoutsExceptFor = (excludedFlyout: DiscoverFlyouts) => {
dismissFlyouts(AllDiscoverFlyouts, excludedFlyout);
};
1 change: 1 addition & 0 deletions packages/kbn-discover-utils/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './get_field_value';
export * from './calc_field_counts';
export * from './get_visible_columns';
export { isLegacyTableEnabled } from './is_legacy_table_enabled';
export { DiscoverFlyouts, dismissAllFlyoutsExceptFor, dismissFlyouts } from './dismiss_flyouts';
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function DocumentationFlyout({
ownFocus
onClose={() => onHelpMenuVisibilityChange(false)}
aria-labelledby="esqlInlineDocumentationFlyout"
data-test-subj="esqlInlineDocumentationFlyout"
type="push"
size={DEFAULT_WIDTH}
paddingSize="m"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { type DataView, DataViewType } from '@kbn/data-views-plugin/public';
import { DataViewPickerProps } from '@kbn/unified-search-plugin/public';
import { ENABLE_ESQL } from '@kbn/esql-utils';
import { TextBasedLanguages } from '@kbn/esql-utils';
import { DiscoverFlyouts, dismissAllFlyoutsExceptFor } from '@kbn/discover-utils';
import { useSavedSearchInitial } from '../../state_management/discover_state_provider';
import { ESQL_TRANSITION_MODAL_KEY } from '../../../../../common/constants';
import { useInternalStateSelector } from '../../state_management/discover_internal_state_container';
Expand Down Expand Up @@ -233,6 +234,12 @@ export const DiscoverTopNav = ({
uiSettings,
]);

const onESQLDocsFlyoutVisibilityChanged = useCallback((isOpen: boolean) => {
if (isOpen) {
dismissAllFlyoutsExceptFor(DiscoverFlyouts.esqlDocs);
}
}, []);

const searchBarCustomization = useDiscoverCustomization('search_bar');

const SearchBar = useMemo(
Expand Down Expand Up @@ -278,6 +285,7 @@ export const DiscoverTopNav = ({
<searchBarCustomization.PrependFilterBar />
) : undefined
}
onESQLDocsFlyoutVisibilityChanged={onESQLDocsFlyoutVisibilityChanged}
/>
{isESQLToDataViewTransitionModalVisible && (
<ESQLToDataViewTransitionModal onClose={onESQLToDataViewTransitionModalClose} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { TopNavMenuBadgeProps } from '@kbn/navigation-plugin/public';
import { getTopNavUnsavedChangesBadge } from '@kbn/unsaved-changes-badge';
import { getManagedContentBadge } from '@kbn/managed-content-badge';
import { i18n } from '@kbn/i18n';
import { dismissFlyouts, DiscoverFlyouts } from '@kbn/discover-utils';
import { DiscoverStateContainer } from '../../state_management/discover_state';
import type { TopNavCustomization } from '../../../../customizations';
import { onSaveSearch } from './on_save_search';
Expand Down Expand Up @@ -47,10 +48,7 @@ export const getTopNavBadges = ({
entries.push({
data: getTopNavUnsavedChangesBadge({
onRevert: async () => {
const lensEditFlyoutCancelButton = document.getElementById('lnsCancelEditOnFlyFlyout');
if (lensEditFlyoutCancelButton) {
lensEditFlyoutCancelButton.click?.();
}
dismissFlyouts([DiscoverFlyouts.lensEdit]);
await stateContainer.actions.undoSavedSearchChanges();
},
onSave:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import type { DataView } from '@kbn/data-views-plugin/public';
import { Filter, Query, AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query';
import { AggregateQuery, Filter, isOfAggregateQueryType, Query } from '@kbn/es-query';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import type { DataTableColumnsMeta } from '@kbn/unified-data-table';
import type { DocViewsRegistry } from '@kbn/unified-doc-viewer';
import { DiscoverFlyouts, dismissAllFlyoutsExceptFor } from '@kbn/discover-utils';
import { UnifiedDocViewerFlyout } from '@kbn/unified-doc-viewer-plugin/public';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { useFlyoutActions } from './use_flyout_actions';
Expand Down Expand Up @@ -88,6 +89,10 @@ export function DiscoverGridFlyout({
return getDocViewer({ record: actualHit });
}, [flyoutCustomization, getDocViewerAccessor, actualHit]);

useEffect(() => {
dismissAllFlyoutsExceptFor(DiscoverFlyouts.docViewer);
}, []);

return (
<UnifiedDocViewerFlyout
flyoutTitle={docViewer.title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { isEqual, isObject } from 'lodash';
import type { LensEmbeddableOutput, Suggestion } from '@kbn/lens-plugin/public';
import type { Datatable } from '@kbn/expressions-plugin/common';
import { EditLensConfigPanelComponent } from '@kbn/lens-plugin/public/plugin';
import { DiscoverFlyouts, dismissAllFlyoutsExceptFor } from '@kbn/discover-utils';
import { deriveLensSuggestionFromLensAttributes } from '../utils/external_vis_context';

import {
Expand Down Expand Up @@ -144,5 +145,13 @@ export function ChartConfigPanel({
currentSuggestionType,
]);

return isPlainRecord ? editLensConfigPanel : null;
const flyoutElement = isPlainRecord ? editLensConfigPanel : null;

useEffect(() => {
if (flyoutElement) {
dismissAllFlyoutsExceptFor(DiscoverFlyouts.lensEdit);
}
}, [flyoutElement]);

return flyoutElement;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ import { FEEDBACK_LINK } from '@kbn/esql-utils';
import { LanguageDocumentationFlyout } from '@kbn/language-documentation';
import type { IUnifiedSearchPluginServices } from '../types';

export const ESQLMenuPopover = () => {
export interface ESQLMenuPopoverProps {
onESQLDocsFlyoutVisibilityChanged?: (isOpen: boolean) => void;
}

export const ESQLMenuPopover: React.FC<ESQLMenuPopoverProps> = ({
onESQLDocsFlyoutVisibilityChanged,
}) => {
const kibana = useKibana<IUnifiedSearchPluginServices>();

const { docLinks } = kibana.services;
Expand All @@ -34,6 +40,14 @@ export const ESQLMenuPopover = () => {
setIsESQLMenuPopoverOpen(false);
}, [isLanguageComponentOpen]);

const onHelpMenuVisibilityChange = useCallback(
(status: boolean) => {
setIsLanguageComponentOpen(status);
onESQLDocsFlyoutVisibilityChanged?.(status);
},
[setIsLanguageComponentOpen, onESQLDocsFlyoutVisibilityChanged]
);

const esqlPanelItems = useMemo(() => {
const panelItems: EuiContextMenuPanelProps['items'] = [];
panelItems.push(
Expand Down Expand Up @@ -122,7 +136,7 @@ export const ESQLMenuPopover = () => {
searchInDescription
linkToDocumentation={docLinks?.links?.query?.queryESQL ?? ''}
isHelpMenuOpen={isLanguageComponentOpen}
onHelpMenuVisibilityChange={setIsLanguageComponentOpen}
onHelpMenuVisibilityChange={onHelpMenuVisibilityChange}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import { NoDataPopover } from './no_data_popover';
import { shallowEqual } from '../utils/shallow_equal';
import { AddFilterPopover } from './add_filter_popover';
import { DataViewPicker, DataViewPickerProps } from '../dataview_picker';
import { ESQLMenuPopover } from './esql_menu_popover';
import { ESQLMenuPopover, type ESQLMenuPopoverProps } from './esql_menu_popover';

import { FilterButtonGroup } from '../filter_bar/filter_button_group/filter_button_group';
import type {
Expand Down Expand Up @@ -186,6 +186,7 @@ export interface QueryBarTopRowProps<QT extends Query | AggregateQuery = Query>
submitOnBlur?: boolean;
renderQueryInputAppend?: () => React.ReactNode;
disableExternalPadding?: boolean;
onESQLDocsFlyoutVisibilityChanged?: ESQLMenuPopoverProps['onESQLDocsFlyoutVisibilityChanged'];
}

export const SharingMetaFields = React.memo(function SharingMetaFields({
Expand Down Expand Up @@ -774,7 +775,11 @@ export const QueryBarTopRow = React.memo(
wrap
>
{props.dataViewPickerOverride || renderDataViewsPicker()}
{Boolean(isQueryLangSelected) && <ESQLMenuPopover />}
{Boolean(isQueryLangSelected) && (
<ESQLMenuPopover
onESQLDocsFlyoutVisibilityChanged={props.onESQLDocsFlyoutVisibilityChanged}
/>
)}
<EuiFlexItem
grow={!shouldShowDatePickerAsBadge()}
style={{ minWidth: shouldShowDatePickerAsBadge() ? 'auto' : 320, maxWidth: '100%' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ export function createSearchBar({
dataTestSubj={props.dataTestSubj}
filtersForSuggestions={props.filtersForSuggestions}
prependFilterBar={props.prependFilterBar}
onESQLDocsFlyoutVisibilityChanged={props.onESQLDocsFlyoutVisibilityChanged}
/>
</core.i18n.Context>
</KibanaContextProvider>
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/unified_search/public/search_bar/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export interface SearchBarOwnProps<QT extends AggregateQuery | Query = Query> {
submitOnBlur?: boolean;

renderQueryInputAppend?: () => React.ReactNode;
onESQLDocsFlyoutVisibilityChanged?: QueryBarTopRowProps['onESQLDocsFlyoutVisibilityChanged'];
}

export type SearchBarProps<QT extends Query | AggregateQuery = Query> = SearchBarOwnProps<QT> &
Expand Down Expand Up @@ -660,6 +661,7 @@ class SearchBarUI<QT extends (Query | AggregateQuery) | Query = Query> extends C
suggestionsAbstraction={this.props.suggestionsAbstraction}
renderQueryInputAppend={this.props.renderQueryInputAppend}
disableExternalPadding={this.props.displayStyle === 'withBorders'}
onESQLDocsFlyoutVisibilityChanged={this.props.onESQLDocsFlyoutVisibilityChanged}
/>
</div>
);
Expand Down
104 changes: 104 additions & 0 deletions test/functional/apps/discover/group8/_flyouts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const { common, discover, timePicker, header } = getPageObjects([
'common',
'discover',
'timePicker',
'header',
]);
const kibanaServer = getService('kibanaServer');
const security = getService('security');
const retry = getService('retry');
const dataGrid = getService('dataGrid');
const esql = getService('esql');
const testSubjects = getService('testSubjects');

describe('discover flyouts', function () {
async function isLensEditFlyoutOpen() {
return await testSubjects.exists('lnsChartSwitchPopover');
}

async function openLensEditFlyout() {
await testSubjects.click('unifiedHistogramEditFlyoutVisualization');
await retry.waitFor('flyout', async () => {
return await isLensEditFlyoutOpen();
});
}

before(async function () {
await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']);
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' });
await timePicker.setDefaultAbsoluteRangeViaUiSettings();
});

beforeEach(async function () {
await common.navigateToApp('discover');
await header.waitUntilLoadingHasFinished();
await discover.waitUntilSearchingHasFinished();
await discover.selectTextBaseLang();
await header.waitUntilLoadingHasFinished();
await discover.waitUntilSearchingHasFinished();
});

after(async () => {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.uiSettings.replace({});
await kibanaServer.savedObjects.cleanStandardList();
});

it('doc viewer flyout should get dismissed on opening ESQL docs flyout', async function () {
await dataGrid.clickRowToggle({ rowIndex: 0 });
expect(await dataGrid.isShowingDocViewer()).to.be(true);
await esql.openQuickReferenceFlyout();
expect(await dataGrid.isShowingDocViewer()).to.be(false);
expect(await esql.isOpenQuickReferenceFlyout()).to.be(true);
});

it('doc viewer flyout should get dismissed on opening Lens Edit flyout', async function () {
await dataGrid.clickRowToggle({ rowIndex: 0 });
expect(await dataGrid.isShowingDocViewer()).to.be(true);
await openLensEditFlyout();
expect(await dataGrid.isShowingDocViewer()).to.be(false);
expect(await isLensEditFlyoutOpen()).to.be(true);
});

it('ESQL docs flyout should get dismissed on opening doc viewer flyout', async function () {
await esql.openQuickReferenceFlyout();
expect(await esql.isOpenQuickReferenceFlyout()).to.be(true);
await dataGrid.clickRowToggle({ rowIndex: 0 });
expect(await dataGrid.isShowingDocViewer()).to.be(true);
expect(await esql.isOpenQuickReferenceFlyout()).to.be(false);
});

it('ESQL docs flyout should get dismissed on opening Lens Edit flyout', async function () {
await esql.openQuickReferenceFlyout();
expect(await esql.isOpenQuickReferenceFlyout()).to.be(true);
await openLensEditFlyout();
expect(await isLensEditFlyoutOpen()).to.be(true);
expect(await esql.isOpenQuickReferenceFlyout()).to.be(false);
});

it('Lens Edit flyout should get dismissed on opening doc viewer flyout', async function () {
await openLensEditFlyout();
expect(await isLensEditFlyoutOpen()).to.be(true);
await dataGrid.clickRowToggle({ rowIndex: 0 });
expect(await dataGrid.isShowingDocViewer()).to.be(true);
expect(await isLensEditFlyoutOpen()).to.be(false);
});
});
}
1 change: 1 addition & 0 deletions test/functional/apps/discover/group8/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {

loadTestFile(require.resolve('./_default_route'));
loadTestFile(require.resolve('./_hide_announcements'));
loadTestFile(require.resolve('./_flyouts'));
});
}
Loading

0 comments on commit 6fc017a

Please sign in to comment.