mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Use sourcerer selected indices in resolver (#90727)
* Use sourcer indices * Add indices to panel requests * Use a separate indices selector for resolver events * Use valid timeline id in tests * Update TimelineId type usage, make selector test clearer * Update tests to use TimelineId type
This commit is contained in:
parent
874960e1c5
commit
f563a82903
23 changed files with 172 additions and 45 deletions
|
@ -280,6 +280,7 @@ export enum TimelineId {
|
|||
active = 'timeline-1',
|
||||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
test2 = 'test2',
|
||||
}
|
||||
|
||||
export const TimelineIdLiteralRt = runtimeTypes.union([
|
||||
|
|
|
@ -119,7 +119,7 @@ describe('EventsViewer', () => {
|
|||
let testProps = {
|
||||
defaultModel: eventsDefaultModel,
|
||||
end: to,
|
||||
id: 'test-stateful-events-viewer',
|
||||
id: TimelineId.test,
|
||||
start: from,
|
||||
scopeId: SourcererScopeName.timeline,
|
||||
};
|
||||
|
@ -155,7 +155,7 @@ describe('EventsViewer', () => {
|
|||
indexName: 'auditbeat-7.10.1-2020.12.18-000001',
|
||||
},
|
||||
tabType: 'query',
|
||||
timelineId: 'test-stateful-events-viewer',
|
||||
timelineId: TimelineId.test,
|
||||
},
|
||||
type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT',
|
||||
});
|
||||
|
@ -199,17 +199,22 @@ describe('EventsViewer', () => {
|
|||
|
||||
defaultHeaders.forEach((header) => {
|
||||
test(`it renders the ${header.id} default EventsViewer column header`, () => {
|
||||
testProps = {
|
||||
...testProps,
|
||||
// Update with a new id, to force columns back to default.
|
||||
id: TimelineId.test2,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
defaultHeaders.forEach((h) =>
|
||||
defaultHeaders.forEach((h) => {
|
||||
expect(wrapper.find(`[data-test-subj="header-text-${header.id}"]`).first().exists()).toBe(
|
||||
true
|
||||
)
|
||||
);
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -117,7 +117,7 @@ interface Props {
|
|||
filters: Filter[];
|
||||
headerFilterGroup?: React.ReactNode;
|
||||
height?: number;
|
||||
id: string;
|
||||
id: TimelineId;
|
||||
indexNames: string[];
|
||||
indexPattern: IIndexPattern;
|
||||
isLive: boolean;
|
||||
|
|
|
@ -16,6 +16,7 @@ import { useMountAppended } from '../../utils/use_mount_appended';
|
|||
import { mockEventViewerResponse } from './mock';
|
||||
import { StatefulEventsViewer } from '.';
|
||||
import { eventsDefaultModel } from './default_model';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { useTimelineEvents } from '../../../timelines/containers';
|
||||
|
||||
|
@ -36,7 +37,7 @@ const testProps = {
|
|||
defaultModel: eventsDefaultModel,
|
||||
end: to,
|
||||
indexNames: [],
|
||||
id: 'test-stateful-events-viewer',
|
||||
id: TimelineId.test,
|
||||
scopeId: SourcererScopeName.default,
|
||||
start: from,
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ import styled from 'styled-components';
|
|||
|
||||
import { inputsModel, inputsSelectors, State } from '../../store';
|
||||
import { inputsActions } from '../../store/actions';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline';
|
||||
import { SubsetTimelineModel, TimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
|
@ -34,7 +35,7 @@ const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>`
|
|||
export interface OwnProps {
|
||||
defaultModel: SubsetTimelineModel;
|
||||
end: string;
|
||||
id: string;
|
||||
id: TimelineId;
|
||||
scopeId: SourcererScopeName;
|
||||
start: string;
|
||||
headerFilterGroup?: React.ReactNode;
|
||||
|
|
|
@ -19,6 +19,7 @@ const initialState: DataState = {
|
|||
data: null,
|
||||
},
|
||||
resolverComponentInstanceID: undefined,
|
||||
indices: [],
|
||||
};
|
||||
/* eslint-disable complexity */
|
||||
export const dataReducer: Reducer<DataState, ResolverAction> = (state = initialState, action) => {
|
||||
|
@ -35,6 +36,7 @@ export const dataReducer: Reducer<DataState, ResolverAction> = (state = initialS
|
|||
},
|
||||
resolverComponentInstanceID: action.payload.resolverComponentInstanceID,
|
||||
locationSearch: action.payload.locationSearch,
|
||||
indices: action.payload.indices,
|
||||
};
|
||||
const panelViewAndParameters = selectors.panelViewAndParameters(nextState);
|
||||
return {
|
||||
|
|
|
@ -664,4 +664,85 @@ describe('data state', () => {
|
|||
`);
|
||||
});
|
||||
});
|
||||
describe('when the resolver tree response is complete, still use non-default indices', () => {
|
||||
beforeEach(() => {
|
||||
const { resolverTree } = mockTreeWithNoAncestorsAnd2Children({
|
||||
originID: 'a',
|
||||
firstChildID: 'b',
|
||||
secondChildID: 'c',
|
||||
});
|
||||
const { schema, dataSource } = endpointSourceSchema();
|
||||
actions = [
|
||||
{
|
||||
type: 'serverReturnedResolverData',
|
||||
payload: {
|
||||
result: resolverTree,
|
||||
dataSource,
|
||||
schema,
|
||||
parameters: {
|
||||
databaseDocumentID: '',
|
||||
indices: ['someNonDefaultIndex'],
|
||||
filters: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
it('should have an empty array for tree parameter indices, and a non empty array for event indices', () => {
|
||||
const treeParameterIndices = selectors.treeParameterIndices(state());
|
||||
expect(treeParameterIndices.length).toBe(0);
|
||||
const eventIndices = selectors.eventIndices(state());
|
||||
expect(eventIndices.length).toBe(1);
|
||||
});
|
||||
});
|
||||
describe('when the resolver tree response is pending use the same indices the user is currently looking at data from', () => {
|
||||
beforeEach(() => {
|
||||
const { resolverTree } = mockTreeWithNoAncestorsAnd2Children({
|
||||
originID: 'a',
|
||||
firstChildID: 'b',
|
||||
secondChildID: 'c',
|
||||
});
|
||||
const { schema, dataSource } = endpointSourceSchema();
|
||||
actions = [
|
||||
{
|
||||
type: 'serverReturnedResolverData',
|
||||
payload: {
|
||||
result: resolverTree,
|
||||
dataSource,
|
||||
schema,
|
||||
parameters: {
|
||||
databaseDocumentID: '',
|
||||
indices: ['defaultIndex'],
|
||||
filters: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'appReceivedNewExternalProperties',
|
||||
payload: {
|
||||
databaseDocumentID: '',
|
||||
resolverComponentInstanceID: '',
|
||||
locationSearch: '',
|
||||
indices: ['someNonDefaultIndex', 'someOtherIndex'],
|
||||
shouldUpdate: false,
|
||||
filters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'appRequestedResolverData',
|
||||
payload: {
|
||||
databaseDocumentID: '',
|
||||
indices: ['someNonDefaultIndex', 'someOtherIndex'],
|
||||
filters: {},
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
it('should have an empty array for tree parameter indices, and the same set of indices as the last tree response', () => {
|
||||
const treeParameterIndices = selectors.treeParameterIndices(state());
|
||||
expect(treeParameterIndices.length).toBe(0);
|
||||
const eventIndices = selectors.eventIndices(state());
|
||||
expect(eventIndices.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -63,6 +63,13 @@ export function resolverComponentInstanceID(state: DataState): string {
|
|||
return state.resolverComponentInstanceID ? state.resolverComponentInstanceID : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* The indices resolver should use, passed in as external props.
|
||||
*/
|
||||
const currentIndices = (state: DataState): string[] => {
|
||||
return state.indices;
|
||||
};
|
||||
|
||||
/**
|
||||
* The last NewResolverTree we received, if any. It may be stale (it might not be for the same databaseDocumentID that
|
||||
* we're currently interested in.
|
||||
|
@ -71,6 +78,12 @@ const resolverTreeResponse = (state: DataState): NewResolverTree | undefined =>
|
|||
return state.tree?.lastResponse?.successful ? state.tree?.lastResponse.result : undefined;
|
||||
};
|
||||
|
||||
const lastResponseIndices = (state: DataState): string[] | undefined => {
|
||||
return state.tree?.lastResponse?.successful
|
||||
? state.tree?.lastResponse?.parameters?.indices
|
||||
: undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* If we received a NewResolverTree, return the schema associated with that tree, otherwise return undefined.
|
||||
* As of writing, this is only used for the info popover in the graph_controls panel
|
||||
|
@ -336,10 +349,22 @@ export const timeRangeFilters = createSelector(
|
|||
/**
|
||||
* The indices to use for the requests with the backend.
|
||||
*/
|
||||
export const treeParamterIndices = createSelector(treeParametersToFetch, (parameters) => {
|
||||
export const treeParameterIndices = createSelector(treeParametersToFetch, (parameters) => {
|
||||
return parameters?.indices ?? [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Panel requests should not use indices derived from the tree parameter selector, as this is only defined briefly while the resolver_tree_fetcher middleware is running.
|
||||
* Instead, panel requests should use the indices used by the last good request, falling back to the indices passed as external props.
|
||||
*/
|
||||
export const eventIndices = createSelector(
|
||||
lastResponseIndices,
|
||||
currentIndices,
|
||||
function eventIndices(lastIndices, current): string[] {
|
||||
return lastIndices ?? current ?? [];
|
||||
}
|
||||
);
|
||||
|
||||
export const layout: (state: DataState) => IsometricTaxiLayout = createSelector(
|
||||
tree,
|
||||
originID,
|
||||
|
|
|
@ -32,7 +32,7 @@ export function CurrentRelatedEventFetcher(
|
|||
const state = api.getState();
|
||||
|
||||
const newParams = selectors.panelViewAndParameters(state);
|
||||
const indices = selectors.treeParameterIndices(state);
|
||||
const indices = selectors.eventIndices(state);
|
||||
|
||||
const oldParams = last;
|
||||
last = newParams;
|
||||
|
|
|
@ -38,7 +38,7 @@ export function NodeDataFetcher(
|
|||
* This gets the visible nodes that we haven't already requested or received data for
|
||||
*/
|
||||
const newIDsToRequest: Set<string> = selectors.newIDsToRequest(state)(Number.POSITIVE_INFINITY);
|
||||
const indices = selectors.treeParameterIndices(state);
|
||||
const indices = selectors.eventIndices(state);
|
||||
|
||||
if (newIDsToRequest.size <= 0) {
|
||||
return;
|
||||
|
|
|
@ -27,7 +27,7 @@ export function RelatedEventsFetcher(
|
|||
|
||||
const newParams = selectors.panelViewAndParameters(state);
|
||||
const isLoadingMoreEvents = selectors.isLoadingMoreNodeEventsInCategory(state);
|
||||
const indices = selectors.treeParameterIndices(state);
|
||||
const indices = selectors.eventIndices(state);
|
||||
|
||||
const oldParams = last;
|
||||
const timeRangeFilters = selectors.timeRangeFilters(state);
|
||||
|
|
|
@ -80,9 +80,14 @@ export const treeRequestParametersToAbort = composeSelectors(
|
|||
*/
|
||||
export const treeParameterIndices = composeSelectors(
|
||||
dataStateSelector,
|
||||
dataSelectors.treeParamterIndices
|
||||
dataSelectors.treeParameterIndices
|
||||
);
|
||||
|
||||
/**
|
||||
* An array of indices to use for resolver panel requests.
|
||||
*/
|
||||
export const eventIndices = composeSelectors(dataStateSelector, dataSelectors.eventIndices);
|
||||
|
||||
export const resolverComponentInstanceID = composeSelectors(
|
||||
dataStateSelector,
|
||||
dataSelectors.resolverComponentInstanceID
|
||||
|
|
|
@ -376,6 +376,8 @@ export interface DataState {
|
|||
*/
|
||||
readonly resolverComponentInstanceID?: string;
|
||||
|
||||
readonly indices: string[];
|
||||
|
||||
/**
|
||||
* The `search` part of the URL.
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
kibanaObservable,
|
||||
createSecuritySolutionStorageMock,
|
||||
} from '../../../common/mock';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { createStore, State } from '../../../common/store';
|
||||
import * as timelineActions from '../../store/timeline/actions';
|
||||
|
||||
|
@ -43,7 +44,7 @@ describe('Flyout', () => {
|
|||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const props = {
|
||||
onAppLeave: jest.fn(),
|
||||
timelineId: 'test',
|
||||
timelineId: TimelineId.test,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -26,7 +26,7 @@ const Visible = styled.div<{ show?: boolean }>`
|
|||
Visible.displayName = 'Visible';
|
||||
|
||||
interface OwnProps {
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
onAppLeave: (handler: AppLeaveHandler) => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,13 +9,14 @@ import { shallow } from 'enzyme';
|
|||
import React from 'react';
|
||||
|
||||
import { TestProviders } from '../../../../common/mock';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import { Pane } from '.';
|
||||
|
||||
describe('Pane', () => {
|
||||
test('renders correctly against snapshot', () => {
|
||||
const EmptyComponent = shallow(
|
||||
<TestProviders>
|
||||
<Pane timelineId={'test'} />
|
||||
<Pane timelineId={TimelineId.test} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(EmptyComponent.find('Pane')).toMatchSnapshot();
|
||||
|
|
|
@ -11,12 +11,13 @@ import styled from 'styled-components';
|
|||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { StatefulTimeline } from '../../timeline';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import * as i18n from './translations';
|
||||
import { timelineActions } from '../../../store/timeline';
|
||||
import { focusActiveTimelineButton } from '../../timeline/helpers';
|
||||
|
||||
interface FlyoutPaneComponentProps {
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
}
|
||||
|
||||
const EuiFlyoutContainer = styled.div`
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
} from '../../../common/containers/use_full_screen';
|
||||
import { mockTimelineModel, TestProviders } from '../../../common/mock';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
|
||||
import { GraphOverlay } from '.';
|
||||
|
||||
jest.mock('../../../common/hooks/use_selector', () => ({
|
||||
|
@ -28,6 +27,10 @@ jest.mock('../../../common/containers/use_full_screen', () => ({
|
|||
useTimelineFullScreen: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../resolver/view/use_resolver_query_params_cleaner');
|
||||
jest.mock('../../../resolver/view/use_state_syncing_actions');
|
||||
jest.mock('../../../resolver/view/use_sync_selected_node');
|
||||
|
||||
describe('GraphOverlay', () => {
|
||||
beforeEach(() => {
|
||||
(useGlobalFullScreen as jest.Mock).mockReturnValue({
|
||||
|
@ -42,12 +45,11 @@ describe('GraphOverlay', () => {
|
|||
|
||||
describe('when used in an events viewer (i.e. in the Detections view, or the Host > Events view)', () => {
|
||||
const isEventViewer = true;
|
||||
const timelineId = 'used-as-an-events-viewer';
|
||||
|
||||
test('it has 100% width when isEventViewer is true and NOT in full screen mode', async () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<GraphOverlay timelineId={timelineId} isEventViewer={isEventViewer} />
|
||||
<GraphOverlay timelineId={TimelineId.test} isEventViewer={isEventViewer} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -69,7 +71,7 @@ describe('GraphOverlay', () => {
|
|||
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<GraphOverlay timelineId={timelineId} isEventViewer={isEventViewer} />
|
||||
<GraphOverlay timelineId={TimelineId.test} isEventViewer={isEventViewer} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import styled from 'styled-components';
|
|||
|
||||
import { FULL_SCREEN } from '../timeline/body/column_headers/translations';
|
||||
import { EXIT_FULL_SCREEN } from '../../../common/components/exit_full_screen/translations';
|
||||
import { DEFAULT_INDEX_KEY, FULL_SCREEN_TOGGLED_CLASS_NAME } from '../../../../common/constants';
|
||||
import { FULL_SCREEN_TOGGLED_CLASS_NAME } from '../../../../common/constants';
|
||||
import {
|
||||
useGlobalFullScreen,
|
||||
useTimelineFullScreen,
|
||||
|
@ -30,6 +30,8 @@ import { TimelineId } from '../../../../common/types/timeline';
|
|||
import { timelineSelectors } from '../../store/timeline';
|
||||
import { timelineDefaults } from '../../store/timeline/defaults';
|
||||
import { isFullScreen } from '../timeline/body/column_headers';
|
||||
import { useSourcererScope } from '../../../common/containers/sourcerer';
|
||||
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
|
||||
import { updateTimelineGraphEventId } from '../../../timelines/store/timeline/actions';
|
||||
import { Resolver } from '../../../resolver/view';
|
||||
import {
|
||||
|
@ -38,8 +40,6 @@ import {
|
|||
endSelector,
|
||||
} from '../../../common/components/super_date_picker/selectors';
|
||||
import * as i18n from './translations';
|
||||
import { useUiSetting$ } from '../../../common/lib/kibana';
|
||||
import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index';
|
||||
|
||||
const OverlayContainer = styled.div`
|
||||
${({ $restrictWidth }: { $restrictWidth: boolean }) =>
|
||||
|
@ -61,14 +61,14 @@ const FullScreenButtonIcon = styled(EuiButtonIcon)`
|
|||
|
||||
interface OwnProps {
|
||||
isEventViewer: boolean;
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
}
|
||||
|
||||
interface NavigationProps {
|
||||
fullScreen: boolean;
|
||||
globalFullScreen: boolean;
|
||||
onCloseOverlay: () => void;
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
timelineFullScreen: boolean;
|
||||
toggleFullScreen: () => void;
|
||||
}
|
||||
|
@ -169,16 +169,14 @@ const GraphOverlayComponent: React.FC<OwnProps> = ({ isEventViewer, timelineId }
|
|||
globalFullScreen,
|
||||
]);
|
||||
|
||||
const { signalIndexName } = useSignalIndex();
|
||||
const [siemDefaultIndices] = useUiSetting$<string[]>(DEFAULT_INDEX_KEY);
|
||||
const indices: string[] | null = useMemo(() => {
|
||||
if (signalIndexName === null) {
|
||||
return null;
|
||||
} else {
|
||||
return [...siemDefaultIndices, signalIndexName];
|
||||
}
|
||||
}, [signalIndexName, siemDefaultIndices]);
|
||||
let sourcereScope = SourcererScopeName.default;
|
||||
if ([TimelineId.detectionsRulesDetailsPage, TimelineId.detectionsPage].includes(timelineId)) {
|
||||
sourcereScope = SourcererScopeName.detections;
|
||||
} else if (timelineId === TimelineId.active) {
|
||||
sourcereScope = SourcererScopeName.timeline;
|
||||
}
|
||||
|
||||
const { selectedPatterns } = useSourcererScope(sourcereScope);
|
||||
return (
|
||||
<OverlayContainer
|
||||
data-test-subj="overlayContainer"
|
||||
|
@ -198,11 +196,11 @@ const GraphOverlayComponent: React.FC<OwnProps> = ({ isEventViewer, timelineId }
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
{graphEventId !== undefined && indices !== null ? (
|
||||
{graphEventId !== undefined ? (
|
||||
<StyledResolver
|
||||
databaseDocumentID={graphEventId}
|
||||
resolverComponentInstanceID={timelineId}
|
||||
indices={indices}
|
||||
indices={selectedPatterns}
|
||||
shouldUpdate={shouldUpdate}
|
||||
filters={{ from, to }}
|
||||
/>
|
||||
|
|
|
@ -9,10 +9,11 @@ import React, { useMemo } from 'react';
|
|||
|
||||
import { timelineSelectors } from '../../../store/timeline';
|
||||
import { useShallowEqualSelector } from '../../../../common/hooks/use_selector';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import { GraphOverlay } from '../../graph_overlay';
|
||||
|
||||
interface GraphTabContentProps {
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
}
|
||||
|
||||
const GraphTabContentComponent: React.FC<GraphTabContentProps> = ({ timelineId }) => {
|
||||
|
|
|
@ -12,7 +12,7 @@ import useResizeObserver from 'use-resize-observer/polyfilled';
|
|||
import { DragDropContextWrapper } from '../../../common/components/drag_and_drop/drag_drop_context_wrapper';
|
||||
import '../../../common/mock/match_media';
|
||||
import { mockBrowserFields, mockDocValueFields } from '../../../common/containers/source/mock';
|
||||
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { mockIndexNames, mockIndexPattern, TestProviders } from '../../../common/mock';
|
||||
|
||||
import { StatefulTimeline, Props as StatefulTimelineOwnProps } from './index';
|
||||
|
@ -55,7 +55,7 @@ jest.mock('../../../common/containers/sourcerer', () => {
|
|||
});
|
||||
describe('StatefulTimeline', () => {
|
||||
const props: StatefulTimelineOwnProps = {
|
||||
timelineId: 'timeline-test',
|
||||
timelineId: TimelineId.test,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -91,7 +91,7 @@ describe('StatefulTimeline', () => {
|
|||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find(`[data-timeline-id="timeline-test"].${SELECTOR_TIMELINE_GLOBAL_CONTAINER}`)
|
||||
.find(`[data-timeline-id="test"].${SELECTOR_TIMELINE_GLOBAL_CONTAINER}`)
|
||||
.first()
|
||||
.exists()
|
||||
).toEqual(true);
|
||||
|
|
|
@ -18,7 +18,7 @@ import { isTab } from '../../../common/components/accessibility/helpers';
|
|||
import { useSourcererScope } from '../../../common/containers/sourcerer';
|
||||
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
|
||||
import { FlyoutHeader, FlyoutHeaderPanel } from '../flyout/header';
|
||||
import { TimelineType, TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { TimelineType, TimelineTabs, TimelineId } from '../../../../common/types/timeline';
|
||||
import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector';
|
||||
import { activeTimeline } from '../../containers/active_timeline_context';
|
||||
import { EVENTS_COUNT_BUTTON_CLASS_NAME, onTimelineTabKeyPressed } from './helpers';
|
||||
|
@ -35,7 +35,7 @@ const TimelineTemplateBadge = styled.div`
|
|||
`;
|
||||
|
||||
export interface Props {
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
}
|
||||
|
||||
const TimelineSavingProgressComponent: React.FC<Props> = ({ timelineId }) => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { EuiBadge, EuiLoadingContent, EuiTabs, EuiTab } from '@elastic/eui';
|
|||
import React, { lazy, memo, Suspense, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import { TimelineTabs } from '../../../../../common/types/timeline';
|
||||
import { TimelineTabs, TimelineId } from '../../../../../common/types/timeline';
|
||||
|
||||
import {
|
||||
useShallowEqualSelector,
|
||||
|
@ -42,7 +42,7 @@ const NotesTabContent = lazy(() => import('../notes_tab_content'));
|
|||
const PinnedTabContent = lazy(() => import('../pinned_tab_content'));
|
||||
|
||||
interface BasicTimelineTab {
|
||||
timelineId: string;
|
||||
timelineId: TimelineId;
|
||||
graphEventId?: string;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue