mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
This PR fixes regressions around the bulk selection action, including: * Incorrect total when opening/closing signals * Selection total persisting after opening/closing signals * `Select All` checkbox remaining selected after opening/closing signals * Bulk action not being enabled after opening/closing via single action while others are selected <details><summary>Before</summary> <p>  </p> </details> <details><summary>After</summary> <p>  </p> </details> <details><summary>Before</summary> <p>  </p> </details> <details><summary>After</summary> <p>  </p> </details> Delete any items that are not applicable to this PR. - [ ] ~Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~ - [ ] ~[Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~ - [ ] ~[Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios~ - [ ] ~This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~ - [ ] ~This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)~ - [ ] ~This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~ - [ ] ~This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~
This commit is contained in:
parent
741c30cdff
commit
b66e88fd99
6 changed files with 61 additions and 8 deletions
|
@ -161,7 +161,6 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
totalCountMinusDeleted
|
||||
) ?? i18n.UNIT(totalCountMinusDeleted)}`;
|
||||
|
||||
// TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
|
||||
return (
|
||||
<>
|
||||
<HeaderSection
|
||||
|
|
|
@ -19,7 +19,7 @@ export interface QueryTemplateProps {
|
|||
startDate?: number;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type FetchMoreOptionsArgs<TData, TVariables> = FetchMoreQueryOptions<any, any> &
|
||||
export type FetchMoreOptionsArgs<TData, TVariables> = FetchMoreQueryOptions<any, any> &
|
||||
FetchMoreOptions<TData, TVariables>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -40,6 +40,19 @@ export class QueryTemplate<
|
|||
tiebreaker?: string
|
||||
) => FetchMoreOptionsArgs<TData, TVariables>;
|
||||
|
||||
private refetch!: (variables?: TVariables) => Promise<ApolloQueryResult<TData>>;
|
||||
|
||||
private executeBeforeFetchMore!: ({ id }: { id?: string }) => void;
|
||||
|
||||
private executeBeforeRefetch!: ({ id }: { id?: string }) => void;
|
||||
|
||||
public setExecuteBeforeFetchMore = (val: ({ id }: { id?: string }) => void) => {
|
||||
this.executeBeforeFetchMore = val;
|
||||
};
|
||||
public setExecuteBeforeRefetch = (val: ({ id }: { id?: string }) => void) => {
|
||||
this.executeBeforeRefetch = val;
|
||||
};
|
||||
|
||||
public setFetchMore = (
|
||||
val: (fetchMoreOptions: FetchMoreOptionsArgs<TData, TVariables>) => PromiseApolloQueryResult
|
||||
) => {
|
||||
|
@ -52,6 +65,17 @@ export class QueryTemplate<
|
|||
this.fetchMoreOptions = val;
|
||||
};
|
||||
|
||||
public wrappedLoadMore = (newCursor: string, tiebreaker?: string) =>
|
||||
this.fetchMore(this.fetchMoreOptions(newCursor, tiebreaker));
|
||||
public setRefetch = (val: (variables?: TVariables) => Promise<ApolloQueryResult<TData>>) => {
|
||||
this.refetch = val;
|
||||
};
|
||||
|
||||
public wrappedLoadMore = (newCursor: string, tiebreaker?: string) => {
|
||||
this.executeBeforeFetchMore({ id: this.props.id });
|
||||
return this.fetchMore(this.fetchMoreOptions(newCursor, tiebreaker));
|
||||
};
|
||||
|
||||
public wrappedRefetch = (variables?: TVariables) => {
|
||||
this.executeBeforeRefetch({ id: this.props.id });
|
||||
return this.refetch(variables);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { getOr } from 'lodash/fp';
|
|||
import memoizeOne from 'memoize-one';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { compose } from 'redux';
|
||||
import { compose, Dispatch } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns';
|
||||
|
@ -26,6 +26,8 @@ import { createFilter } from '../helpers';
|
|||
import { QueryTemplate, QueryTemplateProps } from '../query_template';
|
||||
import { EventType } from '../../store/timeline/model';
|
||||
import { timelineQuery } from './index.gql_query';
|
||||
import { timelineActions } from '../../store/timeline';
|
||||
import { SIGNALS_PAGE_TIMELINE_ID } from '../../pages/detection_engine/components/signals';
|
||||
|
||||
export interface TimelineArgs {
|
||||
events: TimelineItem[];
|
||||
|
@ -40,6 +42,7 @@ export interface TimelineArgs {
|
|||
}
|
||||
|
||||
export interface TimelineQueryReduxProps {
|
||||
clearSignalsState: ({ id }: { id?: string }) => void;
|
||||
isInspected: boolean;
|
||||
}
|
||||
|
||||
|
@ -71,6 +74,7 @@ class TimelineQueryComponent extends QueryTemplate<
|
|||
public render() {
|
||||
const {
|
||||
children,
|
||||
clearSignalsState,
|
||||
eventType = 'raw',
|
||||
id,
|
||||
indexPattern,
|
||||
|
@ -100,6 +104,7 @@ class TimelineQueryComponent extends QueryTemplate<
|
|||
defaultIndex,
|
||||
inspect: isInspected,
|
||||
};
|
||||
|
||||
return (
|
||||
<Query<GetTimelineQuery.Query, GetTimelineQuery.Variables>
|
||||
query={timelineQuery}
|
||||
|
@ -108,6 +113,10 @@ class TimelineQueryComponent extends QueryTemplate<
|
|||
variables={variables}
|
||||
>
|
||||
{({ data, loading, fetchMore, refetch }) => {
|
||||
this.setRefetch(refetch);
|
||||
this.setExecuteBeforeRefetch(clearSignalsState);
|
||||
this.setExecuteBeforeFetchMore(clearSignalsState);
|
||||
|
||||
const timelineEdges = getOr([], 'source.Timeline.edges', data);
|
||||
this.setFetchMore(fetchMore);
|
||||
this.setFetchMoreOptions((newCursor: string, tiebreaker?: string) => ({
|
||||
|
@ -141,7 +150,7 @@ class TimelineQueryComponent extends QueryTemplate<
|
|||
return children!({
|
||||
id,
|
||||
inspect: getOr(null, 'source.Timeline.inspect', data),
|
||||
refetch,
|
||||
refetch: this.wrappedRefetch,
|
||||
loading,
|
||||
totalCount: getOr(0, 'source.Timeline.totalCount', data),
|
||||
pageInfo: getOr({}, 'source.Timeline.pageInfo', data),
|
||||
|
@ -171,7 +180,16 @@ const makeMapStateToProps = () => {
|
|||
return mapStateToProps;
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||
clearSignalsState: ({ id }: { id?: string }) => {
|
||||
if (id != null && id === SIGNALS_PAGE_TIMELINE_ID) {
|
||||
dispatch(timelineActions.clearEventsLoading({ id }));
|
||||
dispatch(timelineActions.clearEventsDeleted({ id }));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const TimelineQuery = compose<React.ComponentClass<OwnProps>>(
|
||||
connect(makeMapStateToProps),
|
||||
connect(makeMapStateToProps, mapDispatchToProps),
|
||||
withKibana
|
||||
)(TimelineQueryComponent);
|
||||
|
|
|
@ -51,7 +51,7 @@ import {
|
|||
} from './types';
|
||||
import { dispatchUpdateTimeline } from '../../../../components/open_timeline/helpers';
|
||||
|
||||
const SIGNALS_PAGE_TIMELINE_ID = 'signals-page';
|
||||
export const SIGNALS_PAGE_TIMELINE_ID = 'signals-page';
|
||||
|
||||
interface ReduxProps {
|
||||
globalQuery: Query;
|
||||
|
|
|
@ -114,6 +114,7 @@ const SignalsUtilityBarComponent: React.FC<SignalsUtilityBarProps> = ({
|
|||
export const SignalsUtilityBar = React.memo(
|
||||
SignalsUtilityBarComponent,
|
||||
(prevProps, nextProps) =>
|
||||
prevProps.areEventsLoading === nextProps.areEventsLoading &&
|
||||
prevProps.selectedEventIds === nextProps.selectedEventIds &&
|
||||
prevProps.totalCount === nextProps.totalCount &&
|
||||
prevProps.showClearSelection === nextProps.showClearSelection
|
||||
|
|
|
@ -1161,11 +1161,22 @@ export const setDeletedTimelineEvents = ({
|
|||
? union(timeline.deletedEventIds, eventIds)
|
||||
: timeline.deletedEventIds.filter(currentEventId => !eventIds.includes(currentEventId));
|
||||
|
||||
const selectedEventIds = Object.fromEntries(
|
||||
Object.entries(timeline.selectedEventIds).filter(
|
||||
([selectedEventId]) => !deletedEventIds.includes(selectedEventId)
|
||||
)
|
||||
);
|
||||
|
||||
const isSelectAllChecked =
|
||||
Object.keys(selectedEventIds).length > 0 ? timeline.isSelectAllChecked : false;
|
||||
|
||||
return {
|
||||
...timelineById,
|
||||
[id]: {
|
||||
...timeline,
|
||||
deletedEventIds,
|
||||
selectedEventIds,
|
||||
isSelectAllChecked,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue