Skip to content

Commit f37b01b

Browse files
[ML] Transforms list: persist pagination through refresh interval (#76786) (#77061)
* use basic table in transforms to persist pagination * simplify state and update types
1 parent ba7c9f9 commit f37b01b

File tree

3 files changed

+376
-183
lines changed

3 files changed

+376
-183
lines changed

x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx

Lines changed: 60 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,30 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { MouseEventHandler, FC, useContext, useState } from 'react';
7+
import React, { MouseEventHandler, FC, useContext, useEffect, useState } from 'react';
88

99
import { i18n } from '@kbn/i18n';
1010

1111
import {
12-
Direction,
13-
EuiBadge,
12+
EuiBasicTable,
13+
EuiBasicTableProps,
1414
EuiButtonEmpty,
1515
EuiButtonIcon,
1616
EuiCallOut,
1717
EuiEmptyPrompt,
1818
EuiFlexGroup,
1919
EuiFlexItem,
20-
EuiInMemoryTable,
21-
EuiSearchBarProps,
20+
EuiSearchBar,
21+
EuiSpacer,
2222
EuiPopover,
2323
EuiTitle,
2424
} from '@elastic/eui';
2525

26-
import { TransformId, TRANSFORM_STATE } from '../../../../../../common';
26+
import { TransformId } from '../../../../../../common';
2727

2828
import {
2929
useRefreshTransformList,
3030
TransformListRow,
31-
TRANSFORM_MODE,
3231
TRANSFORM_LIST_COLUMN,
3332
} from '../../../../common';
3433
import { useStopTransforms } from '../../../../hooks';
@@ -45,9 +44,11 @@ import {
4544
import { useStartAction, StartActionName, StartActionModal } from '../action_start';
4645
import { StopActionName } from '../action_stop';
4746

48-
import { ItemIdToExpandedRowMap, Clause, TermClause, FieldClause, Value } from './common';
49-
import { getTaskStateBadge, useColumns } from './use_columns';
47+
import { ItemIdToExpandedRowMap } from './common';
48+
import { useColumns } from './use_columns';
5049
import { ExpandedRow } from './expanded_row';
50+
import { TransformSearchBar, filterTransforms } from './transform_search_bar';
51+
import { useTableSettings } from './use_table_settings';
5152

5253
function getItemIdToExpandedRowMap(
5354
itemIds: TransformId[],
@@ -62,14 +63,6 @@ function getItemIdToExpandedRowMap(
6263
}, {} as ItemIdToExpandedRowMap);
6364
}
6465

65-
function stringMatch(str: string | undefined, substr: any) {
66-
return (
67-
typeof str === 'string' &&
68-
typeof substr === 'string' &&
69-
(str.toLowerCase().match(substr.toLowerCase()) === null) === false
70-
);
71-
}
72-
7366
interface Props {
7467
errorMessage: any;
7568
isInitialized: boolean;
@@ -88,24 +81,14 @@ export const TransformList: FC<Props> = ({
8881
const [isLoading, setIsLoading] = useState(false);
8982
const { refresh } = useRefreshTransformList({ isLoading: setIsLoading });
9083

91-
const [filterActive, setFilterActive] = useState(false);
92-
84+
const [searchQueryText, setSearchQueryText] = useState<string>('');
9385
const [filteredTransforms, setFilteredTransforms] = useState<TransformListRow[]>([]);
9486
const [expandedRowItemIds, setExpandedRowItemIds] = useState<TransformId[]>([]);
95-
9687
const [transformSelection, setTransformSelection] = useState<TransformListRow[]>([]);
9788
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false);
9889
const bulkStartAction = useStartAction(false);
9990
const bulkDeleteAction = useDeleteAction(false);
10091

101-
const [searchError, setSearchError] = useState<any>(undefined);
102-
103-
const [pageIndex, setPageIndex] = useState(0);
104-
const [pageSize, setPageSize] = useState(10);
105-
106-
const [sortField, setSortField] = useState<string>(TRANSFORM_LIST_COLUMN.ID);
107-
const [sortDirection, setSortDirection] = useState<Direction>('asc');
108-
10992
const stopTransforms = useStopTransforms();
11093

11194
const { capabilities } = useContext(AuthorizationContext);
@@ -114,90 +97,41 @@ export const TransformList: FC<Props> = ({
11497
!capabilities.canPreviewTransform ||
11598
!capabilities.canStartStopTransform;
11699

117-
const onQueryChange = ({
118-
query,
119-
error,
120-
}: Parameters<NonNullable<EuiSearchBarProps['onChange']>>[0]) => {
121-
if (error) {
122-
setSearchError(error.message);
100+
const { columns, modals: singleActionModals } = useColumns(
101+
expandedRowItemIds,
102+
setExpandedRowItemIds,
103+
transformSelection
104+
);
105+
106+
const updateFilteredItems = (queryClauses: any) => {
107+
if (queryClauses.length) {
108+
const filtered = filterTransforms(transforms, queryClauses);
109+
setFilteredTransforms(filtered);
123110
} else {
124-
let clauses: Clause[] = [];
125-
if (query && query.ast !== undefined && query.ast.clauses !== undefined) {
126-
clauses = query.ast.clauses;
127-
}
128-
if (clauses.length > 0) {
129-
setFilterActive(true);
130-
filterTransforms(clauses as Array<TermClause | FieldClause>);
131-
} else {
132-
setFilterActive(false);
133-
}
134-
setSearchError(undefined);
111+
setFilteredTransforms(transforms);
135112
}
136113
};
137114

138-
const filterTransforms = (clauses: Array<TermClause | FieldClause>) => {
139-
setIsLoading(true);
140-
// keep count of the number of matches we make as we're looping over the clauses
141-
// we only want to return transforms which match all clauses, i.e. each search term is ANDed
142-
// { transform-one: { transform: { id: transform-one, config: {}, state: {}, ... }, count: 0 }, transform-two: {...} }
143-
const matches: Record<string, any> = transforms.reduce((p: Record<string, any>, c) => {
144-
p[c.id] = {
145-
transform: c,
146-
count: 0,
147-
};
148-
return p;
149-
}, {});
150-
151-
clauses.forEach((c) => {
152-
// the search term could be negated with a minus, e.g. -bananas
153-
const bool = c.match === 'must';
154-
let ts = [];
155-
156-
if (c.type === 'term') {
157-
// filter term based clauses, e.g. bananas
158-
// match on ID and description
159-
// if the term has been negated, AND the matches
160-
if (bool === true) {
161-
ts = transforms.filter(
162-
(transform) =>
163-
stringMatch(transform.id, c.value) === bool ||
164-
stringMatch(transform.config.description, c.value) === bool
165-
);
166-
} else {
167-
ts = transforms.filter(
168-
(transform) =>
169-
stringMatch(transform.id, c.value) === bool &&
170-
stringMatch(transform.config.description, c.value) === bool
171-
);
115+
useEffect(() => {
116+
const filterList = () => {
117+
if (searchQueryText !== '') {
118+
const query = EuiSearchBar.Query.parse(searchQueryText);
119+
let clauses: any = [];
120+
if (query && query.ast !== undefined && query.ast.clauses !== undefined) {
121+
clauses = query.ast.clauses;
172122
}
123+
updateFilteredItems(clauses);
173124
} else {
174-
// filter other clauses, i.e. the mode and status filters
175-
if (Array.isArray(c.value)) {
176-
// the status value is an array of string(s) e.g. ['failed', 'stopped']
177-
ts = transforms.filter((transform) =>
178-
(c.value as Value[]).includes(transform.stats.state)
179-
);
180-
} else {
181-
ts = transforms.filter((transform) => transform.mode === c.value);
182-
}
125+
updateFilteredItems([]);
183126
}
184-
185-
ts.forEach((t) => matches[t.id].count++);
186-
});
187-
188-
// loop through the matches and return only transforms which have match all the clauses
189-
const filtered = Object.values(matches)
190-
.filter((m) => (m && m.count) >= clauses.length)
191-
.map((m) => m.transform);
192-
193-
setFilteredTransforms(filtered);
194-
setIsLoading(false);
195-
};
196-
197-
const { columns, modals: singleActionModals } = useColumns(
198-
expandedRowItemIds,
199-
setExpandedRowItemIds,
200-
transformSelection
127+
};
128+
filterList();
129+
// eslint-disable-next-line
130+
}, [searchQueryText, transforms]); // missing dependency updateFilteredItems
131+
132+
const { onTableChange, pageOfItems, pagination, sorting } = useTableSettings<TransformListRow>(
133+
TRANSFORM_LIST_COLUMN.ID,
134+
filteredTransforms
201135
);
202136

203137
// Before the transforms have been loaded for the first time, display the loading indicator only.
@@ -246,23 +180,8 @@ export const TransformList: FC<Props> = ({
246180
);
247181
}
248182

249-
const sorting = {
250-
sort: {
251-
field: sortField,
252-
direction: sortDirection,
253-
},
254-
};
255-
256183
const itemIdToExpandedRowMap = getItemIdToExpandedRowMap(expandedRowItemIds, transforms);
257184

258-
const pagination = {
259-
initialPageIndex: pageIndex,
260-
initialPageSize: pageSize,
261-
totalItemCount: transforms.length,
262-
pageSizeOptions: [10, 20, 50],
263-
hidePerPageOptions: false,
264-
};
265-
266185
const bulkActionMenuItems = [
267186
<div key="startAction" className="transform__BulkActionItem">
268187
<EuiButtonEmpty onClick={() => bulkStartAction.openModal(transformSelection)}>
@@ -331,7 +250,7 @@ export const TransformList: FC<Props> = ({
331250
];
332251
};
333252

334-
const renderToolsRight = () => (
253+
const toolsRight = (
335254
<EuiFlexGroup gutterSize="m" justifyContent="spaceAround">
336255
<EuiFlexItem>
337256
<RefreshTransformListButton onClick={refresh} isLoading={isLoading} />
@@ -342,56 +261,6 @@ export const TransformList: FC<Props> = ({
342261
</EuiFlexGroup>
343262
);
344263

345-
const search = {
346-
toolsLeft: transformSelection.length > 0 ? renderToolsLeft() : undefined,
347-
toolsRight: renderToolsRight(),
348-
onChange: onQueryChange,
349-
box: {
350-
incremental: true,
351-
},
352-
filters: [
353-
{
354-
type: 'field_value_selection' as const,
355-
field: 'state.state',
356-
name: i18n.translate('xpack.transform.statusFilter', { defaultMessage: 'Status' }),
357-
multiSelect: 'or' as const,
358-
options: Object.values(TRANSFORM_STATE).map((val) => ({
359-
value: val,
360-
name: val,
361-
view: getTaskStateBadge(val),
362-
})),
363-
},
364-
{
365-
type: 'field_value_selection' as const,
366-
field: 'mode',
367-
name: i18n.translate('xpack.transform.modeFilter', { defaultMessage: 'Mode' }),
368-
multiSelect: false,
369-
options: Object.values(TRANSFORM_MODE).map((val) => ({
370-
value: val,
371-
name: val,
372-
view: (
373-
<EuiBadge className="transform__TaskModeBadge" color="hollow">
374-
{val}
375-
</EuiBadge>
376-
),
377-
})),
378-
},
379-
],
380-
};
381-
382-
const onTableChange = ({
383-
page = { index: 0, size: 10 },
384-
sort = { field: TRANSFORM_LIST_COLUMN.ID as string, direction: 'asc' },
385-
}) => {
386-
const { index, size } = page;
387-
setPageIndex(index);
388-
setPageSize(size);
389-
390-
const { field, direction } = sort;
391-
setSortField(field as string);
392-
setSortDirection(direction as Direction);
393-
};
394-
395264
const selection = {
396265
onSelectionChange: (selected: TransformListRow[]) => setTransformSelection(selected),
397266
};
@@ -404,30 +273,38 @@ export const TransformList: FC<Props> = ({
404273

405274
{/* Single Action Modals */}
406275
{singleActionModals}
407-
408-
<EuiInMemoryTable
409-
allowNeutralSort={false}
410-
className="transform__TransformTable"
276+
<EuiFlexGroup alignItems="center">
277+
{transformSelection.length > 0 ? (
278+
<EuiFlexItem grow={false}>{renderToolsLeft()}</EuiFlexItem>
279+
) : null}
280+
<EuiFlexItem>
281+
<TransformSearchBar
282+
searchQueryText={searchQueryText}
283+
setSearchQueryText={setSearchQueryText}
284+
/>
285+
</EuiFlexItem>
286+
<EuiFlexItem grow={false}>{toolsRight}</EuiFlexItem>
287+
</EuiFlexGroup>
288+
<EuiSpacer size="l" />
289+
<EuiBasicTable<TransformListRow>
411290
columns={columns}
412-
error={searchError}
413291
hasActions={false}
414292
isExpandable={true}
415293
isSelectable={false}
416-
items={filterActive ? filteredTransforms : transforms}
294+
items={pageOfItems as TransformListRow[]}
417295
itemId={TRANSFORM_LIST_COLUMN.ID}
418296
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
419297
loading={isLoading || transformsLoading}
420-
onTableChange={onTableChange}
421-
pagination={pagination}
422-
rowProps={(item) => ({
423-
'data-test-subj': `transformListRow row-${item.id}`,
424-
})}
298+
onChange={onTableChange as EuiBasicTableProps<TransformListRow>['onChange']}
425299
selection={selection}
300+
pagination={pagination!}
426301
sorting={sorting}
427-
search={search}
428302
data-test-subj={`transformListTable ${
429303
isLoading || transformsLoading ? 'loading' : 'loaded'
430304
}`}
305+
rowProps={(item) => ({
306+
'data-test-subj': `transformListRow row-${item.id}`,
307+
})}
431308
/>
432309
</div>
433310
);

0 commit comments

Comments
 (0)