Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Maps] convert MetricsEditor to TS #76727

Merged
merged 4 commits into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/public/components/_index.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import 'action_select';
@import 'metric_editors';
@import 'metrics_editor/metric_editors';
@import './geometry_filter';
@import 'tooltip_selector/tooltip_selector';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions x-pack/plugins/maps/public/components/metrics_editor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { MetricsEditor } from './metrics_editor';
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import React, { ChangeEvent, Fragment } from 'react';
import { i18n } from '@kbn/i18n';

import { EuiFieldText, EuiFormRow } from '@elastic/eui';
import { EuiButtonEmpty, EuiComboBoxOptionOption, EuiFieldText, EuiFormRow } from '@elastic/eui';

import { MetricSelect, METRIC_AGGREGATION_VALUES } from './metric_select';
import { SingleFieldSelect } from './single_field_select';
import { AGG_TYPE } from '../../common/constants';
import { getTermsFields } from '../index_pattern_util';
import { FormattedMessage } from '@kbn/i18n/react';
import { MetricSelect } from './metric_select';
import { SingleFieldSelect } from '../single_field_select';
import { AggDescriptor } from '../../../common/descriptor_types';
import { AGG_TYPE } from '../../../common/constants';
import { getTermsFields } from '../../index_pattern_util';
import { IFieldType } from '../../../../../../src/plugins/data/public';

function filterFieldsForAgg(fields, aggType) {
function filterFieldsForAgg(fields: IFieldType[], aggType: AGG_TYPE) {
if (!fields) {
return [];
}
Expand All @@ -34,8 +36,27 @@ function filterFieldsForAgg(fields, aggType) {
});
}

export function MetricEditor({ fields, metricsFilter, metric, onChange, removeButton }) {
const onAggChange = (metricAggregationType) => {
interface Props {
metric: AggDescriptor;
fields: IFieldType[];
onChange: (metric: AggDescriptor) => void;
onRemove: () => void;
metricsFilter?: (metricOption: EuiComboBoxOptionOption<AGG_TYPE>) => boolean;
showRemoveButton: boolean;
}

export function MetricEditor({
fields,
metricsFilter,
metric,
onChange,
showRemoveButton,
onRemove,
}: Props) {
const onAggChange = (metricAggregationType?: AGG_TYPE) => {
if (!metricAggregationType) {
return;
}
const newMetricProps = {
...metric,
type: metricAggregationType,
Expand All @@ -54,13 +75,16 @@ export function MetricEditor({ fields, metricsFilter, metric, onChange, removeBu

onChange(newMetricProps);
};
const onFieldChange = (fieldName) => {
const onFieldChange = (fieldName?: string) => {
if (!fieldName) {
return;
}
onChange({
...metric,
field: fieldName,
});
};
const onLabelChange = (e) => {
const onLabelChange = (e: ChangeEvent<HTMLInputElement>) => {
onChange({
...metric,
label: e.target.value,
Expand All @@ -80,7 +104,7 @@ export function MetricEditor({ fields, metricsFilter, metric, onChange, removeBu
placeholder={i18n.translate('xpack.maps.metricsEditor.selectFieldPlaceholder', {
defaultMessage: 'Select field',
})}
value={metric.field}
value={metric.field ? metric.field : null}
onChange={onFieldChange}
fields={filterFieldsForAgg(fields, metric.type)}
isClearable={false}
Expand Down Expand Up @@ -108,6 +132,28 @@ export function MetricEditor({ fields, metricsFilter, metric, onChange, removeBu
);
}

let removeButton;
if (showRemoveButton) {
removeButton = (
<div className="mapMetricEditorPanel__metricRemoveButton">
<EuiButtonEmpty
iconType="trash"
size="xs"
color="danger"
onClick={onRemove}
aria-label={i18n.translate('xpack.maps.metricsEditor.deleteMetricAriaLabel', {
defaultMessage: 'Delete metric',
})}
>
<FormattedMessage
id="xpack.maps.metricsEditor.deleteMetricButtonLabel"
defaultMessage="Delete metric"
/>
</EuiButtonEmpty>
</div>
);
}

return (
<Fragment>
<EuiFormRow
Expand All @@ -130,14 +176,3 @@ export function MetricEditor({ fields, metricsFilter, metric, onChange, removeBu
</Fragment>
);
}

MetricEditor.propTypes = {
metric: PropTypes.shape({
type: PropTypes.oneOf(METRIC_AGGREGATION_VALUES),
field: PropTypes.string,
label: PropTypes.string,
}),
fields: PropTypes.array,
onChange: PropTypes.func.isRequired,
metricsFilter: PropTypes.func,
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
*/

import React from 'react';
import PropTypes from 'prop-types';
import { i18n } from '@kbn/i18n';
import { EuiComboBox } from '@elastic/eui';
import { AGG_TYPE } from '../../common/constants';
import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui';
import { AGG_TYPE } from '../../../common/constants';

const AGG_OPTIONS = [
{
Expand Down Expand Up @@ -55,17 +54,19 @@ const AGG_OPTIONS = [
},
];

export const METRIC_AGGREGATION_VALUES = AGG_OPTIONS.map(({ value }) => {
return value;
});
type Props = Omit<EuiComboBoxProps<AGG_TYPE>, 'onChange'> & {
value: AGG_TYPE;
onChange: (aggType: AGG_TYPE) => void;
metricsFilter?: (metricOption: EuiComboBoxOptionOption<AGG_TYPE>) => boolean;
};

export function MetricSelect({ value, onChange, metricsFilter, ...rest }) {
function onAggChange(selectedOptions) {
export function MetricSelect({ value, onChange, metricsFilter, ...rest }: Props) {
function onAggChange(selectedOptions: Array<EuiComboBoxOptionOption<AGG_TYPE>>) {
if (selectedOptions.length === 0) {
return;
}

const aggType = selectedOptions[0].value;
const aggType = selectedOptions[0].value!;
onChange(aggType);
}

Expand All @@ -87,9 +88,3 @@ export function MetricSelect({ value, onChange, metricsFilter, ...rest }) {
/>
);
}

MetricSelect.propTypes = {
metricsFilter: PropTypes.func,
value: PropTypes.oneOf(METRIC_AGGREGATION_VALUES),
onChange: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { MetricsEditor } from './metrics_editor';
import { AGG_TYPE } from '../../common/constants';
import { AGG_TYPE } from '../../../common/constants';

const defaultProps = {
metrics: [
Expand All @@ -19,15 +19,14 @@ const defaultProps = {
fields: [],
onChange: () => {},
allowMultipleMetrics: true,
metricsFilter: () => {},
};

test('should render metrics editor', async () => {
test('should render metrics editor', () => {
const component = shallow(<MetricsEditor {...defaultProps} />);
expect(component).toMatchSnapshot();
});

test('should add default count metric when metrics is empty array', async () => {
test('should add default count metric when metrics is empty array', () => {
const component = shallow(<MetricsEditor {...defaultProps} metrics={[]} />);
expect(component).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,60 @@
*/

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButtonEmpty, EuiSpacer, EuiTextAlign } from '@elastic/eui';
import { EuiButtonEmpty, EuiComboBoxOptionOption, EuiSpacer, EuiTextAlign } from '@elastic/eui';
import { MetricEditor } from './metric_editor';
import { DEFAULT_METRIC } from '../classes/sources/es_agg_source';
// @ts-expect-error
import { DEFAULT_METRIC } from '../../classes/sources/es_agg_source';
import { IFieldType } from '../../../../../../src/plugins/data/public';
import { AggDescriptor } from '../../../common/descriptor_types';
import { AGG_TYPE } from '../../../common/constants';

export function MetricsEditor({ fields, metrics, onChange, allowMultipleMetrics, metricsFilter }) {
interface Props {
allowMultipleMetrics: boolean;
metrics: AggDescriptor[];
fields: IFieldType[];
onChange: (metrics: AggDescriptor[]) => void;
metricsFilter?: (metricOption: EuiComboBoxOptionOption<AGG_TYPE>) => boolean;
}

export function MetricsEditor({
fields,
metrics = [DEFAULT_METRIC],
onChange,
allowMultipleMetrics = true,
metricsFilter,
}: Props) {
function renderMetrics() {
// There was a bug in 7.8 that initialized metrics to [].
// This check is needed to handle any saved objects created before the bug was patched.
const nonEmptyMetrics = metrics.length === 0 ? [DEFAULT_METRIC] : metrics;
return nonEmptyMetrics.map((metric, index) => {
const onMetricChange = (metric) => {
onChange([...metrics.slice(0, index), metric, ...metrics.slice(index + 1)]);
const onMetricChange = (updatedMetric: AggDescriptor) => {
onChange([...metrics.slice(0, index), updatedMetric, ...metrics.slice(index + 1)]);
};

const onRemove = () => {
onChange([...metrics.slice(0, index), ...metrics.slice(index + 1)]);
};

let removeButton;
if (index > 0) {
removeButton = (
<div className="mapMetricEditorPanel__metricRemoveButton">
<EuiButtonEmpty
iconType="trash"
size="xs"
color="danger"
onClick={onRemove}
aria-label={i18n.translate('xpack.maps.metricsEditor.deleteMetricAriaLabel', {
defaultMessage: 'Delete metric',
})}
>
<FormattedMessage
id="xpack.maps.metricsEditor.deleteMetricButtonLabel"
defaultMessage="Delete metric"
/>
</EuiButtonEmpty>
</div>
);
}
return (
<div key={index} className="mapMetricEditorPanel__metricEditor">
<MetricEditor
onChange={onMetricChange}
metric={metric}
fields={fields}
metricsFilter={metricsFilter}
removeButton={removeButton}
showRemoveButton={index > 0}
onRemove={onRemove}
/>
</div>
);
});
}

function addMetric() {
onChange([...metrics, {}]);
onChange([...metrics, { type: AGG_TYPE.AVG }]);
}

function renderAddMetricButton() {
Expand All @@ -71,7 +67,7 @@ export function MetricsEditor({ fields, metrics, onChange, allowMultipleMetrics,
}

return (
<>
<Fragment>
<EuiSpacer size="xs" />
<EuiTextAlign textAlign="center">
<EuiButtonEmpty onClick={addMetric} size="xs" iconType="plusInCircleFilled">
Expand All @@ -81,7 +77,7 @@ export function MetricsEditor({ fields, metrics, onChange, allowMultipleMetrics,
/>
</EuiButtonEmpty>
</EuiTextAlign>
</>
</Fragment>
);
}

Expand All @@ -93,16 +89,3 @@ export function MetricsEditor({ fields, metrics, onChange, allowMultipleMetrics,
</Fragment>
);
}

MetricsEditor.propTypes = {
metrics: PropTypes.array,
fields: PropTypes.array,
onChange: PropTypes.func.isRequired,
allowMultipleMetrics: PropTypes.bool,
metricsFilter: PropTypes.func,
};

MetricsEditor.defaultProps = {
metrics: [DEFAULT_METRIC],
allowMultipleMetrics: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

jest.mock('../../../../components/metric_editor', () => ({
MetricsEditor: () => {
return <div>mockMetricsEditor</div>;
},
}));

import React from 'react';
import { shallow } from 'enzyme';
import { MetricsExpression } from './metrics_expression';
Expand Down