diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index 424e5ab0bf4d5..9187e207ed0d6 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -69,7 +69,6 @@ export { isStringType, isType, isValidInterval, - isValidJson, METRIC_TYPES, OptionedParamType, parentPipelineType, diff --git a/src/legacy/core_plugins/data/public/search/aggs/index.ts b/src/legacy/core_plugins/data/public/search/aggs/index.ts index 8d6fbeacd606a..75d632a0f931f 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/index.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/index.ts @@ -53,7 +53,7 @@ export { toAbsoluteDates } from './buckets/lib/date_utils'; export { convertIPRangeToString } from './buckets/ip_range'; export { aggTypeFilters, propFilter } from './filter'; export { OptionedParamType } from './param_types/optioned'; -export { isValidJson, isValidInterval } from './utils'; +export { isValidInterval } from './utils'; export { BUCKET_TYPES } from './buckets/bucket_agg_types'; export { METRIC_TYPES } from './metrics/metric_agg_types'; diff --git a/src/legacy/core_plugins/data/public/search/aggs/utils.test.tsx b/src/legacy/core_plugins/data/public/search/aggs/utils.test.tsx deleted file mode 100644 index c0662c98755a3..0000000000000 --- a/src/legacy/core_plugins/data/public/search/aggs/utils.test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 { isValidJson } from './utils'; - -const input = { - valid: '{ "test": "json input" }', - invalid: 'strings are not json', -}; - -describe('AggType utils', () => { - describe('isValidJson', () => { - it('should return true when empty string', () => { - expect(isValidJson('')).toBeTruthy(); - }); - - it('should return true when undefine', () => { - expect(isValidJson(undefined as any)).toBeTruthy(); - }); - - it('should return false when invalid string', () => { - expect(isValidJson(input.invalid)).toBeFalsy(); - }); - - it('should return true when valid string', () => { - expect(isValidJson(input.valid)).toBeTruthy(); - }); - - it('should return false if a number', () => { - expect(isValidJson('0')).toBeFalsy(); - }); - }); -}); diff --git a/src/legacy/core_plugins/data/public/search/aggs/utils.ts b/src/legacy/core_plugins/data/public/search/aggs/utils.ts index 67ea373f438fb..9fcd3f7930b06 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/utils.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/utils.ts @@ -20,35 +20,6 @@ import { leastCommonInterval } from 'ui/vis/lib/least_common_interval'; import { isValidEsInterval } from '../../../common'; -/** - * Check a string if it's a valid JSON. - * - * @param {string} value a string that should be validated - * @returns {boolean} true if value is a valid JSON or if value is an empty string, or a string with whitespaces, otherwise false - */ -export function isValidJson(value: string): boolean { - if (!value || value.length === 0) { - return true; - } - - const trimmedValue = value.trim(); - - if (trimmedValue.length === 0) { - return true; - } - - if (trimmedValue[0] === '{' || trimmedValue[0] === '[') { - try { - JSON.parse(trimmedValue); - return true; - } catch (e) { - return false; - } - } else { - return false; - } -} - export function isValidInterval(value: string, baseInterval?: string) { if (baseInterval) { return _parseWithBase(value, baseInterval); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/raw_json.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/raw_json.tsx index 32939c420155f..b6a6da09fb20e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/raw_json.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/raw_json.tsx @@ -17,65 +17,91 @@ * under the License. */ -import React, { useEffect } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; -import { EuiFormRow, EuiIconTip, EuiTextArea } from '@elastic/eui'; +import { EuiFormRow, EuiIconTip, EuiCodeEditor, EuiScreenReaderOnly } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { isValidJson } from '../../legacy_imports'; import { AggParamEditorProps } from '../agg_param_props'; function RawJsonParamEditor({ - agg, showValidation, value = '', setValidity, setValue, setTouched, }: AggParamEditorProps) { - const label = ( - <> - {' '} - - + const [isFieldValid, setFieldValidity] = useState(true); + const [editorReady, setEditorReady] = useState(false); + + const editorTooltipText = useMemo( + () => + i18n.translate('visDefaultEditor.controls.jsonInputTooltip', { + defaultMessage: + "Any JSON formatted properties you add here will be merged with the elasticsearch aggregation definition for this section. For example 'shard_size' on a terms aggregation.", + }), + [] ); - const isValid = isValidJson(value); - const onChange = (ev: React.ChangeEvent) => { - const textValue = ev.target.value; - setValue(textValue); - setValidity(isValidJson(textValue)); - }; + const jsonEditorLabelText = useMemo( + () => + i18n.translate('visDefaultEditor.controls.jsonInputLabel', { + defaultMessage: 'JSON input', + }), + [] + ); - useEffect(() => { - setValidity(isValid); - }, [isValid]); + const label = useMemo( + () => ( + <> + {jsonEditorLabelText}{' '} + + + ), + [jsonEditorLabelText, editorTooltipText] + ); + + const onEditorValidate = useCallback( + (annotations: unknown[]) => { + // The first onValidate returned from EuiCodeEditor is a false negative + if (editorReady) { + const validity = annotations.length === 0; + setFieldValidity(validity); + setValidity(validity); + } else { + setEditorReady(true); + } + }, + [setValidity, editorReady] + ); return ( - + <> + + +

{editorTooltipText}

+
+
); } diff --git a/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts b/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts index 5c02b50286a95..33a5c0fe660c4 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/legacy_imports.ts @@ -42,7 +42,7 @@ export { parentPipelineType } from 'ui/agg_types'; export { siblingPipelineType } from 'ui/agg_types'; export { isType, isStringType } from 'ui/agg_types'; export { OptionedValueProp, OptionedParamEditorProps, OptionedParamType } from 'ui/agg_types'; -export { isValidJson, isValidInterval } from 'ui/agg_types'; +export { isValidInterval } from 'ui/agg_types'; export { AggParamOption } from 'ui/agg_types'; export { CidrMask } from 'ui/agg_types'; diff --git a/src/legacy/ui/public/agg_types/index.ts b/src/legacy/ui/public/agg_types/index.ts index db64bd025b8cb..d066e61df18e9 100644 --- a/src/legacy/ui/public/agg_types/index.ts +++ b/src/legacy/ui/public/agg_types/index.ts @@ -73,7 +73,6 @@ export { isStringType, isType, isValidInterval, - isValidJson, OptionedParamType, parentPipelineType, propFilter,