mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Search Sessions] Disable "save session" due to timeout (#90294)
This commit is contained in:
parent
dccea865e4
commit
bbda20619e
8 changed files with 276 additions and 100 deletions
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public';
|
||||
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
|
||||
import { BfetchPublicSetup } from '../../../../src/plugins/bfetch/public';
|
||||
|
@ -86,6 +87,9 @@ export class DataEnhancedPlugin
|
|||
application: core.application,
|
||||
timeFilter: plugins.data.query.timefilter.timefilter,
|
||||
storage: this.storage,
|
||||
disableSaveAfterSessionCompletesTimeout: moment
|
||||
.duration(this.config.search.sessions.notTouchedTimeout)
|
||||
.asMilliseconds(),
|
||||
})
|
||||
)
|
||||
),
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { StubBrowserStorage } from '@kbn/test/jest';
|
||||
import { render, waitFor, screen, act } from '@testing-library/react';
|
||||
import { Storage } from '../../../../../../../src/plugins/kibana_utils/public/';
|
||||
|
@ -20,6 +20,8 @@ import {
|
|||
} from '../../../../../../../src/plugins/data/public';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { TOUR_RESTORE_STEP_KEY, TOUR_TAKING_TOO_LONG_STEP_KEY } from './search_session_tour';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
const dataStart = dataPluginMock.createStartContract();
|
||||
|
@ -30,6 +32,12 @@ const timeFilter = dataStart.query.timefilter.timefilter as jest.Mocked<Timefilt
|
|||
timeFilter.getRefreshIntervalUpdate$.mockImplementation(() => refreshInterval$);
|
||||
timeFilter.getRefreshInterval.mockImplementation(() => refreshInterval$.getValue());
|
||||
|
||||
const disableSaveAfterSessionCompletesTimeout = 5 * 60 * 1000;
|
||||
|
||||
function Container({ children }: { children?: ReactNode }) {
|
||||
return <IntlProvider locale="en">{children}</IntlProvider>;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
storage = new Storage(new StubBrowserStorage());
|
||||
refreshInterval$.next({ value: 0, pause: true });
|
||||
|
@ -47,8 +55,13 @@ test("shouldn't show indicator in case no active search session", async () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const { getByTestId, container } = render(<SearchSessionIndicator />);
|
||||
const { getByTestId, container } = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
// make sure `searchSessionIndicator` isn't appearing after some time (lazy-loading)
|
||||
await expect(
|
||||
|
@ -69,8 +82,13 @@ test("shouldn't show indicator in case app hasn't opt-in", async () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const { getByTestId, container } = render(<SearchSessionIndicator />);
|
||||
const { getByTestId, container } = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
sessionService.isSessionStorageReady.mockImplementation(() => false);
|
||||
|
||||
// make sure `searchSessionIndicator` isn't appearing after some time (lazy-loading)
|
||||
|
@ -93,8 +111,13 @@ test('should show indicator in case there is an active search session', async ()
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const { getByTestId } = render(<SearchSessionIndicator />);
|
||||
const { getByTestId } = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => getByTestId('searchSessionIndicator'));
|
||||
});
|
||||
|
@ -118,13 +141,20 @@ test('should be disabled in case uiConfig says so ', async () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
|
||||
render(<SearchSessionIndicator />);
|
||||
render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => screen.getByTestId('searchSessionIndicator'));
|
||||
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
await userEvent.click(screen.getByLabelText('Search session loading'));
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled();
|
||||
});
|
||||
|
||||
test('should be disabled during auto-refresh', async () => {
|
||||
|
@ -135,19 +165,82 @@ test('should be disabled during auto-refresh', async () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
|
||||
render(<SearchSessionIndicator />);
|
||||
render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => screen.getByTestId('searchSessionIndicator'));
|
||||
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).not.toBeDisabled();
|
||||
await userEvent.click(screen.getByLabelText('Search session loading'));
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled();
|
||||
|
||||
act(() => {
|
||||
refreshInterval$.next({ value: 0, pause: false });
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled();
|
||||
});
|
||||
|
||||
describe('Completed inactivity', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
test('save should be disabled after completed and timeout', async () => {
|
||||
const state$ = new BehaviorSubject(SearchSessionState.Loading);
|
||||
|
||||
const SearchSessionIndicator = createConnectedSearchSessionIndicator({
|
||||
sessionService: { ...sessionService, state$ },
|
||||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
|
||||
render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => screen.getByTestId('searchSessionIndicator'));
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Search session loading'));
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled();
|
||||
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(5 * 60 * 1000);
|
||||
});
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled();
|
||||
|
||||
act(() => {
|
||||
state$.next(SearchSessionState.Completed);
|
||||
});
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled();
|
||||
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(2.5 * 60 * 1000);
|
||||
});
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled();
|
||||
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(2.5 * 60 * 1000);
|
||||
});
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tour steps', () => {
|
||||
|
@ -167,8 +260,13 @@ describe('tour steps', () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const rendered = render(<SearchSessionIndicator />);
|
||||
const rendered = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => rendered.getByTestId('searchSessionIndicator'));
|
||||
|
||||
|
@ -199,8 +297,13 @@ describe('tour steps', () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const rendered = render(<SearchSessionIndicator />);
|
||||
const rendered = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
const searchSessionIndicator = await rendered.findByTestId('searchSessionIndicator');
|
||||
expect(searchSessionIndicator).toBeTruthy();
|
||||
|
@ -225,8 +328,13 @@ describe('tour steps', () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const rendered = render(<SearchSessionIndicator />);
|
||||
const rendered = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => rendered.getByTestId('searchSessionIndicator'));
|
||||
expect(screen.getByTestId('searchSessionIndicatorPopoverContainer')).toBeInTheDocument();
|
||||
|
@ -242,8 +350,13 @@ describe('tour steps', () => {
|
|||
application: coreStart.application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
});
|
||||
const rendered = render(<SearchSessionIndicator />);
|
||||
const rendered = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await waitFor(() => rendered.getByTestId('searchSessionIndicator'));
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import { debounce, distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { timer } from 'rxjs';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { debounce, distinctUntilChanged, map, mapTo, switchMap } from 'rxjs/operators';
|
||||
import { merge, of, timer } from 'rxjs';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { SearchSessionIndicator, SearchSessionIndicatorRef } from '../search_session_indicator';
|
||||
|
@ -26,6 +26,11 @@ export interface SearchSessionIndicatorDeps {
|
|||
timeFilter: TimefilterContract;
|
||||
application: ApplicationStart;
|
||||
storage: IStorageWrapper;
|
||||
/**
|
||||
* Controls for how long we allow to save a session,
|
||||
* after the last search in the session has completed
|
||||
*/
|
||||
disableSaveAfterSessionCompletesTimeout: number;
|
||||
}
|
||||
|
||||
export const createConnectedSearchSessionIndicator = ({
|
||||
|
@ -33,6 +38,7 @@ export const createConnectedSearchSessionIndicator = ({
|
|||
application,
|
||||
timeFilter,
|
||||
storage,
|
||||
disableSaveAfterSessionCompletesTimeout,
|
||||
}: SearchSessionIndicatorDeps): React.FC => {
|
||||
const isAutoRefreshEnabled = () => !timeFilter.getRefreshInterval().pause;
|
||||
const isAutoRefreshEnabled$ = timeFilter
|
||||
|
@ -43,60 +49,104 @@ export const createConnectedSearchSessionIndicator = ({
|
|||
debounce((_state) => timer(_state === SearchSessionState.None ? 50 : 300)) // switch to None faster to quickly remove indicator when navigating away
|
||||
);
|
||||
|
||||
const disableSaveAfterSessionCompleteTimedOut$ = sessionService.state$.pipe(
|
||||
switchMap((_state) =>
|
||||
_state === SearchSessionState.Completed
|
||||
? merge(of(false), timer(disableSaveAfterSessionCompletesTimeout).pipe(mapTo(true)))
|
||||
: of(false)
|
||||
),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
return () => {
|
||||
const ref = useRef<SearchSessionIndicatorRef>(null);
|
||||
const state = useObservable(debouncedSessionServiceState$, SearchSessionState.None);
|
||||
const autoRefreshEnabled = useObservable(isAutoRefreshEnabled$, isAutoRefreshEnabled());
|
||||
const isDisabledByApp = sessionService.getSearchSessionIndicatorUiConfig().isDisabled();
|
||||
const isSaveDisabledByApp = sessionService.getSearchSessionIndicatorUiConfig().isDisabled();
|
||||
const disableSaveAfterSessionCompleteTimedOut = useObservable(
|
||||
disableSaveAfterSessionCompleteTimedOut$,
|
||||
false
|
||||
);
|
||||
const [
|
||||
searchSessionIndicator,
|
||||
setSearchSessionIndicator,
|
||||
] = useState<SearchSessionIndicatorRef | null>(null);
|
||||
const searchSessionIndicatorRef = useCallback((ref: SearchSessionIndicatorRef) => {
|
||||
if (ref !== null) {
|
||||
setSearchSessionIndicator(ref);
|
||||
}
|
||||
}, []);
|
||||
|
||||
let disabled = false;
|
||||
let disabledReasonText: string = '';
|
||||
let saveDisabled = false;
|
||||
let saveDisabledReasonText: string = '';
|
||||
|
||||
if (autoRefreshEnabled) {
|
||||
disabled = true;
|
||||
disabledReasonText = i18n.translate(
|
||||
saveDisabled = true;
|
||||
saveDisabledReasonText = i18n.translate(
|
||||
'xpack.data.searchSessionIndicator.disabledDueToAutoRefreshMessage',
|
||||
{
|
||||
defaultMessage: 'Search sessions are not available when auto refresh is enabled.',
|
||||
defaultMessage: 'Saving search session is not available when auto refresh is enabled.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (disableSaveAfterSessionCompleteTimedOut) {
|
||||
saveDisabled = true;
|
||||
saveDisabledReasonText = i18n.translate(
|
||||
'xpack.data.searchSessionIndicator.disabledDueToTimeoutMessage',
|
||||
{
|
||||
defaultMessage: 'Search session results expired.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (isSaveDisabledByApp.disabled) {
|
||||
saveDisabled = true;
|
||||
saveDisabledReasonText = isSaveDisabledByApp.reasonText;
|
||||
}
|
||||
|
||||
const { markOpenedDone, markRestoredDone } = useSearchSessionTour(
|
||||
storage,
|
||||
ref,
|
||||
searchSessionIndicator,
|
||||
state,
|
||||
disabled
|
||||
saveDisabled
|
||||
);
|
||||
|
||||
if (isDisabledByApp.disabled) {
|
||||
disabled = true;
|
||||
disabledReasonText = isDisabledByApp.reasonText;
|
||||
}
|
||||
const onOpened = useCallback(
|
||||
(openedState: SearchSessionState) => {
|
||||
markOpenedDone();
|
||||
if (openedState === SearchSessionState.Restored) {
|
||||
markRestoredDone();
|
||||
}
|
||||
},
|
||||
[markOpenedDone, markRestoredDone]
|
||||
);
|
||||
|
||||
const onContinueInBackground = useCallback(() => {
|
||||
if (saveDisabled) return;
|
||||
sessionService.save();
|
||||
}, [saveDisabled]);
|
||||
|
||||
const onSaveResults = useCallback(() => {
|
||||
if (saveDisabled) return;
|
||||
sessionService.save();
|
||||
}, [saveDisabled]);
|
||||
|
||||
const onCancel = useCallback(() => {
|
||||
sessionService.cancel();
|
||||
}, []);
|
||||
|
||||
if (!sessionService.isSessionStorageReady()) return null;
|
||||
return (
|
||||
<RedirectAppLinks application={application}>
|
||||
<SearchSessionIndicator
|
||||
ref={ref}
|
||||
ref={searchSessionIndicatorRef}
|
||||
state={state}
|
||||
onContinueInBackground={() => {
|
||||
sessionService.save();
|
||||
}}
|
||||
onSaveResults={() => {
|
||||
sessionService.save();
|
||||
}}
|
||||
onCancel={() => {
|
||||
sessionService.cancel();
|
||||
}}
|
||||
disabled={disabled}
|
||||
disabledReasonText={disabledReasonText}
|
||||
onOpened={(openedState) => {
|
||||
markOpenedDone();
|
||||
if (openedState === SearchSessionState.Restored) {
|
||||
markRestoredDone();
|
||||
}
|
||||
}}
|
||||
saveDisabled={saveDisabled}
|
||||
saveDisabledReasonText={saveDisabledReasonText}
|
||||
onContinueInBackground={onContinueInBackground}
|
||||
onSaveResults={onSaveResults}
|
||||
onCancel={onCancel}
|
||||
onOpened={onOpened}
|
||||
/>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MutableRefObject, useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { IStorageWrapper } from '../../../../../../../src/plugins/kibana_utils/public';
|
||||
import { SearchSessionIndicatorRef } from '../search_session_indicator';
|
||||
import { SearchSessionState } from '../../../../../../../src/plugins/data/public';
|
||||
|
@ -16,7 +16,7 @@ export const TOUR_RESTORE_STEP_KEY = `data.searchSession.tour.restore`;
|
|||
|
||||
export function useSearchSessionTour(
|
||||
storage: IStorageWrapper,
|
||||
searchSessionIndicatorRef: MutableRefObject<SearchSessionIndicatorRef | null>,
|
||||
searchSessionIndicatorRef: SearchSessionIndicatorRef | null,
|
||||
state: SearchSessionState,
|
||||
searchSessionsDisabled: boolean
|
||||
) {
|
||||
|
@ -30,19 +30,20 @@ export function useSearchSessionTour(
|
|||
|
||||
useEffect(() => {
|
||||
if (searchSessionsDisabled) return;
|
||||
if (!searchSessionIndicatorRef) return;
|
||||
let timeoutHandle: number;
|
||||
|
||||
if (state === SearchSessionState.Loading) {
|
||||
if (!safeHas(storage, TOUR_TAKING_TOO_LONG_STEP_KEY)) {
|
||||
timeoutHandle = window.setTimeout(() => {
|
||||
safeOpen(searchSessionIndicatorRef);
|
||||
searchSessionIndicatorRef.openPopover();
|
||||
}, TOUR_TAKING_TOO_LONG_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
if (state === SearchSessionState.Restored) {
|
||||
if (!safeHas(storage, TOUR_RESTORE_STEP_KEY)) {
|
||||
safeOpen(searchSessionIndicatorRef);
|
||||
searchSessionIndicatorRef.openPopover();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,15 +80,3 @@ function safeSet(storage: IStorageWrapper, key: string) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function safeOpen(searchSessionIndicatorRef: MutableRefObject<SearchSessionIndicatorRef | null>) {
|
||||
if (searchSessionIndicatorRef.current) {
|
||||
searchSessionIndicatorRef.current.openPopover();
|
||||
} else {
|
||||
// TODO: needed for initial open when component is not rendered yet
|
||||
// fix after: https://github.com/elastic/eui/issues/4460
|
||||
setTimeout(() => {
|
||||
searchSessionIndicatorRef.current?.openPopover();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,9 +33,9 @@ storiesOf('components/SearchSessionIndicator', module).add('default', () => (
|
|||
<div>
|
||||
<SearchSessionIndicator
|
||||
state={SearchSessionState.Completed}
|
||||
disabled={true}
|
||||
disabledReasonText={
|
||||
'Send to background capability is unavailable when auto-refresh is enabled'
|
||||
saveDisabled={true}
|
||||
saveDisabledReasonText={
|
||||
'Search results have expired and it is no longer possible to save this search session'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -108,11 +108,21 @@ test('Canceled state', async () => {
|
|||
});
|
||||
|
||||
test('Disabled state', async () => {
|
||||
render(
|
||||
const { rerender } = render(
|
||||
<Container>
|
||||
<SearchSessionIndicator state={SearchSessionState.Loading} disabled={true} />
|
||||
<SearchSessionIndicator state={SearchSessionState.Loading} saveDisabled={true} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
await userEvent.click(screen.getByLabelText('Search session loading'));
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled();
|
||||
|
||||
rerender(
|
||||
<Container>
|
||||
<SearchSessionIndicator state={SearchSessionState.Completed} saveDisabled={true} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled();
|
||||
});
|
||||
|
|
|
@ -31,8 +31,10 @@ export interface SearchSessionIndicatorProps {
|
|||
onCancel?: () => void;
|
||||
viewSearchSessionsLink?: string;
|
||||
onSaveResults?: () => void;
|
||||
disabled?: boolean;
|
||||
disabledReasonText?: string;
|
||||
|
||||
saveDisabled?: boolean;
|
||||
saveDisabledReasonText?: string;
|
||||
|
||||
onOpened?: (openedState: SearchSessionState) => void;
|
||||
}
|
||||
|
||||
|
@ -55,17 +57,22 @@ const CancelButton = ({ onCancel = () => {}, buttonProps = {} }: ActionButtonPro
|
|||
const ContinueInBackgroundButton = ({
|
||||
onContinueInBackground = () => {},
|
||||
buttonProps = {},
|
||||
saveDisabled = false,
|
||||
saveDisabledReasonText,
|
||||
}: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
onClick={onContinueInBackground}
|
||||
data-test-subj={'searchSessionIndicatorContinueInBackgroundBtn'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.searchSessionIndicator.continueInBackgroundButtonText"
|
||||
defaultMessage="Save session"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
<EuiToolTip content={saveDisabledReasonText}>
|
||||
<EuiButtonEmpty
|
||||
onClick={onContinueInBackground}
|
||||
data-test-subj={'searchSessionIndicatorContinueInBackgroundBtn'}
|
||||
isDisabled={saveDisabled}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.searchSessionIndicator.continueInBackgroundButtonText"
|
||||
defaultMessage="Save session"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
);
|
||||
|
||||
const ViewAllSearchSessionsButton = ({
|
||||
|
@ -84,17 +91,25 @@ const ViewAllSearchSessionsButton = ({
|
|||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
const SaveButton = ({ onSaveResults = () => {}, buttonProps = {} }: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
onClick={onSaveResults}
|
||||
data-test-subj={'searchSessionIndicatorSaveBtn'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.searchSessionIndicator.saveButtonText"
|
||||
defaultMessage="Save session"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
const SaveButton = ({
|
||||
onSaveResults = () => {},
|
||||
buttonProps = {},
|
||||
saveDisabled = false,
|
||||
saveDisabledReasonText,
|
||||
}: ActionButtonProps) => (
|
||||
<EuiToolTip content={saveDisabledReasonText}>
|
||||
<EuiButtonEmpty
|
||||
onClick={onSaveResults}
|
||||
data-test-subj={'searchSessionIndicatorSaveBtn'}
|
||||
isDisabled={saveDisabled}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.searchSessionIndicator.saveButtonText"
|
||||
defaultMessage="Save session"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
);
|
||||
|
||||
const searchSessionIndicatorViewStateToProps: {
|
||||
|
@ -325,19 +340,16 @@ export const SearchSessionIndicator = React.forwardRef<
|
|||
className="searchSessionIndicator"
|
||||
data-test-subj={'searchSessionIndicator'}
|
||||
data-state={props.state}
|
||||
data-save-disabled={props.saveDisabled ?? false}
|
||||
panelClassName={'searchSessionIndicator__panel'}
|
||||
repositionOnScroll={true}
|
||||
button={
|
||||
<EuiToolTip
|
||||
content={props.disabled ? props.disabledReasonText : button.tooltipText}
|
||||
delay={props.disabled ? 'regular' : 'long'}
|
||||
>
|
||||
<EuiToolTip content={button.tooltipText} delay={'long'}>
|
||||
<EuiButtonIcon
|
||||
color={button.color}
|
||||
aria-label={button['aria-label']}
|
||||
iconType={button.iconType}
|
||||
onClick={onButtonClick}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
}
|
||||
|
|
|
@ -47,9 +47,7 @@ export function SearchSessionsProvider({ getService }: FtrProviderContext) {
|
|||
|
||||
public async disabledOrFail() {
|
||||
await this.exists();
|
||||
await expect(await (await (await this.find()).findByTagName('button')).isEnabled()).to.be(
|
||||
false
|
||||
);
|
||||
await expect(await (await this.find()).getAttribute('data-save-disabled')).to.be('true');
|
||||
}
|
||||
|
||||
public async expectState(state: SessionStateType) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue