mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Event annotations] Cache annotation group metadata (#167822)
## Summary Fix https://github.com/elastic/kibana/issues/166855 Fix https://github.com/elastic/kibana/issues/167817 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
4c5c6de84e
commit
4c233537ea
12 changed files with 80 additions and 13 deletions
|
@ -322,6 +322,8 @@ const AnnotationEditorControls = ({
|
|||
)}
|
||||
|
||||
<ColorPicker
|
||||
overwriteColor={currentAnnotation.color}
|
||||
isClearable={false}
|
||||
defaultColor={isRange ? defaultAnnotationRangeColor : defaultAnnotationColor}
|
||||
showAlpha={isRange}
|
||||
setConfig={update}
|
||||
|
|
|
@ -31,6 +31,7 @@ const tooltipContent = {
|
|||
export const ColorPicker = ({
|
||||
overwriteColor,
|
||||
defaultColor,
|
||||
isClearable,
|
||||
setConfig,
|
||||
label,
|
||||
disableHelpTooltip,
|
||||
|
@ -39,6 +40,7 @@ export const ColorPicker = ({
|
|||
}: {
|
||||
overwriteColor?: string | null;
|
||||
defaultColor?: string | null;
|
||||
isClearable?: boolean;
|
||||
setConfig: (config: { color?: string }) => void;
|
||||
label?: string;
|
||||
disableHelpTooltip?: boolean;
|
||||
|
@ -93,14 +95,14 @@ export const ColorPicker = ({
|
|||
fullWidth
|
||||
data-test-subj="indexPattern-dimension-colorPicker"
|
||||
compressed
|
||||
isClearable={Boolean(overwriteColor)}
|
||||
isClearable={typeof isClearable !== 'undefined' ? isClearable : Boolean(overwriteColor)}
|
||||
onChange={handleColor}
|
||||
color={isDisabled ? '' : colorText}
|
||||
disabled={isDisabled}
|
||||
placeholder={' '}
|
||||
onBlur={() => {
|
||||
if (!colorText) {
|
||||
setColorText(overwriteColor ?? defaultColor);
|
||||
setColorText(validatedColor ?? defaultColor);
|
||||
}
|
||||
}}
|
||||
aria-label={inputLabel}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { IToasts } from '@kbn/core-notifications-browser';
|
|||
import type { LayerAction, StateSetter } from '../../../../types';
|
||||
import type { XYState, XYByReferenceAnnotationLayerConfig } from '../../types';
|
||||
import { annotationLayerHasUnsavedChanges } from '../../state_helpers';
|
||||
import { getAnnotationLayerTitle } from '../../visualization_helpers';
|
||||
|
||||
export const getRevertChangesAction = ({
|
||||
state,
|
||||
|
@ -50,7 +51,7 @@ export const getRevertChangesAction = ({
|
|||
<RevertChangesConfirmModal
|
||||
modalTitle={i18n.translate('xpack.lens.modalTitle.revertAnnotationGroupTitle', {
|
||||
defaultMessage: 'Revert "{title}" changes?',
|
||||
values: { title: layer.__lastSaved.title },
|
||||
values: { title: getAnnotationLayerTitle(layer) },
|
||||
})}
|
||||
onCancel={() => modal.close()}
|
||||
onConfirm={() => {
|
||||
|
@ -111,7 +112,7 @@ export const revert = ({
|
|||
toasts.addSuccess({
|
||||
title: i18n.translate('xpack.lens.xyChart.annotations.notificationReverted', {
|
||||
defaultMessage: `Reverted "{title}"`,
|
||||
values: { title: layer.__lastSaved.title },
|
||||
values: { title: getAnnotationLayerTitle(layer) },
|
||||
}),
|
||||
text: i18n.translate('xpack.lens.xyChart.annotations.notificationRevertedExplanation', {
|
||||
defaultMessage: 'The most recently saved version of this annotation group has been restored.',
|
||||
|
|
|
@ -28,7 +28,11 @@ import type {
|
|||
StateSetter,
|
||||
} from '../../../../types';
|
||||
import { XYByReferenceAnnotationLayerConfig, XYAnnotationLayerConfig, XYState } from '../../types';
|
||||
import { isByReferenceAnnotationsLayer } from '../../visualization_helpers';
|
||||
import {
|
||||
getAnnotationLayerTitle,
|
||||
getGroupMetadataFromAnnotationLayer,
|
||||
isByReferenceAnnotationsLayer,
|
||||
} from '../../visualization_helpers';
|
||||
|
||||
type ModalOnSaveProps = SavedObjectOnSaveProps & { newTags: string[]; closeModal: () => void };
|
||||
|
||||
|
@ -194,7 +198,7 @@ export const onSave = async ({
|
|||
}) => {
|
||||
const shouldStop = await shouldStopBecauseDuplicateTitle(
|
||||
newTitle,
|
||||
isByReferenceAnnotationsLayer(layer) ? layer.__lastSaved.title : '',
|
||||
getAnnotationLayerTitle(layer),
|
||||
newCopyOnSave,
|
||||
onTitleDuplicate,
|
||||
isTitleDuplicateConfirmed,
|
||||
|
@ -326,6 +330,8 @@ export const getSaveLayerAction = ({
|
|||
),
|
||||
execute: async (domElement) => {
|
||||
if (domElement) {
|
||||
const metadata = getGroupMetadataFromAnnotationLayer(layer);
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={kibanaTheme.theme$}>
|
||||
<I18nProvider>
|
||||
|
@ -345,9 +351,7 @@ export const getSaveLayerAction = ({
|
|||
goToAnnotationLibrary,
|
||||
});
|
||||
}}
|
||||
title={neverSaved ? '' : layer.__lastSaved.title}
|
||||
description={neverSaved ? '' : layer.__lastSaved.description}
|
||||
tags={neverSaved ? [] : layer.__lastSaved.tags}
|
||||
{...metadata}
|
||||
showCopyOnSave={!neverSaved}
|
||||
/>
|
||||
</I18nProvider>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
XYByValueAnnotationLayerConfig,
|
||||
XYState,
|
||||
} from '../../types';
|
||||
import { getAnnotationLayerTitle } from '../../visualization_helpers';
|
||||
|
||||
export const getUnlinkLayerAction = ({
|
||||
state,
|
||||
|
@ -45,7 +46,7 @@ export const getUnlinkLayerAction = ({
|
|||
toasts.addSuccess(
|
||||
i18n.translate('xpack.lens.xyChart.annotations.notificationUnlinked', {
|
||||
defaultMessage: `Unlinked "{title}"`,
|
||||
values: { title: layer.__lastSaved.title },
|
||||
values: { title: getAnnotationLayerTitle(layer) },
|
||||
})
|
||||
);
|
||||
},
|
||||
|
|
|
@ -187,6 +187,11 @@ export function getPersistableState(state: XYState) {
|
|||
} else {
|
||||
const persistableLayer: XYPersistedLinkedByValueAnnotationLayerConfig = {
|
||||
persistanceType: 'linked',
|
||||
cachedMetadata: layer.cachedMetadata || {
|
||||
title: layer.__lastSaved.title,
|
||||
description: layer.__lastSaved.description,
|
||||
tags: layer.__lastSaved.tags,
|
||||
},
|
||||
layerId: layer.layerId,
|
||||
layerType: layer.layerType,
|
||||
annotationGroupRef: referenceName,
|
||||
|
@ -298,6 +303,7 @@ export function injectReferences(
|
|||
ignoreGlobalFilters: persistedLayer.ignoreGlobalFilters,
|
||||
indexPatternId: getIndexPatternIdFromReferences(persistedLayer.layerId),
|
||||
annotations: cloneDeep(persistedLayer.annotations),
|
||||
cachedMetadata: persistedLayer.cachedMetadata,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,13 @@ export interface XYByValueAnnotationLayerConfig {
|
|||
annotations: EventAnnotationConfig[];
|
||||
indexPatternId: string;
|
||||
ignoreGlobalFilters: boolean;
|
||||
// populated only when the annotation has been forked from the
|
||||
// version saved in the library (persisted as XYPersistedLinkedByValueAnnotationLayerConfig)
|
||||
cachedMetadata?: {
|
||||
title: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export type XYPersistedByValueAnnotationLayerConfig = Omit<
|
||||
|
|
|
@ -460,6 +460,11 @@ describe('xy_visualization', () => {
|
|||
annotations: [], // different from the persisted group
|
||||
},
|
||||
{
|
||||
cachedMetadata: {
|
||||
title: 'Local title',
|
||||
description: '',
|
||||
tags: [],
|
||||
},
|
||||
layerId: 'annotation',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
persistanceType: 'linked',
|
||||
|
@ -501,6 +506,7 @@ describe('xy_visualization', () => {
|
|||
{
|
||||
layerId: 'annotation',
|
||||
layerType: layerTypes.ANNOTATIONS,
|
||||
cachedMetadata: persistedAnnotationLayers[1].cachedMetadata,
|
||||
annotationGroupId: annotationGroupId2,
|
||||
ignoreGlobalFilters: persistedAnnotationLayers[1].ignoreGlobalFilters,
|
||||
annotations: persistedAnnotationLayers[1].annotations,
|
||||
|
@ -3718,6 +3724,12 @@ describe('xy_visualization', () => {
|
|||
layerId: 'layer-id',
|
||||
layerType: 'annotations',
|
||||
persistanceType: 'linked',
|
||||
// stores "cached" or "local" metadata
|
||||
cachedMetadata: {
|
||||
description: 'some description',
|
||||
tags: [],
|
||||
title: 'My saved object title',
|
||||
},
|
||||
annotations: layers[0].annotations,
|
||||
ignoreGlobalFilters: layers[0].ignoreGlobalFilters,
|
||||
},
|
||||
|
@ -3726,6 +3738,11 @@ describe('xy_visualization', () => {
|
|||
layerId: 'layer-id2',
|
||||
layerType: 'annotations',
|
||||
persistanceType: 'linked',
|
||||
cachedMetadata: {
|
||||
description: 'some description',
|
||||
tags: [],
|
||||
title: 'My saved object title',
|
||||
},
|
||||
annotations: layers[1].annotations,
|
||||
ignoreGlobalFilters: layers[1].ignoreGlobalFilters,
|
||||
},
|
||||
|
|
|
@ -85,6 +85,7 @@ import {
|
|||
import {
|
||||
checkXAccessorCompatibility,
|
||||
defaultSeriesType,
|
||||
getAnnotationLayerTitle,
|
||||
getAnnotationsLayers,
|
||||
getAxisName,
|
||||
getDataLayers,
|
||||
|
@ -334,7 +335,7 @@ export const getXyVisualization = ({
|
|||
const layerIndex = state.layers.findIndex((l) => l.layerId === layerId);
|
||||
const layer = state.layers[layerIndex];
|
||||
if (layer && isByReferenceAnnotationsLayer(layer)) {
|
||||
return { title: `Delete "${layer.__lastSaved.title}"` };
|
||||
return { title: `Delete "${getAnnotationLayerTitle(layer)}"` };
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -178,6 +178,23 @@ export const isPersistedLinkedByValueAnnotationsLayer = (
|
|||
export const getAnnotationsLayers = (layers: Array<Pick<XYLayerConfig, 'layerType'>>) =>
|
||||
(layers || []).filter((layer): layer is XYAnnotationLayerConfig => isAnnotationsLayer(layer));
|
||||
|
||||
export const getGroupMetadataFromAnnotationLayer = (
|
||||
layer: XYAnnotationLayerConfig
|
||||
): { title: string; description: string; tags: string[] } => {
|
||||
if (layer.cachedMetadata) {
|
||||
return layer.cachedMetadata;
|
||||
}
|
||||
if (isByReferenceAnnotationsLayer(layer)) {
|
||||
const { title, description, tags } = layer.__lastSaved;
|
||||
return { title, description, tags };
|
||||
}
|
||||
return { title: '', description: '', tags: [] };
|
||||
};
|
||||
|
||||
export const getAnnotationLayerTitle = (layer: XYAnnotationLayerConfig): string => {
|
||||
return getGroupMetadataFromAnnotationLayer(layer).title;
|
||||
};
|
||||
|
||||
export interface LayerTypeToLayer {
|
||||
[layerTypes.DATA]: (layer: XYDataLayerConfig) => XYDataLayerConfig;
|
||||
[layerTypes.REFERENCELINE]: (layer: XYReferenceLineLayerConfig) => XYReferenceLineLayerConfig;
|
||||
|
|
|
@ -71,6 +71,15 @@ describe('layer header', () => {
|
|||
.text()
|
||||
.trim()
|
||||
).toBe(byRefGroupTitle);
|
||||
|
||||
const cachedMetadata = { title: 'A cached title', description: '', tags: [] };
|
||||
expect(
|
||||
mountWithIntl(
|
||||
<LayerHeader {...props} state={getStateWithLayers([{ ...byRefLayer, cachedMetadata }])} />
|
||||
)
|
||||
.text()
|
||||
.trim()
|
||||
).toBe(cachedMetadata.title);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,8 +35,8 @@ import {
|
|||
import { ChangeIndexPattern, StaticHeader } from '../../../shared_components';
|
||||
import { updateLayer } from '.';
|
||||
import {
|
||||
getAnnotationLayerTitle,
|
||||
isAnnotationsLayer,
|
||||
isByReferenceAnnotationsLayer,
|
||||
isDataLayer,
|
||||
isReferenceLayer,
|
||||
} from '../visualization_helpers';
|
||||
|
@ -52,7 +52,7 @@ export function LayerHeader(props: VisualizationLayerWidgetProps<State>) {
|
|||
if (isAnnotationsLayer(layer)) {
|
||||
return (
|
||||
<AnnotationsLayerHeader
|
||||
title={isByReferenceAnnotationsLayer(layer) ? layer.__lastSaved.title : undefined}
|
||||
title={getAnnotationLayerTitle(layer)}
|
||||
hasUnsavedChanges={annotationLayerHasUnsavedChanges(layer)}
|
||||
/>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue