Skip to content

Commit 5626631

Browse files
committed
feat(ml): add header content support to data visualizer components
- Enhanced the DataVisualizerStateContextProvider and IndexDataVisualizer components to accept a new `headerContent` prop, allowing for customizable header content. - Updated IndexDataVisualizerView to render the `headerContent` within the layout, improving flexibility for displaying additional information. - Implemented similar changes in ChangePointDetectionPage, LogCategorizationPage, and LogRateAnalysisPage to handle scenarios where no data view is selected, displaying an empty prompt along with the header content. This update enhances the user experience by providing more context and information in the data visualizer components.
1 parent f712677 commit 5626631

7 files changed

Lines changed: 268 additions & 151 deletions

File tree

x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55
* 2.0.
66
*/
77

8-
import { css } from '@emotion/react';
9-
import type { FC } from 'react';
8+
import type { FC, ReactNode } from 'react';
109
import React, { useEffect, useMemo, useState, useCallback, useRef } from 'react';
1110
import type { Required } from 'utility-types';
1211
import { getEsQueryConfig } from '@kbn/data-plugin/common';
1312

1413
import {
15-
useEuiBreakpoint,
1614
useIsWithinMaxBreakpoint,
1715
EuiFlexGroup,
1816
EuiFlexItem,
@@ -104,9 +102,13 @@ export interface IndexDataVisualizerViewProps {
104102
currentSavedSearch: SavedSearch | null;
105103
currentSessionId?: string;
106104
getAdditionalLinks?: GetAdditionalLinks;
105+
headerContent?: ReactNode;
107106
}
108107

109-
export const IndexDataVisualizerView: FC<IndexDataVisualizerViewProps> = (dataVisualizerProps) => {
108+
export const IndexDataVisualizerView: FC<IndexDataVisualizerViewProps> = ({
109+
headerContent,
110+
...dataVisualizerProps
111+
}) => {
110112
const [savedRandomSamplerPreference, saveRandomSamplerPreference] = useStorage<
111113
DVKey,
112114
DVStorageMapped<typeof DV_RANDOM_SAMPLER_PREFERENCE>
@@ -458,12 +460,6 @@ export const IndexDataVisualizerView: FC<IndexDataVisualizerViewProps> = (dataVi
458460
);
459461

460462
const isWithinLargeBreakpoint = useIsWithinMaxBreakpoint('l');
461-
const dvPageHeader = css({
462-
[useEuiBreakpoint(['xs', 's', 'm', 'l'])]: {
463-
flexDirection: 'column',
464-
alignItems: 'flex-start',
465-
},
466-
});
467463

468464
const queryNeedsUpdate = useMemo(
469465
() => (localQueryString !== dataVisualizerListState.searchString ? true : undefined),
@@ -519,40 +515,47 @@ export const IndexDataVisualizerView: FC<IndexDataVisualizerViewProps> = (dataVi
519515
paddingSize="none"
520516
>
521517
<EuiPageTemplate.Section>
522-
<EuiPageTemplate.Header
523-
data-test-subj="dataVisualizerPageHeader"
524-
css={dvPageHeader}
525-
pageTitle={
526-
<>
527-
{currentDataView.getName()}
528-
{/* TODO: This management section shouldn't live inside the header */}
529-
<DataVisualizerDataViewManagement currentDataView={currentDataView} />
530-
</>
531-
}
532-
rightSideGroupProps={{
533-
gutterSize: 's',
534-
'data-test-subj': 'dataVisualizerTimeRangeSelectorSection',
535-
}}
536-
rightSideItems={[
537-
<DatePickerWrapper
538-
isAutoRefreshOnly={!hasValidTimeField}
539-
showRefresh={!hasValidTimeField}
540-
width="full"
541-
needsUpdate={queryNeedsUpdate}
542-
onRefresh={handleRefresh}
543-
/>,
544-
hasValidTimeField && (
545-
<FullTimeRangeSelector
546-
frozenDataPreference={frozenDataPreference}
547-
setFrozenDataPreference={setFrozenDataPreference}
548-
dataView={currentDataView}
549-
query={undefined}
550-
disabled={false}
551-
timefilter={timefilter}
552-
/>
553-
),
554-
]}
555-
/>
518+
<EuiFlexGroup
519+
gutterSize="s"
520+
alignItems="center"
521+
justifyContent="spaceBetween"
522+
responsive={false}
523+
data-test-subj="dataVisualizerTimeRangeSelectorSection"
524+
>
525+
<EuiFlexItem grow={false}>
526+
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
527+
<EuiFlexItem grow={false}>{headerContent ?? null}</EuiFlexItem>
528+
<EuiFlexItem grow={false}>
529+
<DataVisualizerDataViewManagement currentDataView={currentDataView} />
530+
</EuiFlexItem>
531+
</EuiFlexGroup>
532+
</EuiFlexItem>
533+
<EuiFlexItem grow={false}>
534+
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
535+
{hasValidTimeField && (
536+
<EuiFlexItem grow={false}>
537+
<FullTimeRangeSelector
538+
frozenDataPreference={frozenDataPreference}
539+
setFrozenDataPreference={setFrozenDataPreference}
540+
dataView={currentDataView}
541+
query={undefined}
542+
disabled={false}
543+
timefilter={timefilter}
544+
/>
545+
</EuiFlexItem>
546+
)}
547+
<EuiFlexItem grow={false}>
548+
<DatePickerWrapper
549+
isAutoRefreshOnly={!hasValidTimeField}
550+
showRefresh={!hasValidTimeField}
551+
width="full"
552+
needsUpdate={queryNeedsUpdate}
553+
onRefresh={handleRefresh}
554+
/>
555+
</EuiFlexItem>
556+
</EuiFlexGroup>
557+
</EuiFlexItem>
558+
</EuiFlexGroup>
556559
<EuiSpacer size="m" />
557560

558561
<EuiFlexGroup gutterSize="m" direction={isWithinLargeBreakpoint ? 'column' : 'row'}>

x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/index_data_visualizer.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* 2.0.
66
*/
77
import { pick } from 'lodash';
8-
import type { FC } from 'react';
8+
import type { FC, ReactNode } from 'react';
99
import React, { useCallback, useEffect, useState, useMemo } from 'react';
1010
import { useHistory, useLocation } from 'react-router-dom';
1111
import { parse, stringify } from 'query-string';
@@ -52,6 +52,7 @@ const localStorage = new Storage(window.localStorage);
5252
export interface DataVisualizerStateContextProviderProps {
5353
IndexDataVisualizerComponent: FC<IndexDataVisualizerViewProps>;
5454
getAdditionalLinks?: GetAdditionalLinks;
55+
headerContent?: ReactNode;
5556
}
5657
export type IndexDataVisualizerSpec = typeof IndexDataVisualizer;
5758

@@ -113,6 +114,7 @@ const DataVisualizerESQLStateContextProvider = () => {
113114
const DataVisualizerStateContextProvider: FC<DataVisualizerStateContextProviderProps> = ({
114115
IndexDataVisualizerComponent,
115116
getAdditionalLinks,
117+
headerContent,
116118
}) => {
117119
const { services } = useDataVisualizerKibana();
118120
const {
@@ -209,6 +211,11 @@ const DataVisualizerStateContextProvider: FC<DataVisualizerStateContextProviderP
209211
if (typeof parsedQueryString?.index === 'string') {
210212
const dataView = await dataViews.get(parsedQueryString.index);
211213
setCurrentDataView(dataView);
214+
} else if (typeof parsedQueryString?.savedSearchId !== 'string') {
215+
const defaultDataView = await dataViews.getDefaultDataView().catch(() => null);
216+
if (defaultDataView) {
217+
setCurrentDataView(defaultDataView);
218+
}
212219
}
213220
};
214221
getDataView();
@@ -284,8 +291,11 @@ const DataVisualizerStateContextProvider: FC<DataVisualizerStateContextProviderP
284291
currentSavedSearch={currentSavedSearch}
285292
currentSessionId={currentSessionId}
286293
getAdditionalLinks={getAdditionalLinks}
294+
headerContent={headerContent}
287295
/>
288-
) : null}
296+
) : (
297+
headerContent ?? null
298+
)}
289299
</UrlStateContextProvider>
290300
);
291301
};
@@ -294,12 +304,14 @@ export interface Props {
294304
getAdditionalLinks?: GetAdditionalLinks;
295305
showFrozenDataTierChoice?: boolean;
296306
esql?: boolean;
307+
headerContent?: ReactNode;
297308
}
298309

299310
export const IndexDataVisualizer: FC<Props> = ({
300311
getAdditionalLinks,
301312
showFrozenDataTierChoice = true,
302313
esql,
314+
headerContent,
303315
}) => {
304316
const coreStart = getCoreStart();
305317
const {
@@ -352,6 +364,7 @@ export const IndexDataVisualizer: FC<Props> = ({
352364
<DataVisualizerStateContextProvider
353365
IndexDataVisualizerComponent={IndexDataVisualizerView}
354366
getAdditionalLinks={getAdditionalLinks}
367+
headerContent={headerContent}
355368
/>
356369
) : (
357370
<DataVisualizerESQLStateContextProvider />

x-pack/platform/plugins/shared/ml/public/application/aiops/change_point_detection.tsx

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
1414
import { ChangePointDetection } from '@kbn/aiops-plugin/public';
1515
import { AIOPS_EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants';
1616
import { useFieldStatsTrigger, FieldStatsFlyoutProvider } from '@kbn/ml-field-stats-flyout';
17-
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
17+
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
1818

1919
import { useDataSource } from '../contexts/ml/data_source_context';
2020
import { useMlKibana } from '../contexts/kibana';
@@ -58,39 +58,63 @@ export const ChangePointDetectionPage: FC = () => {
5858
</EuiFlexItem>
5959
</EuiFlexGroup>
6060
</MlPageHeader>
61-
<ChangePointDetection
62-
dataView={dataView}
63-
savedSearch={savedSearch}
64-
showFrozenDataTierChoice={showNodeInfo}
65-
headerContent={headerContent}
66-
appContextValue={{
67-
embeddingOrigin: AIOPS_EMBEDDABLE_ORIGIN.ML_AIOPS_LABS,
68-
...pick(services, [
69-
'analytics',
70-
'application',
71-
'cases',
72-
'charts',
73-
'data',
74-
'embeddable',
75-
'executionContext',
76-
'fieldFormats',
77-
'http',
78-
'i18n',
79-
'lens',
80-
'notifications',
81-
'share',
82-
'storage',
83-
'theme',
84-
'uiActions',
85-
'uiSettings',
86-
'unifiedSearch',
87-
'usageCollection',
88-
'userProfile',
89-
'cps',
90-
]),
91-
fieldStats: { useFieldStatsTrigger, FieldStatsFlyoutProvider },
92-
}}
93-
/>
61+
{!dataView ? (
62+
<>
63+
{headerContent}
64+
<EuiEmptyPrompt
65+
title={
66+
<h2>
67+
<FormattedMessage
68+
id="xpack.ml.changePointDetection.noDataViewTitle"
69+
defaultMessage="No data view selected"
70+
/>
71+
</h2>
72+
}
73+
body={
74+
<p>
75+
<FormattedMessage
76+
id="xpack.ml.changePointDetection.noDataViewBody"
77+
defaultMessage="Select a data view or Discover session to get started."
78+
/>
79+
</p>
80+
}
81+
/>
82+
</>
83+
) : (
84+
<ChangePointDetection
85+
dataView={dataView}
86+
savedSearch={savedSearch}
87+
showFrozenDataTierChoice={showNodeInfo}
88+
headerContent={headerContent}
89+
appContextValue={{
90+
embeddingOrigin: AIOPS_EMBEDDABLE_ORIGIN.ML_AIOPS_LABS,
91+
...pick(services, [
92+
'analytics',
93+
'application',
94+
'cases',
95+
'charts',
96+
'data',
97+
'embeddable',
98+
'executionContext',
99+
'fieldFormats',
100+
'http',
101+
'i18n',
102+
'lens',
103+
'notifications',
104+
'share',
105+
'storage',
106+
'theme',
107+
'uiActions',
108+
'uiSettings',
109+
'unifiedSearch',
110+
'usageCollection',
111+
'userProfile',
112+
'cps',
113+
]),
114+
fieldStats: { useFieldStatsTrigger, FieldStatsFlyoutProvider },
115+
}}
116+
/>
117+
)}
94118
<HelpMenu
95119
docLink={services.docLinks.links.aggs.change_point}
96120
appName={i18n.translate('xpack.ml.changePointDetection.pageHeader', {

0 commit comments

Comments
 (0)