mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Transforms/Data Frame Analytics: Fix data grid height. (#98923)
Adds a short term fix/override for the height of data grid in transform and data frame analytics wizards. EUI itself sets the height style of euiDataGrid__virtualized. For some reason it's not able to adjust that height within the wizards when you toggle histogram visibility or change rows shown per page. This PR fixes it by wrapping the data grid with EuiMutationObserver to listen for updates within the data grid and setting the height of euiDataGrid__virtualized to auto on every update. For now this should be a safe short term fix since we don't use the full screen option.
This commit is contained in:
parent
38a8989a9c
commit
763e08a1dd
3 changed files with 125 additions and 84 deletions
|
@ -6,8 +6,9 @@
|
|||
*/
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import React, { memo, useEffect, FC, useMemo } from 'react';
|
||||
import React, { memo, useEffect, useMemo, useRef, FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
|
@ -18,6 +19,7 @@ import {
|
|||
EuiDataGrid,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiMutationObserver,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
EuiToolTip,
|
||||
|
@ -41,6 +43,7 @@ import {
|
|||
FeatureImportance,
|
||||
TopClasses,
|
||||
} from '../../../../common/types/feature_importance';
|
||||
import { isPopulatedObject } from '../../../../common/util/object_utils';
|
||||
import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics';
|
||||
import { DataFrameAnalysisConfigType } from '../../../../common/types/data_frame_analytics';
|
||||
|
||||
|
@ -190,6 +193,8 @@ export const DataGrid: FC<Props> = memo(
|
|||
}
|
||||
}, [invalidSortingColumnns, toastNotifications]);
|
||||
|
||||
const wrapperEl = useRef<HTMLDivElement>(null);
|
||||
|
||||
if (status === INDEX_STATUS.LOADED && data.length === 0) {
|
||||
return (
|
||||
<div data-test-subj={`${dataTestSubj} empty`}>
|
||||
|
@ -261,8 +266,22 @@ export const DataGrid: FC<Props> = memo(
|
|||
}
|
||||
}
|
||||
|
||||
const onMutation = () => {
|
||||
if (wrapperEl.current !== null) {
|
||||
const els = wrapperEl.current.querySelectorAll('.euiDataGrid__virtualized');
|
||||
for (const el of Array.from(els)) {
|
||||
if (isPopulatedObject(el) && isPopulatedObject(el.style) && el.style.height !== 'auto') {
|
||||
el.style.height = 'auto';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-test-subj={`${dataTestSubj} ${status === INDEX_STATUS.ERROR ? 'error' : 'loaded'}`}>
|
||||
<div
|
||||
data-test-subj={`${dataTestSubj} ${status === INDEX_STATUS.ERROR ? 'error' : 'loaded'}`}
|
||||
ref={wrapperEl}
|
||||
>
|
||||
{isWithHeader(props) && (
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem>
|
||||
|
@ -310,62 +329,73 @@ export const DataGrid: FC<Props> = memo(
|
|||
<EuiSpacer size="m" />
|
||||
</div>
|
||||
)}
|
||||
<div className="mlDataGrid">
|
||||
<EuiDataGrid
|
||||
aria-label={isWithHeader(props) ? props.title : ''}
|
||||
columns={columnsWithChartsActionized.map((c) => {
|
||||
c.initialWidth = 165;
|
||||
return c;
|
||||
})}
|
||||
columnVisibility={{ visibleColumns, setVisibleColumns }}
|
||||
gridStyle={euiDataGridStyle}
|
||||
rowCount={rowCount}
|
||||
renderCellValue={renderCellValue}
|
||||
sorting={{ columns: sortingColumns, onSort }}
|
||||
toolbarVisibility={{
|
||||
...euiDataGridToolbarSettings,
|
||||
...(chartsButtonVisible
|
||||
? {
|
||||
additionalControls: (
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.ml.dataGrid.histogramButtonToolTipContent', {
|
||||
defaultMessage:
|
||||
'Queries run to fetch histogram chart data will use a sample size per shard of {samplerShardSize} documents.',
|
||||
values: {
|
||||
samplerShardSize: DEFAULT_SAMPLER_SHARD_SIZE,
|
||||
},
|
||||
})}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
aria-pressed={chartsVisible === true}
|
||||
className={`euiDataGrid__controlBtn${
|
||||
chartsVisible === true ? ' euiDataGrid__controlBtn--active' : ''
|
||||
}`}
|
||||
data-test-subj={`${dataTestSubj}HistogramButton`}
|
||||
size="xs"
|
||||
iconType="visBarVertical"
|
||||
color="text"
|
||||
onClick={toggleChartVisibility}
|
||||
disabled={chartsVisible === undefined}
|
||||
>
|
||||
{i18n.translate('xpack.ml.dataGrid.histogramButtonText', {
|
||||
defaultMessage: 'Histogram charts',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
}}
|
||||
popoverContents={popOverContent}
|
||||
pagination={{
|
||||
...pagination,
|
||||
pageSizeOptions: [5, 10, 25],
|
||||
onChangeItemsPerPage,
|
||||
onChangePage,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<EuiMutationObserver
|
||||
observerOptions={{ subtree: true, attributes: true, childList: true }}
|
||||
onMutation={onMutation}
|
||||
>
|
||||
{(mutationRef) => (
|
||||
<div className="mlDataGrid" ref={mutationRef}>
|
||||
<EuiDataGrid
|
||||
aria-label={isWithHeader(props) ? props.title : ''}
|
||||
columns={columnsWithChartsActionized.map((c) => {
|
||||
c.initialWidth = 165;
|
||||
return c;
|
||||
})}
|
||||
columnVisibility={{ visibleColumns, setVisibleColumns }}
|
||||
gridStyle={euiDataGridStyle}
|
||||
rowCount={rowCount}
|
||||
renderCellValue={renderCellValue}
|
||||
sorting={{ columns: sortingColumns, onSort }}
|
||||
toolbarVisibility={{
|
||||
...euiDataGridToolbarSettings,
|
||||
...(chartsButtonVisible
|
||||
? {
|
||||
additionalControls: (
|
||||
<EuiToolTip
|
||||
content={i18n.translate(
|
||||
'xpack.ml.dataGrid.histogramButtonToolTipContent',
|
||||
{
|
||||
defaultMessage:
|
||||
'Queries run to fetch histogram chart data will use a sample size per shard of {samplerShardSize} documents.',
|
||||
values: {
|
||||
samplerShardSize: DEFAULT_SAMPLER_SHARD_SIZE,
|
||||
},
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
aria-pressed={chartsVisible === true}
|
||||
className={`euiDataGrid__controlBtn${
|
||||
chartsVisible === true ? ' euiDataGrid__controlBtn--active' : ''
|
||||
}`}
|
||||
data-test-subj={`${dataTestSubj}HistogramButton`}
|
||||
size="xs"
|
||||
iconType="visBarVertical"
|
||||
color="text"
|
||||
onClick={toggleChartVisibility}
|
||||
disabled={chartsVisible === undefined}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataGrid.histogramButtonText"
|
||||
defaultMessage="Histogram charts"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
}}
|
||||
popoverContents={popOverContent}
|
||||
pagination={{
|
||||
...pagination,
|
||||
pageSizeOptions: [5, 10, 25],
|
||||
onChangeItemsPerPage,
|
||||
onChangePage,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</EuiMutationObserver>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import { CoreSetup } from 'src/core/public';
|
||||
|
@ -48,7 +49,9 @@ describe('Transform: useIndexData()', () => {
|
|||
test('indexPattern set triggers loading', async () => {
|
||||
const mlShared = await getMlSharedImports();
|
||||
const wrapper: FC = ({ children }) => (
|
||||
<MlSharedContext.Provider value={mlShared}>{children}</MlSharedContext.Provider>
|
||||
<IntlProvider locale="en">
|
||||
<MlSharedContext.Provider value={mlShared}>{children}</MlSharedContext.Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
|
@ -101,17 +104,21 @@ describe('Transform: <DataGrid /> with useIndexData()', () => {
|
|||
return <DataGrid {...props} />;
|
||||
};
|
||||
|
||||
const { queryByText } = render(
|
||||
<MlSharedContext.Provider value={mlSharedImports}>
|
||||
<Wrapper />
|
||||
</MlSharedContext.Provider>
|
||||
render(
|
||||
<IntlProvider locale="en">
|
||||
<MlSharedContext.Provider value={mlSharedImports}>
|
||||
<Wrapper />
|
||||
</MlSharedContext.Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(queryByText('the-index-preview-title')).toBeInTheDocument();
|
||||
expect(queryByText('Cross-cluster search returned no fields data.')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('the-index-preview-title')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('Cross-cluster search returned no fields data.')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -140,17 +147,21 @@ describe('Transform: <DataGrid /> with useIndexData()', () => {
|
|||
return <DataGrid {...props} />;
|
||||
};
|
||||
|
||||
const { queryByText } = render(
|
||||
<MlSharedContext.Provider value={mlSharedImports}>
|
||||
<Wrapper />
|
||||
</MlSharedContext.Provider>
|
||||
render(
|
||||
<IntlProvider locale="en">
|
||||
<MlSharedContext.Provider value={mlSharedImports}>
|
||||
<Wrapper />
|
||||
</MlSharedContext.Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(queryByText('the-index-preview-title')).toBeInTheDocument();
|
||||
expect(queryByText('Cross-cluster search returned no fields data.')).toBeInTheDocument();
|
||||
expect(screen.queryByText('the-index-preview-title')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('Cross-cluster search returned no fields data.')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { render, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import moment from 'moment-timezone';
|
||||
import { TransformListRow } from '../../../../common';
|
||||
|
@ -35,31 +35,31 @@ describe('Transform: Transform List <ExpandedRow />', () => {
|
|||
// @ts-expect-error mock data is too loosely typed
|
||||
const item: TransformListRow = transformListRow;
|
||||
|
||||
const { getByText, getByTestId } = render(
|
||||
render(
|
||||
<MlSharedContext.Provider value={mlShared}>
|
||||
<ExpandedRow item={item} />
|
||||
</MlSharedContext.Provider>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('Details')).toBeInTheDocument();
|
||||
expect(getByText('Stats')).toBeInTheDocument();
|
||||
expect(getByText('JSON')).toBeInTheDocument();
|
||||
expect(getByText('Messages')).toBeInTheDocument();
|
||||
expect(getByText('Preview')).toBeInTheDocument();
|
||||
expect(screen.getByText('Details')).toBeInTheDocument();
|
||||
expect(screen.getByText('Stats')).toBeInTheDocument();
|
||||
expect(screen.getByText('JSON')).toBeInTheDocument();
|
||||
expect(screen.getByText('Messages')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preview')).toBeInTheDocument();
|
||||
|
||||
const tabContent = getByTestId('transformDetailsTabContent');
|
||||
const tabContent = screen.getByTestId('transformDetailsTabContent');
|
||||
expect(tabContent).toBeInTheDocument();
|
||||
|
||||
expect(getByTestId('transformDetailsTab')).toHaveAttribute('aria-selected', 'true');
|
||||
expect(screen.getByTestId('transformDetailsTab')).toHaveAttribute('aria-selected', 'true');
|
||||
expect(within(tabContent).getByText('General')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(getByTestId('transformStatsTab'));
|
||||
fireEvent.click(screen.getByTestId('transformStatsTab'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByTestId('transformStatsTab')).toHaveAttribute('aria-selected', 'true');
|
||||
const tabContent = getByTestId('transformDetailsTabContent');
|
||||
expect(screen.getByTestId('transformStatsTab')).toHaveAttribute('aria-selected', 'true');
|
||||
const tabContent = screen.getByTestId('transformDetailsTabContent');
|
||||
expect(within(tabContent).getByText('Stats')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue