diff --git a/.i18nrc.json b/.i18nrc.json index 6c48a049ce316..d45eac4957bea 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -17,6 +17,7 @@ "timelion": "src/core_plugins/timelion", "tsvb": "src/core_plugins/metrics", "tagCloud": "src/core_plugins/tagcloud", + "tsvb": "src/core_plugins/metrics", "xpack.graph": "x-pack/plugins/graph", "xpack.grokDebugger": "x-pack/plugins/grokdebugger", "xpack.idxMgmt": "x-pack/plugins/index_management", diff --git a/src/core_plugins/metrics/public/components/add_delete_buttons.js b/src/core_plugins/metrics/public/components/add_delete_buttons.js index 222ffa1af0f5e..d6bc91a1320e8 100644 --- a/src/core_plugins/metrics/public/components/add_delete_buttons.js +++ b/src/core_plugins/metrics/public/components/add_delete_buttons.js @@ -20,6 +20,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { EuiToolTip, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; function AddDeleteButtons(props) { const { testSubj } = props; @@ -86,9 +87,9 @@ function AddDeleteButtons(props) { AddDeleteButtons.defaultProps = { testSubj: 'Add', - addTooltip: 'Add', - deleteTooltip: 'Delete', - cloneTooltip: 'Clone' + addTooltip: i18n.translate('tsvb.addDeleteButtons.addButtonDefaultTooltip', { defaultMessage: 'Add' }), + deleteTooltip: i18n.translate('tsvb.addDeleteButtons.deleteButtonDefaultTooltip', { defaultMessage: 'Delete' }), + cloneTooltip: i18n.translate('tsvb.addDeleteButtons.cloneButtonDefaultTooltip', { defaultMessage: 'Clone' }) }; AddDeleteButtons.propTypes = { diff --git a/src/core_plugins/metrics/public/components/add_delete_buttons.test.js b/src/core_plugins/metrics/public/components/add_delete_buttons.test.js index de24bbff55846..71492c5ee1f84 100644 --- a/src/core_plugins/metrics/public/components/add_delete_buttons.test.js +++ b/src/core_plugins/metrics/public/components/add_delete_buttons.test.js @@ -19,14 +19,14 @@ import React from 'react'; import { expect } from 'chai'; -import { shallow } from 'enzyme'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import sinon from 'sinon'; import AddDeleteButtons from './add_delete_buttons'; describe('AddDeleteButtons', () => { it('calls onAdd={handleAdd}', () => { const handleAdd = sinon.spy(); - const wrapper = shallow( + const wrapper = shallowWithIntl( ); wrapper.find('EuiButtonIcon').at(0).simulate('click'); @@ -35,7 +35,7 @@ describe('AddDeleteButtons', () => { it('calls onDelete={handleDelete}', () => { const handleDelete = sinon.spy(); - const wrapper = shallow( + const wrapper = shallowWithIntl( ); wrapper.find('EuiButtonIcon').at(1).simulate('click'); @@ -44,7 +44,7 @@ describe('AddDeleteButtons', () => { it('calls onClone={handleClone}', () => { const handleClone = sinon.spy(); - const wrapper = shallow( + const wrapper = shallowWithIntl( ); wrapper.find('EuiButtonIcon').at(0).simulate('click'); @@ -52,21 +52,21 @@ describe('AddDeleteButtons', () => { }); it('disableDelete={true}', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( ); expect(wrapper.find({ text: 'Delete' })).to.have.length(0); }); it('disableAdd={true}', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( ); expect(wrapper.find({ text: 'Add' })).to.have.length(0); }); it('should not display clone by default', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( ); expect(wrapper.find({ text: 'Clone' })).to.have.length(0); @@ -74,7 +74,7 @@ describe('AddDeleteButtons', () => { it('should not display clone when disableAdd={true}', () => { const fn = sinon.spy(); - const wrapper = shallow( + const wrapper = shallowWithIntl( ); expect(wrapper.find({ text: 'Clone' })).to.have.length(0); diff --git a/src/core_plugins/metrics/public/components/annotations_editor.js b/src/core_plugins/metrics/public/components/annotations_editor.js index 88c8f2062121b..b0d095a731035 100644 --- a/src/core_plugins/metrics/public/components/annotations_editor.js +++ b/src/core_plugins/metrics/public/components/annotations_editor.js @@ -41,6 +41,7 @@ import { EuiCode, EuiText, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; function newAnnotation() { return { @@ -99,7 +100,10 @@ class AnnotationsEditor extends Component { )} fullWidth > - + )} + fullWidth + > )} fullWidth > - Ignore global filters? + + + - Ignore panel filters? + + + - + )} + > )} fullWidth > )} helpText={ - eg. {'{{field}}'} + + {'{{field}}'}) }} + /> + } fullWidth > @@ -222,8 +264,18 @@ class AnnotationsEditor extends Component { .bind(null, this.props, newAnnotation); content = ( -

Click the button below to create an annotation data source.

- Add data source +

+ +

+ + +
); } else { @@ -231,7 +283,12 @@ class AnnotationsEditor extends Component { content = (
- Data sources + + + diff --git a/src/core_plugins/metrics/public/components/color_picker.js b/src/core_plugins/metrics/public/components/color_picker.js index 3458556300079..2bedccb32cdee 100644 --- a/src/core_plugins/metrics/public/components/color_picker.js +++ b/src/core_plugins/metrics/public/components/color_picker.js @@ -24,6 +24,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { EuiIconTip, } from '@elastic/eui'; import Picker from './custom_color_picker'; +import { injectI18n } from '@kbn/i18n/react'; class ColorPicker extends Component { @@ -65,7 +66,10 @@ class ColorPicker extends Component { if (!this.props.value) { return (
); } @@ -124,4 +140,4 @@ ColorPicker.propTypes = { onChange: PropTypes.func }; -export default ColorPicker; +export default injectI18n(ColorPicker); diff --git a/src/core_plugins/metrics/public/components/color_rules.js b/src/core_plugins/metrics/public/components/color_rules.js index 9533ef642a8ae..baaae852b1111 100644 --- a/src/core_plugins/metrics/public/components/color_rules.js +++ b/src/core_plugins/metrics/public/components/color_rules.js @@ -31,6 +31,8 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; class ColorRules extends Component { @@ -55,11 +57,15 @@ class ColorRules extends Component { const model = { ...defaults, ...row }; const handleAdd = collectionActions.handleAdd.bind(null, this.props); const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); + const { intl } = this.props; const operatorOptions = [ - { label: '> greater than', value: 'gt' }, - { label: '>= greater than or equal', value: 'gte' }, - { label: '< less than', value: 'lt' }, - { label: '<= less than or equal', value: 'lte' }, + { label: intl.formatMessage({ id: 'tsvb.colorRules.greaterThanLabel', defaultMessage: '> greater than' }), value: 'gt' }, + { + label: intl.formatMessage({ id: 'tsvb.colorRules.greaterThanOrEqualLabel', defaultMessage: '>= greater than or equal' }), + value: 'gte' + }, + { label: intl.formatMessage({ id: 'tsvb.colorRules.lessThanLabel', defaultMessage: '< less than' }), value: 'lt' }, + { label: intl.formatMessage({ id: 'tsvb.colorRules.lessThanOrEqualLabel', defaultMessage: '<= less than or equal' }), value: 'lte' }, ]; const handleColorChange = (part) => { const handleChange = collectionActions.handleChange.bind(null, this.props); @@ -77,7 +83,15 @@ class ColorRules extends Component { secondary = ( - and {this.props.secondaryName} to + + + - Set {this.props.primaryName} to + + + - if metric is + + + { return defaultValue === option.value; @@ -134,7 +137,13 @@ class DataFormatPicker extends Component {
- + )} + > - + )} + > - + )} + > this.decimals = el} @@ -171,9 +192,18 @@ class DataFormatPicker extends Component { custom = ( )} helpText={ - See Numeral.js + + Numeral.js) }} + /> + } > -
{title || 'The request for this panel failed'}
+
+ {title || + } +
{additionalInfo} ); diff --git a/src/core_plugins/metrics/public/components/icon_select.js b/src/core_plugins/metrics/public/components/icon_select.js index 576f62d2ad5dc..28a9d6bec2070 100644 --- a/src/core_plugins/metrics/public/components/icon_select.js +++ b/src/core_plugins/metrics/public/components/icon_select.js @@ -22,6 +22,7 @@ import React from 'react'; import { EuiComboBox, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; function renderOption(option) { const icon = option.value; @@ -53,21 +54,27 @@ function IconSelect(props) { IconSelect.defaultProps = { icons: [ - { value: 'fa-asterisk', label: 'Asterisk' }, - { value: 'fa-bell', label: 'Bell' }, - { value: 'fa-bolt', label: 'Bolt' }, - { value: 'fa-bomb', label: 'Bomb' }, - { value: 'fa-bug', label: 'Bug' }, - { value: 'fa-comment', label: 'Comment' }, - { value: 'fa-exclamation-circle', label: 'Exclamation Circle' }, - { value: 'fa-exclamation-triangle', label: 'Exclamation Triangle' }, - { value: 'fa-fire', label: 'Fire' }, - { value: 'fa-flag', label: 'Flag' }, - { value: 'fa-heart', label: 'Heart' }, - { value: 'fa-map-marker', label: 'Map Marker' }, - { value: 'fa-map-pin', label: 'Map Pin' }, - { value: 'fa-star', label: 'Star' }, - { value: 'fa-tag', label: 'Tag' }, + { value: 'fa-asterisk', label: i18n.translate('tsvb.iconSelect.asteriskLabel', { defaultMessage: 'Asterisk' }) }, + { value: 'fa-bell', label: i18n.translate('tsvb.iconSelect.bellLabel', { defaultMessage: 'Bell' }) }, + { value: 'fa-bolt', label: i18n.translate('tsvb.iconSelect.boltLabel', { defaultMessage: 'Bolt' }) }, + { value: 'fa-bomb', label: i18n.translate('tsvb.iconSelect.bombLabel', { defaultMessage: 'Bomb' }) }, + { value: 'fa-bug', label: i18n.translate('tsvb.iconSelect.bugLabel', { defaultMessage: 'Bug' }) }, + { value: 'fa-comment', label: i18n.translate('tsvb.iconSelect.commentLabel', { defaultMessage: 'Comment' }) }, + { + value: 'fa-exclamation-circle', + label: i18n.translate('tsvb.iconSelect.exclamationCircleLabel', { defaultMessage: 'Exclamation Circle' }) + }, + { + value: 'fa-exclamation-triangle', + label: i18n.translate('tsvb.iconSelect.exclamationTriangleLabel', { defaultMessage: 'Exclamation Triangle' }) + }, + { value: 'fa-fire', label: i18n.translate('tsvb.iconSelect.fireLabel', { defaultMessage: 'Fire' }) }, + { value: 'fa-flag', label: i18n.translate('tsvb.iconSelect.flagLabel', { defaultMessage: 'Flag' }) }, + { value: 'fa-heart', label: i18n.translate('tsvb.iconSelect.heartLabel', { defaultMessage: 'Heart' }) }, + { value: 'fa-map-marker', label: i18n.translate('tsvb.iconSelect.mapMarkerLabel', { defaultMessage: 'Map Marker' }) }, + { value: 'fa-map-pin', label: i18n.translate('tsvb.iconSelect.mapPinLabel', { defaultMessage: 'Map Pin' }) }, + { value: 'fa-star', label: i18n.translate('tsvb.iconSelect.starLabel', { defaultMessage: 'Star' }) }, + { value: 'fa-tag', label: i18n.translate('tsvb.iconSelect.tagLabel', { defaultMessage: 'Tag' }) }, ] }; diff --git a/src/core_plugins/metrics/public/components/index_pattern.js b/src/core_plugins/metrics/public/components/index_pattern.js index 15712513f416b..dc456026acb5a 100644 --- a/src/core_plugins/metrics/public/components/index_pattern.js +++ b/src/core_plugins/metrics/public/components/index_pattern.js @@ -32,6 +32,7 @@ import { EuiFormLabel, EuiSpacer, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; export const IndexPattern = props => { const { fields, prefix } = props; @@ -57,7 +58,10 @@ export const IndexPattern = props => { )} fullWidth > { )} fullWidth > { )} + helpText={()} > { - Drop last bucket? + + +

- The following variables can be used in the Markdown by using the Handlebar (mustache) syntax.{' '} - - Click here for documentation - {' '} - on the available expressions. + + + + ) + }} + />

- - + + {rows} @@ -169,7 +191,12 @@ class MarkdownEditor extends Component { {rows.length === 0 && ( - No variables available for the selected data metrics. + + + )} @@ -177,8 +204,12 @@ class MarkdownEditor extends Component {

- There is also a special variable named _all which you can use to access the entire tree. This is useful for - creating lists with data from a group by... + _all) }} + />

diff --git a/src/core_plugins/metrics/public/components/no_data.js b/src/core_plugins/metrics/public/components/no_data.js index 579d87076fe05..4a7d62294bc6c 100644 --- a/src/core_plugins/metrics/public/components/no_data.js +++ b/src/core_plugins/metrics/public/components/no_data.js @@ -18,11 +18,17 @@ */ import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; function NoDataComponent() { return (
-
No data to display for the selected metrics
+
+ +
); } diff --git a/src/core_plugins/metrics/public/components/panel_config.js b/src/core_plugins/metrics/public/components/panel_config.js index 91c9449276083..c45c83d5644c2 100644 --- a/src/core_plugins/metrics/public/components/panel_config.js +++ b/src/core_plugins/metrics/public/components/panel_config.js @@ -25,6 +25,7 @@ import topN from './panel_config/top_n'; import table from './panel_config/table'; import gauge from './panel_config/gauge'; import markdown from './panel_config/markdown'; +import { FormattedMessage } from '@kbn/i18n/react'; const types = { timeseries, @@ -41,7 +42,14 @@ function PanelConfig(props) { if (component) { return React.createElement(component, props); } - return (
Missing panel config for “{model.type}”
); + return ( +
+ +
); } PanelConfig.propTypes = { diff --git a/src/core_plugins/metrics/public/components/series.js b/src/core_plugins/metrics/public/components/series.js index 9cec7b7eace1d..fba1a69e1cc72 100644 --- a/src/core_plugins/metrics/public/components/series.js +++ b/src/core_plugins/metrics/public/components/series.js @@ -28,6 +28,7 @@ import table from './vis_types/table/series'; import gauge from './vis_types/gauge/series'; import markdown from './vis_types/markdown/series'; import { sortable } from 'react-anything-sortable'; +import { FormattedMessage } from '@kbn/i18n/react'; const lookup = { top_n: topN, @@ -98,7 +99,15 @@ class Series extends Component { }; return (); } - return (
Missing Series component for panel type: {panel.type}
); + return ( +
+ +
+ ); } } diff --git a/src/core_plugins/metrics/public/components/series_config.js b/src/core_plugins/metrics/public/components/series_config.js index ba46b589f1a04..989a59a21a4a1 100644 --- a/src/core_plugins/metrics/public/components/series_config.js +++ b/src/core_plugins/metrics/public/components/series_config.js @@ -35,6 +35,7 @@ import { EuiFormLabel, EuiSpacer, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; export const SeriesConfig = props => { const defaults = { offset_time: '', value_template: '' }; @@ -55,7 +56,10 @@ export const SeriesConfig = props => { )} fullWidth > { eg.{'{{value}}/s'}} + label={()} + helpText={( + + {'{{value}}/s'}) }} + /> + + )} fullWidth > { )} > { - Override Index Pattern? + + + + + + ); } diff --git a/src/core_plugins/metrics/public/components/vis_editor_visualization.js b/src/core_plugins/metrics/public/components/vis_editor_visualization.js index 278265769b772..eac9a9e99b849 100644 --- a/src/core_plugins/metrics/public/components/vis_editor_visualization.js +++ b/src/core_plugins/metrics/public/components/vis_editor_visualization.js @@ -21,6 +21,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { keyCodes, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui'; import { getVisualizeLoader } from 'ui/visualize/loader/visualize_loader'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; const MIN_CHART_HEIGHT = 250; @@ -140,15 +141,31 @@ class VisEditorVisualization extends Component { style.userSelect = 'none'; } - let applyMessage = 'The latest changes have been applied.'; - if (dirty) applyMessage = 'The changes to this visualization have not been applied.'; - if (autoApply) applyMessage = 'The changes will be automatically applied.'; + let applyMessage = (); + if (dirty) { + applyMessage = (); + } + if (autoApply) { + applyMessage = (); + } const applyButton = ( )} checked={autoApply} onChange={this.props.onToggleAutoApply} /> @@ -164,7 +181,12 @@ class VisEditorVisualization extends Component { {!autoApply && - Apply changes + + + } @@ -189,7 +211,10 @@ class VisEditorVisualization extends Component { onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} onKeyDown={this.onSizeHandleKeyDown} - aria-label="Press up/down to adjust the chart size" + aria-label={this.props.intl.formatMessage({ + id: 'tsvb.colorRules.adjustChartSizeAriaLabel', + defaultMessage: 'Press up/down to adjust the chart size' + })} > @@ -215,4 +240,4 @@ VisEditorVisualization.propTypes = { appState: PropTypes.object, }; -export default VisEditorVisualization; +export default injectI18n(VisEditorVisualization); diff --git a/src/core_plugins/metrics/public/components/vis_editor_visualization.test.js b/src/core_plugins/metrics/public/components/vis_editor_visualization.test.js index 15b6666207f73..146afeb07605d 100644 --- a/src/core_plugins/metrics/public/components/vis_editor_visualization.test.js +++ b/src/core_plugins/metrics/public/components/vis_editor_visualization.test.js @@ -20,7 +20,7 @@ jest.mock('ui/visualize/loader/visualize_loader', () => ({})); import React from 'react'; -import { mount } from 'enzyme'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import VisEditorVisualization from './vis_editor_visualization'; describe('getVisualizeLoader', () => { @@ -45,7 +45,7 @@ describe('getVisualizeLoader', () => { }); it('should not call _handler.update until getVisualizeLoader returns _handler', async () => { - const wrapper = mount( + const wrapper = mountWithIntl( ); diff --git a/src/core_plugins/metrics/public/components/vis_picker.js b/src/core_plugins/metrics/public/components/vis_picker.js index 22579125837a3..d58fa8cfd43a2 100644 --- a/src/core_plugins/metrics/public/components/vis_picker.js +++ b/src/core_plugins/metrics/public/components/vis_picker.js @@ -20,6 +20,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { EuiTabs, EuiTab } from '@elastic/eui'; +import { injectI18n } from '@kbn/i18n/react'; function VisPickerItem(props) { const { label, type, selected } = props; @@ -44,19 +45,19 @@ VisPickerItem.propTypes = { selected: PropTypes.bool }; -function VisPicker(props) { +const VisPicker = injectI18n(function (props) { const handleChange = (type) => { props.onChange({ type }); }; - const { model } = props; + const { model, intl } = props; const tabs = [ - { type: 'timeseries', label: 'Time Series' }, - { type: 'metric', label: 'Metric' }, - { type: 'top_n', label: 'Top N' }, - { type: 'gauge', label: 'Gauge' }, - { type: 'markdown', label: 'Markdown' }, - { type: 'table', label: 'Table' } + { type: 'timeseries', label: intl.formatMessage({ id: 'tsvb.visPicker.timeSeriesLabel', defaultMessage: 'Time Series' }) }, + { type: 'metric', label: intl.formatMessage({ id: 'tsvb.visPicker.metricLabel', defaultMessage: 'Metric' }) }, + { type: 'top_n', label: intl.formatMessage({ id: 'tsvb.visPicker.topNLabel', defaultMessage: 'Top N' }) }, + { type: 'gauge', label: intl.formatMessage({ id: 'tsvb.visPicker.gaugeLabel', defaultMessage: 'Gauge' }) }, + { type: 'markdown', label: intl.formatMessage({ id: 'tsvb.visPicker.markdownLabel', defaultMessage: 'Markdown' }) }, + { type: 'table', label: intl.formatMessage({ id: 'tsvb.visPicker.tableLabel', defaultMessage: 'Table' }) } ].map(item => { return ( ); -} +}); VisPicker.propTypes = { model: PropTypes.object, diff --git a/src/core_plugins/metrics/public/components/vis_types/table/config.js b/src/core_plugins/metrics/public/components/vis_types/table/config.js index 324bc89d4dc86..c0d20467d66dc 100644 --- a/src/core_plugins/metrics/public/components/vis_types/table/config.js +++ b/src/core_plugins/metrics/public/components/vis_types/table/config.js @@ -39,6 +39,7 @@ import { EuiSpacer, EuiTitle, } from '@elastic/eui'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; class TableSeriesConfig extends Component { @@ -57,17 +58,18 @@ class TableSeriesConfig extends Component { const handleSelectChange = createSelectHandler(this.props.onChange); const handleTextChange = createTextHandler(this.props.onChange); const htmlId = htmlIdGenerator(); + const { intl } = this.props; const functionOptions = [ - { label: 'Sum', value: 'sum' }, - { label: 'Max', value: 'max' }, - { label: 'Min', value: 'min' }, - { label: 'Avg', value: 'mean' }, - { label: 'Overall Sum', value: 'overall_sum' }, - { label: 'Overall Max', value: 'overall_max' }, - { label: 'Overall Min', value: 'overall_min' }, - { label: 'Overall Avg', value: 'overall_avg' }, - { label: 'Cumulative Sum', value: 'cumulative_sum' }, + { label: intl.formatMessage({ id: 'tsvb.table.sumLabel', defaultMessage: 'Sum' }), value: 'sum' }, + { label: intl.formatMessage({ id: 'tsvb.table.maxLabel', defaultMessage: 'Max' }), value: 'max' }, + { label: intl.formatMessage({ id: 'tsvb.table.minLabel', defaultMessage: 'Min' }), value: 'min' }, + { label: intl.formatMessage({ id: 'tsvb.table.avgLabel', defaultMessage: 'Avg' }), value: 'mean' }, + { label: intl.formatMessage({ id: 'tsvb.table.overallSumLabel', defaultMessage: 'Overall Sum' }), value: 'overall_sum' }, + { label: intl.formatMessage({ id: 'tsvb.table.overallMaxLabel', defaultMessage: 'Overall Max' }), value: 'overall_max' }, + { label: intl.formatMessage({ id: 'tsvb.table.overallMinLabel', defaultMessage: 'Overall Min' }), value: 'overall_min' }, + { label: intl.formatMessage({ id: 'tsvb.table.overallAvgLabel', defaultMessage: 'Overall Avg' }), value: 'overall_avg' }, + { label: intl.formatMessage({ id: 'tsvb.table.cumulativeSumLabel', defaultMessage: 'Cumulative Sum' }), value: 'cumulative_sum' }, ]; const selectedAggFuncOption = functionOptions.find(option => { return model.aggregate_function === option.value; @@ -86,8 +88,19 @@ class TableSeriesConfig extends Component { eg.{'{{value}}/s'}} + label={()} + helpText={ + + {'{{value}}/s'}) }} + /> + + } fullWidth > )} fullWidth > - Show trend arrows? + + + - + )} + > )} fullWidth > - Color rules + + + + + props.switchTab('metrics')} > - Metrics + props.switchTab('options')} > - Options + {seriesBody} @@ -102,11 +110,16 @@ function TopNSeries(props) { if (!props.disableDelete) { dragHandle = ( - + )} + > @@ -127,7 +140,7 @@ function TopNSeries(props) { iconType={caretIcon} color="text" onClick={props.toggleVisible} - aria-label="Toggle series editor" + aria-label={intl.formatMessage({ id: 'tsvb.table.toggleSeriesEditorAriaLabel', defaultMessage: 'Toggle series editor' })} aria-expanded={props.visible} /> @@ -135,9 +148,9 @@ function TopNSeries(props) { @@ -146,9 +159,9 @@ function TopNSeries(props) { {headerContent} + )} + > + {headerContent} + ); } @@ -176,10 +184,15 @@ class TableVis extends Component { if (_.isArray(visData.series) && visData.series.length) { rows = visData.series.map(this.renderRow); } else { - let message = 'No results available.'; - if (!model.pivot_id) { - message += ' You must choose a group by field for this visualization.'; - } + const message = model.pivot_id ? + () + : (); rows = (
NameValue + + + +
{ return model.stacked === option.value; }); const positionOptions = [ - { label: 'Right', value: 'right' }, - { label: 'Left', value: 'left' } + { label: intl.formatMessage({ id: 'tsvb.timeSeries.rightLabel', defaultMessage: 'Right' }), value: 'right' }, + { label: intl.formatMessage({ id: 'tsvb.timeSeries.leftLabel', defaultMessage: 'Left' }), value: 'left' } ]; const selectedAxisPosOption = positionOptions.find(option => { return model.axis_position === option.value; }); const chartTypeOptions = [ - { label: 'Bar', value: 'bar' }, - { label: 'Line', value: 'line' } + { label: intl.formatMessage({ id: 'tsvb.timeSeries.barLabel', defaultMessage: 'Bar' }), value: 'bar' }, + { label: intl.formatMessage({ id: 'tsvb.timeSeries.lineLabel', defaultMessage: 'Line' }), value: 'line' } ]; const selectedChartTypeOption = chartTypeOptions.find(option => { return model.chart_type === option.value; }); const splitColorOptions = [ - { label: 'Gradient', value: 'gradient' }, - { label: 'Rainbow', value: 'rainbow' } + { label: intl.formatMessage({ id: 'tsvb.timeSeries.gradientLabel', defaultMessage: 'Gradient' }), value: 'gradient' }, + { label: intl.formatMessage({ id: 'tsvb.timeSeries.rainbowLabel', defaultMessage: 'Rainbow' }), value: 'rainbow' } ]; const selectedSplitColorOption = splitColorOptions.find(option => { return model.split_color_mode === option.value; @@ -97,7 +99,10 @@ function TimeseriesConfig(props) { )} > )} > )} > )} > )} > - Steps + + + )} > )} > )} > )} > eg.{'{{value}}/s'}} + label={()} + helpText={( + + {'{{value}}/s'}) }} + /> + + )} fullWidth > )} fullWidth > )} > - Hide in legend + + + )} > - Separate axis? + + + )} > {/* EUITODO: The following input couldn't be converted to EUI because of type mis-match. @@ -345,7 +413,10 @@ function TimeseriesConfig(props) { )} > {/* EUITODO: The following input couldn't be converted to EUI because of type mis-match. @@ -362,7 +433,10 @@ function TimeseriesConfig(props) { )} > - Override Index Pattern? + + + ); -} +}); TimeseriesConfig.propTypes = { fields: PropTypes.object, diff --git a/src/core_plugins/metrics/public/components/vis_types/timeseries/series.js b/src/core_plugins/metrics/public/components/vis_types/timeseries/series.js index 23a349aaac39e..f4eaf628a3500 100644 --- a/src/core_plugins/metrics/public/components/vis_types/timeseries/series.js +++ b/src/core_plugins/metrics/public/components/vis_types/timeseries/series.js @@ -28,8 +28,9 @@ import Split from '../../split'; import createAggRowRender from '../../lib/create_agg_row_render'; import createTextHandler from '../../lib/create_text_handler'; import { createUpDownHandler } from '../../lib/sort_keyhandler'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; -function TimeseriesSeries(props) { +const TimeseriesSeries = injectI18n(function (props) { const { panel, fields, @@ -39,7 +40,8 @@ function TimeseriesSeries(props) { disableAdd, selectedTab, onChange, - visible + visible, + intl } = props; const defaults = { label: '' }; @@ -96,14 +98,20 @@ function TimeseriesSeries(props) { isSelected={selectedTab === 'metrics'} onClick={() => props.switchTab('metrics')} > - Metrics + props.switchTab('options')} > - Options + {seriesBody} @@ -124,11 +132,19 @@ function TimeseriesSeries(props) { if (!props.disableDelete) { dragHandle = ( - + )} + > @@ -149,7 +165,10 @@ function TimeseriesSeries(props) { iconType={caretIcon} color="text" onClick={props.toggleVisible} - aria-label="Toggle series editor" + aria-label={intl.formatMessage({ + id: 'tsvb.timeSeries.toggleSeriesEditorAriaLabel', + defaultMessage: 'Toggle series editor' + })} aria-expanded={props.visible} /> @@ -162,7 +181,7 @@ function TimeseriesSeries(props) { @@ -171,9 +190,9 @@ function TimeseriesSeries(props) { ); -} +}); TimeseriesSeries.propTypes = { className: PropTypes.string, @@ -216,4 +235,4 @@ TimeseriesSeries.propTypes = { visible: PropTypes.bool }; -export default TimeseriesSeries; +export default injectI18n(TimeseriesSeries); diff --git a/src/core_plugins/metrics/public/components/vis_types/top_n/series.js b/src/core_plugins/metrics/public/components/vis_types/top_n/series.js index fa112a41e39c8..754becb788fbc 100644 --- a/src/core_plugins/metrics/public/components/vis_types/top_n/series.js +++ b/src/core_plugins/metrics/public/components/vis_types/top_n/series.js @@ -28,8 +28,9 @@ import { EuiToolTip, EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiFieldText, E import createTextHandler from '../../lib/create_text_handler'; import createAggRowRender from '../../lib/create_agg_row_render'; import { createUpDownHandler } from '../../lib/sort_keyhandler'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; -function TopNSeries(props) { +const TopNSeries = injectI18n(function (props) { const { panel, model, @@ -40,7 +41,8 @@ function TopNSeries(props) { disableDelete, disableAdd, selectedTab, - visible + visible, + intl } = props; const handleChange = createTextHandler(onChange); @@ -94,14 +96,20 @@ function TopNSeries(props) { isSelected={selectedTab === 'metrics'} onClick={() => props.switchTab('metrics')} > - Metrics + props.switchTab('options')} > - Options + {seriesBody} @@ -122,11 +130,16 @@ function TopNSeries(props) { if (!props.disableDelete) { dragHandle = ( - + )} + > @@ -147,7 +160,7 @@ function TopNSeries(props) { iconType={caretIcon} color="text" onClick={props.toggleVisible} - aria-label="Toggle series editor" + aria-label={intl.formatMessage({ id: 'tsvb.topN.toggleSeriesEditorAriaLabel', defaultMessage: 'Toggle series editor' })} aria-expanded={props.visible} /> @@ -160,7 +173,7 @@ function TopNSeries(props) { @@ -169,9 +182,9 @@ function TopNSeries(props) { ); -} +}); TopNSeries.propTypes = { className: PropTypes.string, diff --git a/src/core_plugins/metrics/public/components/visualization.js b/src/core_plugins/metrics/public/components/visualization.js index b72e1b047558c..505809afdd800 100644 --- a/src/core_plugins/metrics/public/components/visualization.js +++ b/src/core_plugins/metrics/public/components/visualization.js @@ -63,18 +63,20 @@ function Visualization(props) { const component = types[model.type]; if (component) { - return React.createElement(component, { - dateFormat: props.dateFormat, - reversed: props.reversed, - backgroundColor: props.backgroundColor, - model: props.model, - onBrush: props.onBrush, - onChange: props.onChange, - onUiState: props.onUiState, - uiState: props.uiState, - visData: visData.type === model.type ? visData : {}, - getConfig: props.getConfig - }); + return ( + React.createElement(component, { + dateFormat: props.dateFormat, + reversed: props.reversed, + backgroundColor: props.backgroundColor, + model: props.model, + onBrush: props.onBrush, + onChange: props.onChange, + onUiState: props.onUiState, + uiState: props.uiState, + visData: visData.type === model.type ? visData : {}, + getConfig: props.getConfig + }) + ); } return
; } diff --git a/src/core_plugins/metrics/public/components/yes_no.js b/src/core_plugins/metrics/public/components/yes_no.js index 7a79ac6ca988c..beda91b8d3c04 100644 --- a/src/core_plugins/metrics/public/components/yes_no.js +++ b/src/core_plugins/metrics/public/components/yes_no.js @@ -21,6 +21,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import _ from 'lodash'; import { EuiRadio, htmlIdGenerator } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; function YesNo(props) { const { name, value } = props; @@ -37,7 +38,10 @@ function YesNo(props) {
)} className="eui-displayInlineBlock" name={inputName} checked={Boolean(value)} @@ -49,7 +53,10 @@ function YesNo(props) { )} className="eui-displayInlineBlock" name={inputName} checked={!Boolean(value)} diff --git a/src/core_plugins/metrics/public/components/yes_no.test.js b/src/core_plugins/metrics/public/components/yes_no.test.js index e931ba5ce0775..09d936fde6f1f 100644 --- a/src/core_plugins/metrics/public/components/yes_no.test.js +++ b/src/core_plugins/metrics/public/components/yes_no.test.js @@ -19,14 +19,14 @@ import React from 'react'; import { expect } from 'chai'; -import { shallow } from 'enzyme'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import sinon from 'sinon'; import YesNo from './yes_no'; describe('YesNo', () => { it('call onChange={handleChange} on yes', () => { const handleChange = sinon.spy(); - const wrapper = shallow( + const wrapper = shallowWithIntl( ); wrapper.find('EuiRadio').first().simulate('change'); @@ -38,7 +38,7 @@ describe('YesNo', () => { it('call onChange={handleChange} on no', () => { const handleChange = sinon.spy(); - const wrapper = shallow( + const wrapper = shallowWithIntl( ); wrapper.find('EuiRadio').last().simulate('change'); diff --git a/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js b/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js index 456a52fe3a4cc..89c859fdd421c 100644 --- a/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js +++ b/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js @@ -42,9 +42,7 @@ function ReactEditorControllerProvider(Private, config) { isEditorMode={true} appState={params.appState} /> - , - this.el - ); + , this.el); } resize() { diff --git a/src/core_plugins/metrics/public/visualizations/components/horizontal_legend.js b/src/core_plugins/metrics/public/visualizations/components/horizontal_legend.js index 8f9dccadbe60d..37fb4bab667b7 100644 --- a/src/core_plugins/metrics/public/visualizations/components/horizontal_legend.js +++ b/src/core_plugins/metrics/public/visualizations/components/horizontal_legend.js @@ -22,8 +22,9 @@ import React from 'react'; import createLegendSeries from '../lib/create_legend_series'; import reactcss from 'reactcss'; import { htmlIdGenerator, EuiButtonIcon } from '@elastic/eui'; +import { injectI18n } from '@kbn/i18n/react'; -function HorizontalLegend(props) { +const HorizontalLegend = injectI18n(function (props) { const rows = props.series.map(createLegendSeries(props)); const htmlId = htmlIdGenerator(); const styles = reactcss({ @@ -46,7 +47,7 @@ function HorizontalLegend(props) { color="text" iconSize="s" onClick={props.onClick} - aria-label="Toggle chart legend" + aria-label={props.intl.formatMessage({ id: 'tsvb.horizontalLegend.toggleChartAriaLabel', defaultMessage: 'Toggle chart legend' })} aria-expanded={!!props.showLegend} aria-controls={htmlId('legend')} /> @@ -55,7 +56,7 @@ function HorizontalLegend(props) {
); -} +}); HorizontalLegend.propTypes = { legendPosition: PropTypes.string, diff --git a/src/core_plugins/metrics/public/visualizations/components/vertical_legend.js b/src/core_plugins/metrics/public/visualizations/components/vertical_legend.js index d2183bfbca34f..cfd9fa840f9ec 100644 --- a/src/core_plugins/metrics/public/visualizations/components/vertical_legend.js +++ b/src/core_plugins/metrics/public/visualizations/components/vertical_legend.js @@ -22,8 +22,9 @@ import React from 'react'; import createLegendSeries from '../lib/create_legend_series'; import reactcss from 'reactcss'; import { htmlIdGenerator, EuiButtonIcon } from '@elastic/eui'; +import { injectI18n } from '@kbn/i18n/react'; -function VerticalLegend(props) { +const VerticalLegend = injectI18n(function (props) { const rows = props.series.map(createLegendSeries(props)); const htmlId = htmlIdGenerator(); const hideLegend = !props.showLegend; @@ -56,7 +57,7 @@ function VerticalLegend(props) { color="text" iconSize="s" onClick={props.onClick} - aria-label="Toggle chart legend" + aria-label={props.intl.formatMessage({ id: 'tsvb.verticalLegend.toggleChartAriaLabel', defaultMessage: 'Toggle chart legend' })} aria-expanded={!!props.showLegend} aria-controls={htmlId('legend')} /> @@ -67,7 +68,7 @@ function VerticalLegend(props) { ); -} +}); VerticalLegend.propTypes = { legendPosition: PropTypes.string, diff --git a/src/core_plugins/metrics/server/lib/vis_data/helpers/bucket_transform.js b/src/core_plugins/metrics/server/lib/vis_data/helpers/bucket_transform.js index ce061464cc994..c487e541beb39 100644 --- a/src/core_plugins/metrics/server/lib/vis_data/helpers/bucket_transform.js +++ b/src/core_plugins/metrics/server/lib/vis_data/helpers/bucket_transform.js @@ -21,11 +21,17 @@ import parseSettings from './parse_settings'; import getBucketsPath from './get_buckets_path'; import { parseInterval } from './parse_interval'; import { set } from 'lodash'; +import { i18n } from '@kbn/i18n'; function checkMetric(metric, fields) { fields.forEach(field => { if (!metric[field]) { - throw new Error(`Metric missing ${field}`); + throw new Error( + i18n.translate('tsvb.metricMissingErrorMessage', { + defaultMessage: 'Metric missing {field}', + values: { field } + }) + ); } }); } diff --git a/src/core_plugins/metrics/server/lib/vis_data/series/handle_response_body.js b/src/core_plugins/metrics/server/lib/vis_data/series/handle_response_body.js index 20eb2588ffde1..f732db82f45ed 100644 --- a/src/core_plugins/metrics/server/lib/vis_data/series/handle_response_body.js +++ b/src/core_plugins/metrics/server/lib/vis_data/series/handle_response_body.js @@ -20,6 +20,7 @@ import buildProcessorFunction from '../build_processor_function'; import processors from '../response_processors/series'; import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; export default function handleResponseBody(panel) { return resp => { @@ -30,12 +31,19 @@ export default function handleResponseBody(panel) { } const aggregations = get(resp, 'aggregations'); if (!aggregations) { - const message = `The aggregations key is missing from the response, - check your permissions for this request.`; + const message = i18n.translate('tsvb.series.missingAggregationKeyErrorMessage', { + defaultMessage: 'The aggregations key is missing from the response, check your permissions for this request.' + }); throw Error(message); } const keys = Object.keys(aggregations); - if (keys.length !== 1) throw Error('There should only be one series per request.'); + if (keys.length !== 1) { + throw Error( + i18n.translate('tsvb.series.shouldOneSeriesPerRequestErrorMessage', { + defaultMessage: 'There should only be one series per request.' + }) + ); + } const seriesId = keys[0]; const series = panel.series.find(s => s.id === seriesId); const processor = buildProcessorFunction(processors, resp, panel, series);