From d8e2aacecbe782dcd187ea4603dbdb5a820200ea Mon Sep 17 00:00:00 2001 From: sulemanof Date: Fri, 7 Jun 2019 17:29:15 +0300 Subject: [PATCH 1/6] EUIficate ranges control --- .../ui/public/agg_types/buckets/range.js | 4 +- .../ui/public/agg_types/controls/ranges.html | 71 -------- .../ui/public/agg_types/controls/ranges.tsx | 154 ++++++++++++++++++ .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 1 - 5 files changed, 156 insertions(+), 76 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/ranges.html b/src/legacy/ui/public/agg_types/controls/ranges.html deleted file mode 100644 index e24b6f4378857..0000000000000 --- a/src/legacy/ui/public/agg_types/controls/ranges.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - -
- - - -
- - - - - -
- - -
-

- - - -

-
- - 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..aa690fd10f15c --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/ranges.tsx @@ -0,0 +1,154 @@ +/* + * 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, + EuiFormLabel, + 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 generateId = htmlIdGenerator(); +const isNil = (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() }))); + + 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 }) => ( + + + + + + + } + aria-labelledby={`visEditorRangeFrom${agg.id}_${id}`} + value={isNil(from) ? '' : from} + placeholder="-∞" + onChange={ev => onChangeRange(id, 'from', ev.target.value)} + fullWidth={true} + compressed={true} + /> + + + + + + } + aria-labelledby={`visEditorRangeTo${agg.id}_${id}`} + value={isNil(to) ? '' : to} + placeholder="+∞" + onChange={ev => 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 4deda4af3610f..75b5db992e6cc 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -230,9 +230,7 @@ "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", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4562977fbbe53..26ca24ae6804d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -210,7 +210,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.requiredRangeDescription": "必须指定至少一个范围。", "common.ui.aggTypes.ranges.requiredRangeTitle": "必需:", From 8b5ed5b0679ced73b9fc50e2b26685a4918b716c Mon Sep 17 00:00:00 2001 From: sulemanof Date: Mon, 10 Jun 2019 13:17:40 +0300 Subject: [PATCH 2/6] Remove unused translations --- src/legacy/ui/public/agg_types/controls/ranges.tsx | 7 ++----- x-pack/plugins/translations/translations/ja-JP.json | 4 ---- x-pack/plugins/translations/translations/zh-CN.json | 4 ---- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/legacy/ui/public/agg_types/controls/ranges.tsx b/src/legacy/ui/public/agg_types/controls/ranges.tsx index aa690fd10f15c..e4db2ce9a861d 100644 --- a/src/legacy/ui/public/agg_types/controls/ranges.tsx +++ b/src/legacy/ui/public/agg_types/controls/ranges.tsx @@ -90,7 +90,7 @@ function RangesParamEditor({ agg, value, setValue }: AggParamEditorProps @@ -107,10 +107,7 @@ function RangesParamEditor({ agg, value, setValue }: AggParamEditorProps - + } aria-labelledby={`visEditorRangeTo${agg.id}_${id}`} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 75b5db992e6cc..58beb46da076f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -230,10 +230,6 @@ "common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage": "{fieldParameter} は必須パラメーターです", "common.ui.aggTypes.placeMarkersOffGridLabel": "グリッド外にマーカーを配置 (ジオセントロイドを使用)", "common.ui.aggTypes.precisionLabel": "精度", - "common.ui.aggTypes.ranges.fromColumnLabel": "From", - "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 26ca24ae6804d..2b0e60430d986 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -210,10 +210,6 @@ "common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage": "{fieldParameter} 是必需字段", "common.ui.aggTypes.placeMarkersOffGridLabel": "将标记置于网格外(使用 geocentroid)", "common.ui.aggTypes.precisionLabel": "精确度", - "common.ui.aggTypes.ranges.fromColumnLabel": "从", - "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": "大小", From 27b85c498a30f21075bb550ca7348b6a3752159a Mon Sep 17 00:00:00 2001 From: sulemanof Date: Mon, 10 Jun 2019 13:55:58 +0300 Subject: [PATCH 3/6] Fix screenreader issue --- src/legacy/ui/public/agg_types/controls/ranges.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/legacy/ui/public/agg_types/controls/ranges.tsx b/src/legacy/ui/public/agg_types/controls/ranges.tsx index e4db2ce9a861d..794ccb947a6ef 100644 --- a/src/legacy/ui/public/agg_types/controls/ranges.tsx +++ b/src/legacy/ui/public/agg_types/controls/ranges.tsx @@ -34,7 +34,7 @@ import { isEqual, omit } from 'lodash'; import { AggParamEditorProps } from 'ui/vis/editors/default'; const generateId = htmlIdGenerator(); -const isNil = (value: any) => value === undefined || value === null; +const isEmpty = (value: any) => value === undefined || value === null; interface RangeValues { from?: number; @@ -96,8 +96,8 @@ function RangesParamEditor({ agg, value, setValue }: AggParamEditorProps } aria-labelledby={`visEditorRangeFrom${agg.id}_${id}`} - value={isNil(from) ? '' : from} - placeholder="-∞" + value={isEmpty(from) ? '' : from} + placeholder="−∞" onChange={ev => onChangeRange(id, 'from', ev.target.value)} fullWidth={true} compressed={true} @@ -111,7 +111,7 @@ function RangesParamEditor({ agg, value, setValue }: AggParamEditorProps } aria-labelledby={`visEditorRangeTo${agg.id}_${id}`} - value={isNil(to) ? '' : to} + value={isEmpty(to) ? '' : to} placeholder="+∞" onChange={ev => onChangeRange(id, 'to', ev.target.value)} fullWidth={true} @@ -122,7 +122,7 @@ function RangesParamEditor({ agg, value, setValue }: AggParamEditorProps Date: Mon, 10 Jun 2019 17:01:08 +0300 Subject: [PATCH 4/6] Add an initial case for safety --- src/legacy/ui/public/agg_types/controls/ranges.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/legacy/ui/public/agg_types/controls/ranges.tsx b/src/legacy/ui/public/agg_types/controls/ranges.tsx index 794ccb947a6ef..ab7b6995dc68e 100644 --- a/src/legacy/ui/public/agg_types/controls/ranges.tsx +++ b/src/legacy/ui/public/agg_types/controls/ranges.tsx @@ -45,9 +45,16 @@ interface RangeValuesModel extends RangeValues { id: string; } -function RangesParamEditor({ agg, value, setValue }: AggParamEditorProps) { +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 From 5cd68f83839bdeb8aac6a79b8fddc7c0229dcd22 Mon Sep 17 00:00:00 2001 From: sulemanof Date: Tue, 11 Jun 2019 14:44:36 +0300 Subject: [PATCH 5/6] Remove labels, add icon between ranges --- .../controls/components/from_to_list.tsx | 5 +++- .../ui/public/agg_types/controls/ranges.tsx | 28 ++++++++----------- 2 files changed, 15 insertions(+), 18 deletions(-) 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} /> + + + {ranges.map(({ from, to, id }) => ( - + - - - } - aria-labelledby={`visEditorRangeFrom${agg.id}_${id}`} + aria-label={i18n.translate('common.ui.aggTypes.ranges.fromLabel', { + defaultMessage: 'From', + })} value={isEmpty(from) ? '' : from} placeholder="−∞" onChange={ev => onChangeRange(id, 'from', ev.target.value)} @@ -110,14 +104,14 @@ function RangesParamEditor({ agg, value = [], setValue }: AggParamEditorProps + + + - - - } - aria-labelledby={`visEditorRangeTo${agg.id}_${id}`} + aria-label={i18n.translate('common.ui.aggTypes.ranges.toLabel', { + defaultMessage: 'To', + })} value={isEmpty(to) ? '' : to} placeholder="+∞" onChange={ev => onChangeRange(id, 'to', ev.target.value)} From 948fb7d0c06fc793b3f34f7f422615d1b3127788 Mon Sep 17 00:00:00 2001 From: sulemanof Date: Wed, 12 Jun 2019 16:13:03 +0300 Subject: [PATCH 6/6] Add title for delete btn --- .../ui/public/agg_types/controls/ranges.tsx | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/src/legacy/ui/public/agg_types/controls/ranges.tsx b/src/legacy/ui/public/agg_types/controls/ranges.tsx index 073c52d3dcf6b..59c7a502b3a9d 100644 --- a/src/legacy/ui/public/agg_types/controls/ranges.tsx +++ b/src/legacy/ui/public/agg_types/controls/ranges.tsx @@ -33,6 +33,9 @@ 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; @@ -89,52 +92,63 @@ function RangesParamEditor({ agg, value = [], setValue }: AggParamEditorProps - {ranges.map(({ from, to, id }) => ( - - - - onChangeRange(id, 'from', ev.target.value)} - fullWidth={true} - compressed={true} - /> - - - - - - onChangeRange(id, 'to', ev.target.value)} - fullWidth={true} - compressed={true} - /> - - - onRemoveRange(id)} - /> - - - - - ))} + {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)} + /> + + + + + ); + })}