From 7f166a40bddd366b4700e3083be92297bc048c67 Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Wed, 12 Jun 2019 17:34:28 +0300 Subject: [PATCH] [Vis: Default editor] EUIficate ranges param editor (#38531) * EUIficate ranges control * Remove unused translations * Fix screenreader issue * Add an initial case for safety * Remove labels, add icon between ranges * Add title for delete btn # Conflicts: # x-pack/plugins/translations/translations/zh-CN.json --- .../ui/public/agg_types/buckets/range.js | 4 +- .../controls/components/from_to_list.tsx | 5 +- .../ui/public/agg_types/controls/ranges.html | 71 -------- .../ui/public/agg_types/controls/ranges.tsx | 166 ++++++++++++++++++ .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 8 +- 6 files changed, 173 insertions(+), 87 deletions(-) delete mode 100644 src/legacy/ui/public/agg_types/controls/ranges.html create mode 100644 src/legacy/ui/public/agg_types/controls/ranges.tsx diff --git a/src/legacy/ui/public/agg_types/buckets/range.js b/src/legacy/ui/public/agg_types/buckets/range.js index 20d0de4b4f02d..ad0dd056fbf00 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.js +++ b/src/legacy/ui/public/agg_types/buckets/range.js @@ -21,7 +21,7 @@ import { BucketAggType } from './_bucket_agg_type'; import { createFilterRange } from './create_filter/range'; import { FieldFormat } from '../../../field_formats/field_format'; import { RangeKey } from './range_key'; -import rangesTemplate from '../controls/ranges.html'; +import { RangesParamEditor } from '../controls/ranges'; import { i18n } from '@kbn/i18n'; const keyCaches = new WeakMap(); @@ -91,7 +91,7 @@ export const rangeBucketAgg = new BucketAggType({ { from: 0, to: 1000 }, { from: 1000, to: 2000 } ], - editor: rangesTemplate, + editorComponent: RangesParamEditor, write: function (aggConfig, output) { output.params.ranges = aggConfig.params.ranges; output.params.keyed = true; diff --git a/src/legacy/ui/public/agg_types/controls/components/from_to_list.tsx b/src/legacy/ui/public/agg_types/controls/components/from_to_list.tsx index 99d689199e24c..0d6277efac317 100644 --- a/src/legacy/ui/public/agg_types/controls/components/from_to_list.tsx +++ b/src/legacy/ui/public/agg_types/controls/components/from_to_list.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; -import { EuiFieldText, EuiFlexItem } from '@elastic/eui'; +import { EuiFieldText, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import Ipv4Address from '../../../utils/ipv4_address'; import { InputList, InputListConfig, InputModel, InputObject, InputItem } from './input_list'; @@ -95,6 +95,9 @@ function FromToList({ showValidation, onBlur, ...rest }: FromToListProps) { onBlur={onBlur} /> + + + - - - - - - - - - - - - - - - - - - - - - - - -
-

- - - -

-
- - diff --git a/src/legacy/ui/public/agg_types/controls/ranges.tsx b/src/legacy/ui/public/agg_types/controls/ranges.tsx new file mode 100644 index 0000000000000..59c7a502b3a9d --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/ranges.tsx @@ -0,0 +1,166 @@ +/* + * 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, { Fragment, useState, useEffect } from 'react'; +import { + htmlIdGenerator, + EuiButtonIcon, + EuiFieldNumber, + EuiFlexItem, + EuiFlexGroup, + EuiIcon, + EuiSpacer, + EuiButtonEmpty, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { isEqual, omit } from 'lodash'; +import { AggParamEditorProps } from 'ui/vis/editors/default'; + +const FROM_PLACEHOLDER = '\u2212\u221E'; +const TO_PLACEHOLDER = '+\u221E'; + +const generateId = htmlIdGenerator(); +const isEmpty = (value: any) => value === undefined || value === null; + +interface RangeValues { + from?: number; + to?: number; +} + +interface RangeValuesModel extends RangeValues { + id: string; +} + +function RangesParamEditor({ agg, value = [], setValue }: AggParamEditorProps) { + const [ranges, setRanges] = useState(() => value.map(range => ({ ...range, id: generateId() }))); + + // set up an initial range when there is no default range + useEffect(() => { + if (!value.length) { + onAddRange(); + } + }, []); + + useEffect( + () => { + // responsible for discarding changes + if ( + value.length !== ranges.length || + value.some((range, index) => !isEqual(range, omit(ranges[index], 'id'))) + ) { + setRanges(value.map(range => ({ ...range, id: generateId() }))); + } + }, + [value] + ); + + const updateRanges = (rangeValues: RangeValuesModel[]) => { + // do not set internal id parameter into saved object + setValue(rangeValues.map(range => omit(range, 'id'))); + setRanges(rangeValues); + }; + const onAddRange = () => updateRanges([...ranges, { id: generateId() }]); + const onRemoveRange = (id: string) => updateRanges(ranges.filter(range => range.id !== id)); + const onChangeRange = (id: string, key: string, newValue: string) => + updateRanges( + ranges.map(range => + range.id === id + ? { + ...range, + [key]: newValue === '' ? undefined : parseFloat(newValue), + } + : range + ) + ); + + return ( + <> + {ranges.map(({ from, to, id }) => { + const deleteBtnTitle = i18n.translate( + 'common.ui.aggTypes.ranges.removeRangeButtonAriaLabel', + { + defaultMessage: 'Remove the range of {from} to {to}', + values: { + from: isEmpty(from) ? FROM_PLACEHOLDER : from, + to: isEmpty(to) ? TO_PLACEHOLDER : to, + }, + } + ); + + return ( + + + + onChangeRange(id, 'from', ev.target.value)} + fullWidth={true} + compressed={true} + /> + + + + + + onChangeRange(id, 'to', ev.target.value)} + fullWidth={true} + compressed={true} + /> + + + onRemoveRange(id)} + /> + + + + + ); + })} + + + + + + + + + ); +} + +export { RangesParamEditor }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 07e72ba2659ec..412eacb4a7de3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -229,12 +229,6 @@ "common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage": "{fieldParameter} は必須パラメーターです", "common.ui.aggTypes.placeMarkersOffGridLabel": "グリッド外にマーカーを配置 (ジオセントロイドを使用)", "common.ui.aggTypes.precisionLabel": "精度", - "common.ui.aggTypes.ranges.addRangeButtonLabel": "範囲を追加", - "common.ui.aggTypes.ranges.fromColumnLabel": "From", - "common.ui.aggTypes.ranges.removeRangeButtonAriaLabel": "この範囲を削除", - "common.ui.aggTypes.ranges.requiredRangeDescription": "範囲を最低 1 つ指定する必要があります。", - "common.ui.aggTypes.ranges.requiredRangeTitle": "必須:", - "common.ui.aggTypes.ranges.toColumnLabel": "To", "common.ui.aggTypes.showEmptyBucketsLabel": "空のバケットを表示", "common.ui.aggTypes.showEmptyBucketsTooltip": "結果のあるバケットだけでなくすべてのバケットを表示します", "common.ui.aggTypes.sizeLabel": "サイズ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c93e41b00854d..e8334b1b9f546 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -228,12 +228,6 @@ "common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage": "{fieldParameter} 是必需字段", "common.ui.aggTypes.placeMarkersOffGridLabel": "将标记置于网格外(使用 geocentroid)", "common.ui.aggTypes.precisionLabel": "精确度", - "common.ui.aggTypes.ranges.addRangeButtonLabel": "添加范围", - "common.ui.aggTypes.ranges.fromColumnLabel": "从", - "common.ui.aggTypes.ranges.removeRangeButtonAriaLabel": "移除此范围", - "common.ui.aggTypes.ranges.requiredRangeDescription": "必须指定至少一个范围。", - "common.ui.aggTypes.ranges.requiredRangeTitle": "必需:", - "common.ui.aggTypes.ranges.toColumnLabel": "到", "common.ui.aggTypes.showEmptyBucketsLabel": "显示空存储桶", "common.ui.aggTypes.showEmptyBucketsTooltip": "显示所有存储桶,不仅仅有结果的存储桶", "common.ui.aggTypes.sizeLabel": "大小", @@ -10008,4 +10002,4 @@ "xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +}