[ML] Use NavigationMenu without angularjs wrapper. (#41343) (#41764)

Follow up to #40830 and #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.
This commit is contained in:
Walter Rafelsberger 2019-07-23 15:45:57 +02:00 committed by GitHub
parent feb77574db
commit b6093548df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 1874 additions and 1738 deletions

View file

@ -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">

View file

@ -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>
);
};

View file

@ -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';
module.directive('mlNavMenu', function (config) {
import { NavigationMenu } from './navigation_menu';
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]
);

View file

@ -4,36 +4,37 @@
* 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
);
@ -41,6 +42,8 @@ export const TopNav: FC<Props> = ({ dateFormat, forceRefresh, timeHistory, timef
timefilter.isTimeRangeSelectorEnabled
);
const dateFormat = navigationMenuContext.chrome.getUiSettingsClient().get('dateFormat');
useEffect(() => {
timefilter.on('refreshIntervalUpdate', timefilterUpdateListener);
timefilter.on('timeUpdate', timefilterUpdateListener);
@ -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({
@ -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}

View file

@ -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) => {
@ -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]);

View file

@ -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 />', () => {

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { SFC } from 'react';
import React, { FC, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
@ -24,70 +24,75 @@ import {
EuiTitle,
} from '@elastic/eui';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
interface PageProps {
goToKibana: () => void;
retry: () => void;
}
export const Page: SFC<PageProps> = ({ goToKibana, retry }) => (
<EuiPage>
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.accessDeniedTitle"
defaultMessage="Access denied"
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="m" />
<EuiCallOut
title={i18n.translate('xpack.ml.dataframe.noPermissionToAccessMLLabel', {
defaultMessage: 'You need permission to access Data Frames',
})}
color="danger"
iconType="cross"
>
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.ml.dataframe.noGrantedPrivilegesDescription"
defaultMessage="You must have the privileges granted in the {kibanaUserParam} and {dataFrameUserParam} roles.{br}Your system admin can set these roles on the Management User page."
values={{
kibanaUserParam: <span className="text-monospace">kibana_user</span>,
dataFrameUserParam: (
<span className="text-monospace">data_frame_transforms_user</span>
),
br: <br />,
}}
/>
</p>
</EuiText>
</EuiCallOut>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButton fill onClick={goToKibana} size="s">
<FormattedMessage
id="xpack.ml.dataframe.accessDenied.backToKibanaHomeButtonLabel"
defaultMessage="Back to Kibana home"
/>
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill onClick={retry} size="s">
<FormattedMessage
id="xpack.ml.dataframe.accessDenied.retryButtonLabel"
defaultMessage="Retry"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
export const Page: FC<PageProps> = ({ goToKibana, retry }) => (
<Fragment>
<NavigationMenu tabId="access-denied" />
<EuiPage>
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.accessDeniedTitle"
defaultMessage="Access denied"
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="m" />
<EuiCallOut
title={i18n.translate('xpack.ml.dataframe.noPermissionToAccessMLLabel', {
defaultMessage: 'You need permission to access Data Frames',
})}
color="danger"
iconType="cross"
>
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.ml.dataframe.noGrantedPrivilegesDescription"
defaultMessage="You must have the privileges granted in the {kibanaUserParam} and {dataFrameUserParam} roles.{br}Your system admin can set these roles on the Management User page."
values={{
kibanaUserParam: <span className="text-monospace">kibana_user</span>,
dataFrameUserParam: (
<span className="text-monospace">data_frame_transforms_user</span>
),
br: <br />,
}}
/>
</p>
</EuiText>
</EuiCallOut>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButton fill onClick={goToKibana} size="s">
<FormattedMessage
id="xpack.ml.dataframe.accessDenied.backToKibanaHomeButtonLabel"
defaultMessage="Back to Kibana home"
/>
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill onClick={retry} size="s">
<FormattedMessage
id="xpack.ml.dataframe.accessDenied.retryButtonLabel"
defaultMessage="Retry"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
</Fragment>
);

View file

@ -9,7 +9,7 @@ import uiRoutes from 'ui/routes';
// @ts-ignore
import { getDataFrameBreadcrumbs } from '../../breadcrumbs';
const template = `<ml-nav-menu name="access-denied" /><ml-data-frame-access-denied />`;
const template = `<ml-data-frame-access-denied />`;
uiRoutes.when('/data_frames/access-denied', {
template,

View file

@ -7,6 +7,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import chrome from 'ui/chrome';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
@ -15,6 +16,7 @@ import { IndexPattern } from 'ui/index_patterns';
import { I18nContext } from 'ui/i18n';
import { IPrivate } from 'ui/private';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { InjectorService } from '../../../../common/types/angular';
// @ts-ignore
@ -26,6 +28,7 @@ type CreateSearchItems = () => {
combinedQuery: any;
};
import { NavigationMenuContext } from '../../../util/context_utils';
import { KibanaContext } from '../../common';
import { Page } from './page';
@ -56,9 +59,11 @@ module.directive('mlNewDataFrame', ($injector: InjectorService) => {
ReactDOM.render(
<I18nContext>
<KibanaContext.Provider value={kibanaContext}>
{React.createElement(Page)}
</KibanaContext.Provider>
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<KibanaContext.Provider value={kibanaContext}>
<Page />
</KibanaContext.Provider>
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { SFC } from 'react';
import React, { FC, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
@ -20,39 +20,43 @@ import {
EuiTitle,
} from '@elastic/eui';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
import { Wizard } from './components/wizard';
export const Page: SFC = () => (
<EuiPage>
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.transformsWizard.newDataFrameTitle"
defaultMessage="New data frame"
/>
<span>&nbsp;</span>
<EuiBetaBadge
label={i18n.translate('xpack.ml.dataframe.transformsWizard.betaBadgeLabel', {
defaultMessage: `Beta`,
})}
tooltipContent={i18n.translate(
'xpack.ml.dataframe.transformsWizard.betaBadgeTooltipContent',
{
defaultMessage: `Data frames are a beta feature. We'd love to hear your feedback.`,
}
)}
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="l" />
<Wizard />
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
export const Page: FC = () => (
<Fragment>
<NavigationMenu tabId="new_data_frame" />
<EuiPage>
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.transformsWizard.newDataFrameTitle"
defaultMessage="New data frame"
/>
<span>&nbsp;</span>
<EuiBetaBadge
label={i18n.translate('xpack.ml.dataframe.transformsWizard.betaBadgeLabel', {
defaultMessage: `Beta`,
})}
tooltipContent={i18n.translate(
'xpack.ml.dataframe.transformsWizard.betaBadgeTooltipContent',
{
defaultMessage: `Data frames are a beta feature. We'd love to hear your feedback.`,
}
)}
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="l" />
<Wizard />
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
</Fragment>
);

View file

@ -21,7 +21,7 @@ import {
getDataFrameIndexOrSearchBreadcrumbs,
} from '../../breadcrumbs';
const wizardTemplate = `<ml-nav-menu name="new_data_frame" /><ml-new-data-frame />`;
const wizardTemplate = `<ml-new-data-frame />`;
uiRoutes.when('/data_frames/new_transform/step/pivot?', {
template: wizardTemplate,

View file

@ -1,62 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Data Frame: Job List <Page /> Minimal initialization 1`] = `
<EuiPage
data-test-subj="mlPageDataFrame"
restrictWidth={false}
>
<EuiPageBody
<Fragment>
<NavigationMenu
tabId="data_frames"
/>
<EuiPage
data-test-subj="mlPageDataFrame"
restrictWidth={false}
>
<EuiPageContentHeader
responsive={true}
<EuiPageBody
restrictWidth={false}
>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
defaultMessage="Data frame transforms"
id="xpack.ml.dataframe.transformList.dataFrameTitle"
values={Object {}}
/>
<span>
 
</span>
<EuiBetaBadge
label="Beta"
tooltipContent="Data frames are a beta feature. We'd love to hear your feedback."
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiFlexGroup
alignItems="center"
>
<EuiFlexItem>
<RefreshTransformListButton
isLoading={false}
onClick={[Function]}
/>
</EuiFlexItem>
<EuiFlexItem>
<CreateTransformButton />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer
size="l"
/>
<EuiPanel
grow={true}
hasShadow={false}
paddingSize="m"
<EuiPageContentHeader
responsive={true}
>
<DataFrameTransformList />
</EuiPanel>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
defaultMessage="Data frame transforms"
id="xpack.ml.dataframe.transformList.dataFrameTitle"
values={Object {}}
/>
<span>
 
</span>
<EuiBetaBadge
label="Beta"
tooltipContent="Data frames are a beta feature. We'd love to hear your feedback."
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiFlexGroup
alignItems="center"
>
<EuiFlexItem>
<RefreshTransformListButton
isLoading={false}
onClick={[Function]}
/>
</EuiFlexItem>
<EuiFlexItem>
<CreateTransformButton />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer
size="l"
/>
<EuiPanel
grow={true}
hasShadow={false}
paddingSize="m"
>
<DataFrameTransformList />
</EuiPanel>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
</Fragment>
`;

View file

@ -10,8 +10,12 @@ import ReactDOM from 'react-dom';
// @ts-ignore
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
import chrome from 'ui/chrome';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { I18nContext } from 'ui/i18n';
import { NavigationMenuContext } from '../../../util/context_utils';
import { Page } from './page';
module.directive('mlDataFramePage', () => {
@ -19,7 +23,14 @@ module.directive('mlDataFramePage', () => {
scope: {},
restrict: 'E',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
ReactDOM.render(<I18nContext>{React.createElement(Page)}</I18nContext>, element[0]);
ReactDOM.render(
<I18nContext>
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<Page />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);
element.on('$destroy', () => {
ReactDOM.unmountComponentAtNode(element[0]);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { SFC, useState } from 'react';
import React, { FC, Fragment, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
@ -23,59 +23,63 @@ import {
EuiTitle,
} from '@elastic/eui';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
import { useRefreshTransformList } from '../../common';
import { CreateTransformButton } from './components/create_transform_button';
import { DataFrameTransformList } from './components/transform_list';
import { RefreshTransformListButton } from './components/refresh_transform_list_button';
export const Page: SFC = () => {
export const Page: FC = () => {
const [isLoading, setIsLoading] = useState(false);
const { refresh } = useRefreshTransformList({ isLoading: setIsLoading });
return (
<EuiPage data-test-subj="mlPageDataFrame">
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.transformList.dataFrameTitle"
defaultMessage="Data frame transforms"
/>
<span>&nbsp;</span>
<EuiBetaBadge
label={i18n.translate('xpack.ml.dataframe.transformList.betaBadgeLabel', {
defaultMessage: `Beta`,
})}
tooltipContent={i18n.translate(
'xpack.ml.dataframe.transformList.betaBadgeTooltipContent',
{
defaultMessage: `Data frames are a beta feature. We'd love to hear your feedback.`,
}
)}
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<RefreshTransformListButton onClick={refresh} isLoading={isLoading} />
</EuiFlexItem>
<EuiFlexItem>
<CreateTransformButton />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="l" />
<EuiPanel>
<DataFrameTransformList />
</EuiPanel>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
<Fragment>
<NavigationMenu tabId="data_frames" />
<EuiPage data-test-subj="mlPageDataFrame">
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle>
<h1>
<FormattedMessage
id="xpack.ml.dataframe.transformList.dataFrameTitle"
defaultMessage="Data frame transforms"
/>
<span>&nbsp;</span>
<EuiBetaBadge
label={i18n.translate('xpack.ml.dataframe.transformList.betaBadgeLabel', {
defaultMessage: `Beta`,
})}
tooltipContent={i18n.translate(
'xpack.ml.dataframe.transformList.betaBadgeTooltipContent',
{
defaultMessage: `Data frames are a beta feature. We'd love to hear your feedback.`,
}
)}
/>
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<RefreshTransformListButton onClick={refresh} isLoading={isLoading} />
</EuiFlexItem>
<EuiFlexItem>
<CreateTransformButton />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiSpacer size="l" />
<EuiPanel>
<DataFrameTransformList />
</EuiPanel>
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>
</Fragment>
);
};

View file

@ -15,7 +15,7 @@ import { loadIndexPatterns } from '../../../util/index_utils';
// @ts-ignore
import { getDataFrameBreadcrumbs } from '../../breadcrumbs';
const template = `<ml-nav-menu name="data_frames" /><ml-data-frame-page />`;
const template = `<ml-data-frame-page />`;
uiRoutes.when('/data_frames/?', {
template,

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="datavisualizer"></ml-nav-menu>
<ml-nav-menu name="datavisualizer" />
<ml-chart-tooltip></ml-chart-tooltip>
<ml-datavisualizer class="data-visualizer-container">
<div ng-controller="MlDataVisualizerViewFields" >

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { Fragment } from 'react';
import {
EuiButton,
@ -22,7 +22,12 @@ import {
import { isFullLicense } from '../../license/check_license';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
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 '../../components/navigation_menu/navigation_menu';
function startTrialDescription() {
return (
@ -32,159 +37,151 @@ function startTrialDescription() {
defaultMessage="To experience the full Machine Learning features that a {platinumSubscriptionLink} offers, start a 30-day trial."
values={{
platinumSubscriptionLink: (
<EuiLink
href="https://www.elastic.co/subscriptions"
target="_blank"
>
<EuiLink href="https://www.elastic.co/subscriptions" target="_blank">
<FormattedMessage
id="xpack.ml.datavisualizer.startTrial.platinumSubscriptionTitle"
defaultMessage="Platinum subscription"
/>
</EuiLink>
)
),
}}
/>
</span>
);
}
export const DatavisualizerSelector = injectI18n(function (props) {
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
const startTrialVisible = (isFullLicense() === false);
const startTrialVisible = isFullLicense() === false;
return (
<EuiPage restrictWidth={1000}>
<EuiPageBody>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<h2>
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<NavigationMenu tabId="datavisualizer" />
<EuiPage restrictWidth={1000}>
<EuiPageBody>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiTitle size="l">
<h2>
<FormattedMessage
id="xpack.ml.datavisualizer.selector.dataVisualizerTitle"
defaultMessage="Data Visualizer"
/>
</h2>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiText color="subdued">
<FormattedMessage
id="xpack.ml.datavisualizer.selector.dataVisualizerTitle"
defaultMessage="Data Visualizer"
/>
</h2>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiText color="subdued">
<FormattedMessage
id="xpack.ml.datavisualizer.selector.dataVisualizerDescription"
defaultMessage="The Machine Learning Data Visualizer tool helps you understand your data,
id="xpack.ml.datavisualizer.selector.dataVisualizerDescription"
defaultMessage="The Machine Learning Data Visualizer tool helps you understand your data,
by analyzing the metrics and fields in a log file or an existing Elasticsearch index."
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
<EuiFlexGroup justifyContent="spaceAround" gutterSize="xl">
<EuiFlexItem>
<EuiCard
icon={<EuiIcon size="xxl" type="addDataApp" />}
title={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.importDataTitle"
defaultMessage="Import data"
/>
}
description={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.importDataDescription"
defaultMessage="Import data from a log file. You can upload files up to 100 MB."
/>
}
betaBadgeLabel={props.intl.formatMessage({
id: 'xpack.ml.datavisualizer.selector.experimentalBadgeLabel',
defaultMessage: 'Experimental'
})}
betaBadgeTooltipContent={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.experimentalBadgeTooltipLabel"
defaultMessage="Experimental feature. We'd love to hear your feedback."
/>
}
footer={
<EuiButton
target="_self"
href="#/filedatavisualizer"
>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
<EuiFlexGroup justifyContent="spaceAround" gutterSize="xl">
<EuiFlexItem>
<EuiCard
icon={<EuiIcon size="xxl" type="addDataApp" />}
title={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.uploadFileButtonLabel"
defaultMessage="Upload file"
id="xpack.ml.datavisualizer.selector.importDataTitle"
defaultMessage="Import data"
/>
</EuiButton>
}
data-test-subj="mlDataVisualizerCardImportData"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
icon={<EuiIcon size="xxl" type="dataVisualizer" />}
title={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.selectIndexPatternTitle"
defaultMessage="Select an index pattern"
/>
}
description={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.selectIndexPatternDescription"
defaultMessage="Visualize the data in an existing Elasticsearch index."
/>
}
footer={
<EuiButton
target="_self"
href="#datavisualizer_index_select"
>
}
description={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.selectIndexButtonLabel"
defaultMessage="Select index"
id="xpack.ml.datavisualizer.selector.importDataDescription"
defaultMessage="Import data from a log file. You can upload files up to 100 MB."
/>
</EuiButton>
}
data-test-subj="mlDataVisualizerCardIndexData"
/>
</EuiFlexItem>
</EuiFlexGroup>
{startTrialVisible === true &&
<React.Fragment>
<EuiSpacer size="xxl" />
<EuiSpacer size="xxl" />
<EuiFlexGroup justifyContent="spaceAround" gutterSize="xl">
<EuiFlexItem grow={false} style={{ width: '600px' }}>
<EuiCard
title={
}
betaBadgeLabel={props.intl.formatMessage({
id: 'xpack.ml.datavisualizer.selector.experimentalBadgeLabel',
defaultMessage: 'Experimental',
})}
betaBadgeTooltipContent={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.experimentalBadgeTooltipLabel"
defaultMessage="Experimental feature. We'd love to hear your feedback."
/>
}
footer={
<EuiButton target="_self" href="#/filedatavisualizer">
<FormattedMessage
id="xpack.ml.datavisualizer.selector.startTrialTitle"
defaultMessage="Start trial"
id="xpack.ml.datavisualizer.selector.uploadFileButtonLabel"
defaultMessage="Upload file"
/>
}
description={startTrialDescription()}
footer={
<EuiButton
target="_blank"
href="kibana#/management/elasticsearch/license_management/home"
>
</EuiButton>
}
data-test-subj="mlDataVisualizerCardImportData"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
icon={<EuiIcon size="xxl" type="dataVisualizer" />}
title={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.selectIndexPatternTitle"
defaultMessage="Select an index pattern"
/>
}
description={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.selectIndexPatternDescription"
defaultMessage="Visualize the data in an existing Elasticsearch index."
/>
}
footer={
<EuiButton target="_self" href="#datavisualizer_index_select">
<FormattedMessage
id="xpack.ml.datavisualizer.selector.selectIndexButtonLabel"
defaultMessage="Select index"
/>
</EuiButton>
}
data-test-subj="mlDataVisualizerCardIndexData"
/>
</EuiFlexItem>
</EuiFlexGroup>
{startTrialVisible === true && (
<Fragment>
<EuiSpacer size="xxl" />
<EuiSpacer size="xxl" />
<EuiFlexGroup justifyContent="spaceAround" gutterSize="xl">
<EuiFlexItem grow={false} style={{ width: '600px' }}>
<EuiCard
title={
<FormattedMessage
id="xpack.ml.datavisualizer.selector.startTrialButtonLabel"
id="xpack.ml.datavisualizer.selector.startTrialTitle"
defaultMessage="Start trial"
/>
</EuiButton>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
</React.Fragment>
}
</EuiPageBody>
</EuiPage>
}
description={startTrialDescription()}
footer={
<EuiButton
target="_blank"
href="kibana#/management/elasticsearch/license_management/home"
>
<FormattedMessage
id="xpack.ml.datavisualizer.selector.startTrialButtonLabel"
defaultMessage="Start trial"
/>
</EuiButton>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
</Fragment>
)}
</EuiPageBody>
</EuiPage>
</NavigationMenuContext.Provider>
);
});

View file

@ -18,7 +18,6 @@ import uiRoutes from 'ui/routes';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="datavisualizer" />
<datavisualizer-selector data-test-subj="mlPageDataVisualizerSelector" />
`;

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="explorer"></ml-nav-menu>
<ml-nav-menu name="explorer" />
<ml-chart-tooltip></ml-chart-tooltip>
<div class="ml-explorer" ng-controller="MlExplorerController" data-test-subj="mlPageAnomalyExplorer" >

View file

@ -7,13 +7,22 @@
import { FileDataVisualizerView } from './components/file_datavisualizer_view';
import React from 'react';
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 '../components/navigation_menu/navigation_menu';
export function FileDataVisualizerPage({ indexPatterns, kibanaConfig }) {
timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
return (
<FileDataVisualizerView indexPatterns={indexPatterns} kibanaConfig={kibanaConfig} />
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<NavigationMenu tabId="datavisualizer" />
<FileDataVisualizerView indexPatterns={indexPatterns} kibanaConfig={kibanaConfig} />
</NavigationMenuContext.Provider>
);
}

View file

@ -24,7 +24,6 @@ import uiRoutes from 'ui/routes';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="datavisualizer" />
<file-datavisualizer-page />
`;

View file

@ -17,10 +17,14 @@ import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { getJobManagementBreadcrumbs } from 'plugins/ml/jobs/breadcrumbs';
import { loadNewJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
import { NavigationMenuContext } from '../../util/context_utils';
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
const template = `<ml-nav-menu name="jobs" /><jobs-page data-test-subj="mlPageJobManagement" />`;
const template = `<jobs-page data-test-subj="mlPageJobManagement" />`;
uiRoutes
.when('/jobs/?', {
@ -45,7 +49,9 @@ module.directive('jobsPage', function () {
link: (scope, element) => {
ReactDOM.render(
<I18nContext>
{React.createElement(JobsPage, { angularWrapperScope: scope })}
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<JobsPage angularWrapperScope={scope} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

View file

@ -4,11 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment } from 'react';
import { NavigationMenu } from '../../components/navigation_menu/navigation_menu';
import { JobsListView } from './components/jobs_list_view';
import React from 'react';
export const JobsPage = (props) => (
<JobsListView {...props} />
<Fragment>
<NavigationMenu tabId="jobs" />
<JobsListView {...props} />
</Fragment>
);

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job_advanced"></ml-nav-menu>
<ml-nav-menu name="new_job_advanced" />
<ml-new-job class="ml-new-job euiPage euiPage--widthIsNotRestricted">
<ml-message-bar></ml-message-bar>
<div ng-controller="MlNewJob" class="euiPageBody">

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job_multi_metric"></ml-nav-menu>
<ml-nav-menu name="new_job_multi_metric" />
<ml-new-job-multi-metric>
<ml-message-bar></ml-message-bar>
<div ng-controller="MlCreateMultiMetricJob" class="multi-metric-job-container">

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job_population"></ml-nav-menu>
<ml-nav-menu name="new_job_population" />
<ml-chart-tooltip></ml-chart-tooltip>
<ml-new-job-population>
<ml-message-bar></ml-message-bar>

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job_multi_metric"></ml-nav-menu>
<ml-nav-menu name="new_job_multi_metric" />
<ml-new-job-recognizer>
<ml-message-bar></ml-message-bar>
<div ng-controller="MlCreateRecognizerJobs" class="recognizer-job-container">

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job_single_metric"></ml-nav-menu>
<ml-nav-menu name="new_job_single_metric" />
<ml-new-job-single-metric>
<ml-message-bar></ml-message-bar>
<div ng-controller="MlCreateSingleMetricJob" class="single-metric-job-container">

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job"></ml-nav-menu>
<ml-nav-menu name="new_job" />
<ml-new-job class="index-or-saved-search-selection">
<ml-message-bar></ml-message-bar>
<div class='kuiViewContent kuiViewContent--constrainedWidth kuiViewContentItem' >

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="new_job"></ml-nav-menu>
<ml-nav-menu name="new_job" />
<ml-new-job class="job-type-gallery">
<ml-message-bar></ml-message-bar>
<div ng-controller="MlNewJobStepJobType">

View file

@ -1,76 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Settings Renders settings page 1`] = `
<EuiPage
className="mlSettingsPage"
data-test-subj="mlPageSettings"
restrictWidth={false}
>
<EuiPageBody
className="mlSettingsPage__body"
restrictWidth={false}
>
<EuiPageContent
className="mlSettingsPage__content"
horizontalPosition="center"
panelPaddingSize="l"
>
<EuiPageContentHeader
responsive={true}
>
<EuiTitle>
<h2>
<FormattedMessage
defaultMessage="Job Management"
id="xpack.ml.settings.jobManagementTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiFlexGroup
gutterSize="xl"
>
<EuiFlexItem
grow={false}
>
<EuiButtonEmpty
color="primary"
data-test-subj="ml_calendar_mng_button"
href="undefined/app/ml#/settings/calendars_list"
iconSide="left"
isDisabled={false}
size="l"
type="button"
>
<FormattedMessage
defaultMessage="Calendar management"
id="xpack.ml.settings.calendarManagementButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<EuiButtonEmpty
color="primary"
data-test-subj="ml_filter_lists_button"
href="undefined/app/ml#/settings/filter_lists"
iconSide="left"
isDisabled={false}
size="l"
type="button"
>
<FormattedMessage
defaultMessage="Filter Lists"
id="xpack.ml.settings.filterListsButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
`;

View file

@ -1,40 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NewCalendar Renders new calendar form 1`] = `
<EuiPage
className="mlCalendarEditForm"
restrictWidth={false}
>
<EuiPageContent
className="mlCalendarEditForm__content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
<Fragment>
<NavigationMenu
tabId="settings"
/>
<EuiPage
className="mlCalendarEditForm"
restrictWidth={false}
>
<InjectIntl(CalendarForm)
calendarId=""
canCreateCalendar={true}
canDeleteCalendar={true}
description=""
eventsList={Array []}
groupIds={Array []}
isEdit={false}
isNewCalendarIdValid={true}
jobIds={Array []}
onCalendarIdChange={[Function]}
onCreate={[Function]}
onCreateGroupOption={[Function]}
onDescriptionChange={[Function]}
onEdit={[Function]}
onEventDelete={[Function]}
onGroupSelection={[Function]}
onJobSelection={[Function]}
saving={false}
selectedGroupOptions={Array []}
selectedJobOptions={Array []}
showImportModal={[Function]}
showNewEventModal={[Function]}
/>
</EuiPageContent>
</EuiPage>
<EuiPageContent
className="mlCalendarEditForm__content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<InjectIntl(CalendarForm)
calendarId=""
canCreateCalendar={true}
canDeleteCalendar={true}
description=""
eventsList={Array []}
groupIds={Array []}
isEdit={false}
isNewCalendarIdValid={true}
jobIds={Array []}
onCalendarIdChange={[Function]}
onCreate={[Function]}
onCreateGroupOption={[Function]}
onDescriptionChange={[Function]}
onEdit={[Function]}
onEventDelete={[Function]}
onGroupSelection={[Function]}
onJobSelection={[Function]}
saving={false}
selectedGroupOptions={Array []}
selectedJobOptions={Array []}
showImportModal={[Function]}
showNewEventModal={[Function]}
/>
</EuiPageContent>
</EuiPage>
</Fragment>
`;

View file

@ -17,13 +17,16 @@ import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check
import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
import { getCreateCalendarBreadcrumbs, getEditCalendarBreadcrumbs } from '../../breadcrumbs';
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { I18nContext } from 'ui/i18n';
import { NavigationMenuContext } from '../../../util/context_utils';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="settings" />
<ml-new-calendar />
`;
@ -63,7 +66,9 @@ module.directive('mlNewCalendar', function ($route) {
ReactDOM.render(
<I18nContext>
{React.createElement(NewCalendar, props)}
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<NewCalendar {...props} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

View file

@ -6,11 +6,11 @@
import React, {
Component
} from 'react';
import React, { Component, Fragment } from 'react';
import { PropTypes } from 'prop-types';
import { injectI18n } from '@kbn/i18n/react';
import {
EuiPage,
EuiPageContent,
@ -18,14 +18,16 @@ import {
} from '@elastic/eui';
import chrome from 'ui/chrome';
import { toastNotifications } from 'ui/notify';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
import { getCalendarSettingsData, validateCalendarId } from './utils';
import { CalendarForm } from './calendar_form/';
import { NewEventModal } from './new_event_modal/';
import { ImportModal } from './import_modal';
import { ml } from '../../../services/ml_api_service';
import { toastNotifications } from 'ui/notify';
import { injectI18n } from '@kbn/i18n/react';
export const NewCalendar = injectI18n(class NewCalendar extends Component {
static propTypes = {
@ -333,39 +335,42 @@ export const NewCalendar = injectI18n(class NewCalendar extends Component {
}
return (
<EuiPage className="mlCalendarEditForm">
<EuiPageContent
className="mlCalendarEditForm__content"
verticalPosition="center"
horizontalPosition="center"
>
<CalendarForm
calendarId={selectedCalendar ? selectedCalendar.calendar_id : formCalendarId}
canCreateCalendar={this.props.canCreateCalendar}
canDeleteCalendar={this.props.canDeleteCalendar}
description={selectedCalendar ? selectedCalendar.description : description}
eventsList={events}
groupIds={groupIdOptions}
isEdit={selectedCalendar !== undefined}
isNewCalendarIdValid={(selectedCalendar || isNewCalendarIdValid === null) ? true : isNewCalendarIdValid}
jobIds={jobIdOptions}
onCalendarIdChange={this.onCalendarIdChange}
onCreate={this.onCreate}
onDescriptionChange={this.onDescriptionChange}
onEdit={this.onEdit}
onEventDelete={this.onEventDelete}
onGroupSelection={this.onGroupSelection}
showImportModal={this.showImportModal}
onJobSelection={this.onJobSelection}
saving={saving}
selectedGroupOptions={selectedGroupOptions}
selectedJobOptions={selectedJobOptions}
onCreateGroupOption={this.onCreateGroupOption}
showNewEventModal={this.showNewEventModal}
/>
</EuiPageContent>
{modal}
</EuiPage>
<Fragment>
<NavigationMenu tabId="settings" />
<EuiPage className="mlCalendarEditForm">
<EuiPageContent
className="mlCalendarEditForm__content"
verticalPosition="center"
horizontalPosition="center"
>
<CalendarForm
calendarId={selectedCalendar ? selectedCalendar.calendar_id : formCalendarId}
canCreateCalendar={this.props.canCreateCalendar}
canDeleteCalendar={this.props.canDeleteCalendar}
description={selectedCalendar ? selectedCalendar.description : description}
eventsList={events}
groupIds={groupIdOptions}
isEdit={selectedCalendar !== undefined}
isNewCalendarIdValid={(selectedCalendar || isNewCalendarIdValid === null) ? true : isNewCalendarIdValid}
jobIds={jobIdOptions}
onCalendarIdChange={this.onCalendarIdChange}
onCreate={this.onCreate}
onDescriptionChange={this.onDescriptionChange}
onEdit={this.onEdit}
onEventDelete={this.onEventDelete}
onGroupSelection={this.onGroupSelection}
showImportModal={this.showImportModal}
onJobSelection={this.onJobSelection}
saving={saving}
selectedGroupOptions={selectedGroupOptions}
selectedJobOptions={selectedJobOptions}
onCreateGroupOption={this.onCreateGroupOption}
showNewEventModal={this.showNewEventModal}
/>
</EuiPageContent>
{modal}
</EuiPage>
</Fragment>
);
}
});

View file

@ -4,13 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../../../components/navigation_menu/navigation_menu', () => ({
NavigationMenu: () => <div id="mockNavigationMenu" />
}));
jest.mock('../../../privilege/check_privilege', () => ({
checkPermission: () => true
}));
jest.mock('../../../license/check_license', () => ({
hasLicenseExpired: () => false
hasLicenseExpired: () => false,
isFullLicense: () => false
}));
jest.mock('../../../privilege/get_privileges', () => ({
getPrivileges: () => {}
@ -18,9 +20,6 @@ jest.mock('../../../privilege/get_privileges', () => ({
jest.mock('../../../ml_nodes_check/check_ml_nodes', () => ({
mlNodesAvailable: () => true
}));
jest.mock('ui/chrome', () => ({
getBasePath: jest.fn()
}));
jest.mock('../../../services/ml_api_service', () => ({
ml: {
calendars: () => {

View file

@ -1,69 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CalendarsList Renders calendar list with calendars 1`] = `
<EuiPage
className="mlCalendarList"
restrictWidth={false}
>
<EuiPageContent
className="mlCalendarList__content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
<Fragment>
<NavigationMenu
tabId="settings"
/>
<EuiPage
className="mlCalendarList"
restrictWidth={false}
>
<CalendarsListHeader
refreshCalendars={[Function]}
totalCount={2}
/>
<InjectIntl(CalendarsListTable)
calendarsList={
Array [
Object {
"calendar_id": "farequote-calendar",
"description": "test ",
"events": Array [
Object {
"calendar_id": "farequote-calendar",
"description": "Downtime feb 9 2017 10:10 to 10:30",
"end_time": 1486657800000,
"event_id": "Ee-YgGcBxHgQWEhCO_xj",
"start_time": 1486656600000,
},
],
"events_length": 1,
"job_ids": Array [
"farequote",
],
"job_ids_string": "farequote",
},
Object {
"calendar_id": "this-is-a-new-calendar",
"description": "new calendar",
"events": Array [
Object {
"calendar_id": "this-is-a-new-calendar",
"description": "New event!",
"end_time": 1544162400000,
"event_id": "ehWKhGcBqHkXuWNrIrSV",
"start_time": 1544076000000,
},
],
"events_length": 1,
"job_ids": Array [
"test",
],
"job_ids_string": "test",
},
]
}
canCreateCalendar={true}
canDeleteCalendar={true}
itemsSelected={false}
loading={false}
mlNodesAvailable={true}
onDeleteClick={[Function]}
setSelectedCalendarList={[Function]}
/>
</EuiPageContent>
</EuiPage>
<EuiPageContent
className="mlCalendarList__content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<CalendarsListHeader
refreshCalendars={[Function]}
totalCount={2}
/>
<InjectIntl(CalendarsListTable)
calendarsList={
Array [
Object {
"calendar_id": "farequote-calendar",
"description": "test ",
"events": Array [
Object {
"calendar_id": "farequote-calendar",
"description": "Downtime feb 9 2017 10:10 to 10:30",
"end_time": 1486657800000,
"event_id": "Ee-YgGcBxHgQWEhCO_xj",
"start_time": 1486656600000,
},
],
"events_length": 1,
"job_ids": Array [
"farequote",
],
"job_ids_string": "farequote",
},
Object {
"calendar_id": "this-is-a-new-calendar",
"description": "new calendar",
"events": Array [
Object {
"calendar_id": "this-is-a-new-calendar",
"description": "New event!",
"end_time": 1544162400000,
"event_id": "ehWKhGcBqHkXuWNrIrSV",
"start_time": 1544076000000,
},
],
"events_length": 1,
"job_ids": Array [
"test",
],
"job_ids_string": "test",
},
]
}
canCreateCalendar={true}
canDeleteCalendar={true}
itemsSelected={false}
loading={false}
mlNodesAvailable={true}
onDeleteClick={[Function]}
setSelectedCalendarList={[Function]}
/>
</EuiPageContent>
</EuiPage>
</Fragment>
`;

View file

@ -6,9 +6,7 @@
import React, {
Component
} from 'react';
import React, { Component, Fragment } from 'react';
import { PropTypes } from 'prop-types';
import {
@ -19,6 +17,7 @@ import {
EUI_MODAL_CONFIRM_BUTTON,
} from '@elastic/eui';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
import { CalendarsListHeader } from './header';
import { CalendarsListTable } from './table/';
import { ml } from '../../../services/ml_api_service';
@ -147,29 +146,32 @@ export const CalendarsList = injectI18n(class CalendarsList extends Component {
}
return (
<EuiPage className="mlCalendarList">
<EuiPageContent
className="mlCalendarList__content"
verticalPosition="center"
horizontalPosition="center"
>
<CalendarsListHeader
totalCount={calendars.length}
refreshCalendars={this.loadCalendars}
/>
<CalendarsListTable
loading={loading}
calendarsList={this.addRequiredFieldsToList(calendars)}
onDeleteClick={this.showDestroyModal}
canCreateCalendar={canCreateCalendar}
canDeleteCalendar={canDeleteCalendar}
mlNodesAvailable={nodesAvailable}
setSelectedCalendarList={this.setSelectedCalendarList}
itemsSelected={selectedForDeletion.length > 0}
/>
</EuiPageContent>
{destroyModal}
</EuiPage>
<Fragment>
<NavigationMenu tabId="settings" />
<EuiPage className="mlCalendarList">
<EuiPageContent
className="mlCalendarList__content"
verticalPosition="center"
horizontalPosition="center"
>
<CalendarsListHeader
totalCount={calendars.length}
refreshCalendars={this.loadCalendars}
/>
<CalendarsListTable
loading={loading}
calendarsList={this.addRequiredFieldsToList(calendars)}
onDeleteClick={this.showDestroyModal}
canCreateCalendar={canCreateCalendar}
canDeleteCalendar={canDeleteCalendar}
mlNodesAvailable={nodesAvailable}
setSelectedCalendarList={this.setSelectedCalendarList}
itemsSelected={selectedForDeletion.length > 0}
/>
</EuiPageContent>
{destroyModal}
</EuiPage>
</Fragment>
);
}
});

View file

@ -4,13 +4,21 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ml } from '../../../services/ml_api_service';
import { CalendarsList } from './calendars_list';
jest.mock('../../../components/navigation_menu/navigation_menu', () => ({
NavigationMenu: () => <div id="mockNavigationMenu" />
}));
jest.mock('../../../privilege/check_privilege', () => ({
checkPermission: () => true
}));
jest.mock('../../../license/check_license', () => ({
hasLicenseExpired: () => false
hasLicenseExpired: () => false,
isFullLicense: () => false
}));
jest.mock('../../../privilege/get_privileges', () => ({
getPrivileges: () => {}
@ -18,9 +26,6 @@ jest.mock('../../../privilege/get_privileges', () => ({
jest.mock('../../../ml_nodes_check/check_ml_nodes', () => ({
mlNodesAvailable: () => true
}));
jest.mock('ui/chrome', () => ({
getBasePath: jest.fn()
}));
jest.mock('../../../services/ml_api_service', () => ({
ml: {
calendars: () => {
@ -30,12 +35,6 @@ jest.mock('../../../services/ml_api_service', () => ({
}
}));
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ml } from '../../../services/ml_api_service';
import { CalendarsList } from './calendars_list';
const testingState = {
loading: false,
calendars: [

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import 'ngreact';
import React from 'react';
import ReactDOM from 'react-dom';
@ -17,27 +16,28 @@ import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
import { getCalendarManagementBreadcrumbs } from '../../breadcrumbs';
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { I18nContext } from 'ui/i18n';
import { NavigationMenuContext } from '../../../util/context_utils';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="settings" />
<ml-calendars-list />
`;
uiRoutes
.when('/settings/calendars_list', {
template,
k7Breadcrumbs: getCalendarManagementBreadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
mlNodeCount: getMlNodeCount,
}
});
uiRoutes.when('/settings/calendars_list', {
template,
k7Breadcrumbs: getCalendarManagementBreadcrumbs,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
mlNodeCount: getMlNodeCount,
},
});
import { CalendarsList } from './calendars_list';
@ -54,10 +54,12 @@ module.directive('mlCalendarsList', function () {
ReactDOM.render(
<I18nContext>
{React.createElement(CalendarsList, props)}
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<CalendarsList {...props} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);
}
},
};
});

View file

@ -9,8 +9,6 @@ import 'ngreact';
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nContext } from 'ui/i18n';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
@ -20,11 +18,16 @@ import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/che
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { EditFilterList } from './edit_filter_list';
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { I18nContext } from 'ui/i18n';
import { NavigationMenuContext } from '../../../util/context_utils';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="settings" />
<ml-edit-filter-list />
`;
@ -62,7 +65,9 @@ module.directive('mlEditFilterList', function ($route) {
ReactDOM.render(
<I18nContext>
{React.createElement(EditFilterList, props)}
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<EditFilterList {...props} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

View file

@ -11,9 +11,7 @@
*/
import PropTypes from 'prop-types';
import React, {
Component
} from 'react';
import React, { Component, Fragment } from 'react';
import {
EuiButton,
@ -33,6 +31,7 @@ import { toastNotifications } from 'ui/notify';
import { EditFilterListHeader } from './header';
import { EditFilterListToolbar } from './toolbar';
import { ItemsGrid } from '../../../components/items_grid';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
import {
isValidFilterListId,
saveFilterList
@ -309,71 +308,74 @@ export const EditFilterList = injectI18n(class extends Component {
const totalItemCount = (items !== undefined) ? items.length : 0;
return (
<EuiPage className="ml-edit-filter-lists">
<EuiPageContent
className="ml-edit-filter-lists-content"
verticalPosition="center"
horizontalPosition="center"
>
<EditFilterListHeader
canCreateFilter={canCreateFilter}
filterId={this.props.filterId}
newFilterId={newFilterId}
isNewFilterIdInvalid={isNewFilterIdInvalid}
updateNewFilterId={this.updateNewFilterId}
description={description}
updateDescription={this.updateDescription}
totalItemCount={totalItemCount}
usedBy={loadedFilter.used_by}
/>
<EditFilterListToolbar
canCreateFilter={canCreateFilter}
canDeleteFilter={canDeleteFilter}
onSearchChange={this.onSearchChange}
addItems={this.addItems}
deleteSelectedItems={this.deleteSelectedItems}
selectedItemCount={selectedItems.length}
/>
<EuiSpacer size="xl" />
<ItemsGrid
totalItemCount={totalItemCount}
items={matchingItems}
selectedItems={selectedItems}
itemsPerPage={itemsPerPage}
setItemsPerPage={this.setItemsPerPage}
setItemSelected={this.setItemSelected}
activePage={activePage}
setActivePage={this.setActivePage}
/>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={returnToFiltersList}
>
<FormattedMessage
id="xpack.ml.settings.filterLists.editFilterList.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={this.save}
disabled={(saveInProgress === true) ||
<Fragment>
<NavigationMenu tabId="settings" />
<EuiPage className="ml-edit-filter-lists">
<EuiPageContent
className="ml-edit-filter-lists-content"
verticalPosition="center"
horizontalPosition="center"
>
<EditFilterListHeader
canCreateFilter={canCreateFilter}
filterId={this.props.filterId}
newFilterId={newFilterId}
isNewFilterIdInvalid={isNewFilterIdInvalid}
updateNewFilterId={this.updateNewFilterId}
description={description}
updateDescription={this.updateDescription}
totalItemCount={totalItemCount}
usedBy={loadedFilter.used_by}
/>
<EditFilterListToolbar
canCreateFilter={canCreateFilter}
canDeleteFilter={canDeleteFilter}
onSearchChange={this.onSearchChange}
addItems={this.addItems}
deleteSelectedItems={this.deleteSelectedItems}
selectedItemCount={selectedItems.length}
/>
<EuiSpacer size="xl" />
<ItemsGrid
totalItemCount={totalItemCount}
items={matchingItems}
selectedItems={selectedItems}
itemsPerPage={itemsPerPage}
setItemsPerPage={this.setItemsPerPage}
setItemSelected={this.setItemSelected}
activePage={activePage}
setActivePage={this.setActivePage}
/>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={returnToFiltersList}
>
<FormattedMessage
id="xpack.ml.settings.filterLists.editFilterList.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={this.save}
disabled={(saveInProgress === true) ||
(isNewFilterIdInvalid === true) ||
(canCreateFilter === false)
}
fill
>
<FormattedMessage
id="xpack.ml.settings.filterLists.editFilterList.saveButtonLabel"
defaultMessage="Save"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
}
fill
>
<FormattedMessage
id="xpack.ml.settings.filterLists.editFilterList.saveButtonLabel"
defaultMessage="Save"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
</Fragment>
);
}
});

View file

@ -4,6 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../../../components/navigation_menu/navigation_menu', () => ({
NavigationMenu: () => <div id="mockNavigationMenu" />
}));
// Define the required mocks used for loading, saving and validating the filter list.
jest.mock('./utils', () => ({
isValidFilterListId: () => true,
@ -19,7 +23,7 @@ const mockTestFilter = {
items: ['google.com', 'google.co.uk', 'elastic.co', 'youtube.com'],
used_by: {
detectors: ['high info content'],
jobs: ['dns_exfiltration']
jobs: ['dns_exfiltration'],
},
};
jest.mock('../../../services/ml_api_service', () => ({
@ -27,12 +31,11 @@ jest.mock('../../../services/ml_api_service', () => ({
filters: {
filters: () => {
return Promise.resolve(mockTestFilter);
}
}
}
},
},
},
}));
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
@ -40,14 +43,11 @@ import { EditFilterList } from './edit_filter_list';
const props = {
canCreateFilter: true,
canDeleteFilter: true
canDeleteFilter: true,
};
function prepareEditTest() {
const wrapper = shallowWithIntl(
<EditFilterList.WrappedComponent {...props}/>
);
const wrapper = shallowWithIntl(<EditFilterList.WrappedComponent {...props} />);
// Cannot find a way to generate the snapshot after the Promise in the mock ml.filters
// has resolved.
@ -61,11 +61,8 @@ function prepareEditTest() {
}
describe('EditFilterList', () => {
test('renders the edit page for a new filter list and updates ID', () => {
const wrapper = shallowWithIntl(
<EditFilterList.WrappedComponent {...props}/>
);
const wrapper = shallowWithIntl(<EditFilterList.WrappedComponent {...props} />);
expect(wrapper).toMatchSnapshot();
const instance = wrapper.instance();
@ -114,5 +111,4 @@ describe('EditFilterList', () => {
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -1,41 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Filter Lists renders a list of filters 1`] = `
<EuiPage
className="ml-list-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-list-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
<Fragment>
<NavigationMenu
tabId="settings"
/>
<EuiPage
className="ml-list-filter-lists"
restrictWidth={false}
>
<FilterListsHeader
refreshFilterLists={[Function]}
totalCount={1}
/>
<FilterListsTable
canCreateFilter={true}
canDeleteFilter={true}
filterLists={
Array [
Object {
"description": "List of known safe domains",
"filter_id": "safe_domains",
"item_count": 500,
"used_by": Object {
"jobs": Array [
"dns_exfiltration",
],
<EuiPageContent
className="ml-list-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<FilterListsHeader
refreshFilterLists={[Function]}
totalCount={1}
/>
<FilterListsTable
canCreateFilter={true}
canDeleteFilter={true}
filterLists={
Array [
Object {
"description": "List of known safe domains",
"filter_id": "safe_domains",
"item_count": 500,
"used_by": Object {
"jobs": Array [
"dns_exfiltration",
],
},
},
},
]
}
refreshFilterLists={[Function]}
selectedFilterLists={Array []}
setSelectedFilterLists={[Function]}
/>
</EuiPageContent>
</EuiPage>
]
}
refreshFilterLists={[Function]}
selectedFilterLists={Array []}
setSelectedFilterLists={[Function]}
/>
</EuiPageContent>
</EuiPage>
</Fragment>
`;

View file

@ -9,8 +9,6 @@ import 'ngreact';
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nContext } from 'ui/i18n';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['react']);
@ -20,11 +18,16 @@ import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/che
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { FilterLists } from './filter_lists';
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
import { I18nContext } from 'ui/i18n';
import { NavigationMenuContext } from '../../../util/context_utils';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="settings" />
<ml-filter-lists />
`;
@ -52,7 +55,9 @@ module.directive('mlFilterLists', function () {
ReactDOM.render(
<I18nContext>
{React.createElement(FilterLists, props)}
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }}>
<FilterLists {...props} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

View file

@ -9,9 +9,7 @@
* React table for displaying a table of filter lists.
*/
import React, {
Component
} from 'react';
import React, { Component, Fragment } from 'react';
import { PropTypes } from 'prop-types';
import {
@ -23,6 +21,8 @@ import { injectI18n } from '@kbn/i18n/react';
import { toastNotifications } from 'ui/notify';
import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu';
import { FilterListsHeader } from './header';
import { FilterListsTable } from './table';
import { ml } from '../../../services/ml_api_service';
@ -88,26 +88,29 @@ export const FilterLists = injectI18n(class extends Component {
const { canCreateFilter, canDeleteFilter } = this.props;
return (
<EuiPage className="ml-list-filter-lists">
<EuiPageContent
className="ml-list-filter-lists-content"
verticalPosition="center"
horizontalPosition="center"
>
<FilterListsHeader
totalCount={filterLists.length}
refreshFilterLists={this.refreshFilterLists}
/>
<FilterListsTable
canCreateFilter={canCreateFilter}
canDeleteFilter={canDeleteFilter}
filterLists={filterLists}
selectedFilterLists={selectedFilterLists}
setSelectedFilterLists={this.setSelectedFilterLists}
refreshFilterLists={this.refreshFilterLists}
/>
</EuiPageContent>
</EuiPage>
<Fragment>
<NavigationMenu tabId="settings" />
<EuiPage className="ml-list-filter-lists">
<EuiPageContent
className="ml-list-filter-lists-content"
verticalPosition="center"
horizontalPosition="center"
>
<FilterListsHeader
totalCount={filterLists.length}
refreshFilterLists={this.refreshFilterLists}
/>
<FilterListsTable
canCreateFilter={canCreateFilter}
canDeleteFilter={canDeleteFilter}
filterLists={filterLists}
selectedFilterLists={selectedFilterLists}
setSelectedFilterLists={this.setSelectedFilterLists}
refreshFilterLists={this.refreshFilterLists}
/>
</EuiPageContent>
</EuiPage>
</Fragment>
);
}
});

View file

@ -4,9 +4,16 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { FilterLists } from './filter_lists';
jest.mock('../../../components/navigation_menu/navigation_menu', () => ({
NavigationMenu: () => <div id="mockNavigationMenu" />
}));
jest.mock('../../../privilege/check_privilege', () => ({
checkPermission: () => true
checkPermission: () => true,
}));
// Mock the call for loading the list of filters.
@ -23,28 +30,19 @@ jest.mock('../../../services/ml_api_service', () => ({
filters: {
filtersStats: () => {
return Promise.resolve([mockTestFilter]);
}
}
}
},
},
},
}));
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { FilterLists } from './filter_lists';
const props = {
canCreateFilter: true,
canDeleteFilter: true
canDeleteFilter: true,
};
describe('Filter Lists', () => {
test('renders a list of filters', () => {
const wrapper = shallowWithIntl(
<FilterLists.WrappedComponent {...props}/>
);
const wrapper = shallowWithIntl(<FilterLists.WrappedComponent {...props} />);
// Cannot find a way to generate the snapshot after the Promise in the mock ml.filters
// has resolved.
@ -54,5 +52,4 @@ describe('Filter Lists', () => {
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -6,7 +6,7 @@
import React from 'react';
import React, { Fragment } from 'react';
import { PropTypes } from 'prop-types';
import {
@ -20,67 +20,73 @@ import {
EuiTitle
} from '@elastic/eui';
import chrome from 'ui/chrome';
import { FormattedMessage } from '@kbn/i18n/react';
import { useNavigationMenuContext } from '../util/context_utils';
import { NavigationMenu } from '../components/navigation_menu/navigation_menu';
export function Settings({
canGetFilters,
canGetCalendars
}) {
const basePath = useNavigationMenuContext().chrome.getBasePath();
return (
<EuiPage className="mlSettingsPage" data-test-subj="mlPageSettings">
<EuiPageBody className="mlSettingsPage__body">
<EuiPageContent
className="mlSettingsPage__content"
horizontalPosition="center"
>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<FormattedMessage
id="xpack.ml.settings.jobManagementTitle"
defaultMessage="Job Management"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<Fragment>
<NavigationMenu tabId="settings" />
<EuiPage className="mlSettingsPage" data-test-subj="mlPageSettings">
<EuiPageBody className="mlSettingsPage__body">
<EuiPageContent
className="mlSettingsPage__content"
horizontalPosition="center"
>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<FormattedMessage
id="xpack.ml.settings.jobManagementTitle"
defaultMessage="Job Management"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="ml_calendar_mng_button"
size="l"
color="primary"
href={`${chrome.getBasePath()}/app/ml#/settings/calendars_list`}
isDisabled={canGetCalendars === false}
>
<FormattedMessage
id="xpack.ml.settings.calendarManagementButtonLabel"
defaultMessage="Calendar management"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="ml_calendar_mng_button"
size="l"
color="primary"
href={`${basePath}/app/ml#/settings/calendars_list`}
isDisabled={canGetCalendars === false}
>
<FormattedMessage
id="xpack.ml.settings.calendarManagementButtonLabel"
defaultMessage="Calendar management"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="ml_filter_lists_button"
size="l"
color="primary"
href={`${chrome.getBasePath()}/app/ml#/settings/filter_lists`}
isDisabled={canGetFilters === false}
>
<FormattedMessage
id="xpack.ml.settings.filterListsButtonLabel"
defaultMessage="Filter Lists"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj="ml_filter_lists_button"
size="l"
color="primary"
href={`${basePath}/app/ml#/settings/filter_lists`}
isDisabled={canGetFilters === false}
>
<FormattedMessage
id="xpack.ml.settings.filterListsButtonLabel"
defaultMessage="Filter Lists"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</Fragment>
);
}

View file

@ -4,46 +4,65 @@
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('ui/chrome', () => ({
getBasePath: jest.fn()
}));
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import PropTypes from 'prop-types';
import React from 'react';
import * as ContextUtils from '../util/context_utils';
import { Settings } from './settings';
const navigationMenuMock = ContextUtils.navigationMenuMock;
const mountOptions = {
context: { NavigationMenuContext: navigationMenuMock },
childContextTypes: { NavigationMenuContext: PropTypes.object }
};
jest.mock('../components/navigation_menu/navigation_menu', () => ({
NavigationMenu: () => <div id="mockNavigationMenu" />
}));
jest.spyOn(ContextUtils, 'useNavigationMenuContext').mockImplementation(() => navigationMenuMock);
describe('Settings', () => {
test('Renders settings page with all buttons enabled.', () => {
const wrapper = mountWithIntl(<Settings canGetFilters={true} canGetCalendars={true} />, mountOptions);
test('Renders settings page', () => {
const wrapper = shallowWithIntl(
<Settings canGetFilters={true} canGetCalendars={true}/>
);
const filterButton = wrapper
.find('[data-test-subj="ml_filter_lists_button"]')
.find('EuiButtonEmpty');
expect(filterButton.prop('isDisabled')).toBe(false);
expect(wrapper).toMatchSnapshot();
const calendarButton = wrapper
.find('[data-test-subj="ml_calendar_mng_button"]')
.find('EuiButtonEmpty');
expect(calendarButton.prop('isDisabled')).toBe(false);
});
test('Filter Lists button disabled if canGetFilters is false', () => {
const wrapper = mountWithIntl(
<Settings canGetFilters={false} canGetCalendars={true}/>
);
const wrapper = mountWithIntl(<Settings canGetFilters={false} canGetCalendars={true} />, mountOptions);
const button = wrapper.find('[data-test-subj="ml_filter_lists_button"]');
const filterButton = button.find('EuiButtonEmpty');
const filterButton = wrapper
.find('[data-test-subj="ml_filter_lists_button"]')
.find('EuiButtonEmpty');
expect(filterButton.prop('isDisabled')).toBe(true);
const calendarButton = wrapper
.find('[data-test-subj="ml_calendar_mng_button"]')
.find('EuiButtonEmpty');
expect(calendarButton.prop('isDisabled')).toBe(false);
});
test('Calendar management button disabled if canGetCalendars is false', () => {
const wrapper = mountWithIntl(
<Settings canGetFilters={true} canGetCalendars={false} />
);
const wrapper = mountWithIntl(<Settings canGetFilters={true} canGetCalendars={false} />, mountOptions);
const button = wrapper.find('[data-test-subj="ml_calendar_mng_button"]');
const calendarButton = button.find('EuiButtonEmpty');
const filterButton = wrapper
.find('[data-test-subj="ml_filter_lists_button"]')
.find('EuiButtonEmpty');
expect(filterButton.prop('isDisabled')).toBe(false);
const calendarButton = wrapper
.find('[data-test-subj="ml_calendar_mng_button"]')
.find('EuiButtonEmpty');
expect(calendarButton.prop('isDisabled')).toBe(true);
});
});

View file

@ -15,15 +15,17 @@ const module = uiModules.get('apps/ml', ['react']);
import { checkFullLicense } from '../license/check_license';
import { checkGetJobsPrivilege, checkPermission } from '../privilege/check_privilege';
import { getMlNodeCount } from '../ml_nodes_check/check_ml_nodes';
import { NavigationMenuContext } from '../util/context_utils';
import { getSettingsBreadcrumbs } from './breadcrumbs';
import { I18nContext } from 'ui/i18n';
import chrome from 'ui/chrome';
import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { timeHistory } from 'ui/timefilter/time_history';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="settings" />
<ml-settings />
`;
@ -56,12 +58,9 @@ module.directive('mlSettings', function () {
ReactDOM.render(
<I18nContext>
{React.createElement(
Settings, {
canGetFilters,
canGetCalendars
})
}
<NavigationMenuContext.Provider value={{ chrome, timefilter, timeHistory }} >
<Settings canGetCalendars={canGetCalendars} canGetFilters={canGetFilters} />
</NavigationMenuContext.Provider>
</I18nContext>,
element[0]
);

View file

@ -1,4 +1,4 @@
<ml-nav-menu name="timeseriesexplorer"></ml-nav-menu>
<ml-nav-menu name="timeseriesexplorer" />
<ml-chart-tooltip></ml-chart-tooltip>
<div class="ml-time-series-explorer" ng-controller="MlTimeSeriesExplorerController" data-test-subj="mlPageSingleMetricViewer" >

View file

@ -0,0 +1,66 @@
/*
* 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, { useContext } from 'react';
import { Chrome } from 'ui/chrome';
import { Timefilter } from 'ui/timefilter';
import { TimeHistory } from 'ui/timefilter/time_history';
export const ChromeContext = React.createContext<Chrome>({} as Chrome);
export const TimefilterContext = React.createContext<Timefilter>({} as Timefilter);
export const TimeHistoryContext = React.createContext<TimeHistory>({} as TimeHistory);
interface NavigationMenuContextValue {
chrome: Chrome;
timefilter: Timefilter;
timeHistory: TimeHistory;
}
export const NavigationMenuContext = React.createContext<NavigationMenuContextValue>({
chrome: {} as Chrome,
timefilter: {} as Timefilter,
timeHistory: {} as TimeHistory,
});
export const useNavigationMenuContext = () => {
return useContext(NavigationMenuContext);
};
// testing mocks
export const chromeMock = {
getBasePath: () => 'basePath',
getUiSettingsClient: () => {
return {
get: (key: string) => {
switch (key) {
case 'dateFormat':
case 'timepicker:timeDefaults':
return {};
case 'timepicker:refreshIntervalDefaults':
return { pause: false, value: 0 };
default:
throw new Error(`Unexpected config key: ${key}`);
}
},
};
},
} as Chrome;
export const timefilterMock = ({
getRefreshInterval: () => '30s',
getTime: () => ({ from: 0, to: 0 }),
on: (event: string, reload: () => void) => {},
} as unknown) as Timefilter;
export const timeHistoryMock = ({
get: () => [{ from: 0, to: 0 }],
} as unknown) as TimeHistory;
export const navigationMenuMock = {
chrome: chromeMock,
timefilter: timefilterMock,
timeHistory: timeHistoryMock,
};