Skip to content

Commit e2ee734

Browse files
authored
[SECURITY_SOLUTION][ENDPOINT] Grid view for trusted apps. (#79485) (#79704)
* Refactored store code to group properties related to location so that would be easy to introduce a new view type parameter. * Added view type to the location and routing. * Added a simple hook to make navigation easier. * Improved the navigation hook to get params. * Some fix for double notification after creating trusted app. * Added a hook to perform trusted app store actions. * Fixed trusted app card delete callback. * Added grid view. * Fixed the stories structuring. * Shared more logic between grid and list. * Finalized the grid view. * Flattened the props. * Improved memoization. * Moved the flex item elements inside conditions. * Fixed broken stories. * Updated the snapshot. * Updated the snapshot.
1 parent 3d2fd2f commit e2ee734

File tree

32 files changed

+9631
-177
lines changed

32 files changed

+9631
-177
lines changed

x-pack/plugins/security_solution/public/common/components/conditions_table/index.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ addDecorator((storyFn) => (
1515
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>{storyFn()}</ThemeProvider>
1616
));
1717

18-
storiesOf('Components|ConditionsTable', module)
18+
storiesOf('Components/ConditionsTable', module)
1919
.add('single item', () => {
2020
return <ConditionsTable items={createItems(1)} columns={TEST_COLUMNS} badge="and" />;
2121
})

x-pack/plugins/security_solution/public/common/components/item_details_card/index.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ addDecorator((storyFn) => (
1414
<ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>{storyFn()}</ThemeProvider>
1515
));
1616

17-
storiesOf('Components|ItemDetailsCard', module).add('default', () => {
17+
storiesOf('Components/ItemDetailsCard', module).add('default', () => {
1818
return (
1919
<ItemDetailsCard>
2020
<ItemDetailsPropertySummary name={'property 1'} value={'value 1'} />

x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ import { ServerApiError } from '../../../../common/types';
88
import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types/trusted_apps';
99
import { AsyncResourceState } from '.';
1010

11-
export interface PaginationInfo {
12-
index: number;
13-
size: number;
11+
export interface Pagination {
12+
pageIndex: number;
13+
pageSize: number;
14+
totalItemCount: number;
15+
pageSizeOptions: number[];
1416
}
1517

1618
export interface TrustedAppsListData {
1719
items: TrustedApp[];
18-
totalItemsCount: number;
19-
paginationInfo: PaginationInfo;
20+
pageIndex: number;
21+
pageSize: number;
2022
timestamp: number;
23+
totalItemsCount: number;
2124
}
2225

2326
/** Store State when an API request has been sent to create a new trusted app entry */

x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { applyMiddleware, createStore } from 'redux';
99
import { createSpyMiddleware } from '../../../../common/store/test_utils';
1010

1111
import {
12+
createDefaultPagination,
1213
createListLoadedResourceState,
1314
createLoadedListViewWithPagination,
1415
createSampleTrustedApp,
@@ -19,7 +20,7 @@ import {
1920
} from '../test_utils';
2021

2122
import { TrustedAppsService } from '../service';
22-
import { PaginationInfo, TrustedAppsListPageState } from '../state';
23+
import { Pagination, TrustedAppsListPageState } from '../state';
2324
import { initialTrustedAppsPageState, trustedAppsPageReducer } from './reducer';
2425
import { createTrustedAppsPageMiddleware } from './middleware';
2526

@@ -31,12 +32,16 @@ Date.now = dateNowMock;
3132

3233
const initialState = initialTrustedAppsPageState();
3334

34-
const createGetTrustedListAppsResponse = (pagination: PaginationInfo, totalItemsCount: number) => ({
35-
data: createSampleTrustedApps(pagination),
36-
page: pagination.index,
37-
per_page: pagination.size,
38-
total: totalItemsCount,
39-
});
35+
const createGetTrustedListAppsResponse = (pagination: Partial<Pagination>) => {
36+
const fullPagination = { ...createDefaultPagination(), ...pagination };
37+
38+
return {
39+
data: createSampleTrustedApps(pagination),
40+
page: fullPagination.pageIndex,
41+
per_page: fullPagination.pageSize,
42+
total: fullPagination.totalItemCount,
43+
};
44+
};
4045

4146
const createTrustedAppsServiceMock = (): jest.Mocked<TrustedAppsService> => ({
4247
getTrustedAppsList: jest.fn(),
@@ -74,14 +79,12 @@ describe('middleware', () => {
7479

7580
describe('refreshing list resource state', () => {
7681
it('refreshes the list when location changes and data gets outdated', async () => {
77-
const pagination = { index: 2, size: 50 };
82+
const pagination = { pageIndex: 2, pageSize: 50 };
7883
const location = { page_index: 2, page_size: 50, show: undefined, view_type: 'grid' };
7984
const service = createTrustedAppsServiceMock();
8085
const { store, spyMiddleware } = createStoreSetup(service);
8186

82-
service.getTrustedAppsList.mockResolvedValue(
83-
createGetTrustedListAppsResponse(pagination, 500)
84-
);
87+
service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination));
8588

8689
store.dispatch(createUserChangedUrlAction('/trusted_apps', '?page_index=2&page_size=50'));
8790

@@ -102,21 +105,19 @@ describe('middleware', () => {
102105

103106
expect(store.getState()).toStrictEqual({
104107
...initialState,
105-
listView: createLoadedListViewWithPagination(initialNow, pagination, 500),
108+
listView: createLoadedListViewWithPagination(initialNow, pagination),
106109
active: true,
107110
location,
108111
});
109112
});
110113

111114
it('does not refresh the list when location changes and data does not get outdated', async () => {
112-
const pagination = { index: 2, size: 50 };
115+
const pagination = { pageIndex: 2, pageSize: 50 };
113116
const location = { page_index: 2, page_size: 50, show: undefined, view_type: 'grid' };
114117
const service = createTrustedAppsServiceMock();
115118
const { store, spyMiddleware } = createStoreSetup(service);
116119

117-
service.getTrustedAppsList.mockResolvedValue(
118-
createGetTrustedListAppsResponse(pagination, 500)
119-
);
120+
service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination));
120121

121122
store.dispatch(createUserChangedUrlAction('/trusted_apps', '?page_index=2&page_size=50'));
122123

@@ -127,22 +128,20 @@ describe('middleware', () => {
127128
expect(service.getTrustedAppsList).toBeCalledTimes(1);
128129
expect(store.getState()).toStrictEqual({
129130
...initialState,
130-
listView: createLoadedListViewWithPagination(initialNow, pagination, 500),
131+
listView: createLoadedListViewWithPagination(initialNow, pagination),
131132
active: true,
132133
location,
133134
});
134135
});
135136

136137
it('refreshes the list when data gets outdated with and outdate action', async () => {
137138
const newNow = 222222;
138-
const pagination = { index: 0, size: 10 };
139+
const pagination = { pageIndex: 0, pageSize: 10 };
139140
const location = { page_index: 0, page_size: 10, show: undefined, view_type: 'grid' };
140141
const service = createTrustedAppsServiceMock();
141142
const { store, spyMiddleware } = createStoreSetup(service);
142143

143-
service.getTrustedAppsList.mockResolvedValue(
144-
createGetTrustedListAppsResponse(pagination, 500)
145-
);
144+
service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination));
146145

147146
store.dispatch(createUserChangedUrlAction('/trusted_apps'));
148147

@@ -157,7 +156,7 @@ describe('middleware', () => {
157156
listView: {
158157
listResourceState: {
159158
type: 'LoadingResourceState',
160-
previousState: createListLoadedResourceState(pagination, 500, initialNow),
159+
previousState: createListLoadedResourceState(pagination, initialNow),
161160
},
162161
freshDataTimestamp: newNow,
163162
},
@@ -169,7 +168,7 @@ describe('middleware', () => {
169168

170169
expect(store.getState()).toStrictEqual({
171170
...initialState,
172-
listView: createLoadedListViewWithPagination(newNow, pagination, 500),
171+
listView: createLoadedListViewWithPagination(newNow, pagination),
173172
active: true,
174173
location,
175174
});
@@ -211,11 +210,11 @@ describe('middleware', () => {
211210
const newNow = 222222;
212211
const entry = createSampleTrustedApp(3);
213212
const notFoundError = createServerApiError('Not Found');
214-
const pagination = { index: 0, size: 10 };
213+
const pagination = { pageIndex: 0, pageSize: 10 };
215214
const location = { page_index: 0, page_size: 10, show: undefined, view_type: 'grid' };
216-
const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination, 500);
217-
const listView = createLoadedListViewWithPagination(initialNow, pagination, 500);
218-
const listViewNew = createLoadedListViewWithPagination(newNow, pagination, 500);
215+
const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination);
216+
const listView = createLoadedListViewWithPagination(initialNow, pagination);
217+
const listViewNew = createLoadedListViewWithPagination(newNow, pagination);
219218
const testStartState = { ...initialState, listView, active: true, location };
220219

221220
it('does not submit when entry is undefined', async () => {

x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ const refreshListIfNeeded = async (
7575
type: 'LoadedResourceState',
7676
data: {
7777
items: response.data,
78+
pageIndex,
79+
pageSize,
7880
totalItemsCount: response.total,
79-
paginationInfo: { index: pageIndex, size: pageSize },
8081
timestamp: Date.now(),
8182
},
8283
})

x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ describe('reducer', () => {
7171
describe('TrustedAppsListResourceStateChanged', () => {
7272
it('sets the current list resource state', () => {
7373
const listResourceState = createListLoadedResourceState(
74-
{ index: 3, size: 50 },
75-
200,
74+
{ pageIndex: 3, pageSize: 50 },
7675
initialNow
7776
);
7877
const result = trustedAppsPageReducer(

x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
} from './selectors';
3030

3131
import {
32-
createDefaultPaginationInfo,
32+
createDefaultPagination,
3333
createListComplexLoadingResourceState,
3434
createListFailedResourceState,
3535
createListLoadedResourceState,
@@ -66,13 +66,13 @@ describe('selectors', () => {
6666
});
6767

6868
it('returns true when current loaded page index is outdated', () => {
69-
const listView = createLoadedListViewWithPagination(initialNow, { index: 1, size: 20 });
69+
const listView = createLoadedListViewWithPagination(initialNow, { pageIndex: 1 });
7070

7171
expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true);
7272
});
7373

7474
it('returns true when current loaded page size is outdated', () => {
75-
const listView = createLoadedListViewWithPagination(initialNow, { index: 0, size: 50 });
75+
const listView = createLoadedListViewWithPagination(initialNow, { pageSize: 50 });
7676

7777
expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true);
7878
});
@@ -112,16 +112,15 @@ describe('selectors', () => {
112112
...initialState,
113113
listView: {
114114
listResourceState: createListComplexLoadingResourceState(
115-
createDefaultPaginationInfo(),
116-
200,
115+
createDefaultPagination(),
117116
initialNow
118117
),
119118
freshDataTimestamp: initialNow,
120119
},
121120
};
122121

123122
expect(getLastLoadedListResourceState(state)).toStrictEqual(
124-
createListLoadedResourceState(createDefaultPaginationInfo(), 200, initialNow)
123+
createListLoadedResourceState(createDefaultPagination(), initialNow)
125124
);
126125
});
127126
});
@@ -136,17 +135,14 @@ describe('selectors', () => {
136135
...initialState,
137136
listView: {
138137
listResourceState: createListComplexLoadingResourceState(
139-
createDefaultPaginationInfo(),
140-
200,
138+
createDefaultPagination(),
141139
initialNow
142140
),
143141
freshDataTimestamp: initialNow,
144142
},
145143
};
146144

147-
expect(getListItems(state)).toStrictEqual(
148-
createSampleTrustedApps(createDefaultPaginationInfo())
149-
);
145+
expect(getListItems(state)).toStrictEqual(createSampleTrustedApps(createDefaultPagination()));
150146
});
151147
});
152148

@@ -160,8 +156,7 @@ describe('selectors', () => {
160156
...initialState,
161157
listView: {
162158
listResourceState: createListComplexLoadingResourceState(
163-
createDefaultPaginationInfo(),
164-
200,
159+
createDefaultPagination(),
165160
initialNow
166161
),
167162
freshDataTimestamp: initialNow,
@@ -202,8 +197,7 @@ describe('selectors', () => {
202197
...initialState,
203198
listView: {
204199
listResourceState: createListComplexLoadingResourceState(
205-
createDefaultPaginationInfo(),
206-
200,
200+
createDefaultPagination(),
207201
initialNow
208202
),
209203
freshDataTimestamp: initialNow,
@@ -236,8 +230,7 @@ describe('selectors', () => {
236230
...initialState,
237231
listView: {
238232
listResourceState: createListComplexLoadingResourceState(
239-
createDefaultPaginationInfo(),
240-
200,
233+
createDefaultPagination(),
241234
initialNow
242235
),
243236
freshDataTimestamp: initialNow,

x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { ServerApiError } from '../../../../common/types';
88
import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types';
9+
import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants';
910

1011
import {
1112
AsyncResourceState,
@@ -16,6 +17,7 @@ import {
1617
isLoadingResourceState,
1718
isOutdatedResourceState,
1819
LoadedResourceState,
20+
Pagination,
1921
TrustedAppCreateFailure,
2022
TrustedAppsListData,
2123
TrustedAppsListPageLocation,
@@ -36,8 +38,8 @@ export const needsRefreshOfListData = (state: Immutable<TrustedAppsListPageState
3638
state.active &&
3739
isOutdatedResourceState(currentPage, (data) => {
3840
return (
39-
data.paginationInfo.index === location.page_index &&
40-
data.paginationInfo.size === location.page_size &&
41+
data.pageIndex === location.page_index &&
42+
data.pageSize === location.page_size &&
4143
data.timestamp >= freshDataTimestamp
4244
);
4345
})
@@ -74,6 +76,17 @@ export const getListTotalItemsCount = (state: Immutable<TrustedAppsListPageState
7476
return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0;
7577
};
7678

79+
export const getListPagination = (state: Immutable<TrustedAppsListPageState>): Pagination => {
80+
const lastLoadedResourceState = getLastLoadedResourceState(state.listView.listResourceState);
81+
82+
return {
83+
pageIndex: state.location.page_index,
84+
pageSize: state.location.page_size,
85+
totalItemCount: lastLoadedResourceState?.data.totalItemsCount || 0,
86+
pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS],
87+
};
88+
};
89+
7790
export const getCurrentLocation = (
7891
state: Immutable<TrustedAppsListPageState>
7992
): TrustedAppsListPageLocation => state.location;

0 commit comments

Comments
 (0)