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
+}