mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ML] NavMenu conversion to React follow-up (#41054)
* remove old nav_menu direcory * Bit of cleanup of types and in datavis controller * replace angular service with importable observable * wrap tabs in links
This commit is contained in:
parent
5875be54e2
commit
01d8f93c76
11 changed files with 58 additions and 149 deletions
|
@ -1 +0,0 @@
|
|||
@import 'nav_menu'
|
|
@ -1,4 +0,0 @@
|
|||
.disabled-nav-link {
|
||||
color: $euiColorMediumShade;
|
||||
pointer-events: none;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<kbn-top-nav name="dashboard" config="topNavMenu">
|
||||
<div data-transclude-slots>
|
||||
<!-- Tabs -->
|
||||
<div data-transclude-slot="bottomRow">
|
||||
<div ng-if="showTabs" class="kuiLocalTabs" role="tablist">
|
||||
<a kbn-href="#/jobs" class="kuiLocalTab" role="tab" data-test-subj="mlTabJobManagement"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('jobs'), 'disabled-nav-link': disableLinks}">
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.jobManagementTabLinkText"
|
||||
i18n-default-message="Job Management"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/explorer" class="kuiLocalTab" role="tab" data-test-subj="mlTabAnomalyExplorer"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('explorer'), 'disabled-nav-link': disableLinks}">
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.anomalyExplorerTabLinkText"
|
||||
i18n-default-message="Anomaly Explorer"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/timeseriesexplorer" class="kuiLocalTab" role="tab" data-test-subj="mlTabSingleMetricViewer"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('timeseriesexplorer'), 'disabled-nav-link': disableLinks}">
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.singleMetricViewerTabLinkText"
|
||||
i18n-default-message="Single Metric Viewer"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/data_frames" class="kuiLocalTab" role="tab" data-test-subj="mlTabDataFrames"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('data_frame')}">
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.dataFrameTabLinkText"
|
||||
i18n-default-message="Data Frames"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/datavisualizer" class="kuiLocalTab" role="tab" data-test-subj="mlTabDataVisualizer"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('datavisualizer')}">
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.dataVisualizerTabLinkText"
|
||||
i18n-default-message="Data Visualizer"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/settings" class="kuiLocalTab" role="tab" data-test-subj="mlTabSettings"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('settings'), 'disabled-nav-link': disableLinks}">
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.settingsTabLinkText"
|
||||
i18n-default-message="Settings"
|
||||
></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</kbn-top-nav>
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* 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 template from './nav_menu.html';
|
||||
import { isFullLicense } from '../../license/check_license';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
import 'ui/directives/kbn_href';
|
||||
|
||||
module.directive('mlNavMenu', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
template,
|
||||
link: function (scope, el, attrs) {
|
||||
|
||||
// Tabs
|
||||
scope.name = attrs.name;
|
||||
|
||||
scope.showTabs = false;
|
||||
if (scope.name === 'jobs' ||
|
||||
scope.name === 'settings' ||
|
||||
scope.name === 'data_frame' ||
|
||||
scope.name === 'datavisualizer' ||
|
||||
scope.name === 'filedatavisualizer' ||
|
||||
scope.name === 'timeseriesexplorer' ||
|
||||
scope.name === 'access-denied' ||
|
||||
scope.name === 'explorer') {
|
||||
scope.showTabs = true;
|
||||
}
|
||||
scope.isActiveTab = function (path) {
|
||||
return scope.name === path;
|
||||
};
|
||||
|
||||
scope.disableLinks = (isFullLicense() === false);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -12,13 +12,13 @@ import { isFullLicense } from '../../license/check_license';
|
|||
import { timeHistory } from 'ui/timefilter/time_history';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { Subject } from 'rxjs';
|
||||
const module = uiModules.get('apps/ml');
|
||||
import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service';
|
||||
|
||||
import 'ui/directives/kbn_href';
|
||||
|
||||
|
||||
module.directive('mlNavMenu', function (config, mlTimefilterRefreshService) {
|
||||
module.directive('mlNavMenu', function (config) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
|
@ -44,7 +44,7 @@ module.directive('mlNavMenu', function (config, mlTimefilterRefreshService) {
|
|||
tabId: name,
|
||||
timeHistory,
|
||||
timefilter,
|
||||
forceRefresh: () => mlTimefilterRefreshService.next()
|
||||
forceRefresh: () => mlTimefilterRefresh$.next()
|
||||
};
|
||||
|
||||
ReactDOM.render(React.createElement(NavigationMenu, props),
|
||||
|
@ -57,7 +57,4 @@ module.directive('mlNavMenu', function (config, mlTimefilterRefreshService) {
|
|||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.service('mlTimefilterRefreshService', function () {
|
||||
return new Subject();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React, { FC, useState } from 'react';
|
||||
import { EuiTabs, EuiTab } from '@elastic/eui';
|
||||
import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
|
@ -15,10 +15,6 @@ interface Tab {
|
|||
disabled: boolean;
|
||||
}
|
||||
|
||||
interface TestSubjMap {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
disableLinks: boolean;
|
||||
tabId: string;
|
||||
|
@ -71,23 +67,20 @@ function getTabs(disableLinks: boolean): Tab[] {
|
|||
];
|
||||
}
|
||||
|
||||
const TAB_TEST_SUBJ_MAP: TestSubjMap = {
|
||||
jobs: 'mlTabJobManagement',
|
||||
explorer: 'mlTabAnomalyExplorer',
|
||||
timeseriesexplorer: 'mlTabSingleMetricViewer',
|
||||
data_frames: 'mlTabDataFrames',
|
||||
datavisualizer: 'mlTabDataVisualizer',
|
||||
settings: 'mlTabSettings',
|
||||
};
|
||||
|
||||
function moveToSelectedTab(selectedTabId: string) {
|
||||
window.location.href = `${chrome.getBasePath()}/app/ml#/${selectedTabId}`;
|
||||
enum TAB_TEST_SUBJECT {
|
||||
jobs = 'mlTabJobManagement',
|
||||
explorer = 'mlTabAnomalyExplorer',
|
||||
timeseriesexplorer = 'mlTabSingleMetricViewer',
|
||||
data_frames = 'mlTabDataFrames', // eslint-disable-line
|
||||
datavisualizer = 'mlTabDataVisualizer',
|
||||
settings = 'mlTabSettings',
|
||||
}
|
||||
|
||||
type TAB_TEST_SUBJECTS = keyof typeof TAB_TEST_SUBJECT;
|
||||
|
||||
export const Tabs: FC<Props> = ({ tabId, disableLinks }) => {
|
||||
const [selectedTabId, setSelectedTabId] = useState(tabId);
|
||||
function onSelectedTabChanged(id: string) {
|
||||
moveToSelectedTab(id);
|
||||
setSelectedTabId(id);
|
||||
}
|
||||
|
||||
|
@ -95,18 +88,26 @@ export const Tabs: FC<Props> = ({ tabId, disableLinks }) => {
|
|||
|
||||
return (
|
||||
<EuiTabs>
|
||||
{tabs.map((tab: Tab) => (
|
||||
<EuiTab
|
||||
className="mlNavigationMenu__tab"
|
||||
onClick={() => onSelectedTabChanged(tab.id)}
|
||||
isSelected={tab.id === selectedTabId}
|
||||
disabled={tab.disabled}
|
||||
key={`${tab.id}-key`}
|
||||
data-test-subj={TAB_TEST_SUBJ_MAP[tab.id]}
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
))}
|
||||
{tabs.map((tab: Tab) => {
|
||||
const id = tab.id;
|
||||
return (
|
||||
<EuiLink
|
||||
data-test-subj={TAB_TEST_SUBJECT[id as TAB_TEST_SUBJECTS]}
|
||||
href={`${chrome.getBasePath()}/app/ml#/${id}`}
|
||||
key={`${id}-key`}
|
||||
color="text"
|
||||
>
|
||||
<EuiTab
|
||||
className="mlNavigationMenu__tab"
|
||||
onClick={() => onSelectedTabChanged(id)}
|
||||
isSelected={id === selectedTabId}
|
||||
disabled={tab.disabled}
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
</EuiLink>
|
||||
);
|
||||
})}
|
||||
</EuiTabs>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,12 @@ interface Props {
|
|||
timefilter: Timefilter;
|
||||
}
|
||||
|
||||
function getRecentlyUsedRanges(timeHistory: TimeHistory): Array<{ start: string; end: string }> {
|
||||
interface Duration {
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
||||
|
||||
function getRecentlyUsedRanges(timeHistory: TimeHistory): Duration[] {
|
||||
return timeHistory.get().map(({ from, to }: TimeRange) => {
|
||||
return {
|
||||
start: from,
|
||||
|
@ -60,7 +65,7 @@ export const TopNav: FC<Props> = ({ dateFormat, forceRefresh, timeHistory, timef
|
|||
setIsTimeRangeSelectorEnabled(timefilter.isTimeRangeSelectorEnabled);
|
||||
}
|
||||
|
||||
function updateFilter({ start, end }: { start: string; end: string }) {
|
||||
function updateFilter({ start, end }: Duration) {
|
||||
const newTime = { from: start, to: end };
|
||||
// Update timefilter for controllers listening for changes
|
||||
timefilter.setTime(newTime);
|
||||
|
|
|
@ -33,6 +33,7 @@ import { loadCurrentIndexPattern, loadCurrentSavedSearch, timeBasedIndexCheck }
|
|||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||
import template from './datavisualizer.html';
|
||||
import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service';
|
||||
|
||||
uiRoutes
|
||||
.when('/jobs/new_job/datavisualizer', {
|
||||
|
@ -52,11 +53,16 @@ import { uiModules } from 'ui/modules';
|
|||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module
|
||||
.controller('MlDataVisualizerViewFields', function ($injector, $scope, $timeout, $window, Private, AppState, config) {
|
||||
.controller('MlDataVisualizerViewFields', function (
|
||||
$scope,
|
||||
$timeout,
|
||||
$window,
|
||||
Private,
|
||||
AppState,
|
||||
config) {
|
||||
|
||||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
const mlTimefilterRefreshService = $injector.get('mlTimefilterRefreshService');
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const {
|
||||
|
@ -152,11 +158,9 @@ module
|
|||
}
|
||||
|
||||
// Refresh the data when the time range is altered.
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', function () {
|
||||
refresh();
|
||||
});
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', refresh);
|
||||
|
||||
const timefilterRefreshServiceSub = mlTimefilterRefreshService.subscribe(refresh);
|
||||
const timefilterRefreshServiceSub = mlTimefilterRefresh$.subscribe(refresh);
|
||||
|
||||
$scope.$on('$destroy', () => {
|
||||
timefilterRefreshServiceSub.unsubscribe();
|
||||
|
|
|
@ -30,6 +30,7 @@ import { checkGetJobsPrivilege } from '../privilege/check_privilege';
|
|||
import { getIndexPatterns, loadIndexPatterns } from '../util/index_utils';
|
||||
import { MlTimeBuckets } from 'plugins/ml/util/ml_time_buckets';
|
||||
import { explorer$ } from './explorer_dashboard_service';
|
||||
import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service';
|
||||
import { mlFieldFormatService } from 'plugins/ml/services/field_format_service';
|
||||
import { mlJobService } from '../services/job_service';
|
||||
import { refreshIntervalWatcher } from '../util/refresh_interval_watcher';
|
||||
|
@ -71,7 +72,6 @@ module.controller('MlExplorerController', function (
|
|||
$injector.get('mlSelectSeverityService');
|
||||
|
||||
const mlJobSelectService = $injector.get('mlJobSelectService');
|
||||
const mlTimefilterRefreshService = $injector.get('mlTimefilterRefreshService');
|
||||
|
||||
// $scope should only contain what's actually still necessary for the angular part.
|
||||
// For the moment that's the job selector and the (hidden) filter bar.
|
||||
|
@ -204,7 +204,7 @@ module.controller('MlExplorerController', function (
|
|||
}
|
||||
});
|
||||
|
||||
const timefilterRefreshServiceSub = mlTimefilterRefreshService.subscribe(() => {
|
||||
const timefilterRefreshServiceSub = mlTimefilterRefresh$.subscribe(() => {
|
||||
if ($scope.jobSelectionUpdateInProgress === false) {
|
||||
explorer$.next({ action: EXPLORER_ACTION.RELOAD });
|
||||
}
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import './nav_menu';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export const mlTimefilterRefresh$ = new Subject();
|
|
@ -60,6 +60,7 @@ import { annotationsRefresh$ } from '../services/annotations_service';
|
|||
import { interval$ } from '../components/controls/select_interval/select_interval';
|
||||
import { severity$ } from '../components/controls/select_severity/select_severity';
|
||||
import { setGlobalState, getSelectedJobIds } from '../components/job_selector/job_select_service_utils';
|
||||
import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service';
|
||||
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
|
@ -93,7 +94,6 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
$injector.get('mlSelectIntervalService');
|
||||
$injector.get('mlSelectSeverityService');
|
||||
const mlJobSelectService = $injector.get('mlJobSelectService');
|
||||
const mlTimefilterRefreshService = $injector.get('mlTimefilterRefreshService');
|
||||
|
||||
$scope.timeFieldName = 'timestamp';
|
||||
timefilter.enableTimeRangeSelector();
|
||||
|
@ -712,7 +712,7 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
}
|
||||
});
|
||||
|
||||
const timefilterRefreshServiceSub = mlTimefilterRefreshService.subscribe($scope.refresh);
|
||||
const timefilterRefreshServiceSub = mlTimefilterRefresh$.subscribe($scope.refresh);
|
||||
|
||||
$scope.$on('$destroy', () => {
|
||||
refreshWatcher.cancel();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue