Skip to content

Commit

Permalink
[ML] Fix annotation flyout init when typing. Fix date picker refresh.
Browse files Browse the repository at this point in the history
  • Loading branch information
walterra committed Jan 7, 2020
1 parent 91a8be9 commit a9d9fea
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Component, Fragment, FC, ReactNode } from 'react';
import React, { useEffect, Component, Fragment, FC, ReactNode } from 'react';
import useObservable from 'react-use/lib/useObservable';
import * as Rx from 'rxjs';

Expand All @@ -24,13 +24,14 @@ import {
} from '@elastic/eui';

import { CommonProps } from '@elastic/eui';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import { InjectedIntlProps } from 'react-intl';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import { toastNotifications } from 'ui/notify';
import { ANNOTATION_MAX_LENGTH_CHARS } from '../../../../../common/constants/annotations';
import {
annotation$,
annotationsRefresh$,
annotationsRefreshed,
AnnotationState,
} from '../../../services/annotations_service';
import { AnnotationDescriptionList } from '../annotation_description_list';
Expand All @@ -46,7 +47,7 @@ interface State {
isDeleteModalVisible: boolean;
}

class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlProps> {
class AnnotationFlyoutIntl extends Component<CommonProps & Props> {
public state: State = {
isDeleteModalVisible: false,
};
Expand All @@ -73,7 +74,7 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP
};

public deleteHandler = async () => {
const { annotation, intl } = this.props;
const { annotation } = this.props;

if (annotation === null) {
return;
Expand All @@ -82,31 +83,30 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP
try {
await ml.annotations.deleteAnnotation(annotation._id);
toastNotifications.addSuccess(
intl.formatMessage(
i18n.translate(
'xpack.ml.timeSeriesExplorer.timeSeriesChart.deletedAnnotationNotificationMessage',
{
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.deletedAnnotationNotificationMessage',
defaultMessage: 'Deleted annotation for job with ID {jobId}.',
},
{ jobId: annotation.job_id }
values: { jobId: annotation.job_id },
}
)
);
} catch (err) {
toastNotifications.addDanger(
intl.formatMessage(
i18n.translate(
'xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithDeletingAnnotationNotificationErrorMessage',
{
id:
'xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithDeletingAnnotationNotificationErrorMessage',
defaultMessage:
'An error occurred deleting the annotation for job with ID {jobId}: {error}',
},
{ jobId: annotation.job_id, error: JSON.stringify(err) }
values: { jobId: annotation.job_id, error: JSON.stringify(err) },
}
)
);
}

this.closeDeleteModal();
annotation$.next(null);
annotationsRefresh$.next(true);
annotationsRefreshed();
};

public closeDeleteModal = () => {
Expand All @@ -116,16 +116,15 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP
public validateAnnotationText = () => {
// Validates the entered text, returning an array of error messages
// for display in the form. An empty array is returned if the text is valid.
const { annotation, intl } = this.props;
const { annotation } = this.props;
const errors: string[] = [];
if (annotation === null) {
return errors;
}

if (annotation.annotation.trim().length === 0) {
errors.push(
intl.formatMessage({
id: 'xpack.ml.timeSeriesExplorer.annotationFlyout.noAnnotationTextError',
i18n.translate('xpack.ml.timeSeriesExplorer.annotationFlyout.noAnnotationTextError', {
defaultMessage: 'Enter annotation text',
})
);
Expand All @@ -135,25 +134,22 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP
if (textLength > ANNOTATION_MAX_LENGTH_CHARS) {
const charsOver = textLength - ANNOTATION_MAX_LENGTH_CHARS;
errors.push(
intl.formatMessage(
{
id: 'xpack.ml.timeSeriesExplorer.annotationFlyout.maxLengthError',
defaultMessage:
'{charsOver, number} {charsOver, plural, one {character} other {characters}} above maximum length of {maxChars}',
},
{
i18n.translate('xpack.ml.timeSeriesExplorer.annotationFlyout.maxLengthError', {
defaultMessage:
'{charsOver, number} {charsOver, plural, one {character} other {characters}} above maximum length of {maxChars}',
values: {
maxChars: ANNOTATION_MAX_LENGTH_CHARS,
charsOver,
}
)
},
})
);
}

return errors;
};

public saveOrUpdateAnnotation = () => {
const { annotation, intl } = this.props;
const { annotation } = this.props;

if (annotation === null) {
return;
Expand All @@ -164,62 +160,58 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP
ml.annotations
.indexAnnotation(annotation)
.then(() => {
annotationsRefresh$.next(true);
annotationsRefreshed();
if (typeof annotation._id === 'undefined') {
toastNotifications.addSuccess(
intl.formatMessage(
i18n.translate(
'xpack.ml.timeSeriesExplorer.timeSeriesChart.addedAnnotationNotificationMessage',
{
id:
'xpack.ml.timeSeriesExplorer.timeSeriesChart.addedAnnotationNotificationMessage',
defaultMessage: 'Added an annotation for job with ID {jobId}.',
},
{ jobId: annotation.job_id }
values: { jobId: annotation.job_id },
}
)
);
} else {
toastNotifications.addSuccess(
intl.formatMessage(
i18n.translate(
'xpack.ml.timeSeriesExplorer.timeSeriesChart.updatedAnnotationNotificationMessage',
{
id:
'xpack.ml.timeSeriesExplorer.timeSeriesChart.updatedAnnotationNotificationMessage',
defaultMessage: 'Updated annotation for job with ID {jobId}.',
},
{ jobId: annotation.job_id }
values: { jobId: annotation.job_id },
}
)
);
}
})
.catch(resp => {
if (typeof annotation._id === 'undefined') {
toastNotifications.addDanger(
intl.formatMessage(
i18n.translate(
'xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithCreatingAnnotationNotificationErrorMessage',
{
id:
'xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithCreatingAnnotationNotificationErrorMessage',
defaultMessage:
'An error occurred creating the annotation for job with ID {jobId}: {error}',
},
{ jobId: annotation.job_id, error: JSON.stringify(resp) }
values: { jobId: annotation.job_id, error: JSON.stringify(resp) },
}
)
);
} else {
toastNotifications.addDanger(
intl.formatMessage(
i18n.translate(
'xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithUpdatingAnnotationNotificationErrorMessage',
{
id:
'xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithUpdatingAnnotationNotificationErrorMessage',
defaultMessage:
'An error occurred updating the annotation for job with ID {jobId}: {error}',
},
{ jobId: annotation.job_id, error: JSON.stringify(resp) }
values: { jobId: annotation.job_id, error: JSON.stringify(resp) },
}
)
);
}
});
};

public render(): ReactNode {
const { annotation, intl } = this.props;
const { annotation } = this.props;
const { isDeleteModalVisible } = this.state;

if (annotation === null) {
Expand All @@ -238,13 +230,13 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP
isInvalid === false &&
annotation.annotation.length > ANNOTATION_MAX_LENGTH_CHARS * lengthRatioToShowWarning
) {
helpText = intl.formatMessage(
helpText = i18n.translate(
'xpack.ml.timeSeriesExplorer.annotationFlyout.approachingMaxLengthWarning',
{
id: 'xpack.ml.timeSeriesExplorer.annotationFlyout.approachingMaxLengthWarning',
defaultMessage:
'{charsRemaining, number} {charsRemaining, plural, one {character} other {characters}} remaining',
},
{ charsRemaining: ANNOTATION_MAX_LENGTH_CHARS - annotation.annotation.length }
values: { charsRemaining: ANNOTATION_MAX_LENGTH_CHARS - annotation.annotation.length },
}
);
}

Expand Down Expand Up @@ -346,9 +338,10 @@ class AnnotationFlyoutIntl extends Component<CommonProps & Props & InjectedIntlP

export const AnnotationFlyout: FC<any> = props => {
const annotationProp = useObservable(annotation$);

if (annotationProp === undefined) {
return null;
}
const AnnotationFlyoutIntlInjected = injectI18n(AnnotationFlyoutIntl);
return <AnnotationFlyoutIntlInjected annotation={annotationProp} {...props} />;

return <AnnotationFlyoutIntl annotation={annotationProp} {...props} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ import {
isTimeSeriesViewJob,
} from '../../../../../common/util/job_utils';

import { annotation$, annotationsRefresh$ } from '../../../services/annotations_service';
import {
annotation$,
annotationsRefresh$,
annotationsRefreshed,
} from '../../../services/annotations_service';

import { FormattedMessage, injectI18n } from '@kbn/i18n/react';

Expand Down Expand Up @@ -136,7 +140,7 @@ const AnnotationsTable = injectI18n(
this.annotationsRefreshSubscription = annotationsRefresh$.subscribe(() =>
this.getAnnotations()
);
annotationsRefresh$.next(true);
annotationsRefreshed();
}
}

Expand All @@ -150,7 +154,7 @@ const AnnotationsTable = injectI18n(
this.state.isLoading === false &&
this.state.jobId !== this.props.jobs[0].job_id
) {
annotationsRefresh$.next(true);
annotationsRefreshed();
this.previousJobId = this.props.jobs[0].job_id;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { EuiSuperDatePicker } from '@elastic/eui';
import { TimeHistory } from 'ui/timefilter';
import { TimeRange } from 'src/plugins/data/public';

import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service';
import {
mlTimefilterRefresh$,
mlTimefilterTimeChange$,
} from '../../../services/timefilter_refresh_service';
import { useUiContext } from '../../../contexts/ui/use_ui_context';

interface Duration {
Expand Down Expand Up @@ -74,6 +77,7 @@ export const TopNav: FC = () => {
timefilter.setTime(newTime);
setTime(newTime);
setRecentlyUsedRanges(getRecentlyUsedRanges());
mlTimefilterTimeChange$.next({ lastRefresh: Date.now(), timeRange: { start, end } });
}

function updateInterval({
Expand Down Expand Up @@ -104,7 +108,9 @@ export const TopNav: FC = () => {
isAutoRefreshOnly={!isTimeRangeSelectorEnabled}
refreshInterval={refreshInterval.value}
onTimeChange={updateFilter}
onRefresh={() => mlTimefilterRefresh$.next()}
onRefresh={timeRange => {
mlTimefilterRefresh$.next({ lastRefresh: Date.now(), timeRange });
}}
onRefreshChange={updateInterval}
recentlyUsedRanges={recentlyUsedRanges}
dateFormat={dateFormat}
Expand Down
Loading

0 comments on commit a9d9fea

Please sign in to comment.