[i18n] Translate ML - components - rule editor (#28262)

* Translate rule editor

* Fix 'then' -> 'than'

* Update snapshots

* Fix rule description

* Resolve issues from review comments

* Remove extra space

* Update snapshots

* Resolve review comments

* Update snapshots
This commit is contained in:
Nox911 2019-01-17 12:17:05 +03:00 committed by GitHub
parent 90fbaf11c2
commit 89543df907
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1156 additions and 320 deletions

View file

@ -7,7 +7,11 @@ exports[`ActionsSection renders with no actions selected 1`] = `
size="m"
>
<p>
Choose the action(s) to take when the rule matches an anomaly.
<FormattedMessage
defaultMessage="Choose the action(s) to take when the rule matches an anomaly."
id="xpack.ml.ruleEditor.actionsSection.chooseActionsDescription"
values={Object {}}
/>
</p>
</EuiText>
<EuiSpacer
@ -32,7 +36,13 @@ exports[`ActionsSection renders with no actions selected 1`] = `
disabled={false}
id="skip_result_cb"
indeterminate={false}
label="Skip result (recommended)"
label={
<FormattedMessage
defaultMessage="Skip result (recommended)"
id="xpack.ml.ruleEditor.actionsSection.skipResultLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
</EuiFlexItem>
@ -42,7 +52,13 @@ exports[`ActionsSection renders with no actions selected 1`] = `
>
<EuiIconTip
aria-label="Info"
content="The result will not be created."
content={
<FormattedMessage
defaultMessage="The result will not be created."
id="xpack.ml.ruleEditor.actionsSection.resultWillNotBeCreatedTooltip"
values={Object {}}
/>
}
position="right"
size="s"
type="questionInCircle"
@ -71,7 +87,13 @@ exports[`ActionsSection renders with no actions selected 1`] = `
disabled={false}
id="skip_model_update_cb"
indeterminate={false}
label="Skip model update"
label={
<FormattedMessage
defaultMessage="Skip model update"
id="xpack.ml.ruleEditor.actionsSection.skipModelUpdateLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
</EuiFlexItem>
@ -81,7 +103,13 @@ exports[`ActionsSection renders with no actions selected 1`] = `
>
<EuiIconTip
aria-label="Info"
content="The value for that series will not be used to update the model."
content={
<FormattedMessage
defaultMessage="The value for that series will not be used to update the model."
id="xpack.ml.ruleEditor.actionsSection.valueWillNotBeUsedToUpdateModelTooltip"
values={Object {}}
/>
}
position="right"
size="s"
type="questionInCircle"
@ -98,7 +126,11 @@ exports[`ActionsSection renders with skip_result and skip_model_update selected
size="m"
>
<p>
Choose the action(s) to take when the rule matches an anomaly.
<FormattedMessage
defaultMessage="Choose the action(s) to take when the rule matches an anomaly."
id="xpack.ml.ruleEditor.actionsSection.chooseActionsDescription"
values={Object {}}
/>
</p>
</EuiText>
<EuiSpacer
@ -123,7 +155,13 @@ exports[`ActionsSection renders with skip_result and skip_model_update selected
disabled={false}
id="skip_result_cb"
indeterminate={false}
label="Skip result (recommended)"
label={
<FormattedMessage
defaultMessage="Skip result (recommended)"
id="xpack.ml.ruleEditor.actionsSection.skipResultLabel"
values={Object {}}
/>
}
onChange={[Function]}
/>
</EuiFlexItem>
@ -133,7 +171,13 @@ exports[`ActionsSection renders with skip_result and skip_model_update selected
>
<EuiIconTip
aria-label="Info"
content="The result will not be created."
content={
<FormattedMessage
defaultMessage="The result will not be created."
id="xpack.ml.ruleEditor.actionsSection.resultWillNotBeCreatedTooltip"
values={Object {}}
/>
}
position="right"
size="s"
type="questionInCircle"
@ -162,7 +206,13 @@ exports[`ActionsSection renders with skip_result and skip_model_update selected
disabled={false}
id="skip_model_update_cb"
indeterminate={false}
label="Skip model update"
label={
<FormattedMessage
defaultMessage="Skip model update"
id="xpack.ml.ruleEditor.actionsSection.skipModelUpdateLabel"
values={Object {}}
/>
}
onChange={[Function]}
/>
</EuiFlexItem>
@ -172,7 +222,13 @@ exports[`ActionsSection renders with skip_result and skip_model_update selected
>
<EuiIconTip
aria-label="Info"
content="The value for that series will not be used to update the model."
content={
<FormattedMessage
defaultMessage="The value for that series will not be used to update the model."
id="xpack.ml.ruleEditor.actionsSection.valueWillNotBeUsedToUpdateModelTooltip"
values={Object {}}
/>
}
position="right"
size="s"
type="questionInCircle"
@ -189,7 +245,11 @@ exports[`ActionsSection renders with skip_result selected 1`] = `
size="m"
>
<p>
Choose the action(s) to take when the rule matches an anomaly.
<FormattedMessage
defaultMessage="Choose the action(s) to take when the rule matches an anomaly."
id="xpack.ml.ruleEditor.actionsSection.chooseActionsDescription"
values={Object {}}
/>
</p>
</EuiText>
<EuiSpacer
@ -214,7 +274,13 @@ exports[`ActionsSection renders with skip_result selected 1`] = `
disabled={false}
id="skip_result_cb"
indeterminate={false}
label="Skip result (recommended)"
label={
<FormattedMessage
defaultMessage="Skip result (recommended)"
id="xpack.ml.ruleEditor.actionsSection.skipResultLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
</EuiFlexItem>
@ -224,7 +290,13 @@ exports[`ActionsSection renders with skip_result selected 1`] = `
>
<EuiIconTip
aria-label="Info"
content="The result will not be created."
content={
<FormattedMessage
defaultMessage="The result will not be created."
id="xpack.ml.ruleEditor.actionsSection.resultWillNotBeCreatedTooltip"
values={Object {}}
/>
}
position="right"
size="s"
type="questionInCircle"
@ -253,7 +325,13 @@ exports[`ActionsSection renders with skip_result selected 1`] = `
disabled={false}
id="skip_model_update_cb"
indeterminate={false}
label="Skip model update"
label={
<FormattedMessage
defaultMessage="Skip model update"
id="xpack.ml.ruleEditor.actionsSection.skipModelUpdateLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
</EuiFlexItem>
@ -263,7 +341,13 @@ exports[`ActionsSection renders with skip_result selected 1`] = `
>
<EuiIconTip
aria-label="Info"
content="The value for that series will not be used to update the model."
content={
<FormattedMessage
defaultMessage="The value for that series will not be used to update the model."
id="xpack.ml.ruleEditor.actionsSection.valueWillNotBeUsedToUpdateModelTooltip"
values={Object {}}
/>
}
position="right"
size="s"
type="questionInCircle"

View file

@ -19,7 +19,13 @@ exports[`ConditionExpression renders with appliesTo, operator and value supplied
button={
<EuiExpression
color="secondary"
description="when"
description={
<FormattedMessage
defaultMessage="when"
id="xpack.ml.ruleEditor.conditionExpression.appliesToButtonLabel"
values={Object {}}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -42,7 +48,11 @@ exports[`ConditionExpression renders with appliesTo, operator and value supplied
}
>
<EuiPopoverTitle>
When
<FormattedMessage
defaultMessage="When"
id="xpack.ml.ruleEditor.conditionExpression.appliesToPopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"
@ -89,7 +99,17 @@ exports[`ConditionExpression renders with appliesTo, operator and value supplied
button={
<EuiExpression
color="secondary"
description="is greater than"
description={
<FormattedMessage
defaultMessage="is {operator}"
id="xpack.ml.ruleEditor.conditionExpression.operatorValueButtonLabel"
values={
Object {
"operator": "greater than",
}
}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -112,7 +132,11 @@ exports[`ConditionExpression renders with appliesTo, operator and value supplied
}
>
<EuiPopoverTitle>
Is
<FormattedMessage
defaultMessage="Is"
id="xpack.ml.ruleEditor.conditionExpression.operatorValuePopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"
@ -196,7 +220,7 @@ exports[`ConditionExpression renders with appliesTo, operator and value supplied
grow={false}
>
<EuiButtonIcon
aria-label="Next"
aria-label="Delete condition"
color="danger"
iconSize="m"
iconType="trash"
@ -227,7 +251,13 @@ exports[`ConditionExpression renders with only value supplied 1`] = `
button={
<EuiExpression
color="secondary"
description="when"
description={
<FormattedMessage
defaultMessage="when"
id="xpack.ml.ruleEditor.conditionExpression.appliesToButtonLabel"
values={Object {}}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -250,7 +280,11 @@ exports[`ConditionExpression renders with only value supplied 1`] = `
}
>
<EuiPopoverTitle>
When
<FormattedMessage
defaultMessage="When"
id="xpack.ml.ruleEditor.conditionExpression.appliesToPopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"
@ -296,7 +330,17 @@ exports[`ConditionExpression renders with only value supplied 1`] = `
button={
<EuiExpression
color="secondary"
description="is "
description={
<FormattedMessage
defaultMessage="is {operator}"
id="xpack.ml.ruleEditor.conditionExpression.operatorValueButtonLabel"
values={
Object {
"operator": "",
}
}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -319,7 +363,11 @@ exports[`ConditionExpression renders with only value supplied 1`] = `
}
>
<EuiPopoverTitle>
Is
<FormattedMessage
defaultMessage="Is"
id="xpack.ml.ruleEditor.conditionExpression.operatorValuePopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"
@ -402,7 +450,7 @@ exports[`ConditionExpression renders with only value supplied 1`] = `
grow={false}
>
<EuiButtonIcon
aria-label="Next"
aria-label="Delete condition"
color="danger"
iconSize="m"
iconType="trash"

View file

@ -15,7 +15,11 @@ exports[`ConditionsSectionExpression renders when enabled with empty conditions
onClick={[Function]}
type="button"
>
Add new condition
<FormattedMessage
defaultMessage="Add new condition"
id="xpack.ml.ruleEditor.conditionsSection.addNewConditionButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</React.Fragment>
`;
@ -31,14 +35,18 @@ exports[`ConditionsSectionExpression renders when enabled with no conditions sup
onClick={[Function]}
type="button"
>
Add new condition
<FormattedMessage
defaultMessage="Add new condition"
id="xpack.ml.ruleEditor.conditionsSection.addNewConditionButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</React.Fragment>
`;
exports[`ConditionsSectionExpression renders when enabled with one condition 1`] = `
<React.Fragment>
<ConditionExpression
<InjectIntl(ConditionExpression)
appliesTo="actual"
deleteCondition={[MockFunction]}
index={0}
@ -56,14 +64,18 @@ exports[`ConditionsSectionExpression renders when enabled with one condition 1`]
onClick={[Function]}
type="button"
>
Add new condition
<FormattedMessage
defaultMessage="Add new condition"
id="xpack.ml.ruleEditor.conditionsSection.addNewConditionButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</React.Fragment>
`;
exports[`ConditionsSectionExpression renders when enabled with two conditions 1`] = `
<React.Fragment>
<ConditionExpression
<InjectIntl(ConditionExpression)
appliesTo="actual"
deleteCondition={[MockFunction]}
index={0}
@ -72,7 +84,7 @@ exports[`ConditionsSectionExpression renders when enabled with two conditions 1`
updateCondition={[MockFunction]}
value={1}
/>
<ConditionExpression
<InjectIntl(ConditionExpression)
appliesTo="typical"
deleteCondition={[MockFunction]}
index={1}
@ -90,7 +102,11 @@ exports[`ConditionsSectionExpression renders when enabled with two conditions 1`
onClick={[Function]}
type="button"
>
Add new condition
<FormattedMessage
defaultMessage="Add new condition"
id="xpack.ml.ruleEditor.conditionsSection.addNewConditionButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</React.Fragment>
`;

View file

@ -26,7 +26,11 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
<h1
id="flyoutTitle"
>
Create Rule
<FormattedMessage
defaultMessage="Create Rule"
id="xpack.ml.ruleEditor.ruleEditorFlyout.createRuleTitle"
values={Object {}}
/>
</h1>
</EuiTitle>
</EuiFlyoutHeader>
@ -95,15 +99,26 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
size="m"
>
<p>
Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide. When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are satisfied, its actions are triggered.
<EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
Learn more
</EuiLink>
<FormattedMessage
defaultMessage="Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide. When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are satisfied, its actions are triggered. {learnMoreLink}"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription"
values={
Object {
"learnMoreLink": <EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="Learn more"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription.learnMoreLinkText"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiText>
<EuiSpacer
@ -114,7 +129,11 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
textTransform="none"
>
<h2>
Action
<FormattedMessage
defaultMessage="Action"
id="xpack.ml.ruleEditor.ruleEditorFlyout.actionTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<ActionsSection
@ -134,7 +153,11 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
textTransform="none"
>
<h2>
Conditions
<FormattedMessage
defaultMessage="Conditions"
id="xpack.ml.ruleEditor.ruleEditorFlyout.conditionsTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -182,13 +205,27 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
color="warning"
iconType="help"
size="m"
title="Rerun job"
title={
<FormattedMessage
defaultMessage="Rerun job"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rerunJobTitle"
values={Object {}}
/>
}
>
<p>
Changes to rules take effect for new results only.
<FormattedMessage
defaultMessage="Changes to rules take effect for new results only."
id="xpack.ml.ruleEditor.ruleEditorFlyout.whenChangesTakeEffectDescription"
values={Object {}}
/>
</p>
<p>
To apply these changes to existing results you must clone and rerun the job. Note rerunning the job may take some time and should only be done once you have completed all your changes to the rules for this job.
<FormattedMessage
defaultMessage="To apply these changes to existing results you must clone and rerun the job. Note rerunning the job may take some time and should only be done once you have completed all your changes to the rules for this job."
id="xpack.ml.ruleEditor.ruleEditorFlyout.howToApplyChangesToExistingResultsDescription"
values={Object {}}
/>
</p>
</EuiCallOut>
</EuiFlyoutBody>
@ -214,7 +251,11 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
onClick={[Function]}
type="button"
>
Close
<FormattedMessage
defaultMessage="Close"
id="xpack.ml.ruleEditor.ruleEditorFlyout.closeButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
@ -229,7 +270,11 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
onClick={[Function]}
type="button"
>
Save
<FormattedMessage
defaultMessage="Save"
id="xpack.ml.ruleEditor.ruleEditorFlyout.saveButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
@ -260,7 +305,11 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
<h1
id="flyoutTitle"
>
Edit Rule
<FormattedMessage
defaultMessage="Edit Rule"
id="xpack.ml.ruleEditor.ruleEditorFlyout.editRuleTitle"
values={Object {}}
/>
</h1>
</EuiTitle>
</EuiFlyoutHeader>
@ -343,15 +392,26 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
size="m"
>
<p>
Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide. When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are satisfied, its actions are triggered.
<EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
Learn more
</EuiLink>
<FormattedMessage
defaultMessage="Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide. When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are satisfied, its actions are triggered. {learnMoreLink}"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription"
values={
Object {
"learnMoreLink": <EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="Learn more"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription.learnMoreLinkText"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiText>
<EuiSpacer
@ -362,7 +422,11 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
textTransform="none"
>
<h2>
Action
<FormattedMessage
defaultMessage="Action"
id="xpack.ml.ruleEditor.ruleEditorFlyout.actionTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<ActionsSection
@ -382,7 +446,11 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
textTransform="none"
>
<h2>
Conditions
<FormattedMessage
defaultMessage="Conditions"
id="xpack.ml.ruleEditor.ruleEditorFlyout.conditionsTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -430,13 +498,27 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
color="warning"
iconType="help"
size="m"
title="Rerun job"
title={
<FormattedMessage
defaultMessage="Rerun job"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rerunJobTitle"
values={Object {}}
/>
}
>
<p>
Changes to rules take effect for new results only.
<FormattedMessage
defaultMessage="Changes to rules take effect for new results only."
id="xpack.ml.ruleEditor.ruleEditorFlyout.whenChangesTakeEffectDescription"
values={Object {}}
/>
</p>
<p>
To apply these changes to existing results you must clone and rerun the job. Note rerunning the job may take some time and should only be done once you have completed all your changes to the rules for this job.
<FormattedMessage
defaultMessage="To apply these changes to existing results you must clone and rerun the job. Note rerunning the job may take some time and should only be done once you have completed all your changes to the rules for this job."
id="xpack.ml.ruleEditor.ruleEditorFlyout.howToApplyChangesToExistingResultsDescription"
values={Object {}}
/>
</p>
</EuiCallOut>
</EuiFlyoutBody>
@ -462,7 +544,11 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
onClick={[Function]}
type="button"
>
Close
<FormattedMessage
defaultMessage="Close"
id="xpack.ml.ruleEditor.ruleEditorFlyout.closeButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
@ -477,7 +563,11 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
onClick={[Function]}
type="button"
>
Save
<FormattedMessage
defaultMessage="Save"
id="xpack.ml.ruleEditor.ruleEditorFlyout.saveButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
@ -508,7 +598,11 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
<h1
id="flyoutTitle"
>
Create Rule
<FormattedMessage
defaultMessage="Create Rule"
id="xpack.ml.ruleEditor.ruleEditorFlyout.createRuleTitle"
values={Object {}}
/>
</h1>
</EuiTitle>
</EuiFlyoutHeader>
@ -577,15 +671,26 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
size="m"
>
<p>
Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide. When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are satisfied, its actions are triggered.
<EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
Learn more
</EuiLink>
<FormattedMessage
defaultMessage="Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide. When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are satisfied, its actions are triggered. {learnMoreLink}"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription"
values={
Object {
"learnMoreLink": <EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
<FormattedMessage
defaultMessage="Learn more"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription.learnMoreLinkText"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</p>
</EuiText>
<EuiSpacer
@ -596,7 +701,11 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
textTransform="none"
>
<h2>
Action
<FormattedMessage
defaultMessage="Action"
id="xpack.ml.ruleEditor.ruleEditorFlyout.actionTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<ActionsSection
@ -616,7 +725,11 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
textTransform="none"
>
<h2>
Conditions
<FormattedMessage
defaultMessage="Conditions"
id="xpack.ml.ruleEditor.ruleEditorFlyout.conditionsTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -656,13 +769,27 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
color="warning"
iconType="help"
size="m"
title="Rerun job"
title={
<FormattedMessage
defaultMessage="Rerun job"
id="xpack.ml.ruleEditor.ruleEditorFlyout.rerunJobTitle"
values={Object {}}
/>
}
>
<p>
Changes to rules take effect for new results only.
<FormattedMessage
defaultMessage="Changes to rules take effect for new results only."
id="xpack.ml.ruleEditor.ruleEditorFlyout.whenChangesTakeEffectDescription"
values={Object {}}
/>
</p>
<p>
To apply these changes to existing results you must clone and rerun the job. Note rerunning the job may take some time and should only be done once you have completed all your changes to the rules for this job.
<FormattedMessage
defaultMessage="To apply these changes to existing results you must clone and rerun the job. Note rerunning the job may take some time and should only be done once you have completed all your changes to the rules for this job."
id="xpack.ml.ruleEditor.ruleEditorFlyout.howToApplyChangesToExistingResultsDescription"
values={Object {}}
/>
</p>
</EuiCallOut>
</EuiFlyoutBody>
@ -688,7 +815,11 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
onClick={[Function]}
type="button"
>
Close
<FormattedMessage
defaultMessage="Close"
id="xpack.ml.ruleEditor.ruleEditorFlyout.closeButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
@ -703,7 +834,11 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
onClick={[Function]}
type="button"
>
Save
<FormattedMessage
defaultMessage="Save"
id="xpack.ml.ruleEditor.ruleEditorFlyout.saveButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
@ -734,7 +869,11 @@ exports[`RuleEditorFlyout renders the select action component for a detector wit
<h1
id="flyoutTitle"
>
Edit Rules
<FormattedMessage
defaultMessage="Edit Rules"
id="xpack.ml.ruleEditor.ruleEditorFlyout.editRulesTitle"
values={Object {}}
/>
</h1>
</EuiTitle>
</EuiFlyoutHeader>
@ -814,7 +953,11 @@ exports[`RuleEditorFlyout renders the select action component for a detector wit
onClick={[Function]}
type="button"
>
Close
<FormattedMessage
defaultMessage="Close"
id="xpack.ml.ruleEditor.ruleEditorFlyout.closeButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -31,7 +31,13 @@ exports[`ScopeExpression renders when empty list of filter IDs is supplied 1`] =
<EuiExpression
className="scope-field-button"
color="secondary"
description="when"
description={
<FormattedMessage
defaultMessage="when"
id="xpack.ml.ruleEditor.scopeExpression.scopeFieldWhenLabel"
values={Object {}}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -72,7 +78,13 @@ exports[`ScopeExpression renders when enabled set to false 1`] = `
<EuiExpression
className="scope-field-button"
color="secondary"
description="when"
description={
<FormattedMessage
defaultMessage="when"
id="xpack.ml.ruleEditor.scopeExpression.scopeFieldWhenLabel"
values={Object {}}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -88,7 +100,17 @@ exports[`ScopeExpression renders when enabled set to false 1`] = `
button={
<EuiExpression
color="secondary"
description="is in"
description={
<FormattedMessage
defaultMessage="is {filterType}"
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypeButtonLabel"
values={
Object {
"filterType": "in",
}
}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -111,7 +133,11 @@ exports[`ScopeExpression renders when enabled set to false 1`] = `
}
>
<EuiPopoverTitle>
Is
<FormattedMessage
defaultMessage="Is"
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypePopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"
@ -233,7 +259,13 @@ exports[`ScopeExpression renders when filter ID and type supplied 1`] = `
<EuiExpression
className="scope-field-button"
color="secondary"
description="when"
description={
<FormattedMessage
defaultMessage="when"
id="xpack.ml.ruleEditor.scopeExpression.scopeFieldWhenLabel"
values={Object {}}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -249,7 +281,17 @@ exports[`ScopeExpression renders when filter ID and type supplied 1`] = `
button={
<EuiExpression
color="secondary"
description="is in"
description={
<FormattedMessage
defaultMessage="is {filterType}"
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypeButtonLabel"
values={
Object {
"filterType": "in",
}
}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -272,7 +314,11 @@ exports[`ScopeExpression renders when filter ID and type supplied 1`] = `
}
>
<EuiPopoverTitle>
Is
<FormattedMessage
defaultMessage="Is"
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypePopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"
@ -394,7 +440,13 @@ exports[`ScopeExpression renders when no filter ID or type supplied 1`] = `
<EuiExpression
className="scope-field-button"
color="secondary"
description="when"
description={
<FormattedMessage
defaultMessage="when"
id="xpack.ml.ruleEditor.scopeExpression.scopeFieldWhenLabel"
values={Object {}}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -410,7 +462,17 @@ exports[`ScopeExpression renders when no filter ID or type supplied 1`] = `
button={
<EuiExpression
color="secondary"
description="is "
description={
<FormattedMessage
defaultMessage="is {filterType}"
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypeButtonLabel"
values={
Object {
"filterType": "",
}
}
/>
}
isActive={false}
onClick={[Function]}
uppercase={true}
@ -433,7 +495,11 @@ exports[`ScopeExpression renders when no filter ID or type supplied 1`] = `
}
>
<EuiPopoverTitle>
Is
<FormattedMessage
defaultMessage="Is"
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypePopoverTitle"
values={Object {}}
/>
</EuiPopoverTitle>
<div
className="euiExpression"

View file

@ -9,7 +9,11 @@ exports[`ScopeSection false canGetFilters privilege show NoPermissionCallOut whe
textTransform="none"
>
<h2>
Scope
<FormattedMessage
defaultMessage="Scope"
id="xpack.ml.ruleEditor.scopeSection.scopeTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -21,7 +25,13 @@ exports[`ScopeSection false canGetFilters privilege show NoPermissionCallOut whe
disabled={false}
id="enable_scope_checkbox"
indeterminate={false}
label="Add a filter list to limit where the rule applies."
label={
<FormattedMessage
defaultMessage="Add a filter list to limit where the rule applies."
id="xpack.ml.ruleEditor.scopeSection.addFilterListLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
<EuiSpacer
@ -43,7 +53,11 @@ exports[`ScopeSection renders when enabled with no scope supplied 1`] = `
textTransform="none"
>
<h2>
Scope
<FormattedMessage
defaultMessage="Scope"
id="xpack.ml.ruleEditor.scopeSection.scopeTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -55,7 +69,13 @@ exports[`ScopeSection renders when enabled with no scope supplied 1`] = `
disabled={false}
id="enable_scope_checkbox"
indeterminate={false}
label="Add a filter list to limit where the rule applies."
label={
<FormattedMessage
defaultMessage="Add a filter list to limit where the rule applies."
id="xpack.ml.ruleEditor.scopeSection.addFilterListLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
<EuiSpacer
@ -91,7 +111,11 @@ exports[`ScopeSection renders when enabled with scope supplied 1`] = `
textTransform="none"
>
<h2>
Scope
<FormattedMessage
defaultMessage="Scope"
id="xpack.ml.ruleEditor.scopeSection.scopeTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -103,7 +127,13 @@ exports[`ScopeSection renders when enabled with scope supplied 1`] = `
disabled={false}
id="enable_scope_checkbox"
indeterminate={false}
label="Add a filter list to limit where the rule applies."
label={
<FormattedMessage
defaultMessage="Add a filter list to limit where the rule applies."
id="xpack.ml.ruleEditor.scopeSection.addFilterListLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
<EuiSpacer
@ -139,7 +169,11 @@ exports[`ScopeSection renders when not enabled 1`] = `
textTransform="none"
>
<h2>
Scope
<FormattedMessage
defaultMessage="Scope"
id="xpack.ml.ruleEditor.scopeSection.scopeTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -151,7 +185,13 @@ exports[`ScopeSection renders when not enabled 1`] = `
disabled={false}
id="enable_scope_checkbox"
indeterminate={false}
label="Add a filter list to limit where the rule applies."
label={
<FormattedMessage
defaultMessage="Add a filter list to limit where the rule applies."
id="xpack.ml.ruleEditor.scopeSection.addFilterListLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
<EuiSpacer
@ -170,7 +210,11 @@ exports[`ScopeSection show NoFilterListsCallOut when no filter list IDs 1`] = `
textTransform="none"
>
<h2>
Scope
<FormattedMessage
defaultMessage="Scope"
id="xpack.ml.ruleEditor.scopeSection.scopeTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
<EuiSpacer
@ -182,7 +226,13 @@ exports[`ScopeSection show NoFilterListsCallOut when no filter list IDs 1`] = `
disabled={false}
id="enable_scope_checkbox"
indeterminate={false}
label="Add a filter list to limit where the rule applies."
label={
<FormattedMessage
defaultMessage="Add a filter list to limit where the rule applies."
id="xpack.ml.ruleEditor.scopeSection.addFilterListLabel"
values={Object {}}
/>
}
onChange={[MockFunction]}
/>
<EuiSpacer

View file

@ -22,6 +22,7 @@ import {
} from '@elastic/eui';
import { ACTION } from '../../../common/constants/detector_rule';
import { FormattedMessage } from '@kbn/i18n/react';
export function ActionsSection({
actions,
@ -32,7 +33,10 @@ export function ActionsSection({
<React.Fragment>
<EuiText>
<p>
Choose the action(s) to take when the rule matches an anomaly.
<FormattedMessage
id="xpack.ml.ruleEditor.actionsSection.chooseActionsDescription"
defaultMessage="Choose the action(s) to take when the rule matches an anomaly."
/>
</p>
</EuiText>
<EuiSpacer size="s" />
@ -40,7 +44,10 @@ export function ActionsSection({
<EuiFlexItem grow={false}>
<EuiCheckbox
id="skip_result_cb"
label="Skip result (recommended)"
label={<FormattedMessage
id="xpack.ml.ruleEditor.actionsSection.skipResultLabel"
defaultMessage="Skip result (recommended)"
/>}
checked={actions.indexOf(ACTION.SKIP_RESULT) > -1}
onChange={onSkipResultChange}
/>
@ -48,7 +55,10 @@ export function ActionsSection({
<EuiFlexItem grow={false}>
<EuiIconTip
content="The result will not be created."
content={<FormattedMessage
id="xpack.ml.ruleEditor.actionsSection.resultWillNotBeCreatedTooltip"
defaultMessage="The result will not be created."
/>}
size="s"
position="right"
/>
@ -61,7 +71,10 @@ export function ActionsSection({
<EuiFlexItem grow={false}>
<EuiCheckbox
id="skip_model_update_cb"
label="Skip model update"
label={<FormattedMessage
id="xpack.ml.ruleEditor.actionsSection.skipModelUpdateLabel"
defaultMessage="Skip model update"
/>}
checked={actions.indexOf(ACTION.SKIP_MODEL_UPDATE) > -1}
onChange={onSkipModelUpdateChange}
/>
@ -69,7 +82,10 @@ export function ActionsSection({
<EuiFlexItem grow={false}>
<EuiIconTip
content="The value for that series will not be used to update the model."
content={<FormattedMessage
id="xpack.ml.ruleEditor.actionsSection.valueWillNotBeUsedToUpdateModelTooltip"
defaultMessage="The value for that series will not be used to update the model."
/>}
size="s"
position="right"
/>

View file

@ -5,7 +5,7 @@
*/
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ActionsSection } from './actions_section';
@ -27,7 +27,7 @@ describe('ActionsSection', () => {
actions: [],
};
const component = shallow(
const component = shallowWithIntl(
<ActionsSection {...props} />
);
@ -41,7 +41,7 @@ describe('ActionsSection', () => {
actions: [ACTION.SKIP_RESULT],
};
const component = shallow(
const component = shallowWithIntl(
<ActionsSection {...props} />
);
@ -52,7 +52,7 @@ describe('ActionsSection', () => {
test('renders with skip_result and skip_model_update selected', () => {
const component = shallow(
const component = shallowWithIntl(
<ActionsSection
actions={[ACTION.SKIP_RESULT, ACTION.SKIP_MODEL_UPDATE]}
onSkipResultChange={() => {}}

View file

@ -9,15 +9,36 @@ exports[`DetectorDescriptionList render for detector with anomaly values 1`] = `
Array [
Object {
"description": "responsetimes",
"title": "Job ID",
"title": <FormattedMessage
defaultMessage="Job ID"
id="xpack.ml.ruleEditor.detectorDescriptionList.jobIdTitle"
values={Object {}}
/>,
},
Object {
"description": "mean response time",
"title": "Detector",
"title": <FormattedMessage
defaultMessage="Detector"
id="xpack.ml.ruleEditor.detectorDescriptionList.detectorTitle"
values={Object {}}
/>,
},
Object {
"description": "actual 50, typical 1.23",
"title": "Selected anomaly",
"description": <FormattedMessage
defaultMessage="actual {actual}, typical {typical}"
id="xpack.ml.ruleEditor.detectorDescriptionList.selectedAnomalyDescription"
values={
Object {
"actual": 50,
"typical": 1.23,
}
}
/>,
"title": <FormattedMessage
defaultMessage="Selected anomaly"
id="xpack.ml.ruleEditor.detectorDescriptionList.selectedAnomalyTitle"
values={Object {}}
/>,
},
]
}
@ -35,11 +56,19 @@ exports[`DetectorDescriptionList render for population detector with no anomaly
Array [
Object {
"description": "population",
"title": "Job ID",
"title": <FormattedMessage
defaultMessage="Job ID"
id="xpack.ml.ruleEditor.detectorDescriptionList.jobIdTitle"
values={Object {}}
/>,
},
Object {
"description": "count by status over clientip",
"title": "Detector",
"title": <FormattedMessage
defaultMessage="Detector"
id="xpack.ml.ruleEditor.detectorDescriptionList.detectorTitle"
values={Object {}}
/>,
},
]
}

View file

@ -19,6 +19,8 @@ import {
import { formatValue } from '../../../../formatters/format_value';
import { FormattedMessage } from '@kbn/i18n/react';
export function DetectorDescriptionList({
job,
detector,
@ -26,11 +28,21 @@ export function DetectorDescriptionList({
const listItems = [
{
title: 'Job ID',
title: (
<FormattedMessage
id="xpack.ml.ruleEditor.detectorDescriptionList.jobIdTitle"
defaultMessage="Job ID"
/>
),
description: job.job_id,
},
{
title: 'Detector',
title: (
<FormattedMessage
id="xpack.ml.ruleEditor.detectorDescriptionList.detectorTitle"
defaultMessage="Detector"
/>
),
description: detector.detector_description,
}
];
@ -44,8 +56,19 @@ export function DetectorDescriptionList({
listItems.push(
{
title: 'Selected anomaly',
description: `actual ${actual}, typical ${typical}`,
title: (
<FormattedMessage
id="xpack.ml.ruleEditor.detectorDescriptionList.selectedAnomalyTitle"
defaultMessage="Selected anomaly"
/>
),
description: (
<FormattedMessage
id="xpack.ml.ruleEditor.detectorDescriptionList.selectedAnomalyDescription"
defaultMessage="actual {actual}, typical {typical}"
values={{ actual, typical }}
/>
),
}
);
}

View file

@ -5,7 +5,7 @@
*/
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { DetectorDescriptionList } from './detector_description_list';
@ -28,7 +28,7 @@ describe('DetectorDescriptionList', () => {
},
};
const component = shallow(
const component = shallowWithIntl(
<DetectorDescriptionList {...props} />
);
@ -60,7 +60,7 @@ describe('DetectorDescriptionList', () => {
},
};
const component = shallow(
const component = shallowWithIntl(
<DetectorDescriptionList {...props} />
);

View file

@ -27,12 +27,31 @@ import {
import { APPLIES_TO, OPERATOR } from '../../../common/constants/detector_rule';
import { appliesToText, operatorToText } from './utils';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
// Raise the popovers above GuidePageSideNav
const POPOVER_STYLE = { zIndex: '200' };
export class ConditionExpression extends Component {
export const ConditionExpression = injectI18n(class ConditionExpression extends Component {
static propTypes = {
index: PropTypes.number.isRequired,
appliesTo: PropTypes.oneOf([
APPLIES_TO.ACTUAL,
APPLIES_TO.TYPICAL,
APPLIES_TO.DIFF_FROM_TYPICAL
]),
operator: PropTypes.oneOf([
OPERATOR.LESS_THAN,
OPERATOR.LESS_THAN_OR_EQUAL,
OPERATOR.GREATER_THAN,
OPERATOR.GREATER_THAN_OR_EQUAL
]),
value: PropTypes.number.isRequired,
updateCondition: PropTypes.func.isRequired,
deleteCondition: PropTypes.func.isRequired
};
constructor(props) {
super(props);
@ -98,7 +117,12 @@ export class ConditionExpression extends Component {
renderAppliesToPopover() {
return (
<div style={POPOVER_STYLE}>
<EuiPopoverTitle>When</EuiPopoverTitle>
<EuiPopoverTitle>
<FormattedMessage
id="xpack.ml.ruleEditor.conditionExpression.appliesToPopoverTitle"
defaultMessage="When"
/>
</EuiPopoverTitle>
<div className="euiExpression" style={{ width: 200 }}>
<EuiSelect
value={this.props.appliesTo}
@ -117,7 +141,12 @@ export class ConditionExpression extends Component {
renderOperatorValuePopover() {
return (
<div style={POPOVER_STYLE}>
<EuiPopoverTitle>Is</EuiPopoverTitle>
<EuiPopoverTitle>
<FormattedMessage
id="xpack.ml.ruleEditor.conditionExpression.operatorValuePopoverTitle"
defaultMessage="Is"
/>
</EuiPopoverTitle>
<div className="euiExpression">
<EuiFlexGroup style={{ maxWidth: 450 }}>
<EuiFlexItem grow={false} style={{ width: 250 }}>
@ -161,7 +190,10 @@ export class ConditionExpression extends Component {
id="appliesToPopover"
button={(
<EuiExpression
description="when"
description={(<FormattedMessage
id="xpack.ml.ruleEditor.conditionExpression.appliesToButtonLabel"
defaultMessage="when"
/>)}
value={appliesToText(appliesTo)}
isActive={this.state.isAppliesToOpen}
onClick={this.openAppliesTo}
@ -183,7 +215,11 @@ export class ConditionExpression extends Component {
id="operatorValuePopover"
button={(
<EuiExpression
description={`is ${operatorToText(operator)}`}
description={(<FormattedMessage
id="xpack.ml.ruleEditor.conditionExpression.operatorValueButtonLabel"
defaultMessage="is {operator}"
values={{ operator: operatorToText(operator) }}
/>)}
value={`${value}`}
isActive={this.state.isOperatorValueOpen}
onClick={this.openOperatorValue}
@ -205,27 +241,13 @@ export class ConditionExpression extends Component {
color="danger"
onClick={() => deleteCondition(index)}
iconType="trash"
aria-label="Next"
aria-label={this.props.intl.formatMessage({
id: 'xpack.ml.ruleEditor.conditionExpression.deleteConditionButtonAriaLabel',
defaultMessage: 'Delete condition'
})}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}
}
ConditionExpression.propTypes = {
index: PropTypes.number.isRequired,
appliesTo: PropTypes.oneOf([
APPLIES_TO.ACTUAL,
APPLIES_TO.TYPICAL,
APPLIES_TO.DIFF_FROM_TYPICAL
]),
operator: PropTypes.oneOf([
OPERATOR.LESS_THAN,
OPERATOR.LESS_THAN_OR_EQUAL,
OPERATOR.GREATER_THAN,
OPERATOR.GREATER_THAN_OR_EQUAL
]),
value: PropTypes.number.isRequired,
updateCondition: PropTypes.func.isRequired,
deleteCondition: PropTypes.func.isRequired
};
});

View file

@ -7,7 +7,7 @@
// Mock the mlJobService that is imported for saving rules.
jest.mock('../../services/job_service.js', () => 'mlJobService');
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ConditionExpression } from './condition_expression';
@ -30,8 +30,8 @@ describe('ConditionExpression', () => {
value: 123,
};
const component = shallow(
<ConditionExpression {...props} />
const component = shallowWithIntl(
<ConditionExpression.WrappedComponent {...props} />
);
expect(component).toMatchSnapshot();
@ -45,8 +45,8 @@ describe('ConditionExpression', () => {
value: 123,
};
const component = shallow(
<ConditionExpression {...props} />
const component = shallowWithIntl(
<ConditionExpression.WrappedComponent {...props} />
);
expect(component).toMatchSnapshot();

View file

@ -18,6 +18,7 @@ import {
} from '@elastic/eui';
import { ConditionExpression } from './condition_expression';
import { FormattedMessage } from '@kbn/i18n/react';
export function ConditionsSection({
@ -55,7 +56,10 @@ export function ConditionsSection({
<EuiButtonEmpty
onClick={() => addCondition()}
>
Add new condition
<FormattedMessage
id="xpack.ml.ruleEditor.conditionsSection.addNewConditionButtonLabel"
defaultMessage="Add new condition"
/>
</EuiButtonEmpty>
</React.Fragment>
);

View file

@ -7,7 +7,7 @@
// Mock the mlJobService that is imported for saving rules.
jest.mock('../../services/job_service.js', () => 'mlJobService');
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ConditionsSection } from './conditions_section';
@ -38,7 +38,7 @@ describe('ConditionsSectionExpression', () => {
isEnabled: false,
};
const component = shallow(
const component = shallowWithIntl(
<ConditionsSection {...props} />
);
@ -51,7 +51,7 @@ describe('ConditionsSectionExpression', () => {
isEnabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ConditionsSection {...props} />
);
@ -65,7 +65,7 @@ describe('ConditionsSectionExpression', () => {
conditions: [],
};
const component = shallow(
const component = shallowWithIntl(
<ConditionsSection {...props} />
);
@ -79,7 +79,7 @@ describe('ConditionsSectionExpression', () => {
conditions: [getNewConditionDefaults()],
};
const component = shallow(
const component = shallowWithIntl(
<ConditionsSection {...props} />
);
@ -93,7 +93,7 @@ describe('ConditionsSectionExpression', () => {
conditions: [getNewConditionDefaults(), testCondition],
};
const component = shallow(
const component = shallowWithIntl(
<ConditionsSection {...props} />
);
@ -107,7 +107,7 @@ describe('ConditionsSectionExpression', () => {
conditions: [getNewConditionDefaults(), testCondition],
};
const component = shallow(
const component = shallowWithIntl(
<ConditionsSection {...props} />
);

View file

@ -52,11 +52,17 @@ import { getPartitioningFieldNames } from '../../../common/util/job_utils';
import { mlJobService } from '../../services/job_service';
import { ml } from '../../services/ml_api_service';
import { metadata } from 'ui/metadata';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
// metadata.branch corresponds to the version used in documentation links.
const docsUrl = `https://www.elastic.co/guide/en/elastic-stack-overview/${metadata.branch}/ml-rules.html`;
export class RuleEditorFlyout extends Component {
export const RuleEditorFlyout = injectI18n(class RuleEditorFlyout extends Component {
static propTypes = {
setShowFunction: PropTypes.func.isRequired,
unsetShowFunction: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
@ -90,12 +96,17 @@ export class RuleEditorFlyout extends Component {
showFlyout = (anomaly) => {
let ruleIndex = -1;
const { intl } = this.props;
const job = mlJobService.getJob(anomaly.jobId);
if (job === undefined) {
// No details found for this job, display an error and
// don't open the Flyout as no edits can be made without the job.
toastNotifications.addDanger(
`Unable to configure rules as an error occurred obtaining details for job ID ${anomaly.jobId}`);
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.unableToConfigureRulesNotificationMesssage',
defaultMessage: 'Unable to configure rules as an error occurred obtaining details for job ID {jobId}'
}, { jobId: anomaly.jobId })
);
this.setState({
job,
isFlyoutVisible: false
@ -139,7 +150,12 @@ export class RuleEditorFlyout extends Component {
})
.catch((resp) => {
console.log('Error loading list of filters:', resp);
toastNotifications.addDanger('Error loading the filter lists used in the rule scope');
toastNotifications.addDanger(
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.errorWithLoadingFilterListsNotificationMesssage',
defaultMessage: 'Error loading the filter lists used in the rule scope'
})
);
});
}
}
@ -312,6 +328,7 @@ export class RuleEditorFlyout extends Component {
}
updateRuleAtIndex = (ruleIndex, editedRule) => {
const { intl } = this.props;
const {
job,
anomaly,
@ -325,24 +342,41 @@ export class RuleEditorFlyout extends Component {
if (resp.success) {
toastNotifications.add(
{
title: `Changes to ${jobId} detector rules saved`,
title: intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.changesToJobDetectorRulesSavedNotificationMessageTitle',
defaultMessage: 'Changes to {jobId} detector rules saved'
}, { jobId }),
color: 'success',
iconType: 'check',
text: 'Note that changes will take effect for new results only.'
text: intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.changesToJobDetectorRulesSavedNotificationMessageDescription',
defaultMessage: 'Note that changes will take effect for new results only.'
})
}
);
this.closeFlyout();
} else {
toastNotifications.addDanger(`Error saving changes to ${jobId} detector rules`);
toastNotifications.addDanger(
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.errorWithSavingChangesToJobDetectorRulesNotificationMessage',
defaultMessage: 'Error saving changes to {jobId} detector rules'
}, { jobId })
);
}
})
.catch((error) => {
console.error(error);
toastNotifications.addDanger(`Error saving changes to ${jobId} detector rules`);
toastNotifications.addDanger(
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.errorWithSavingChangesToJobDetectorRulesNotificationMessage',
defaultMessage: 'Error saving changes to {jobId} detector rules'
}, { jobId })
);
});
}
deleteRuleAtIndex = (index) => {
const { intl } = this.props;
const {
job,
anomaly
@ -353,15 +387,28 @@ export class RuleEditorFlyout extends Component {
deleteJobRule(job, detectorIndex, index)
.then((resp) => {
if (resp.success) {
toastNotifications.addSuccess(`Rule deleted from ${jobId} detector`);
toastNotifications.addSuccess(
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.ruleDeletedFromJobDetectorNotificationMessage',
defaultMessage: 'Rule deleted from {jobId} detector'
}, { jobId })
);
this.closeFlyout();
} else {
toastNotifications.addDanger(`Error deleting rule from ${jobId} detector`);
toastNotifications.addDanger(
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.errorWithDeletingRuleFromJobDetectorNotificationMessage',
defaultMessage: 'Error deleting rule from {jobId} detector'
}, { jobId })
);
}
})
.catch((error) => {
console.error(error);
let errorMessage = `Error deleting rule from ${jobId} detector`;
let errorMessage = intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.errorWithDeletingRuleFromJobDetectorNotificationMessage',
defaultMessage: 'Error deleting rule from {jobId} detector'
}, { jobId });
if (error.message) {
errorMessage += ` : ${error.message}`;
}
@ -370,15 +417,22 @@ export class RuleEditorFlyout extends Component {
}
addItemToFilterList = (item, filterId, closeFlyoutOnAdd) => {
const { intl } = this.props;
addItemToFilter(item, filterId)
.then(() => {
if (closeFlyoutOnAdd === true) {
toastNotifications.add(
{
title: `Added ${item} to ${filterId}`,
title: intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.addedItemToFilterListNotificationMessageTitle',
defaultMessage: 'Added {item} to {filterId}'
}, { item, filterId }),
color: 'success',
iconType: 'check',
text: 'Note that changes will take effect for new results only.'
text: intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.addedItemToFilterListNotificationMessageDescription',
defaultMessage: 'Note that changes will take effect for new results only.'
})
}
);
this.closeFlyout();
@ -386,11 +440,17 @@ export class RuleEditorFlyout extends Component {
})
.catch((error) => {
console.log(`Error adding ${item} to filter ${filterId}:`, error);
toastNotifications.addDanger(`An error occurred adding ${item} to filter ${filterId}`);
toastNotifications.addDanger(
intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.errorWithAddingItemToFilterListNotificationMessage',
defaultMessage: 'An error occurred adding {item} to filter {filterId}'
}, { item, filterId })
);
});
}
render() {
const { intl } = this.props;
const {
isFlyoutVisible,
job,
@ -417,7 +477,10 @@ export class RuleEditorFlyout extends Component {
<EuiFlyoutHeader hasBorder={true}>
<EuiTitle size="l">
<h1 id="flyoutTitle">
Edit Rules
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.editRulesTitle"
defaultMessage="Edit Rules"
/>
</h1>
</EuiTitle>
</EuiFlyoutHeader>
@ -441,7 +504,10 @@ export class RuleEditorFlyout extends Component {
onClick={this.closeFlyout}
flush="left"
>
Close
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.closeButtonLabel"
defaultMessage="Close"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
@ -456,8 +522,10 @@ export class RuleEditorFlyout extends Component {
const hasPartitioningFields = (this.partitioningFieldNames && this.partitioningFieldNames.length > 0);
const conditionSupported = (CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(anomaly.source.function) === -1);
const conditionsText = 'Add numeric conditions for when the rule applies. ' +
'Multiple conditions are combined using AND.';
const conditionsText = intl.formatMessage({
id: 'xpack.ml.ruleEditor.ruleEditorFlyout.conditionsDescription',
defaultMessage: 'Add numeric conditions for when the rule applies. Multiple conditions are combined using AND.'
});
flyout = (
<EuiFlyout
@ -468,7 +536,17 @@ export class RuleEditorFlyout extends Component {
<EuiFlyoutHeader hasBorder={true}>
<EuiTitle size="l">
<h1 id="flyoutTitle">
{(isCreate === true) ? 'Create Rule' : 'Edit Rule'}
{(isCreate === true) ? (
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.createRuleTitle"
defaultMessage="Create Rule"
/>
) : (
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.editRuleTitle"
defaultMessage="Edit Rule"
/>
)}
</h1>
</EuiTitle>
</EuiFlyoutHeader>
@ -482,16 +560,35 @@ export class RuleEditorFlyout extends Component {
<EuiSpacer size="m" />
<EuiText>
<p>
Rules instruct anomaly detectors to change their behavior based on domain-specific knowledge that you provide.
When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are
satisfied, its actions are triggered. <EuiLink href={docsUrl} target="_blank">Learn more</EuiLink>
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription"
defaultMessage="Rules instruct anomaly detectors to change their behavior
based on domain-specific knowledge that you provide.
When you create a rule, you can specify conditions, scope, and actions. When the conditions of a rule are
satisfied, its actions are triggered. {learnMoreLink}"
values={{
learnMoreLink: (
<EuiLink href={docsUrl} target="_blank">
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.rulesDescription.learnMoreLinkText"
defaultMessage="Learn more"
/>
</EuiLink>
)
}}
/>
</p>
</EuiText>
<EuiSpacer />
<EuiTitle>
<h2>Action</h2>
<h2>
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.actionTitle"
defaultMessage="Action"
/>
</h2>
</EuiTitle>
<ActionsSection
actions={rule.actions}
@ -502,7 +599,12 @@ export class RuleEditorFlyout extends Component {
<EuiSpacer size="xl" />
<EuiTitle>
<h2>Conditions</h2>
<h2>
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.conditionsTitle"
defaultMessage="Conditions"
/>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
{(conditionSupported === true) ?
@ -517,7 +619,11 @@ export class RuleEditorFlyout extends Component {
/>
) : (
<EuiCallOut
title={`Conditions are not supported for detectors using the ${anomaly.source.function} function`}
title={<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.conditionsNotSupportedTitle"
defaultMessage="Conditions are not supported for detectors using the {functionName} function"
values={{ functionName: anomaly.source.function }}
/>}
iconType="iInCircle"
/>
)
@ -543,17 +649,26 @@ export class RuleEditorFlyout extends Component {
/>
<EuiCallOut
title="Rerun job"
title={<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.rerunJobTitle"
defaultMessage="Rerun job"
/>}
color="warning"
iconType="help"
>
<p>
Changes to rules take effect for new results only.
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.whenChangesTakeEffectDescription"
defaultMessage="Changes to rules take effect for new results only."
/>
</p>
<p>
To apply these changes to existing results you must clone and rerun the job.
Note rerunning the job may take some time and should only be done once
you have completed all your changes to the rules for this job.
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.howToApplyChangesToExistingResultsDescription"
defaultMessage="To apply these changes to existing results you must clone and rerun the job.
Note rerunning the job may take some time and should only be done once
you have completed all your changes to the rules for this job."
/>
</p>
</EuiCallOut>
@ -567,7 +682,10 @@ export class RuleEditorFlyout extends Component {
onClick={this.closeFlyout}
flush="left"
>
Close
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.closeButtonLabel"
defaultMessage="Close"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
@ -576,7 +694,10 @@ export class RuleEditorFlyout extends Component {
isDisabled={!isValidRule(rule)}
fill
>
Save
<FormattedMessage
id="xpack.ml.ruleEditor.ruleEditorFlyout.saveButtonLabel"
defaultMessage="Save"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
@ -593,8 +714,4 @@ export class RuleEditorFlyout extends Component {
);
}
}
RuleEditorFlyout.propTypes = {
setShowFunction: PropTypes.func.isRequired,
unsetShowFunction: PropTypes.func.isRequired,
};
});

View file

@ -52,7 +52,7 @@ jest.mock('../../privilege/check_privilege', () => ({
checkPermission: () => true
}));
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { RuleEditorFlyout } from './rule_editor_flyout';
@ -84,10 +84,10 @@ function prepareTest() {
};
const component = (
<RuleEditorFlyout {...requiredProps} />
<RuleEditorFlyout.WrappedComponent {...requiredProps} />
);
const wrapper = shallow(component);
const wrapper = shallowWithIntl(component);
return { wrapper };
}

View file

@ -26,6 +26,7 @@ import {
import { FILTER_TYPE } from '../../../common/constants/detector_rule';
import { filterTypeToText } from './utils';
import { FormattedMessage } from '@kbn/i18n/react';
// Raise the popovers above GuidePageSideNav
const POPOVER_STYLE = { zIndex: '200' };
@ -94,7 +95,12 @@ export class ScopeExpression extends Component {
return (
<div style={POPOVER_STYLE}>
<EuiPopoverTitle>Is</EuiPopoverTitle>
<EuiPopoverTitle>
<FormattedMessage
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypePopoverTitle"
defaultMessage="Is"
/>
</EuiPopoverTitle>
<div className="euiExpression">
<EuiFlexGroup style={{ maxWidth: 450 }}>
<EuiFlexItem grow={false} style={{ width: 150 }}>
@ -142,7 +148,10 @@ export class ScopeExpression extends Component {
<EuiFlexItem grow={false}>
<EuiExpression
className="scope-field-button"
description="when"
description={<FormattedMessage
id="xpack.ml.ruleEditor.scopeExpression.scopeFieldWhenLabel"
defaultMessage="when"
/>}
value={fieldName}
isActive={false}
onClick={(event) => event.preventDefault()}
@ -155,7 +164,11 @@ export class ScopeExpression extends Component {
id="operatorValuePopover"
button={(
<EuiExpression
description={`is ${filterTypeToText(filterType)}`}
description={<FormattedMessage
id="xpack.ml.ruleEditor.scopeExpression.scopeFilterTypeButtonLabel"
defaultMessage="is {filterType}"
values={{ filterType: filterTypeToText(filterType) }}
/>}
value={filterId || ''}
isActive={this.state.isFilterListOpen}
onClick={this.openFilterList}

View file

@ -8,7 +8,7 @@
// Mock the mlJobService that is imported for saving rules.
jest.mock('../../services/job_service.js', () => 'mlJobService');
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ScopeExpression } from './scope_expression';
@ -36,7 +36,7 @@ describe('ScopeExpression', () => {
enabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeExpression {...props} />
);
@ -50,7 +50,7 @@ describe('ScopeExpression', () => {
enabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeExpression {...props} />
);
@ -66,7 +66,7 @@ describe('ScopeExpression', () => {
enabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeExpression {...props} />
);
@ -82,7 +82,7 @@ describe('ScopeExpression', () => {
enabled: false,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeExpression {...props} />
);

View file

@ -23,18 +23,34 @@ import {
import { ScopeExpression } from './scope_expression';
import { checkPermission } from '../../privilege/check_privilege';
import { getScopeFieldDefaults } from './utils';
import { FormattedMessage } from '@kbn/i18n/react';
function NoFilterListsCallOut() {
return (
<EuiCallOut
title="No filter lists configured"
title={<FormattedMessage
id="xpack.ml.ruleEditor.scopeSection.noFilterListsConfiguredTitle"
defaultMessage="No filter lists configured"
/>}
iconType="gear"
>
<p>
To configure scope, you must first use the&nbsp;
<EuiLink href="#/settings/filter_lists">Filter Lists</EuiLink> settings page
to create the list of values you want to include or exclude in the rule.
<FormattedMessage
id="xpack.ml.ruleEditor.scopeSection.createFilterListsDescription"
defaultMessage="To configure scope, you must first use the&nbsp;{filterListsLink} settings page
to create the list of values you want to include or exclude in the rule."
values={{
filterListsLink: (
<EuiLink href="#/settings/filter_lists">
<FormattedMessage
id="xpack.ml.ruleEditor.scopeSection.createFilterListsDescription.filterListsLinkText"
defaultMessage="Filter Lists"
/>
</EuiLink>
)
}}
/>
</p>
</EuiCallOut>
);
@ -43,7 +59,10 @@ function NoFilterListsCallOut() {
function NoPermissionCallOut() {
return (
<EuiCallOut
title="You do not have permission to view filter lists"
title={<FormattedMessage
id="xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle"
defaultMessage="You do not have permission to view filter lists"
/>}
iconType="gear"
/>
);
@ -94,12 +113,20 @@ export function ScopeSection({
return (
<React.Fragment>
<EuiTitle>
<h2>Scope</h2>
<h2>
<FormattedMessage
id="xpack.ml.ruleEditor.scopeSection.scopeTitle"
defaultMessage="Scope"
/>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiCheckbox
id="enable_scope_checkbox"
label="Add a filter list to limit where the rule applies."
label={<FormattedMessage
id="xpack.ml.ruleEditor.scopeSection.addFilterListLabel"
defaultMessage="Add a filter list to limit where the rule applies."
/>}
checked={isEnabled}
onChange={onEnabledChange}
/>

View file

@ -16,7 +16,7 @@ jest.mock('../../privilege/check_privilege', () => ({
checkPermission: (privilege) => mockCheckPermission(privilege)
}));
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { ScopeSection } from './scope_section';
@ -55,7 +55,7 @@ describe('ScopeSection', () => {
isEnabled: false,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeSection {...props} />
);
@ -69,7 +69,7 @@ describe('ScopeSection', () => {
isEnabled: false,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeSection {...props} />
);
@ -84,7 +84,7 @@ describe('ScopeSection', () => {
isEnabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeSection {...props} />
);
@ -98,7 +98,7 @@ describe('ScopeSection', () => {
isEnabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeSection {...props} />
);
@ -113,7 +113,7 @@ describe('ScopeSection', () => {
isEnabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeSection {...props} />
);
@ -150,7 +150,7 @@ describe('ScopeSection false canGetFilters privilege', () => {
isEnabled: true,
};
const component = shallow(
const component = shallowWithIntl(
<ScopeSection {...props} />
);

View file

@ -6,9 +6,15 @@ exports[`AddToFilterListLink renders the add to filter list link for a value 1`]
onClick={[Function]}
type="button"
>
Add
elastic.co
to
safe_domains
<FormattedMessage
defaultMessage="Add {fieldValue} to {filterId}"
id="xpack.ml.ruleEditor.addValueToFilterListLinkText"
values={
Object {
"fieldValue": "elastic.co",
"filterId": "safe_domains",
}
}
/>
</EuiLink>
`;

View file

@ -7,7 +7,11 @@ exports[`DeleteRuleModal renders as delete button after opening and closing moda
onClick={[Function]}
type="button"
>
Delete rule
<FormattedMessage
defaultMessage="Delete rule"
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleLinkText"
values={Object {}}
/>
</EuiLink>
</React.Fragment>
`;
@ -19,7 +23,11 @@ exports[`DeleteRuleModal renders as delete button when not visible 1`] = `
onClick={[Function]}
type="button"
>
Delete rule
<FormattedMessage
defaultMessage="Delete rule"
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleLinkText"
values={Object {}}
/>
</EuiLink>
</React.Fragment>
`;
@ -31,20 +39,46 @@ exports[`DeleteRuleModal renders modal after clicking delete rule link 1`] = `
onClick={[Function]}
type="button"
>
Delete rule
<FormattedMessage
defaultMessage="Delete rule"
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleLinkText"
values={Object {}}
/>
</EuiLink>
<EuiOverlayMask>
<EuiConfirmModal
buttonColor="primary"
cancelButtonText="Cancel"
confirmButtonText="Delete"
cancelButtonText={
<FormattedMessage
defaultMessage="Cancel"
id="xpack.ml.ruleEditor.deleteRuleModal.cancelButtonLabel"
values={Object {}}
/>
}
confirmButtonText={
<FormattedMessage
defaultMessage="Delete"
id="xpack.ml.ruleEditor.deleteRuleModal.deleteButtonLabel"
values={Object {}}
/>
}
defaultFocusedButton="confirm"
onCancel={[Function]}
onConfirm={[Function]}
title="Delete rule"
title={
<FormattedMessage
defaultMessage="Delete rule"
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleTitle"
values={Object {}}
/>
}
>
<p>
Are you sure you want to delete this rule?
<FormattedMessage
defaultMessage="Are you sure you want to delete this rule?"
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleDescription"
values={Object {}}
/>
</p>
</EuiConfirmModal>
</EuiOverlayMask>

View file

@ -18,9 +18,15 @@ exports[`EditConditionLink renders for a condition using actual 1`] = `
grow={true}
size="m"
>
Update rule condition from
5
to
<FormattedMessage
defaultMessage="Update rule condition from {conditionValue} to"
id="xpack.ml.ruleEditor.editConditionLink.updateRuleConditionFromText"
values={
Object {
"conditionValue": 5,
}
}
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
@ -48,7 +54,11 @@ exports[`EditConditionLink renders for a condition using actual 1`] = `
size="s"
type="button"
>
Update
<FormattedMessage
defaultMessage="Update"
id="xpack.ml.ruleEditor.editConditionLink.updateLinkText"
values={Object {}}
/>
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
@ -72,9 +82,15 @@ exports[`EditConditionLink renders for a condition using diff from typical 1`] =
grow={true}
size="m"
>
Update rule condition from
5
to
<FormattedMessage
defaultMessage="Update rule condition from {conditionValue} to"
id="xpack.ml.ruleEditor.editConditionLink.updateRuleConditionFromText"
values={
Object {
"conditionValue": 5,
}
}
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
@ -102,7 +118,11 @@ exports[`EditConditionLink renders for a condition using diff from typical 1`] =
size="s"
type="button"
>
Update
<FormattedMessage
defaultMessage="Update"
id="xpack.ml.ruleEditor.editConditionLink.updateLinkText"
values={Object {}}
/>
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
@ -126,9 +146,15 @@ exports[`EditConditionLink renders for a condition using typical 1`] = `
grow={true}
size="m"
>
Update rule condition from
5
to
<FormattedMessage
defaultMessage="Update rule condition from {conditionValue} to"
id="xpack.ml.ruleEditor.editConditionLink.updateRuleConditionFromText"
values={
Object {
"conditionValue": 5,
}
}
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
@ -156,7 +182,11 @@ exports[`EditConditionLink renders for a condition using typical 1`] = `
size="s"
type="button"
>
Update
<FormattedMessage
defaultMessage="Update"
id="xpack.ml.ruleEditor.editConditionLink.updateLinkText"
values={Object {}}
/>
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -14,10 +14,14 @@ exports[`RuleActionPanel renders panel for rule with a condition 1`] = `
Array [
Object {
"description": "skip result when actual is less than 1",
"title": "rule",
"title": <FormattedMessage
defaultMessage="rule"
id="xpack.ml.ruleEditor.ruleActionPanel.ruleTitle"
values={Object {}}
/>,
},
Object {
"description": <EditConditionLink
"description": <InjectIntl(EditConditionLink)
anomaly={
Object {
"actual": Array [
@ -48,7 +52,11 @@ exports[`RuleActionPanel renders panel for rule with a condition 1`] = `
onClick={[Function]}
type="button"
>
Edit rule
<FormattedMessage
defaultMessage="Edit rule"
id="xpack.ml.ruleEditor.ruleActionPanel.editRuleLinkText"
values={Object {}}
/>
</EuiLink>,
"title": "",
},
@ -81,7 +89,11 @@ exports[`RuleActionPanel renders panel for rule with a condition and scope, valu
Array [
Object {
"description": "skip model update when airline is not in eu-airlines",
"title": "rule",
"title": <FormattedMessage
defaultMessage="rule"
id="xpack.ml.ruleEditor.ruleActionPanel.ruleTitle"
values={Object {}}
/>,
},
Object {
"description": <AddToFilterListLink
@ -97,7 +109,11 @@ exports[`RuleActionPanel renders panel for rule with a condition and scope, valu
onClick={[Function]}
type="button"
>
Edit rule
<FormattedMessage
defaultMessage="Edit rule"
id="xpack.ml.ruleEditor.ruleActionPanel.editRuleLinkText"
values={Object {}}
/>
</EuiLink>,
"title": "",
},
@ -130,7 +146,11 @@ exports[`RuleActionPanel renders panel for rule with scope, value in filter list
Array [
Object {
"description": "skip model update when airline is not in eu-airlines",
"title": "rule",
"title": <FormattedMessage
defaultMessage="rule"
id="xpack.ml.ruleEditor.ruleActionPanel.ruleTitle"
values={Object {}}
/>,
},
Object {
"description": <EuiLink
@ -138,7 +158,11 @@ exports[`RuleActionPanel renders panel for rule with scope, value in filter list
onClick={[Function]}
type="button"
>
Edit rule
<FormattedMessage
defaultMessage="Edit rule"
id="xpack.ml.ruleEditor.ruleActionPanel.editRuleLinkText"
values={Object {}}
/>
</EuiLink>,
"title": "actions",
},

View file

@ -16,6 +16,7 @@ import React from 'react';
import {
EuiLink,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export function AddToFilterListLink({
fieldValue,
@ -27,7 +28,11 @@ export function AddToFilterListLink({
<EuiLink
onClick={() => addItemToFilterList(fieldValue, filterId, true)}
>
Add {fieldValue} to {filterId}
<FormattedMessage
id="xpack.ml.ruleEditor.addValueToFilterListLinkText"
defaultMessage="Add {fieldValue} to {filterId}"
values={{ fieldValue, filterId }}
/>
</EuiLink>
);
}

View file

@ -5,7 +5,7 @@
*/
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { AddToFilterListLink } from './add_to_filter_list_link';
@ -15,7 +15,7 @@ describe('AddToFilterListLink', () => {
test(`renders the add to filter list link for a value`, () => {
const addItemToFilterList = jest.fn(() => {});
const wrapper = shallow(
const wrapper = shallowWithIntl(
<AddToFilterListLink
fieldValue="elastic.co"
filterId="safe_domains"

View file

@ -20,6 +20,7 @@ import {
EuiOverlayMask,
EUI_MODAL_CONFIRM_BUTTON,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export class DeleteRuleModal extends Component {
constructor(props) {
@ -51,14 +52,28 @@ export class DeleteRuleModal extends Component {
modal = (
<EuiOverlayMask>
<EuiConfirmModal
title="Delete rule"
title={<FormattedMessage
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleTitle"
defaultMessage="Delete rule"
/>}
onCancel={this.closeModal}
onConfirm={this.deleteRule}
cancelButtonText="Cancel"
confirmButtonText="Delete"
cancelButtonText={<FormattedMessage
id="xpack.ml.ruleEditor.deleteRuleModal.cancelButtonLabel"
defaultMessage="Cancel"
/>}
confirmButtonText={<FormattedMessage
id="xpack.ml.ruleEditor.deleteRuleModal.deleteButtonLabel"
defaultMessage="Delete"
/>}
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
>
<p>Are you sure you want to delete this rule?</p>
<p>
<FormattedMessage
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleDescription"
defaultMessage="Are you sure you want to delete this rule?"
/>
</p>
</EuiConfirmModal>
</EuiOverlayMask>
);
@ -70,7 +85,10 @@ export class DeleteRuleModal extends Component {
color="danger"
onClick={() => this.showModal()}
>
Delete rule
<FormattedMessage
id="xpack.ml.ruleEditor.deleteRuleModal.deleteRuleLinkText"
defaultMessage="Delete rule"
/>
</EuiLink>
{modal}
</React.Fragment>

View file

@ -5,7 +5,7 @@
*/
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { DeleteRuleModal } from './delete_rule_modal';
@ -24,7 +24,7 @@ describe('DeleteRuleModal', () => {
...requiredProps,
};
const component = shallow(
const component = shallowWithIntl(
<DeleteRuleModal {...props} />
);
@ -37,7 +37,7 @@ describe('DeleteRuleModal', () => {
...requiredProps,
};
const wrapper = shallow(<DeleteRuleModal {...props} />);
const wrapper = shallowWithIntl(<DeleteRuleModal {...props} />);
wrapper.find('EuiLink').simulate('click');
wrapper.update();
expect(wrapper).toMatchSnapshot();
@ -49,7 +49,7 @@ describe('DeleteRuleModal', () => {
...requiredProps,
};
const wrapper = shallow(<DeleteRuleModal {...props} />);
const wrapper = shallowWithIntl(<DeleteRuleModal {...props} />);
wrapper.find('EuiLink').simulate('click');
const instance = wrapper.instance();
instance.closeModal();

View file

@ -28,8 +28,21 @@ import { formatValue } from '../../../formatters/format_value';
import {
getAppliesToValueFromAnomaly,
} from '../utils';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
export const EditConditionLink = injectI18n(class EditConditionLink extends Component {
static propTypes = {
conditionIndex: PropTypes.number.isRequired,
conditionValue: PropTypes.number.isRequired,
appliesTo: PropTypes.oneOf([
APPLIES_TO.ACTUAL,
APPLIES_TO.TYPICAL,
APPLIES_TO.DIFF_FROM_TYPICAL
]),
anomaly: PropTypes.object.isRequired,
updateConditionValue: PropTypes.func.isRequired,
};
export class EditConditionLink extends Component {
constructor(props) {
super(props);
@ -62,21 +75,32 @@ export class EditConditionLink extends Component {
}
render() {
const { intl } = this.props;
const value = this.state.value;
return (
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiText>
Update rule condition from {this.props.conditionValue} to
<FormattedMessage
id="xpack.ml.ruleEditor.editConditionLink.updateRuleConditionFromText"
defaultMessage="Update rule condition from {conditionValue} to"
values={{ conditionValue: this.props.conditionValue }}
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false} className="condition-edit-value-field">
<EuiFieldNumber
placeholder="Enter value"
placeholder={intl.formatMessage({
id: 'xpack.ml.ruleEditor.editConditionLink.enterValuePlaceholder',
defaultMessage: 'Enter value'
})}
compressed={true}
value={value}
onChange={this.onChangeValue}
aria-label="Enter numeric value for condition"
aria-label={intl.formatMessage({
id: 'xpack.ml.ruleEditor.editConditionLink.enterNumericValueForConditionAriaLabel',
defaultMessage: 'Enter numeric value for condition'
})}
/>
</EuiFlexItem>
{value !== '' &&
@ -85,22 +109,14 @@ export class EditConditionLink extends Component {
size="s"
onClick={() => this.onUpdateClick()}
>
Update
<FormattedMessage
id="xpack.ml.ruleEditor.editConditionLink.updateLinkText"
defaultMessage="Update"
/>
</EuiLink>
</EuiFlexItem>
}
</EuiFlexGroup>
);
}
}
EditConditionLink.propTypes = {
conditionIndex: PropTypes.number.isRequired,
conditionValue: PropTypes.number.isRequired,
appliesTo: PropTypes.oneOf([
APPLIES_TO.ACTUAL,
APPLIES_TO.TYPICAL,
APPLIES_TO.DIFF_FROM_TYPICAL
]),
anomaly: PropTypes.object.isRequired,
updateConditionValue: PropTypes.func.isRequired,
};
});

View file

@ -6,7 +6,7 @@
jest.mock('../../../services/job_service.js', () => 'mlJobService');
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { EditConditionLink } from './edit_condition_link';
@ -32,8 +32,8 @@ function prepareTest(updateConditionValueFn, appliesTo) {
updateConditionValue: updateConditionValueFn,
};
const wrapper = shallow(
<EditConditionLink {...props} />
const wrapper = shallowWithIntl(
<EditConditionLink.WrappedComponent {...props} />
);
return wrapper;

View file

@ -27,6 +27,7 @@ import { DeleteRuleModal } from './delete_rule_modal';
import { EditConditionLink } from './edit_condition_link';
import { buildRuleDescription } from '../utils';
import { ml } from '../../../services/ml_api_service';
import { FormattedMessage } from '@kbn/i18n/react';
export class RuleActionPanel extends Component {
@ -86,7 +87,10 @@ export class RuleActionPanel extends Component {
<EuiLink
onClick={() => setEditRuleIndex(ruleIndex)}
>
Edit rule
<FormattedMessage
id="xpack.ml.ruleEditor.ruleActionPanel.editRuleLinkText"
defaultMessage="Edit rule"
/>
</EuiLink>
);
}
@ -168,7 +172,7 @@ export class RuleActionPanel extends Component {
// Add items for the standard Edit and Delete links.
const descriptionListItems = [
{
title: 'rule',
title: (<FormattedMessage id="xpack.ml.ruleEditor.ruleActionPanel.ruleTitle" defaultMessage="rule" />),
description: buildRuleDescription(this.rule, this.props.anomaly),
},
{

View file

@ -28,7 +28,7 @@ jest.mock('../../../services/ml_api_service', () => ({
}
}));
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import { RuleActionPanel } from './rule_action_panel';
@ -122,7 +122,7 @@ describe('RuleActionPanel', () => {
ruleIndex: 0,
};
const component = shallow(
const component = shallowWithIntl(
<RuleActionPanel {...props} />
);
@ -136,7 +136,7 @@ describe('RuleActionPanel', () => {
ruleIndex: 1,
};
const component = shallow(
const component = shallowWithIntl(
<RuleActionPanel {...props} />
);
@ -150,7 +150,7 @@ describe('RuleActionPanel', () => {
ruleIndex: 1,
};
const wrapper = shallow(
const wrapper = shallowWithIntl(
<RuleActionPanel {...props} />
);
wrapper.setState({ showAddToFilterListLink: true });

View file

@ -20,7 +20,7 @@ import {
import { DetectorDescriptionList } from '../components/detector_description_list';
import { RuleActionPanel } from './rule_action_panel';
import { FormattedMessage } from '@kbn/i18n/react';
export function SelectRuleAction({
job,
@ -66,14 +66,20 @@ export function SelectRuleAction({
{ruleActionPanels}
<EuiSpacer size="m" />
<EuiText style={{ display: 'inline' }}>
or&nbsp;
<FormattedMessage
id="xpack.ml.ruleEditor.selectRuleAction.orText"
defaultMessage="or&nbsp;"
/>
</EuiText>
</React.Fragment>
}
<EuiLink
onClick={() => setEditRuleIndex(rules.length)}
>
create a rule
<FormattedMessage
id="xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText"
defaultMessage="create a rule"
/>
</EuiLink>
</div>
);

View file

@ -14,6 +14,7 @@ import {
import { cloneDeep } from 'lodash';
import { ml } from '../../services/ml_api_service';
import { mlJobService } from '../../services/job_service';
import { i18n } from '@kbn/i18n';
export function getNewConditionDefaults() {
return {
@ -111,7 +112,14 @@ export function deleteJobRule(job, detectorIndex, ruleIndex) {
return updateJobRules(job, detectorIndex, customRules);
} else {
return Promise.reject(new Error(
`Rule no longer exists for detector index ${detectorIndex} in job ${job.job_id}`));
i18n.translate('xpack.ml.ruleEditor.deleteJobRule.ruleNoLongerExistsErrorMessage', {
defaultMessage: 'Rule no longer exists for detector index {detectorIndex} in job {jobId}',
values: {
detectorIndex,
jobId: job.job_id
}
})
));
}
}
@ -179,57 +187,85 @@ export function addItemToFilter(item, filterId) {
export function buildRuleDescription(rule) {
const { actions, conditions, scope } = rule;
let description = 'skip ';
let actionsText = '';
let conditionsText = '';
let filtersText = '';
actions.forEach((action, i) => {
if (i > 0) {
description += ' AND ';
actionsText += ' AND ';
}
switch (action) {
case ACTION.SKIP_RESULT:
description += 'result';
actionsText += i18n.translate('xpack.ml.ruleEditor.ruleDescription.resultActionTypeText', {
defaultMessage: 'result',
description: 'Part of composite text: xpack.ml.ruleEditor.ruleDescription.[actionName]ActionTypeText +' +
'xpack.ml.ruleEditor.ruleDescription.conditionsText + xpack.ml.ruleEditor.ruleDescription.filtersText'
});
break;
case ACTION.SKIP_MODEL_UPDATE:
description += 'model update';
actionsText += i18n.translate('xpack.ml.ruleEditor.ruleDescription.modelUpdateActionTypeText', {
defaultMessage: 'model update',
description: 'Part of composite text: xpack.ml.ruleEditor.ruleDescription.[actionName]ActionTypeText + ' +
'xpack.ml.ruleEditor.ruleDescription.conditionsText + xpack.ml.ruleEditor.ruleDescription.filtersText'
});
break;
}
});
description += ' when ';
if (conditions !== undefined) {
conditions.forEach((condition, i) => {
if (i > 0) {
description += ' AND ';
conditionsText += ' AND ';
}
description += `${appliesToText(condition.applies_to)} is ${operatorToText(condition.operator)} ${condition.value}`;
conditionsText += i18n.translate('xpack.ml.ruleEditor.ruleDescription.conditionsText', {
defaultMessage: '{appliesTo} is {operator} {value}',
values: { appliesTo: appliesToText(condition.applies_to), operator: operatorToText(condition.operator), value: condition.value },
description: 'Part of composite text: xpack.ml.ruleEditor.ruleDescription.[actionName]ActionTypeText + ' +
'xpack.ml.ruleEditor.ruleDescription.conditionsText + xpack.ml.ruleEditor.ruleDescription.filtersText'
});
});
}
if (scope !== undefined) {
if (conditions !== undefined && conditions.length > 0) {
description += ' AND ';
filtersText += ' AND ';
}
const fieldNames = Object.keys(scope);
fieldNames.forEach((fieldName, i) => {
if (i > 0) {
description += ' AND ';
filtersText += ' AND ';
}
const filter = scope[fieldName];
description += `${fieldName} is ${filterTypeToText(filter.filter_type)} ${filter.filter_id}`;
filtersText += i18n.translate('xpack.ml.ruleEditor.ruleDescription.filtersText', {
defaultMessage: '{fieldName} is {filterType} {filterId}',
values: { fieldName, filterType: filterTypeToText(filter.filter_type), filterId: filter.filter_id },
description: 'Part of composite text: xpack.ml.ruleEditor.ruleDescription.[actionName]ActionTypeText + ' +
'xpack.ml.ruleEditor.ruleDescription.conditionsText + xpack.ml.ruleEditor.ruleDescription.filtersText'
});
});
}
return description;
return i18n.translate('xpack.ml.ruleEditor.ruleDescription', {
defaultMessage: 'skip {actions} when {conditions}{filters}',
values: {
actions: actionsText,
conditions: conditionsText,
filters: filtersText
},
description: 'Composite text: xpack.ml.ruleEditor.ruleDescription.[actionName]ActionTypeText + ' +
'xpack.ml.ruleEditor.ruleDescription.conditionsText + xpack.ml.ruleEditor.ruleDescription.filtersText.' +
' (Example: skip model update when actual is less than 1 AND ip is in xxx)'
});
}
export function filterTypeToText(filterType) {
switch (filterType) {
case FILTER_TYPE.INCLUDE:
return 'in';
return i18n.translate('xpack.ml.ruleEditor.includeFilterTypeText', { defaultMessage: 'in' });
case FILTER_TYPE.EXCLUDE:
return 'not in';
return i18n.translate('xpack.ml.ruleEditor.excludeFilterTypeText', { defaultMessage: 'not in' });
default:
return (filterType !== undefined) ? filterType : '';
@ -239,13 +275,12 @@ export function filterTypeToText(filterType) {
export function appliesToText(appliesTo) {
switch (appliesTo) {
case APPLIES_TO.ACTUAL:
return 'actual';
return i18n.translate('xpack.ml.ruleEditor.actualAppliesTypeText', { defaultMessage: 'actual' });
case APPLIES_TO.TYPICAL:
return 'typical';
return i18n.translate('xpack.ml.ruleEditor.typicalAppliesTypeText', { defaultMessage: 'typical' });
case APPLIES_TO.DIFF_FROM_TYPICAL:
return 'diff from typical';
return i18n.translate('xpack.ml.ruleEditor.diffFromTypicalAppliesTypeText', { defaultMessage: 'diff from typical' });
default:
return (appliesTo !== undefined) ? appliesTo : '';
@ -255,16 +290,16 @@ export function appliesToText(appliesTo) {
export function operatorToText(operator) {
switch (operator) {
case OPERATOR.LESS_THAN:
return 'less than';
return i18n.translate('xpack.ml.ruleEditor.lessThanOperatorTypeText', { defaultMessage: 'less than' });
case OPERATOR.LESS_THAN_OR_EQUAL:
return 'less than or equal to';
return i18n.translate('xpack.ml.ruleEditor.lessThanOrEqualToOperatorTypeText', { defaultMessage: 'less than or equal to' });
case OPERATOR.GREATER_THAN:
return 'greater than';
return i18n.translate('xpack.ml.ruleEditor.greaterThanOperatorTypeText', { defaultMessage: 'greater than' });
case OPERATOR.GREATER_THAN_OR_EQUAL:
return 'greater than or equal to';
return i18n.translate('xpack.ml.ruleEditor.greaterThanOrEqualToOperatorTypeText', { defaultMessage: 'greater than or equal to' });
default:
return (operator !== undefined) ? operator : '';