mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Localize ml components ( part 1 ) (#27957)
* feature(ml): localize components * fix xpack.i18n localization strings * Resolve review comments * Add I18nProvider * Fix adding label to angular scope * Fix case of word * Update test snapshots. * Resolve review comments
This commit is contained in:
parent
dca47b89a1
commit
bc4b197fbd
20 changed files with 362 additions and 111 deletions
|
@ -6,7 +6,7 @@
|
|||
ng-click="ok()"
|
||||
ng-disabled="(saveLock === true)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
aria-label="OK">
|
||||
aria-label="{{ ::'xpack.ml.confirmModal.okButtonAriaLabel' | i18n: {defaultMessage: 'Ok'} }}">
|
||||
{{okLabel}}
|
||||
</button>
|
||||
<button
|
||||
|
@ -14,7 +14,7 @@
|
|||
ng-click="cancel()"
|
||||
ng-disabled="(saveLock === true)"
|
||||
class="kuiButton kuiButton--primary"
|
||||
aria-label="Cancel">
|
||||
aria-label="{{ ::'xpack.ml.confirmModal.cancelButtonAriaLabel' | i18n: {defaultMessage: 'Cancel'} }}">
|
||||
{{cancelLabel}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.controller('MlConfirmModal', function ($scope, $modalInstance, params) {
|
||||
module.controller('MlConfirmModal', function ($scope, $modalInstance, params, i18n) {
|
||||
|
||||
$scope.okFunc = params.ok;
|
||||
$scope.cancelFunc = params.cancel;
|
||||
|
@ -17,8 +17,13 @@ module.controller('MlConfirmModal', function ($scope, $modalInstance, params) {
|
|||
$scope.message = params.message || '';
|
||||
$scope.title = params.title || '';
|
||||
|
||||
$scope.okLabel = params.okLabel || 'OK';
|
||||
$scope.cancelLabel = params.cancelLabel || 'Cancel';
|
||||
$scope.okLabel = params.okLabel || i18n('xpack.ml.confirmModal.okButtonLabel', {
|
||||
defaultMessage: 'OK',
|
||||
});
|
||||
|
||||
$scope.cancelLabel = params.cancelLabel || i18n('xpack.ml.confirmModal.cancelButtonLabel', {
|
||||
defaultMessage: 'Cancel',
|
||||
});
|
||||
|
||||
$scope.hideCancel = params.hideCancel || false;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import { EuiText, EuiToolTip } from '@elastic/eui';
|
||||
|
||||
import { FieldTypeIcon } from '../field_type_icon';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export function FieldTitleBar({ card }) {
|
||||
// don't render and fail gracefully if card prop isn't set
|
||||
|
@ -26,7 +27,9 @@ export function FieldTitleBar({ card }) {
|
|||
classNames.push(card.type);
|
||||
}
|
||||
|
||||
const fieldName = card.fieldName || 'document count';
|
||||
const fieldName = card.fieldName || i18n.translate('xpack.ml.fieldTitleBar.documentCountLabel', {
|
||||
defaultMessage: 'document count'
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiText className={classNames.join(' ')}>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import React from 'react';
|
||||
|
||||
import { FieldTitleBar } from './field_title_bar';
|
||||
|
@ -27,7 +27,7 @@ describe('FieldTitleBar', () => {
|
|||
test(`card prop is an empty object`, () => {
|
||||
const props = { card: {} };
|
||||
|
||||
const wrapper = mount(<FieldTitleBar {...props} />);
|
||||
const wrapper = mountWithIntl(<FieldTitleBar {...props} />);
|
||||
|
||||
const fieldName = wrapper.find({ className: 'field-name' }).text();
|
||||
expect(fieldName).toEqual('document count');
|
||||
|
@ -40,7 +40,7 @@ describe('FieldTitleBar', () => {
|
|||
const testFieldName = 'foo';
|
||||
const props = { card: { fieldName: testFieldName, isUnsupportedType: true } };
|
||||
|
||||
const wrapper = mount(<FieldTitleBar {...props} />);
|
||||
const wrapper = mountWithIntl(<FieldTitleBar {...props} />);
|
||||
|
||||
const fieldName = wrapper.find({ className: 'field-name' }).text();
|
||||
expect(fieldName).toEqual(testFieldName);
|
||||
|
@ -54,7 +54,7 @@ describe('FieldTitleBar', () => {
|
|||
const testType = 'bar';
|
||||
const props = { card: { fieldName: testFieldName, type: testType } };
|
||||
|
||||
const wrapper = mount(<FieldTitleBar {...props} />);
|
||||
const wrapper = mountWithIntl(<FieldTitleBar {...props} />);
|
||||
|
||||
const fieldName = wrapper.find({ className: 'field-name' }).text();
|
||||
expect(fieldName).toEqual(testFieldName);
|
||||
|
@ -65,7 +65,7 @@ describe('FieldTitleBar', () => {
|
|||
|
||||
test(`tooltip hovering`, () => {
|
||||
const props = { card: { fieldName: 'foo', type: 'bar' } };
|
||||
const wrapper = mount(<FieldTitleBar {...props} />);
|
||||
const wrapper = mountWithIntl(<FieldTitleBar {...props} />);
|
||||
const container = wrapper.find({ className: 'field-name' });
|
||||
|
||||
expect(wrapper.find('EuiToolTip').children()).toHaveLength(1);
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { FieldTitleBar } from './field_title_bar';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
@ -32,7 +33,9 @@ module.directive('mlFieldTitleBar', function () {
|
|||
};
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(FieldTitleBar, props),
|
||||
<I18nProvider>
|
||||
{React.createElement(FieldTitleBar, props)}
|
||||
</I18nProvider>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,43 +12,68 @@ import { EuiToolTip } from '@elastic/eui';
|
|||
// don't use something like plugins/ml/../common
|
||||
// because it won't work with the jest tests
|
||||
import { ML_JOB_FIELD_TYPES } from '../../../common/constants/field_types';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
export function FieldTypeIcon({ tooltipEnabled = false, type }) {
|
||||
export const FieldTypeIcon = injectI18n(function FieldTypeIcon({ tooltipEnabled = false, type, intl }) {
|
||||
let ariaLabel = '';
|
||||
let iconClass = '';
|
||||
let iconChar = '';
|
||||
|
||||
switch (type) {
|
||||
case ML_JOB_FIELD_TYPES.BOOLEAN:
|
||||
ariaLabel = 'boolean type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.booleanTypeAriaLabel',
|
||||
defaultMessage: 'boolean type'
|
||||
});
|
||||
iconClass = 'fa-adjust';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.DATE:
|
||||
ariaLabel = 'date type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.dateTypeAriaLabel',
|
||||
defaultMessage: 'date type'
|
||||
});
|
||||
iconClass = 'fa-clock-o';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.NUMBER:
|
||||
ariaLabel = 'number type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.numberTypeAriaLabel',
|
||||
defaultMessage: 'number type'
|
||||
});
|
||||
iconChar = '#';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.GEO_POINT:
|
||||
ariaLabel = 'geo_point type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.geoPointTypeAriaLabel',
|
||||
defaultMessage: '{geoPointParam} type'
|
||||
}, { geoPointParam: 'geo_point' });
|
||||
iconClass = 'fa-globe';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.KEYWORD:
|
||||
ariaLabel = 'keyword type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.keywordTypeAriaLabel',
|
||||
defaultMessage: 'keyword type'
|
||||
});
|
||||
iconChar = 't';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.TEXT:
|
||||
ariaLabel = 'text type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.textTypeAriaLabel',
|
||||
defaultMessage: 'text type'
|
||||
});
|
||||
iconClass = 'fa-file-text-o';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.IP:
|
||||
ariaLabel = 'IP type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.ipTypeAriaLabel',
|
||||
defaultMessage: 'IP type'
|
||||
});
|
||||
iconClass = 'fa-laptop';
|
||||
break;
|
||||
case ML_JOB_FIELD_TYPES.UNKNOWN:
|
||||
ariaLabel = 'Unknown type';
|
||||
ariaLabel = intl.formatMessage({
|
||||
id: 'xpack.ml.fieldTypeIcon.unknownTypeAriaLabel',
|
||||
defaultMessage: 'Unknown type'
|
||||
});
|
||||
iconChar = '?';
|
||||
break;
|
||||
default:
|
||||
|
@ -73,15 +98,22 @@ export function FieldTypeIcon({ tooltipEnabled = false, type }) {
|
|||
// to support having another component directly inside the tooltip anchor
|
||||
// see https://github.com/elastic/eui/issues/839
|
||||
return (
|
||||
<EuiToolTip position="left" content={`${type} type`}>
|
||||
<EuiToolTip
|
||||
position="left"
|
||||
content={<FormattedMessage
|
||||
id="xpack.ml.fieldTypeIcon.fieldTypeTooltip"
|
||||
defaultMessage="{type} type"
|
||||
values={{ type }}
|
||||
/>}
|
||||
>
|
||||
<FieldTypeIconContainer {...containerProps} />
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
return <FieldTypeIconContainer {...containerProps} />;
|
||||
}
|
||||
FieldTypeIcon.propTypes = {
|
||||
});
|
||||
FieldTypeIcon.WrappedComponent.propTypes = {
|
||||
tooltipEnabled: PropTypes.bool,
|
||||
type: PropTypes.string
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import React from 'react';
|
||||
|
||||
import { FieldTypeIcon } from './field_type_icon';
|
||||
|
@ -13,22 +13,22 @@ import { ML_JOB_FIELD_TYPES } from '../../../common/constants/field_types';
|
|||
describe('FieldTypeIcon', () => {
|
||||
|
||||
test(`don't render component when type is undefined`, () => {
|
||||
const wrapper = shallow(<FieldTypeIcon />);
|
||||
const wrapper = shallowWithIntl(<FieldTypeIcon.WrappedComponent />);
|
||||
expect(wrapper.isEmptyRender()).toBeTruthy();
|
||||
});
|
||||
|
||||
test(`don't render component when type doesn't match a field type`, () => {
|
||||
const wrapper = shallow(<FieldTypeIcon type="foo" />);
|
||||
const wrapper = shallowWithIntl(<FieldTypeIcon.WrappedComponent type="foo" />);
|
||||
expect(wrapper.isEmptyRender()).toBeTruthy();
|
||||
});
|
||||
|
||||
test(`render component when type matches a field type`, () => {
|
||||
const wrapper = shallow(<FieldTypeIcon type={ML_JOB_FIELD_TYPES.KEYWORD} />);
|
||||
const wrapper = shallowWithIntl(<FieldTypeIcon.WrappedComponent type={ML_JOB_FIELD_TYPES.KEYWORD} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(`render with tooltip and test hovering`, () => {
|
||||
const wrapper = mount(<FieldTypeIcon type={ML_JOB_FIELD_TYPES.KEYWORD} tooltipEnabled={true} />);
|
||||
const wrapper = mountWithIntl(<FieldTypeIcon.WrappedComponent type={ML_JOB_FIELD_TYPES.KEYWORD} tooltipEnabled={true} />);
|
||||
const container = wrapper.find({ className: 'field-type-icon-container' });
|
||||
|
||||
expect(wrapper.find('EuiToolTip').children()).toHaveLength(1);
|
||||
|
@ -41,7 +41,7 @@ describe('FieldTypeIcon', () => {
|
|||
});
|
||||
|
||||
test(`update component`, () => {
|
||||
const wrapper = shallow(<FieldTypeIcon />);
|
||||
const wrapper = shallowWithIntl(<FieldTypeIcon.WrappedComponent />);
|
||||
expect(wrapper.isEmptyRender()).toBeTruthy();
|
||||
wrapper.setProps({ type: ML_JOB_FIELD_TYPES.IP });
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { FieldTypeIcon } from './field_type_icon.js';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
@ -34,7 +35,9 @@ module.directive('mlFieldTypeIcon', function () {
|
|||
};
|
||||
|
||||
ReactDOM.render(
|
||||
React.createElement(FieldTypeIcon, props),
|
||||
<I18nProvider>
|
||||
{React.createElement(FieldTypeIcon, props)}
|
||||
</I18nProvider>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<input
|
||||
class="form-control ml-filter-input-left-icon"
|
||||
type="text"
|
||||
aria-label="{{placeholder}}"
|
||||
aria-label="{{ariaLabel}}"
|
||||
placeholder="{{placeholder}}"
|
||||
ng-model="filter"
|
||||
ng-change="filterChanged()" />
|
||||
|
@ -11,7 +11,10 @@
|
|||
<i class='fa fa-spinner fa-spin'></i>
|
||||
</div>
|
||||
<div ng-show="filterIcon===0">
|
||||
<a aria-label="Clear filter" ng-click="clearFilter()">
|
||||
<a
|
||||
aria-label="{{ ::'xpack.ml.formFilterInput.clearFilterAriaLabel' | i18n: {defaultMessage: 'Clear filter'} }}"
|
||||
ng-click="clearFilter()"
|
||||
>
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@ import angular from 'angular';
|
|||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlFormFilterInput', function () {
|
||||
module.directive('mlFormFilterInput', function (i18n) {
|
||||
return {
|
||||
scope: {
|
||||
placeholder: '@?',
|
||||
|
@ -25,7 +25,15 @@ module.directive('mlFormFilterInput', function () {
|
|||
replace: false,
|
||||
template,
|
||||
link(scope) {
|
||||
scope.placeholder = angular.isDefined(scope.placeholder) ? scope.placeholder : 'Filter';
|
||||
const placeholderIsDefined = angular.isDefined(scope.placeholder);
|
||||
|
||||
scope.placeholder = placeholderIsDefined
|
||||
? scope.placeholder
|
||||
: i18n('xpack.ml.formFilterInput.filterPlaceholder', { defaultMessage: 'Filter' });
|
||||
|
||||
scope.ariaLabel = placeholderIsDefined
|
||||
? scope.placeholder
|
||||
: i18n('xpack.ml.formFilterInput.filterAriaLabel', { defaultMessage: 'Filter' });
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
type="button"
|
||||
ng-disabled="disabled"
|
||||
class="euiButton euiButton--primary euiButton--small euiButton--fill">
|
||||
<span class="euiButton__content">
|
||||
Use full {{indexPattern.title}} data
|
||||
</span>
|
||||
<span
|
||||
class="euiButton__content"
|
||||
i18n-id="xpack.ml.fullTimeRangeSelector.useFullDataButtonLabel"
|
||||
i18n-default-message="Use full {indexPatternTitle} data"
|
||||
i18n-values="{ indexPatternTitle: indexPattern.title }"
|
||||
></span>
|
||||
</button>
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
EuiTitle,
|
||||
EuiToolTip
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { abbreviateWholeNumber } from 'plugins/ml/formatters/abbreviate_whole_number';
|
||||
import { getSeverity } from 'plugins/ml/../common/util/anomaly_utils';
|
||||
|
@ -28,8 +29,20 @@ import { getSeverity } from 'plugins/ml/../common/util/anomaly_utils';
|
|||
function getTooltipContent(maxScoreLabel, totalScoreLabel) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p>Maximum anomaly score: {maxScoreLabel}</p>
|
||||
<p>Total anomaly score: {totalScoreLabel}</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.influencersList.maxAnomalyScoreTooltipDescription"
|
||||
defaultMessage="Maximum anomaly score: {maxScoreLabel}"
|
||||
values={{ maxScoreLabel }}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.influencersList.totalAnomalyScoreTooltipDescription"
|
||||
defaultMessage="Total anomaly score: {totalScoreLabel}"
|
||||
values={{ totalScoreLabel }}
|
||||
/>
|
||||
</p>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -124,7 +137,12 @@ export function InfluencersList({ influencers }) {
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="xxl" />
|
||||
<EuiText>
|
||||
<h4>No influencers found</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.influencersList.noInfluencersFoundTitle"
|
||||
defaultMessage="No influencers found"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -8,19 +8,25 @@
|
|||
tagging='mlGroupSelect.createNewItem'
|
||||
append-to-body=true
|
||||
>
|
||||
<ui-select-match placeholder="Job Group">
|
||||
<ui-select-match
|
||||
placeholder="{{:: 'xpack.ml.jobGroupSelect.jobGroupPlaceholder' | i18n: { defaultMessage: 'Job Group' } }}">
|
||||
>
|
||||
{{$item.id}}
|
||||
</ui-select-match>
|
||||
<ui-select-choices
|
||||
repeat="group in mlGroupSelect.groups | filter: { id: $select.search }"
|
||||
group-by="mlGroupSelect.groupTypes"
|
||||
>
|
||||
<div ng-if="group.isTag" class="select-item" ng-bind-html="(group.id | highlight: $select.search) +' <small>(new group)</small>'"></div>
|
||||
<div ng-if="group.isTag" class="select-item" ng-bind-html="(group.id | highlight: $select.search) +' <small>' + newGroupLabel + '</small>'"></div>
|
||||
<div ng-if="!group.isTag" class="select-item" >
|
||||
<div ng-bind-html="group.id | highlight: $select.search"></div>
|
||||
<small>
|
||||
Other jobs in this group: {{group.count}}
|
||||
</small>
|
||||
<small
|
||||
i18n-id="xpack.ml.jobGroupSelect.otherJobsInGroupLabel"
|
||||
i18n-default-message="Other jobs in this group: {groupCount}"
|
||||
i18n-values="{
|
||||
groupCount: group.count,
|
||||
}"
|
||||
></small>
|
||||
</div>
|
||||
</ui-select-choices>
|
||||
</ui-select>
|
||||
|
|
|
@ -16,7 +16,7 @@ import { InitAfterBindingsWorkaround } from 'ui/compat';
|
|||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlJobGroupSelect', function () {
|
||||
module.directive('mlJobGroupSelect', function (i18n) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template,
|
||||
|
@ -33,6 +33,7 @@ module.directive('mlJobGroupSelect', function () {
|
|||
this.$scope = $scope;
|
||||
this.selectedGroups = [];
|
||||
this.groups = [];
|
||||
this.$scope.newGroupLabel = i18n('xpack.ml.jobGroupSelect.newGroupLabel', { defaultMessage: '(new group)' });
|
||||
|
||||
// load the jobs, in case they've not been loaded before
|
||||
// in order to get the job groups
|
||||
|
@ -111,7 +112,7 @@ module.directive('mlJobGroupSelect', function () {
|
|||
|
||||
groupTypes(group) {
|
||||
if(group.isTag === false) {
|
||||
return 'Existing groups';
|
||||
return i18n('xpack.ml.jobGroupSelect.existingGroupsLabel', { defaultMessage: 'Existing groups' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,23 +16,38 @@
|
|||
<div ng-if="showTabs" class="kuiLocalTabs" role="tablist">
|
||||
<a kbn-href="#/jobs" class="kuiLocalTab" role="tab"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('jobs'), 'disabled-nav-link': disableLinks}">
|
||||
Job Management
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.jobManagementTabLinkText"
|
||||
i18n-default-message="Job Management"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/explorer" class="kuiLocalTab" role="tab"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('explorer'), 'disabled-nav-link': disableLinks}">
|
||||
Anomaly Explorer
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.anomalyExplorerTabLinkText"
|
||||
i18n-default-message="Anomaly Explorer"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/timeseriesexplorer" class="kuiLocalTab" role="tab"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('timeseriesexplorer'), 'disabled-nav-link': disableLinks}">
|
||||
Single Metric Viewer
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.singleMetricViewerTabLinkText"
|
||||
i18n-default-message="Single Metric Viewer"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/datavisualizer" class="kuiLocalTab" role="tab"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('datavisualizer')}">
|
||||
Data Visualizer
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.dataVisualizerTabLinkText"
|
||||
i18n-default-message="Data Visualizer"
|
||||
></span>
|
||||
</a>
|
||||
<a kbn-href="#/settings" class="kuiLocalTab" role="tab"
|
||||
ng-class="{'kuiLocalTab-isSelected': isActiveTab('settings'), 'disabled-nav-link': disableLinks}">
|
||||
Settings
|
||||
<span
|
||||
i18n-id="xpack.ml.navMenu.settingsTabLinkText"
|
||||
i18n-default-message="Settings"
|
||||
></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,7 @@ import { isFullLicense } from '../../license/check_license';
|
|||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
module.directive('mlNavMenu', function (config) {
|
||||
module.directive('mlNavMenu', function (config, i18n) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
|
@ -43,27 +43,81 @@ module.directive('mlNavMenu', function (config) {
|
|||
const isK7Design = chrome.getUiSettingsClient().get('k7design', false);
|
||||
if (isK7Design === false) {
|
||||
// Breadcrumbs
|
||||
const crumbNames = {
|
||||
jobs: { label: 'Job Management', url: '#/jobs' },
|
||||
new_job: { label: 'Create New Job', url: '#/jobs/new_job' },
|
||||
single_metric: { label: 'Single Metric Job', url: '' },
|
||||
multi_metric: { label: 'Multi Metric job', url: '' },
|
||||
population: { label: 'Population job', url: '' },
|
||||
advanced: { label: 'Advanced Job Configuration', url: '' },
|
||||
datavisualizer: { label: 'Data Visualizer', url: '' },
|
||||
filedatavisualizer: { label: 'File Data Visualizer (Experimental)', url: '' },
|
||||
explorer: { label: 'Anomaly Explorer', url: '#/explorer' },
|
||||
timeseriesexplorer: { label: 'Single Metric Viewer', url: '#/timeseriesexplorer' },
|
||||
settings: { label: 'Settings', url: '#/settings' },
|
||||
calendars_list: { label: 'Calendar Management', url: '#/settings/calendars_list' },
|
||||
new_calendar: { label: 'New Calendar', url: '#/settings/calendars_list/new_calendar' },
|
||||
edit_calendar: { label: 'Edit Calendar', url: '#/settings/calendars_list/edit_calendar' },
|
||||
filter_lists: { label: 'Filter Lists', url: '#/settings/filter_lists' },
|
||||
new_filter_list: { label: 'New Filter List', url: '#/settings/filter_lists/new' },
|
||||
edit_filter_list: { label: 'Edit Filter List', url: '#/settings/filter_lists/edit' },
|
||||
};
|
||||
|
||||
const breadcrumbs = [{ label: 'Machine Learning', url: '#/' }];
|
||||
const crumbNames = {
|
||||
jobs: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.jobManagementLabel', { defaultMessage: 'Job Management' }),
|
||||
url: '#/jobs'
|
||||
},
|
||||
new_job: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.createNewJobLabel', { defaultMessage: 'Create New Job' }),
|
||||
url: '#/jobs/new_job'
|
||||
},
|
||||
single_metric: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.singleMetricJobLabel', { defaultMessage: 'Single Metric Job' }),
|
||||
url: ''
|
||||
},
|
||||
multi_metric: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.multiMetricJobLabel', { defaultMessage: 'Multi Metric job' }),
|
||||
url: ''
|
||||
},
|
||||
population: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.populationJobLabel', { defaultMessage: 'Population job' }),
|
||||
url: ''
|
||||
},
|
||||
advanced: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.advancedJobConfigurationLabel', { defaultMessage: 'Advanced Job Configuration' }),
|
||||
url: ''
|
||||
},
|
||||
datavisualizer: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.dataVisualizerLabel', { defaultMessage: 'Data Visualizer' }),
|
||||
url: ''
|
||||
},
|
||||
filedatavisualizer: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.fileDataVisualizerLabel', { defaultMessage: 'File Data Visualizer (Experimental)' }),
|
||||
url: ''
|
||||
},
|
||||
explorer: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.anomalyExplorerLabel', { defaultMessage: 'Anomaly Explorer' }),
|
||||
url: '#/explorer'
|
||||
},
|
||||
timeseriesexplorer: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.singleMetricViewerLabel', { defaultMessage: 'Single Metric Viewer' }),
|
||||
url: '#/timeseriesexplorer'
|
||||
},
|
||||
settings: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.settingsLabel', { defaultMessage: 'Settings' }),
|
||||
url: '#/settings'
|
||||
},
|
||||
calendars_list: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.calendarManagementLabel', { defaultMessage: 'Calendar Management' }),
|
||||
url: '#/settings/calendars_list'
|
||||
},
|
||||
new_calendar: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.newCalendarLabel', { defaultMessage: 'New Calendar' }),
|
||||
url: '#/settings/calendars_list/new_calendar'
|
||||
},
|
||||
edit_calendar: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.editCalendarLabel', { defaultMessage: 'Edit Calendar' }),
|
||||
url: '#/settings/calendars_list/edit_calendar'
|
||||
},
|
||||
filter_lists: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.filterListsLabel', { defaultMessage: 'Filter Lists' }),
|
||||
url: '#/settings/filter_lists'
|
||||
},
|
||||
new_filter_list: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.newFilterListLabel', { defaultMessage: 'New Filter List' }),
|
||||
url: '#/settings/filter_lists/new'
|
||||
},
|
||||
edit_filter_list: {
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.editFilterListLabel', { defaultMessage: 'Edit Filter List' }),
|
||||
url: '#/settings/filter_lists/edit'
|
||||
},
|
||||
};
|
||||
const breadcrumbs = [{
|
||||
label: i18n('xpack.ml.navMenu.breadcrumbs.machineLearningLabel', { defaultMessage: 'Machine Learning' }),
|
||||
url: '#/'
|
||||
}];
|
||||
|
||||
// get crumbs from url
|
||||
const crumbs = uiRouter.getBreadcrumbs();
|
||||
|
|
|
@ -13,11 +13,25 @@ exports[`ValidateJob renders button and modal with a message 1`] = `
|
|||
size="s"
|
||||
type="button"
|
||||
>
|
||||
Validate Job
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate Job"
|
||||
id="xpack.ml.validateJob.validateJobButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
<Modal
|
||||
close={[Function]}
|
||||
title="Validate job test-id"
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate job {title}"
|
||||
id="xpack.ml.validateJob.modal.validateJobTitle"
|
||||
values={
|
||||
Object {
|
||||
"title": "test-id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Callout
|
||||
key="over_field_low_cardinality_0"
|
||||
|
@ -35,22 +49,36 @@ exports[`ValidateJob renders button and modal with a message 1`] = `
|
|||
grow={true}
|
||||
size="m"
|
||||
>
|
||||
Job validation performs certain checks against job configurations and underlying source data and provides specific advice on how to adjust settings that are more likely to produce insightful results.
|
||||
<FormattedMessage
|
||||
defaultMessage="Job validation performs certain checks against job configurations and underlying source data and provides specific advice on how to adjust settings that are more likely to produce insightful results."
|
||||
id="xpack.ml.validateJob.modal.jobValidationDescriptionText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="m"
|
||||
>
|
||||
For more information, see
|
||||
<EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/kibana/my-metadata-branch/job-tips.html"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
Machine Learning Job Tips
|
||||
</EuiLink>
|
||||
.
|
||||
<FormattedMessage
|
||||
defaultMessage="For more information, see {mlJobTipsLink}."
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText"
|
||||
values={
|
||||
Object {
|
||||
"mlJobTipsLink": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/kibana/my-metadata-branch/job-tips.html"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Machine Learning Job Tips"
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText.mlJobTipsLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>
|
||||
</Modal>
|
||||
</div>
|
||||
|
@ -69,7 +97,11 @@ exports[`ValidateJob renders the button 1`] = `
|
|||
size="s"
|
||||
type="button"
|
||||
>
|
||||
Validate Job
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate Job"
|
||||
id="xpack.ml.validateJob.validateJobButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</div>
|
||||
`;
|
||||
|
@ -87,32 +119,60 @@ exports[`ValidateJob renders the button and modal with a success message 1`] = `
|
|||
size="s"
|
||||
type="button"
|
||||
>
|
||||
Validate Job
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate Job"
|
||||
id="xpack.ml.validateJob.validateJobButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
<Modal
|
||||
close={[Function]}
|
||||
title="Validate job test-id"
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Validate job {title}"
|
||||
id="xpack.ml.validateJob.modal.validateJobTitle"
|
||||
values={
|
||||
Object {
|
||||
"title": "test-id",
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="m"
|
||||
>
|
||||
Job validation performs certain checks against job configurations and underlying source data and provides specific advice on how to adjust settings that are more likely to produce insightful results.
|
||||
<FormattedMessage
|
||||
defaultMessage="Job validation performs certain checks against job configurations and underlying source data and provides specific advice on how to adjust settings that are more likely to produce insightful results."
|
||||
id="xpack.ml.validateJob.modal.jobValidationDescriptionText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="m"
|
||||
>
|
||||
For more information, see
|
||||
<EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/kibana/my-metadata-branch/job-tips.html"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
Machine Learning Job Tips
|
||||
</EuiLink>
|
||||
.
|
||||
<FormattedMessage
|
||||
defaultMessage="For more information, see {mlJobTipsLink}."
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText"
|
||||
values={
|
||||
Object {
|
||||
"mlJobTipsLink": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/kibana/my-metadata-branch/job-tips.html"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Machine Learning Job Tips"
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText.mlJobTipsLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiText>
|
||||
</Modal>
|
||||
</div>
|
||||
|
|
|
@ -14,10 +14,11 @@ const module = uiModules.get('apps/ml', ['react']);
|
|||
|
||||
import { ValidateJob } from './validate_job_view';
|
||||
import { mlJobService } from 'plugins/ml/services/job_service';
|
||||
import { injectI18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
module.directive('mlValidateJob', function (reactDirective) {
|
||||
return reactDirective(
|
||||
ValidateJob,
|
||||
injectI18nProvider(ValidateJob),
|
||||
undefined,
|
||||
{ restrict: 'E' },
|
||||
{ mlJobService }
|
||||
|
|
|
@ -27,6 +27,8 @@ import {
|
|||
EuiText
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { metadata } from 'ui/metadata';
|
||||
// metadata.branch corresponds to the version used in documentation links.
|
||||
const jobTipsUrl = `https://www.elastic.co/guide/en/kibana/${metadata.branch}/job-tips.html`;
|
||||
|
@ -82,7 +84,14 @@ const statusToEuiIconType = (status) => {
|
|||
}
|
||||
};
|
||||
|
||||
const Link = ({ url }) => (<EuiLink href={url} target="_BLANK">Learn more</EuiLink>);
|
||||
const Link = ({ url }) => (
|
||||
<EuiLink href={url} target="_BLANK">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.validateJob.learnMoreLinkText"
|
||||
defaultMessage="Learn more"
|
||||
/>
|
||||
</EuiLink>
|
||||
);
|
||||
Link.propTypes = {
|
||||
url: PropTypes.string.isRequired
|
||||
};
|
||||
|
@ -101,6 +110,7 @@ Message.propTypes = {
|
|||
})
|
||||
};
|
||||
|
||||
|
||||
const Callout = ({ message }) => (
|
||||
<React.Fragment>
|
||||
<EuiCallOut
|
||||
|
@ -142,7 +152,10 @@ const Modal = ({ close, title, children }) => (
|
|||
size="s"
|
||||
fill
|
||||
>
|
||||
Close
|
||||
<FormattedMessage
|
||||
id="xpack.ml.validateJob.modal.closeButtonLabel"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
|
@ -230,23 +243,46 @@ class ValidateJob extends Component {
|
|||
isDisabled={isDisabled}
|
||||
isLoading={this.state.ui.isLoading}
|
||||
>
|
||||
Validate Job
|
||||
<FormattedMessage
|
||||
id="xpack.ml.validateJob.validateJobButtonLabel"
|
||||
defaultMessage="Validate Job"
|
||||
/>
|
||||
</EuiButton>
|
||||
|
||||
{!isDisabled && this.state.ui.isModalVisible &&
|
||||
<Modal
|
||||
close={this.closeModal}
|
||||
title={`Validate job ${this.state.title}`}
|
||||
title={<FormattedMessage
|
||||
id="xpack.ml.validateJob.modal.validateJobTitle"
|
||||
defaultMessage="Validate job {title}"
|
||||
values={{ title: this.state.title }}
|
||||
/>}
|
||||
>
|
||||
{this.state.data.messages.map(
|
||||
(m, i) => <Callout key={`${m.id}_${i}`} message={m} />
|
||||
)}
|
||||
<EuiText>
|
||||
Job validation performs certain checks against job configurations and underlying source data
|
||||
and provides specific advice on how to adjust settings that are more likely to produce insightful results.
|
||||
<FormattedMessage
|
||||
id="xpack.ml.validateJob.modal.jobValidationDescriptionText"
|
||||
defaultMessage="Job validation performs certain checks against job configurations and underlying source data
|
||||
and provides specific advice on how to adjust settings that are more likely to produce insightful results."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiText>
|
||||
For more information, see <EuiLink href={jobTipsUrl} target="_blank">Machine Learning Job Tips</EuiLink>.
|
||||
<FormattedMessage
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText"
|
||||
defaultMessage="For more information, see {mlJobTipsLink}."
|
||||
values={{
|
||||
mlJobTipsLink: (
|
||||
<EuiLink href={jobTipsUrl} target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.validateJob.modal.linkToJobTipsText.mlJobTipsLinkText"
|
||||
defaultMessage="Machine Learning Job Tips"
|
||||
/>
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</Modal>
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import React from 'react';
|
||||
|
||||
import { ValidateJob } from './validate_job_view';
|
||||
|
@ -26,7 +26,7 @@ function prepareTest(messages) {
|
|||
<ValidateJob getJobConfig={getJobConfig} mlJobService={mlJobService} />
|
||||
);
|
||||
|
||||
const wrapper = shallow(component);
|
||||
const wrapper = shallowWithIntl(component);
|
||||
|
||||
return { wrapper, p };
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue