Clean up TSVB type client code to conform to the schema (#68519)

* Clean up TSVB type client code to conform to the schema

Part of #57342

* Replace FieldDescriptor with IFieldType, add UIRestrictions interface

* Replace expect from chai with @kbn/expect, remove unnecessary type

* Add TimeseriesUIRestrictions type and refactor add_delete_buttons.test

* Replace some types with MetricsItemsSchema['values'] to avoid duplications

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Diana Derevyankina 2020-06-25 10:22:13 +03:00 committed by GitHub
parent 42b87c0154
commit 2b72de5231
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 369 additions and 254 deletions

View file

@ -0,0 +1,25 @@
/*
* 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 { TypeOf } from '@kbn/config-schema';
import { metricsItems, panel, seriesItems } from './vis_schema';
export type SeriesItemsSchema = TypeOf<typeof seriesItems>;
export type MetricsItemsSchema = TypeOf<typeof metricsItems>;
export type PanelSchema = TypeOf<typeof panel>;

View file

@ -22,21 +22,30 @@
* @constant
* @public
*/
export const RESTRICTIONS_KEYS = {
export enum RESTRICTIONS_KEYS {
/**
* Key for getting the white listed group by fields from the UIRestrictions object.
*/
WHITE_LISTED_GROUP_BY_FIELDS: 'whiteListedGroupByFields',
WHITE_LISTED_GROUP_BY_FIELDS = 'whiteListedGroupByFields',
/**
* Key for getting the white listed metrics from the UIRestrictions object.
*/
WHITE_LISTED_METRICS: 'whiteListedMetrics',
WHITE_LISTED_METRICS = 'whiteListedMetrics',
/**
* Key for getting the white listed Time Range modes from the UIRestrictions object.
*/
WHITE_LISTED_TIMERANGE_MODES: 'whiteListedTimerangeModes',
WHITE_LISTED_TIMERANGE_MODES = 'whiteListedTimerangeModes',
}
export interface UIRestrictions {
'*': boolean;
[restriction: string]: boolean;
}
export type TimeseriesUIRestrictions = {
[key in RESTRICTIONS_KEYS]: Record<string, UIRestrictions>;
};
/**
@ -44,6 +53,6 @@ export const RESTRICTIONS_KEYS = {
* @constant
* @public
*/
export const DEFAULT_UI_RESTRICTION = {
export const DEFAULT_UI_RESTRICTION: UIRestrictions = {
'*': true,
};

View file

@ -76,7 +76,7 @@ const gaugeColorRulesItems = schema.object({
operator: stringOptionalNullable,
value: schema.maybe(schema.nullable(schema.number())),
});
const metricsItems = schema.object({
export const metricsItems = schema.object({
field: stringOptionalNullable,
id: stringRequired,
metric_agg: stringOptionalNullable,
@ -133,7 +133,7 @@ const splitFiltersItems = schema.object({
label: stringOptionalNullable,
});
const seriesItems = schema.object({
export const seriesItems = schema.object({
aggregate_by: stringOptionalNullable,
aggregate_function: stringOptionalNullable,
axis_position: stringRequired,
@ -195,66 +195,66 @@ const seriesItems = schema.object({
var_name: stringOptionalNullable,
});
export const panel = schema.object({
annotations: schema.maybe(schema.arrayOf(annotationsItems)),
axis_formatter: stringRequired,
axis_position: stringRequired,
axis_scale: stringRequired,
axis_min: stringOrNumberOptionalNullable,
axis_max: stringOrNumberOptionalNullable,
bar_color_rules: schema.maybe(arrayNullable),
background_color: stringOptionalNullable,
background_color_rules: schema.maybe(schema.arrayOf(backgroundColorRulesItems)),
default_index_pattern: stringOptionalNullable,
default_timefield: stringOptionalNullable,
drilldown_url: stringOptionalNullable,
drop_last_bucket: numberIntegerOptional,
filter: schema.nullable(
schema.oneOf([
stringOptionalNullable,
schema.object({
language: stringOptionalNullable,
query: stringOptionalNullable,
}),
])
),
gauge_color_rules: schema.maybe(schema.arrayOf(gaugeColorRulesItems)),
gauge_width: schema.nullable(schema.oneOf([stringOptionalNullable, numberOptional])),
gauge_inner_color: stringOptionalNullable,
gauge_inner_width: stringOrNumberOptionalNullable,
gauge_style: stringOptionalNullable,
gauge_max: stringOrNumberOptionalNullable,
id: stringRequired,
ignore_global_filters: numberOptional,
ignore_global_filter: numberOptional,
index_pattern: stringRequired,
interval: stringRequired,
isModelInvalid: schema.maybe(schema.boolean()),
legend_position: stringOptionalNullable,
markdown: stringOptionalNullable,
markdown_scrollbars: numberIntegerOptional,
markdown_openLinksInNewTab: numberIntegerOptional,
markdown_vertical_align: stringOptionalNullable,
markdown_less: stringOptionalNullable,
markdown_css: stringOptionalNullable,
pivot_id: stringOptionalNullable,
pivot_label: stringOptionalNullable,
pivot_type: stringOptionalNullable,
pivot_rows: stringOptionalNullable,
series: schema.arrayOf(seriesItems),
show_grid: numberIntegerRequired,
show_legend: numberIntegerRequired,
tooltip_mode: schema.maybe(
schema.oneOf([schema.literal('show_all'), schema.literal('show_focused')])
),
time_field: stringOptionalNullable,
time_range_mode: stringOptionalNullable,
type: stringRequired,
});
export const visPayloadSchema = schema.object({
filters: arrayNullable,
panels: schema.arrayOf(
schema.object({
annotations: schema.maybe(schema.arrayOf(annotationsItems)),
axis_formatter: stringRequired,
axis_position: stringRequired,
axis_scale: stringRequired,
axis_min: stringOrNumberOptionalNullable,
axis_max: stringOrNumberOptionalNullable,
bar_color_rules: schema.maybe(arrayNullable),
background_color: stringOptionalNullable,
background_color_rules: schema.maybe(schema.arrayOf(backgroundColorRulesItems)),
default_index_pattern: stringOptionalNullable,
default_timefield: stringOptionalNullable,
drilldown_url: stringOptionalNullable,
drop_last_bucket: numberIntegerOptional,
filter: schema.nullable(
schema.oneOf([
stringOptionalNullable,
schema.object({
language: stringOptionalNullable,
query: stringOptionalNullable,
}),
])
),
gauge_color_rules: schema.maybe(schema.arrayOf(gaugeColorRulesItems)),
gauge_width: schema.nullable(schema.oneOf([stringOptionalNullable, numberOptional])),
gauge_inner_color: stringOptionalNullable,
gauge_inner_width: stringOrNumberOptionalNullable,
gauge_style: stringOptionalNullable,
gauge_max: stringOrNumberOptionalNullable,
id: stringRequired,
ignore_global_filters: numberOptional,
ignore_global_filter: numberOptional,
index_pattern: stringRequired,
interval: stringRequired,
isModelInvalid: schema.maybe(schema.boolean()),
legend_position: stringOptionalNullable,
markdown: stringOptionalNullable,
markdown_scrollbars: numberIntegerOptional,
markdown_openLinksInNewTab: numberIntegerOptional,
markdown_vertical_align: stringOptionalNullable,
markdown_less: stringOptionalNullable,
markdown_css: stringOptionalNullable,
pivot_id: stringOptionalNullable,
pivot_label: stringOptionalNullable,
pivot_type: stringOptionalNullable,
pivot_rows: stringOptionalNullable,
series: schema.arrayOf(seriesItems),
show_grid: numberIntegerRequired,
show_legend: numberIntegerRequired,
tooltip_mode: schema.maybe(
schema.oneOf([schema.literal('show_all'), schema.literal('show_focused')])
),
time_field: stringOptionalNullable,
time_range_mode: stringOptionalNullable,
type: stringRequired,
})
),
panels: schema.arrayOf(panel),
// general
query: schema.nullable(schema.arrayOf(queryObject)),
state: schema.object({

View file

@ -18,51 +18,49 @@
*/
import React from 'react';
import { expect } from 'chai';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import sinon from 'sinon';
import { AddDeleteButtons } from './add_delete_buttons';
describe('AddDeleteButtons', () => {
it('calls onAdd={handleAdd}', () => {
const handleAdd = sinon.spy();
const handleAdd = jest.fn();
const wrapper = shallowWithIntl(<AddDeleteButtons onAdd={handleAdd} />);
wrapper.find('EuiButtonIcon').at(0).simulate('click');
expect(handleAdd.calledOnce).to.equal(true);
expect(handleAdd).toHaveBeenCalled();
});
it('calls onDelete={handleDelete}', () => {
const handleDelete = sinon.spy();
const handleDelete = jest.fn();
const wrapper = shallowWithIntl(<AddDeleteButtons onDelete={handleDelete} />);
wrapper.find('EuiButtonIcon').at(1).simulate('click');
expect(handleDelete.calledOnce).to.equal(true);
expect(handleDelete).toHaveBeenCalled();
});
it('calls onClone={handleClone}', () => {
const handleClone = sinon.spy();
const handleClone = jest.fn();
const wrapper = shallowWithIntl(<AddDeleteButtons onClone={handleClone} />);
wrapper.find('EuiButtonIcon').at(0).simulate('click');
expect(handleClone.calledOnce).to.equal(true);
expect(handleClone).toHaveBeenCalled();
});
it('disableDelete={true}', () => {
const wrapper = shallowWithIntl(<AddDeleteButtons disableDelete={true} />);
expect(wrapper.find({ text: 'Delete' })).to.have.length(0);
expect(wrapper.find({ text: 'Delete' })).toHaveLength(0);
});
it('disableAdd={true}', () => {
const wrapper = shallowWithIntl(<AddDeleteButtons disableAdd={true} />);
expect(wrapper.find({ text: 'Add' })).to.have.length(0);
expect(wrapper.find({ text: 'Add' })).toHaveLength(0);
});
it('should not display clone by default', () => {
const wrapper = shallowWithIntl(<AddDeleteButtons />);
expect(wrapper.find({ text: 'Clone' })).to.have.length(0);
expect(wrapper.find({ text: 'Clone' })).toHaveLength(0);
});
it('should not display clone when disableAdd={true}', () => {
const fn = sinon.spy();
const fn = jest.fn();
const wrapper = shallowWithIntl(<AddDeleteButtons onClone={fn} disableAdd={true} />);
expect(wrapper.find({ text: 'Clone' })).to.have.length(0);
expect(wrapper.find({ text: 'Clone' })).toHaveLength(0);
});
});

View file

@ -17,13 +17,29 @@
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import { EuiToolTip, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { MouseEvent } from 'react';
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { isBoolean } from 'lodash';
export function AddDeleteButtons(props) {
interface AddDeleteButtonsProps {
addTooltip: string;
deleteTooltip: string;
cloneTooltip: string;
activatePanelTooltip: string;
deactivatePanelTooltip: string;
isPanelActive?: boolean;
disableAdd?: boolean;
disableDelete?: boolean;
responsive?: boolean;
testSubj: string;
togglePanelActivation?: () => void;
onClone?: () => void;
onAdd?: () => void;
onDelete?: (event: MouseEvent) => void;
}
export function AddDeleteButtons(props: AddDeleteButtonsProps) {
const { testSubj } = props;
const createDelete = () => {
if (props.disableDelete) {
@ -147,19 +163,3 @@ AddDeleteButtons.defaultProps = {
}
),
};
AddDeleteButtons.propTypes = {
addTooltip: PropTypes.string,
deleteTooltip: PropTypes.string,
cloneTooltip: PropTypes.string,
activatePanelTooltip: PropTypes.string,
deactivatePanelTooltip: PropTypes.string,
togglePanelActivation: PropTypes.func,
isPanelActive: PropTypes.bool,
disableAdd: PropTypes.bool,
disableDelete: PropTypes.bool,
onClone: PropTypes.func,
onAdd: PropTypes.func,
onDelete: PropTypes.func,
responsive: PropTypes.bool,
};

View file

@ -17,15 +17,33 @@
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import React, { HTMLAttributes } from 'react';
// @ts-ignore
import { aggToComponent } from '../lib/agg_to_component';
// @ts-ignore
import { isMetricEnabled } from '../../lib/check_ui_restrictions';
import { UnsupportedAgg } from './unsupported_agg';
import { TemporaryUnsupportedAgg } from './temporary_unsupported_agg';
import { MetricsItemsSchema, PanelSchema, SeriesItemsSchema } from '../../../../common/types';
import { DragHandleProps } from '../../../types';
import { TimeseriesUIRestrictions } from '../../../../common/ui_restrictions';
import { IFieldType } from '../../../../../data/common/index_patterns/fields';
import { isMetricEnabled } from '../../lib/check_ui_restrictions';
interface AggProps extends HTMLAttributes<HTMLElement> {
disableDelete: boolean;
fields: IFieldType[];
model: MetricsItemsSchema;
panel: PanelSchema;
series: SeriesItemsSchema;
siblings: MetricsItemsSchema[];
uiRestrictions: TimeseriesUIRestrictions;
dragHandleProps: DragHandleProps;
onAdd: () => void;
onChange: () => void;
onDelete: () => void;
}
export function Agg(props) {
export function Agg(props: AggProps) {
const { model, uiRestrictions } = props;
let Component = aggToComponent[model.type];
@ -59,17 +77,3 @@ export function Agg(props) {
</div>
);
}
Agg.propTypes = {
disableDelete: PropTypes.bool,
fields: PropTypes.object,
model: PropTypes.object,
onAdd: PropTypes.func,
onChange: PropTypes.func,
onDelete: PropTypes.func,
panel: PropTypes.object,
series: PropTypes.object,
siblings: PropTypes.array,
uiRestrictions: PropTypes.object,
dragHandleProps: PropTypes.object,
};

View file

@ -17,15 +17,26 @@
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import { last } from 'lodash';
import { AddDeleteButtons } from '../add_delete_buttons';
import { EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { SeriesDragHandler } from '../series_drag_handler';
import { i18n } from '@kbn/i18n';
import { AddDeleteButtons } from '../add_delete_buttons';
import { SeriesDragHandler } from '../series_drag_handler';
import { MetricsItemsSchema } from '../../../../common/types';
import { DragHandleProps } from '../../../types';
export function AggRow(props) {
interface AggRowProps {
disableDelete: boolean;
model: MetricsItemsSchema;
siblings: MetricsItemsSchema[];
dragHandleProps: DragHandleProps;
children: React.ReactNode;
onAdd: () => void;
onDelete: () => void;
}
export function AggRow(props: AggRowProps) {
let iconType = 'eyeClosed';
let iconColor = 'subdued';
const lastSibling = last(props.siblings);
@ -71,12 +82,3 @@ export function AggRow(props) {
</div>
);
}
AggRow.propTypes = {
disableDelete: PropTypes.bool,
model: PropTypes.object,
onAdd: PropTypes.func,
onDelete: PropTypes.func,
siblings: PropTypes.array,
dragHandleProps: PropTypes.object,
};

View file

@ -17,14 +17,17 @@
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import { EuiComboBox } from '@elastic/eui';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { injectI18n } from '@kbn/i18n/react';
// @ts-ignore
import { isMetricEnabled } from '../../lib/check_ui_restrictions';
import { MetricsItemsSchema } from '../../../../common/types';
import { TimeseriesUIRestrictions } from '../../../../common/ui_restrictions';
const metricAggs = [
type AggSelectOption = EuiComboBoxOptionOption;
const metricAggs: AggSelectOption[] = [
{
label: i18n.translate('visTypeTimeseries.aggSelect.metricsAggs.averageLabel', {
defaultMessage: 'Average',
@ -123,7 +126,7 @@ const metricAggs = [
},
];
const pipelineAggs = [
const pipelineAggs: AggSelectOption[] = [
{
label: i18n.translate('visTypeTimeseries.aggSelect.pipelineAggs.bucketScriptLabel', {
defaultMessage: 'Bucket Script',
@ -162,7 +165,7 @@ const pipelineAggs = [
},
];
const siblingAggs = [
const siblingAggs: AggSelectOption[] = [
{
label: i18n.translate('visTypeTimeseries.aggSelect.siblingAggs.overallAverageLabel', {
defaultMessage: 'Overall Average',
@ -207,7 +210,7 @@ const siblingAggs = [
},
];
const specialAggs = [
const specialAggs: AggSelectOption[] = [
{
label: i18n.translate('visTypeTimeseries.aggSelect.specialAggs.seriesAggLabel', {
defaultMessage: 'Series Agg',
@ -224,14 +227,23 @@ const specialAggs = [
const allAggOptions = [...metricAggs, ...pipelineAggs, ...siblingAggs, ...specialAggs];
function filterByPanelType(panelType) {
return (agg) => {
function filterByPanelType(panelType: string) {
return (agg: AggSelectOption) => {
if (panelType === 'table') return agg.value !== 'series_agg';
return true;
};
}
function AggSelectUi(props) {
interface AggSelectUiProps {
id: string;
panelType: string;
siblings: MetricsItemsSchema[];
value: string;
uiRestrictions?: TimeseriesUIRestrictions;
onChange: (currentlySelectedOptions: AggSelectOption[]) => void;
}
export function AggSelect(props: AggSelectUiProps) {
const { siblings, panelType, value, onChange, uiRestrictions, ...rest } = props;
const selectedOptions = allAggOptions.filter((option) => {
@ -242,11 +254,11 @@ function AggSelectUi(props) {
if (siblings.length <= 1) enablePipelines = false;
let options;
let options: EuiComboBoxOptionOption[];
if (panelType === 'metrics') {
options = metricAggs;
} else {
const disableSiblingAggs = (agg) => ({
const disableSiblingAggs = (agg: AggSelectOption) => ({
...agg,
disabled: !enablePipelines || !isMetricEnabled(agg.value, uiRestrictions),
});
@ -282,9 +294,9 @@ function AggSelectUi(props) {
];
}
const handleChange = (selectedOptions) => {
if (!selectedOptions || selectedOptions.length <= 0) return;
onChange(selectedOptions);
const handleChange = (currentlySelectedOptions: AggSelectOption[]) => {
if (!currentlySelectedOptions || currentlySelectedOptions.length <= 0) return;
onChange(currentlySelectedOptions);
};
return (
@ -303,13 +315,3 @@ function AggSelectUi(props) {
</div>
);
}
AggSelectUi.propTypes = {
onChange: PropTypes.func,
panelType: PropTypes.string,
siblings: PropTypes.array,
value: PropTypes.string,
uiRestrictions: PropTypes.object,
};
export const AggSelect = injectI18n(AggSelectUi);

View file

@ -18,18 +18,29 @@
*/
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { EuiDraggable, EuiDroppable } from '@elastic/eui';
import { Agg } from './agg';
import { newMetricAggFn } from '../lib/new_metric_agg_fn';
// @ts-ignore
import { seriesChangeHandler } from '../lib/series_change_handler';
// @ts-ignore
import { handleAdd, handleDelete } from '../lib/collection_actions';
import { newMetricAggFn } from '../lib/new_metric_agg_fn';
import { PanelSchema, SeriesItemsSchema } from '../../../../common/types';
import { TimeseriesUIRestrictions } from '../../../../common/ui_restrictions';
import { IFieldType } from '../../../../../data/common/index_patterns/fields';
const DROPPABLE_ID = 'aggs_dnd';
export class Aggs extends PureComponent {
export interface AggsProps {
panel: PanelSchema;
model: SeriesItemsSchema;
fields: IFieldType[];
uiRestrictions: TimeseriesUIRestrictions;
}
export class Aggs extends PureComponent<AggsProps> {
render() {
const { panel, model, fields, uiRestrictions } = this.props;
const list = model.metrics;
@ -68,12 +79,3 @@ export class Aggs extends PureComponent {
);
}
}
Aggs.propTypes = {
name: PropTypes.string.isRequired,
fields: PropTypes.object.isRequired,
model: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
panel: PropTypes.object.isRequired,
dragHandleProps: PropTypes.object,
};

View file

@ -16,8 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import React, { ChangeEvent } from 'react';
import { get } from 'lodash';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@ -31,10 +30,29 @@ import {
import { AddDeleteButtons } from '../../add_delete_buttons';
export const MultiValueRow = ({ model, onChange, onDelete, onAdd, disableAdd, disableDelete }) => {
interface MultiValueRowProps {
model: {
id: number;
value: string;
};
disableAdd: boolean;
disableDelete: boolean;
onChange: ({ value, id }: { id: number; value: string }) => void;
onDelete: (model: { id: number; value: string }) => void;
onAdd: () => void;
}
export const MultiValueRow = ({
model,
onChange,
onDelete,
onAdd,
disableAdd,
disableDelete,
}: MultiValueRowProps) => {
const htmlId = htmlIdGenerator();
const onFieldNumberChange = (event) =>
const onFieldNumberChange = (event: ChangeEvent<HTMLInputElement>) =>
onChange({
...model,
value: get(event, 'target.value'),
@ -54,7 +72,7 @@ export const MultiValueRow = ({ model, onChange, onDelete, onAdd, disableAdd, di
<EuiFlexItem grow={false}>
<EuiFieldNumber
value={model.value === '' ? '' : Number(model.value)}
placeholder={0}
placeholder="0"
onChange={onFieldNumberChange}
/>
</EuiFlexItem>
@ -78,12 +96,3 @@ MultiValueRow.defaultProps = {
value: '',
},
};
MultiValueRow.propTypes = {
model: PropTypes.object,
onChange: PropTypes.func,
onDelete: PropTypes.func,
onAdd: PropTypes.func,
defaultAddValue: PropTypes.string,
disableDelete: PropTypes.bool,
};

View file

@ -17,16 +17,7 @@
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import { assign } from 'lodash';
import { AggSelect } from '../agg_select';
import { FieldSelect } from '../field_select';
import { AggRow } from '../agg_row';
import { createChangeHandler } from '../../lib/create_change_handler';
import { createSelectHandler } from '../../lib/create_select_handler';
import { PercentileRankValues } from './percentile_rank_values';
import {
htmlIdGenerator,
EuiFlexGroup,
@ -36,11 +27,36 @@ import {
EuiSpacer,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public';
import { AggSelect } from '../agg_select';
// @ts-ignore
import { FieldSelect } from '../field_select';
// @ts-ignore
import { createChangeHandler } from '../../lib/create_change_handler';
// @ts-ignore
import { createSelectHandler } from '../../lib/create_select_handler';
import { AggRow } from '../agg_row';
import { PercentileRankValues } from './percentile_rank_values';
import { IFieldType, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public';
import { MetricsItemsSchema, PanelSchema, SeriesItemsSchema } from '../../../../../common/types';
import { DragHandleProps } from '../../../../types';
const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER];
export const PercentileRankAgg = (props) => {
interface PercentileRankAggProps {
disableDelete: boolean;
fields: IFieldType[];
model: MetricsItemsSchema;
panel: PanelSchema;
series: SeriesItemsSchema;
siblings: MetricsItemsSchema[];
dragHandleProps: DragHandleProps;
onAdd(): void;
onChange(): void;
onDelete(): void;
}
export const PercentileRankAgg = (props: PercentileRankAggProps) => {
const { series, panel, fields } = props;
const defaults = { values: [''] };
const model = { ...defaults, ...props.model };
@ -52,12 +68,11 @@ export const PercentileRankAgg = (props) => {
const handleChange = createChangeHandler(props.onChange, model);
const handleSelectChange = createSelectHandler(handleChange);
const handlePercentileRankValuesChange = (values) => {
handleChange(
assign({}, model, {
values,
})
);
const handlePercentileRankValuesChange = (values: MetricsItemsSchema['values']) => {
handleChange({
...model,
values,
});
};
return (
@ -108,25 +123,15 @@ export const PercentileRankAgg = (props) => {
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<PercentileRankValues
disableAdd={isTablePanel}
disableDelete={isTablePanel}
showOnlyLastRow={isTablePanel}
model={model.values}
onChange={handlePercentileRankValuesChange}
/>
{model.values && (
<PercentileRankValues
disableAdd={isTablePanel}
disableDelete={isTablePanel}
showOnlyLastRow={isTablePanel}
model={model.values}
onChange={handlePercentileRankValuesChange}
/>
)}
</AggRow>
);
};
PercentileRankAgg.propTypes = {
disableDelete: PropTypes.bool,
fields: PropTypes.object,
model: PropTypes.object,
onAdd: PropTypes.func,
onChange: PropTypes.func,
onDelete: PropTypes.func,
panel: PropTypes.object,
series: PropTypes.object,
siblings: PropTypes.array,
};

View file

@ -16,34 +16,49 @@
* specific language governing permissions and limitations
* under the License.
*/
import PropTypes from 'prop-types';
import React from 'react';
import { last } from 'lodash';
import { EuiFlexGroup } from '@elastic/eui';
import { MultiValueRow } from './multi_value_row';
export const PercentileRankValues = (props) => {
interface PercentileRankValuesProps {
model: Array<string | null>;
disableDelete: boolean;
disableAdd: boolean;
showOnlyLastRow: boolean;
onChange: (values: any[]) => void;
}
export const PercentileRankValues = (props: PercentileRankValuesProps) => {
const model = props.model || [];
const { onChange, disableAdd, disableDelete, showOnlyLastRow } = props;
const onChangeValue = ({ value, id }) => {
const onChangeValue = ({ value, id }: { value: string; id: number }) => {
model[id] = value;
onChange(model);
};
const onDeleteValue = ({ id }) =>
const onDeleteValue = ({ id }: { id: number }) =>
onChange(model.filter((item, currentIndex) => id !== currentIndex));
const onAddValue = () => onChange([...model, '']);
const renderRow = ({ rowModel, disableDelete, disableAdd }) => (
const renderRow = ({
rowModel,
disableDeleteRow,
disableAddRow,
}: {
rowModel: { id: number; value: string };
disableDeleteRow: boolean;
disableAddRow: boolean;
}) => (
<MultiValueRow
key={`percentileRankValue__item${rowModel.id}`}
onAdd={onAddValue}
onChange={onChangeValue}
onDelete={onDeleteValue}
disableDelete={disableDelete}
disableAdd={disableAdd}
disableDelete={disableDeleteRow}
disableAdd={disableAddRow}
model={rowModel}
/>
);
@ -54,10 +69,10 @@ export const PercentileRankValues = (props) => {
renderRow({
rowModel: {
id: model.length - 1,
value: last(model),
value: last(model) || '',
},
disableAdd: true,
disableDelete: true,
disableAddRow: true,
disableDeleteRow: true,
})}
{!showOnlyLastRow &&
@ -65,20 +80,12 @@ export const PercentileRankValues = (props) => {
renderRow({
rowModel: {
id,
value,
value: value || '',
},
disableAdd,
disableDelete: disableDelete || array.length < 2,
disableAddRow: disableAdd,
disableDeleteRow: disableDelete || array.length < 2,
})
)}
</EuiFlexGroup>
);
};
PercentileRankValues.propTypes = {
model: PropTypes.array,
onChange: PropTypes.func,
disableDelete: PropTypes.bool,
disableAdd: PropTypes.bool,
showOnlyLastRow: PropTypes.bool,
};

View file

@ -17,12 +17,23 @@
* under the License.
*/
import { AggRow } from './agg_row';
import React from 'react';
import { EuiCode, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { AggRow } from './agg_row';
import { MetricsItemsSchema } from '../../../../common/types';
import { DragHandleProps } from '../../../types';
export function TemporaryUnsupportedAgg(props) {
interface TemporaryUnsupportedAggProps {
disableDelete: boolean;
model: MetricsItemsSchema;
siblings: MetricsItemsSchema[];
dragHandleProps: DragHandleProps;
onAdd: () => void;
onDelete: () => void;
}
export function TemporaryUnsupportedAgg(props: TemporaryUnsupportedAggProps) {
return (
<AggRow
disableDelete={props.disableDelete}

View file

@ -17,12 +17,23 @@
* under the License.
*/
import { AggRow } from './agg_row';
import React from 'react';
import { EuiCode, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { AggRow } from './agg_row';
import { MetricsItemsSchema } from '../../../../common/types';
import { DragHandleProps } from '../../../types';
export function UnsupportedAgg(props) {
interface UnsupportedAggProps {
disableDelete: boolean;
model: MetricsItemsSchema;
siblings: MetricsItemsSchema[];
dragHandleProps: DragHandleProps;
onAdd: () => void;
onDelete: () => void;
}
export function UnsupportedAgg(props: UnsupportedAggProps) {
return (
<AggRow
disableDelete={props.disableDelete}

View file

@ -18,8 +18,9 @@
*/
import uuid from 'uuid';
import { MetricsItemsSchema } from '../../../../common/types';
export const newMetricAggFn = () => {
export const newMetricAggFn = (): MetricsItemsSchema => {
return {
id: uuid.v1(),
type: 'count',

View file

@ -18,11 +18,20 @@
*/
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { EuiFlexItem, EuiToolTip, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DragHandleProps } from '../../types';
interface SeriesDragHandlerProps {
hideDragHandler: boolean;
dragHandleProps: DragHandleProps;
}
export class SeriesDragHandler extends PureComponent<SeriesDragHandlerProps> {
static defaultProps = {
hideDragHandler: true,
};
export class SeriesDragHandler extends PureComponent {
render() {
const { dragHandleProps, hideDragHandler } = this.props;
@ -49,12 +58,3 @@ export class SeriesDragHandler extends PureComponent {
);
}
}
SeriesDragHandler.defaultProps = {
hideDragHandler: true,
};
SeriesDragHandler.propTypes = {
hideDragHandler: PropTypes.bool,
dragHandleProps: PropTypes.object.isRequired,
};

View file

@ -0,0 +1,29 @@
/*
* 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 { EuiDraggable } from '@elastic/eui';
type PropsOf<T> = T extends React.ComponentType<infer ComponentProps> ? ComponentProps : never;
type FirstArgumentOf<Func> = Func extends (arg1: infer FirstArgument, ...rest: any[]) => any
? FirstArgument
: never;
export type DragHandleProps = FirstArgumentOf<
Exclude<PropsOf<typeof EuiDraggable>['children'], React.ReactElement>
>['dragHandleProps'];

View file

@ -20,7 +20,7 @@
import { IRouter, KibanaRequest } from 'kibana/server';
import { schema } from '@kbn/config-schema';
import { getVisData, GetVisDataOptions } from '../lib/get_vis_data';
import { visPayloadSchema } from './post_vis_schema';
import { visPayloadSchema } from '../../common/vis_schema';
import { Framework, ValidationTelemetryServiceSetup } from '../index';
const escapeHatch = schema.object({}, { unknowns: 'allow' });