Skip to content

Commit

Permalink
[ML] Add grouping to the side nav (#123805)
Browse files Browse the repository at this point in the history
* wip side nav grouping, open anomaly explorer

* single metric viewer job selection

* add top level data vis link

* resolve active route based on path

* rename hook

* fix breadcrumbs

* update Model Management structure

* fix i18n

* add test subj

* handle empty job selection on the Anomaly Explorer page

* checks for Anomaly Explorer page

* update SMV side nav link

* hide job selection control

* SMV job selection flyout on open

* fix spacer

* fix start trial panel positioning

* timeseriesOnly for SMV

* fix isSelected check for data-test-subj

* fix i18n

* model management experimental tag

* update model management navigation in functional tests

* update SMV test with unsupported job

* no jobs selected callout in smv

* show EuiLoadingContent while loading

* update breadcrumbs for model management pages

* update images
  • Loading branch information
darnautov authored Jan 28, 2022
1 parent 7e69c7d commit d66aa7a
Show file tree
Hide file tree
Showing 40 changed files with 569 additions and 547 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.mlJobSelectorBar {
padding: $euiSizeS;
}

.mlJobSelectorFlyoutBody>.euiFlyoutBody__overflow {
padding-top: $euiSizeS;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@

import React, { useState, useEffect, useCallback } from 'react';

import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiFlyout } from '@elastic/eui';
import {
EuiButtonEmpty,
EuiFlexItem,
EuiFlexGroup,
EuiFlyout,
EuiHorizontalRule,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { FormattedMessage } from '@kbn/i18n-react';
import './_index.scss';

import { Dictionary } from '../../../../common/types/common';
Expand Down Expand Up @@ -137,37 +143,49 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J

function renderJobSelectionBar() {
return (
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexGroup
wrap
responsive={false}
gutterSize="xs"
alignItems="center"
data-test-subj="mlJobSelectionBadges"
>
<IdBadges
limit={BADGE_LIMIT}
maps={maps}
onLinkClick={() => setShowAllBarBadges(!showAllBarBadges)}
selectedIds={selectedIds}
showAllBarBadges={showAllBarBadges}
/>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="xs"
iconType="pencil"
onClick={handleJobSelectionClick}
data-test-subj="mlButtonEditJobSelection"
>
{i18n.translate('xpack.ml.jobSelector.jobSelectionButton', {
defaultMessage: 'Edit job selection',
})}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<>
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
<EuiFlexItem grow={false}>
{selectedIds.length > 0 ? (
<EuiFlexGroup
wrap
responsive={false}
gutterSize="xs"
alignItems="center"
data-test-subj="mlJobSelectionBadges"
>
<IdBadges
limit={BADGE_LIMIT}
maps={maps}
onLinkClick={() => setShowAllBarBadges(!showAllBarBadges)}
selectedIds={selectedIds}
showAllBarBadges={showAllBarBadges}
/>
</EuiFlexGroup>
) : (
<span>
<FormattedMessage
id="xpack.ml.jobSelector.noJobsSelectedLabel"
defaultMessage="No jobs selected"
/>
</span>
)}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="xs"
iconType="pencil"
onClick={handleJobSelectionClick}
data-test-subj="mlButtonEditJobSelection"
>
{i18n.translate('xpack.ml.jobSelector.jobSelectionButton', {
defaultMessage: 'Edit job selection',
})}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
</>
);
}

Expand Down Expand Up @@ -197,8 +215,8 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J
}

return (
<div className="mlJobSelectorBar">
{selectedIds.length > 0 && renderJobSelectionBar()}
<div>
{renderJobSelectionBar()}
{renderFlyout()}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,20 @@ import { JobSelectionMaps } from './job_selector';
export const BADGE_LIMIT = 10;
export const DEFAULT_GANTT_BAR_WIDTH = 299; // pixels

export interface JobSelectionResult {
newSelection: string[];
jobIds: string[];
groups: Array<{ groupId: string; jobIds: string[] }>;
time: { from: string; to: string } | undefined;
}

export interface JobSelectorFlyoutProps {
dateFormatTz: string;
selectedIds?: string[];
newSelection?: string[];
onFlyoutClose: () => void;
onJobsFetched?: (maps: JobSelectionMaps) => void;
onSelectionConfirmed: (payload: {
newSelection: string[];
jobIds: string[];
groups: Array<{ groupId: string; jobIds: string[] }>;
time: any;
}) => void;
onSelectionConfirmed: (payload: JobSelectionResult) => void;
singleSelection: boolean;
timeseriesOnly: boolean;
maps: JobSelectionMaps;
Expand Down Expand Up @@ -192,7 +194,7 @@ export const JobSelectorFlyoutContent: FC<JobSelectorFlyoutProps> = ({
</EuiTitle>
</EuiFlyoutHeader>

<EuiFlyoutBody className="mlJobSelectorFlyoutBody">
<EuiFlyoutBody className="mlJobSelectorFlyoutBody" data-test-subj={'mlJobSelectorFlyoutBody'}>
<EuiResizeObserver onResize={handleResize}>
{(resizeRef) => (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_j

import { useUrlState } from '../../util/url_state';

import { getTimeRangeFromSelection } from './job_select_service_utils';
import { useNotifications } from '../../contexts/kibana';
import { useJobSelectionFlyout } from '../../contexts/ml/use_job_selection_flyout';

// check that the ids read from the url exist by comparing them to the
// jobs loaded via mlJobsService.
Expand All @@ -35,6 +35,8 @@ export const useJobSelection = (jobs: MlJobWithTimeRange[]) => {
const [globalState, setGlobalState] = useUrlState('_g');
const { toasts: toastNotifications } = useNotifications();

const getJobSelection = useJobSelectionFlyout();

const tmpIds = useMemo(() => {
const ids = globalState?.ml?.jobIds || [];
return (typeof ids === 'string' ? [ids] : ids).map((id: string) => String(id));
Expand Down Expand Up @@ -71,23 +73,21 @@ export const useJobSelection = (jobs: MlJobWithTimeRange[]) => {
}, [invalidIds]);

useEffect(() => {
// if there are no valid ids, warn and then select the first job
// if there are no valid ids, ask the user to provide job selection with the flyout
if (validIds.length === 0 && jobs.length > 0) {
toastNotifications.addWarning(
i18n.translate('xpack.ml.jobSelect.noJobsSelectedWarningMessage', {
defaultMessage: 'No jobs selected, auto selecting first job',
getJobSelection({ singleSelection: false })
.then(({ jobIds, time }) => {
const mlGlobalState = globalState?.ml || {};
mlGlobalState.jobIds = jobIds;

setGlobalState({
...{ ml: mlGlobalState },
...(time !== undefined ? { time } : {}),
});
})
);

const mlGlobalState = globalState?.ml || {};
mlGlobalState.jobIds = [jobs[0].job_id];

const time = getTimeRangeFromSelection(jobs, mlGlobalState.jobIds);

setGlobalState({
...{ ml: mlGlobalState },
...(time !== undefined ? { time } : {}),
});
.catch(() => {
// flyout closed without selection
});
}
}, [jobs, validIds, setGlobalState, globalState?.ml]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import React, { createContext, FC, useCallback, useMemo, useReducer } from 'react';
import { EuiPageContentBody } from '@elastic/eui';
import { EuiLoadingContent, EuiPageContentBody } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Route } from 'react-router-dom';
import type { AppMountParameters } from 'kibana/public';
Expand Down Expand Up @@ -94,16 +94,10 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps
defaultMessage: 'Machine Learning',
}),
icon: 'machineLearningApp',
items: [
{
id: '',
name: '',
items: useSideNavItems(activeRoute),
},
],
items: useSideNavItems(activeRoute),
}}
pageHeader={{
pageTitle: pageState.pageHeader,
pageTitle: pageState.pageHeader ?? <EuiLoadingContent lines={1} />,
rightSideItems: [...(activeRoute.enableDatePicker ? [<DatePickerWrapper />] : [])],
restrictWidth: false,
}}
Expand Down
Loading

0 comments on commit d66aa7a

Please sign in to comment.