Skip to content

Commit

Permalink
[ML] Use NavigationMenu without angularjs wrapper. (elastic#41343)
Browse files Browse the repository at this point in the history
Follow up to elastic#40830 and elastic#41054 in preparation for single metric viewer migration.
The previous PR introduced the navigation menu as a React component. This PR moves dependencies down from the angularjs wrapper directive directly to the React component so the component can also be used stand-alone without the angularjs wrapper.
For simple angularjs based HTML templates this stand-alone usage is also part of this PR. Unfortunately the PR turned out to be quite big due to: Most page react components had to be wrapped in another <Fragment> to allow the addition of <NavigationMenu> thus leading to large diffs for the components. All component code inside the <Fragment> was not touched though.
  • Loading branch information
walterra committed Jul 23, 2019
1 parent feb7757 commit 88b7ec0
Show file tree
Hide file tree
Showing 52 changed files with 1,874 additions and 1,738 deletions.
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/ml/public/access_denied/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<ml-nav-menu name="access-denied"></ml-nav-menu>
<ml-nav-menu name="access-denied" />
<div class="col-md-12">
<div class="euiSpacer euiSpacer--m"></div>
<div class="euiCallOut euiCallOut--danger">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,40 @@

import React, { Fragment, FC } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';

// @ts-ignore
import { isFullLicense } from '../../license/check_license';

import { TopNav } from './top_nav';
import { Tabs } from './tabs';

const tabSupport = [
'jobs',
'settings',
'data_frames',
'datavisualizer',
'filedatavisualizer',
'timeseriesexplorer',
'access-denied',
'explorer',
];

interface Props {
dateFormat: string;
disableLinks: boolean;
forceRefresh: () => void;
showTabs: boolean;
tabId: string;
timeHistory: any;
timefilter: any;
}

export const NavigationMenu: FC<Props> = ({
dateFormat,
disableLinks,
forceRefresh,
showTabs,
tabId,
timeHistory,
timefilter,
}) => (
<Fragment>
<EuiFlexGroup justifyContent="flexEnd" gutterSize="xs">
<EuiFlexItem grow={false}>
<TopNav
dateFormat={dateFormat}
timeHistory={timeHistory}
timefilter={timefilter}
forceRefresh={forceRefresh}
/>
</EuiFlexItem>
</EuiFlexGroup>
{showTabs && <Tabs tabId={tabId} disableLinks={disableLinks} />}
</Fragment>
);
export const NavigationMenu: FC<Props> = ({ tabId }) => {
const disableLinks = isFullLicense() === false;
const showTabs = tabSupport.includes(tabId);

return (
<Fragment>
<EuiFlexGroup justifyContent="flexEnd" gutterSize="xs">
<EuiFlexItem grow={false}>
<TopNav />
</EuiFlexItem>
</EuiFlexGroup>
{showTabs && <Tabs tabId={tabId} disableLinks={disableLinks} />}
</Fragment>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,28 @@

import React from 'react';
import ReactDOM from 'react-dom';
import { NavigationMenu } from './navigation_menu';
import { isFullLicense } from '../../license/check_license';
import { timeHistory } from 'ui/timefilter/time_history';

import { uiModules } from 'ui/modules';
import { timefilter } from 'ui/timefilter';
const module = uiModules.get('apps/ml');
import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service';

import 'ui/directives/kbn_href';
import chrome from 'ui/chrome';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';

import { NavigationMenuContext } from '../../util/context_utils';

import { NavigationMenu } from './navigation_menu';

module.directive('mlNavMenu', function (config) {
module.directive('mlNavMenu', function () {
return {
restrict: 'E',
transclude: true,
link: function (scope, element, attrs) {
const { name } = attrs;
let showTabs = false;

if (name === 'jobs' ||
name === 'settings' ||
name === 'data_frames' ||
name === 'datavisualizer' ||
name === 'filedatavisualizer' ||
name === 'timeseriesexplorer' ||
name === 'access-denied' ||
name === 'explorer') {
showTabs = true;
}

const props = {
dateFormat: config.get('dateFormat'),
disableLinks: (isFullLicense() === false),
showTabs,
tabId: name,
timeHistory,
timefilter,
forceRefresh: () => mlTimefilterRefresh$.next()
};

ReactDOM.render(React.createElement(NavigationMenu, props),
ReactDOM.render(
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<NavigationMenu tabId={attrs.name} />
</NavigationMenuContext.Provider>,
element[0]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,46 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FC, Fragment, useState, useEffect } from 'react';
import React, { FC, Fragment, useContext, useState, useEffect } from 'react';
import { EuiSuperDatePicker } from '@elastic/eui';
import { TimeHistory, TimeRange } from 'src/legacy/ui/public/timefilter/time_history';
import { Timefilter } from 'ui/timefilter';
import { TimeHistory, TimeRange } from 'ui/timefilter/time_history';

interface Props {
dateFormat: string;
forceRefresh: () => void;
timeHistory: TimeHistory;
timefilter: Timefilter;
}
import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service';
import { NavigationMenuContext } from '../../../util/context_utils';

interface Duration {
start: string;
end: string;
}

function getRecentlyUsedRanges(timeHistory: TimeHistory): Duration[] {
return timeHistory.get().map(({ from, to }: TimeRange) => {
return {
start: from,
end: to,
};
});
function getRecentlyUsedRangesFactory(timeHistory: TimeHistory) {
return function(): Duration[] {
return timeHistory.get().map(({ from, to }: TimeRange) => {
return {
start: from,
end: to,
};
});
};
}

export const TopNav: FC<Props> = ({ dateFormat, forceRefresh, timeHistory, timefilter }) => {
export const TopNav: FC = () => {
const navigationMenuContext = useContext(NavigationMenuContext);
const timefilter = navigationMenuContext.timefilter;
const getRecentlyUsedRanges = getRecentlyUsedRangesFactory(navigationMenuContext.timeHistory);

const [refreshInterval, setRefreshInterval] = useState(timefilter.getRefreshInterval());
const [time, setTime] = useState(timefilter.getTime());
const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges(timeHistory));
const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges());
const [isAutoRefreshSelectorEnabled, setIsAutoRefreshSelectorEnabled] = useState(
timefilter.isAutoRefreshSelectorEnabled
);
const [isTimeRangeSelectorEnabled, setIsTimeRangeSelectorEnabled] = useState(
timefilter.isTimeRangeSelectorEnabled
);

const dateFormat = navigationMenuContext.chrome.getUiSettingsClient().get('dateFormat');

useEffect(() => {
timefilter.on('refreshIntervalUpdate', timefilterUpdateListener);
timefilter.on('timeUpdate', timefilterUpdateListener);
Expand Down Expand Up @@ -70,7 +73,7 @@ export const TopNav: FC<Props> = ({ dateFormat, forceRefresh, timeHistory, timef
// Update timefilter for controllers listening for changes
timefilter.setTime(newTime);
setTime(newTime);
setRecentlyUsedRanges(getRecentlyUsedRanges(timeHistory));
setRecentlyUsedRanges(getRecentlyUsedRanges());
}

function updateInterval({
Expand Down Expand Up @@ -101,7 +104,7 @@ export const TopNav: FC<Props> = ({ dateFormat, forceRefresh, timeHistory, timef
isAutoRefreshOnly={!isTimeRangeSelectorEnabled}
refreshInterval={refreshInterval.value}
onTimeChange={updateFilter}
onRefresh={forceRefresh}
onRefresh={() => mlTimefilterRefresh$.next()}
onRefreshChange={updateInterval}
recentlyUsedRanges={recentlyUsedRanges}
dateFormat={dateFormat}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import uiChrome from 'ui/chrome';
const module = uiModules.get('apps/ml', ['react']);

import { I18nContext } from 'ui/i18n';
import chrome from 'ui/chrome';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { InjectorService } from '../../../../common/types/angular';

import { NavigationMenuContext } from '../../../util/context_utils';

import { Page } from './page';

module.directive('mlDataFrameAccessDenied', ($injector: InjectorService) => {
Expand All @@ -34,9 +39,14 @@ module.directive('mlDataFrameAccessDenied', ($injector: InjectorService) => {
kbnUrl.redirect('/data_frames');
};

const props = { goToKibana, retry };

ReactDOM.render(<I18nContext>{React.createElement(Page, props)}</I18nContext>, element[0]);
ReactDOM.render(
<I18nContext>
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<Page goToKibana={goToKibana} retry={retry} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { I18nProvider } from '@kbn/i18n/react';

import { Page } from './page';

jest.mock('../../../components/navigation_menu/navigation_menu', () => ({
NavigationMenu: () => <div id="mockNavigationMenu" />,
}));

afterEach(cleanup);

describe('Data Frame: Access denied <Page />', () => {
Expand Down
Loading

0 comments on commit 88b7ec0

Please sign in to comment.