Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML][UX]: Consistent Layout and UI Enhancements for ML Pages #203813

Merged
merged 28 commits into from
Dec 23, 2024
Merged
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6a0d029
anomaly timeline use eui button for add to actions popover
rbrtj Dec 11, 2024
28d27b0
timeseries explorer controls layout update
rbrtj Dec 11, 2024
924c396
log rate analysis use eui button for add to actions popover
rbrtj Dec 11, 2024
a3b2030
log rate analysis use eui button for add to actions popover & auto sa…
rbrtj Dec 11, 2024
02aa1f3
remove unused import
rbrtj Dec 11, 2024
3ae73e8
change point detection use eui button for add to actions popover
rbrtj Dec 11, 2024
0e0f1de
update button colors to text
rbrtj Dec 12, 2024
9960e5c
single metric viewer embeddable color
rbrtj Dec 12, 2024
a0bb316
update buttons to button icon with boxes horizontal icon
rbrtj Dec 12, 2024
18fe658
use size s as default size for add to button
rbrtj Dec 13, 2024
9142f10
keep consistent spaces between controls around ml pages
rbrtj Dec 13, 2024
a0bea28
fix overflowing date picker inside various ml pages
rbrtj Dec 13, 2024
9aa70f6
fix data visualizer layout
rbrtj Dec 13, 2024
63c3193
update toasts
rbrtj Dec 16, 2024
4db66c5
Merge remote-tracking branch 'upstream/main' into ml-add-to-action-ui…
rbrtj Dec 17, 2024
24eaf3d
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 17, 2024
e9108d6
update anomaly panel menu button
rbrtj Dec 17, 2024
4c193b7
add icons for attachements actions
rbrtj Dec 18, 2024
7f408c6
internationalization for cases toast messages && plural for anomaly c…
rbrtj Dec 19, 2024
cd56f03
timeseriesexplorer detector dropdown min width && wrap controls
rbrtj Dec 19, 2024
dfd2494
Merge remote-tracking branch 'upstream/main' into ml-add-to-action-ui…
rbrtj Dec 19, 2024
847d00e
different message for change point table and chart attachments && plu…
rbrtj Dec 19, 2024
d73ab5d
revert accidentaly removed comment
rbrtj Dec 19, 2024
7f47fad
rename titles variable to cases_toast_messages_titles
rbrtj Dec 19, 2024
e679f8e
data visualizer view revert time range selector id
rbrtj Dec 19, 2024
c9bf590
update cases functional tests to respect new toast title
rbrtj Dec 19, 2024
1368a37
update more tests to respect new toast title
rbrtj Dec 19, 2024
f54f968
Merge branch 'main' into ml-add-to-action-ui-update
rbrtj Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
timeseries explorer controls layout update
rbrtj committed Dec 11, 2024

Verified

This commit was signed with the committer’s verified signature.
commit 28d27b0b1bec6bc8d4ac5e44af0992aee6182754
Original file line number Diff line number Diff line change
@@ -9,11 +9,12 @@ import type { FC } from 'react';
import React, { useCallback, useState } from 'react';
import type { EuiContextMenuPanelDescriptor } from '@elastic/eui';
import {
EuiButtonIcon,
EuiButton,
EuiCheckbox,
EuiContextMenu,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiPopover,
htmlIdGenerator,
} from '@elastic/eui';
@@ -25,19 +26,22 @@ import {
withSuspense,
} from '@kbn/presentation-util-plugin/public';
import { useTimeRangeUpdates } from '@kbn/ml-date-picker';
import type { MlJobState } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { CombinedJobWithStats } from '../../../../../server/shared';
import type { JobId } from '../../../../../common/types/anomaly_detection_jobs/job';
import { useMlKibana } from '../../../contexts/kibana';
import { useCasesModal } from '../../../contexts/kibana/use_cases_modal';
import { getDefaultSingleMetricViewerPanelTitle } from '../../../../embeddables/single_metric_viewer/get_default_panel_title';
import type { MlEntity } from '../../../../embeddables';
import { ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE } from '../../../../embeddables/constants';
import type { SingleMetricViewerEmbeddableState } from '../../../../embeddables/types';
import { ForecastingModal } from '../forecasting_modal/forecasting_modal';
import type { Entity } from '../entity_control/entity_control';

interface Props {
forecastId?: string;
selectedDetectorIndex: number;
selectedEntities?: MlEntity;
selectedJobId: JobId;
showAnnotationsCheckbox: boolean;
showAnnotations: boolean;
showForecastCheckbox: boolean;
@@ -47,6 +51,17 @@ interface Props {
onShowModelBoundsChange: () => void;
onShowAnnotationsChange: () => void;
onShowForecastChange: () => void;
fullRefresh: boolean;
loading: boolean;
hasResults: boolean;
selectedJob: CombinedJobWithStats;
selectedJobId: string;
jobs: CombinedJobWithStats[];
setForecastId: (forecastId: string) => void;
entities: Entity[];
jobState: MlJobState;
earliestRecordTimestamp: number;
latestRecordTimestamp: number;
}

const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard);
@@ -74,6 +89,16 @@ export const TimeSeriesExplorerControls: FC<Props> = ({
onShowAnnotationsChange,
onShowModelBoundsChange,
onShowForecastChange,
fullRefresh,
loading,
hasResults,
setForecastId,
selectedJob,
entities,
jobs,
jobState,
earliestRecordTimestamp,
latestRecordTimestamp,
}) => {
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
const [createInDashboard, setCreateInDashboard] = useState<boolean>(false);
@@ -102,6 +127,9 @@ export const TimeSeriesExplorerControls: FC<Props> = ({

const openCasesModalCallback = useCasesModal(ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE);

const showControls =
(fullRefresh === false || loading === false) && hasResults === true && jobs.length > 0;

const menuPanels: EuiContextMenuPanelDescriptor[] = [
{
id: 0,
@@ -175,74 +203,104 @@ export const TimeSeriesExplorerControls: FC<Props> = ({

return (
<>
<EuiFlexGroup style={{ float: 'right' }} alignItems="center">
{showModelBoundsCheckbox && (
<EuiFlexItem grow={false}>
<EuiCheckbox
id="toggleModelBoundsCheckbox"
label={i18n.translate('xpack.ml.timeSeriesExplorer.showModelBoundsLabel', {
defaultMessage: 'show model bounds',
})}
checked={showModelBounds}
onChange={onShowModelBoundsChange}
/>
</EuiFlexItem>
)}
<EuiFlexGroup style={{ float: 'right' }} alignItems="center" justifyContent="flexEnd">
{showControls && (
<>
{showModelBoundsCheckbox && (
<EuiFlexItem grow={false}>
<EuiFormRow hasEmptyLabelSpace>
<EuiCheckbox
id="toggleModelBoundsCheckbox"
label={i18n.translate('xpack.ml.timeSeriesExplorer.showModelBoundsLabel', {
defaultMessage: 'show model bounds',
})}
checked={showModelBounds}
onChange={onShowModelBoundsChange}
/>
</EuiFormRow>
</EuiFlexItem>
)}

{showAnnotationsCheckbox && (
<EuiFlexItem grow={false}>
<EuiCheckbox
id="toggleAnnotationsCheckbox"
label={i18n.translate('xpack.ml.timeSeriesExplorer.annotationsLabel', {
defaultMessage: 'annotations',
})}
checked={showAnnotations}
onChange={onShowAnnotationsChange}
/>
</EuiFlexItem>
)}
{showAnnotationsCheckbox && (
<EuiFlexItem grow={false}>
<EuiFormRow hasEmptyLabelSpace>
<EuiCheckbox
id="toggleAnnotationsCheckbox"
label={i18n.translate('xpack.ml.timeSeriesExplorer.annotationsLabel', {
defaultMessage: 'annotations',
})}
checked={showAnnotations}
onChange={onShowAnnotationsChange}
/>
</EuiFormRow>
</EuiFlexItem>
)}

{showForecastCheckbox && (
<EuiFlexItem grow={false}>
<EuiCheckbox
id="toggleShowForecastCheckbox"
label={
<span data-test-subj={'mlForecastCheckbox'}>
{i18n.translate('xpack.ml.timeSeriesExplorer.showForecastLabel', {
defaultMessage: 'show forecast',
})}
</span>
}
checked={showForecast}
onChange={onShowForecastChange}
/>
</EuiFlexItem>
)}
{showForecastCheckbox && (
<EuiFlexItem grow={false}>
<EuiFormRow hasEmptyLabelSpace>
<EuiCheckbox
id="toggleShowForecastCheckbox"
label={
<span data-test-subj={'mlForecastCheckbox'}>
{i18n.translate('xpack.ml.timeSeriesExplorer.showForecastLabel', {
defaultMessage: 'show forecast',
})}
</span>
}
checked={showForecast}
onChange={onShowForecastChange}
/>
</EuiFormRow>
</EuiFlexItem>
)}

{canEditDashboards ? (
<EuiFlexItem grow={false} css={{ marginLeft: 'auto !important', alignSelf: 'baseline' }}>
<EuiPopover
button={
<EuiButtonIcon
size="s"
aria-label={i18n.translate('xpack.ml.explorer.swimlaneActions', {
defaultMessage: 'Actions',
})}
color="text"
iconType="boxesHorizontal"
onClick={setIsMenuOpen.bind(null, !isMenuOpen)}
data-test-subj="mlAnomalyTimelinePanelMenu"
/>
}
isOpen={isMenuOpen}
closePopover={setIsMenuOpen.bind(null, false)}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenu initialPanelId={0} panels={menuPanels} />
</EuiPopover>
</EuiFlexItem>
) : null}
{canEditDashboards ? (
<EuiFlexItem grow={false}>
<EuiFormRow hasEmptyLabelSpace>
<EuiPopover
button={
<EuiButton
aria-label={i18n.translate('xpack.ml.explorer.swimlaneActions', {
defaultMessage: 'Actions',
})}
iconSide="right"
isSelected={isMenuOpen}
iconType={isMenuOpen ? 'arrowUp' : 'arrowDown'}
onClick={setIsMenuOpen.bind(null, !isMenuOpen)}
data-test-subj="mlAnomalyTimelinePanelMenu"
>
<FormattedMessage
id="xpack.ml.explorer.addToLabel"
defaultMessage="Add to"
/>
</EuiButton>
}
isOpen={isMenuOpen}
closePopover={setIsMenuOpen.bind(null, false)}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenu initialPanelId={0} panels={menuPanels} />
</EuiPopover>
</EuiFormRow>
</EuiFlexItem>
) : null}
</>
)}
<EuiFormRow hasEmptyLabelSpace>
<ForecastingModal
job={selectedJob}
jobState={jobState}
detectorIndex={selectedDetectorIndex}
entities={entities}
earliestRecordTimestamp={earliestRecordTimestamp}
latestRecordTimestamp={latestRecordTimestamp}
setForecastId={setForecastId}
className="forecast-controls"
selectedForecastId={forecastId}
/>
</EuiFormRow>
</EuiFlexGroup>
{createInDashboard ? (
<SavedObjectSaveModalDashboard
Original file line number Diff line number Diff line change
@@ -1070,20 +1070,34 @@ export class TimeSeriesExplorer extends React.Component {
setFunctionDescription={this.setFunctionDescription}
>
{arePartitioningFieldsProvided && (
<EuiFlexItem style={{ textAlign: 'right' }}>
<EuiFormRow hasEmptyLabelSpace style={{ maxWidth: '100%' }}>
<ForecastingModal
job={selectedJob}
jobState={selectedJob.state}
detectorIndex={selectedDetectorIndex}
entities={entityControls}
earliestRecordTimestamp={selectedJob.data_counts.earliest_record_timestamp}
latestRecordTimestamp={selectedJob.data_counts.latest_record_timestamp}
setForecastId={this.setForecastId}
className="forecast-controls"
selectedForecastId={this.props.selectedForecastId}
/>
</EuiFormRow>
<EuiFlexItem>
<TimeSeriesExplorerControls
forecastId={this.props.selectedForecastId}
selectedDetectorIndex={selectedDetectorIndex}
selectedEntities={selectedEntities}
selectedJob={selectedJob}
showAnnotationsCheckbox={showAnnotationsCheckbox}
showAnnotations={showAnnotations}
showForecastCheckbox={showForecastCheckbox}
showForecast={showForecast}
showModelBoundsCheckbox={showModelBoundsCheckbox}
showModelBounds={showModelBounds}
onShowModelBoundsChange={this.toggleShowModelBoundsHandler}
onShowAnnotationsChange={this.toggleShowAnnotationsHandler}
onShowForecastChange={this.toggleShowForecastHandler}
fullRefresh={fullRefresh}
loading={loading}
hasResults={hasResults}
setForecastId={this.setForecastId}
entities={entityControls}
jobs={jobs}
selectedJobId={selectedJobId}
// It seems like props below can be easily extracted from the selectedJob
// However, it seems like we are losing sync at some point and they need to be passed directly
jobState={selectedJob.state}
earliestRecordTimestamp={selectedJob.data_counts.earliest_record_timestamp}
latestRecordTimestamp={selectedJob.data_counts.latest_record_timestamp}
/>
</EuiFlexItem>
)}
</SeriesControls>
@@ -1182,22 +1196,6 @@ export class TimeSeriesExplorer extends React.Component {
</EuiFlexItem>
</EuiFlexGroup>

<TimeSeriesExplorerControls
forecastId={this.props.selectedForecastId}
selectedDetectorIndex={selectedDetectorIndex}
selectedEntities={selectedEntities}
selectedJobId={selectedJobId}
showAnnotationsCheckbox={showAnnotationsCheckbox}
showAnnotations={showAnnotations}
showForecastCheckbox={showForecastCheckbox}
showForecast={showForecast}
showModelBoundsCheckbox={showModelBoundsCheckbox}
showModelBounds={showModelBounds}
onShowModelBoundsChange={this.toggleShowModelBoundsHandler}
onShowAnnotationsChange={this.toggleShowAnnotationsHandler}
onShowForecastChange={this.toggleShowForecastHandler}
/>

<TimeSeriesChartWithTooltips
chartProps={chartProps}
contextAggregationInterval={contextAggregationInterval}