From 59d3e0d21e6a8c86c21eade946b89cd49bca8652 Mon Sep 17 00:00:00 2001
From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com>
Date: Wed, 29 May 2019 14:25:16 +0300
Subject: [PATCH] [Vis: Default editor] EUIficate top_aggregate and size param
editors (#36567)
* EUIficate top_aggregate_and_size param editor
* Remove template
* Change typescript interfaces
* Fix browser tests
* Add an icon alert
* Changes due to comments
* Move error to a help text
* Move error to a field
* Change validation logic
* Fix discarding changes action
* Remove changed translation
---
.../agg_types/__tests__/metrics/top_hit.js | 2 +-
.../agg_types/buckets/_inline_comp_wrapper.js | 2 +-
.../ui/public/agg_types/controls/field.tsx | 8 +-
.../ui/public/agg_types/controls/order.tsx | 3 +-
.../ui/public/agg_types/controls/size.tsx | 26 +++-
.../agg_types/controls/top_aggregate.tsx | 138 ++++++++++++++++++
.../controls/top_aggregate_and_size.html | 43 ------
.../public/agg_types/controls/top_field.tsx | 40 +++++
.../ui/public/agg_types/controls/top_size.tsx | 47 ++++++
.../ui/public/agg_types/metrics/top_hit.js | 47 +++---
.../public/agg_types/param_types/select.d.ts | 8 +-
.../public/vis/editors/default/agg_param.js | 2 +
.../editors/default/agg_param_editor_props.ts | 10 +-
.../default/agg_param_react_wrapper.tsx | 17 +--
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
16 files changed, 290 insertions(+), 105 deletions(-)
create mode 100644 src/legacy/ui/public/agg_types/controls/top_aggregate.tsx
delete mode 100644 src/legacy/ui/public/agg_types/controls/top_aggregate_and_size.html
create mode 100644 src/legacy/ui/public/agg_types/controls/top_field.tsx
create mode 100644 src/legacy/ui/public/agg_types/controls/top_size.tsx
diff --git a/src/legacy/ui/public/agg_types/__tests__/metrics/top_hit.js b/src/legacy/ui/public/agg_types/__tests__/metrics/top_hit.js
index 3e1446ed1ff97..4ed86da68c408 100644
--- a/src/legacy/ui/public/agg_types/__tests__/metrics/top_hit.js
+++ b/src/legacy/ui/public/agg_types/__tests__/metrics/top_hit.js
@@ -42,7 +42,7 @@ describe('Top hit metric', function () {
value: sortOrder
};
params.aggregate = {
- val: aggregate
+ value: aggregate
};
params.size = size;
const vis = new Vis(indexPattern, {
diff --git a/src/legacy/ui/public/agg_types/buckets/_inline_comp_wrapper.js b/src/legacy/ui/public/agg_types/buckets/_inline_comp_wrapper.js
index 8091d8e28dde5..0c8161035adcf 100644
--- a/src/legacy/ui/public/agg_types/buckets/_inline_comp_wrapper.js
+++ b/src/legacy/ui/public/agg_types/buckets/_inline_comp_wrapper.js
@@ -21,7 +21,7 @@ import React from 'react';
const wrapWithInlineComp = Component => props => (
-
+
);
export { wrapWithInlineComp };
diff --git a/src/legacy/ui/public/agg_types/controls/field.tsx b/src/legacy/ui/public/agg_types/controls/field.tsx
index 254ae4fa92715..0fb44cc9f920b 100644
--- a/src/legacy/ui/public/agg_types/controls/field.tsx
+++ b/src/legacy/ui/public/agg_types/controls/field.tsx
@@ -31,12 +31,14 @@ import { FieldParamType } from '../param_types';
const label = i18n.translate('common.ui.aggTypes.field.fieldLabel', { defaultMessage: 'Field' });
interface FieldParamEditorProps extends AggParamEditorProps {
+ customError?: string;
customLabel?: string;
}
function FieldParamEditor({
agg,
aggParam,
+ customError,
customLabel,
indexedFields = [],
showValidation,
@@ -61,6 +63,10 @@ function FieldParamEditor({
};
const errors = [];
+ if (customError) {
+ errors.push(customError);
+ }
+
if (!indexedFields.length) {
errors.push(
i18n.translate('common.ui.aggTypes.field.noCompatibleFieldsDescription', {
@@ -75,7 +81,7 @@ function FieldParamEditor({
setTouched();
}
- const isValid = !!value && !!indexedFields.length;
+ const isValid = !!value && !errors.length;
useEffect(
() => {
diff --git a/src/legacy/ui/public/agg_types/controls/order.tsx b/src/legacy/ui/public/agg_types/controls/order.tsx
index 0042c3d97d0ba..a122ad4c0543d 100644
--- a/src/legacy/ui/public/agg_types/controls/order.tsx
+++ b/src/legacy/ui/public/agg_types/controls/order.tsx
@@ -30,6 +30,7 @@ function OrderParamEditor({
setValue,
setValidity,
setTouched,
+ wrappedWithInlineComp,
}: AggParamEditorProps & SelectParamEditorProps) {
const label = i18n.translate('common.ui.aggTypes.orderLabel', {
defaultMessage: 'Order',
@@ -48,7 +49,7 @@ function OrderParamEditor({
label={label}
fullWidth={true}
isInvalid={showValidation ? !isValid : false}
- className="visEditorSidebar__aggParamFormRow"
+ className={wrappedWithInlineComp ? undefined : 'visEditorSidebar__aggParamFormRow'}
>
{
+ iconTip?: React.ReactNode;
+ disabled?: boolean;
+}
function SizeParamEditor({
+ disabled,
+ iconTip,
value,
setValue,
showValidation,
setValidity,
setTouched,
-}: AggParamEditorProps) {
- const label = i18n.translate('common.ui.aggTypes.sizeLabel', {
- defaultMessage: 'Size',
- });
- const isValid = Number(value) > 0;
+ wrappedWithInlineComp,
+}: SizeParamEditorProps) {
+ const label = (
+ <>
+
+ {iconTip}
+ >
+ );
+ const isValid = disabled || Number(value) > 0;
useEffect(
() => {
@@ -47,7 +58,7 @@ function SizeParamEditor({
label={label}
fullWidth={true}
isInvalid={showValidation ? !isValid : false}
- className="visEditorSidebar__aggParamFormRow"
+ className={wrappedWithInlineComp ? undefined : 'visEditorSidebar__aggParamFormRow'}
>
diff --git a/src/legacy/ui/public/agg_types/controls/top_aggregate.tsx b/src/legacy/ui/public/agg_types/controls/top_aggregate.tsx
new file mode 100644
index 0000000000000..6416ab96ffac1
--- /dev/null
+++ b/src/legacy/ui/public/agg_types/controls/top_aggregate.tsx
@@ -0,0 +1,138 @@
+/*
+ * 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, { useEffect, useRef } from 'react';
+import { EuiFormRow, EuiIconTip, EuiSelect } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { AggParamEditorProps } from 'ui/vis/editors/default';
+import { AggConfig } from 'ui/vis';
+import { AggParam } from '../agg_param';
+import { SelectValueProp, SelectParamEditorProps } from '../param_types/select';
+
+interface AggregateValueProp extends SelectValueProp {
+ isCompatibleType(filedType: string): boolean;
+ isCompatibleVis(visName: string): boolean;
+}
+
+function getCompatibleAggs(agg: AggConfig, visName: string): AggregateValueProp[] {
+ const fieldType = agg.params.field && agg.params.field.type;
+ const { options = [] } = agg.getAggParams().find(({ name }: AggParam) => name === 'aggregate');
+ return options.filter(
+ (option: AggregateValueProp) =>
+ fieldType && option.isCompatibleType(fieldType) && option.isCompatibleVis(visName)
+ );
+}
+
+function TopAggregateParamEditor({
+ agg,
+ aggParam,
+ value,
+ visName,
+ showValidation,
+ setValue,
+ setValidity,
+ setTouched,
+ wrappedWithInlineComp,
+}: AggParamEditorProps & SelectParamEditorProps) {
+ const isFirstRun = useRef(true);
+ const fieldType = agg.params.field && agg.params.field.type;
+ const emptyValue = { text: '', value: 'EMPTY_VALUE', disabled: true, hidden: true };
+ const filteredOptions = getCompatibleAggs(agg, visName)
+ .map(({ text, value: val }) => ({ text, value: val }))
+ .sort((a, b) => a.text.toLowerCase().localeCompare(b.text.toLowerCase()));
+ const options = [emptyValue, ...filteredOptions];
+ const disabled = fieldType && !filteredOptions.length;
+ const isValid = disabled || !!value;
+
+ const label = (
+ <>
+ {' '}
+
+ >
+ );
+
+ useEffect(
+ () => {
+ setValidity(isValid);
+ },
+ [isValid]
+ );
+
+ useEffect(
+ () => {
+ if (isFirstRun.current) {
+ isFirstRun.current = false;
+ return;
+ }
+
+ if (value) {
+ if (aggParam.options.byValue[value.value]) {
+ return;
+ }
+
+ setValue();
+ }
+
+ if (filteredOptions.length === 1) {
+ setValue(aggParam.options.byValue[filteredOptions[0].value]);
+ }
+ },
+ [fieldType, visName]
+ );
+
+ const handleChange = (event: React.ChangeEvent) => {
+ if (event.target.value === emptyValue.value) {
+ setValue();
+ } else {
+ setValue(aggParam.options.byValue[event.target.value]);
+ }
+ };
+
+ return (
+
+
+
+ );
+}
+
+export { TopAggregateParamEditor, getCompatibleAggs };
diff --git a/src/legacy/ui/public/agg_types/controls/top_aggregate_and_size.html b/src/legacy/ui/public/agg_types/controls/top_aggregate_and_size.html
deleted file mode 100644
index c96f7570081e1..0000000000000
--- a/src/legacy/ui/public/agg_types/controls/top_aggregate_and_size.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
diff --git a/src/legacy/ui/public/agg_types/controls/top_field.tsx b/src/legacy/ui/public/agg_types/controls/top_field.tsx
new file mode 100644
index 0000000000000..9069f081ccf99
--- /dev/null
+++ b/src/legacy/ui/public/agg_types/controls/top_field.tsx
@@ -0,0 +1,40 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { AggParamEditorProps } from '../../vis/editors/default';
+import { FieldParamType } from '../param_types';
+import { FieldParamEditor } from './field';
+import { getCompatibleAggs } from './top_aggregate';
+
+function TopFieldParamEditor(props: AggParamEditorProps) {
+ const compatibleAggs = getCompatibleAggs(props.agg, props.visName);
+ let customError;
+
+ if (!compatibleAggs.length) {
+ customError = i18n.translate('common.ui.aggTypes.aggregateWith.noAggsErrorTooltip', {
+ defaultMessage: 'The chosen field has no compatible aggregations.',
+ });
+ }
+
+ return ;
+}
+
+export { TopFieldParamEditor };
diff --git a/src/legacy/ui/public/agg_types/controls/top_size.tsx b/src/legacy/ui/public/agg_types/controls/top_size.tsx
new file mode 100644
index 0000000000000..513b4681ecc74
--- /dev/null
+++ b/src/legacy/ui/public/agg_types/controls/top_size.tsx
@@ -0,0 +1,47 @@
+/*
+ * 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 { AggParamEditorProps } from 'ui/vis/editors/default';
+import { EuiIconTip } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { SizeParamEditor } from './size';
+import { getCompatibleAggs } from './top_aggregate';
+
+function TopSizeParamEditor(props: AggParamEditorProps) {
+ const iconTip = (
+ <>
+ {' '}
+
+ >
+ );
+ const fieldType = props.agg.params.field && props.agg.params.field.type;
+ const disabled = fieldType && !getCompatibleAggs(props.agg, props.visName).length;
+
+ return ;
+}
+
+export { TopSizeParamEditor };
diff --git a/src/legacy/ui/public/agg_types/metrics/top_hit.js b/src/legacy/ui/public/agg_types/metrics/top_hit.js
index bedac2a82c157..602df5fb6bc8b 100644
--- a/src/legacy/ui/public/agg_types/metrics/top_hit.js
+++ b/src/legacy/ui/public/agg_types/metrics/top_hit.js
@@ -19,11 +19,14 @@
import _ from 'lodash';
import { MetricAggType } from './metric_agg_type';
-import aggregateAndSizeEditor from '../controls/top_aggregate_and_size.html';
import { TopSortFieldParamEditor } from '../controls/top_sort_field';
import { OrderParamEditor } from '../controls/order';
import { aggTypeFieldFilters } from '../param_types/filter';
import { i18n } from '@kbn/i18n';
+import { wrapWithInlineComp } from '../buckets/_inline_comp_wrapper';
+import { TopFieldParamEditor } from '../controls/top_field';
+import { TopSizeParamEditor } from '../controls/top_size';
+import { TopAggregateParamEditor } from '../controls/top_aggregate';
const isNumber = function (type) {
return type === 'number';
@@ -66,6 +69,7 @@ export const topHitMetricAgg = new MetricAggType({
{
name: 'field',
type: 'field',
+ editorComponent: TopFieldParamEditor,
onlyAggregatable: false,
filterFieldTypes: '*',
write(agg, output) {
@@ -95,47 +99,47 @@ export const topHitMetricAgg = new MetricAggType({
},
{
name: 'aggregate',
- type: 'optioned',
- editor: aggregateAndSizeEditor,
+ type: 'select',
+ editorComponent: wrapWithInlineComp(TopAggregateParamEditor),
options: [
{
- display: i18n.translate('common.ui.aggTypes.metrics.topHit.minLabel', {
+ text: i18n.translate('common.ui.aggTypes.metrics.topHit.minLabel', {
defaultMessage: 'Min'
}),
isCompatibleType: isNumber,
isCompatibleVis: _.constant(true),
disabled: true,
- val: 'min'
+ value: 'min'
},
{
- display: i18n.translate('common.ui.aggTypes.metrics.topHit.maxLabel', {
+ text: i18n.translate('common.ui.aggTypes.metrics.topHit.maxLabel', {
defaultMessage: 'Max'
}),
isCompatibleType: isNumber,
isCompatibleVis: _.constant(true),
disabled: true,
- val: 'max'
+ value: 'max'
},
{
- display: i18n.translate('common.ui.aggTypes.metrics.topHit.sumLabel', {
+ text: i18n.translate('common.ui.aggTypes.metrics.topHit.sumLabel', {
defaultMessage: 'Sum'
}),
isCompatibleType: isNumber,
isCompatibleVis: _.constant(true),
disabled: true,
- val: 'sum'
+ value: 'sum'
},
{
- display: i18n.translate('common.ui.aggTypes.metrics.topHit.averageLabel', {
+ text: i18n.translate('common.ui.aggTypes.metrics.topHit.averageLabel', {
defaultMessage: 'Average'
}),
isCompatibleType: isNumber,
isCompatibleVis: _.constant(true),
disabled: true,
- val: 'average'
+ value: 'average'
},
{
- display: i18n.translate('common.ui.aggTypes.metrics.topHit.concatenateLabel', {
+ text: i18n.translate('common.ui.aggTypes.metrics.topHit.concatenateLabel', {
defaultMessage: 'Concatenate'
}),
isCompatibleType: _.constant(true),
@@ -143,27 +147,14 @@ export const topHitMetricAgg = new MetricAggType({
return name === 'metric' || name === 'table';
},
disabled: true,
- val: 'concat'
+ value: 'concat'
}
],
- controller: function ($scope) {
- $scope.options = [];
- $scope.$watchGroup([ 'vis.type.name', 'agg.params.field.type' ], function ([ visName, fieldType ]) {
- if (fieldType && visName) {
- $scope.options = _.filter($scope.aggParam.options, option => {
- return option.isCompatibleVis(visName) && option.isCompatibleType(fieldType);
- });
- if ($scope.options.length === 1) {
- $scope.agg.params.aggregate = $scope.options[0];
- }
- }
- });
- },
write: _.noop
},
{
name: 'size',
- editor: null, // size setting is done together with the aggregation setting
+ editorComponent: wrapWithInlineComp(TopSizeParamEditor),
default: 1
},
{
@@ -245,7 +236,7 @@ export const topHitMetricAgg = new MetricAggType({
if (!_.compact(values).length) {
return null;
}
- switch (agg.params.aggregate.val) {
+ switch (agg.params.aggregate.value) {
case 'max':
return _.max(values);
case 'min':
diff --git a/src/legacy/ui/public/agg_types/param_types/select.d.ts b/src/legacy/ui/public/agg_types/param_types/select.d.ts
index 06aeb0b37e066..d2f26b34ade2e 100644
--- a/src/legacy/ui/public/agg_types/param_types/select.d.ts
+++ b/src/legacy/ui/public/agg_types/param_types/select.d.ts
@@ -24,15 +24,15 @@ interface SelectValueProp {
text: string;
}
-interface SelectOptions extends IndexedArray {
+interface SelectOptions extends IndexedArray {
byValue: {
- [key: string]: SelectValueProp;
+ [key: string]: T;
};
}
-interface SelectParamEditorProps {
+interface SelectParamEditorProps {
aggParam: {
- options: SelectOptions;
+ options: SelectOptions;
};
}
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 cc5a8f2ec05fc..4640dbe08c004 100644
--- a/src/legacy/ui/public/vis/editors/default/agg_param.js
+++ b/src/legacy/ui/public/vis/editors/default/agg_param.js
@@ -36,6 +36,7 @@ uiModules
['setValidity', { watchDepth: 'reference' }],
'showValidation',
'value',
+ 'visName'
]))
.directive('visAggParamEditor', function (config) {
return {
@@ -64,6 +65,7 @@ uiModules
indexed-fields="indexedFields"
show-validation="showValidation"
value="paramValue"
+ vis-name="vis.type.name"
on-change="onChange"
set-touched="setTouched"
set-validity="setValidity"
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
index 0b65e1a631a63..b696ffe40965a 100644
--- 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
@@ -26,7 +26,8 @@ import { EditorConfig } from '../config/types';
// as there is currently a bug on babel typescript transform plugin for it
// https://github.com/babel/babel/issues/7641
//
-export interface AggParamEditorProps {
+
+export interface AggParamCommonProps {
agg: AggConfig;
aggParam: AggParam;
config: any;
@@ -34,7 +35,12 @@ export interface AggParamEditorProps {
indexedFields?: FieldParamType[];
showValidation: boolean;
value: T;
+ visName: string;
setValidity(isValid: boolean): void;
- setValue(value?: T): void;
setTouched(): void;
}
+
+export interface AggParamEditorProps extends AggParamCommonProps {
+ wrappedWithInlineComp?: boolean;
+ setValue(value?: T): void;
+}
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
index ebc9a8ff253b4..eb694385f3be8 100644
--- 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
@@ -19,24 +19,11 @@
import React, { useEffect } from 'react';
-import { AggParam } from '../../../agg_types';
-import { FieldParamType } from '../../../agg_types/param_types';
-import { AggConfig } from '../../agg_config';
-import { AggParamEditorProps } from './agg_param_editor_props';
-import { EditorConfig } from '../config/types';
+import { AggParamEditorProps, AggParamCommonProps } from './agg_param_editor_props';
-interface AggParamReactWrapperProps {
- agg: AggConfig;
- aggParam: AggParam;
- config: any;
- editorConfig: EditorConfig;
- indexedFields: FieldParamType[];
- showValidation: boolean;
+interface AggParamReactWrapperProps extends AggParamCommonProps {
paramEditor: React.FunctionComponent>;
- value: T;
onChange(value?: T): void;
- setTouched(): void;
- setValidity(isValid: boolean): void;
}
function AggParamReactWrapper(props: AggParamReactWrapperProps) {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 57a9137e9e770..3e15b1df6d743 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -80,7 +80,6 @@
"common.ui.aggResponse.fieldLabel": "フィールド",
"common.ui.aggResponse.valueLabel": "値",
"common.ui.aggTypes.aggregateWithLabel": "集約基準",
- "common.ui.aggTypes.aggregateWithTooltip": "複数ヒットまたは複数値のフィールドを 1 つのメトリックにまとめる方法を選択してください",
"common.ui.aggTypes.buckets.dateHistogramLabel": "{intervalDescription} ごとに {fieldName}",
"common.ui.aggTypes.buckets.dateHistogramTitle": "Date Histogram",
"common.ui.aggTypes.buckets.dateRangeTitle": "日付範囲",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 913f51b63a336..86def61ba7415 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -80,7 +80,6 @@
"common.ui.aggResponse.fieldLabel": "字段",
"common.ui.aggResponse.valueLabel": "值",
"common.ui.aggTypes.aggregateWithLabel": "聚合对象",
- "common.ui.aggTypes.aggregateWithTooltip": "选择将多个命中或多值字段组合成单个指标的策略",
"common.ui.aggTypes.buckets.dateHistogramLabel": "{fieldName}/{intervalDescription}",
"common.ui.aggTypes.buckets.dateHistogramTitle": "日期直方图",
"common.ui.aggTypes.buckets.dateRangeTitle": "日期范围",