[ML] Show mini histograms by default if row count below threshold. (#92021)

Show mini histograms by default if row count below threshold of 10000 docs.
This commit is contained in:
Walter Rafelsberger 2021-02-23 17:34:59 +01:00 committed by GitHub
parent b337d49bcb
commit 45155f089d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 135 additions and 42 deletions

View file

@ -5,7 +5,7 @@
* 2.0.
*/
export { SearchResponse7 } from './types/es_client';
export { HitsTotalRelation, SearchResponse7, HITS_TOTAL_RELATION } from './types/es_client';
export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from './constants/anomalies';
export { getSeverityColor, getSeverityType } from './util/anomaly_utils';
export { composeValidators, patternValidator } from './util/validators';

View file

@ -7,13 +7,19 @@
import { SearchResponse, ShardsResponse } from 'elasticsearch';
export const HITS_TOTAL_RELATION = {
EQ: 'eq',
GTE: 'gte',
} as const;
export type HitsTotalRelation = typeof HITS_TOTAL_RELATION[keyof typeof HITS_TOTAL_RELATION];
// The types specified in `@types/elasticsearch` are out of date and still have `total: number`.
interface SearchResponse7Hits<T> {
hits: SearchResponse<T>['hits']['hits'];
max_score: number;
total: {
value: number;
relation: string;
relation: HitsTotalRelation;
};
}
export interface SearchResponse7<T = any> {

View file

@ -53,6 +53,7 @@ import { RuntimeMappings } from '../../../../common/types/fields';
import { isPopulatedObject } from '../../../../common/util/object_utils';
export const INIT_MAX_COLUMNS = 10;
export const COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED = 10000;
export const euiDataGridStyle: EuiDataGridStyle = {
border: 'all',

View file

@ -312,15 +312,16 @@ export const DataGrid: FC<Props> = memo(
})}
>
<EuiButtonEmpty
aria-pressed={chartsVisible}
aria-pressed={chartsVisible === true}
className={`euiDataGrid__controlBtn${
chartsVisible ? ' euiDataGrid__controlBtn--active' : ''
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',

View file

@ -23,6 +23,7 @@ export {
DataGridItem,
EsSorting,
RenderCellValue,
RowCountRelation,
UseDataGridReturnType,
UseIndexDataReturnType,
} from './types';

View file

@ -10,6 +10,7 @@ import { Dispatch, SetStateAction } from 'react';
import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';
import { Dictionary } from '../../../../common/types/common';
import { HitsTotalRelation } from '../../../../common/types/es_client';
import { INDEX_STATUS } from '../../data_frame_analytics/common/analytics';
@ -19,6 +20,10 @@ import { FeatureImportanceBaseline } from '../../../../common/types/feature_impo
export type ColumnId = string;
export type DataGridItem = Record<string, any>;
// `undefined` is used to indicate a non-initialized state.
export type ChartsVisible = boolean | undefined;
export type RowCountRelation = HitsTotalRelation | undefined;
export type IndexPagination = Pick<EuiDataGridPaginationProps, 'pageIndex' | 'pageSize'>;
export type OnChangeItemsPerPage = (pageSize: any) => void;
@ -60,6 +65,7 @@ export interface UseIndexDataReturnType
| 'setPagination'
| 'setVisibleColumns'
| 'rowCount'
| 'rowCountRelation'
| 'sortingColumns'
| 'status'
| 'tableItems'
@ -73,7 +79,7 @@ export interface UseIndexDataReturnType
}
export interface UseDataGridReturnType {
chartsVisible: boolean;
chartsVisible: ChartsVisible;
chartsButtonVisible: boolean;
columnsWithCharts: EuiDataGridColumn[];
errorMessage: string;
@ -85,11 +91,13 @@ export interface UseDataGridReturnType {
pagination: IndexPagination;
resetPagination: () => void;
rowCount: number;
rowCountRelation: RowCountRelation;
setColumnCharts: Dispatch<SetStateAction<ChartData[]>>;
setErrorMessage: Dispatch<SetStateAction<string>>;
setNoDataMessage: Dispatch<SetStateAction<string>>;
setPagination: Dispatch<SetStateAction<IndexPagination>>;
setRowCount: Dispatch<SetStateAction<number>>;
setRowCountRelation: Dispatch<SetStateAction<RowCountRelation>>;
setSortingColumns: Dispatch<SetStateAction<EuiDataGridSorting['columns']>>;
setStatus: Dispatch<SetStateAction<INDEX_STATUS>>;
setTableItems: Dispatch<SetStateAction<DataGridItem[]>>;

View file

@ -9,17 +9,21 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';
import { HITS_TOTAL_RELATION } from '../../../../common/types/es_client';
import { INDEX_STATUS } from '../../data_frame_analytics/common';
import { ColumnChart } from './column_chart';
import { INIT_MAX_COLUMNS } from './common';
import { COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED, INIT_MAX_COLUMNS } from './common';
import {
ChartsVisible,
ColumnId,
DataGridItem,
IndexPagination,
OnChangeItemsPerPage,
OnChangePage,
OnSort,
RowCountRelation,
UseDataGridReturnType,
} from './types';
import { ChartData } from './use_column_chart';
@ -36,14 +40,17 @@ export const useDataGrid = (
const [errorMessage, setErrorMessage] = useState('');
const [status, setStatus] = useState(INDEX_STATUS.UNUSED);
const [rowCount, setRowCount] = useState(0);
const [rowCountRelation, setRowCountRelation] = useState<RowCountRelation>(undefined);
const [columnCharts, setColumnCharts] = useState<ChartData[]>([]);
const [tableItems, setTableItems] = useState<DataGridItem[]>([]);
const [pagination, setPagination] = useState(defaultPagination);
const [sortingColumns, setSortingColumns] = useState<EuiDataGridSorting['columns']>([]);
const [chartsVisible, setChartsVisible] = useState(false);
const [chartsVisible, setChartsVisible] = useState<ChartsVisible>(undefined);
const toggleChartVisibility = () => {
setChartsVisible(!chartsVisible);
if (chartsVisible !== undefined) {
setChartsVisible(!chartsVisible);
}
};
const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback((pageSize) => {
@ -131,6 +138,19 @@ export const useDataGrid = (
});
}, [columns, columnCharts, chartsVisible, JSON.stringify(visibleColumns)]);
// Initialize the mini histogram charts toggle button.
// On load `chartsVisible` is set to `undefined`, the button will be disabled.
// Once we know how many rows have been returned,
// we decide whether to show or hide the charts by default.
useEffect(() => {
if (chartsVisible === undefined && rowCount > 0 && rowCountRelation !== undefined) {
setChartsVisible(
rowCount <= COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED &&
rowCountRelation !== HITS_TOTAL_RELATION.GTE
);
}
}, [chartsVisible, rowCount, rowCountRelation]);
return {
chartsVisible,
chartsButtonVisible: true,
@ -144,11 +164,13 @@ export const useDataGrid = (
pagination,
resetPagination,
rowCount,
rowCountRelation,
setColumnCharts,
setErrorMessage,
setNoDataMessage,
setPagination,
setRowCount,
setRowCountRelation,
setSortingColumns,
setStatus,
setTableItems,

View file

@ -28,6 +28,7 @@ export const getIndexData = async (
pagination,
setErrorMessage,
setRowCount,
setRowCountRelation,
setStatus,
setTableItems,
sortingColumns,
@ -64,6 +65,7 @@ export const getIndexData = async (
if (!options.didCancel) {
setRowCount(resp.hits.total.value);
setRowCountRelation(resp.hits.total.relation);
setTableItems(
resp.hits.hits.map((d) =>
getProcessedFields(d.fields, (key: string) =>

View file

@ -49,13 +49,22 @@ import { DataGrid } from '../../../../../components/data_grid';
import { fetchExplainData } from '../shared';
import { useIndexData } from '../../hooks';
import { ExplorationQueryBar } from '../../../analytics_exploration/components/exploration_query_bar';
import { useSavedSearch } from './use_saved_search';
import { useSavedSearch, SavedSearchQuery } from './use_saved_search';
import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search';
import { ExplorationQueryBarProps } from '../../../analytics_exploration/components/exploration_query_bar/exploration_query_bar';
import { Query } from '../../../../../../../../../../src/plugins/data/common/query';
import { ScatterplotMatrix } from '../../../../../components/scatterplot_matrix';
function getIndexDataQuery(savedSearchQuery: SavedSearchQuery, jobConfigQuery: any) {
// Return `undefined` if savedSearchQuery itself is `undefined`, meaning it hasn't been initialized yet.
if (savedSearchQuery === undefined) {
return;
}
return savedSearchQuery !== null ? savedSearchQuery : jobConfigQuery;
}
const requiredFieldsErrorText = i18n.translate(
'xpack.ml.dataframe.analytics.createWizard.requiredFieldsErrorMessage',
{
@ -130,7 +139,7 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
const indexData = useIndexData(
currentIndexPattern,
savedSearchQuery !== undefined ? savedSearchQuery : jobConfigQuery,
getIndexDataQuery(savedSearchQuery, jobConfigQuery),
toastNotifications
);
@ -296,7 +305,7 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
}, []);
useEffect(() => {
if (savedSearchQueryStr !== undefined) {
if (typeof savedSearchQueryStr === 'string') {
setFormState({ jobConfigQuery: savedSearchQuery, jobConfigQueryString: savedSearchQueryStr });
}
}, [JSON.stringify(savedSearchQuery), savedSearchQueryStr]);
@ -343,12 +352,16 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
!dependentVariableEmpty)) &&
scatterplotFieldOptions.length > 1;
// Don't render until `savedSearchQuery` has been initialized.
// `undefined` means uninitialized, `null` means initialized but not used.
if (savedSearchQuery === undefined) return null;
return (
<Fragment>
<Messages messages={requestMessages} />
<SupportedFieldsMessage jobType={jobType} />
<JobType type={jobType} setFormState={setFormState} />
{savedSearchQuery === undefined && (
{savedSearchQuery === null && (
<EuiFormRow
label={i18n.translate('xpack.ml.dataframe.analytics.create.sourceQueryLabel', {
defaultMessage: 'Query',
@ -365,7 +378,7 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
<EuiFormRow
label={
<Fragment>
{savedSearchQuery !== undefined && (
{savedSearchQuery !== null && (
<EuiText>
{i18n.translate('xpack.ml.dataframe.analytics.create.savedSearchLabel', {
defaultMessage: 'Saved search',
@ -373,7 +386,7 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
</EuiText>
)}
<EuiBadge color="hollow">
{savedSearchQuery !== undefined
{savedSearchQuery !== null
? currentSavedSearch?.attributes.title
: currentIndexPattern.title}
</EuiBadge>

View file

@ -11,9 +11,20 @@ import { esQuery, esKuery } from '../../../../../../../../../../src/plugins/data
import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search';
import { getQueryFromSavedSearch } from '../../../../../util/index_utils';
// `undefined` is used for a non-initialized state
// `null` is set if no saved search is used
export type SavedSearchQuery = Record<string, any> | null | undefined;
export type SavedSearchQueryStr =
| string
| {
[key: string]: any;
}
| null
| undefined;
export function useSavedSearch() {
const [savedSearchQuery, setSavedSearchQuery] = useState<any>(undefined);
const [savedSearchQueryStr, setSavedSearchQueryStr] = useState<any>(undefined);
const [savedSearchQuery, setSavedSearchQuery] = useState<SavedSearchQuery>(undefined);
const [savedSearchQueryStr, setSavedSearchQueryStr] = useState<SavedSearchQueryStr>(undefined);
const mlContext = useMlContext();
const { currentSavedSearch, currentIndexPattern, kibanaConfig } = mlContext;
@ -37,6 +48,9 @@ export function useSavedSearch() {
setSavedSearchQuery(qry);
setSavedSearchQueryStr(qryString);
} else {
setSavedSearchQuery(null);
setSavedSearchQueryStr(null);
}
};

View file

@ -36,7 +36,7 @@ type IndexSearchResponse = SearchResponse7;
export const useIndexData = (
indexPattern: IndexPattern,
query: any,
query: Record<string, any> | undefined,
toastNotifications: CoreSetup['notifications']['toasts']
): UseIndexDataReturnType => {
const indexPatternFields = useMemo(() => getFieldsFromKibanaIndexPattern(indexPattern), [
@ -59,6 +59,7 @@ export const useIndexData = (
resetPagination,
setErrorMessage,
setRowCount,
setRowCountRelation,
setStatus,
setTableItems,
sortingColumns,
@ -81,8 +82,7 @@ export const useIndexData = (
const esSearchRequest = {
index: indexPattern.title,
body: {
// Instead of using the default query (`*`), fall back to a more efficient `match_all` query.
query, // isDefaultQuery(query) ? matchAllQuery : query,
query,
from: pagination.pageIndex * pagination.pageSize,
size: pagination.pageSize,
fields: ['*'],
@ -97,6 +97,7 @@ export const useIndexData = (
const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields));
setRowCount(resp.hits.total.value);
setRowCountRelation(resp.hits.total.relation);
setTableItems(docs);
setStatus(INDEX_STATUS.LOADED);
} catch (e) {
@ -106,7 +107,9 @@ export const useIndexData = (
};
useEffect(() => {
getIndexData();
if (query !== undefined) {
getIndexData();
}
// custom comparison
}, [indexPattern.title, indexPatternFields, JSON.stringify([query, pagination, sortingColumns])]);
@ -114,7 +117,7 @@ export const useIndexData = (
indexPattern,
]);
const fetchColumnChartsData = async function () {
const fetchColumnChartsData = async function (fieldHistogramsQuery: Record<string, any>) {
try {
const columnChartsData = await dataLoader.loadFieldHistograms(
columns
@ -123,7 +126,7 @@ export const useIndexData = (
fieldName: cT.id,
type: getFieldType(cT.schema),
})),
query
fieldHistogramsQuery
);
dataGrid.setColumnCharts(columnChartsData);
} catch (e) {
@ -132,8 +135,8 @@ export const useIndexData = (
};
useEffect(() => {
if (dataGrid.chartsVisible) {
fetchColumnChartsData();
if (dataGrid.chartsVisible && query !== undefined) {
fetchColumnChartsData(query);
}
// custom comparison
}, [

View file

@ -18,10 +18,15 @@ import {
isClassificationAnalysis,
isRegressionAnalysis,
} from '../../../../../../../common/util/analytics_utils';
import { HITS_TOTAL_RELATION } from '../../../../../../../common/types/es_client';
import { getToastNotifications } from '../../../../../util/dependency_cache';
import { useColorRange, ColorRangeLegend } from '../../../../../components/color_range_legend';
import { DataGrid, UseIndexDataReturnType } from '../../../../../components/data_grid';
import {
DataGrid,
RowCountRelation,
UseIndexDataReturnType,
} from '../../../../../components/data_grid';
import { SavedSearchQuery } from '../../../../../contexts/ml';
import {
@ -59,6 +64,7 @@ const getResultsSectionHeaderItems = (
status: INDEX_STATUS,
tableItems: Array<Record<string, any>>,
rowCount: number,
rowCountRelation: RowCountRelation,
colorRange?: ReturnType<typeof useColorRange>
): ExpandableSectionProps['headerItems'] => {
return columnsWithCharts.length > 0 && (tableItems.length > 0 || status === INDEX_STATUS.LOADED)
@ -71,7 +77,7 @@ const getResultsSectionHeaderItems = (
defaultMessage="Total docs"
/>
),
value: rowCount,
value: `${rowCountRelation === HITS_TOTAL_RELATION.GTE ? '>' : ''}${rowCount}`,
},
...(colorRange !== undefined
? [
@ -120,6 +126,7 @@ export const ExpandableSectionResults: FC<ExpandableSectionResultsProps> = ({
status,
tableItems,
indexData.rowCount,
indexData.rowCountRelation,
colorRange
);
const analysisType =

View file

@ -50,6 +50,7 @@ export {
getSeverityType,
getFormattedSeverityScore,
} from '../common/util/anomaly_utils';
export { HITS_TOTAL_RELATION } from '../common/types/es_client';
export { ANOMALY_SEVERITY } from '../common';
export { useMlHref, ML_PAGES, MlUrlGenerator } from './ml_url_generator';

View file

@ -9,11 +9,13 @@ import { IScopedClusterClient } from 'kibana/server';
import { validateJob, ValidateJobPayload } from './job_validation';
import { JobValidationMessage } from '../../../common/constants/messages';
import { HITS_TOTAL_RELATION } from '../../../common/types/es_client';
import type { MlClient } from '../../lib/ml_client';
const callAs = {
fieldCaps: () => Promise.resolve({ body: { fields: [] } }),
search: () => Promise.resolve({ body: { hits: { total: { value: 1, relation: 'eq' } } } }),
search: () =>
Promise.resolve({ body: { hits: { total: { value: 1, relation: HITS_TOTAL_RELATION.EQ } } } }),
};
const mlClusterClient = ({

View file

@ -5,5 +5,6 @@
* 2.0.
*/
export type { SearchResponse7 } from '../../ml/common';
export type { HitsTotalRelation, SearchResponse7 } from '../../ml/common';
export { HITS_TOTAL_RELATION } from '../../ml/common';
export { composeValidators, patternValidator } from '../../ml/common';

View file

@ -16,4 +16,4 @@ export const useRequest = jest.fn(() => ({
export const createSavedSearchesLoader = jest.fn();
// just passing through the reimports
export { getMlSharedImports } from '../../../ml/public';
export { getMlSharedImports, HITS_TOTAL_RELATION } from '../../../ml/public';

View file

@ -140,7 +140,7 @@ const apiFactory = () => ({
hits: [],
total: {
value: 0,
relation: 'the-relation',
relation: 'eq',
},
max_score: 0,
},

View file

@ -87,6 +87,7 @@ export const useIndexData = (
setColumnCharts,
setErrorMessage,
setRowCount,
setRowCountRelation,
setStatus,
setTableItems,
sortingColumns,
@ -135,6 +136,7 @@ export const useIndexData = (
const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields));
setRowCount(resp.hits.total.value);
setRowCountRelation(resp.hits.total.relation);
setTableItems(docs);
setStatus(INDEX_STATUS.LOADED);
};

View file

@ -18,7 +18,7 @@ import type { PreviewMappingsProperties } from '../../../common/api_schemas/tran
import { isPostTransformsPreviewResponseSchema } from '../../../common/api_schemas/type_guards';
import { getNestedProperty } from '../../../common/utils/object_utils';
import { RenderCellValue, UseIndexDataReturnType } from '../../shared_imports';
import { RenderCellValue, UseIndexDataReturnType, HITS_TOTAL_RELATION } from '../../shared_imports';
import { getErrorMessage } from '../../../common/utils/errors';
import { useAppDependencies } from '../app_dependencies';
@ -117,6 +117,7 @@ export const usePivotData = (
setErrorMessage,
setNoDataMessage,
setRowCount,
setRowCountRelation,
setStatus,
setTableItems,
sortingColumns,
@ -127,6 +128,7 @@ export const usePivotData = (
if (!validationStatus.isValid) {
setTableItems([]);
setRowCount(0);
setRowCountRelation(HITS_TOTAL_RELATION.EQ);
setNoDataMessage(validationStatus.errorMessage!);
return;
}
@ -147,6 +149,7 @@ export const usePivotData = (
setErrorMessage(getErrorMessage(resp));
setTableItems([]);
setRowCount(0);
setRowCountRelation(HITS_TOTAL_RELATION.EQ);
setPreviewMappingsProperties({});
setStatus(INDEX_STATUS.ERROR);
return;
@ -154,6 +157,7 @@ export const usePivotData = (
setTableItems(resp.preview);
setRowCount(resp.preview.length);
setRowCountRelation(HITS_TOTAL_RELATION.EQ);
setPreviewMappingsProperties(resp.generated_dest_index.mappings.properties);
setStatus(INDEX_STATUS.LOADED);

View file

@ -15,6 +15,7 @@ export {
UseIndexDataReturnType,
EsSorting,
RenderCellValue,
HITS_TOTAL_RELATION,
} from '../../ml/public';
import { XJson } from '../../../../src/plugins/es_ui_shared/public';

View file

@ -265,7 +265,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('displays the source data preview');
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists();
await ml.testExecution.logTestStep('enables the source data preview histogram charts');
await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts();
await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true);
await ml.testExecution.logTestStep('displays the include fields selection');
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
// EuiDataGrid does not have row roles

View file

@ -117,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists();
await ml.testExecution.logTestStep('enables the source data preview histogram charts');
await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts();
await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts(true);
await ml.testExecution.logTestStep('displays the source data preview histogram charts');
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramCharts(

View file

@ -396,7 +396,7 @@ export default function ({ getService }: FtrProviderContext) {
await transform.wizard.assertAdvancedQueryEditorSwitchCheckState(false);
await transform.testExecution.logTestStep('enables the index preview histogram charts');
await transform.wizard.enableIndexPreviewHistogramCharts();
await transform.wizard.enableIndexPreviewHistogramCharts(true);
await transform.testExecution.logTestStep('displays the index preview histogram charts');
await transform.wizard.assertIndexPreviewHistogramCharts(

View file

@ -116,10 +116,12 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
await testSubjects.existOrFail('mlAnalyticsCreationDataGridHistogramButton');
},
async enableSourceDataPreviewHistogramCharts() {
await this.assertSourceDataPreviewHistogramChartButtonCheckState(false);
await testSubjects.click('mlAnalyticsCreationDataGridHistogramButton');
await this.assertSourceDataPreviewHistogramChartButtonCheckState(true);
async enableSourceDataPreviewHistogramCharts(expectedDefaultButtonState: boolean) {
await this.assertSourceDataPreviewHistogramChartButtonCheckState(expectedDefaultButtonState);
if (expectedDefaultButtonState === false) {
await testSubjects.click('mlAnalyticsCreationDataGridHistogramButton');
await this.assertSourceDataPreviewHistogramChartButtonCheckState(true);
}
},
async assertSourceDataPreviewHistogramChartButtonCheckState(expectedCheckState: boolean) {

View file

@ -177,10 +177,12 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) {
await testSubjects.existOrFail('transformIndexPreviewHistogramButton');
},
async enableIndexPreviewHistogramCharts() {
await this.assertIndexPreviewHistogramChartButtonCheckState(false);
await testSubjects.click('transformIndexPreviewHistogramButton');
await this.assertIndexPreviewHistogramChartButtonCheckState(true);
async enableIndexPreviewHistogramCharts(expectedDefaultButtonState: boolean) {
await this.assertIndexPreviewHistogramChartButtonCheckState(expectedDefaultButtonState);
if (expectedDefaultButtonState === false) {
await testSubjects.click('transformIndexPreviewHistogramButton');
await this.assertIndexPreviewHistogramChartButtonCheckState(true);
}
},
async assertIndexPreviewHistogramChartButtonCheckState(expectedCheckState: boolean) {