[ML] Transforms: Use EuiInMemoryTable instead of custom typed table. (#59782)

Before EuiInMemoryTable had TypeScript support we used our own typings and some state & CSS fixes. This is now all solved by the original EUI component.
- Use EuiInMemoryTable instead of custom typed table.
- Deletes some legacy leftover files.
This commit is contained in:
Walter Rafelsberger 2020-03-11 09:37:03 +01:00 committed by GitHub
parent fa16b2b849
commit ce6029b9b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 56 additions and 259 deletions

1
.github/CODEOWNERS vendored
View file

@ -88,7 +88,6 @@
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
# ML team owns the transform plugin, ES team added here for visibility
# because the plugin lives in Kibana's Elasticsearch management section.
/x-pack/legacy/plugins/transform/ @elastic/ml-ui @elastic/es-ui
/x-pack/plugins/transform/ @elastic/ml-ui @elastic/es-ui
/x-pack/test/functional/apps/transform/ @elastic/ml-ui
/x-pack/test/functional/services/transform_ui/ @elastic/ml-ui

View file

@ -31,7 +31,6 @@ import { crossClusterReplication } from './legacy/plugins/cross_cluster_replicat
import { upgradeAssistant } from './legacy/plugins/upgrade_assistant';
import { uptime } from './legacy/plugins/uptime';
import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects';
import { transform } from './legacy/plugins/transform';
import { actions } from './legacy/plugins/actions';
import { alerting } from './legacy/plugins/alerting';
import { lens } from './legacy/plugins/lens';
@ -61,7 +60,6 @@ module.exports = function(kibana) {
infra(kibana),
taskManager(kibana),
rollup(kibana),
transform(kibana),
siem(kibana),
remoteClusters(kibana),
crossClusterReplication(kibana),

View file

@ -1,12 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export function transform(kibana: any) {
return new kibana.Plugin({
id: 'transform',
configPrefix: 'xpack.transform',
});
}

View file

@ -15,4 +15,3 @@
@import 'sections/create_transform/components/wizard/index';
@import 'sections/transform_management/components/create_transform_button/index';
@import 'sections/transform_management/components/stats_bar/index';
@import 'sections/transform_management/components/transform_list/index';

View file

@ -1,48 +0,0 @@
.transform__TransformTable {
// Using an override as a last resort because we cannot set custom classes on
// nested upstream components. The opening animation limits the height
// of the expanded row to 1000px which turned out to be not predictable.
// The animation could also result in flickering with expanded rows
// where the inner content would result in the DOM changing the height.
.euiTableRow-isExpandedRow .euiTableCellContent {
animation: none !important;
.euiTableCellContent__text {
width: 100%;
}
}
// Another override: Because an update to the table replaces the DOM, the same
// icon would still again fade in with an animation. If the table refreshes with
// e.g. 1s this would result in a blinking icon effect.
.euiIcon-isLoaded {
animation: none !important;
}
}
.transform__BulkActionItem {
display: block;
padding: $euiSizeS;
width: 100%;
text-align: left;
}
.transform__BulkActionsBorder {
height: 20px;
border-right: $euiBorderThin;
width: 1px;
display: inline-block;
vertical-align: middle;
height: 35px;
margin: 0px 5px;
margin-top: -5px;
}
.transform__ProgressBar {
margin-bottom: $euiSizeM;
}
.transform__TaskStateBadge, .transform__TaskModeBadge {
max-width: 100px;
}
.transform__TransformTable__messagesPaneTable .euiTableCellContent__text {
text-align: left;
}

View file

@ -9,6 +9,9 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiBadge,
EuiTableActionsColumnType,
EuiTableComputedColumnType,
EuiTableFieldDataColumnType,
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
@ -21,13 +24,6 @@ import {
import { TransformId, TRANSFORM_STATE } from '../../../../../../common';
import {
ActionsColumnType,
ComputedColumnType,
ExpanderColumnType,
FieldDataColumnType,
} from '../../../../../shared_imports';
import {
getTransformProgress,
TransformListRow,
@ -89,15 +85,15 @@ export const getColumns = (
}
const columns: [
ExpanderColumnType<TransformListRow>,
FieldDataColumnType<TransformListRow>,
FieldDataColumnType<TransformListRow>,
FieldDataColumnType<TransformListRow>,
FieldDataColumnType<TransformListRow>,
ComputedColumnType<TransformListRow>,
ComputedColumnType<TransformListRow>,
ComputedColumnType<TransformListRow>,
ActionsColumnType<TransformListRow>
EuiTableComputedColumnType<TransformListRow>,
EuiTableFieldDataColumnType<TransformListRow>,
EuiTableFieldDataColumnType<TransformListRow>,
EuiTableFieldDataColumnType<TransformListRow>,
EuiTableFieldDataColumnType<TransformListRow>,
EuiTableComputedColumnType<TransformListRow>,
EuiTableComputedColumnType<TransformListRow>,
EuiTableComputedColumnType<TransformListRow>,
EuiTableActionsColumnType<TransformListRow>
] = [
{
name: (

View file

@ -4,11 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment, MouseEventHandler, FC, useContext, useState } from 'react';
import React, { MouseEventHandler, FC, useContext, useState } from 'react';
import { i18n } from '@kbn/i18n';
import {
Direction,
EuiBadge,
EuiButtonEmpty,
EuiButtonIcon,
@ -16,15 +17,13 @@ import {
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiInMemoryTable,
EuiPopover,
EuiTitle,
Direction,
} from '@elastic/eui';
import { TransformId, TRANSFORM_STATE } from '../../../../../../common';
import { OnTableChangeArg, SortDirection, SORT_DIRECTION } from '../../../../../shared_imports';
import {
useRefreshTransformList,
TransformListRow,
@ -43,7 +42,6 @@ import { StopAction } from './action_stop';
import { ItemIdToExpandedRowMap, Query, Clause } from './common';
import { getColumns } from './columns';
import { ExpandedRow } from './expanded_row';
import { ProgressBar, transformTableFactory } from './transform_table';
function getItemIdToExpandedRowMap(
itemIds: TransformId[],
@ -74,8 +72,6 @@ interface Props {
transformsLoading: boolean;
}
const TransformTable = transformTableFactory<TransformListRow>();
export const TransformList: FC<Props> = ({
errorMessage,
isInitialized,
@ -100,7 +96,7 @@ export const TransformList: FC<Props> = ({
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<string>(TRANSFORM_LIST_COLUMN.ID);
const [sortDirection, setSortDirection] = useState<SortDirection | Direction>(SORT_DIRECTION.ASC);
const [sortDirection, setSortDirection] = useState<Direction>('asc');
const { capabilities } = useContext(AuthorizationContext);
const disabled =
@ -186,52 +182,46 @@ export const TransformList: FC<Props> = ({
// Before the transforms have been loaded for the first time, display the loading indicator only.
// Otherwise a user would see 'No transforms found' during the initial loading.
if (!isInitialized) {
return <ProgressBar isLoading={isLoading || transformsLoading} />;
return null;
}
if (typeof errorMessage !== 'undefined') {
return (
<Fragment>
<ProgressBar isLoading={isLoading || transformsLoading} />
<EuiCallOut
title={i18n.translate('xpack.transform.list.errorPromptTitle', {
defaultMessage: 'An error occurred getting the transform list.',
})}
color="danger"
iconType="alert"
>
<pre>{JSON.stringify(errorMessage)}</pre>
</EuiCallOut>
</Fragment>
<EuiCallOut
title={i18n.translate('xpack.transform.list.errorPromptTitle', {
defaultMessage: 'An error occurred getting the transform list.',
})}
color="danger"
iconType="alert"
>
<pre>{JSON.stringify(errorMessage)}</pre>
</EuiCallOut>
);
}
if (transforms.length === 0) {
return (
<Fragment>
<ProgressBar isLoading={isLoading || transformsLoading} />
<EuiEmptyPrompt
title={
<h2>
{i18n.translate('xpack.transform.list.emptyPromptTitle', {
defaultMessage: 'No transforms found',
})}
</h2>
}
actions={[
<EuiButtonEmpty
onClick={onCreateTransform}
isDisabled={disabled}
data-test-subj="transformCreateFirstButton"
>
{i18n.translate('xpack.transform.list.emptyPromptButtonText', {
defaultMessage: 'Create your first transform',
})}
</EuiButtonEmpty>,
]}
data-test-subj="transformNoTransformsFound"
/>
</Fragment>
<EuiEmptyPrompt
title={
<h2>
{i18n.translate('xpack.transform.list.emptyPromptTitle', {
defaultMessage: 'No transforms found',
})}
</h2>
}
actions={[
<EuiButtonEmpty
onClick={onCreateTransform}
isDisabled={disabled}
data-test-subj="transformCreateFirstButton"
>
{i18n.translate('xpack.transform.list.emptyPromptButtonText', {
defaultMessage: 'Create your first transform',
})}
</EuiButtonEmpty>,
]}
data-test-subj="transformNoTransformsFound"
/>
);
}
@ -362,15 +352,15 @@ export const TransformList: FC<Props> = ({
const onTableChange = ({
page = { index: 0, size: 10 },
sort = { field: TRANSFORM_LIST_COLUMN.ID, direction: SORT_DIRECTION.ASC },
}: OnTableChangeArg) => {
sort = { field: TRANSFORM_LIST_COLUMN.ID as string, direction: 'asc' },
}) => {
const { index, size } = page;
setPageIndex(index);
setPageSize(size);
const { field, direction } = sort;
setSortField(field);
setSortDirection(direction);
setSortField(field as string);
setSortDirection(direction as Direction);
};
const selection = {
@ -379,8 +369,7 @@ export const TransformList: FC<Props> = ({
return (
<div data-test-subj="transformListTableContainer">
<ProgressBar isLoading={isLoading || transformsLoading} />
<TransformTable
<EuiInMemoryTable
allowNeutralSort={false}
className="transform__TransformTable"
columns={columns}
@ -391,6 +380,7 @@ export const TransformList: FC<Props> = ({
items={filterActive ? filteredTransforms : transforms}
itemId={TRANSFORM_LIST_COLUMN.ID}
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
loading={isLoading || transformsLoading}
onTableChange={onTableChange}
pagination={pagination}
rowProps={item => ({
@ -399,11 +389,9 @@ export const TransformList: FC<Props> = ({
selection={selection}
sorting={sorting}
search={search}
data-test-subj={
isLoading || transformsLoading
? 'transformListTable loading'
: 'transformListTable loaded'
}
data-test-subj={`transformListTable ${
isLoading || transformsLoading ? 'loading' : 'loaded'
}`}
/>
</div>
);

View file

@ -1,107 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// This component extends EuiInMemoryTable with some
// fixes and TS specs until the changes become available upstream.
import React, { Fragment } from 'react';
import { EuiProgress } from '@elastic/eui';
import { mlInMemoryTableBasicFactory } from '../../../../../shared_imports';
// The built in loading progress bar of EuiInMemoryTable causes a full DOM replacement
// of the table and doesn't play well with auto-refreshing. That's why we're displaying
// our own progress bar on top of the table. `EuiProgress` after `isLoading` displays
// the loading indicator. The variation after `!isLoading` displays an empty progress
// bar fixed to 0%. Without it, the display would vertically jump when showing/hiding
// the progress bar.
export const ProgressBar = ({ isLoading = false }) => {
return (
<Fragment>
{isLoading && <EuiProgress className="transform__ProgressBar" size="xs" color="primary" />}
{!isLoading && (
<EuiProgress className="transform__ProgressBar" value={0} max={100} size="xs" />
)}
</Fragment>
);
};
// copied from EUI to be available to the extended getDerivedStateFromProps()
function findColumnByProp(columns: any, prop: any, value: any) {
for (let i = 0; i < columns.length; i++) {
const column = columns[i];
if (column[prop] === value) {
return column;
}
}
}
// copied from EUI to be available to the extended getDerivedStateFromProps()
const getInitialSorting = (columns: any, sorting: any) => {
if (!sorting || !sorting.sort) {
return {
sortName: undefined,
sortDirection: undefined,
};
}
const { field: sortable, direction: sortDirection } = sorting.sort;
// sortable could be a column's `field` or its `name`
// for backwards compatibility `field` must be checked first
let sortColumn = findColumnByProp(columns, 'field', sortable);
if (sortColumn == null) {
sortColumn = findColumnByProp(columns, 'name', sortable);
}
if (sortColumn == null) {
return {
sortName: undefined,
sortDirection: undefined,
};
}
const sortName = sortColumn.name;
return {
sortName,
sortDirection,
};
};
export function transformTableFactory<T>() {
const MlInMemoryTableBasic = mlInMemoryTableBasicFactory<T>();
return class TransformTable extends MlInMemoryTableBasic {
static getDerivedStateFromProps(nextProps: any, prevState: any) {
const derivedState = {
...prevState.prevProps,
pageIndex: nextProps.pagination.initialPageIndex,
pageSize: nextProps.pagination.initialPageSize,
};
if (nextProps.items !== prevState.prevProps.items) {
Object.assign(derivedState, {
prevProps: {
items: nextProps.items,
},
});
}
const { sortName, sortDirection } = getInitialSorting(nextProps.columns, nextProps.sorting);
if (
sortName !== prevState.prevProps.sortName ||
sortDirection !== prevState.prevProps.sortDirection
) {
Object.assign(derivedState, {
sortName,
sortDirection,
});
}
return derivedState;
}
};
}

View file

@ -24,21 +24,6 @@ export {
DAY,
} from '../../../../src/plugins/es_ui_shared/public/components/cron_editor';
// Custom version of EuiInMemoryTable with TypeScript
// support and a fix for updating sorting props.
export {
ActionsColumnType,
ComputedColumnType,
ExpanderColumnType,
FieldDataColumnType,
ColumnType,
mlInMemoryTableBasicFactory,
OnTableChangeArg,
SortingPropType,
SortDirection,
SORT_DIRECTION,
} from '../../../legacy/plugins/ml/public/application/components/ml_in_memory_table';
// Needs to be imported because we're reusing KqlFilterBar which depends on it.
export { setDependencyCache } from '../../../legacy/plugins/ml/public/application/util/dependency_cache';