mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Remove sub navigation menu from the Anomaly Detection pages (#68663)
* [ML] Remove sub navigation menu from the Anomaly Detection pages * [ML] Use data-test-subj in results view selector button tests * [ML] Fix settings page tests * [ML] Remove redundant top_nav component * [ML] Tidy up job selector scss * [ML] Use EUI condensed tab style for main tabs * [ML] Address review comments and fix type error * [ML] Edits following review. Fix translations. * [ML] Add anomaly_detection_settings_context
This commit is contained in:
parent
8cdc2a1493
commit
c307c8622f
45 changed files with 921 additions and 692 deletions
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
import { AnomalyResultsViewSelector } from './index';
|
||||
|
||||
describe('AnomalyResultsViewSelector', () => {
|
||||
test('should create selector with correctly selected value', () => {
|
||||
const history = createBrowserHistory();
|
||||
|
||||
const { getByTestId } = render(
|
||||
<I18nProvider>
|
||||
<Router history={history}>
|
||||
<AnomalyResultsViewSelector viewId="timeseriesexplorer" />
|
||||
</Router>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
// Check the Single Metric Viewer element exists in the selector, and that it is checked.
|
||||
expect(getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer')).toBeInTheDocument();
|
||||
expect(
|
||||
getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer').hasAttribute('checked')
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should open window to other results view when clicking on non-checked input', () => {
|
||||
// Create mock for window.open
|
||||
const mockedOpen = jest.fn();
|
||||
const originalOpen = window.open;
|
||||
window.open = mockedOpen;
|
||||
|
||||
const history = createBrowserHistory();
|
||||
|
||||
const { getByTestId } = render(
|
||||
<I18nProvider>
|
||||
<Router history={history}>
|
||||
<AnomalyResultsViewSelector viewId="timeseriesexplorer" />
|
||||
</Router>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
fireEvent.click(getByTestId('mlAnomalyResultsViewSelectorExplorer'));
|
||||
expect(mockedOpen).toHaveBeenCalledWith('#/explorer', '_self');
|
||||
|
||||
// Clean-up window.open.
|
||||
window.open = originalOpen;
|
||||
});
|
||||
});
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import { encode } from 'rison-node';
|
||||
|
||||
import { EuiButtonGroup } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
|
||||
interface Props {
|
||||
viewId: 'timeseriesexplorer' | 'explorer';
|
||||
}
|
||||
|
||||
// Component for rendering a set of buttons for switching between the Anomaly Detection results views.
|
||||
|
||||
export const AnomalyResultsViewSelector: FC<Props> = ({ viewId }) => {
|
||||
const toggleButtonsIcons = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'timeseriesexplorer',
|
||||
label: i18n.translate('xpack.ml.anomalyResultsViewSelector.singleMetricViewerLabel', {
|
||||
defaultMessage: 'View results in the Single Metric Viewer',
|
||||
}),
|
||||
iconType: 'stats',
|
||||
value: 'timeseriesexplorer',
|
||||
'data-test-subj': 'mlAnomalyResultsViewSelectorSingleMetricViewer',
|
||||
},
|
||||
{
|
||||
id: 'explorer',
|
||||
label: i18n.translate('xpack.ml.anomalyResultsViewSelector.anomalyExplorerLabel', {
|
||||
defaultMessage: 'View results in the Anomaly Explorer',
|
||||
}),
|
||||
iconType: 'tableOfContents',
|
||||
value: 'explorer',
|
||||
'data-test-subj': 'mlAnomalyResultsViewSelectorExplorer',
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const onChangeView = (newViewId: string) => {
|
||||
const fullGlobalStateString = globalState !== undefined ? `?_g=${encode(globalState)}` : '';
|
||||
window.open(`#/${newViewId}${fullGlobalStateString}`, '_self');
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiButtonGroup
|
||||
legend={i18n.translate('xpack.ml.anomalyResultsViewSelector.buttonGroupLegend', {
|
||||
defaultMessage: 'Anomaly results view selector',
|
||||
})}
|
||||
name="anomalyResultsViewSelector"
|
||||
data-test-subj="mlAnomalyResultsViewSelector"
|
||||
options={toggleButtonsIcons}
|
||||
idSelected={viewId}
|
||||
onChange={onChangeView}
|
||||
isIconOnly
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export { AnomalyResultsViewSelector } from './anomaly_results_view_selector';
|
|
@ -1,8 +1,8 @@
|
|||
.mlJobSelectorBar {
|
||||
padding: $euiSize;
|
||||
padding: $euiSizeS;
|
||||
}
|
||||
|
||||
.mlJobSelectorFlyoutBody > .euiFlyoutBody__overflow {
|
||||
.mlJobSelectorFlyoutBody>.euiFlyoutBody__overflow {
|
||||
padding-top: $euiSizeS;
|
||||
}
|
||||
|
||||
|
@ -26,54 +26,14 @@
|
|||
}
|
||||
|
||||
.mlJobSelector__ganttBarRunning {
|
||||
background-image:-webkit-gradient(linear,
|
||||
0 100%, 100% 0,
|
||||
color-stop(0.25, rgba(255, 255, 255, 0.15)),
|
||||
color-stop(0.25, transparent),
|
||||
color-stop(0.5, transparent),
|
||||
color-stop(0.5, rgba(255, 255, 255, 0.15)),
|
||||
color-stop(0.75, rgba(255, 255, 255, 0.15)),
|
||||
color-stop(0.75, transparent),
|
||||
to(transparent));
|
||||
background-image:-webkit-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%, transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-image:-moz-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-image:-o-linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-image:linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
-webkit-background-size:40px 40px;
|
||||
-moz-background-size:40px 40px;
|
||||
-o-background-size:40px 40px;
|
||||
background-size:40px 40px;
|
||||
|
||||
-webkit-animation:progress-bar-stripes 2s linear infinite;
|
||||
-moz-animation:progress-bar-stripes 2s linear infinite;
|
||||
-ms-animation:progress-bar-stripes 2s linear infinite;
|
||||
-o-animation:progress-bar-stripes 2s linear infinite;
|
||||
animation:progress-bar-stripes 2s linear infinite;
|
||||
background-image: linear-gradient(45deg,
|
||||
rgba(255, 255, 255, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.15) 50%,
|
||||
rgba(255, 255, 255, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent);
|
||||
background-size: $euiSizeXXL $euiSizeXXL;
|
||||
animation: progress-bar-stripes 2s linear infinite;
|
||||
}
|
||||
|
|
|
@ -1,25 +1,5 @@
|
|||
.mlNavigationMenu__tab {
|
||||
padding-bottom: 0;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
margin-left: $euiSizeM;
|
||||
}
|
||||
|
||||
.mlNavigationMenu__mainTab {
|
||||
margin-left: $euiSizeM;
|
||||
padding-bottom: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.mlNavigationMenu__topNav {
|
||||
padding-top: $euiSizeS;
|
||||
margin-right: $euiSizeS;
|
||||
}
|
||||
|
||||
.mlNavHorizontalRule {
|
||||
margin: $euiSizeM 0 0 0;
|
||||
}
|
||||
|
||||
.mlSubTabs {
|
||||
margin-top: $euiSizeS;
|
||||
.mlNavigationMenu {
|
||||
padding: 0 $euiSizeM;
|
||||
border-bottom: $euiBorderThin;
|
||||
background-color: $euiColorEmptyShade;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { EuiSuperDatePicker } from '@elastic/eui';
|
|||
|
||||
import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service';
|
||||
|
||||
import { TopNav } from './top_nav';
|
||||
import { DatePickerWrapper } from './date_picker_wrapper';
|
||||
|
||||
jest.mock('../../../contexts/kibana', () => ({
|
||||
useMlKibana: () => {
|
||||
|
@ -43,7 +43,7 @@ jest.mock('../../../contexts/kibana', () => ({
|
|||
|
||||
const noop = () => {};
|
||||
|
||||
describe('Navigation Menu: <TopNav />', () => {
|
||||
describe('Navigation Menu: <DatePickerWrapper />', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
@ -58,17 +58,17 @@ describe('Navigation Menu: <TopNav />', () => {
|
|||
|
||||
const wrapper = mount(
|
||||
<MemoryRouter>
|
||||
<TopNav />
|
||||
<DatePickerWrapper />
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(wrapper.find(TopNav)).toHaveLength(1);
|
||||
expect(wrapper.find(DatePickerWrapper)).toHaveLength(1);
|
||||
expect(refreshListener).toBeCalledTimes(0);
|
||||
|
||||
refreshSubscription.unsubscribe();
|
||||
});
|
||||
|
||||
// The following tests are written against EuiSuperDatePicker
|
||||
// instead of TopNav. TopNav uses hooks and we cannot write tests
|
||||
// instead of DatePickerWrapper. DatePickerWrapper uses hooks and we cannot write tests
|
||||
// with async hook updates yet until React 16.9 is available.
|
||||
test('Listen for consecutive super date picker refreshs.', async () => {
|
||||
const onRefresh = jest.fn();
|
|
@ -43,7 +43,7 @@ function updateLastRefresh(timeRange: OnRefreshProps) {
|
|||
mlTimefilterRefresh$.next({ lastRefresh: Date.now(), timeRange });
|
||||
}
|
||||
|
||||
export const TopNav: FC = () => {
|
||||
export const DatePickerWrapper: FC = () => {
|
||||
const { services } = useMlKibana();
|
||||
const config = services.uiSettings;
|
||||
const { timefilter, history } = services.data.query.timefilter;
|
||||
|
@ -124,7 +124,7 @@ export const TopNav: FC = () => {
|
|||
return (
|
||||
<Fragment>
|
||||
{(isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled) && (
|
||||
<div className="mlNavigationMenu__topNav">
|
||||
<div className="mlNavigationMenu__datePickerWrapper">
|
||||
<EuiSuperDatePicker
|
||||
start={time.from}
|
||||
end={time.to}
|
|
@ -4,4 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { TopNav } from './top_nav';
|
||||
export { DatePickerWrapper } from './date_picker_wrapper';
|
|
@ -55,6 +55,13 @@ function getTabs(disableLinks: boolean): Tab[] {
|
|||
}),
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
name: i18n.translate('xpack.ml.navMenu.settingsTabLinkText', {
|
||||
defaultMessage: 'Settings',
|
||||
}),
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
interface TabData {
|
||||
|
@ -63,16 +70,19 @@ interface TabData {
|
|||
}
|
||||
|
||||
const TAB_DATA: Record<TabId, TabData> = {
|
||||
overview: { testSubject: 'mlMainTab overview', pathId: 'overview' },
|
||||
overview: { testSubject: 'mlMainTab overview' },
|
||||
// Note that anomaly detection jobs list is mapped to ml#/jobs.
|
||||
anomaly_detection: { testSubject: 'mlMainTab anomalyDetection', pathId: 'jobs' },
|
||||
data_frame_analytics: { testSubject: 'mlMainTab dataFrameAnalytics' },
|
||||
datavisualizer: { testSubject: 'mlMainTab dataVisualizer' },
|
||||
settings: { testSubject: 'mlMainTab settings' },
|
||||
'access-denied': { testSubject: 'mlMainTab overview' },
|
||||
};
|
||||
|
||||
export const MainTabs: FC<Props> = ({ tabId, disableLinks }) => {
|
||||
const [globalState] = useUrlState('_g');
|
||||
const [selectedTabId, setSelectedTabId] = useState(tabId);
|
||||
function onSelectedTabChanged(id: string) {
|
||||
function onSelectedTabChanged(id: TabId) {
|
||||
setSelectedTabId(id);
|
||||
}
|
||||
|
||||
|
@ -93,20 +103,23 @@ export const MainTabs: FC<Props> = ({ tabId, disableLinks }) => {
|
|||
{tab.name}
|
||||
</EuiTab>
|
||||
) : (
|
||||
<EuiLink
|
||||
data-test-subj={testSubject + (id === selectedTabId ? ' selected' : '')}
|
||||
href={`#/${defaultPathId}${fullGlobalStateString}`}
|
||||
key={`${id}-key`}
|
||||
color="text"
|
||||
>
|
||||
<EuiTab
|
||||
className={'mlNavigationMenu__mainTab'}
|
||||
onClick={() => onSelectedTabChanged(id)}
|
||||
isSelected={id === selectedTabId}
|
||||
<div className="euiTab" key={`div-${id}-key`}>
|
||||
<EuiLink
|
||||
data-test-subj={testSubject + (id === selectedTabId ? ' selected' : '')}
|
||||
href={`#/${defaultPathId}${fullGlobalStateString}`}
|
||||
key={`${id}-key`}
|
||||
color="text"
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
</EuiLink>
|
||||
<EuiTab
|
||||
className={'mlNavigationMenu__mainTab'}
|
||||
onClick={() => onSelectedTabChanged(id)}
|
||||
isSelected={id === selectedTabId}
|
||||
key={`tab-${id}-key`}
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
</EuiLink>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</EuiTabs>
|
||||
|
|
|
@ -5,28 +5,19 @@
|
|||
*/
|
||||
|
||||
import React, { Fragment, FC } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import { isFullLicense } from '../../license';
|
||||
|
||||
import { TopNav } from './top_nav';
|
||||
import { MainTabs } from './main_tabs';
|
||||
import { Tabs } from './tabs';
|
||||
|
||||
export type TabId = string;
|
||||
type TabSupport = Record<TabId, string | null>;
|
||||
|
||||
const tabSupport: TabSupport = {
|
||||
overview: null,
|
||||
jobs: 'anomaly_detection',
|
||||
settings: 'anomaly_detection',
|
||||
data_frame_analytics: null,
|
||||
datavisualizer: null,
|
||||
filedatavisualizer: null,
|
||||
timeseriesexplorer: 'anomaly_detection',
|
||||
'access-denied': null,
|
||||
explorer: 'anomaly_detection',
|
||||
};
|
||||
export type TabId =
|
||||
| 'access-denied'
|
||||
| 'anomaly_detection'
|
||||
| 'data_frame_analytics'
|
||||
| 'datavisualizer'
|
||||
| 'overview'
|
||||
| 'settings';
|
||||
|
||||
interface Props {
|
||||
tabId: TabId;
|
||||
|
@ -34,23 +25,14 @@ interface Props {
|
|||
|
||||
export const NavigationMenu: FC<Props> = ({ tabId }) => {
|
||||
const disableLinks = isFullLicense() === false;
|
||||
const showTabs = typeof tabSupport[tabId] !== 'undefined';
|
||||
const mainTabId = tabSupport[tabId] || tabId;
|
||||
// show horizontal rule if there are no subtabs
|
||||
const showHorizontalRule = tabSupport[tabId] === null;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexGroup justifyContent="spaceBetween" className="mlNavigationMenu" gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
{showTabs && <MainTabs tabId={mainTabId} disableLinks={disableLinks} />}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<TopNav />
|
||||
<MainTabs tabId={tabId} disableLinks={disableLinks} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{showHorizontalRule && <EuiHorizontalRule className="mlNavHorizontalRule" />}
|
||||
{showTabs && <Tabs tabId={tabId} mainTabId={mainTabId} disableLinks={disableLinks} />}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,23 +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 { getTabs } from './tabs';
|
||||
|
||||
describe('Navigation Menu: Tabs', () => {
|
||||
test('getTabs() always returns an array', () => {
|
||||
const tabs1 = getTabs('anomaly_detection', false);
|
||||
expect(Array.isArray(tabs1)).toBeTruthy();
|
||||
expect(tabs1).toHaveLength(4);
|
||||
|
||||
const tabs2 = getTabs('access-denied', false);
|
||||
expect(Array.isArray(tabs2)).toBeTruthy();
|
||||
expect(tabs2).toHaveLength(0);
|
||||
|
||||
const tabs3 = getTabs('datavisualizer', false);
|
||||
expect(Array.isArray(tabs3)).toBeTruthy();
|
||||
expect(tabs3).toHaveLength(0);
|
||||
});
|
||||
});
|
|
@ -1,116 +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 React, { FC, useState } from 'react';
|
||||
import { encode } from 'rison-node';
|
||||
|
||||
import { EuiTabs, EuiTab, EuiLink } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
|
||||
import { Tab } from './main_tabs';
|
||||
import { TabId } from './navigation_menu';
|
||||
|
||||
interface Props {
|
||||
disableLinks: boolean;
|
||||
mainTabId: TabId;
|
||||
tabId: TabId;
|
||||
}
|
||||
|
||||
export function getTabs(tabId: TabId, disableLinks: boolean): Tab[] {
|
||||
const TAB_MAP: Partial<Record<TabId, Tab[]>> = {
|
||||
overview: [],
|
||||
datavisualizer: [],
|
||||
data_frame_analytics: [],
|
||||
anomaly_detection: [
|
||||
{
|
||||
id: 'jobs',
|
||||
name: i18n.translate('xpack.ml.navMenu.jobManagementTabLinkText', {
|
||||
defaultMessage: 'Job Management',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
},
|
||||
{
|
||||
id: 'explorer',
|
||||
name: i18n.translate('xpack.ml.navMenu.anomalyExplorerTabLinkText', {
|
||||
defaultMessage: 'Anomaly Explorer',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
},
|
||||
{
|
||||
id: 'timeseriesexplorer',
|
||||
name: i18n.translate('xpack.ml.navMenu.singleMetricViewerTabLinkText', {
|
||||
defaultMessage: 'Single Metric Viewer',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
name: i18n.translate('xpack.ml.navMenu.settingsTabLinkText', {
|
||||
defaultMessage: 'Settings',
|
||||
}),
|
||||
disabled: disableLinks,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return TAB_MAP[tabId] || [];
|
||||
}
|
||||
|
||||
enum TAB_TEST_SUBJECT {
|
||||
overview = 'mlOverview',
|
||||
jobs = 'mlSubTab jobManagement',
|
||||
explorer = 'mlSubTab anomalyExplorer',
|
||||
timeseriesexplorer = 'mlSubTab singleMetricViewer',
|
||||
settings = 'mlSubTab settings',
|
||||
}
|
||||
|
||||
type TAB_TEST_SUBJECTS = keyof typeof TAB_TEST_SUBJECT;
|
||||
|
||||
export const Tabs: FC<Props> = ({ tabId, mainTabId, disableLinks }) => {
|
||||
const [globalState] = useUrlState('_g');
|
||||
const [selectedTabId, setSelectedTabId] = useState(tabId);
|
||||
function onSelectedTabChanged(id: string) {
|
||||
setSelectedTabId(id);
|
||||
}
|
||||
|
||||
const tabs = getTabs(mainTabId, disableLinks);
|
||||
|
||||
if (tabs.length === 0) return null;
|
||||
|
||||
return (
|
||||
<EuiTabs size="s" className={tabId === 'settings' ? 'mlSubTabs' : ''}>
|
||||
{tabs.map((tab: Tab) => {
|
||||
const id = tab.id;
|
||||
// globalState (e.g. selected jobs and time range) should be retained when changing pages.
|
||||
// appState will not be considered.
|
||||
const fullGlobalStateString = globalState !== undefined ? `?_g=${encode(globalState)}` : '';
|
||||
|
||||
return (
|
||||
<EuiLink
|
||||
data-test-subj={
|
||||
TAB_TEST_SUBJECT[id as TAB_TEST_SUBJECTS] + (id === selectedTabId ? ' selected' : '')
|
||||
}
|
||||
href={`#/${id}${fullGlobalStateString}`}
|
||||
key={`${id}-key`}
|
||||
color="text"
|
||||
>
|
||||
<EuiTab
|
||||
className="mlNavigationMenu__tab"
|
||||
onClick={() => onSelectedTabChanged(id)}
|
||||
isSelected={id === selectedTabId}
|
||||
disabled={tab.disabled}
|
||||
>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
</EuiLink>
|
||||
);
|
||||
})}
|
||||
</EuiTabs>
|
||||
);
|
||||
};
|
|
@ -48,7 +48,6 @@ import {
|
|||
SORT_DIRECTION,
|
||||
} from '../../../../../components/ml_in_memory_table';
|
||||
import { AnalyticStatsBarStats, StatsBar } from '../../../../../components/stats_bar';
|
||||
import { RefreshAnalyticsListButton } from '../refresh_analytics_list_button';
|
||||
import { CreateAnalyticsButton } from '../create_analytics_button';
|
||||
import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form';
|
||||
import { getSelectedJobIdFromUrl } from '../../../../../jobs/jobs_list/components/utils';
|
||||
|
@ -398,9 +397,6 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<RefreshAnalyticsListButton />
|
||||
</EuiFlexItem>
|
||||
{!isManagementTable && createAnalyticsForm && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<CreateAnalyticsButton
|
||||
|
@ -412,7 +408,7 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiSpacer size="m" />
|
||||
<div data-test-subj="mlAnalyticsTableContainer">
|
||||
<MlInMemoryTable
|
||||
allowNeutralSort={false}
|
||||
|
|
|
@ -39,6 +39,6 @@ describe('Data Frame Analytics: <CreateAnalyticsButton />', () => {
|
|||
<CreateAnalyticsButton {...props} setIsSourceIndexModalVisible={jest.fn()} />
|
||||
);
|
||||
|
||||
expect(wrapper.find('EuiButton').text()).toBe('Create analytics job');
|
||||
expect(wrapper.find('EuiButton').text()).toBe('Create job');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,7 +35,7 @@ export const CreateAnalyticsButton: FC<Props> = ({
|
|||
data-test-subj="mlAnalyticsButtonCreate"
|
||||
>
|
||||
{i18n.translate('xpack.ml.dataframe.analyticsList.createDataFrameAnalyticsButton', {
|
||||
defaultMessage: 'Create analytics job',
|
||||
defaultMessage: 'Create job',
|
||||
})}
|
||||
</EuiButton>
|
||||
);
|
||||
|
|
|
@ -11,16 +11,21 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import {
|
||||
EuiBetaBadge,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiTitle,
|
||||
EuiPageContent,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { NavigationMenu } from '../../../components/navigation_menu';
|
||||
import { DatePickerWrapper } from '../../../components/navigation_menu/date_picker_wrapper';
|
||||
import { DataFrameAnalyticsList } from './components/analytics_list';
|
||||
import { useRefreshInterval } from './components/analytics_list/use_refresh_interval';
|
||||
import { RefreshAnalyticsListButton } from './components/refresh_analytics_list_button';
|
||||
import { useCreateAnalyticsForm } from './hooks/use_create_analytics_form';
|
||||
import { NodeAvailableWarning } from '../../../components/node_available_warning';
|
||||
import { UpgradeWarning } from '../../../components/upgrade';
|
||||
|
@ -63,15 +68,27 @@ export const Page: FC = () => {
|
|||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<RefreshAnalyticsListButton />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<DatePickerWrapper />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
|
||||
<NodeAvailableWarning />
|
||||
<UpgradeWarning />
|
||||
|
||||
<DataFrameAnalyticsList
|
||||
blockRefresh={blockRefresh}
|
||||
createAnalyticsForm={createAnalyticsForm}
|
||||
/>
|
||||
<EuiPageContent>
|
||||
<DataFrameAnalyticsList
|
||||
blockRefresh={blockRefresh}
|
||||
createAnalyticsForm={createAnalyticsForm}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</Fragment>
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
}
|
||||
|
||||
.mlAnomalyExplorer__filterBar {
|
||||
margin-bottom: $euiSize;
|
||||
padding-right: $euiSize;
|
||||
padding-left: $euiSize;
|
||||
}
|
||||
|
|
|
@ -20,10 +20,12 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
EuiHorizontalRule,
|
||||
EuiIconTip,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiScreenReaderOnly,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiSelect,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
|
@ -38,12 +40,14 @@ import {
|
|||
} from './components';
|
||||
import { ExplorerSwimlane } from './explorer_swimlane';
|
||||
import { getTimeBucketsFromCache } from '../util/time_buckets';
|
||||
import { DatePickerWrapper } from '../components/navigation_menu/date_picker_wrapper';
|
||||
import { InfluencersList } from '../components/influencers_list';
|
||||
import {
|
||||
ALLOW_CELL_RANGE_SELECTION,
|
||||
dragSelect$,
|
||||
explorerService,
|
||||
} from './explorer_dashboard_service';
|
||||
import { AnomalyResultsViewSelector } from '../components/anomaly_results_view_selector';
|
||||
import { LoadingIndicator } from '../components/loading_indicator/loading_indicator';
|
||||
import { NavigationMenu } from '../components/navigation_menu';
|
||||
import { CheckboxShowCharts } from '../components/controls/checkbox_showcharts';
|
||||
|
@ -89,17 +93,67 @@ function mapSwimlaneOptionsToEuiOptions(options) {
|
|||
text: option,
|
||||
}));
|
||||
}
|
||||
const ExplorerPage = ({ children, jobSelectorProps, resizeRef }) => (
|
||||
|
||||
const ExplorerPage = ({
|
||||
children,
|
||||
jobSelectorProps,
|
||||
noInfluencersConfigured,
|
||||
influencers,
|
||||
filterActive,
|
||||
filterPlaceHolder,
|
||||
indexPattern,
|
||||
queryString,
|
||||
filterIconTriggeredQuery,
|
||||
updateLanguage,
|
||||
resizeRef,
|
||||
}) => (
|
||||
<div ref={resizeRef} data-test-subj="mlPageAnomalyExplorer">
|
||||
<NavigationMenu tabId="explorer" />
|
||||
<EuiPage style={{ padding: '0px', background: 'none' }}>
|
||||
<NavigationMenu tabId="anomaly_detection" />
|
||||
<EuiPage style={{ background: 'none' }}>
|
||||
<EuiPageBody>
|
||||
<EuiScreenReaderOnly>
|
||||
<h1>
|
||||
<FormattedMessage id="xpack.ml.explorer.pageTitle" defaultMessage="Anomaly Explorer" />
|
||||
</h1>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<AnomalyResultsViewSelector viewId="explorer" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle className="eui-textNoWrap">
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.explorer.pageTitle"
|
||||
defaultMessage="Anomaly Explorer"
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection style={{ width: '100%' }}>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="flexEnd" gutterSize="s">
|
||||
{noInfluencersConfigured === false && influencers !== undefined && (
|
||||
<EuiFlexItem>
|
||||
<div className="mlAnomalyExplorer__filterBar">
|
||||
<ExplorerQueryBar
|
||||
filterActive={filterActive}
|
||||
filterPlaceHolder={filterPlaceHolder}
|
||||
indexPattern={indexPattern}
|
||||
queryString={queryString}
|
||||
filterIconTriggeredQuery={filterIconTriggeredQuery}
|
||||
updateLanguage={updateLanguage}
|
||||
/>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<DatePickerWrapper />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<JobSelector {...jobSelectorProps} />
|
||||
<EuiHorizontalRule margin="none" />
|
||||
{children}
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
|
@ -314,7 +368,18 @@ export class Explorer extends React.Component {
|
|||
|
||||
if (loading === true) {
|
||||
return (
|
||||
<ExplorerPage jobSelectorProps={jobSelectorProps} resizeRef={this.resizeRef}>
|
||||
<ExplorerPage
|
||||
jobSelectorProps={jobSelectorProps}
|
||||
noInfluencersConfigured={noInfluencersConfigured}
|
||||
influencers={influencers}
|
||||
filterActive={filterActive}
|
||||
filterPlaceHolder={filterPlaceHolder}
|
||||
filterIconTriggeredQuery={this.state.filterIconTriggeredQuery}
|
||||
indexPattern={indexPattern}
|
||||
queryString={queryString}
|
||||
updateLanguage={this.updateLanguage}
|
||||
resizeRef={this.resizeRef}
|
||||
>
|
||||
<LoadingIndicator
|
||||
label={i18n.translate('xpack.ml.explorer.loadingLabel', {
|
||||
defaultMessage: 'Loading',
|
||||
|
@ -356,21 +421,19 @@ export class Explorer extends React.Component {
|
|||
const bounds = timefilter.getActiveBounds();
|
||||
|
||||
return (
|
||||
<ExplorerPage jobSelectorProps={jobSelectorProps} resizeRef={this.resizeRef}>
|
||||
<ExplorerPage
|
||||
jobSelectorProps={jobSelectorProps}
|
||||
noInfluencersConfigured={noInfluencersConfigured}
|
||||
influencers={influencers}
|
||||
filterActive={filterActive}
|
||||
filterPlaceHolder={filterPlaceHolder}
|
||||
filterIconTriggeredQuery={this.state.filterIconTriggeredQuery}
|
||||
indexPattern={indexPattern}
|
||||
queryString={queryString}
|
||||
updateLanguage={this.updateLanguage}
|
||||
resizeRef={this.resizeRef}
|
||||
>
|
||||
<div className="results-container">
|
||||
{noInfluencersConfigured === false && influencers !== undefined && (
|
||||
<div className="mlAnomalyExplorer__filterBar">
|
||||
<ExplorerQueryBar
|
||||
filterActive={filterActive}
|
||||
filterPlaceHolder={filterPlaceHolder}
|
||||
indexPattern={indexPattern}
|
||||
queryString={queryString}
|
||||
filterIconTriggeredQuery={this.state.filterIconTriggeredQuery}
|
||||
updateLanguage={this.updateLanguage}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{noInfluencersConfigured && (
|
||||
<div className="no-influencers-warning">
|
||||
<EuiIconTip
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiSpacer,
|
||||
|
@ -30,6 +31,7 @@ import { MultiJobActions } from '../multi_job_actions';
|
|||
import { NewJobButton } from '../new_job_button';
|
||||
import { JobStatsBar } from '../jobs_stats_bar';
|
||||
import { NodeAvailableWarning } from '../../../../components/node_available_warning';
|
||||
import { DatePickerWrapper } from '../../../../components/navigation_menu/date_picker_wrapper';
|
||||
import { UpgradeWarning } from '../../../../components/upgrade';
|
||||
import { RefreshJobsListButton } from '../refresh_jobs_list_button';
|
||||
import { isEqual } from 'lodash';
|
||||
|
@ -352,6 +354,7 @@ export class JobsListView extends Component {
|
|||
renderJobsListComponents() {
|
||||
const { isRefreshing, loading, jobsSummaryList } = this.state;
|
||||
const jobIds = jobsSummaryList.map((j) => j.id);
|
||||
|
||||
return (
|
||||
<EuiPage data-test-subj="ml-jobs-list">
|
||||
<EuiPageBody>
|
||||
|
@ -366,17 +369,7 @@ export class JobsListView extends Component {
|
|||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
|
||||
<NodeAvailableWarning />
|
||||
|
||||
<UpgradeWarning />
|
||||
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<JobStatsBar jobsSummaryList={jobsSummaryList} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<RefreshJobsListButton
|
||||
|
@ -385,60 +378,75 @@ export class JobsListView extends Component {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<NewJobButton />
|
||||
<DatePickerWrapper />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
<NodeAvailableWarning />
|
||||
|
||||
<div>
|
||||
<div className="actions-bar">
|
||||
<MultiJobActions
|
||||
selectedJobs={this.state.selectedJobs}
|
||||
allJobIds={jobIds}
|
||||
showStartDatafeedModal={this.showStartDatafeedModal}
|
||||
<UpgradeWarning />
|
||||
|
||||
<EuiPageContent>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<JobStatsBar jobsSummaryList={jobsSummaryList} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<NewJobButton />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<div>
|
||||
<div className="actions-bar">
|
||||
<MultiJobActions
|
||||
selectedJobs={this.state.selectedJobs}
|
||||
allJobIds={jobIds}
|
||||
showStartDatafeedModal={this.showStartDatafeedModal}
|
||||
showDeleteJobModal={this.showDeleteJobModal}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
/>
|
||||
<JobFilterBar setFilters={this.setFilters} />
|
||||
</div>
|
||||
<JobsList
|
||||
jobsSummaryList={this.state.filteredJobsSummaryList}
|
||||
fullJobsList={this.state.fullJobsList}
|
||||
itemIdToExpandedRowMap={this.state.itemIdToExpandedRowMap}
|
||||
toggleRow={this.toggleRow}
|
||||
selectJobChange={this.selectJobChange}
|
||||
showEditJobFlyout={this.showEditJobFlyout}
|
||||
showDeleteJobModal={this.showDeleteJobModal}
|
||||
showStartDatafeedModal={this.showStartDatafeedModal}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
selectedJobsCount={this.state.selectedJobs.length}
|
||||
loading={loading}
|
||||
/>
|
||||
<EditJobFlyout
|
||||
setShowFunction={this.setShowEditJobFlyoutFunction}
|
||||
unsetShowFunction={this.unsetShowEditJobFlyoutFunction}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
allJobIds={jobIds}
|
||||
/>
|
||||
<DeleteJobModal
|
||||
setShowFunction={this.setShowDeleteJobModalFunction}
|
||||
unsetShowFunction={this.unsetShowDeleteJobModalFunction}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
/>
|
||||
<JobFilterBar setFilters={this.setFilters} />
|
||||
<StartDatafeedModal
|
||||
setShowFunction={this.setShowStartDatafeedModalFunction}
|
||||
unsetShowFunction={this.unsetShowDeleteJobModalFunction}
|
||||
getShowCreateWatchFlyoutFunction={this.getShowCreateWatchFlyoutFunction}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
/>
|
||||
<CreateWatchFlyout
|
||||
setShowFunction={this.setShowCreateWatchFlyoutFunction}
|
||||
unsetShowFunction={this.unsetShowCreateWatchFlyoutFunction}
|
||||
/>
|
||||
</div>
|
||||
<JobsList
|
||||
jobsSummaryList={this.state.filteredJobsSummaryList}
|
||||
fullJobsList={this.state.fullJobsList}
|
||||
itemIdToExpandedRowMap={this.state.itemIdToExpandedRowMap}
|
||||
toggleRow={this.toggleRow}
|
||||
selectJobChange={this.selectJobChange}
|
||||
showEditJobFlyout={this.showEditJobFlyout}
|
||||
showDeleteJobModal={this.showDeleteJobModal}
|
||||
showStartDatafeedModal={this.showStartDatafeedModal}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
selectedJobsCount={this.state.selectedJobs.length}
|
||||
loading={loading}
|
||||
/>
|
||||
<EditJobFlyout
|
||||
setShowFunction={this.setShowEditJobFlyoutFunction}
|
||||
unsetShowFunction={this.unsetShowEditJobFlyoutFunction}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
allJobIds={jobIds}
|
||||
/>
|
||||
<DeleteJobModal
|
||||
setShowFunction={this.setShowDeleteJobModalFunction}
|
||||
unsetShowFunction={this.unsetShowDeleteJobModalFunction}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
/>
|
||||
<StartDatafeedModal
|
||||
setShowFunction={this.setShowStartDatafeedModalFunction}
|
||||
unsetShowFunction={this.unsetShowDeleteJobModalFunction}
|
||||
getShowCreateWatchFlyoutFunction={this.getShowCreateWatchFlyoutFunction}
|
||||
refreshJobs={() => this.refreshJobSummaryList(true)}
|
||||
/>
|
||||
<CreateWatchFlyout
|
||||
setShowFunction={this.setShowCreateWatchFlyoutFunction}
|
||||
unsetShowFunction={this.unsetShowCreateWatchFlyoutFunction}
|
||||
/>
|
||||
</div>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
|
|
|
@ -21,7 +21,7 @@ interface JobsPageProps {
|
|||
export const JobsPage: FC<JobsPageProps> = (props) => {
|
||||
return (
|
||||
<div data-test-subj="mlPageJobManagement">
|
||||
<NavigationMenu tabId="jobs" />
|
||||
<NavigationMenu tabId="anomaly_detection" />
|
||||
<JobsListView {...props} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -28,6 +28,13 @@ export const ANOMALY_DETECTION_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
|||
href: '#/jobs',
|
||||
});
|
||||
|
||||
export const DATA_FRAME_ANALYTICS_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.dataFrameAnalyticsLabel', {
|
||||
defaultMessage: 'Data Frame Analytics',
|
||||
}),
|
||||
href: '#/data_frame_analytics',
|
||||
});
|
||||
|
||||
export const DATA_VISUALIZER_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
|
||||
text: i18n.translate('xpack.ml.datavisualizerBreadcrumbLabel', {
|
||||
defaultMessage: 'Data Visualizer',
|
||||
|
|
|
@ -13,13 +13,14 @@ import { useResolver } from '../../use_resolver';
|
|||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_exploration';
|
||||
import { ANALYSIS_CONFIG_TYPE } from '../../../data_frame_analytics/common/analytics';
|
||||
import { ML_BREADCRUMB } from '../../breadcrumbs';
|
||||
import { ML_BREADCRUMB, DATA_FRAME_ANALYTICS_BREADCRUMB } from '../../breadcrumbs';
|
||||
|
||||
const breadcrumbs = [
|
||||
ML_BREADCRUMB,
|
||||
DATA_FRAME_ANALYTICS_BREADCRUMB,
|
||||
{
|
||||
text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.dataFrameExplorationLabel', {
|
||||
defaultMessage: 'Data Frame Analytics',
|
||||
defaultMessage: 'Exploration',
|
||||
}),
|
||||
href: '',
|
||||
},
|
||||
|
|
|
@ -11,13 +11,14 @@ import { MlRoute, PageLoader, PageProps } from '../../router';
|
|||
import { useResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_management';
|
||||
import { ML_BREADCRUMB } from '../../breadcrumbs';
|
||||
import { ML_BREADCRUMB, DATA_FRAME_ANALYTICS_BREADCRUMB } from '../../breadcrumbs';
|
||||
|
||||
const breadcrumbs = [
|
||||
ML_BREADCRUMB,
|
||||
DATA_FRAME_ANALYTICS_BREADCRUMB,
|
||||
{
|
||||
text: i18n.translate('xpack.ml.dataFrameAnalyticsBreadcrumbs.dataFrameListLabel', {
|
||||
defaultMessage: 'Data Frame Analytics',
|
||||
defaultMessage: 'Job Management',
|
||||
}),
|
||||
href: '',
|
||||
},
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
checkPermission,
|
||||
} from '../../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { Settings } from '../../../settings';
|
||||
import { AnomalyDetectionSettingsContext, Settings } from '../../../settings';
|
||||
import { ML_BREADCRUMB, SETTINGS } from '../../breadcrumbs';
|
||||
|
||||
const breadcrumbs = [ML_BREADCRUMB, SETTINGS];
|
||||
|
@ -42,11 +42,17 @@ const PageWrapper: FC<PageProps> = ({ deps }) => {
|
|||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
const canGetFilters = checkPermission('canGetFilters');
|
||||
const canCreateFilter = checkPermission('canCreateFilter');
|
||||
const canGetCalendars = checkPermission('canGetCalendars');
|
||||
const canCreateCalendar = checkPermission('canCreateCalendar');
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Settings canGetCalendars={canGetCalendars} canGetFilters={canGetFilters} />
|
||||
<AnomalyDetectionSettingsContext.Provider
|
||||
value={{ canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar }}
|
||||
>
|
||||
<Settings />
|
||||
</AnomalyDetectionSettingsContext.Provider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
.mlSettingsPage {
|
||||
|
||||
.mlSettingsPage__header {
|
||||
width: map-get($euiBreakpoints, 'xl');
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.mlSettingsPage__content {
|
||||
width: map-get($euiBreakpoints, 'xl');
|
||||
margin-top: $euiSize;
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC, Fragment, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
EuiBadge,
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPageContent,
|
||||
EuiPageContentHeader,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTextColor,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context';
|
||||
import { useNotifications } from '../contexts/kibana';
|
||||
import { ml } from '../services/ml_api_service';
|
||||
|
||||
export const AnomalyDetectionSettings: FC = () => {
|
||||
const [calendarsCount, setCalendarsCount] = useState(0);
|
||||
const [filterListsCount, setFilterListsCount] = useState(0);
|
||||
|
||||
const { canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar } = useContext(
|
||||
AnomalyDetectionSettingsContext
|
||||
);
|
||||
|
||||
const { toasts } = useNotifications();
|
||||
|
||||
useEffect(() => {
|
||||
loadSummaryStats();
|
||||
}, []);
|
||||
|
||||
async function loadSummaryStats() {
|
||||
// Obtain the counts of calendars and filter lists.
|
||||
if (canGetCalendars === true) {
|
||||
try {
|
||||
const calendars = await ml.calendars();
|
||||
setCalendarsCount(calendars.length);
|
||||
} catch (e) {
|
||||
toasts.addDanger(
|
||||
i18n.translate('xpack.ml.settings.anomalyDetection.loadingCalendarsCountErrorMessage', {
|
||||
defaultMessage: 'An error occurred obtaining the count of calendars',
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (canGetFilters === true) {
|
||||
try {
|
||||
const filterLists = await ml.filters.filtersStats();
|
||||
setFilterListsCount(filterLists.length);
|
||||
} catch (e) {
|
||||
toasts.addDanger(
|
||||
i18n.translate('xpack.ml.settings.anomalyDetection.loadingFilterListCountErrorMessage', {
|
||||
defaultMessage: 'An error occurred obtaining the count of filter lists',
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiPageContent className="mlSettingsPage__content" horizontalPosition="center">
|
||||
<EuiPageContentHeader>
|
||||
<EuiTitle>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.anomalyDetectionTitle"
|
||||
defaultMessage="Anomaly Detection"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
|
||||
<EuiFlexGroup gutterSize="xl">
|
||||
<EuiFlexItem grow={5}>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.calendarsTitle"
|
||||
defaultMessage="Calendars"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s">
|
||||
<EuiTextColor color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.calendarsText"
|
||||
defaultMessage="Calendars contain a list of scheduled events for which you do not want to generate anomalies, such as planned system outages or public holidays."
|
||||
/>
|
||||
</p>
|
||||
</EuiTextColor>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup alignItems="center">
|
||||
{canGetCalendars && (
|
||||
<EuiFlexItem grow={false} style={{ display: 'block' }}>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.calendarsSummaryCount"
|
||||
defaultMessage="You have {calendarsCountBadge} {calendarsCount, plural, one {calendar} other {calendars}}"
|
||||
values={{
|
||||
calendarsCountBadge: <EuiBadge>{calendarsCount}</EuiBadge>,
|
||||
calendarsCount,
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="mlCalendarsMngButton"
|
||||
flush="left"
|
||||
size="l"
|
||||
color="primary"
|
||||
href="#/settings/calendars_list"
|
||||
isDisabled={canGetCalendars === false}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.manageCalendarsLink"
|
||||
defaultMessage="Manage"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="mlCalendarsCreateButton"
|
||||
flush="left"
|
||||
size="l"
|
||||
color="primary"
|
||||
href="#/settings/calendars_list/new_calendar"
|
||||
isDisabled={canCreateCalendar === false}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.createCalendarLink"
|
||||
defaultMessage="Create"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={5}>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.filterListsTitle"
|
||||
defaultMessage="Filter Lists"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s">
|
||||
<EuiTextColor color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.filterListsText"
|
||||
defaultMessage=" Filter lists contain values that you can use to include or exclude events from the machine learning analysis."
|
||||
/>
|
||||
</p>
|
||||
</EuiTextColor>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup alignItems="center">
|
||||
{canGetFilters && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.filterListsSummaryCount"
|
||||
defaultMessage="You have {filterListsCountBadge} {filterListsCount, plural, one {filter list} other {filter lists}}"
|
||||
values={{
|
||||
filterListsCountBadge: <EuiBadge>{filterListsCount}</EuiBadge>,
|
||||
filterListsCount,
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="mlFilterListsMngButton"
|
||||
flush="left"
|
||||
size="l"
|
||||
color="primary"
|
||||
href="#/settings/filter_lists"
|
||||
isDisabled={canGetFilters === false}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.manageFilterListsLink"
|
||||
defaultMessage="Manage"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="mlFilterListsCreateButton"
|
||||
size="l"
|
||||
color="primary"
|
||||
href="#/settings/filter_lists/new_filter_list"
|
||||
isDisabled={canCreateFilter === false}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.anomalyDetection.createFilterListsLink"
|
||||
defaultMessage="Create"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { createContext } from 'react';
|
||||
|
||||
export interface AnomalyDetectionSettingsContextValue {
|
||||
canGetFilters: boolean;
|
||||
canCreateFilter: boolean;
|
||||
canGetCalendars: boolean;
|
||||
canCreateCalendar: boolean;
|
||||
}
|
||||
|
||||
export const AnomalyDetectionSettingsContext = createContext<AnomalyDetectionSettingsContextValue>({
|
||||
canGetFilters: false,
|
||||
canCreateFilter: false,
|
||||
canGetCalendars: false,
|
||||
canCreateCalendar: false,
|
||||
});
|
|
@ -150,7 +150,7 @@ exports[`CalendarForm Renders calendar form 1`] = `
|
|||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
data-test-subj="ml_save_calendar_button"
|
||||
data-test-subj="mlSaveCalendarButton"
|
||||
fill={true}
|
||||
isDisabled={true}
|
||||
onClick={[MockFunction]}
|
||||
|
|
|
@ -221,7 +221,7 @@ export const CalendarForm = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="ml_save_calendar_button"
|
||||
data-test-subj="mlSaveCalendarButton"
|
||||
fill
|
||||
onClick={isEdit ? onEdit : onCreate}
|
||||
isDisabled={saveButtonDisabled}
|
||||
|
|
|
@ -133,7 +133,7 @@ exports[`EventsTable Renders events table with search bar 1`] = `
|
|||
"filters": Array [],
|
||||
"toolsRight": Array [
|
||||
<EuiButton
|
||||
data-test-subj="ml_new_event"
|
||||
data-test-subj="mlNewEvent"
|
||||
iconType="plusInCircle"
|
||||
isDisabled={false}
|
||||
onClick={[MockFunction]}
|
||||
|
@ -146,7 +146,7 @@ exports[`EventsTable Renders events table with search bar 1`] = `
|
|||
/>
|
||||
</EuiButton>,
|
||||
<EuiButton
|
||||
data-test-subj="ml_import_events"
|
||||
data-test-subj="mlImportEvents"
|
||||
iconType="importAction"
|
||||
isDisabled={false}
|
||||
onClick={[MockFunction]}
|
||||
|
|
|
@ -91,7 +91,7 @@ export const EventsTable = ({
|
|||
name: '',
|
||||
render: (event) => (
|
||||
<DeleteButton
|
||||
data-test-subj="event_delete"
|
||||
data-test-subj="mlEventDelete"
|
||||
canDeleteCalendar={canDeleteCalendar}
|
||||
onClick={() => {
|
||||
onDeleteClick(event.event_id);
|
||||
|
@ -106,7 +106,7 @@ export const EventsTable = ({
|
|||
<EuiButton
|
||||
isDisabled={canCreateCalendar === false}
|
||||
key="ml_new_event"
|
||||
data-test-subj="ml_new_event"
|
||||
data-test-subj="mlNewEvent"
|
||||
size="s"
|
||||
iconType="plusInCircle"
|
||||
onClick={showNewEventModal}
|
||||
|
@ -119,7 +119,7 @@ export const EventsTable = ({
|
|||
<EuiButton
|
||||
isDisabled={canCreateCalendar === false}
|
||||
key="ml_import_event"
|
||||
data-test-subj="ml_import_events"
|
||||
data-test-subj="mlImportEvents"
|
||||
size="s"
|
||||
iconType="importAction"
|
||||
onClick={showImportModal}
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('ImportModal', () => {
|
|||
instance.setState(testState);
|
||||
wrapper.update();
|
||||
expect(wrapper.state('selectedEvents').length).toBe(2);
|
||||
const deleteButton = wrapper.find('[data-test-subj="event_delete"]');
|
||||
const deleteButton = wrapper.find('[data-test-subj="mlEventDelete"]');
|
||||
const button = deleteButton.find('EuiButtonEmpty').first();
|
||||
button.simulate('click');
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ describe('NewCalendar', () => {
|
|||
test('Import modal shown on Import Events button click', () => {
|
||||
const wrapper = mountWithIntl(<NewCalendar {...props} />);
|
||||
|
||||
const importButton = wrapper.find('[data-test-subj="ml_import_events"]');
|
||||
const importButton = wrapper.find('[data-test-subj="mlImportEvents"]');
|
||||
const button = importButton.find('EuiButton');
|
||||
button.simulate('click');
|
||||
|
||||
|
@ -127,7 +127,7 @@ describe('NewCalendar', () => {
|
|||
test('New event modal shown on New event button click', () => {
|
||||
const wrapper = mountWithIntl(<NewCalendar {...props} />);
|
||||
|
||||
const importButton = wrapper.find('[data-test-subj="ml_new_event"]');
|
||||
const importButton = wrapper.find('[data-test-subj="mlNewEvent"]');
|
||||
const button = importButton.find('EuiButton');
|
||||
button.simulate('click');
|
||||
|
||||
|
@ -154,7 +154,7 @@ describe('NewCalendar', () => {
|
|||
|
||||
const wrapper = mountWithIntl(<NewCalendar {...noCreateProps} />);
|
||||
|
||||
const buttons = wrapper.find('[data-test-subj="ml_save_calendar_button"]');
|
||||
const buttons = wrapper.find('[data-test-subj="mlSaveCalendarButton"]');
|
||||
const saveButton = buttons.find('EuiButton');
|
||||
|
||||
expect(saveButton.prop('isDisabled')).toBe(true);
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context';
|
||||
export { Settings } from './settings';
|
||||
|
|
|
@ -1,58 +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 { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import React from 'react';
|
||||
|
||||
import { Settings } from './settings';
|
||||
|
||||
jest.mock('../components/navigation_menu', () => ({
|
||||
NavigationMenu: () => <div id="mockNavigationMenu" />,
|
||||
}));
|
||||
|
||||
describe('Settings', () => {
|
||||
test('Renders settings page with all buttons enabled.', () => {
|
||||
const wrapper = mountWithIntl(<Settings canGetFilters={true} canGetCalendars={true} />);
|
||||
|
||||
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(false);
|
||||
});
|
||||
|
||||
test('Filter Lists button disabled if canGetFilters is false', () => {
|
||||
const wrapper = mountWithIntl(<Settings canGetFilters={false} canGetCalendars={true} />);
|
||||
|
||||
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 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);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import React from 'react';
|
||||
|
||||
import { AnomalyDetectionSettingsContext } from './anomaly_detection_settings_context';
|
||||
import { Settings } from './settings';
|
||||
|
||||
jest.mock('../components/navigation_menu', () => ({
|
||||
NavigationMenu: () => <div id="mockNavigationMenu" />,
|
||||
}));
|
||||
|
||||
jest.mock('../contexts/kibana', () => ({
|
||||
useNotifications: () => {
|
||||
return {
|
||||
toasts: { addDanger: jest.fn() },
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Settings', () => {
|
||||
function runCheckButtonsDisabledTest(
|
||||
canGetFilters: boolean,
|
||||
canCreateFilter: boolean,
|
||||
canGetCalendars: boolean,
|
||||
canCreateCalendar: boolean,
|
||||
isFilterListsMngDisabled: boolean,
|
||||
isFilterListCreateDisabled: boolean,
|
||||
isCalendarsMngDisabled: boolean,
|
||||
isCalendarCreateDisabled: boolean
|
||||
) {
|
||||
const wrapper = mountWithIntl(
|
||||
<AnomalyDetectionSettingsContext.Provider
|
||||
value={{ canGetFilters, canCreateFilter, canGetCalendars, canCreateCalendar }}
|
||||
>
|
||||
<Settings />
|
||||
</AnomalyDetectionSettingsContext.Provider>
|
||||
);
|
||||
|
||||
const filterMngButton = wrapper
|
||||
.find('[data-test-subj="mlFilterListsMngButton"]')
|
||||
.find('EuiButtonEmpty');
|
||||
expect(filterMngButton.prop('isDisabled')).toBe(isFilterListsMngDisabled);
|
||||
|
||||
const filterCreateButton = wrapper
|
||||
.find('[data-test-subj="mlFilterListsCreateButton"]')
|
||||
.find('EuiButtonEmpty');
|
||||
expect(filterCreateButton.prop('isDisabled')).toBe(isFilterListCreateDisabled);
|
||||
|
||||
const calendarMngButton = wrapper
|
||||
.find('[data-test-subj="mlCalendarsMngButton"]')
|
||||
.find('EuiButtonEmpty');
|
||||
expect(calendarMngButton.prop('isDisabled')).toBe(isCalendarsMngDisabled);
|
||||
|
||||
const calendarCreateButton = wrapper
|
||||
.find('[data-test-subj="mlCalendarsCreateButton"]')
|
||||
.find('EuiButtonEmpty');
|
||||
expect(calendarCreateButton.prop('isDisabled')).toBe(isCalendarCreateDisabled);
|
||||
}
|
||||
|
||||
test('should render settings page with all buttons enabled when full user capabilities', () => {
|
||||
runCheckButtonsDisabledTest(true, true, true, true, false, false, false, false);
|
||||
});
|
||||
|
||||
test('should disable Filter Lists buttons if filters capabilities are false', () => {
|
||||
runCheckButtonsDisabledTest(false, false, true, true, true, true, false, false);
|
||||
});
|
||||
|
||||
test('should disable Calendars buttons if calendars capabilities are false', () => {
|
||||
runCheckButtonsDisabledTest(true, true, false, false, false, false, true, true);
|
||||
});
|
||||
});
|
|
@ -6,76 +6,30 @@
|
|||
|
||||
import React, { FC, Fragment } from 'react';
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPage,
|
||||
EuiPageContentHeader,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { EuiPage, EuiPageBody, EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { AnomalyDetectionSettings } from './anomaly_detection_settings';
|
||||
|
||||
import { NavigationMenu } from '../components/navigation_menu';
|
||||
|
||||
interface Props {
|
||||
canGetFilters: boolean;
|
||||
canGetCalendars: boolean;
|
||||
}
|
||||
|
||||
export const Settings: FC<Props> = ({ canGetFilters, canGetCalendars }) => {
|
||||
export const Settings: FC = () => {
|
||||
return (
|
||||
<Fragment>
|
||||
<NavigationMenu tabId="settings" />
|
||||
<EuiPage className="mlSettingsPage" data-test-subj="mlPageSettings">
|
||||
<EuiPageBody className="mlSettingsPage__body">
|
||||
<EuiPageContent className="mlSettingsPage__content" horizontalPosition="center">
|
||||
<EuiPageContentHeader>
|
||||
<EuiPageBody>
|
||||
<EuiPageHeader className="mlSettingsPage__header">
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.jobManagementTitle"
|
||||
defaultMessage="Job Management"
|
||||
/>
|
||||
<FormattedMessage id="xpack.ml.settings.title" defaultMessage="Settings" />
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageContentHeader>
|
||||
|
||||
<EuiFlexGroup gutterSize="xl">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="ml_calendar_mng_button"
|
||||
size="l"
|
||||
color="primary"
|
||||
href="#/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="#/settings/filter_lists"
|
||||
isDisabled={canGetFilters === false}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.settings.filterListsButtonLabel"
|
||||
defaultMessage="Filter Lists"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<AnomalyDetectionSettings />
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</Fragment>
|
||||
|
|
|
@ -491,7 +491,6 @@ export class ForecastingModalUI extends Component {
|
|||
<EuiButton
|
||||
onClick={this.openModal}
|
||||
isDisabled={isForecastingDisabled}
|
||||
fill
|
||||
data-test-subj="mlSingleMetricViewerButtonForecast"
|
||||
>
|
||||
<FormattedMessage
|
||||
|
|
|
@ -1107,11 +1107,7 @@ export class TimeSeriesExplorer extends React.Component {
|
|||
let hasEmptyFieldValues = false;
|
||||
|
||||
return (
|
||||
<TimeSeriesExplorerPage
|
||||
dateFormatTz={dateFormatTz}
|
||||
loading={loading}
|
||||
resizeRef={this.resizeRef}
|
||||
>
|
||||
<TimeSeriesExplorerPage dateFormatTz={dateFormatTz} resizeRef={this.resizeRef}>
|
||||
{fieldNamesWithEmptyValues.length > 0 && (
|
||||
<EuiCallOut
|
||||
className="single-metric-request-callout"
|
||||
|
|
|
@ -8,42 +8,36 @@ import React, { FC } from 'react';
|
|||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { EuiPage, EuiPageBody, EuiProgress, EuiScreenReaderOnly } from '@elastic/eui';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { AnomalyResultsViewSelector } from '../components/anomaly_results_view_selector';
|
||||
import { JobSelector } from '../components/job_selector';
|
||||
import { NavigationMenu } from '../components/navigation_menu';
|
||||
import { DatePickerWrapper } from '../components/navigation_menu/date_picker_wrapper';
|
||||
|
||||
interface TimeSeriesExplorerPageProps {
|
||||
dateFormatTz: string;
|
||||
loading?: boolean;
|
||||
resizeRef?: any;
|
||||
}
|
||||
|
||||
export const TimeSeriesExplorerPage: FC<TimeSeriesExplorerPageProps> = ({
|
||||
children,
|
||||
dateFormatTz,
|
||||
loading,
|
||||
resizeRef,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<NavigationMenu tabId="timeseriesexplorer" />
|
||||
{/* Show animated progress bar while loading */}
|
||||
{loading === true && (
|
||||
<EuiProgress className="mlTimeSeriesExplorerProgress" color="primary" size="xs" />
|
||||
)}
|
||||
{/* Show a progress bar with progress 0% when not loading.
|
||||
If we'd just show no progress bar when not loading it would result in a flickering height effect. */}
|
||||
{loading === false && (
|
||||
<EuiProgress
|
||||
className="mlTimeSeriesExplorerProgress"
|
||||
value={0}
|
||||
max={100}
|
||||
color="primary"
|
||||
size="xs"
|
||||
/>
|
||||
)}
|
||||
<JobSelector dateFormatTz={dateFormatTz} singleSelection={true} timeseriesOnly={true} />
|
||||
<NavigationMenu tabId="anomaly_detection" />
|
||||
<div
|
||||
className="ml-time-series-explorer"
|
||||
ref={resizeRef}
|
||||
|
@ -51,14 +45,36 @@ export const TimeSeriesExplorerPage: FC<TimeSeriesExplorerPageProps> = ({
|
|||
>
|
||||
<EuiPage style={{ background: 'none' }}>
|
||||
<EuiPageBody>
|
||||
<EuiScreenReaderOnly>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.timeSeriesExplorer.pageTitle"
|
||||
defaultMessage="Single Metric Viewer"
|
||||
/>
|
||||
</h1>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<AnomalyResultsViewSelector viewId="timeseriesexplorer" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle className="eui-textNoWrap">
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.timeSeriesExplorer.pageTitle"
|
||||
defaultMessage="Single Metric Viewer"
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<DatePickerWrapper />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<JobSelector dateFormatTz={dateFormatTz} singleSelection={true} timeseriesOnly={true} />
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<EuiSpacer size="s" />
|
||||
{children}
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
|
|
|
@ -398,6 +398,9 @@
|
|||
"core.ui.securityNavList.label": "セキュリティ",
|
||||
"core.ui.welcomeErrorMessage": "Elastic Kibana が正常に読み込まれませんでした。詳細はサーバーアウトプットを確認してください。",
|
||||
"core.ui.welcomeMessage": "Elastic Kibana の読み込み中",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "{advancedSettingsLink}で{storeInSessionStorageParam}オプションを有効にするか、オンスクリーンビジュアルを簡素化してください。",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高度な設定",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URLが大きく、Kibanaの動作が停止する可能性があります",
|
||||
"dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化",
|
||||
"dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "全画面",
|
||||
"dashboard.addExistingVisualizationLinkText": "既存のユーザーを追加",
|
||||
|
@ -2116,6 +2119,28 @@
|
|||
"share.advancedSettings.csv.quoteValuesTitle": "CSV の値を引用",
|
||||
"share.advancedSettings.csv.separatorText": "エクスポートされた値をこの文字列で区切ります",
|
||||
"share.advancedSettings.csv.separatorTitle": "CSV セパレーター",
|
||||
"share.contextMenu.embedCodeLabel": "埋め込みコード",
|
||||
"share.contextMenu.embedCodePanelTitle": "埋め込みコード",
|
||||
"share.contextMenu.permalinkPanelTitle": "パーマリンク",
|
||||
"share.contextMenu.permalinksLabel": "パーマリンク",
|
||||
"share.contextMenuTitle": "この {objectType} を共有",
|
||||
"share.urlGenerators.error.createUrlFnProvided": "このジェネレーターは非推奨とマークされています。createUrl fn を付けないでください。",
|
||||
"share.urlGenerators.error.migrateCalledNotDeprecated": "非推奨以外のジェネレーターで migrate を呼び出すことはできません。",
|
||||
"share.urlGenerators.error.migrationFnGivenNotDeprecated": "移行機能を提供する場合、このジェネレーターに非推奨マークを付ける必要があります",
|
||||
"share.urlGenerators.error.noCreateUrlFnProvided": "このジェネレーターには非推奨のマークがありません。createUrl fn を付けてください。",
|
||||
"share.urlGenerators.error.noMigrationFnProvided": "アクセスリンクジェネレーターに非推奨マークが付いている場合、移行機能を提供する必要があります。",
|
||||
"share.urlGenerators.errors.noGeneratorWithId": "{id} という ID のジェネレーターはありません",
|
||||
"share.urlPanel.canNotShareAsSavedObjectHelpText": "{objectType} が保存されるまで保存されたオブジェクトを共有することはできません。",
|
||||
"share.urlPanel.copyIframeCodeButtonLabel": "iFrame コードをコピー",
|
||||
"share.urlPanel.copyLinkButtonLabel": "リンクをコピー",
|
||||
"share.urlPanel.generateLinkAsLabel": "名前を付けてリンクを生成",
|
||||
"share.urlPanel.savedObjectDescription": "この URL を共有することで、他のユーザーがこの {objectType} の最も最近保存されたバージョンを読み込めるようになります。",
|
||||
"share.urlPanel.savedObjectLabel": "保存されたオブジェクト",
|
||||
"share.urlPanel.shortUrlHelpText": "互換性が最も高くなるよう、短いスナップショット URL を共有することをお勧めします。Internet Explorer は URL の長さに制限があり、一部の wiki やマークアップパーサーは長い完全なスナップショット URL に対応していませんが、短い URL は正常に動作するはずです。",
|
||||
"share.urlPanel.shortUrlLabel": "短い URL",
|
||||
"share.urlPanel.snapshotDescription": "スナップショット URL には、{objectType} の現在の状態がエンコードされています。保存された {objectType} への編集内容はこの URL には反映されません。.",
|
||||
"share.urlPanel.snapshotLabel": "スナップショット",
|
||||
"share.urlPanel.unableCreateShortUrlErrorMessage": "短い URL を作成できません。エラー: {errorMessage}",
|
||||
"kbn.advancedSettings.darkModeText": "Kibana UI のダークモードを有効にします。この設定を適用するにはページの更新が必要です。",
|
||||
"kbn.advancedSettings.darkModeTitle": "ダークモード",
|
||||
"kbn.advancedSettings.dateFormat.dayOfWeekText": "週の初めの曜日を設定します",
|
||||
|
@ -2167,9 +2192,37 @@
|
|||
"kbn.advancedSettings.visualization.tileMap.wmsDefaultsTitle": "デフォルトの WMS プロパティ",
|
||||
"visualizations.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。",
|
||||
"visualizations.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "{advancedSettingsLink}で{storeInSessionStorageParam}オプションを有効にするか、オンスクリーンビジュアルを簡素化してください。",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高度な設定",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URLが大きく、Kibanaの動作が停止する可能性があります",
|
||||
"visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。",
|
||||
"visualizations.disabledLabVisualizationTitle": "{title} はラボビジュアライゼーションです。",
|
||||
"visualizations.displayName": "ビジュアライゼーション",
|
||||
"visualizations.function.range.from.help": "範囲の開始",
|
||||
"visualizations.function.range.help": "範囲オブジェクトを生成します",
|
||||
"visualizations.function.range.to.help": "範囲の終了",
|
||||
"visualizations.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名)",
|
||||
"visualizations.function.visDimension.error.accessor": "入力された列名は無効です。",
|
||||
"visualizations.function.visDimension.format.help": "フォーマット",
|
||||
"visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター",
|
||||
"visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します",
|
||||
"visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです",
|
||||
"visualizations.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません",
|
||||
"visualizations.newVisWizard.betaTitle": "ベータ",
|
||||
"visualizations.newVisWizard.chooseSourceTitle": "ソースの選択",
|
||||
"visualizations.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。",
|
||||
"visualizations.newVisWizard.experimentalTitle": "実験的",
|
||||
"visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。",
|
||||
"visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング",
|
||||
"visualizations.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。",
|
||||
"visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました",
|
||||
"visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索",
|
||||
"visualizations.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください",
|
||||
"visualizations.newVisWizard.title": "新規ビジュアライゼーション",
|
||||
"visualizations.newVisWizard.visTypeAliasDescription": "Visualize 外で Kibana アプリケーションを開きます。",
|
||||
"visualizations.newVisWizard.visTypeAliasTitle": "Kibana アプリケーション",
|
||||
"visualizations.savedObjectName": "ビジュアライゼーション",
|
||||
"kibana_legacy.notify.fatalError.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}",
|
||||
"kibana_legacy.notify.fatalError.unavailableServerErrorMessage": "HTTP リクエストで接続に失敗しました。Kibana サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。",
|
||||
"kibana_legacy.notify.toaster.errorMessage": "エラー: {errorMessage}\n {errorStack}",
|
||||
|
@ -2421,28 +2474,6 @@
|
|||
"server.status.redTitle": "赤",
|
||||
"server.status.uninitializedTitle": "アンインストールしました",
|
||||
"server.status.yellowTitle": "黄色",
|
||||
"share.contextMenu.embedCodeLabel": "埋め込みコード",
|
||||
"share.contextMenu.embedCodePanelTitle": "埋め込みコード",
|
||||
"share.contextMenu.permalinkPanelTitle": "パーマリンク",
|
||||
"share.contextMenu.permalinksLabel": "パーマリンク",
|
||||
"share.contextMenuTitle": "この {objectType} を共有",
|
||||
"share.urlGenerators.error.createUrlFnProvided": "このジェネレーターは非推奨とマークされています。createUrl fn を付けないでください。",
|
||||
"share.urlGenerators.error.migrateCalledNotDeprecated": "非推奨以外のジェネレーターで migrate を呼び出すことはできません。",
|
||||
"share.urlGenerators.error.migrationFnGivenNotDeprecated": "移行機能を提供する場合、このジェネレーターに非推奨マークを付ける必要があります",
|
||||
"share.urlGenerators.error.noCreateUrlFnProvided": "このジェネレーターには非推奨のマークがありません。createUrl fn を付けてください。",
|
||||
"share.urlGenerators.error.noMigrationFnProvided": "アクセスリンクジェネレーターに非推奨マークが付いている場合、移行機能を提供する必要があります。",
|
||||
"share.urlGenerators.errors.noGeneratorWithId": "{id} という ID のジェネレーターはありません",
|
||||
"share.urlPanel.canNotShareAsSavedObjectHelpText": "{objectType} が保存されるまで保存されたオブジェクトを共有することはできません。",
|
||||
"share.urlPanel.copyIframeCodeButtonLabel": "iFrame コードをコピー",
|
||||
"share.urlPanel.copyLinkButtonLabel": "リンクをコピー",
|
||||
"share.urlPanel.generateLinkAsLabel": "名前を付けてリンクを生成",
|
||||
"share.urlPanel.savedObjectDescription": "この URL を共有することで、他のユーザーがこの {objectType} の最も最近保存されたバージョンを読み込めるようになります。",
|
||||
"share.urlPanel.savedObjectLabel": "保存されたオブジェクト",
|
||||
"share.urlPanel.shortUrlHelpText": "互換性が最も高くなるよう、短いスナップショット URL を共有することをお勧めします。Internet Explorer は URL の長さに制限があり、一部の wiki やマークアップパーサーは長い完全なスナップショット URL に対応していませんが、短い URL は正常に動作するはずです。",
|
||||
"share.urlPanel.shortUrlLabel": "短い URL",
|
||||
"share.urlPanel.snapshotDescription": "スナップショット URL には、{objectType} の現在の状態がエンコードされています。保存された {objectType} への編集内容はこの URL には反映されません。.",
|
||||
"share.urlPanel.snapshotLabel": "スナップショット",
|
||||
"share.urlPanel.unableCreateShortUrlErrorMessage": "短い URL を作成できません。エラー: {errorMessage}",
|
||||
"statusPage.loadStatus.serverIsDownErrorMessage": "サーバーステータスのリクエストに失敗しました。サーバーがダウンしている可能性があります。",
|
||||
"statusPage.loadStatus.serverStatusCodeErrorMessage": "サーバーステータスのリクエストに失敗しました。ステータスコード: {responseStatus}",
|
||||
"statusPage.metricsTiles.columns.heapTotalHeader": "ヒープ合計",
|
||||
|
@ -3839,37 +3870,6 @@
|
|||
"visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション",
|
||||
"visTypeVislib.vislib.tooltip.fieldLabel": "フィールド",
|
||||
"visTypeVislib.vislib.tooltip.valueLabel": "値",
|
||||
"visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。",
|
||||
"visualizations.disabledLabVisualizationTitle": "{title} はラボビジュアライゼーションです。",
|
||||
"visualizations.displayName": "ビジュアライゼーション",
|
||||
"visualizations.function.range.from.help": "範囲の開始",
|
||||
"visualizations.function.range.help": "範囲オブジェクトを生成します",
|
||||
"visualizations.function.range.to.help": "範囲の終了",
|
||||
"visualizations.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名)",
|
||||
"visualizations.function.visDimension.error.accessor": "入力された列名は無効です。",
|
||||
"visualizations.function.visDimension.format.help": "フォーマット",
|
||||
"visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター",
|
||||
"visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します",
|
||||
"visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです",
|
||||
"visualizations.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません",
|
||||
"visualizations.newVisWizard.betaTitle": "ベータ",
|
||||
"visualizations.newVisWizard.chooseSourceTitle": "ソースの選択",
|
||||
"visualizations.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。",
|
||||
"visualizations.newVisWizard.experimentalTitle": "実験的",
|
||||
"visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。",
|
||||
"visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング",
|
||||
"visualizations.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。",
|
||||
"visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました",
|
||||
"visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索",
|
||||
"visualizations.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください",
|
||||
"visualizations.newVisWizard.title": "新規ビジュアライゼーション",
|
||||
"visualizations.newVisWizard.visTypeAliasDescription": "Visualize 外で Kibana アプリケーションを開きます。",
|
||||
"visualizations.newVisWizard.visTypeAliasTitle": "Kibana アプリケーション",
|
||||
"visualizations.savedObjectName": "ビジュアライゼーション",
|
||||
"visualize.badge.readOnly.text": "読み取り専用",
|
||||
"visualize.badge.readOnly.tooltip": "ビジュアライゼーションを保存できません",
|
||||
"visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "indexPatternまたはsavedSearchIdが必要です",
|
||||
|
@ -3969,6 +3969,8 @@
|
|||
"xpack.uiActionsEnhanced.customizePanelTimeRange.modal.updatePanelTimeRangeButtonTitle": "更新",
|
||||
"xpack.uiActionsEnhanced.customizeTimeRange.modal.headerTitle": "パネルの時間範囲のカスタマイズ",
|
||||
"xpack.uiActionsEnhanced.customizeTimeRangeMenuItem.displayName": "時間範囲のカスタマイズ",
|
||||
"xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "対象インデックスパターンを選択",
|
||||
"xpack.uiActionsEnhanced.drilldown.goToDiscover": "Discoverに移動(例)",
|
||||
"xpack.alerts.alertNavigationRegistry.get.missingNavigationError": "「{consumer}」内のアラートタイプ「{alertType}」のナビゲーションは登録されていません。",
|
||||
"xpack.alerts.alertNavigationRegistry.register.duplicateDefaultError": "「{consumer}」内のデフォルトナビゲーションは既に登録されています。",
|
||||
"xpack.alerts.alertNavigationRegistry.register.duplicateNavigationError": "「{consumer}」内のアラートタイプ「{alertType}」のナビゲーションは既に登録されています。",
|
||||
|
@ -10450,13 +10452,10 @@
|
|||
"xpack.ml.models.jobValidation.validateJobObject.jobIsNotObjectErrorMessage": "無効な {invalidParamName}:オブジェクトでなければなりません。",
|
||||
"xpack.ml.models.jobValidation.validateJobObject.timeFieldIsNotStringErrorMessage": "無効な {invalidParamName}:文字列でなければなりません。",
|
||||
"xpack.ml.navMenu.anomalyDetectionTabLinkText": "異常検知",
|
||||
"xpack.ml.navMenu.anomalyExplorerTabLinkText": "異常エクスプローラー",
|
||||
"xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "分析",
|
||||
"xpack.ml.navMenu.dataVisualizerTabLinkText": "データビジュアライザー",
|
||||
"xpack.ml.navMenu.jobManagementTabLinkText": "ジョブ管理",
|
||||
"xpack.ml.navMenu.overviewTabLinkText": "概要",
|
||||
"xpack.ml.navMenu.settingsTabLinkText": "設定",
|
||||
"xpack.ml.navMenu.singleMetricViewerTabLinkText": "シングルメトリックビューアー",
|
||||
"xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "リセット",
|
||||
"xpack.ml.newJob.page.createJob": "ジョブを作成",
|
||||
"xpack.ml.newJob.recognize.advancedLabel": "高度な設定",
|
||||
|
@ -10885,7 +10884,6 @@
|
|||
"xpack.ml.settings.breadcrumbs.filterLists.createLabel": "作成",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.editLabel": "編集",
|
||||
"xpack.ml.settings.breadcrumbs.filterListsLabel": "フィルターリスト",
|
||||
"xpack.ml.settings.calendarManagementButtonLabel": "カレンダー管理",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription": "カレンダーにはシステム停止日や祝日などの予定を含むことができ、異常検知から除外します。カレンダーは複数のジョブに割り当てることができます。{br}{learnMoreLink}",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "詳細",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsListTotalCount": "合計 {totalCount}",
|
||||
|
@ -10935,8 +10933,6 @@
|
|||
"xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "フィルターが 1 つも作成されていません",
|
||||
"xpack.ml.settings.filterLists.table.notInUseAriaLabel": "使用されていません",
|
||||
"xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "アイテムを削除",
|
||||
"xpack.ml.settings.filterListsButtonLabel": "フィルターリスト",
|
||||
"xpack.ml.settings.jobManagementTitle": "ジョブ管理",
|
||||
"xpack.ml.settingsBreadcrumbLabel": "設定",
|
||||
"xpack.ml.singleMetricViewerPageLabel": "シングルメトリックビューアー",
|
||||
"xpack.ml.stepDefineForm.invalidQuery": "無効なクエリ",
|
||||
|
@ -16377,8 +16373,6 @@
|
|||
"xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, one {秒} other {秒}}",
|
||||
"xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "オブジェクトタイプ「{id}」は登録されていません。",
|
||||
"xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "オブジェクトタイプ「{id}」は既に登録されています。",
|
||||
"xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "対象インデックスパターンを選択",
|
||||
"xpack.uiActionsEnhanced.drilldown.goToDiscover": "Discoverに移動(例)",
|
||||
"xpack.upgradeAssistant.appTitle": "{version} アップグレードアシスタント",
|
||||
"xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail": "{snapshotRestoreDocsButton} でデータをバックアップします。",
|
||||
"xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel": "API のスナップショットと復元",
|
||||
|
|
|
@ -398,6 +398,9 @@
|
|||
"core.ui.securityNavList.label": "安全",
|
||||
"core.ui.welcomeErrorMessage": "Elastic Kibana 未正确加载。检查服务器输出以了解详情。",
|
||||
"core.ui.welcomeMessage": "正在加载 Elastic Kibana",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "在{advancedSettingsLink}中启用“{storeInSessionStorageParam}”选项或简化屏幕视觉效果。",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高级设置",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URL 过长,Kibana 可能无法工作",
|
||||
"dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化",
|
||||
"dashboard.actions.toggleExpandPanelMenuItem.notExpandedDisplayName": "全屏",
|
||||
"dashboard.addExistingVisualizationLinkText": "将现有",
|
||||
|
@ -2119,6 +2122,28 @@
|
|||
"share.advancedSettings.csv.quoteValuesTitle": "使用引号引起 CSV 值",
|
||||
"share.advancedSettings.csv.separatorText": "使用此字符串分隔导出的值",
|
||||
"share.advancedSettings.csv.separatorTitle": "CSV 分隔符",
|
||||
"share.contextMenu.embedCodeLabel": "嵌入代码",
|
||||
"share.contextMenu.embedCodePanelTitle": "嵌入代码",
|
||||
"share.contextMenu.permalinkPanelTitle": "固定链接",
|
||||
"share.contextMenu.permalinksLabel": "固定链接",
|
||||
"share.contextMenuTitle": "共享此 {objectType}",
|
||||
"share.urlGenerators.error.createUrlFnProvided": "此生成器标记为已过时。切勿提供 createUrl 函数。",
|
||||
"share.urlGenerators.error.migrateCalledNotDeprecated": "无法在非过时的生成器上调用迁移。",
|
||||
"share.urlGenerators.error.migrationFnGivenNotDeprecated": "如果提供了迁移函数,则必须将此生成器标记为已过时",
|
||||
"share.urlGenerators.error.noCreateUrlFnProvided": "此生成器未标记为已过时。请提供 createUrl 函数。",
|
||||
"share.urlGenerators.error.noMigrationFnProvided": "如果访问链接生成器标记为已过时,则必须提供迁移函数。",
|
||||
"share.urlGenerators.errors.noGeneratorWithId": "未找到 ID 为 {id} 的生成器",
|
||||
"share.urlPanel.canNotShareAsSavedObjectHelpText": "只有保存 {objectType} 后,才能共享为已保存对象。",
|
||||
"share.urlPanel.copyIframeCodeButtonLabel": "复制 iFrame 代码",
|
||||
"share.urlPanel.copyLinkButtonLabel": "复制链接",
|
||||
"share.urlPanel.generateLinkAsLabel": "将链接生成为",
|
||||
"share.urlPanel.savedObjectDescription": "您可以将此 URL 共享给相关人员,以便他们可以加载此 {objectType} 最新的已保存版本。",
|
||||
"share.urlPanel.savedObjectLabel": "已保存对象",
|
||||
"share.urlPanel.shortUrlHelpText": "建议共享缩短的快照 URL,以实现最大的兼容性。Internet Explorer 有 URL 长度限制,某些 wiki 和标记分析器无法很好地处理全长版本的快照 URL,但应能很好地处理短 URL。",
|
||||
"share.urlPanel.shortUrlLabel": "短 URL",
|
||||
"share.urlPanel.snapshotDescription": "快照 URL 将 {objectType} 的当前状态编入 URL 自身之中。通过此 URL 无法看到对已保存 {objectType} 的编辑。",
|
||||
"share.urlPanel.snapshotLabel": "快照",
|
||||
"share.urlPanel.unableCreateShortUrlErrorMessage": "无法创建短 URL。错误:{errorMessage}",
|
||||
"kbn.advancedSettings.darkModeText": "为 Kibana UI 启用深色模式需要刷新页面,才能应用设置。",
|
||||
"kbn.advancedSettings.darkModeTitle": "深色模式",
|
||||
"kbn.advancedSettings.dateFormat.dayOfWeekText": "一周从哪一日开始?",
|
||||
|
@ -2170,9 +2195,37 @@
|
|||
"kbn.advancedSettings.visualization.tileMap.wmsDefaultsTitle": "默认 WMS 属性",
|
||||
"visualizations.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。",
|
||||
"visualizations.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage": "在{advancedSettingsLink}中启用“{storeInSessionStorageParam}”选项或简化屏幕视觉效果。",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高级设置",
|
||||
"core.ui.errorUrlOverflow.bigUrlWarningNotificationTitle": "URL 过长,Kibana 可能无法工作",
|
||||
"visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验室模式,以查看实验室可视化。",
|
||||
"visualizations.disabledLabVisualizationTitle": "{title} 为实验室可视化。",
|
||||
"visualizations.displayName": "可视化",
|
||||
"visualizations.function.range.from.help": "范围起始",
|
||||
"visualizations.function.range.help": "生成范围对象",
|
||||
"visualizations.function.range.to.help": "范围结束",
|
||||
"visualizations.function.visDimension.accessor.help": "要使用的数据集列(列索引或列名称)",
|
||||
"visualizations.function.visDimension.error.accessor": "提供的列名称无效",
|
||||
"visualizations.function.visDimension.format.help": "格式",
|
||||
"visualizations.function.visDimension.formatParams.help": "格式参数",
|
||||
"visualizations.function.visDimension.help": "生成 visConfig 维度对象",
|
||||
"visualizations.functions.visualization.help": "简单可视化",
|
||||
"visualizations.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束",
|
||||
"visualizations.newVisWizard.betaTitle": "公测版",
|
||||
"visualizations.newVisWizard.chooseSourceTitle": "选择源",
|
||||
"visualizations.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。",
|
||||
"visualizations.newVisWizard.experimentalTitle": "实验性",
|
||||
"visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。",
|
||||
"visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型",
|
||||
"visualizations.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。",
|
||||
"visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }",
|
||||
"visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索",
|
||||
"visualizations.newVisWizard.selectVisType": "选择可视化类型",
|
||||
"visualizations.newVisWizard.title": "新建可视化",
|
||||
"visualizations.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。",
|
||||
"visualizations.newVisWizard.visTypeAliasTitle": "Kibana 应用程序",
|
||||
"visualizations.savedObjectName": "可视化",
|
||||
"kibana_legacy.notify.fatalError.errorStatusMessage": "错误 {errStatus} {errStatusText}:{errMessage}",
|
||||
"kibana_legacy.notify.fatalError.unavailableServerErrorMessage": "HTTP 请求无法连接。请检查 Kibana 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。",
|
||||
"kibana_legacy.notify.toaster.errorMessage": "错误:{errorMessage}\n {errorStack}",
|
||||
|
@ -2424,28 +2477,6 @@
|
|||
"server.status.redTitle": "红",
|
||||
"server.status.uninitializedTitle": "未初始化",
|
||||
"server.status.yellowTitle": "黄",
|
||||
"share.contextMenu.embedCodeLabel": "嵌入代码",
|
||||
"share.contextMenu.embedCodePanelTitle": "嵌入代码",
|
||||
"share.contextMenu.permalinkPanelTitle": "固定链接",
|
||||
"share.contextMenu.permalinksLabel": "固定链接",
|
||||
"share.contextMenuTitle": "共享此 {objectType}",
|
||||
"share.urlGenerators.error.createUrlFnProvided": "此生成器标记为已过时。切勿提供 createUrl 函数。",
|
||||
"share.urlGenerators.error.migrateCalledNotDeprecated": "无法在非过时的生成器上调用迁移。",
|
||||
"share.urlGenerators.error.migrationFnGivenNotDeprecated": "如果提供了迁移函数,则必须将此生成器标记为已过时",
|
||||
"share.urlGenerators.error.noCreateUrlFnProvided": "此生成器未标记为已过时。请提供 createUrl 函数。",
|
||||
"share.urlGenerators.error.noMigrationFnProvided": "如果访问链接生成器标记为已过时,则必须提供迁移函数。",
|
||||
"share.urlGenerators.errors.noGeneratorWithId": "未找到 ID 为 {id} 的生成器",
|
||||
"share.urlPanel.canNotShareAsSavedObjectHelpText": "只有保存 {objectType} 后,才能共享为已保存对象。",
|
||||
"share.urlPanel.copyIframeCodeButtonLabel": "复制 iFrame 代码",
|
||||
"share.urlPanel.copyLinkButtonLabel": "复制链接",
|
||||
"share.urlPanel.generateLinkAsLabel": "将链接生成为",
|
||||
"share.urlPanel.savedObjectDescription": "您可以将此 URL 共享给相关人员,以便他们可以加载此 {objectType} 最新的已保存版本。",
|
||||
"share.urlPanel.savedObjectLabel": "已保存对象",
|
||||
"share.urlPanel.shortUrlHelpText": "建议共享缩短的快照 URL,以实现最大的兼容性。Internet Explorer 有 URL 长度限制,某些 wiki 和标记分析器无法很好地处理全长版本的快照 URL,但应能很好地处理短 URL。",
|
||||
"share.urlPanel.shortUrlLabel": "短 URL",
|
||||
"share.urlPanel.snapshotDescription": "快照 URL 将 {objectType} 的当前状态编入 URL 自身之中。通过此 URL 无法看到对已保存 {objectType} 的编辑。",
|
||||
"share.urlPanel.snapshotLabel": "快照",
|
||||
"share.urlPanel.unableCreateShortUrlErrorMessage": "无法创建短 URL。错误:{errorMessage}",
|
||||
"statusPage.loadStatus.serverIsDownErrorMessage": "无法请求服务器状态。也许您的服务器已关闭?",
|
||||
"statusPage.loadStatus.serverStatusCodeErrorMessage": "无法使用状态代码 {responseStatus} 请求服务器状态",
|
||||
"statusPage.metricsTiles.columns.heapTotalHeader": "堆总计",
|
||||
|
@ -3842,37 +3873,6 @@
|
|||
"visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项",
|
||||
"visTypeVislib.vislib.tooltip.fieldLabel": "字段",
|
||||
"visTypeVislib.vislib.tooltip.valueLabel": "值",
|
||||
"visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验室模式,以查看实验室可视化。",
|
||||
"visualizations.disabledLabVisualizationTitle": "{title} 为实验室可视化。",
|
||||
"visualizations.displayName": "可视化",
|
||||
"visualizations.function.range.from.help": "范围起始",
|
||||
"visualizations.function.range.help": "生成范围对象",
|
||||
"visualizations.function.range.to.help": "范围结束",
|
||||
"visualizations.function.visDimension.accessor.help": "要使用的数据集列(列索引或列名称)",
|
||||
"visualizations.function.visDimension.error.accessor": "提供的列名称无效",
|
||||
"visualizations.function.visDimension.format.help": "格式",
|
||||
"visualizations.function.visDimension.formatParams.help": "格式参数",
|
||||
"visualizations.function.visDimension.help": "生成 visConfig 维度对象",
|
||||
"visualizations.functions.visualization.help": "简单可视化",
|
||||
"visualizations.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束",
|
||||
"visualizations.newVisWizard.betaTitle": "公测版",
|
||||
"visualizations.newVisWizard.chooseSourceTitle": "选择源",
|
||||
"visualizations.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。",
|
||||
"visualizations.newVisWizard.experimentalTitle": "实验性",
|
||||
"visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。",
|
||||
"visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型",
|
||||
"visualizations.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。",
|
||||
"visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }",
|
||||
"visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式",
|
||||
"visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索",
|
||||
"visualizations.newVisWizard.selectVisType": "选择可视化类型",
|
||||
"visualizations.newVisWizard.title": "新建可视化",
|
||||
"visualizations.newVisWizard.visTypeAliasDescription": "打开 Visualize 外部的 Kibana 应用程序。",
|
||||
"visualizations.newVisWizard.visTypeAliasTitle": "Kibana 应用程序",
|
||||
"visualizations.savedObjectName": "可视化",
|
||||
"visualize.badge.readOnly.text": "只读",
|
||||
"visualize.badge.readOnly.tooltip": "无法保存可视化",
|
||||
"visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage": "必须提供 indexPattern 或 savedSearchId",
|
||||
|
@ -3972,6 +3972,8 @@
|
|||
"xpack.uiActionsEnhanced.customizePanelTimeRange.modal.updatePanelTimeRangeButtonTitle": "更新",
|
||||
"xpack.uiActionsEnhanced.customizeTimeRange.modal.headerTitle": "定制面板时间范围",
|
||||
"xpack.uiActionsEnhanced.customizeTimeRangeMenuItem.displayName": "定制时间范围",
|
||||
"xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "选择目标索引模式",
|
||||
"xpack.uiActionsEnhanced.drilldown.goToDiscover": "前往 Discover(示例)",
|
||||
"xpack.alerts.alertNavigationRegistry.get.missingNavigationError": "在“{consumer}”内针对告警类型“{alertType}”的导航未注册。",
|
||||
"xpack.alerts.alertNavigationRegistry.register.duplicateDefaultError": "“{consumer}”内的默认导航已注册。",
|
||||
"xpack.alerts.alertNavigationRegistry.register.duplicateNavigationError": "在“{consumer}”内针对告警类型“{alertType}”的导航已注册。",
|
||||
|
@ -10454,13 +10456,10 @@
|
|||
"xpack.ml.models.jobValidation.validateJobObject.jobIsNotObjectErrorMessage": "无效的 {invalidParamName}:需要是对象。",
|
||||
"xpack.ml.models.jobValidation.validateJobObject.timeFieldIsNotStringErrorMessage": "无效的 {invalidParamName}:需要是字符串。",
|
||||
"xpack.ml.navMenu.anomalyDetectionTabLinkText": "异常检测",
|
||||
"xpack.ml.navMenu.anomalyExplorerTabLinkText": "Anomaly Explorer",
|
||||
"xpack.ml.navMenu.dataFrameAnalyticsTabLinkText": "分析",
|
||||
"xpack.ml.navMenu.dataVisualizerTabLinkText": "数据可视化工具",
|
||||
"xpack.ml.navMenu.jobManagementTabLinkText": "作业管理",
|
||||
"xpack.ml.navMenu.overviewTabLinkText": "概览",
|
||||
"xpack.ml.navMenu.settingsTabLinkText": "设置",
|
||||
"xpack.ml.navMenu.singleMetricViewerTabLinkText": "Single Metric Viewer",
|
||||
"xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "重置",
|
||||
"xpack.ml.newJob.page.createJob": "创建作业",
|
||||
"xpack.ml.newJob.recognize.advancedLabel": "高级",
|
||||
|
@ -10889,7 +10888,6 @@
|
|||
"xpack.ml.settings.breadcrumbs.filterLists.createLabel": "创建",
|
||||
"xpack.ml.settings.breadcrumbs.filterLists.editLabel": "编辑",
|
||||
"xpack.ml.settings.breadcrumbs.filterListsLabel": "筛选列表",
|
||||
"xpack.ml.settings.calendarManagementButtonLabel": "日历管理",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription": "日志包含不想生成异常的已计划事件列表,例如已计划系统中断或公共假期。同一日历可分配给多个作业。{br}{learnMoreLink}",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsDescription.learnMoreLinkText": "了解详情",
|
||||
"xpack.ml.settings.calendars.listHeader.calendarsListTotalCount": "合计 {totalCount}",
|
||||
|
@ -10939,8 +10937,6 @@
|
|||
"xpack.ml.settings.filterLists.table.noFiltersCreatedTitle": "未创建任何筛选",
|
||||
"xpack.ml.settings.filterLists.table.notInUseAriaLabel": "未在使用",
|
||||
"xpack.ml.settings.filterLists.toolbar.deleteItemButtonLabel": "删除项",
|
||||
"xpack.ml.settings.filterListsButtonLabel": "筛选列表",
|
||||
"xpack.ml.settings.jobManagementTitle": "作业管理",
|
||||
"xpack.ml.settingsBreadcrumbLabel": "设置",
|
||||
"xpack.ml.singleMetricViewerPageLabel": "Single Metric Viewer",
|
||||
"xpack.ml.stepDefineForm.invalidQuery": "无效查询",
|
||||
|
@ -16383,8 +16379,6 @@
|
|||
"xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, one {秒} other {秒}}",
|
||||
"xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "未注册对象类型“{id}”。",
|
||||
"xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "已注册对象类型“{id}”。",
|
||||
"xpack.uiActionsEnhanced.components.DiscoverDrilldownConfig.chooseIndexPattern": "选择目标索引模式",
|
||||
"xpack.uiActionsEnhanced.drilldown.goToDiscover": "前往 Discover(示例)",
|
||||
"xpack.upgradeAssistant.appTitle": "{version} 升级助手",
|
||||
"xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail": "使用 {snapshotRestoreDocsButton} 备份您的数据。",
|
||||
"xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel": "快照和还原 API",
|
||||
|
|
|
@ -35,20 +35,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.jobManagement.assertCreateNewJobButtonExists();
|
||||
});
|
||||
|
||||
it('loads the anomaly explorer page', async () => {
|
||||
await ml.navigation.navigateToAnomalyExplorer();
|
||||
await ml.anomalyExplorer.assertAnomalyExplorerEmptyListMessageExists();
|
||||
});
|
||||
|
||||
it('loads the single metric viewer page', async () => {
|
||||
await ml.navigation.navigateToSingleMetricViewer();
|
||||
await ml.singleMetricViewer.assertSingleMetricViewerEmptyListMessageExsist();
|
||||
});
|
||||
|
||||
it('loads the settings page', async () => {
|
||||
await ml.navigation.navigateToSettings();
|
||||
await ml.settings.assertSettingsCalendarLinkExists();
|
||||
await ml.settings.assertSettingsFilterlistLinkExists();
|
||||
await ml.settings.assertSettingsManageCalendarsLinkExists();
|
||||
await ml.settings.assertSettingsCreateCalendarLinkExists();
|
||||
await ml.settings.assertSettingsManageFilterListsLinkExists();
|
||||
await ml.settings.assertSettingsCreateFilterListLinkExists();
|
||||
});
|
||||
|
||||
it('loads the data frame analytics page', async () => {
|
||||
|
|
|
@ -59,42 +59,22 @@ export function MachineLearningNavigationProvider({
|
|||
|
||||
async navigateToAnomalyDetection() {
|
||||
await this.navigateToArea('~mlMainTab & ~anomalyDetection', 'mlPageJobManagement');
|
||||
await this.assertTabsExist('mlSubTab', [
|
||||
'jobManagement',
|
||||
'anomalyExplorer',
|
||||
'singleMetricViewer',
|
||||
'settings',
|
||||
]);
|
||||
},
|
||||
|
||||
async navigateToDataFrameAnalytics() {
|
||||
await this.navigateToArea('~mlMainTab & ~dataFrameAnalytics', 'mlPageDataFrameAnalytics');
|
||||
await this.assertTabsExist('mlSubTab', []);
|
||||
},
|
||||
|
||||
async navigateToDataVisualizer() {
|
||||
await this.navigateToArea('~mlMainTab & ~dataVisualizer', 'mlPageDataVisualizerSelector');
|
||||
await this.assertTabsExist('mlSubTab', []);
|
||||
},
|
||||
|
||||
async navigateToJobManagement() {
|
||||
await this.navigateToAnomalyDetection();
|
||||
await this.navigateToArea('~mlSubTab & ~jobManagement', 'mlPageJobManagement');
|
||||
},
|
||||
|
||||
async navigateToAnomalyExplorer() {
|
||||
await this.navigateToAnomalyDetection();
|
||||
await this.navigateToArea('~mlSubTab & ~anomalyExplorer', 'mlPageAnomalyExplorer');
|
||||
},
|
||||
|
||||
async navigateToSingleMetricViewer() {
|
||||
await this.navigateToAnomalyDetection();
|
||||
await this.navigateToArea('~mlSubTab & ~singleMetricViewer', 'mlPageSingleMetricViewer');
|
||||
},
|
||||
|
||||
async navigateToSettings() {
|
||||
await this.navigateToAnomalyDetection();
|
||||
await this.navigateToArea('~mlSubTab & ~settings', 'mlPageSettings');
|
||||
await this.navigateToArea('~mlMainTab & ~settings', 'mlPageSettings');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,12 +10,20 @@ export function MachineLearningSettingsProvider({ getService }: FtrProviderConte
|
|||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertSettingsCalendarLinkExists() {
|
||||
await testSubjects.existOrFail('ml_calendar_mng_button');
|
||||
async assertSettingsManageCalendarsLinkExists() {
|
||||
await testSubjects.existOrFail('mlCalendarsMngButton');
|
||||
},
|
||||
|
||||
async assertSettingsFilterlistLinkExists() {
|
||||
await testSubjects.existOrFail('ml_filter_lists_button');
|
||||
async assertSettingsCreateCalendarLinkExists() {
|
||||
await testSubjects.existOrFail('mlCalendarsCreateButton');
|
||||
},
|
||||
|
||||
async assertSettingsManageFilterListsLinkExists() {
|
||||
await testSubjects.existOrFail('mlFilterListsMngButton');
|
||||
},
|
||||
|
||||
async assertSettingsCreateFilterListLinkExists() {
|
||||
await testSubjects.existOrFail('mlFilterListsCreateButton');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue