From 1674a3d3b5288f10d49c6967f8cf0c1ba22010cc Mon Sep 17 00:00:00 2001 From: Maryia Lapata Date: Tue, 11 Sep 2018 08:40:45 +0300 Subject: [PATCH] Translate Input Control component (#22400) (#22895) * Translate Input Control component * Remove export from variables that get wrapped by a helper * Refactoring * Update message ids * Fix unit tests --- .i18nrc.json | 1 + .../__snapshots__/controls_tab.test.js.snap | 10 ++- .../list_control_editor.test.js.snap | 20 ++--- .../__snapshots__/options_tab.test.js.snap | 24 +++++- .../range_control_editor.test.js.snap | 20 ++++- .../components/editor/control_editor.js | 24 ++++-- .../public/components/editor/controls_tab.js | 31 +++++-- .../components/editor/controls_tab.test.js | 12 +-- .../public/components/editor/field_select.js | 18 ++++- .../components/editor/index_pattern_select.js | 19 ++++- .../components/editor/list_control_editor.js | 53 +++++++++--- .../editor/list_control_editor.test.js | 16 ++-- .../public/components/editor/options_tab.js | 17 +++- .../components/editor/options_tab.test.js | 9 ++- .../components/editor/range_control_editor.js | 12 ++- .../editor/range_control_editor.test.js | 7 +- .../input_control_vis.test.js.snap | 80 +++++++++++++++---- .../components/vis/input_control_vis.js | 8 +- .../components/vis/input_control_vis.test.js | 9 ++- .../public/components/vis/list_control.js | 23 ++++-- .../components/vis/list_control.test.js | 6 +- .../public/components/vis/range_control.js | 17 +++- .../components/vis/range_control.test.js | 8 +- .../public/control/control.js | 20 +++-- .../public/control/list_control_factory.js | 11 ++- .../public/control/range_control_factory.js | 6 +- .../input_control_vis/public/register_vis.js | 17 +++- .../public/vis_controller.js | 26 +++--- .../public/vis/editors/default/vis_options.js | 6 +- 29 files changed, 388 insertions(+), 142 deletions(-) diff --git a/.i18nrc.json b/.i18nrc.json index 60484e5efc307..d53285671b768 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -1,6 +1,7 @@ { "paths": { "common.ui": "src/ui", + "inputControl":"src/core_plugins/input_control_vis", "kbn": "src/core_plugins/kibana", "statusPage": "src/core_plugins/status_page", "xpack.idxMgmt": "x-pack/plugins/index_management" diff --git a/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap b/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap index 8f7e4014bd786..57dbeedd94a77 100644 --- a/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap +++ b/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.js.snap @@ -2,7 +2,7 @@ exports[`renders ControlsTab 1`] = `
- - - Add + diff --git a/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap b/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap index 692a57aec1b11..1d7e06fdd4bcd 100644 --- a/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap +++ b/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/list_control_editor.test.js.snap @@ -2,14 +2,14 @@ exports[`renders dynamic options should display disabled dynamic options with tooltip for non-string fields 1`] = `
- - - - - - - - - - + } onChange={[Function]} /> @@ -24,7 +30,13 @@ exports[`renders OptionsTab 1`] = ` + } onChange={[Function]} /> @@ -36,7 +48,13 @@ exports[`renders OptionsTab 1`] = ` > + } onChange={[Function]} /> diff --git a/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/range_control_editor.test.js.snap b/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/range_control_editor.test.js.snap index 43f936ce6e588..c3c1b2b607ace 100644 --- a/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/range_control_editor.test.js.snap +++ b/src/core_plugins/input_control_vis/public/components/editor/__snapshots__/range_control_editor.test.js.snap @@ -2,14 +2,14 @@ exports[`renders RangeControlEditor 1`] = `
- - + } > + } > { this.props.handleLabelChange(this.props.controlIndex, evt); @@ -101,7 +102,7 @@ export class ControlEditor extends Component { } > @@ -151,12 +154,21 @@ export class ControlsTab extends Component { > this.setState({ type: evt.target.value })} - aria-label="Select control type" + aria-label={intl.formatMessage({ + id: 'inputControl.editor.controlsTab.select.controlTypeAriaLabel', + defaultMessage: 'Select control type' + })} /> @@ -169,9 +181,12 @@ export class ControlsTab extends Component { onClick={this.handleAddControl} iconType="plusInCircle" data-test-subj="inputControlEditorAddBtn" - aria-label="Add control" + aria-label={intl.formatMessage({ + id: 'inputControl.editor.controlsTab.select.addControlAriaLabel', + defaultMessage: 'Add control' + })} > - Add + @@ -183,7 +198,9 @@ export class ControlsTab extends Component { } } -ControlsTab.propTypes = { +ControlsTabUi.propTypes = { scope: PropTypes.object.isRequired, stageEditorParams: PropTypes.func.isRequired }; + +export const ControlsTab = injectI18n(ControlsTabUi); diff --git a/src/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js b/src/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js index ee933d8894f37..b6a49cdaa0619 100644 --- a/src/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js +++ b/src/core_plugins/input_control_vis/public/components/editor/controls_tab.test.js @@ -19,7 +19,7 @@ import React from 'react'; import sinon from 'sinon'; -import { mount, shallow } from 'enzyme'; +import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { getIndexPatternMock } from './__tests__/get_index_pattern_mock'; import { @@ -87,7 +87,7 @@ beforeEach(() => { }); test('renders ControlsTab', () => { - const component = shallow( { describe('behavior', () => { test('add control button', () => { - const component = mount( { }); test('remove control button', () => { - const component = mount( { test('move down control button', () => { - const component = mount( { }); test('move up control button', () => { - const component = mount( 0) { const parentCandidatesOptions = [ { value: '', text: '' }, @@ -110,8 +112,14 @@ export class ListControlEditor extends Component { options.push( { this.props.handleCheckboxOptionChange(this.props.controlIndex, 'multiselect', evt); @@ -143,8 +157,14 @@ export class ListControlEditor extends Component { ); const dynamicOptionsHelpText = this.state.isStringField - ? 'Update options in response to user input' - : 'Only available for "string" fields'; + ? intl.formatMessage({ + id: 'inputControl.editor.listControl.dynamicOptions.updateDescription', + defaultMessage: 'Update options in response to user input' + }) + : intl.formatMessage({ + id: 'inputControl.editor.listControl.dynamicOptions.stringFieldDescription', + defaultMessage: 'Only available for "string" fields' + }); options.push( { this.props.handleCheckboxOptionChange(this.props.controlIndex, 'dynamicOptions', evt); @@ -168,9 +191,15 @@ export class ListControlEditor extends Component { options.push( { size: 5, } }; - const component = shallow( { { value: '1', text: 'fieldA' }, { value: '2', text: 'fieldB' } ]; - const component = shallow( { size: 5, } }; - const component = shallow( { size: 5, } }; - const component = shallow( { size: 5, } }; - const component = shallow( { }); test('handleCheckboxOptionChange - multiselect', async () => { - const component = mount( { }); test('handleNumberOptionChange - size', async () => { - const component = mount( { @@ -54,7 +56,10 @@ export class OptionsTab extends Component { id="updateFiltersOnChange" > } checked={this.props.editorState.params.updateFiltersOnChange} onChange={this.handleUpdateFiltersChange} data-test-subj="inputControlEditorUpdateFiltersOnChangeCheckbox" @@ -65,7 +70,10 @@ export class OptionsTab extends Component { id="useTimeFilter" > } checked={this.props.editorState.params.useTimeFilter} onChange={this.handleUseTimeFilter} data-test-subj="inputControlEditorUseTimeFilterCheckbox" @@ -76,7 +84,10 @@ export class OptionsTab extends Component { id="pinFilters" > } checked={this.props.editorState.params.pinFilters} onChange={this.handlePinFilters} data-test-subj="inputControlEditorPinFiltersCheckbox" diff --git a/src/core_plugins/input_control_vis/public/components/editor/options_tab.test.js b/src/core_plugins/input_control_vis/public/components/editor/options_tab.test.js index b4540c6037477..ba4d43bea133f 100644 --- a/src/core_plugins/input_control_vis/public/components/editor/options_tab.test.js +++ b/src/core_plugins/input_control_vis/public/components/editor/options_tab.test.js @@ -19,7 +19,8 @@ import React from 'react'; import sinon from 'sinon'; -import { mount, shallow } from 'enzyme'; +import { shallow } from 'enzyme'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { OptionsTab, @@ -49,7 +50,7 @@ test('renders OptionsTab', () => { }); test('updateFiltersOnChange', () => { - const component = mount( { }); test('useTimeFilter', () => { - const component = mount( { }); test('pinFilters', () => { - const component = mount(} > } > { }); test('handleNumberOptionChange - step', () => { - const component = mount( { }); test('handleNumberOptionChange - decimalPlaces', () => { - const component = mount( - - Clear form + @@ -97,7 +101,11 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = ` onClick={[Function]} type="button" > - Cancel changes + @@ -119,7 +127,11 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = ` onClick={[Function]} type="button" > - Apply changes + @@ -151,7 +163,7 @@ exports[`Clear btns enabled when there are values 1`] = ` } } > - - Clear form + @@ -224,7 +240,11 @@ exports[`Clear btns enabled when there are values 1`] = ` onClick={[Function]} type="button" > - Cancel changes + @@ -246,7 +266,11 @@ exports[`Clear btns enabled when there are values 1`] = ` onClick={[Function]} type="button" > - Apply changes + @@ -278,7 +302,7 @@ exports[`Renders list control 1`] = ` } } > - - Clear form + @@ -351,7 +379,11 @@ exports[`Renders list control 1`] = ` onClick={[Function]} type="button" > - Cancel changes + @@ -373,7 +405,11 @@ exports[`Renders list control 1`] = ` onClick={[Function]} type="button" > - Apply changes + @@ -405,7 +441,7 @@ exports[`Renders range control 1`] = ` } } > - - Clear form + @@ -478,7 +518,11 @@ exports[`Renders range control 1`] = ` onClick={[Function]} type="button" > - Cancel changes + @@ -500,7 +544,11 @@ exports[`Renders range control 1`] = ` onClick={[Function]} type="button" > - Apply changes + diff --git a/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.js b/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.js index bf96d98efca6d..112ec9c43ae9f 100644 --- a/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.js +++ b/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.js @@ -28,6 +28,8 @@ import { EuiFormRow, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + export class InputControlVis extends Component { constructor(props) { super(props); @@ -104,7 +106,7 @@ export class InputControlVis extends Component { disabled={!this.props.hasValues()} data-test-subj="inputControlClearBtn" > - Clear form + @@ -115,7 +117,7 @@ export class InputControlVis extends Component { disabled={!this.props.hasChanges()} data-test-subj="inputControlCancelBtn" > - Cancel changes + @@ -127,7 +129,7 @@ export class InputControlVis extends Component { disabled={!this.props.hasChanges()} data-test-subj="inputControlSubmitBtn" > - Apply changes + diff --git a/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js b/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js index e72a5cf7f6afb..5feb0e447c0a1 100644 --- a/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js +++ b/src/core_plugins/input_control_vis/public/components/vis/input_control_vis.test.js @@ -19,7 +19,8 @@ import React from 'react'; import sinon from 'sinon'; -import { mount, shallow } from 'enzyme'; +import { shallow } from 'enzyme'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { @@ -129,7 +130,7 @@ test('Clear btns enabled when there are values', () => { }); test('clearControls', () => { - const component = mount( { }); test('submitFilters', () => { - const component = mount( { }); test('resetControls', () => { - const component = mount( ); @@ -81,7 +87,10 @@ export class ListControl extends Component { return ( { }); test('renders ListControl', () => { - const component = shallow( { }); test('disableMsg', () => { - const component = shallow( { return state; }; -export class RangeControl extends Component { +class RangeControlUi extends Component { constructor(props) { super(props); @@ -93,12 +94,18 @@ export class RangeControl extends Component { if ((!isMinValid && isMaxValid) || (isMinValid && !isMaxValid)) { isRangeValid = false; - errorMessage = 'both min and max must be set'; + errorMessage = this.props.intl.formatMessage({ + id: 'inputControl.vis.rangeControl.minMaxValidErrorMessage', + defaultMessage: 'both min and max must be set' + }); } if (isMinValid && isMaxValid && max < min) { isRangeValid = false; - errorMessage = 'max must be greater or equal to min'; + errorMessage = this.props.intl.formatMessage({ + id: 'inputControl.vis.rangeControl.maxValidErrorMessage', + defaultMessage: 'max must be greater or equal to min' + }); } this.setState({ @@ -196,8 +203,10 @@ export class RangeControl extends Component { } } -RangeControl.propTypes = { +RangeControlUi.propTypes = { control: PropTypes.object.isRequired, controlIndex: PropTypes.number.isRequired, stageFilter: PropTypes.func.isRequired }; + +export const RangeControl = injectI18n(RangeControlUi); \ No newline at end of file diff --git a/src/core_plugins/input_control_vis/public/components/vis/range_control.test.js b/src/core_plugins/input_control_vis/public/components/vis/range_control.test.js index 0d12cf496b71f..2697a898d8566 100644 --- a/src/core_plugins/input_control_vis/public/components/vis/range_control.test.js +++ b/src/core_plugins/input_control_vis/public/components/vis/range_control.test.js @@ -18,7 +18,7 @@ */ import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { @@ -43,7 +43,7 @@ const control = { }; test('renders RangeControl', () => { - const component = shallow( {}} @@ -66,7 +66,7 @@ test('disabled', () => { return false; } }; - const component = shallow( {}} @@ -75,7 +75,7 @@ test('disabled', () => { }); describe('min and max input values', () => { - const component = mount( {}} diff --git a/src/core_plugins/input_control_vis/public/control/control.js b/src/core_plugins/input_control_vis/public/control/control.js index 7f1bcfb4e398a..636b9ba699d3e 100644 --- a/src/core_plugins/input_control_vis/public/control/control.js +++ b/src/core_plugins/input_control_vis/public/control/control.js @@ -17,16 +17,24 @@ * under the License. */ +/* eslint-disable no-multi-str*/ + import _ from 'lodash'; +import { i18n } from '@kbn/i18n'; export function noValuesDisableMsg(fieldName, indexPatternName) { - return `Filtering occurs on the "${fieldName}" field, -which doesn't exist on any documents in the "${indexPatternName}" index pattern. -Choose a different field or index documents that contain values for this field.`; + return i18n.translate('inputControl.control.noValuesDisableTootip', { + defaultMessage: 'Filtering occurs on the "{fieldName}" field, which doesn\'t exist on any documents in the "{indexPatternName}" \ +index pattern. Choose a different field or index documents that contain values for this field.', + values: { fieldName: fieldName, indexPatternName: indexPatternName } + }); } export function noIndexPatternMsg(indexPatternId) { - return `Could not locate index-pattern id: ${indexPatternId}.`; + return i18n.translate('inputControl.control.noIndexPatternTootip', { + defaultMessage: 'Could not locate index-pattern id: {indexPatternId}.', + values: { indexPatternId } + }); } export class Control { @@ -43,7 +51,9 @@ export class Control { // restore state from kibana filter context this.reset(); // disable until initialized - this.disable('Control has not been initialized'); + this.disable(i18n.translate('inputControl.control.notInitializedTootip', { + defaultMessage: 'Control has not been initialized' + })); } async fetch() { diff --git a/src/core_plugins/input_control_vis/public/control/list_control_factory.js b/src/core_plugins/input_control_vis/public/control/list_control_factory.js index bb9a05711bfcd..65471f15657bc 100644 --- a/src/core_plugins/input_control_vis/public/control/list_control_factory.js +++ b/src/core_plugins/input_control_vis/public/control/list_control_factory.js @@ -25,6 +25,7 @@ import { } from './control'; import { PhraseFilterManager } from './filter_manager/phrase_filter_manager'; import { createSearchSource } from './create_search_source'; +import { i18n } from '@kbn/i18n'; function getEscapedQuery(query = '') { // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#_standard_operators @@ -75,7 +76,10 @@ class ListControl extends Control { let ancestorFilters; if (this.hasAncestors()) { if (this.hasUnsetAncestor()) { - this.disable(`Disabled until '${this.ancestors[0].label}' is set.`); + this.disable(i18n.translate('inputControl.listControl.disableTootip', { + defaultMessage: 'Disabled until \'{label}\' is set.', + values: { label: this.ancestors[0].label } + })); return; } @@ -113,7 +117,10 @@ class ListControl extends Control { try { resp = await searchSource.fetch(); } catch(error) { - this.disable(`Unable to fetch terms, error: ${error.message}`); + this.disable(i18n.translate('inputControl.listControl.unableToFetchTootip', { + defaultMessage: 'Unable to fetch terms, error: {errorMessage}', + values: { errorMessage: error.message } + })); return; } diff --git a/src/core_plugins/input_control_vis/public/control/range_control_factory.js b/src/core_plugins/input_control_vis/public/control/range_control_factory.js index b7e5e114b949d..4e8cb18a82100 100644 --- a/src/core_plugins/input_control_vis/public/control/range_control_factory.js +++ b/src/core_plugins/input_control_vis/public/control/range_control_factory.js @@ -25,6 +25,7 @@ import { } from './control'; import { RangeFilterManager } from './filter_manager/range_filter_manager'; import { createSearchSource } from './create_search_source'; +import { i18n } from '@kbn/i18n'; const minMaxAgg = (field) => { const aggBody = {}; @@ -64,7 +65,10 @@ class RangeControl extends Control { try { resp = await searchSource.fetch(); } catch(error) { - this.disable(`Unable to fetch range min and max, error: ${error.message}`); + this.disable(i18n.translate('inputControl.rangeControl.unableToFetchTootip', { + defaultMessage: 'Unable to fetch range min and max, error: {errorMessage}', + values: { errorMessage: error.message } + })); return; } diff --git a/src/core_plugins/input_control_vis/public/register_vis.js b/src/core_plugins/input_control_vis/public/register_vis.js index 4bca018e06599..41c659f7896b1 100644 --- a/src/core_plugins/input_control_vis/public/register_vis.js +++ b/src/core_plugins/input_control_vis/public/register_vis.js @@ -27,6 +27,7 @@ import { OptionsTab } from './components/editor/options_tab'; import { defaultFeedbackMessage } from 'ui/vis/default_feedback_message'; import image from './images/icon-input-control.svg'; import { Status } from 'ui/vis/update_status'; +import { i18n } from '@kbn/i18n'; function InputControlVisProvider(Private) { const VisFactory = Private(VisFactoryProvider); @@ -34,9 +35,13 @@ function InputControlVisProvider(Private) { // return the visType object, which kibana will use to display and configure new Vis object of this type. return VisFactory.createBaseVisualization({ name: 'input_control_vis', - title: 'Controls', + title: i18n.translate('inputControl.register.controlsTitle', { + defaultMessage: 'Controls' + }), image, - description: 'Create interactive controls for easy dashboard manipulation.', + description: i18n.translate('inputControl.register.controlsDescription', { + defaultMessage: 'Create interactive controls for easy dashboard manipulation.' + }), category: CATEGORY.OTHER, stage: 'lab', requiresUpdateStatus: [Status.PARAMS, Status.TIME], @@ -55,12 +60,16 @@ function InputControlVisProvider(Private) { optionTabs: [ { name: 'controls', - title: 'Controls', + title: i18n.translate('inputControl.register.tabs.controlsTitle', { + defaultMessage: 'Controls' + }), editor: ControlsTab }, { name: 'options', - title: 'Options', + title: i18n.translate('inputControl.register.tabs.optionsTitle', { + defaultMessage: 'Options' + }), editor: OptionsTab } ] diff --git a/src/core_plugins/input_control_vis/public/vis_controller.js b/src/core_plugins/input_control_vis/public/vis_controller.js index 480fd8bd13f54..3a92c7e10b229 100644 --- a/src/core_plugins/input_control_vis/public/vis_controller.js +++ b/src/core_plugins/input_control_vis/public/vis_controller.js @@ -23,6 +23,8 @@ import { InputControlVis } from './components/vis/input_control_vis'; import { controlFactory } from './control/control_factory'; import { getLineageMap } from './lineage'; +import { I18nProvider } from '@kbn/i18n/react'; + class VisController { constructor(el, vis) { this.el = el; @@ -50,17 +52,19 @@ class VisController { drawVis = () => { render( - , + + + , this.el); } diff --git a/src/ui/public/vis/editors/default/vis_options.js b/src/ui/public/vis/editors/default/vis_options.js index 69d58bbd9274c..cb1e3131cce0c 100644 --- a/src/ui/public/vis/editors/default/vis_options.js +++ b/src/ui/public/vis/editors/default/vis_options.js @@ -22,6 +22,7 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { uiModules } from '../../../modules'; import visOptionsTemplate from './vis_options.html'; +import { I18nProvider } from '@kbn/i18n/react'; /** * This directive sort of "transcludes" in whatever template you pass in via the `editor` attribute. @@ -53,7 +54,10 @@ uiModules }; const renderReactComponent = () => { const Component = $scope.editor; - render(, $el[0]); + render( + + + , $el[0]); }; // Bind the `editor` template with the scope. if (reactOptionsComponent) {