Skip to content

Commit

Permalink
[ML] Transforms: Single Column Wizard. (#64436)
Browse files Browse the repository at this point in the history
Rearranges the layout of the transform wizard pivot configuration step into a single-column. This allows us to have the data grids for source index and pivot preview having the full width. The advanced editors for source query and pivot configuration also cover a wider width.
  • Loading branch information
walterra authored May 5, 2020
1 parent 6349575 commit 4f66dfd
Show file tree
Hide file tree
Showing 45 changed files with 1,823 additions and 1,197 deletions.
269 changes: 146 additions & 123 deletions x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useEffect, FC } from 'react';
import { isEqual } from 'lodash';
import React, { memo, useEffect, FC } from 'react';

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

Expand Down Expand Up @@ -50,132 +51,154 @@ function isWithHeader(arg: any): arg is PropsWithHeader {

type Props = PropsWithHeader | PropsWithoutHeader;

export const DataGrid: FC<Props> = props => {
const {
columns,
dataTestSubj,
errorMessage,
invalidSortingColumnns,
noDataMessage,
onChangeItemsPerPage,
onChangePage,
onSort,
pagination,
setVisibleColumns,
renderCellValue,
rowCount,
sortingColumns,
status,
tableItems: data,
toastNotifications,
visibleColumns,
} = props;

useEffect(() => {
if (invalidSortingColumnns.length > 0) {
invalidSortingColumnns.forEach(columnId => {
toastNotifications.addDanger(
i18n.translate('xpack.ml.dataGrid.invalidSortingColumnError', {
defaultMessage: `The column '{columnId}' cannot be used for sorting.`,
values: { columnId },
})
);
});
}
}, [invalidSortingColumnns, toastNotifications]);

if (status === INDEX_STATUS.LOADED && data.length === 0) {
return (
<div data-test-subj={`${dataTestSubj} empty`}>
{isWithHeader(props) && <DataGridTitle title={props.title} />}
<EuiCallOut
title={i18n.translate('xpack.ml.dataGrid.IndexNoDataCalloutTitle', {
defaultMessage: 'Empty index query result.',
})}
color="primary"
>
<p>
{i18n.translate('xpack.ml.dataGrid.IndexNoDataCalloutBody', {
defaultMessage:
'The query for the index returned no results. Please make sure you have sufficient permissions, the index contains documents and your query is not too restrictive.',
export const DataGrid: FC<Props> = memo(
props => {
const {
columns,
dataTestSubj,
errorMessage,
invalidSortingColumnns,
noDataMessage,
onChangeItemsPerPage,
onChangePage,
onSort,
pagination,
setVisibleColumns,
renderCellValue,
rowCount,
sortingColumns,
status,
tableItems: data,
toastNotifications,
visibleColumns,
} = props;

useEffect(() => {
if (invalidSortingColumnns.length > 0) {
invalidSortingColumnns.forEach(columnId => {
toastNotifications.addDanger(
i18n.translate('xpack.ml.dataGrid.invalidSortingColumnError', {
defaultMessage: `The column '{columnId}' cannot be used for sorting.`,
values: { columnId },
})
);
});
}
}, [invalidSortingColumnns, toastNotifications]);

if (status === INDEX_STATUS.LOADED && data.length === 0) {
return (
<div data-test-subj={`${dataTestSubj} empty`}>
{isWithHeader(props) && <DataGridTitle title={props.title} />}
<EuiCallOut
title={i18n.translate('xpack.ml.dataGrid.IndexNoDataCalloutTitle', {
defaultMessage: 'Empty index query result.',
})}
</p>
</EuiCallOut>
</div>
);
}
color="primary"
>
<p>
{i18n.translate('xpack.ml.dataGrid.IndexNoDataCalloutBody', {
defaultMessage:
'The query for the index returned no results. Please make sure you have sufficient permissions, the index contains documents and your query is not too restrictive.',
})}
</p>
</EuiCallOut>
</div>
);
}

if (noDataMessage !== '') {
return (
<div data-test-subj={`${dataTestSubj} empty`}>
{isWithHeader(props) && <DataGridTitle title={props.title} />}
<EuiCallOut
title={i18n.translate('xpack.ml.dataGrid.dataGridNoDataCalloutTitle', {
defaultMessage: 'Index preview not available',
})}
color="primary"
>
<p>{noDataMessage}</p>
</EuiCallOut>
</div>
);
}

return (
<div data-test-subj={`${dataTestSubj} ${status === INDEX_STATUS.ERROR ? 'error' : 'loaded'}`}>
{isWithHeader(props) && (
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem>
<DataGridTitle title={props.title} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiCopy
beforeMessage={props.copyToClipboardDescription}
textToCopy={props.copyToClipboard}
>
{(copy: () => void) => (
<EuiButtonIcon
onClick={copy}
iconType="copyClipboard"
aria-label={props.copyToClipboardDescription}
/>
)}
</EuiCopy>
</EuiFlexItem>
</EuiFlexGroup>
)}
{status === INDEX_STATUS.ERROR && (
<div data-test-subj={`${dataTestSubj} error`}>
if (noDataMessage !== '') {
return (
<div data-test-subj={`${dataTestSubj} empty`}>
{isWithHeader(props) && <DataGridTitle title={props.title} />}
<EuiCallOut
title={i18n.translate('xpack.ml.dataGrid.indexDataError', {
defaultMessage: 'An error occurred loading the index data.',
title={i18n.translate('xpack.ml.dataGrid.dataGridNoDataCalloutTitle', {
defaultMessage: 'Index preview not available',
})}
color="danger"
iconType="cross"
color="primary"
>
<EuiCodeBlock language="json" fontSize="s" paddingSize="s" isCopyable>
{errorMessage}
</EuiCodeBlock>
<p>{noDataMessage}</p>
</EuiCallOut>
<EuiSpacer size="m" />
</div>
)}
<EuiDataGrid
aria-label={isWithHeader(props) ? props.title : ''}
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
gridStyle={euiDataGridStyle}
rowCount={rowCount}
renderCellValue={renderCellValue}
sorting={{ columns: sortingColumns, onSort }}
toolbarVisibility={euiDataGridToolbarSettings}
pagination={{
...pagination,
pageSizeOptions: [5, 10, 25],
onChangeItemsPerPage,
onChangePage,
}}
/>
</div>
);
};
);
}

return (
<div data-test-subj={`${dataTestSubj} ${status === INDEX_STATUS.ERROR ? 'error' : 'loaded'}`}>
{isWithHeader(props) && (
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem>
<DataGridTitle title={props.title} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiCopy
beforeMessage={props.copyToClipboardDescription}
textToCopy={props.copyToClipboard}
>
{(copy: () => void) => (
<EuiButtonIcon
onClick={copy}
iconType="copyClipboard"
aria-label={props.copyToClipboardDescription}
/>
)}
</EuiCopy>
</EuiFlexItem>
</EuiFlexGroup>
)}
{status === INDEX_STATUS.ERROR && (
<div data-test-subj={`${dataTestSubj} error`}>
<EuiCallOut
title={i18n.translate('xpack.ml.dataGrid.indexDataError', {
defaultMessage: 'An error occurred loading the index data.',
})}
color="danger"
iconType="cross"
>
<EuiCodeBlock language="json" fontSize="s" paddingSize="s" isCopyable>
{errorMessage}
</EuiCodeBlock>
</EuiCallOut>
<EuiSpacer size="m" />
</div>
)}
<EuiDataGrid
aria-label={isWithHeader(props) ? props.title : ''}
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
gridStyle={euiDataGridStyle}
rowCount={rowCount}
renderCellValue={renderCellValue}
sorting={{ columns: sortingColumns, onSort }}
toolbarVisibility={euiDataGridToolbarSettings}
pagination={{
...pagination,
pageSizeOptions: [5, 10, 25],
onChangeItemsPerPage,
onChangePage,
}}
/>
</div>
);
},
(prevProps, nextProps) => isEqual(pickProps(prevProps), pickProps(nextProps))
);

function pickProps(props: Props) {
return [
props.columns,
props.dataTestSubj,
props.errorMessage,
props.invalidSortingColumnns,
props.noDataMessage,
props.pagination,
props.rowCount,
props.sortingColumns,
props.status,
props.tableItems,
props.visibleColumns,
...(isWithHeader(props)
? [props.copyToClipboard, props.copyToClipboardDescription, props.title]
: []),
];
}
2 changes: 1 addition & 1 deletion x-pack/plugins/transform/public/app/common/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { PivotGroupByConfig } from '../common';

import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form';
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';

import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/transform/public/app/common/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { DefaultOperator } from 'elasticsearch';
import { dictionaryToArray } from '../../../common/types/common';
import { SavedSearchQuery } from '../hooks/use_search_items';

import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form';
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';

import { IndexPattern } from '../../../../../../src/plugins/data/public';
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ export const usePivotData = (
tableItems,
} = dataGrid;

const previewRequest = getPreviewRequestBody(indexPatternTitle, query, groupByArr, aggsArr);

const getPreviewData = async () => {
if (aggsArr.length === 0 || groupByArr.length === 0) {
setTableItems([]);
Expand All @@ -142,6 +140,7 @@ export const usePivotData = (
setStatus(INDEX_STATUS.LOADING);

try {
const previewRequest = getPreviewRequestBody(indexPatternTitle, query, groupByArr, aggsArr);
const resp = await api.getTransformsPreview(previewRequest);
setTableItems(resp.preview);
setRowCount(resp.preview.length);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.
*/

import { isEqual } from 'lodash';
import React, { memo, FC } from 'react';

import { EuiCodeEditor, EuiFormRow } from '@elastic/eui';

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

import { StepDefineFormHook } from '../step_define';

export const AdvancedPivotEditor: FC<StepDefineFormHook['advancedPivotEditor']> = memo(
({
actions: { convertToJson, setAdvancedEditorConfig, setAdvancedPivotEditorApplyButtonEnabled },
state: { advancedEditorConfigLastApplied, advancedEditorConfig, xJsonMode },
}) => {
return (
<EuiFormRow
fullWidth
label={i18n.translate('xpack.transform.stepDefineForm.advancedEditorLabel', {
defaultMessage: 'Pivot configuration object',
})}
>
<EuiCodeEditor
data-test-subj="transformAdvancedPivotEditor"
style={{ border: '1px solid #e3e6ef' }}
height="250px"
width="100%"
mode={xJsonMode}
value={advancedEditorConfig}
onChange={(d: string) => {
setAdvancedEditorConfig(d);

// Disable the "Apply"-Button if the config hasn't changed.
if (advancedEditorConfigLastApplied === d) {
setAdvancedPivotEditorApplyButtonEnabled(false);
return;
}

// Try to parse the string passed on from the editor.
// If parsing fails, the "Apply"-Button will be disabled
try {
JSON.parse(convertToJson(d));
setAdvancedPivotEditorApplyButtonEnabled(true);
} catch (e) {
setAdvancedPivotEditorApplyButtonEnabled(false);
}
}}
setOptions={{
fontSize: '12px',
}}
theme="textmate"
aria-label={i18n.translate('xpack.transform.stepDefineForm.advancedEditorAriaLabel', {
defaultMessage: 'Advanced pivot editor',
})}
/>
</EuiFormRow>
);
},
(prevProps, nextProps) => isEqual(pickProps(prevProps), pickProps(nextProps))
);

function pickProps(props: StepDefineFormHook['advancedPivotEditor']) {
return [props.state.advancedEditorConfigLastApplied, props.state.advancedEditorConfig];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 { AdvancedPivotEditor } from './advanced_pivot_editor';
Loading

0 comments on commit 4f66dfd

Please sign in to comment.