diff --git a/x-pack/legacy/plugins/ml/public/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/breadcrumbs.js index fdc8be67ce0cb..bdde734be7c1a 100644 --- a/x-pack/legacy/plugins/ml/public/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/breadcrumbs.js @@ -12,3 +12,24 @@ export const ML_BREADCRUMB = Object.freeze({ }), href: '#/' }); + +export const SETTINGS = Object.freeze({ + text: i18n.translate('xpack.ml.settingsBreadcrumbLabel', { + defaultMessage: 'Settings' + }), + href: '#/settings?' +}); + +export const ANOMALY_DETECTION_BREADCRUMB = Object.freeze({ + text: i18n.translate('xpack.ml.anomalyDetectionBreadcrumbLabel', { + defaultMessage: 'Anomaly Detection' + }), + href: '#/jobs?' +}); + +export const DATA_VISUALIZER_BREADCRUMB = Object.freeze({ + text: i18n.translate('xpack.ml.datavisualizerBreadcrumbLabel', { + defaultMessage: 'Data Visualizer' + }), + href: '#/datavisualizer?' +}); diff --git a/x-pack/legacy/plugins/ml/public/components/navigation_menu/_navigation_menu.scss b/x-pack/legacy/plugins/ml/public/components/navigation_menu/_navigation_menu.scss index 08dcbfc014499..957587d4ef278 100644 --- a/x-pack/legacy/plugins/ml/public/components/navigation_menu/_navigation_menu.scss +++ b/x-pack/legacy/plugins/ml/public/components/navigation_menu/_navigation_menu.scss @@ -1,7 +1,24 @@ .mlNavigationMenu__tab { padding-bottom: 0; + padding-left: 0px; + padding-right: 0px; + margin-left: $euiSizeM; +} + +.mlNavigationMenu__mainTab { + margin-left: $euiSizeM; + padding-bottom: 0; + font-weight: normal; } .mlNavigationMenu__topNav { padding-top: $euiSizeS; } + +.mlNavHorizontalRule { + margin: $euiSizeM 0 0 0; +} + +.mlSubTabs { + margin-top: $euiSizeS; +} diff --git a/x-pack/legacy/plugins/ml/public/components/navigation_menu/main_tabs.tsx b/x-pack/legacy/plugins/ml/public/components/navigation_menu/main_tabs.tsx new file mode 100644 index 0000000000000..3e72e50ec45c1 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/navigation_menu/main_tabs.tsx @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useState } from 'react'; +import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import chrome from 'ui/chrome'; +import { TabId } from './navigation_menu'; + +export interface Tab { + id: TabId; + name: any; + disabled: boolean; +} + +interface Props { + disableLinks: boolean; + tabId: TabId; +} + +function getTabs(disableLinks: boolean): Tab[] { + return [ + // { + // id: 'overview', + // name: i18n.translate('xpack.ml.navMenu.overviewTabLinkText', { + // defaultMessage: 'Overview', + // }), + // disabled: disableLinks, + // }, + { + id: 'anomaly_detection', + name: i18n.translate('xpack.ml.navMenu.anomalyDetectionTabLinkText', { + defaultMessage: 'Anomaly Detection', + }), + disabled: disableLinks, + }, + { + id: 'data_frames', + name: i18n.translate('xpack.ml.navMenu.dataFrameTabLinkText', { + defaultMessage: 'Transforms', + }), + disabled: false, + }, + { + id: 'data_frame_analytics', + name: i18n.translate('xpack.ml.navMenu.dataFrameAnalyticsTabLinkText', { + defaultMessage: 'Data Frame Analytics', + }), + disabled: disableLinks, + }, + { + id: 'datavisualizer', + name: i18n.translate('xpack.ml.navMenu.dataVisualizerTabLinkText', { + defaultMessage: 'Data Visualizer', + }), + disabled: false, + }, + ]; +} +interface TabData { + testSubject: string; + pathId?: string; +} + +const TAB_DATA: Record = { + // overview: { testSubject: 'mlTabOverview', pathId: 'overview' }, + anomaly_detection: { testSubject: 'mlTabAnomalyDetection', pathId: 'jobs' }, + data_frames: { testSubject: 'mlTabDataFrames' }, + data_frame_analytics: { testSubject: 'mlTabDataFrameAnalytics' }, + datavisualizer: { testSubject: 'mlTabDataVisualizer' }, +}; + +export const MainTabs: FC = ({ tabId, disableLinks }) => { + const [selectedTabId, setSelectedTabId] = useState(tabId); + function onSelectedTabChanged(id: string) { + setSelectedTabId(id); + } + + const tabs = getTabs(disableLinks); + + return ( + + {tabs.map((tab: Tab) => { + const id = tab.id; + const testSubject = TAB_DATA[id].testSubject; + const defaultPathId = TAB_DATA[id].pathId || id; + return ( + + onSelectedTabChanged(id)} + isSelected={id === selectedTabId} + disabled={tab.disabled} + > + {tab.name} + + + ); + })} + + ); +}; diff --git a/x-pack/legacy/plugins/ml/public/components/navigation_menu/navigation_menu.tsx b/x-pack/legacy/plugins/ml/public/components/navigation_menu/navigation_menu.tsx index 60fd9d219c5bc..d61972bc0d850 100644 --- a/x-pack/legacy/plugins/ml/public/components/navigation_menu/navigation_menu.tsx +++ b/x-pack/legacy/plugins/ml/public/components/navigation_menu/navigation_menu.tsx @@ -5,42 +5,54 @@ */ import React, { Fragment, FC } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; // @ts-ignore import { isFullLicense } from '../../license/check_license'; import { TopNav } from './top_nav'; +import { MainTabs } from './main_tabs'; import { Tabs } from './tabs'; -const tabSupport = [ - 'jobs', - 'settings', - 'data_frames', - 'data_frame_analytics', - 'datavisualizer', - 'filedatavisualizer', - 'timeseriesexplorer', - 'access-denied', - 'explorer', -]; +export type TabId = string; +type TabSupport = Record; + +const tabSupport: TabSupport = { + // overview: null, + jobs: 'anomaly_detection', + settings: 'anomaly_detection', + data_frames: null, + data_frame_analytics: null, + datavisualizer: null, + filedatavisualizer: null, + timeseriesexplorer: 'anomaly_detection', + 'access-denied': null, + explorer: 'anomaly_detection', +}; interface Props { - tabId: string; + tabId: TabId; } export const NavigationMenu: FC = ({ tabId }) => { const disableLinks = isFullLicense() === false; - const showTabs = tabSupport.includes(tabId); + const showTabs = typeof tabSupport[tabId] !== 'undefined'; + const mainTabId = tabSupport[tabId] || tabId; + // show horizontal rule if there are no subtabs + const showHorizontalRule = tabSupport[tabId] === null; return ( - + + + {showTabs && } + - {showTabs && } + {showHorizontalRule && } + {showTabs && } ); }; diff --git a/x-pack/legacy/plugins/ml/public/components/navigation_menu/tabs.tsx b/x-pack/legacy/plugins/ml/public/components/navigation_menu/tabs.tsx index 4a9b912a92ea6..787f44f7cef90 100644 --- a/x-pack/legacy/plugins/ml/public/components/navigation_menu/tabs.tsx +++ b/x-pack/legacy/plugins/ml/public/components/navigation_menu/tabs.tsx @@ -8,94 +8,77 @@ import React, { FC, useState } from 'react'; import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; - -interface Tab { - id: string; - name: any; - disabled: boolean; -} +import { Tab } from './main_tabs'; +import { TabId } from './navigation_menu'; interface Props { disableLinks: boolean; - tabId: string; + mainTabId: TabId; + tabId: TabId; } -function getTabs(disableLinks: boolean): Tab[] { - return [ - { - id: 'jobs', - name: i18n.translate('xpack.ml.navMenu.jobManagementTabLinkText', { - defaultMessage: 'Job Management', - }), - disabled: disableLinks, - }, - { - id: 'explorer', - name: i18n.translate('xpack.ml.navMenu.anomalyExplorerTabLinkText', { - defaultMessage: 'Anomaly Explorer', - }), - disabled: disableLinks, - }, - { - id: 'timeseriesexplorer', - name: i18n.translate('xpack.ml.navMenu.singleMetricViewerTabLinkText', { - defaultMessage: 'Single Metric Viewer', - }), - disabled: disableLinks, - }, - { - id: 'data_frames', - name: i18n.translate('xpack.ml.navMenu.dataFrameTabLinkText', { - defaultMessage: 'Transforms', - }), - disabled: false, - }, - { - id: 'data_frame_analytics', - name: i18n.translate('xpack.ml.navMenu.dataFrameAnalyticsTabLinkText', { - defaultMessage: 'Analytics', - }), - disabled: disableLinks, - }, - { - id: 'datavisualizer', - name: i18n.translate('xpack.ml.navMenu.dataVisualizerTabLinkText', { - defaultMessage: 'Data Visualizer', - }), - disabled: false, - }, - { - id: 'settings', - name: i18n.translate('xpack.ml.navMenu.settingsTabLinkText', { - defaultMessage: 'Settings', - }), - disabled: disableLinks, - }, - ]; +function getTabs(tabId: TabId, disableLinks: boolean): Tab[] { + const TAB_MAP: Record = { + // overview: [], + datavisualizer: [], + data_frames: [], + data_frame_analytics: [], + anomaly_detection: [ + { + id: 'jobs', + name: i18n.translate('xpack.ml.navMenu.jobManagementTabLinkText', { + defaultMessage: 'Job Management', + }), + disabled: disableLinks, + }, + { + id: 'explorer', + name: i18n.translate('xpack.ml.navMenu.anomalyExplorerTabLinkText', { + defaultMessage: 'Anomaly Explorer', + }), + disabled: disableLinks, + }, + { + id: 'timeseriesexplorer', + name: i18n.translate('xpack.ml.navMenu.singleMetricViewerTabLinkText', { + defaultMessage: 'Single Metric Viewer', + }), + disabled: disableLinks, + }, + { + id: 'settings', + name: i18n.translate('xpack.ml.navMenu.settingsTabLinkText', { + defaultMessage: 'Settings', + }), + disabled: disableLinks, + }, + ], + }; + + return TAB_MAP[tabId]; } enum TAB_TEST_SUBJECT { + // overview = 'mlOverview', jobs = 'mlTabJobManagement', explorer = 'mlTabAnomalyExplorer', timeseriesexplorer = 'mlTabSingleMetricViewer', - data_frames = 'mlTabDataFrames', // eslint-disable-line - data_frame_analytics = 'mlTabDataFrameAnalytics', // eslint-disable-line datavisualizer = 'mlTabDataVisualizer', settings = 'mlTabSettings', } type TAB_TEST_SUBJECTS = keyof typeof TAB_TEST_SUBJECT; -export const Tabs: FC = ({ tabId, disableLinks }) => { +export const Tabs: FC = ({ tabId, mainTabId, disableLinks }) => { const [selectedTabId, setSelectedTabId] = useState(tabId); function onSelectedTabChanged(id: string) { setSelectedTabId(id); } - const tabs = getTabs(disableLinks); + const tabs = getTabs(mainTabId, disableLinks); return ( - + {tabs.map((tab: Tab) => { const id = tab.id; return ( diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/breadcrumbs.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/breadcrumbs.ts index 6e6cf9e107e10..0b03f00cd9782 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/breadcrumbs.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/breadcrumbs.ts @@ -14,7 +14,7 @@ export function getDataFrameAnalyticsBreadcrumbs() { ML_BREADCRUMB, { text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.dataFrameLabel', { - defaultMessage: 'Analytics', + defaultMessage: 'Data Frame Analytics', }), href: '', }, diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/breadcrumbs.ts b/x-pack/legacy/plugins/ml/public/datavisualizer/breadcrumbs.ts index 49e1608733ea3..03c447c2ee923 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/breadcrumbs.ts +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/breadcrumbs.ts @@ -5,10 +5,10 @@ */ // @ts-ignore -import { ML_BREADCRUMB } from '../breadcrumbs'; +import { ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB } from '../breadcrumbs'; export function getDataVisualizerBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. - return [ML_BREADCRUMB]; + return [ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB]; } diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/breadcrumbs.js index 40319b2ba4d24..ae87699bcc974 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/breadcrumbs.js @@ -5,14 +5,22 @@ */ -import { ML_BREADCRUMB } from '../../breadcrumbs'; +import { ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB } from '../../breadcrumbs'; +import { i18n } from '@kbn/i18n'; export function getFileDataVisualizerBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. return [ - ML_BREADCRUMB + ML_BREADCRUMB, + DATA_VISUALIZER_BREADCRUMB, + { + text: i18n.translate('xpack.ml.dataVisualizer.fileBasedLabel', { + defaultMessage: 'File' + }), + href: '' + } ]; } diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/breadcrumbs.ts b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/breadcrumbs.ts index d708ddd7fbbbe..03c66335ddb6c 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/breadcrumbs.ts +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/breadcrumbs.ts @@ -4,11 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { ML_BREADCRUMB } from '../../breadcrumbs'; +import { i18n } from '@kbn/i18n'; +import { + ML_BREADCRUMB, + DATA_VISUALIZER_BREADCRUMB, + // @ts-ignore +} from '../../breadcrumbs'; export function getDataVisualizerBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. - return [ML_BREADCRUMB]; + return [ + ML_BREADCRUMB, + DATA_VISUALIZER_BREADCRUMB, + { + text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.indexLabel', { + defaultMessage: 'Index', + }), + href: '', + }, + ]; } diff --git a/x-pack/legacy/plugins/ml/public/explorer/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/explorer/breadcrumbs.js index f602febfb0fbe..9f70505fc8dca 100644 --- a/x-pack/legacy/plugins/ml/public/explorer/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/explorer/breadcrumbs.js @@ -5,14 +5,22 @@ */ -import { ML_BREADCRUMB } from '../breadcrumbs'; +import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB } from '../breadcrumbs'; +import { i18n } from '@kbn/i18n'; export function getAnomalyExplorerBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. return [ - ML_BREADCRUMB + ML_BREADCRUMB, + ANOMALY_DETECTION_BREADCRUMB, + { + text: i18n.translate('xpack.ml.anomalyDetection.anomalyExplorerLabel', { + defaultMessage: 'Anomaly Explorer' + }), + href: '' + } ]; } diff --git a/x-pack/legacy/plugins/ml/public/jobs/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/jobs/breadcrumbs.js index 3b6f275b44743..d066a524d70aa 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/jobs/breadcrumbs.js @@ -5,7 +5,7 @@ */ -import { ML_BREADCRUMB } from '../breadcrumbs'; +import { ML_BREADCRUMB, DATA_VISUALIZER_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB } from '../breadcrumbs'; import { i18n } from '@kbn/i18n'; @@ -13,13 +13,21 @@ export function getJobManagementBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. return [ - ML_BREADCRUMB + ML_BREADCRUMB, + ANOMALY_DETECTION_BREADCRUMB, + { + text: i18n.translate('xpack.ml.anomalyDetection.jobManagementLabel', { + defaultMessage: 'Job Management' + }), + href: '' + } ]; } export function getCreateJobBreadcrumbs() { return [ ML_BREADCRUMB, + ANOMALY_DETECTION_BREADCRUMB, { text: i18n.translate('xpack.ml.jobsBreadcrumbs.createJobLabel', { defaultMessage: 'Create job' @@ -90,6 +98,7 @@ export function getCreateRecognizerJobBreadcrumbs($routeParams) { export function getDataVisualizerIndexOrSearchBreadcrumbs() { return [ ML_BREADCRUMB, + DATA_VISUALIZER_BREADCRUMB, { text: i18n.translate('xpack.ml.jobsBreadcrumbs.selectIndexOrSearchLabel', { defaultMessage: 'Select index or search' diff --git a/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js index a8671a8f4bd16..e4e37b67c1872 100644 --- a/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js @@ -5,7 +5,7 @@ */ -import { ML_BREADCRUMB } from '../breadcrumbs'; +import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS } from '../breadcrumbs'; import { i18n } from '@kbn/i18n'; @@ -13,7 +13,9 @@ export function getSettingsBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. return [ - ML_BREADCRUMB + ML_BREADCRUMB, + ANOMALY_DETECTION_BREADCRUMB, + SETTINGS ]; } diff --git a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/breadcrumbs.js index e0e5e5d1431a5..fd32a7c4d04b1 100644 --- a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/breadcrumbs.js @@ -5,14 +5,23 @@ */ -import { ML_BREADCRUMB } from '../breadcrumbs'; +import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB } from '../breadcrumbs'; +import { i18n } from '@kbn/i18n'; export function getSingleMetricViewerBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. return [ - ML_BREADCRUMB + ML_BREADCRUMB, + ANOMALY_DETECTION_BREADCRUMB, + { + text: i18n.translate('xpack.ml.anomalyDetection.singleMetricViewerLabel', { + defaultMessage: 'Single Metric Viewer' + }), + href: '' + } + ]; } diff --git a/x-pack/test/functional/apps/machine_learning/pages.ts b/x-pack/test/functional/apps/machine_learning/pages.ts index b3537abb4044b..9f4a94c134134 100644 --- a/x-pack/test/functional/apps/machine_learning/pages.ts +++ b/x-pack/test/functional/apps/machine_learning/pages.ts @@ -41,6 +41,12 @@ export default function({ getService }: FtrProviderContext) { await ml.singleMetricViewer.assertSingleMetricViewerEmptyListMessageExsist(); }); + it('loads the settings page', async () => { + await ml.navigation.navigateToSettings(); + await ml.settings.assertSettingsCalendarLinkExists(); + await ml.settings.assertSettingsFilterlistLinkExists(); + }); + it('loads the data frame page', async () => { await ml.navigation.navigateToDataFrames(); await ml.dataFrames.assertDataFrameEmptyListMessageExists(); @@ -51,11 +57,5 @@ export default function({ getService }: FtrProviderContext) { await ml.dataVisualizer.assertDataVisualizerImportDataCardExists(); await ml.dataVisualizer.assertDataVisualizerIndexDataCardExists(); }); - - it('loads the settings page', async () => { - await ml.navigation.navigateToSettings(); - await ml.settings.assertSettingsCalendarLinkExists(); - await ml.settings.assertSettingsFilterlistLinkExists(); - }); }); } diff --git a/x-pack/test/functional/services/machine_learning/navigation.ts b/x-pack/test/functional/services/machine_learning/navigation.ts index 12f97712ccb11..652a82239efc6 100644 --- a/x-pack/test/functional/services/machine_learning/navigation.ts +++ b/x-pack/test/functional/services/machine_learning/navigation.ts @@ -40,6 +40,10 @@ export function MachineLearningNavigationProvider({ await this.navigateToArea('mlTabSingleMetricViewer', 'mlPageSingleMetricViewer'); }, + async navigateToSettings() { + await this.navigateToArea('mlTabSettings', 'mlPageSettings'); + }, + async navigateToDataFrames() { await this.navigateToArea('mlTabDataFrames', 'mlPageDataFrame'); }, @@ -47,9 +51,5 @@ export function MachineLearningNavigationProvider({ async navigateToDataVisualizer() { await this.navigateToArea('mlTabDataVisualizer', 'mlPageDataVisualizerSelector'); }, - - async navigateToSettings() { - await this.navigateToArea('mlTabSettings', 'mlPageSettings'); - }, }; }