Translate Input Control component (#22400)

* Translate Input Control component

* Remove export from variables that get wrapped by a helper

* Refactoring

* Update message ids

* Fix unit tests
This commit is contained in:
Maryia Lapata 2018-09-10 20:35:03 +03:00 committed by GitHub
parent cd5eb594a3
commit 1a2e8970b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 388 additions and 142 deletions

View file

@ -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"

View file

@ -2,7 +2,7 @@
exports[`renders ControlsTab 1`] = `
<div>
<ControlEditor
<InjectIntl(ControlEditorUi)
controlIndex={0}
controlParams={
Object {
@ -39,7 +39,7 @@ exports[`renders ControlsTab 1`] = `
]
}
/>
<ControlEditor
<InjectIntl(ControlEditorUi)
controlIndex={1}
controlParams={
Object {
@ -140,7 +140,11 @@ exports[`renders ControlsTab 1`] = `
onClick={[Function]}
type="button"
>
Add
<FormattedMessage
defaultMessage="Add"
id="inputControl.editor.controlsTab.addButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>

View file

@ -2,14 +2,14 @@
exports[`renders dynamic options should display disabled dynamic options with tooltip for non-string fields 1`] = `
<div>
<IndexPatternSelect
<InjectIntl(IndexPatternSelectUi)
controlIndex={0}
getIndexPattern={[Function]}
getIndexPatterns={[Function]}
indexPatternId="mockIndexPattern"
onChange={[Function]}
/>
<FieldSelect
<InjectIntl(FieldSelectUi)
controlIndex={0}
fieldName="numberField"
filterField={[Function]}
@ -72,14 +72,14 @@ exports[`renders dynamic options should display disabled dynamic options with to
exports[`renders dynamic options should display dynamic options for string fields 1`] = `
<div>
<IndexPatternSelect
<InjectIntl(IndexPatternSelectUi)
controlIndex={0}
getIndexPattern={[Function]}
getIndexPatterns={[Function]}
indexPatternId="mockIndexPattern"
onChange={[Function]}
/>
<FieldSelect
<InjectIntl(FieldSelectUi)
controlIndex={0}
fieldName="keywordField"
filterField={[Function]}
@ -123,14 +123,14 @@ exports[`renders dynamic options should display dynamic options for string field
exports[`renders dynamic options should display size field when dynamic options is disabled 1`] = `
<div>
<IndexPatternSelect
<InjectIntl(IndexPatternSelectUi)
controlIndex={0}
getIndexPattern={[Function]}
getIndexPatterns={[Function]}
indexPatternId="mockIndexPattern"
onChange={[Function]}
/>
<FieldSelect
<InjectIntl(FieldSelectUi)
controlIndex={0}
fieldName="keywordField"
filterField={[Function]}
@ -193,14 +193,14 @@ exports[`renders dynamic options should display size field when dynamic options
exports[`renders should display chaining input when parents are provided 1`] = `
<div>
<IndexPatternSelect
<InjectIntl(IndexPatternSelectUi)
controlIndex={0}
getIndexPattern={[Function]}
getIndexPatterns={[Function]}
indexPatternId="indexPattern1"
onChange={[Function]}
/>
<FieldSelect
<InjectIntl(FieldSelectUi)
controlIndex={0}
fieldName="keywordField"
filterField={[Function]}
@ -296,14 +296,14 @@ exports[`renders should display chaining input when parents are provided 1`] = `
exports[`renders should not display any options until field is selected 1`] = `
<div>
<IndexPatternSelect
<InjectIntl(IndexPatternSelectUi)
controlIndex={0}
getIndexPattern={[Function]}
getIndexPatterns={[Function]}
indexPatternId="mockIndexPattern"
onChange={[Function]}
/>
<FieldSelect
<InjectIntl(FieldSelectUi)
controlIndex={0}
fieldName=""
filterField={[Function]}

View file

@ -11,7 +11,13 @@ exports[`renders OptionsTab 1`] = `
<EuiSwitch
checked={false}
data-test-subj="inputControlEditorUpdateFiltersOnChangeCheckbox"
label="Update Kibana filters on each change"
label={
<FormattedMessage
defaultMessage="Update Kibana filters on each change"
id="inputControl.editor.optionsTab.updateFilterLabel"
values={Object {}}
/>
}
onChange={[Function]}
/>
</EuiFormRow>
@ -24,7 +30,13 @@ exports[`renders OptionsTab 1`] = `
<EuiSwitch
checked={false}
data-test-subj="inputControlEditorUseTimeFilterCheckbox"
label="Use time filter"
label={
<FormattedMessage
defaultMessage="Use time filter"
id="inputControl.editor.optionsTab.useTimeFielterLabel"
values={Object {}}
/>
}
onChange={[Function]}
/>
</EuiFormRow>
@ -36,7 +48,13 @@ exports[`renders OptionsTab 1`] = `
>
<EuiSwitch
data-test-subj="inputControlEditorPinFiltersCheckbox"
label="Pin filters to global state"
label={
<FormattedMessage
defaultMessage="Pin filters to global state"
id="inputControl.editor.optionsTab.pinFieltersLabel"
values={Object {}}
/>
}
onChange={[Function]}
/>
</EuiFormRow>

View file

@ -2,14 +2,14 @@
exports[`renders RangeControlEditor 1`] = `
<div>
<IndexPatternSelect
<InjectIntl(IndexPatternSelectUi)
controlIndex={0}
getIndexPattern={[Function]}
getIndexPatterns={[Function]}
indexPatternId="indexPattern1"
onChange={[Function]}
/>
<FieldSelect
<InjectIntl(FieldSelectUi)
controlIndex={0}
fieldName="numberField"
filterField={[Function]}
@ -22,7 +22,13 @@ exports[`renders RangeControlEditor 1`] = `
fullWidth={false}
hasEmptyLabelSpace={false}
id="stepSize-0"
label="Step Size"
label={
<FormattedMessage
defaultMessage="Step Size"
id="inputControl.editor.rangeControl.stepSizeLabel"
values={Object {}}
/>
}
>
<EuiFieldNumber
compressed={false}
@ -38,7 +44,13 @@ exports[`renders RangeControlEditor 1`] = `
fullWidth={false}
hasEmptyLabelSpace={false}
id="decimalPlaces-0"
label="Decimal Places"
label={
<FormattedMessage
defaultMessage="Decimal Places"
id="inputControl.editor.rangeControl.decimalPlacesLabel"
values={Object {}}
/>
}
>
<EuiFieldNumber
compressed={false}

View file

@ -23,6 +23,7 @@ import React, { Component } from 'react';
import { RangeControlEditor } from './range_control_editor';
import { ListControlEditor } from './list_control_editor';
import { getTitle } from '../../editor_utils';
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
import {
EuiAccordion,
@ -34,7 +35,7 @@ import {
EuiSpacer,
} from '@elastic/eui';
export class ControlEditor extends Component {
class ControlEditorUi extends Component {
changeLabel = (evt) => {
this.props.handleLabelChange(this.props.controlIndex, evt);
@ -101,7 +102,7 @@ export class ControlEditor extends Component {
<EuiForm>
<EuiFormRow
id={labelId}
label="Control Label"
label={<FormattedMessage id="inputControl.editor.controlEditor.controlLabel" defaultMessage="Control Label"/>}
>
<EuiFieldText
value={this.props.controlParams.label}
@ -118,21 +119,30 @@ export class ControlEditor extends Component {
return (
<div>
<EuiButtonIcon
aria-label="Move control up"
aria-label={this.props.intl.formatMessage({
id: 'inputControl.editor.controlEditor.moveControlUpAriaLabel',
defaultMessage: 'Move control up'
})}
color="primary"
onClick={this.moveUpControl}
iconType="sortUp"
data-test-subj={`inputControlEditorMoveUpControl${this.props.controlIndex}`}
/>
<EuiButtonIcon
aria-label="Move control down"
aria-label={this.props.intl.formatMessage({
id: 'inputControl.editor.controlEditor.moveControlDownAriaLabel',
defaultMessage: 'Move control down'
})}
color="primary"
onClick={this.moveDownControl}
iconType="sortDown"
data-test-subj={`inputControlEditorMoveDownControl${this.props.controlIndex}`}
/>
<EuiButtonIcon
aria-label="Remove control"
aria-label={this.props.intl.formatMessage({
id: 'inputControl.editor.controlEditor.removeControlAriaLabel',
defaultMessage: 'Remove control'
})}
color="danger"
onClick={this.removeControl}
iconType="cross"
@ -161,7 +171,7 @@ export class ControlEditor extends Component {
}
}
ControlEditor.propTypes = {
ControlEditorUi.propTypes = {
controlIndex: PropTypes.number.isRequired,
controlParams: PropTypes.object.isRequired,
handleLabelChange: PropTypes.func.isRequired,
@ -179,3 +189,5 @@ ControlEditor.propTypes = {
})).isRequired,
handleParentChange: PropTypes.func.isRequired,
};
export const ControlEditor = injectI18n(ControlEditorUi);

View file

@ -23,6 +23,7 @@ import React, { Component } from 'react';
import { ControlEditor } from './control_editor';
import { addControl, moveControl, newControl, removeControl, setControl } from '../../editor_utils';
import { getLineageMap, getParentCandidates } from '../../lineage';
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
import {
EuiButton,
@ -33,7 +34,7 @@ import {
EuiSelect,
} from '@elastic/eui';
export class ControlsTab extends Component {
class ControlsTabUi extends Component {
state = {
type: 'list'
@ -138,6 +139,8 @@ export class ControlsTab extends Component {
}
render() {
const { intl } = this.props;
return (
<div>
@ -151,12 +154,21 @@ export class ControlsTab extends Component {
>
<EuiSelect
options={[
{ value: 'range', text: 'Range slider' },
{ value: 'list', text: 'Options list' },
{ value: 'range', text: intl.formatMessage({
id: 'inputControl.editor.controlsTab.select.rangeDropDownOptionLabel',
defaultMessage: 'Range slider' })
},
{ value: 'list', text: intl.formatMessage({
id: 'inputControl.editor.controlsTab.select.listDropDownOptionLabel',
defaultMessage: 'Options list' })
},
]}
value={this.state.type}
onChange={evt => 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'
})}
/>
</EuiFormRow>
</EuiFlexItem>
@ -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
<FormattedMessage id="inputControl.editor.controlsTab.addButtonLabel" defaultMessage="Add"/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -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);

View file

@ -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(<ControlsTab
const component = shallowWithIntl(<ControlsTab.WrappedComponent
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}
@ -98,7 +98,7 @@ test('renders ControlsTab', () => {
describe('behavior', () => {
test('add control button', () => {
const component = mount(<ControlsTab
const component = mountWithIntl(<ControlsTab.WrappedComponent
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}
@ -114,7 +114,7 @@ describe('behavior', () => {
});
test('remove control button', () => {
const component = mount(<ControlsTab
const component = mountWithIntl(<ControlsTab.WrappedComponent
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}
@ -139,7 +139,7 @@ describe('behavior', () => {
test('move down control button', () => {
const component = mount(<ControlsTab
const component = mountWithIntl(<ControlsTab.WrappedComponent
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}
@ -176,7 +176,7 @@ describe('behavior', () => {
});
test('move up control button', () => {
const component = mount(<ControlsTab
const component = mountWithIntl(<ControlsTab.WrappedComponent
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}

View file

@ -20,13 +20,14 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { injectI18n } from '@kbn/i18n/react';
import {
EuiFormRow,
EuiComboBox,
} from '@elastic/eui';
export class FieldSelect extends Component {
class FieldSelectUi extends Component {
constructor(props) {
super(props);
@ -132,6 +133,7 @@ export class FieldSelect extends Component {
const selectId = `fieldSelect-${this.props.controlIndex}`;
const selectedOptions = [];
const { intl } = this.props;
if (this.props.fieldName) {
selectedOptions.push({ value: this.props.fieldName, label: this.props.fieldName });
}
@ -139,10 +141,16 @@ export class FieldSelect extends Component {
return (
<EuiFormRow
id={selectId}
label="Field"
label={intl.formatMessage({
id: 'inputControl.editor.fieldSelect.fieldLabel',
defaultMessage: 'Field'
})}
>
<EuiComboBox
placeholder="Select field..."
placeholder={intl.formatMessage({
id: 'inputControl.editor.fieldSelect.selectFieldPlaceholder',
defaultMessage: 'Select field...'
})}
singleSelection={true}
isLoading={this.state.isLoading}
options={this.state.fields}
@ -155,7 +163,7 @@ export class FieldSelect extends Component {
}
}
FieldSelect.propTypes = {
FieldSelectUi.propTypes = {
getIndexPattern: PropTypes.func.isRequired,
indexPatternId: PropTypes.string,
onChange: PropTypes.func.isRequired,
@ -163,3 +171,5 @@ FieldSelect.propTypes = {
filterField: PropTypes.func,
controlIndex: PropTypes.number.isRequired,
};
export const FieldSelect = injectI18n(FieldSelectUi);

View file

@ -20,13 +20,14 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { injectI18n } from '@kbn/i18n/react';
import {
EuiFormRow,
EuiComboBox,
} from '@elastic/eui';
export class IndexPatternSelect extends Component {
class IndexPatternSelectUi extends Component {
constructor(props) {
super(props);
@ -122,16 +123,24 @@ export class IndexPatternSelect extends Component {
render() {
const selectId = `indexPatternSelect-${this.props.controlIndex}`;
const selectedOptions = [];
const { intl } = this.props;
if (this.state.selectedIndexPattern) {
selectedOptions.push(this.state.selectedIndexPattern);
}
return (
<EuiFormRow
id={selectId}
label="Index Pattern"
label={intl.formatMessage({
id: 'inputControl.editor.indexPatternSelect.patternLabel',
defaultMessage: 'Index Pattern'
})}
>
<EuiComboBox
placeholder="Select index pattern..."
placeholder={intl.formatMessage({
id: 'inputControl.editor.indexPatternSelect.patternPlaceholder',
defaultMessage: 'Select index pattern...'
})}
singleSelection={true}
isLoading={this.state.isLoading}
onSearchChange={this.fetchOptions}
@ -145,10 +154,12 @@ export class IndexPatternSelect extends Component {
}
}
IndexPatternSelect.propTypes = {
IndexPatternSelectUi.propTypes = {
getIndexPatterns: PropTypes.func.isRequired,
getIndexPattern: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
indexPatternId: PropTypes.string,
controlIndex: PropTypes.number.isRequired,
};
export const IndexPatternSelect = injectI18n(IndexPatternSelectUi);

View file

@ -21,6 +21,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { IndexPatternSelect } from './index_pattern_select';
import { FieldSelect } from './field_select';
import { injectI18n } from '@kbn/i18n/react';
import {
EuiFormRow,
@ -33,7 +34,7 @@ function filterField(field) {
return field.aggregatable && ['number', 'boolean', 'date', 'ip', 'string'].includes(field.type);
}
export class ListControlEditor extends Component {
class ListControlEditorUi extends Component {
state = {
isLoadingFieldType: true,
isStringField: false,
@ -102,6 +103,7 @@ export class ListControlEditor extends Component {
}
const options = [];
const { intl } = this.props;
if (this.props.parentCandidates && this.props.parentCandidates.length > 0) {
const parentCandidatesOptions = [
{ value: '', text: '' },
@ -110,8 +112,14 @@ export class ListControlEditor extends Component {
options.push(
<EuiFormRow
id={`parentSelect-${this.props.controlIndex}`}
label="Parent control"
helpText="Options are based on the value of parent control. Disabled if parent is not set."
label={intl.formatMessage({
id: 'inputControl.editor.listControl.parentLabel',
defaultMessage: 'Parent control'
})}
helpText={intl.formatMessage({
id: 'inputControl.editor.listControl.parentDescription',
defaultMessage: 'Options are based on the value of parent control. Disabled if parent is not set.'
})}
key="parentSelect"
>
<EuiSelect
@ -129,10 +137,16 @@ export class ListControlEditor extends Component {
<EuiFormRow
id={`multiselect-${this.props.controlIndex}`}
key="multiselect"
helpText="Allow multiple selection"
helpText={intl.formatMessage({
id: 'inputControl.editor.listControl.multiselectDescription',
defaultMessage: 'Allow multiple selection'
})}
>
<EuiSwitch
label="Multiselect"
label={intl.formatMessage({
id: 'inputControl.editor.listControl.multiselectLabel',
defaultMessage: 'Multiselect'
})}
checked={this.props.controlParams.options.multiselect}
onChange={(evt) => {
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(
<EuiFormRow
id={`dynamicOptions-${this.props.controlIndex}`}
@ -152,7 +172,10 @@ export class ListControlEditor extends Component {
helpText={dynamicOptionsHelpText}
>
<EuiSwitch
label="Dynamic Options"
label={intl.formatMessage({
id: 'inputControl.editor.listControl.dynamicOptionsLabel',
defaultMessage: 'Dynamic Options'
})}
checked={this.props.controlParams.options.dynamicOptions}
onChange={(evt) => {
this.props.handleCheckboxOptionChange(this.props.controlIndex, 'dynamicOptions', evt);
@ -168,9 +191,15 @@ export class ListControlEditor extends Component {
options.push(
<EuiFormRow
id={`size-${this.props.controlIndex}`}
label="Size"
label={intl.formatMessage({
id: 'inputControl.editor.listControl.sizeLabel',
defaultMessage: 'Size'
})}
key="size"
helpText="Number of options"
helpText={intl.formatMessage({
id: 'inputControl.editor.listControl.sizeDescription',
defaultMessage: 'Number of options'
})}
>
<EuiFieldNumber
min={1}
@ -215,7 +244,7 @@ export class ListControlEditor extends Component {
}
}
ListControlEditor.propTypes = {
ListControlEditorUi.propTypes = {
getIndexPatterns: PropTypes.func.isRequired,
getIndexPattern: PropTypes.func.isRequired,
controlIndex: PropTypes.number.isRequired,
@ -230,3 +259,5 @@ ListControlEditor.propTypes = {
})).isRequired,
handleParentChange: PropTypes.func.isRequired,
};
export const ListControlEditor = injectI18n(ListControlEditorUi);

View file

@ -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 { getIndexPatternsMock } from './__tests__/get_index_patterns_mock';
@ -67,7 +67,7 @@ describe('renders', () => {
size: 5,
}
};
const component = shallow(<ListControlEditor
const component = shallowWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -93,7 +93,7 @@ describe('renders', () => {
{ value: '1', text: 'fieldA' },
{ value: '2', text: 'fieldB' }
];
const component = shallow(<ListControlEditor
const component = shallowWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -128,7 +128,7 @@ describe('renders', () => {
size: 5,
}
};
const component = shallow(<ListControlEditor
const component = shallowWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -162,7 +162,7 @@ describe('renders', () => {
size: 5,
}
};
const component = shallow(<ListControlEditor
const component = shallowWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -196,7 +196,7 @@ describe('renders', () => {
size: 5,
}
};
const component = shallow(<ListControlEditor
const component = shallowWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -220,7 +220,7 @@ describe('renders', () => {
});
test('handleCheckboxOptionChange - multiselect', async () => {
const component = mount(<ListControlEditor
const component = mountWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -258,7 +258,7 @@ test('handleCheckboxOptionChange - multiselect', async () => {
});
test('handleNumberOptionChange - size', async () => {
const component = mount(<ListControlEditor
const component = mountWithIntl(<ListControlEditor.WrappedComponent
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}

View file

@ -27,6 +27,8 @@ import {
EuiSwitch,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export class OptionsTab extends Component {
setVisParam = (paramName, paramValue) => {
@ -54,7 +56,10 @@ export class OptionsTab extends Component {
id="updateFiltersOnChange"
>
<EuiSwitch
label="Update Kibana filters on each change"
label={<FormattedMessage
id="inputControl.editor.optionsTab.updateFilterLabel"
defaultMessage="Update Kibana filters on each change"
/>}
checked={this.props.editorState.params.updateFiltersOnChange}
onChange={this.handleUpdateFiltersChange}
data-test-subj="inputControlEditorUpdateFiltersOnChangeCheckbox"
@ -65,7 +70,10 @@ export class OptionsTab extends Component {
id="useTimeFilter"
>
<EuiSwitch
label="Use time filter"
label={<FormattedMessage
id="inputControl.editor.optionsTab.useTimeFielterLabel"
defaultMessage="Use time filter"
/>}
checked={this.props.editorState.params.useTimeFilter}
onChange={this.handleUseTimeFilter}
data-test-subj="inputControlEditorUseTimeFilterCheckbox"
@ -76,7 +84,10 @@ export class OptionsTab extends Component {
id="pinFilters"
>
<EuiSwitch
label="Pin filters to global state"
label={<FormattedMessage
id="inputControl.editor.optionsTab.pinFieltersLabel"
defaultMessage="Pin filters to global state"
/>}
checked={this.props.editorState.params.pinFilters}
onChange={this.handlePinFilters}
data-test-subj="inputControlEditorPinFiltersCheckbox"

View file

@ -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(<OptionsTab
const component = mountWithIntl(<OptionsTab
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}
@ -64,7 +65,7 @@ test('updateFiltersOnChange', () => {
});
test('useTimeFilter', () => {
const component = mount(<OptionsTab
const component = mountWithIntl(<OptionsTab
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}
@ -79,7 +80,7 @@ test('useTimeFilter', () => {
});
test('pinFilters', () => {
const component = mount(<OptionsTab
const component = mountWithIntl(<OptionsTab
scope={scopeMock}
editorState={scopeMock.editorState}
stageEditorParams={stageEditorParams}

View file

@ -27,6 +27,8 @@ import {
EuiFieldNumber,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
function filterField(field) {
return field.type === 'number';
}
@ -62,7 +64,10 @@ export function RangeControlEditor(props) {
<EuiFormRow
id={stepSizeId}
label="Step Size"
label={<FormattedMessage
id="inputControl.editor.rangeControl.stepSizeLabel"
defaultMessage="Step Size"
/>}
>
<EuiFieldNumber
value={props.controlParams.options.step}
@ -73,7 +78,10 @@ export function RangeControlEditor(props) {
<EuiFormRow
id={decimalPlacesId}
label="Decimal Places"
label={<FormattedMessage
id="inputControl.editor.rangeControl.decimalPlacesLabel"
defaultMessage="Decimal Places"
/>}
>
<EuiFieldNumber
min={0}

View file

@ -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 { getIndexPatternMock } from './__tests__/get_index_pattern_mock';
import { getIndexPatternsMock } from './__tests__/get_index_patterns_mock';
@ -63,7 +64,7 @@ test('renders RangeControlEditor', () => {
});
test('handleNumberOptionChange - step', () => {
const component = mount(<RangeControlEditor
const component = mountWithIntl(<RangeControlEditor
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}
@ -90,7 +91,7 @@ test('handleNumberOptionChange - step', () => {
});
test('handleNumberOptionChange - decimalPlaces', () => {
const component = mount(<RangeControlEditor
const component = mountWithIntl(<RangeControlEditor
getIndexPatterns={getIndexPatternsMock}
getIndexPattern={getIndexPatternMock}
controlIndex={0}

View file

@ -24,7 +24,7 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = `
}
}
>
<ListControl
<InjectIntl(ListControlUi)
controlIndex={0}
disableMsg={null}
fetchOptions={[Function]}
@ -75,7 +75,11 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = `
onClick={[Function]}
type="button"
>
Clear form
<FormattedMessage
defaultMessage="Clear form"
id="inputControl.vis.inputControlVis.clearFormButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -97,7 +101,11 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = `
onClick={[Function]}
type="button"
>
Cancel changes
<FormattedMessage
defaultMessage="Cancel changes"
id="inputControl.vis.inputControlVis.cancelChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -119,7 +127,11 @@ exports[`Apply and Cancel change btns enabled when there are changes 1`] = `
onClick={[Function]}
type="button"
>
Apply changes
<FormattedMessage
defaultMessage="Apply changes"
id="inputControl.vis.inputControlVis.applyChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -151,7 +163,7 @@ exports[`Clear btns enabled when there are values 1`] = `
}
}
>
<ListControl
<InjectIntl(ListControlUi)
controlIndex={0}
disableMsg={null}
fetchOptions={[Function]}
@ -202,7 +214,11 @@ exports[`Clear btns enabled when there are values 1`] = `
onClick={[Function]}
type="button"
>
Clear form
<FormattedMessage
defaultMessage="Clear form"
id="inputControl.vis.inputControlVis.clearFormButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -224,7 +240,11 @@ exports[`Clear btns enabled when there are values 1`] = `
onClick={[Function]}
type="button"
>
Cancel changes
<FormattedMessage
defaultMessage="Cancel changes"
id="inputControl.vis.inputControlVis.cancelChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -246,7 +266,11 @@ exports[`Clear btns enabled when there are values 1`] = `
onClick={[Function]}
type="button"
>
Apply changes
<FormattedMessage
defaultMessage="Apply changes"
id="inputControl.vis.inputControlVis.applyChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -278,7 +302,7 @@ exports[`Renders list control 1`] = `
}
}
>
<ListControl
<InjectIntl(ListControlUi)
controlIndex={0}
disableMsg={null}
fetchOptions={[Function]}
@ -329,7 +353,11 @@ exports[`Renders list control 1`] = `
onClick={[Function]}
type="button"
>
Clear form
<FormattedMessage
defaultMessage="Clear form"
id="inputControl.vis.inputControlVis.clearFormButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -351,7 +379,11 @@ exports[`Renders list control 1`] = `
onClick={[Function]}
type="button"
>
Cancel changes
<FormattedMessage
defaultMessage="Cancel changes"
id="inputControl.vis.inputControlVis.cancelChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -373,7 +405,11 @@ exports[`Renders list control 1`] = `
onClick={[Function]}
type="button"
>
Apply changes
<FormattedMessage
defaultMessage="Apply changes"
id="inputControl.vis.inputControlVis.applyChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -405,7 +441,7 @@ exports[`Renders range control 1`] = `
}
}
>
<RangeControl
<InjectIntl(RangeControlUi)
control={
Object {
"id": "mock-range-control",
@ -456,7 +492,11 @@ exports[`Renders range control 1`] = `
onClick={[Function]}
type="button"
>
Clear form
<FormattedMessage
defaultMessage="Clear form"
id="inputControl.vis.inputControlVis.clearFormButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -478,7 +518,11 @@ exports[`Renders range control 1`] = `
onClick={[Function]}
type="button"
>
Cancel changes
<FormattedMessage
defaultMessage="Cancel changes"
id="inputControl.vis.inputControlVis.cancelChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -500,7 +544,11 @@ exports[`Renders range control 1`] = `
onClick={[Function]}
type="button"
>
Apply changes
<FormattedMessage
defaultMessage="Apply changes"
id="inputControl.vis.inputControlVis.applyChangesButtonLabel"
values={Object {}}
/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>

View file

@ -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
<FormattedMessage id="inputControl.vis.inputControlVis.clearFormButtonLabel" defaultMessage="Clear form"/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -115,7 +117,7 @@ export class InputControlVis extends Component {
disabled={!this.props.hasChanges()}
data-test-subj="inputControlCancelBtn"
>
Cancel changes
<FormattedMessage id="inputControl.vis.inputControlVis.cancelChangesButtonLabel" defaultMessage="Cancel changes"/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
@ -127,7 +129,7 @@ export class InputControlVis extends Component {
disabled={!this.props.hasChanges()}
data-test-subj="inputControlSubmitBtn"
>
Apply changes
<FormattedMessage id="inputControl.vis.inputControlVis.applyChangesButtonLabel" defaultMessage="Apply changes"/>
</EuiButton>
</EuiFormRow>
</EuiFlexItem>

View file

@ -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(<InputControlVis
const component = mountWithIntl(<InputControlVis
stageFilter={stageFilter}
submitFilters={submitFilters}
resetControls={resetControls}
@ -148,7 +149,7 @@ test('clearControls', () => {
});
test('submitFilters', () => {
const component = mount(<InputControlVis
const component = mountWithIntl(<InputControlVis
stageFilter={stageFilter}
submitFilters={submitFilters}
resetControls={resetControls}
@ -167,7 +168,7 @@ test('submitFilters', () => {
});
test('resetControls', () => {
const component = mount(<InputControlVis
const component = mountWithIntl(<InputControlVis
stageFilter={stageFilter}
submitFilters={submitFilters}
resetControls={resetControls}

View file

@ -21,13 +21,14 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import _ from 'lodash';
import { FormRow } from './form_row';
import { injectI18n } from '@kbn/i18n/react';
import {
EuiFieldText,
EuiComboBox,
} from '@elastic/eui';
export class ListControl extends Component {
class ListControlUi extends Component {
state = {
isLoading: false
@ -62,10 +63,15 @@ export class ListControl extends Component {
}
renderControl() {
const { intl } = this.props;
if (this.props.disableMsg) {
return (
<EuiFieldText
placeholder="Select..."
placeholder={intl.formatMessage({
id: 'inputControl.vis.listControl.selectTextPlaceholder',
defaultMessage: 'Select...'
})}
disabled={true}
/>
);
@ -81,7 +87,10 @@ export class ListControl extends Component {
return (
<EuiComboBox
placeholder="Select..."
placeholder={intl.formatMessage({
id: 'inputControl.vis.listControl.selectPlaceholder',
defaultMessage: 'Select...'
})}
options={options}
isLoading={this.state.isLoading}
async={this.props.dynamicOptions}
@ -113,7 +122,7 @@ const comboBoxOptionShape = PropTypes.shape({
value: PropTypes.string.isRequired,
});
ListControl.propTypes = {
ListControlUi.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
selectedOptions: PropTypes.arrayOf(comboBoxOptionShape).isRequired,
@ -126,12 +135,14 @@ ListControl.propTypes = {
fetchOptions: PropTypes.func,
};
ListControl.defaultProps = {
ListControlUi.defaultProps = {
dynamicOptions: false,
multiselect: true,
};
ListControl.defaultProps = {
ListControlUi.defaultProps = {
selectedOptions: [],
options: [],
};
export const ListControl = injectI18n(ListControlUi);

View file

@ -19,7 +19,7 @@
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import {
ListControl,
@ -37,7 +37,7 @@ beforeEach(() => {
});
test('renders ListControl', () => {
const component = shallow(<ListControl
const component = shallowWithIntl(<ListControl.WrappedComponent
id="mock-list-control"
label="list control"
options={options}
@ -50,7 +50,7 @@ test('renders ListControl', () => {
});
test('disableMsg', () => {
const component = shallow(<ListControl
const component = shallowWithIntl(<ListControl.WrappedComponent
id="mock-list-control"
label="list control"
multiselect={true}

View file

@ -22,6 +22,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import InputRange from 'react-input-range';
import { FormRow } from './form_row';
import { injectI18n } from '@kbn/i18n/react';
import {
EuiFormRow,
@ -49,7 +50,7 @@ const toState = ({ control }) => {
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);

View file

@ -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(<RangeControl
const component = shallowWithIntl(<RangeControl.WrappedComponent
control={control}
controlIndex={0}
stageFilter={() => {}}
@ -66,7 +66,7 @@ test('disabled', () => {
return false;
}
};
const component = shallow(<RangeControl
const component = shallowWithIntl(<RangeControl.WrappedComponent
control={disabledRangeControl}
controlIndex={0}
stageFilter={() => {}}
@ -75,7 +75,7 @@ test('disabled', () => {
});
describe('min and max input values', () => {
const component = mount(<RangeControl
const component = mountWithIntl(<RangeControl.WrappedComponent
control={control}
controlIndex={0}
stageFilter={() => {}}

View file

@ -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() {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
}
]

View file

@ -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(
<InputControlVis
updateFiltersOnChange={this.vis.params.updateFiltersOnChange}
controls={this.controls}
stageFilter={this.stageFilter}
submitFilters={this.submitFilters}
resetControls={this.updateControlsFromKbn}
clearControls={this.clearControls}
hasChanges={this.hasChanges}
hasValues={this.hasValues}
refreshControl={this.refreshControl}
/>,
<I18nProvider>
<InputControlVis
updateFiltersOnChange={this.vis.params.updateFiltersOnChange}
controls={this.controls}
stageFilter={this.stageFilter}
submitFilters={this.submitFilters}
resetControls={this.updateControlsFromKbn}
clearControls={this.clearControls}
hasChanges={this.hasChanges}
hasValues={this.hasValues}
refreshControl={this.refreshControl}
/>
</I18nProvider>,
this.el);
}

View file

@ -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(<Component scope={$scope} editorState={$scope.editorState} stageEditorParams={stageEditorParams} />, $el[0]);
render(
<I18nProvider>
<Component scope={$scope} editorState={$scope.editorState} stageEditorParams={stageEditorParams} />
</I18nProvider>, $el[0]);
};
// Bind the `editor` template with the scope.
if (reactOptionsComponent) {