From 40162b17ee51c3e2a968bbb8683a987c8e4b6302 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Fri, 18 Jun 2021 17:32:17 +0300 Subject: [PATCH 1/2] [TSVB] Index pattern select field disappear in Annotation tab (#102314) * [TSVB] Index pattern select field disappear in Annotation tab * Refactor AnnotationsEditor and move renderRow logic into AnnotationRow * Remove duplicated license, add useCallback to functions in AnnotationRow, remove cross refecrence and update types * Refactor AnnotationEditor and AnnotationRow * refactoring * remove extra props * fix nits Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Alexey Antonov # Conflicts: # src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx --- .../application/components/annotation_row.tsx | 280 +++++++++++++++ .../components/annotations_editor.js | 322 ------------------ .../components/annotations_editor.tsx | 113 ++++++ .../components/panel_config/timeseries.tsx | 4 +- .../public/application/components/yes_no.tsx | 8 +- 5 files changed, 398 insertions(+), 329 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx delete mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js create mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx new file mode 100644 index 0000000000000..715cf4d6709da --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx @@ -0,0 +1,280 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect, useCallback, useMemo, ChangeEvent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiCode, + EuiComboBoxOptionOption, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSpacer, + htmlIdGenerator, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { getDataStart } from '../../services'; +import { KBN_FIELD_TYPES, Query } from '../../../../../plugins/data/public'; + +import { AddDeleteButtons } from './add_delete_buttons'; +import { ColorPicker } from './color_picker'; +import { FieldSelect } from './aggs/field_select'; +import { IndexPatternSelect } from './lib/index_pattern_select'; +import { QueryBarWrapper } from './query_bar_wrapper'; +import { YesNo } from './yes_no'; +import { fetchIndexPattern } from '../../../common/index_patterns_utils'; +import { getDefaultQueryLanguage } from './lib/get_default_query_language'; + +// @ts-expect-error not typed yet +import { IconSelect } from './icon_select/icon_select'; + +import type { Annotation, FetchedIndexPattern, IndexPatternValue } from '../../../common/types'; +import type { VisFields } from '../lib/fetch_fields'; + +const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; + +const INDEX_PATTERN_KEY = 'index_pattern'; +const TIME_FIELD_KEY = 'time_field'; + +export interface AnnotationRowProps { + annotation: Annotation; + fields: VisFields; + onChange: (partialModel: Partial) => void; + handleAdd: () => void; + handleDelete: () => void; +} + +const getAnnotationDefaults = () => ({ + fields: '', + template: '', + index_pattern: '', + query_string: { query: '', language: getDefaultQueryLanguage() }, +}); + +export const AnnotationRow = ({ + annotation, + fields, + onChange, + handleAdd, + handleDelete, +}: AnnotationRowProps) => { + const model = useMemo(() => ({ ...getAnnotationDefaults(), ...annotation }), [annotation]); + const htmlId = htmlIdGenerator(model.id); + + const [fetchedIndex, setFetchedIndex] = useState(null); + + useEffect(() => { + const updateFetchedIndex = async (index: IndexPatternValue) => { + const { indexPatterns } = getDataStart(); + + setFetchedIndex( + index + ? await fetchIndexPattern(index, indexPatterns) + : { + indexPattern: undefined, + indexPatternString: undefined, + } + ); + }; + + updateFetchedIndex(model.index_pattern); + }, [model.index_pattern]); + + const togglePanelActivation = useCallback( + () => + onChange({ + hidden: !model.hidden, + }), + [model.hidden, onChange] + ); + + const handleChange = useCallback( + (name: string) => ( + event: Array> | ChangeEvent + ) => + onChange({ + [name]: Array.isArray(event) ? event?.[0]?.value : event.target.value, + }), + [onChange] + ); + + const handleQueryChange = useCallback( + (filter: Query) => + onChange({ + query_string: filter, + }), + [onChange] + ); + + return ( +
+ + + + + + + + + + + + + } + restrict={RESTRICT_FIELDS} + value={model.time_field} + onChange={handleChange(TIME_FIELD_KEY)} + indexPattern={model.index_pattern} + fields={fields} + /> + + + + + + + + + } + fullWidth + > + + + + + + + + + + + + + + + + + + + + + } + > + + + + + + } + fullWidth + > + + + + + + } + helpText={ + + {'{{field}}'} }} + /> + + } + fullWidth + > + + + + + + + + + + +
+ ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js deleted file mode 100644 index 09ce57639b952..0000000000000 --- a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import _ from 'lodash'; -import { collectionActions } from './lib/collection_actions'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; -import { AddDeleteButtons } from './add_delete_buttons'; -import { ColorPicker } from './color_picker'; -import { FieldSelect } from './aggs/field_select'; -import uuid from 'uuid'; -import { IconSelect } from './icon_select/icon_select'; -import { YesNo } from './yes_no'; -import { QueryBarWrapper } from './query_bar_wrapper'; -import { getDefaultQueryLanguage } from './lib/get_default_query_language'; -import { - htmlIdGenerator, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiSpacer, - EuiFieldText, - EuiTitle, - EuiButton, - EuiCode, - EuiText, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { IndexPatternSelect } from './lib/index_pattern_select'; - -function newAnnotation() { - return { - id: uuid.v1(), - color: '#F00', - index_pattern: '', - time_field: '', - icon: 'fa-tag', - ignore_global_filters: 1, - ignore_panel_filters: 1, - }; -} - -const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; - -export class AnnotationsEditor extends Component { - constructor(props) { - super(props); - this.renderRow = this.renderRow.bind(this); - } - - handleChange(item, name) { - return (e) => { - const handleChange = collectionActions.handleChange.bind(null, this.props); - const part = {}; - part[name] = _.get(e, '[0].value', _.get(e, 'target.value')); - handleChange(_.assign({}, item, part)); - }; - } - - handleQueryChange = (model, filter) => { - const part = { query_string: filter }; - collectionActions.handleChange(this.props, { - ...model, - ...part, - }); - }; - renderRow(row) { - const defaults = { - fields: '', - template: '', - index_pattern: '', - query_string: { query: '', language: getDefaultQueryLanguage() }, - }; - const model = { ...defaults, ...row }; - const handleChange = (part) => { - const fn = collectionActions.handleChange.bind(null, this.props); - fn(_.assign({}, model, part)); - }; - const togglePanelActivation = () => { - handleChange({ - hidden: !model.hidden, - }); - }; - const htmlId = htmlIdGenerator(model.id); - const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation); - const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); - - return ( -
- - - - - - - - - - - - - } - restrict={RESTRICT_FIELDS} - value={model.time_field} - onChange={this.handleChange(model, 'time_field')} - indexPattern={model.index_pattern} - fields={this.props.fields} - fullWidth - /> - - - - - - - - - } - fullWidth - > - this.handleQueryChange(model, query)} - indexPatterns={[model.index_pattern]} - /> - - - - - - - - - - - - - - - - - - - - } - > - - - - - - } - fullWidth - > - - - - - - } - helpText={ - - {'{{field}}'} }} - /> - - } - fullWidth - > - - - - - - - - - - -
- ); - } - - render() { - const { model } = this.props; - let content; - if (!model.annotations || !model.annotations.length) { - const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation); - content = ( - -

- -

- - - -
- ); - } else { - const annotations = model.annotations.map(this.renderRow); - content = ( -
- - - - - - - - {annotations} -
- ); - } - return
{content}
; - } -} - -AnnotationsEditor.defaultProps = { - name: 'annotations', -}; - -AnnotationsEditor.propTypes = { - fields: PropTypes.object, - model: PropTypes.object, - name: PropTypes.string, - onChange: PropTypes.func, - uiSettings: PropTypes.object, -}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx new file mode 100644 index 0000000000000..b3b4993d2ca06 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback } from 'react'; +import uuid from 'uuid'; +import { EuiSpacer, EuiTitle, EuiButton, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { AnnotationRow } from './annotation_row'; +import { collectionActions, CollectionActionsProps } from './lib/collection_actions'; + +import type { Panel, Annotation } from '../../../common/types'; +import type { VisFields } from '../lib/fetch_fields'; + +interface AnnotationsEditorProps { + fields: VisFields; + model: Panel; + onChange: (partialModel: Partial) => void; +} + +export const newAnnotation = () => ({ + id: uuid.v1(), + color: '#F00', + index_pattern: '', + time_field: '', + icon: 'fa-tag', + ignore_global_filters: 1, + ignore_panel_filters: 1, +}); + +const NoContent = ({ handleAdd }: { handleAdd: () => void }) => ( + +

+ +

+ + + +
+); + +const getCollectionActionsProps = (props: AnnotationsEditorProps) => + ({ + name: 'annotations', + ...props, + } as CollectionActionsProps); + +export const AnnotationsEditor = (props: AnnotationsEditorProps) => { + const { annotations } = props.model; + + const handleAdd = useCallback( + () => collectionActions.handleAdd(getCollectionActionsProps(props), newAnnotation), + [props] + ); + + const handleDelete = useCallback( + (annotation) => () => + collectionActions.handleDelete(getCollectionActionsProps(props), annotation), + [props] + ); + + const onChange = useCallback( + (annotation: Annotation) => { + return (part: Partial) => + collectionActions.handleChange(getCollectionActionsProps(props), { + ...annotation, + ...part, + }); + }, + [props] + ); + + return ( +
+ {annotations?.length ? ( +
+ + + + + + + {annotations.map((annotation) => ( + + ))} +
+ ) : ( + + )} +
+ ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx b/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx index ae9d7326140a7..c7c754be5f74f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx @@ -28,9 +28,8 @@ import { // @ts-expect-error not typed yet import { SeriesEditor } from '../series_editor'; // @ts-expect-error not typed yet -import { AnnotationsEditor } from '../annotations_editor'; -// @ts-expect-error not typed yet import { IndexPattern } from '../index_pattern'; +import { AnnotationsEditor } from '../annotations_editor'; import { createSelectHandler } from '../lib/create_select_handler'; import { ColorPicker } from '../color_picker'; import { YesNo } from '../yes_no'; @@ -160,7 +159,6 @@ export class TimeseriesPanelConfig extends Component< ); diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx b/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx index 708892bbc681d..f37083e3e95ab 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx @@ -11,21 +11,21 @@ import { EuiRadio, htmlIdGenerator } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { TimeseriesVisParams } from '../../types'; -interface YesNoProps { - name: ParamName; +interface YesNoProps { + name: string; value: TimeseriesVisParams[ParamName]; disabled?: boolean; 'data-test-subj'?: string; onChange: (partialModel: Partial) => void; } -export function YesNo({ +export function YesNo({ name, value, disabled, 'data-test-subj': dataTestSubj, onChange, -}: YesNoProps) { +}: YesNoProps) { const handleChange = useCallback( (val: number) => { return () => onChange({ [name]: val }); From 7bf1555b85171ada5c4d2c5ef9d3475b2847d7b2 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 18 Jun 2021 18:25:13 +0300 Subject: [PATCH 2/2] fix merge conflicts --- .../vis_type_timeseries/common/vis_schema.ts | 4 ++-- .../application/components/annotation_row.tsx | 10 +++++++--- .../application/components/annotations_editor.tsx | 14 +++++++------- .../public/application/components/yes_no.tsx | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/plugins/vis_type_timeseries/common/vis_schema.ts b/src/plugins/vis_type_timeseries/common/vis_schema.ts index d31fed4639ffe..b5e6418069295 100644 --- a/src/plugins/vis_type_timeseries/common/vis_schema.ts +++ b/src/plugins/vis_type_timeseries/common/vis_schema.ts @@ -48,7 +48,7 @@ export const fieldObject = stringOptionalNullable; export const annotationsItems = schema.object({ color: stringOptionalNullable, - fields: stringOptionalNullable, + fields: schema.string(), hidden: schema.maybe(schema.boolean()), icon: stringOptionalNullable, id: schema.string(), @@ -56,7 +56,7 @@ export const annotationsItems = schema.object({ ignore_panel_filters: numberIntegerOptional, index_pattern: indexPattern, query_string: schema.maybe(queryObject), - template: stringOptionalNullable, + template: schema.string(), time_field: fieldObject, }); diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx index 715cf4d6709da..233e261af59cc 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx @@ -35,7 +35,11 @@ import { getDefaultQueryLanguage } from './lib/get_default_query_language'; // @ts-expect-error not typed yet import { IconSelect } from './icon_select/icon_select'; -import type { Annotation, FetchedIndexPattern, IndexPatternValue } from '../../../common/types'; +import type { + AnnotationItemsSchema, + FetchedIndexPattern, + IndexPatternValue, +} from '../../../common/types'; import type { VisFields } from '../lib/fetch_fields'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; @@ -44,9 +48,9 @@ const INDEX_PATTERN_KEY = 'index_pattern'; const TIME_FIELD_KEY = 'time_field'; export interface AnnotationRowProps { - annotation: Annotation; + annotation: AnnotationItemsSchema; fields: VisFields; - onChange: (partialModel: Partial) => void; + onChange: (partialModel: Partial) => void; handleAdd: () => void; handleDelete: () => void; } diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx index b3b4993d2ca06..e031ac658ef6f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx @@ -14,13 +14,13 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { AnnotationRow } from './annotation_row'; import { collectionActions, CollectionActionsProps } from './lib/collection_actions'; -import type { Panel, Annotation } from '../../../common/types'; +import type { PanelSchema, AnnotationItemsSchema } from '../../../common/types'; import type { VisFields } from '../lib/fetch_fields'; interface AnnotationsEditorProps { fields: VisFields; - model: Panel; - onChange: (partialModel: Partial) => void; + model: PanelSchema; + onChange: (partialModel: Partial) => void; } export const newAnnotation = () => ({ @@ -54,7 +54,7 @@ const getCollectionActionsProps = (props: AnnotationsEditorProps) => ({ name: 'annotations', ...props, - } as CollectionActionsProps); + } as CollectionActionsProps); export const AnnotationsEditor = (props: AnnotationsEditorProps) => { const { annotations } = props.model; @@ -71,8 +71,8 @@ export const AnnotationsEditor = (props: AnnotationsEditorProps) => { ); const onChange = useCallback( - (annotation: Annotation) => { - return (part: Partial) => + (annotation: AnnotationItemsSchema) => { + return (part: Partial) => collectionActions.handleChange(getCollectionActionsProps(props), { ...annotation, ...part, @@ -94,7 +94,7 @@ export const AnnotationsEditor = (props: AnnotationsEditorProps) => { - {annotations.map((annotation) => ( + {annotations.map((annotation: AnnotationItemsSchema) => ( ) => void;