mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[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 <alexwizp@gmail.com>
This commit is contained in:
parent
d65416c60c
commit
853de830c2
5 changed files with 398 additions and 329 deletions
|
@ -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<Annotation>) => 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<FetchedIndexPattern | null>(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<EuiComboBoxOptionOption<string>> | ChangeEvent<HTMLInputElement>
|
||||
) =>
|
||||
onChange({
|
||||
[name]: Array.isArray(event) ? event?.[0]?.value : event.target.value,
|
||||
}),
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const handleQueryChange = useCallback(
|
||||
(filter: Query) =>
|
||||
onChange({
|
||||
query_string: filter,
|
||||
}),
|
||||
[onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="tvbAnnotationsEditor" key={model.id}>
|
||||
<EuiFlexGroup responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ColorPicker disableTrash={true} onChange={onChange} name="color" value={model.color} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem className="tvbAggRow__children">
|
||||
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<IndexPatternSelect
|
||||
value={model[INDEX_PATTERN_KEY]}
|
||||
indexPatternName={INDEX_PATTERN_KEY}
|
||||
onChange={onChange}
|
||||
fetchedIndex={fetchedIndex}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<FieldSelect
|
||||
type={TIME_FIELD_KEY}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.timeFieldLabel"
|
||||
defaultMessage="Time field (required)"
|
||||
/>
|
||||
}
|
||||
restrict={RESTRICT_FIELDS}
|
||||
value={model.time_field}
|
||||
onChange={handleChange(TIME_FIELD_KEY)}
|
||||
indexPattern={model.index_pattern}
|
||||
fields={fields}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('queryString')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.queryStringLabel"
|
||||
defaultMessage="Query string"
|
||||
/>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<QueryBarWrapper
|
||||
query={{
|
||||
language: model.query_string.language || getDefaultQueryLanguage(),
|
||||
query: model.query_string.query || '',
|
||||
}}
|
||||
onChange={handleQueryChange}
|
||||
indexPatterns={[model.index_pattern]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'visTypeTimeseries.annotationsEditor.ignoreGlobalFiltersLabel',
|
||||
{
|
||||
defaultMessage: 'Ignore global filters?',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<YesNo
|
||||
value={model.ignore_global_filters}
|
||||
name="ignore_global_filters"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'visTypeTimeseries.annotationsEditor.ignorePanelFiltersLabel',
|
||||
{
|
||||
defaultMessage: 'Ignore panel filters?',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<YesNo
|
||||
value={model.ignore_panel_filters}
|
||||
name="ignore_panel_filters"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('icon')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.iconLabel"
|
||||
defaultMessage="Icon (required)"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<IconSelect value={model.icon} onChange={handleChange('icon')} />
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('fields')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.fieldsLabel"
|
||||
defaultMessage="Fields (required - comma separated paths)"
|
||||
/>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<EuiFieldText onChange={handleChange('fields')} value={model.fields} fullWidth />
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('rowTemplate')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.rowTemplateLabel"
|
||||
defaultMessage="Row template (required)"
|
||||
/>
|
||||
}
|
||||
helpText={
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.rowTemplateHelpText"
|
||||
defaultMessage="eg.{rowTemplateExample}"
|
||||
values={{ rowTemplateExample: <EuiCode>{'{{field}}'}</EuiCode> }}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<EuiFieldText
|
||||
onChange={handleChange('template')}
|
||||
value={model.template}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<AddDeleteButtons
|
||||
onAdd={handleAdd}
|
||||
onDelete={handleDelete}
|
||||
togglePanelActivation={togglePanelActivation}
|
||||
isPanelActive={!model.hidden}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -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 (
|
||||
<div className="tvbAnnotationsEditor" key={model.id}>
|
||||
<EuiFlexGroup responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ColorPicker
|
||||
disableTrash={true}
|
||||
onChange={handleChange}
|
||||
name="color"
|
||||
value={model.color}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem className="tvbAggRow__children">
|
||||
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<IndexPatternSelect
|
||||
value={model.index_pattern}
|
||||
indexPatternName={'index_pattern'}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<FieldSelect
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.timeFieldLabel"
|
||||
defaultMessage="Time field (required)"
|
||||
/>
|
||||
}
|
||||
restrict={RESTRICT_FIELDS}
|
||||
value={model.time_field}
|
||||
onChange={this.handleChange(model, 'time_field')}
|
||||
indexPattern={model.index_pattern}
|
||||
fields={this.props.fields}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('queryString')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.queryStringLabel"
|
||||
defaultMessage="Query string"
|
||||
/>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<QueryBarWrapper
|
||||
query={{
|
||||
language: model.query_string.language || getDefaultQueryLanguage(),
|
||||
query: model.query_string.query || '',
|
||||
}}
|
||||
onChange={(query) => this.handleQueryChange(model, query)}
|
||||
indexPatterns={[model.index_pattern]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'visTypeTimeseries.annotationsEditor.ignoreGlobalFiltersLabel',
|
||||
{
|
||||
defaultMessage: 'Ignore global filters?',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<YesNo
|
||||
value={model.ignore_global_filters}
|
||||
name="ignore_global_filters"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'visTypeTimeseries.annotationsEditor.ignorePanelFiltersLabel',
|
||||
{
|
||||
defaultMessage: 'Ignore panel filters?',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<YesNo
|
||||
value={model.ignore_panel_filters}
|
||||
name="ignore_panel_filters"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup responsive={false} wrap={true} gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('icon')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.iconLabel"
|
||||
defaultMessage="Icon (required)"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<IconSelect value={model.icon} onChange={this.handleChange(model, 'icon')} />
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('fields')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.fieldsLabel"
|
||||
defaultMessage="Fields (required - comma separated paths)"
|
||||
/>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<EuiFieldText
|
||||
onChange={this.handleChange(model, 'fields')}
|
||||
value={model.fields}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('rowTemplate')}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.rowTemplateLabel"
|
||||
defaultMessage="Row template (required)"
|
||||
/>
|
||||
}
|
||||
helpText={
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.rowTemplateHelpText"
|
||||
defaultMessage="eg.{rowTemplateExample}"
|
||||
values={{ rowTemplateExample: <EuiCode>{'{{field}}'}</EuiCode> }}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<EuiFieldText
|
||||
onChange={this.handleChange(model, 'template')}
|
||||
value={model.template}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<AddDeleteButtons
|
||||
onAdd={handleAdd}
|
||||
onDelete={handleDelete}
|
||||
togglePanelActivation={togglePanelActivation}
|
||||
isPanelActive={!model.hidden}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { model } = this.props;
|
||||
let content;
|
||||
if (!model.annotations || !model.annotations.length) {
|
||||
const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation);
|
||||
content = (
|
||||
<EuiText textAlign="center">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.howToCreateAnnotationDataSourceDescription"
|
||||
defaultMessage="Click the button below to create an annotation data source."
|
||||
/>
|
||||
</p>
|
||||
<EuiButton fill onClick={handleAdd}>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.addDataSourceButtonLabel"
|
||||
defaultMessage="Add data source"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiText>
|
||||
);
|
||||
} else {
|
||||
const annotations = model.annotations.map(this.renderRow);
|
||||
content = (
|
||||
<div>
|
||||
<EuiTitle size="s">
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.dataSourcesLabel"
|
||||
defaultMessage="Data sources"
|
||||
/>
|
||||
</span>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{annotations}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div className="tvbAnnotationsEditor__container">{content}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationsEditor.defaultProps = {
|
||||
name: 'annotations',
|
||||
};
|
||||
|
||||
AnnotationsEditor.propTypes = {
|
||||
fields: PropTypes.object,
|
||||
model: PropTypes.object,
|
||||
name: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
uiSettings: PropTypes.object,
|
||||
};
|
|
@ -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<Panel>) => 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 }) => (
|
||||
<EuiText textAlign="center">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.howToCreateAnnotationDataSourceDescription"
|
||||
defaultMessage="Click the button below to create an annotation data source."
|
||||
/>
|
||||
</p>
|
||||
<EuiButton fill onClick={handleAdd}>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.addDataSourceButtonLabel"
|
||||
defaultMessage="Add data source"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiText>
|
||||
);
|
||||
|
||||
const getCollectionActionsProps = (props: AnnotationsEditorProps) =>
|
||||
({
|
||||
name: 'annotations',
|
||||
...props,
|
||||
} as CollectionActionsProps<Panel>);
|
||||
|
||||
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<Annotation>) =>
|
||||
collectionActions.handleChange(getCollectionActionsProps(props), {
|
||||
...annotation,
|
||||
...part,
|
||||
});
|
||||
},
|
||||
[props]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="tvbAnnotationsEditor__container">
|
||||
{annotations?.length ? (
|
||||
<div>
|
||||
<EuiTitle size="s">
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="visTypeTimeseries.annotationsEditor.dataSourcesLabel"
|
||||
defaultMessage="Data sources"
|
||||
/>
|
||||
</span>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
{annotations.map((annotation) => (
|
||||
<AnnotationRow
|
||||
key={annotation.id}
|
||||
annotation={annotation}
|
||||
fields={props.fields}
|
||||
onChange={onChange(annotation)}
|
||||
handleAdd={handleAdd}
|
||||
handleDelete={handleDelete(annotation)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<NoContent handleAdd={handleAdd} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -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';
|
||||
|
@ -162,7 +161,6 @@ export class TimeseriesPanelConfig extends Component<
|
|||
<AnnotationsEditor
|
||||
fields={this.props.fields}
|
||||
model={this.props.model}
|
||||
name="annotations"
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -11,21 +11,21 @@ import { EuiRadio, htmlIdGenerator } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { TimeseriesVisParams } from '../../types';
|
||||
|
||||
interface YesNoProps<ParamName extends keyof TimeseriesVisParams> {
|
||||
name: ParamName;
|
||||
interface YesNoProps {
|
||||
name: string;
|
||||
value: boolean | number | undefined;
|
||||
disabled?: boolean;
|
||||
'data-test-subj'?: string;
|
||||
onChange: (partialModel: Partial<TimeseriesVisParams>) => void;
|
||||
}
|
||||
|
||||
export function YesNo<ParamName extends keyof TimeseriesVisParams>({
|
||||
export function YesNo({
|
||||
name,
|
||||
value,
|
||||
disabled,
|
||||
'data-test-subj': dataTestSubj,
|
||||
onChange,
|
||||
}: YesNoProps<ParamName>) {
|
||||
}: YesNoProps) {
|
||||
const handleChange = useCallback(
|
||||
(val: number) => {
|
||||
return () => onChange({ [name]: val });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue