From 680e58f1154dec757fd9550d88232181358626b5 Mon Sep 17 00:00:00 2001 From: maryia-lapata Date: Mon, 11 Mar 2019 16:06:40 +0300 Subject: [PATCH 1/3] EUIficate raw json control --- .../buckets/date_histogram/_editor.js | 6 ++ .../public/agg_types/controls/raw_json.html | 24 +----- .../ui/public/agg_types/controls/raw_json.tsx | 74 +++++++++++++++++++ .../agg_types/controls/raw_json_select.js | 30 ++++++++ src/legacy/ui/public/agg_types/index.js | 1 + src/legacy/ui/public/agg_types/utils.tsx | 39 ++++++++++ .../editors/default/__tests__/agg_params.js | 7 ++ .../public/vis/editors/default/agg_params.js | 17 ++++- 8 files changed, 174 insertions(+), 24 deletions(-) create mode 100644 src/legacy/ui/public/agg_types/controls/raw_json.tsx create mode 100644 src/legacy/ui/public/agg_types/controls/raw_json_select.js create mode 100644 src/legacy/ui/public/agg_types/utils.tsx diff --git a/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js b/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js index afacdc522562e..20dea556306ee 100644 --- a/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js +++ b/src/legacy/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js @@ -21,6 +21,7 @@ import _ from 'lodash'; import $ from 'jquery'; import ngMock from 'ng_mock'; import expect from 'expect.js'; +import sinon from 'sinon'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { VisProvider } from '../../../../vis'; import { intervalOptions } from '../../../buckets/_interval_options'; @@ -58,6 +59,10 @@ describe('editor', function () { ] }); + const formCtrl = { + $addControl: sinon.fake() + }; + const $el = $('' + @@ -68,6 +73,7 @@ describe('editor', function () { $parentScope.groupName = 'buckets'; $parentScope.vis = vis; + $el.data('$formController', formCtrl); $compile($el)($parentScope); $scope = $el.scope(); $scope.$digest(); diff --git a/src/legacy/ui/public/agg_types/controls/raw_json.html b/src/legacy/ui/public/agg_types/controls/raw_json.html index f7d3f78a1f663..04294d6f9e3a5 100644 --- a/src/legacy/ui/public/agg_types/controls/raw_json.html +++ b/src/legacy/ui/public/agg_types/controls/raw_json.html @@ -1,23 +1 @@ -
- - - -

- -

-
+ diff --git a/src/legacy/ui/public/agg_types/controls/raw_json.tsx b/src/legacy/ui/public/agg_types/controls/raw_json.tsx new file mode 100644 index 0000000000000..e722f2e76cff1 --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/raw_json.tsx @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { EuiFormRow, EuiIcon, EuiTextArea, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { get } from 'lodash'; +import React, { useState } from 'react'; +import { AggConfig } from 'ui/vis/agg_config'; +import { isValidJson } from '../utils'; + +interface RawJSONSelectProps { + agg: AggConfig; + onParamsChange: ( + type: string, + agg: AggConfig, + field: any, + options?: { isValid?: boolean } + ) => void; +} + +function RawJSONSelect({ agg = {}, onParamsChange }: RawJSONSelectProps) { + const [isInvalid, setIsInvalid] = useState(false); + + const label = ( + <> + {' '} + + + + + ); + const onTextAreaChange = (e: React.ChangeEvent) => { + const value: string = get(e, 'target.value'); + const isValid = isValidJson(value); + setIsInvalid(!isValid); + onParamsChange('json', agg, value, { isValid }); + }; + + return ( + + + + ); +} + +export { RawJSONSelect }; diff --git a/src/legacy/ui/public/agg_types/controls/raw_json_select.js b/src/legacy/ui/public/agg_types/controls/raw_json_select.js new file mode 100644 index 0000000000000..f61576b59c3a4 --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/raw_json_select.js @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import 'ngreact'; +import { uiModules } from '../../modules'; +import { RawJSONSelect } from './raw_json'; +import { wrapInI18nContext } from 'ui/i18n'; + +uiModules + .get('app/kibana', ['react']) + .directive('rawJsonSelect', reactDirective => reactDirective(wrapInI18nContext(RawJSONSelect), [ + 'onParamsChange', + ['agg', { watchDepth: 'collection' }] + ])); diff --git a/src/legacy/ui/public/agg_types/index.js b/src/legacy/ui/public/agg_types/index.js index 93ca63ccde786..e1cec60d47db9 100644 --- a/src/legacy/ui/public/agg_types/index.js +++ b/src/legacy/ui/public/agg_types/index.js @@ -19,6 +19,7 @@ import '../directives/validate_agg'; import './agg_params'; +import '../agg_types/controls/raw_json_select'; import { IndexedArray } from '../indexed_array'; import { countMetricAgg } from './metrics/count'; import { avgMetricAgg } from './metrics/avg'; diff --git a/src/legacy/ui/public/agg_types/utils.tsx b/src/legacy/ui/public/agg_types/utils.tsx new file mode 100644 index 0000000000000..4839639e015fd --- /dev/null +++ b/src/legacy/ui/public/agg_types/utils.tsx @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +function isValidJson(value: string) { + if (!value || value.length === 0) { + return true; + } + + const trimmedValue = value.trim(); + + if (trimmedValue[0] === '{' || trimmedValue[0] === '[') { + try { + JSON.parse(trimmedValue); + return true; + } catch (e) { + return false; + } + } else { + return false; + } +} + +export { isValidJson }; diff --git a/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js b/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js index 9e84cdf4b90ad..d3ee5e5ec3d43 100644 --- a/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js +++ b/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js @@ -21,6 +21,7 @@ import angular from 'angular'; import _ from 'lodash'; import expect from 'expect.js'; +import sinon from 'sinon'; import ngMock from 'ng_mock'; import '../agg_params'; import { VisProvider } from '../../..'; @@ -61,6 +62,10 @@ describe('Vis-Editor-Agg-Params plugin directive', function () { orderAggSchema = (new Schemas([config])).all[0]; $parentScope.groupName = 'metrics'; + const formCtrl = { + $addControl: sinon.fake() + }; + const state = { schema: orderAggSchema, type: 'count' @@ -84,6 +89,8 @@ describe('Vis-Editor-Agg-Params plugin directive', function () { `` ); + $elem.data('$formController', formCtrl); + // compile the html compile($elem)($parentScope); diff --git a/src/legacy/ui/public/vis/editors/default/agg_params.js b/src/legacy/ui/public/vis/editors/default/agg_params.js index 9df0029bc9f8e..7a4c33b7c4658 100644 --- a/src/legacy/ui/public/vis/editors/default/agg_params.js +++ b/src/legacy/ui/public/vis/editors/default/agg_params.js @@ -39,7 +39,8 @@ uiModules restrict: 'E', template: aggParamsTemplate, scope: true, - link: function ($scope, $el, attr) { + require: '^form', + link: function ($scope, $el, attr, aggForm) { $scope.$bind('agg', attr.agg); $scope.$bind('groupName', attr.groupName); $scope.$bind('indexPattern', attr.indexPattern); @@ -56,6 +57,20 @@ uiModules updateEditorConfig('default'); }); + $scope.onParamsChange = (type, agg, value, options) => { + if(agg.params[type] !== value) { + agg.params[type] = value; + } + + if (aggForm && aggForm[type]) { + aggForm[type].$setDirty(); + } + + if (options && typeof options.isValid === 'boolean') { + aggForm[type].$setValidity(type, options.isValid); + } + }; + function updateEditorConfig(property = 'fixedValue') { $scope.editorConfig = editorConfigProviders.getConfigForAgg( aggTypes.byType[$scope.groupName], From b73491cdbb80eb540cbe6685e2d80d980f93a321 Mon Sep 17 00:00:00 2001 From: maryia-lapata Date: Wed, 13 Mar 2019 15:54:32 +0300 Subject: [PATCH 2/3] Add controls to form --- src/legacy/ui/public/agg_types/agg_param.d.ts | 26 ++++++++++ src/legacy/ui/public/agg_types/agg_type.js | 2 + .../ui/public/agg_types/buckets/terms.js | 2 + .../ui/public/agg_types/controls/raw_json.tsx | 15 +++--- .../ui/public/agg_types/controls/string.html | 13 ----- .../ui/public/agg_types/controls/string.tsx | 37 ++++++++++++++ src/legacy/ui/public/agg_types/index.d.ts | 1 + .../ui/public/agg_types/param_types/json.js | 4 +- .../ui/public/agg_types/param_types/string.js | 7 +-- .../editors/default/__tests__/agg_params.js | 4 +- .../public/vis/editors/default/agg_param.js | 49 +++++++++++++++++-- .../editors/default/agg_param_editor_props.ts | 30 ++++++++++++ .../default/agg_param_react_wrapper.tsx | 39 +++++++++++++++ .../public/vis/editors/default/agg_params.js | 40 ++++++++------- .../ui/public/vis/editors/default/index.ts | 20 ++++++++ .../functional/page_objects/visualize_page.js | 2 +- 16 files changed, 241 insertions(+), 50 deletions(-) create mode 100644 src/legacy/ui/public/agg_types/agg_param.d.ts delete mode 100644 src/legacy/ui/public/agg_types/controls/string.html create mode 100644 src/legacy/ui/public/agg_types/controls/string.tsx create mode 100644 src/legacy/ui/public/vis/editors/default/agg_param_editor_props.ts create mode 100644 src/legacy/ui/public/vis/editors/default/agg_param_react_wrapper.tsx create mode 100644 src/legacy/ui/public/vis/editors/default/index.ts diff --git a/src/legacy/ui/public/agg_types/agg_param.d.ts b/src/legacy/ui/public/agg_types/agg_param.d.ts new file mode 100644 index 0000000000000..4e1af89c9e613 --- /dev/null +++ b/src/legacy/ui/public/agg_types/agg_param.d.ts @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +interface AggParam { + type: string; + name: string; + displayName?: string; +} + +export { AggParam }; diff --git a/src/legacy/ui/public/agg_types/agg_type.js b/src/legacy/ui/public/agg_types/agg_type.js index 955ecbd80e472..859541956ef93 100644 --- a/src/legacy/ui/public/agg_types/agg_type.js +++ b/src/legacy/ui/public/agg_types/agg_type.js @@ -20,6 +20,7 @@ import _ from 'lodash'; import { AggParams } from './agg_params'; import { fieldFormats } from '../registry/field_formats'; +import { i18n } from '@kbn/i18n'; /** * Generic AggType Constructor @@ -118,6 +119,7 @@ function AggType(config) { if (config.customLabels !== false) { this.params.push({ name: 'customLabel', + displayName: i18n.translate('common.ui.aggTypes.string.customLabel', { defaultMessage: 'Custom label' }), type: 'string', write: _.noop }); diff --git a/src/legacy/ui/public/agg_types/buckets/terms.js b/src/legacy/ui/public/agg_types/buckets/terms.js index dcf15a29cff53..c8d323d46db92 100644 --- a/src/legacy/ui/public/agg_types/buckets/terms.js +++ b/src/legacy/ui/public/agg_types/buckets/terms.js @@ -311,6 +311,7 @@ export const termsBucketAgg = new BucketAggType({ }, { name: 'exclude', + displayName: i18n.translate('common.ui.aggTypes.buckets.terms.excludeLabel', { defaultMessage: 'Exclude' }), type: 'string', advanced: true, disabled: isNotType('string'), @@ -318,6 +319,7 @@ export const termsBucketAgg = new BucketAggType({ }, { name: 'include', + displayName: i18n.translate('common.ui.aggTypes.buckets.terms.includeLabel', { defaultMessage: 'Include' }), type: 'string', advanced: true, disabled: isNotType('string'), diff --git a/src/legacy/ui/public/agg_types/controls/raw_json.tsx b/src/legacy/ui/public/agg_types/controls/raw_json.tsx index e722f2e76cff1..4cda912afd98e 100644 --- a/src/legacy/ui/public/agg_types/controls/raw_json.tsx +++ b/src/legacy/ui/public/agg_types/controls/raw_json.tsx @@ -27,15 +27,11 @@ import { isValidJson } from '../utils'; interface RawJSONSelectProps { agg: AggConfig; - onParamsChange: ( - type: string, - agg: AggConfig, - field: any, - options?: { isValid?: boolean } - ) => void; + value: any; + setValue: (value: any, options?: { isValid?: boolean }) => void; } -function RawJSONSelect({ agg = {}, onParamsChange }: RawJSONSelectProps) { +function RawJSONSelect({ agg = {}, value, setValue }: RawJSONSelectProps) { const [isInvalid, setIsInvalid] = useState(false); const label = ( @@ -53,16 +49,17 @@ function RawJSONSelect({ agg = {}, onParamsChange }: RawJSONSelectProps) { ); const onTextAreaChange = (e: React.ChangeEvent) => { - const value: string = get(e, 'target.value'); + const fieldValue: string = get(e, 'target.value'); const isValid = isValidJson(value); setIsInvalid(!isValid); - onParamsChange('json', agg, value, { isValid }); + setValue(fieldValue, { isValid }); }; return ( -
- - -
- -
- diff --git a/src/legacy/ui/public/agg_types/controls/string.tsx b/src/legacy/ui/public/agg_types/controls/string.tsx new file mode 100644 index 0000000000000..b4706821bd89d --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/string.tsx @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; + +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { AggParamEditorProps } from '../../vis/editors/default'; + +function StringParamEditor({ agg, aggParam, value, setValue }: AggParamEditorProps) { + return ( + + setValue(ev.target.value)} + /> + + ); +} + +export { StringParamEditor }; diff --git a/src/legacy/ui/public/agg_types/index.d.ts b/src/legacy/ui/public/agg_types/index.d.ts index 81d52bb8e788b..6832c4ebcbd4a 100644 --- a/src/legacy/ui/public/agg_types/index.d.ts +++ b/src/legacy/ui/public/agg_types/index.d.ts @@ -17,4 +17,5 @@ * under the License. */ +export { AggParam } from './agg_param'; export { AggType } from './agg_type'; diff --git a/src/legacy/ui/public/agg_types/param_types/json.js b/src/legacy/ui/public/agg_types/param_types/json.js index 6eb7e8662b629..b8585951699ed 100644 --- a/src/legacy/ui/public/agg_types/param_types/json.js +++ b/src/legacy/ui/public/agg_types/param_types/json.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import editorHtml from '../controls/raw_json.html'; +import { RawJSONSelect } from '../controls/raw_json'; import { BaseParamType } from './base'; import { createLegacyClass } from '../../utils/legacy_class'; @@ -30,7 +30,7 @@ function JsonParamType(config) { JsonParamType.Super.call(this, config); } -JsonParamType.prototype.editor = editorHtml; +JsonParamType.prototype.editorComponent = RawJSONSelect; /** * Write the aggregation parameter. diff --git a/src/legacy/ui/public/agg_types/param_types/string.js b/src/legacy/ui/public/agg_types/param_types/string.js index 68c7c3aa4bfd5..19c9bfe132deb 100644 --- a/src/legacy/ui/public/agg_types/param_types/string.js +++ b/src/legacy/ui/public/agg_types/param_types/string.js @@ -17,16 +17,16 @@ * under the License. */ -import editorHtml from '../controls/string.html'; -import { BaseParamType } from './base'; import { createLegacyClass } from '../../utils/legacy_class'; +import { StringParamEditor } from '../controls/string'; +import { BaseParamType } from './base'; createLegacyClass(StringParamType).inherits(BaseParamType); function StringParamType(config) { StringParamType.Super.call(this, config); } -StringParamType.prototype.editor = editorHtml; +StringParamType.prototype.editorComponent = StringParamEditor; /** * Write the aggregation parameter. @@ -45,3 +45,4 @@ StringParamType.prototype.write = function (aggConfig, output) { }; export { StringParamType }; + diff --git a/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js b/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js index d3ee5e5ec3d43..5a283e2c624be 100644 --- a/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js +++ b/src/legacy/ui/public/vis/editors/default/__tests__/agg_params.js @@ -111,7 +111,7 @@ describe('Vis-Editor-Agg-Params plugin directive', function () { aggFilter: aggFilter }); - const customLabelElement = $elem.find('label:contains("Custom Label")'); + const customLabelElement = $elem.find('label:contains("Custom label")'); expect(customLabelElement.length).to.be(1); }); @@ -124,7 +124,7 @@ describe('Vis-Editor-Agg-Params plugin directive', function () { aggFilter: aggFilter }); - const customLabelElement = $elem.find('label:contains("Custom Label")'); + const customLabelElement = $elem.find('label:contains("Custom label")'); expect(customLabelElement.length).to.be(0); }); }); diff --git a/src/legacy/ui/public/vis/editors/default/agg_param.js b/src/legacy/ui/public/vis/editors/default/agg_param.js index 36e3d8c680fdd..081e4db55cbcb 100644 --- a/src/legacy/ui/public/vis/editors/default/agg_param.js +++ b/src/legacy/ui/public/vis/editors/default/agg_param.js @@ -17,32 +17,75 @@ * under the License. */ -import _ from 'lodash'; +import { isFunction } from 'lodash'; +import { wrapInI18nContext } from 'ui/i18n'; import { uiModules } from '../../../modules'; +import { AggParamReactWrapper } from './agg_param_react_wrapper'; uiModules .get('app/visualize') + .directive('visAggParamReactWrapper', reactDirective => reactDirective(wrapInI18nContext(AggParamReactWrapper), [ + ['agg', { watchDepth: 'collection' }], + ['aggParam', { watchDepth: 'reference' }], + ['paramEditor', { wrapApply: false }], + ['onChange', { watchDepth: 'reference' }], + 'value', + ])) .directive('visAggParamEditor', function (config) { return { restrict: 'E', + // We can't use scope binding here yet, since quiet a lot of child directives arbitrary access + // parent scope values right now. So we cannot easy change this, until we remove the whole directive. scope: true, - template: function ($el) { + template: function ($el, attrs) { + if (attrs.editorComponent) { + // Why do we need the `ng-if` here? + // Short answer: Preventing black magic + // Longer answer: The way this component is mounted in agg_params.js (by manually compiling) + // and adding to some array, once you switch an aggregation type, this component will once + // render once with a "broken state" (something like new aggParam, but still old template), + // before agg_params.js actually removes it from the DOM and create a correct version with + // the correct template. That ng-if check prevents us from crashing during that broken render. + return ``; + } + return $el.html(); }, link: { pre: function ($scope, $el, attr) { $scope.$bind('aggParam', attr.aggParam); + $scope.$bind('agg', attr.agg); + $scope.$bind('editorComponent', attr.editorComponent); }, post: function ($scope) { $scope.config = config; $scope.optionEnabled = function (option) { - if (option && _.isFunction(option.enabled)) { + if (option && isFunction(option.enabled)) { return option.enabled($scope.agg); } return true; }; + + $scope.$watch('agg.params[aggParam.name]', (value) => { + // Whenever the value of the parameter changed (e.g. by a reset or actually by calling) + // we store the new value in $scope.paramValue, which will be passed as a new value to the react component. + $scope.paramValue = value; + }, true); + + $scope.onChange = (value, options) => { + // This is obviously not a good code quality, but without using scope binding (which we can't see above) + // to bind function values, this is right now the best temporary fix, until all of this will be gone. + $scope.$parent.onParamChange($scope.agg, $scope.aggParam.name, value, options); + }; } } }; diff --git a/src/legacy/ui/public/vis/editors/default/agg_param_editor_props.ts b/src/legacy/ui/public/vis/editors/default/agg_param_editor_props.ts new file mode 100644 index 0000000000000..3e7623b6c25fc --- /dev/null +++ b/src/legacy/ui/public/vis/editors/default/agg_param_editor_props.ts @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AggParam } from '../../../agg_types'; +import { AggConfig } from '../../agg_config'; + +interface AggParamEditorProps { + agg: AggConfig; + aggParam: AggParam; + value: T; + setValue(value: T): void; +} + +export { AggParamEditorProps }; diff --git a/src/legacy/ui/public/vis/editors/default/agg_param_react_wrapper.tsx b/src/legacy/ui/public/vis/editors/default/agg_param_react_wrapper.tsx new file mode 100644 index 0000000000000..4b5380c92559c --- /dev/null +++ b/src/legacy/ui/public/vis/editors/default/agg_param_react_wrapper.tsx @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; + +import { AggParam } from '../../../agg_types'; +import { AggConfig } from '../../agg_config'; +import { AggParamEditorProps } from './agg_param_editor_props'; + +interface AggParamReactWrapperProps { + agg: AggConfig; + aggParam: AggParam; + paramEditor: React.FunctionComponent>; + value: T; + onChange(value: T): void; +} + +function AggParamReactWrapper(props: AggParamReactWrapperProps) { + const { agg, aggParam, paramEditor: ParamEditor, onChange, value } = props; + return ; +} + +export { AggParamReactWrapper }; diff --git a/src/legacy/ui/public/vis/editors/default/agg_params.js b/src/legacy/ui/public/vis/editors/default/agg_params.js index 7a4c33b7c4658..5915583c874e6 100644 --- a/src/legacy/ui/public/vis/editors/default/agg_params.js +++ b/src/legacy/ui/public/vis/editors/default/agg_params.js @@ -18,18 +18,18 @@ */ import $ from 'jquery'; -import { has, get } from 'lodash'; -import aggSelectHtml from './agg_select.html'; -import advancedToggleHtml from './advanced_toggle.html'; -import '../../../filters/match_any'; -import './agg_param'; +import { get, has } from 'lodash'; import { aggTypes } from '../../../agg_types'; -import { uiModules } from '../../../modules'; -import { documentationLinks } from '../../../documentation_links/documentation_links'; -import aggParamsTemplate from './agg_params.html'; import { aggTypeFilters } from '../../../agg_types/filter'; -import { editorConfigProviders } from '../config/editor_config_providers'; import { aggTypeFieldFilters } from '../../../agg_types/param_types/filter'; +import { documentationLinks } from '../../../documentation_links/documentation_links'; +import '../../../filters/match_any'; +import { uiModules } from '../../../modules'; +import { editorConfigProviders } from '../config/editor_config_providers'; +import advancedToggleHtml from './advanced_toggle.html'; +import './agg_param'; +import aggParamsTemplate from './agg_params.html'; +import aggSelectHtml from './agg_select.html'; uiModules .get('app/visualize') @@ -57,17 +57,17 @@ uiModules updateEditorConfig('default'); }); - $scope.onParamsChange = (type, agg, value, options) => { - if(agg.params[type] !== value) { - agg.params[type] = value; + $scope.onParamChange = (agg, paramName, value, options) => { + if(agg.params[paramName] !== value) { + agg.params[paramName] = value; } - if (aggForm && aggForm[type]) { - aggForm[type].$setDirty(); + if (aggForm) { + aggForm[paramName].$setDirty(); } if (options && typeof options.isValid === 'boolean') { - aggForm[type].$setValidity(type, options.isValid); + aggForm[paramName].$setValidity(paramName, options.isValid); } }; @@ -200,18 +200,24 @@ uiModules // build HTML editor given an aggParam and index function getAggParamHTML(param, idx) { // don't show params without an editor - if (!param.editor) { + if (!param.editor && !param.editorComponent) { return; } const attrs = { - 'agg-param': 'agg.type.params[' + idx + ']' + 'agg-param': 'agg.type.params[' + idx + ']', + 'agg': 'agg', }; if (param.advanced) { attrs['ng-show'] = 'advancedToggled'; } + if (param.editorComponent) { + attrs['editor-component'] = `agg.type.params[${idx}].editorComponent`; + attrs['ng-model'] = `agg.params.${param.name}`; + attrs.name = param.name; + } return $('') .attr(attrs) .append(param.editor) diff --git a/src/legacy/ui/public/vis/editors/default/index.ts b/src/legacy/ui/public/vis/editors/default/index.ts new file mode 100644 index 0000000000000..7167a09492b5c --- /dev/null +++ b/src/legacy/ui/public/vis/editors/default/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { AggParamEditorProps } from './agg_param_editor_props'; diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js index c811a3ef6485f..d7506442aabc8 100644 --- a/test/functional/page_objects/visualize_page.js +++ b/test/functional/page_objects/visualize_page.js @@ -499,7 +499,7 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli } async setCustomLabel(label, index = 1) { - const customLabel = await find.byCssSelector(`#visEditorStringInput${index}customLabel`); + const customLabel = await testSubjects.find(`visEditorStringInput${index}customLabel`); customLabel.type(label); } From fae775e457afa25448a1f6772e9f5271e15bd210 Mon Sep 17 00:00:00 2001 From: maryia-lapata Date: Wed, 13 Mar 2019 16:02:44 +0300 Subject: [PATCH 3/3] Add model and manage validity in model --- .../ui/public/agg_types/controls/raw_json.tsx | 2 +- .../ui/public/vis/editors/default/agg_param.js | 11 +++++++++-- .../ui/public/vis/editors/default/agg_params.js | 16 +++------------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/legacy/ui/public/agg_types/controls/raw_json.tsx b/src/legacy/ui/public/agg_types/controls/raw_json.tsx index 4cda912afd98e..3bc163f9d5aa0 100644 --- a/src/legacy/ui/public/agg_types/controls/raw_json.tsx +++ b/src/legacy/ui/public/agg_types/controls/raw_json.tsx @@ -50,7 +50,7 @@ function RawJSONSelect({ agg = {}, value, setValue }: RawJSONSelectProps) { ); const onTextAreaChange = (e: React.ChangeEvent) => { const fieldValue: string = get(e, 'target.value'); - const isValid = isValidJson(value); + const isValid = isValidJson(fieldValue); setIsInvalid(!isValid); setValue(fieldValue, { isValid }); }; diff --git a/src/legacy/ui/public/vis/editors/default/agg_param.js b/src/legacy/ui/public/vis/editors/default/agg_param.js index 081e4db55cbcb..5b858e1bd0548 100644 --- a/src/legacy/ui/public/vis/editors/default/agg_param.js +++ b/src/legacy/ui/public/vis/editors/default/agg_param.js @@ -37,6 +37,7 @@ uiModules // We can't use scope binding here yet, since quiet a lot of child directives arbitrary access // parent scope values right now. So we cannot easy change this, until we remove the whole directive. scope: true, + require: '^ngModel', template: function ($el, attrs) { if (attrs.editorComponent) { // Why do we need the `ng-if` here? @@ -64,7 +65,7 @@ uiModules $scope.$bind('agg', attr.agg); $scope.$bind('editorComponent', attr.editorComponent); }, - post: function ($scope) { + post: function ($scope, $el, attr, ngModelCtrl) { $scope.config = config; $scope.optionEnabled = function (option) { @@ -84,7 +85,13 @@ uiModules $scope.onChange = (value, options) => { // This is obviously not a good code quality, but without using scope binding (which we can't see above) // to bind function values, this is right now the best temporary fix, until all of this will be gone. - $scope.$parent.onParamChange($scope.agg, $scope.aggParam.name, value, options); + $scope.$parent.onParamChange($scope.agg, $scope.aggParam.name, value); + + ngModelCtrl.$setDirty(); + + if (options && typeof options.isValid === 'boolean') { + ngModelCtrl.$setValidity(`someError${$scope.aggParam.name}`, options.isValid); + } }; } } diff --git a/src/legacy/ui/public/vis/editors/default/agg_params.js b/src/legacy/ui/public/vis/editors/default/agg_params.js index 5915583c874e6..96add99df0ace 100644 --- a/src/legacy/ui/public/vis/editors/default/agg_params.js +++ b/src/legacy/ui/public/vis/editors/default/agg_params.js @@ -39,8 +39,7 @@ uiModules restrict: 'E', template: aggParamsTemplate, scope: true, - require: '^form', - link: function ($scope, $el, attr, aggForm) { + link: function ($scope, $el, attr) { $scope.$bind('agg', attr.agg); $scope.$bind('groupName', attr.groupName); $scope.$bind('indexPattern', attr.indexPattern); @@ -57,18 +56,10 @@ uiModules updateEditorConfig('default'); }); - $scope.onParamChange = (agg, paramName, value, options) => { + $scope.onParamChange = (agg, paramName, value) => { if(agg.params[paramName] !== value) { agg.params[paramName] = value; } - - if (aggForm) { - aggForm[paramName].$setDirty(); - } - - if (options && typeof options.isValid === 'boolean') { - aggForm[paramName].$setValidity(paramName, options.isValid); - } }; function updateEditorConfig(property = 'fixedValue') { @@ -208,6 +199,7 @@ uiModules 'agg-param': 'agg.type.params[' + idx + ']', 'agg': 'agg', }; + attrs['ng-model'] = `someModal${param.name}`; if (param.advanced) { attrs['ng-show'] = 'advancedToggled'; @@ -215,8 +207,6 @@ uiModules if (param.editorComponent) { attrs['editor-component'] = `agg.type.params[${idx}].editorComponent`; - attrs['ng-model'] = `agg.params.${param.name}`; - attrs.name = param.name; } return $('') .attr(attrs)