From 172de682c53503a3a1879b86bcc335d298c2d513 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 20 Sep 2023 01:09:17 -0600 Subject: [PATCH] [Event annotations] Individual annotation editing from library (#163346) ## Summary Resolve https://github.com/elastic/kibana/issues/158774 Part of https://github.com/elastic/kibana/issues/159053 Screenshot 2023-09-13 at 2 00 25 PM Screenshot 2023-09-13 at 2 00 09 PM Screenshot 2023-09-13 at 2 01 07 PM ### Known issues - [ ] ~Responsive layout~ **Proposal:** don't optimize for mobile - [x] Recovering embeddable from problematic data view state - [x] margin around dimension buttons - [x] Functional test coverage ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli --- .github/CODEOWNERS | 1 + .i18nrc.json | 34 +- docs/developer/plugin-list.asciidoc | 4 + .../lens_saveAnnotationLayerButton_8.9.0.png | Bin 0 -> 10815 bytes docs/user/dashboard/lens.asciidoc | 20 +- package.json | 1 + .../src/table_list_view_table.tsx | 2 +- .../annotation_editor_controls.tsx | 1 - .../query_annotation_panel.tsx | 1 + .../tooltip_annotation_panel.tsx | 1 + .../components/group_editor_flyout.tsx | 152 ---- .../components/index.ts | 4 - .../kbn-event-annotation-components/index.ts | 2 +- .../jest.config.js | 1 + .../tsconfig.json | 10 +- packages/kbn-optimizer/limits.yml | 3 +- .../components/color_picker.tsx | 12 +- .../lib/embeddables/embeddable_root.tsx | 102 +-- src/plugins/event_annotation/common/index.ts | 2 +- src/plugins/event_annotation/kibana.jsonc | 1 - src/plugins/event_annotation/public/plugin.ts | 43 - src/plugins/event_annotation/tsconfig.json | 7 - .../event_annotation_listing/.i18nrc.json | 6 + .../event_annotation_listing/README.md | 3 + .../event_annotation_listing/jest.config.js | 19 + .../event_annotation_listing/kibana.jsonc | 30 + .../group_editor_flyout.test.tsx.snap | 2 +- .../group_editor_controls/annotation_list.tsx | 70 +- .../group_editor_controls.test.tsx | 95 +- .../group_editor_controls.tsx | 186 ++-- .../group_editor_controls/index.ts | 0 .../group_editor_flyout.test.tsx | 34 +- .../group_editor_flyout.tsx | 253 ++++++ .../group_preview.test.tsx | 307 +++++++ .../group_editor_flyout/group_preview.tsx | 260 ++++++ .../components/group_editor_flyout/index.ts | 9 + .../group_editor_flyout/lens_attributes.ts | 153 ++++ .../public}/components/table_list.test.tsx | 27 +- .../public}/components/table_list.tsx | 69 +- .../public/constants.ts | 9 + .../public/get_table_list.tsx | 10 +- .../event_annotation_listing/public/index.ts | 26 + .../event_annotation_listing/public/plugin.ts | 101 +++ .../event_annotation_listing/tsconfig.json | 48 + .../visualize/group3/_annotation_listing.ts | 182 ++++ .../functional/apps/visualize/group3/index.ts | 1 + .../annotation_listing_page_search.json | 832 ++++++++++++++++++ .../annotation_library_editor_page.ts | 84 ++ test/functional/page_objects/index.ts | 2 + test/functional/services/listing_table.ts | 12 +- tsconfig.base.json | 2 + .../lens/public/embeddable/embeddable.tsx | 29 +- .../embeddable/embeddable_component.tsx | 2 + x-pack/plugins/lens/public/index.ts | 1 + x-pack/plugins/lens/public/plugin.ts | 7 +- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - yarn.lock | 4 + 59 files changed, 2735 insertions(+), 550 deletions(-) create mode 100644 docs/user/dashboard/images/lens_saveAnnotationLayerButton_8.9.0.png delete mode 100644 packages/kbn-event-annotation-components/components/group_editor_flyout.tsx create mode 100755 src/plugins/event_annotation_listing/.i18nrc.json create mode 100644 src/plugins/event_annotation_listing/README.md create mode 100644 src/plugins/event_annotation_listing/jest.config.js create mode 100644 src/plugins/event_annotation_listing/kibana.jsonc rename {packages/kbn-event-annotation-components/components => src/plugins/event_annotation_listing/public/components/group_editor_flyout}/__snapshots__/group_editor_flyout.test.tsx.snap (95%) rename {packages/kbn-event-annotation-components/components => src/plugins/event_annotation_listing/public/components/group_editor_flyout}/group_editor_controls/annotation_list.tsx (75%) rename {packages/kbn-event-annotation-components/components => src/plugins/event_annotation_listing/public/components/group_editor_flyout}/group_editor_controls/group_editor_controls.test.tsx (71%) rename {packages/kbn-event-annotation-components/components => src/plugins/event_annotation_listing/public/components/group_editor_flyout}/group_editor_controls/group_editor_controls.tsx (52%) rename {packages/kbn-event-annotation-components/components => src/plugins/event_annotation_listing/public/components/group_editor_flyout}/group_editor_controls/index.ts (100%) rename {packages/kbn-event-annotation-components/components => src/plugins/event_annotation_listing/public/components/group_editor_flyout}/group_editor_flyout.test.tsx (81%) create mode 100644 src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.tsx create mode 100644 src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.test.tsx create mode 100644 src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.tsx create mode 100644 src/plugins/event_annotation_listing/public/components/group_editor_flyout/index.ts create mode 100644 src/plugins/event_annotation_listing/public/components/group_editor_flyout/lens_attributes.ts rename {packages/kbn-event-annotation-components => src/plugins/event_annotation_listing/public}/components/table_list.test.tsx (85%) rename {packages/kbn-event-annotation-components => src/plugins/event_annotation_listing/public}/components/table_list.tsx (71%) create mode 100644 src/plugins/event_annotation_listing/public/constants.ts rename src/plugins/{event_annotation => event_annotation_listing}/public/get_table_list.tsx (82%) create mode 100644 src/plugins/event_annotation_listing/public/index.ts create mode 100644 src/plugins/event_annotation_listing/public/plugin.ts create mode 100644 src/plugins/event_annotation_listing/tsconfig.json create mode 100644 test/functional/apps/visualize/group3/_annotation_listing.ts create mode 100644 test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json create mode 100644 test/functional/page_objects/annotation_library_editor_page.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33321137c8412..ecfb835cbc4d0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -364,6 +364,7 @@ packages/kbn-eslint-plugin-telemetry @elastic/actionable-observability x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security packages/kbn-event-annotation-common @elastic/kibana-visualizations packages/kbn-event-annotation-components @elastic/kibana-visualizations +src/plugins/event_annotation_listing @elastic/kibana-visualizations src/plugins/event_annotation @elastic/kibana-visualizations x-pack/test/plugin_api_integration/plugins/event_log @elastic/response-ops x-pack/plugins/event_log @elastic/response-ops diff --git a/.i18nrc.json b/.i18nrc.json index f5de3b91a9a01..b5e17c18d3542 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -6,17 +6,12 @@ "apmOss": "src/plugins/apm_oss", "autocomplete": "packages/kbn-securitysolution-autocomplete/src", "bfetch": "src/plugins/bfetch", - "cases": [ - "packages/kbn-cases-components" - ], + "cases": ["packages/kbn-cases-components"], "cellActions": "packages/kbn-cell-actions", "charts": "src/plugins/charts", "console": "src/plugins/console", "contentManagement": "packages/content-management", - "core": [ - "src/core", - "packages/core" - ], + "core": ["src/core", "packages/core"], "customIntegrations": "src/plugins/custom_integrations", "customIntegrationsPackage": "packages/kbn-custom-integrations", "dashboard": "src/plugins/dashboard", @@ -27,10 +22,7 @@ "dataViews": "src/plugins/data_views", "defaultNavigation": "packages/default-nav", "devTools": "src/plugins/dev_tools", - "discover": [ - "src/plugins/discover", - "packages/kbn-discover-utils" - ], + "discover": ["src/plugins/discover", "packages/kbn-discover-utils"], "savedSearch": "src/plugins/saved_search", "embeddableApi": "src/plugins/embeddable", "embeddableExamples": "examples/embeddable_examples", @@ -52,6 +44,7 @@ "expressionShape": "src/plugins/expression_shape", "expressionTagcloud": "src/plugins/chart_expressions/expression_tagcloud", "eventAnnotation": "src/plugins/event_annotation", + "eventAnnotationListing": "src/plugins/event_annotation_listing", "eventAnnotationCommon": "packages/kbn-event-annotation-common", "eventAnnotationComponents": "packages/kbn-event-annotation-components", "fieldFormats": "src/plugins/field_formats", @@ -110,17 +103,9 @@ "languageDocumentationPopover": "packages/kbn-language-documentation-popover/src", "textBasedLanguages": "src/plugins/text_based_languages", "statusPage": "src/legacy/core_plugins/status_page", - "telemetry": [ - "src/plugins/telemetry", - "src/plugins/telemetry_management_section" - ], - "timelion": [ - "src/plugins/vis_types/timelion" - ], - "uiActions": [ - "src/plugins/ui_actions", - "packages/kbn-ui-actions-browser" - ], + "telemetry": ["src/plugins/telemetry", "src/plugins/telemetry_management_section"], + "timelion": ["src/plugins/vis_types/timelion"], + "uiActions": ["src/plugins/ui_actions", "packages/kbn-ui-actions-browser"], "uiActionsEnhanced": "src/plugins/ui_actions_enhanced", "uiActionsExamples": "examples/ui_action_examples", "usageCollection": "src/plugins/usage_collection", @@ -140,10 +125,7 @@ "visTypeXy": "src/plugins/vis_types/xy", "visualizations": "src/plugins/visualizations", "visualizationUiComponents": "packages/kbn-visualization-ui-components", - "unifiedDocViewer": [ - "src/plugins/unified_doc_viewer", - "packages/kbn-unified-doc-viewer" - ], + "unifiedDocViewer": ["src/plugins/unified_doc_viewer", "packages/kbn-unified-doc-viewer"], "unifiedSearch": "src/plugins/unified_search", "unifiedFieldList": "packages/kbn-unified-field-list", "unifiedHistogram": "src/plugins/unified_histogram", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 14b498001225d..71190c26c4894 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -102,6 +102,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |The Event Annotation service contains expressions for event annotations +|{kib-repo}blob/{branch}/src/plugins/event_annotation_listing/README.md[eventAnnotationListing] +|This plugin contains the library listing page for event annotation groups. + + |{kib-repo}blob/{branch}/src/plugins/expression_error/README.md[expressionError] |Expression Error plugin adds an error renderer to the expression plugin. The renderer will display the error image. diff --git a/docs/user/dashboard/images/lens_saveAnnotationLayerButton_8.9.0.png b/docs/user/dashboard/images/lens_saveAnnotationLayerButton_8.9.0.png new file mode 100644 index 0000000000000000000000000000000000000000..902ef7acfa300964499a42e4a8c9edfcb64b7fb3 GIT binary patch literal 10815 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 BVJ-jw literal 0 HcmV?d00001 diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc index 38a21cf3e97c8..f97c22c769a4c 100644 --- a/docs/user/dashboard/lens.asciidoc +++ b/docs/user/dashboard/lens.asciidoc @@ -222,9 +222,11 @@ Annotations support two placement types: * *Custom query* — Displays annotations based on custom {es} queries. For detailed information about queries, check <>. -Create the annotation layer. +Any annotation layer can be saved as an annotation group to the *Visualize Library* in order to reuse it in other visualizations. Any changes made to the annotation group will be reflected in all visualizations to which it is added. -. In the layer pane, click *Add layer > Annotations*. +Create a new annotation layer. + +. In the layer pane, click *Add layer > Annotations > New annotation*. . Select the {data-source} for the annotation. @@ -263,6 +265,20 @@ Specify the annotation appearance. . To close, click *X*. +Save the annotation group to the library. + +. In the layer pane, on your annotation layer, click image:images/lens_saveAnnotationLayerButton_8.9.0.png[Save button on annotations layer]. + +. Enter the *Title*, *Description*, and add any applicable <>. + +. Click *Save group*. + +Add a library annotation group to a visualization. + +. In the layer pane, click *Add layer > Annotations > Load from library*. + +. Select the annotation group you want to use. + [float] [[add-reference-lines]] ==== Add reference lines diff --git a/package.json b/package.json index 88b29766c1ded..e39b82676682d 100644 --- a/package.json +++ b/package.json @@ -401,6 +401,7 @@ "@kbn/eso-plugin": "link:x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin", "@kbn/event-annotation-common": "link:packages/kbn-event-annotation-common", "@kbn/event-annotation-components": "link:packages/kbn-event-annotation-components", + "@kbn/event-annotation-listing-plugin": "link:src/plugins/event_annotation_listing", "@kbn/event-annotation-plugin": "link:src/plugins/event_annotation", "@kbn/event-log-fixture-plugin": "link:x-pack/test/plugin_api_integration/plugins/event_log", "@kbn/event-log-plugin": "link:x-pack/plugins/event_log", diff --git a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx index 23a0a7fa29ae4..2de5e8855f681 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -946,7 +946,7 @@ function TableListViewTableComp({ if (!showFetchError && hasNoItems) { return ( - + diff --git a/packages/kbn-event-annotation-components/components/annotation_editor_controls/annotation_editor_controls.tsx b/packages/kbn-event-annotation-components/components/annotation_editor_controls/annotation_editor_controls.tsx index 21b7870db45db..e2a0dbdeae380 100644 --- a/packages/kbn-event-annotation-components/components/annotation_editor_controls/annotation_editor_controls.tsx +++ b/packages/kbn-event-annotation-components/components/annotation_editor_controls/annotation_editor_controls.tsx @@ -322,7 +322,6 @@ const AnnotationEditorControls = ({ )} void; - onClose: () => void; - onSave: () => void; - savedObjectsTagging: SavedObjectsTaggingApi; - dataViews: DataView[]; - createDataView: (spec: DataViewSpec) => Promise; - queryInputServices: QueryInputServices; -}) => { - const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); - const flyoutBodyOverflowRef = useRef(null); - useEffect(() => { - if (!flyoutBodyOverflowRef.current) { - flyoutBodyOverflowRef.current = document.querySelector('.euiFlyoutBody__overflow'); - } - }, []); - - const [hasAttemptedSave, setHasAttemptedSave] = useState(false); - - const resetContentScroll = useCallback( - () => flyoutBodyOverflowRef.current && flyoutBodyOverflowRef.current.scroll(0, 0), - [] - ); - - const [selectedAnnotation, _setSelectedAnnotation] = useState(); - const setSelectedAnnotation = useCallback( - (newValue: EventAnnotationConfig | undefined) => { - if ((!newValue && selectedAnnotation) || (newValue && !selectedAnnotation)) - resetContentScroll(); - _setSelectedAnnotation(newValue); - }, - [resetContentScroll, selectedAnnotation] - ); - - const onClose = () => (selectedAnnotation ? setSelectedAnnotation(undefined) : parentOnClose()); - - return ( - - - -

- -

-
-
- - - - - - - - {selectedAnnotation ? ( - - setSelectedAnnotation(undefined)} - > - - - - ) : ( - <> - - - - - - - { - setHasAttemptedSave(true); - - if (isGroupValid(group)) { - onSave(); - } - }} - > - - - - - )} - - -
- ); -}; diff --git a/packages/kbn-event-annotation-components/components/index.ts b/packages/kbn-event-annotation-components/components/index.ts index d2a46bce9ef8c..f06df6bc48b59 100644 --- a/packages/kbn-event-annotation-components/components/index.ts +++ b/packages/kbn-event-annotation-components/components/index.ts @@ -9,8 +9,4 @@ // TODO - is this file needed? export { AnnotationEditorControls, annotationsIconSet } from './annotation_editor_controls'; -export * from './group_editor_controls'; - export * from './get_annotation_accessor'; - -export * from './table_list'; diff --git a/packages/kbn-event-annotation-components/index.ts b/packages/kbn-event-annotation-components/index.ts index 5364609ab7e1e..e5dcd2f52e695 100644 --- a/packages/kbn-event-annotation-components/index.ts +++ b/packages/kbn-event-annotation-components/index.ts @@ -10,7 +10,7 @@ export { AnnotationEditorControls, annotationsIconSet, } from './components/annotation_editor_controls'; -export { EventAnnotationGroupTableList, getAnnotationAccessor } from './components'; +export { getAnnotationAccessor } from './components'; export { defaultAnnotationColor, defaultAnnotationRangeColor, diff --git a/packages/kbn-event-annotation-components/jest.config.js b/packages/kbn-event-annotation-components/jest.config.js index 542264f8faaa4..18afc01787e6d 100644 --- a/packages/kbn-event-annotation-components/jest.config.js +++ b/packages/kbn-event-annotation-components/jest.config.js @@ -10,4 +10,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../..', roots: ['/packages/kbn-event-annotation-components'], + setupFiles: ['jest-canvas-mock'], }; diff --git a/packages/kbn-event-annotation-components/tsconfig.json b/packages/kbn-event-annotation-components/tsconfig.json index 7a26b61878e2f..462501c387a04 100644 --- a/packages/kbn-event-annotation-components/tsconfig.json +++ b/packages/kbn-event-annotation-components/tsconfig.json @@ -22,19 +22,11 @@ "@kbn/unified-field-list", "@kbn/data-views-plugin", "@kbn/data-plugin", - "@kbn/test-jest-helpers", - "@kbn/saved-objects-tagging-oss-plugin", "@kbn/core-ui-settings-browser", - "@kbn/dom-drag-drop", - "@kbn/content-management-table-list-view-table", - "@kbn/content-management-tabbed-table-list-view", + "@kbn/content-management-plugin", "@kbn/event-annotation-common", "@kbn/i18n-react", "@kbn/saved-objects-finder-plugin", - "@kbn/core-notifications-browser-mocks", - "@kbn/core-notifications-browser", - "@kbn/core-saved-objects-api-browser", "@kbn/expressions-plugin", - "@kbn/content-management-plugin", ] } diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ce349c219dddc..78bba1df4220a 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -40,7 +40,8 @@ pageLoadAssetSize: embeddableEnhanced: 22107 enterpriseSearch: 50858 esUiShared: 326654 - eventAnnotation: 48565 + eventAnnotation: 30000 + eventAnnotationListing: 25841 exploratoryView: 74673 expressionError: 22127 expressionGauge: 25000 diff --git a/packages/kbn-visualization-ui-components/components/color_picker.tsx b/packages/kbn-visualization-ui-components/components/color_picker.tsx index 74c17b76ba2f1..702c17d06241f 100644 --- a/packages/kbn-visualization-ui-components/components/color_picker.tsx +++ b/packages/kbn-visualization-ui-components/components/color_picker.tsx @@ -97,12 +97,12 @@ export const ColorPicker = ({ onChange={handleColor} color={isDisabled ? '' : colorText} disabled={isDisabled} - placeholder={ - defaultColor?.toUpperCase() || - i18n.translate('visualizationUiComponents.colorPicker.seriesColor.auto', { - defaultMessage: 'Auto', - }) - } + placeholder={' '} + onBlur={() => { + if (!colorText) { + setColorText(overwriteColor ?? defaultColor); + } + }} aria-label={inputLabel} showAlpha={showAlpha} swatches={ diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_root.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable_root.tsx index f94bb99ada83c..47fc20241e97f 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_root.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_root.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; import { EuiText } from '@elastic/eui'; import { isPromise } from '@kbn/std'; @@ -21,83 +21,45 @@ interface Props { input?: EmbeddableInput; } -interface State { - node?: ReactNode; -} - -export class EmbeddableRoot extends React.Component { - private root?: React.RefObject; - private alreadyMounted: boolean = false; - - constructor(props: Props) { - super(props); - - this.root = React.createRef(); - this.state = {}; - } - - private updateNode = (node: MaybePromise) => { - if (isPromise(node)) { - node.then(this.updateNode); +export const EmbeddableRoot: React.FC = ({ embeddable, loading, error, input }) => { + const [node, setNode] = useState(); + const [embeddableHasMounted, setEmbeddableHasMounted] = useState(false); + const rootRef = useRef(null); + const updateNode = useCallback((newNode: MaybePromise) => { + if (isPromise(newNode)) { + newNode.then(updateNode); return; } - this.setState({ node }); - }; + setNode(newNode); + }, []); - public componentDidMount() { - if (!this.root?.current || !this.props.embeddable) { + useEffect(() => { + if (!rootRef.current || !embeddable) { return; } - this.alreadyMounted = true; - this.updateNode(this.props.embeddable.render(this.root.current) ?? undefined); - } + setEmbeddableHasMounted(true); + updateNode(embeddable.render(rootRef.current) ?? undefined); + embeddable.render(rootRef.current); + }, [updateNode, embeddable]); - public componentDidUpdate(prevProps?: Props) { - let justRendered = false; - if (this.root?.current && this.props.embeddable && !this.alreadyMounted) { - this.alreadyMounted = true; - this.updateNode(this.props.embeddable.render(this.root.current) ?? undefined); - justRendered = true; + useEffect(() => { + if (input && embeddable && embeddableHasMounted) { + embeddable.updateInput(input); } + }, [input, embeddable, embeddableHasMounted]); - if ( - !justRendered && - this.root && - this.root.current && - this.props.embeddable && - this.alreadyMounted && - this.props.input && - prevProps?.input !== this.props.input - ) { - this.props.embeddable.updateInput(this.props.input); - } - } - - public shouldComponentUpdate({ embeddable, error, input, loading }: Props, { node }: State) { - return Boolean( - error !== this.props.error || - loading !== this.props.loading || - embeddable !== this.props.embeddable || - (this.root && this.root.current && embeddable && !this.alreadyMounted) || - input !== this.props.input || - node !== this.state.node - ); - } - - public render() { - return ( - -
{this.state.node}
- {this.props.loading && } - {this.props.error && ( - - {({ message }) => {message}} - - )} -
- ); - } -} + return ( + <> +
{node}
+ {loading && } + {error && ( + + {({ message }) => {message}} + + )} + + ); +}; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index a99204be39694..4389c4e7f4c89 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -24,7 +24,7 @@ export { eventAnnotationGroup } from './event_annotation_group'; export type { EventAnnotationGroupArgs } from './event_annotation_group'; export type { FetchEventAnnotationsArgs } from './fetch_event_annotations/types'; -export type { EventAnnotationArgs, EventAnnotationOutput } from './types'; +export type { EventAnnotationOutput } from './types'; export type { EventAnnotationGroupGetIn, diff --git a/src/plugins/event_annotation/kibana.jsonc b/src/plugins/event_annotation/kibana.jsonc index 315db78b8a68c..79a2dfb105820 100644 --- a/src/plugins/event_annotation/kibana.jsonc +++ b/src/plugins/event_annotation/kibana.jsonc @@ -24,7 +24,6 @@ "data", "savedObjectsFinder", "dataViews", - "kibanaReact", ], "extraPublicDirs": [ "common" diff --git a/src/plugins/event_annotation/public/plugin.ts b/src/plugins/event_annotation/public/plugin.ts index bb1485210835f..316fb8022a611 100644 --- a/src/plugins/event_annotation/public/plugin.ts +++ b/src/plugins/event_annotation/public/plugin.ts @@ -10,7 +10,6 @@ import type { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; import { ContentManagementPublicSetup, ContentManagementPublicStart, @@ -28,8 +27,6 @@ import { eventAnnotationGroup, } from '../common'; import { getFetchEventAnnotations } from './fetch_event_annotations'; -import type { EventAnnotationListingPageServices } from './get_table_list'; -import { ANNOTATIONS_LISTING_VIEW_ID } from '../common/constants'; import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; export interface EventAnnotationStartDependencies { @@ -76,46 +73,6 @@ export class EventAnnotationPlugin defaultMessage: 'Annotation group', }), }); - - dependencies.visualizations.listingViewRegistry.add({ - title: i18n.translate('eventAnnotation.listingViewTitle', { - defaultMessage: 'Annotation groups', - }), - id: ANNOTATIONS_LISTING_VIEW_ID, - getTableList: async (props) => { - const [coreStart, pluginsStart] = await core.getStartServices(); - - const eventAnnotationService = await new EventAnnotationService( - coreStart, - pluginsStart.contentManagement - ).getService(); - - const ids = await pluginsStart.dataViews.getIds(); - const dataViews = await Promise.all(ids.map((id) => pluginsStart.dataViews.get(id))); - - const services: EventAnnotationListingPageServices = { - core: coreStart, - savedObjectsTagging: pluginsStart.savedObjectsTagging, - eventAnnotationService, - PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, - dataViews, - createDataView: pluginsStart.dataViews.create.bind(pluginsStart.dataViews), - queryInputServices: { - http: coreStart.http, - docLinks: coreStart.docLinks, - notifications: coreStart.notifications, - uiSettings: coreStart.uiSettings, - dataViews: pluginsStart.dataViews, - unifiedSearch: pluginsStart.unifiedSearch, - data: pluginsStart.data, - storage: new Storage(localStorage), - }, - }; - - const { getTableList } = await import('./get_table_list'); - return getTableList(props, services); - }, - }); } public start( diff --git a/src/plugins/event_annotation/tsconfig.json b/src/plugins/event_annotation/tsconfig.json index 0c279bcd92436..c57bb18e0e19f 100644 --- a/src/plugins/event_annotation/tsconfig.json +++ b/src/plugins/event_annotation/tsconfig.json @@ -34,13 +34,6 @@ "@kbn/core-saved-objects-api-server", "@kbn/event-annotation-components", "@kbn/event-annotation-common", - "@kbn/kibana-react-plugin", - "@kbn/content-management-table-list-view-table", - "@kbn/content-management-tabbed-table-list-view", - "@kbn/core-lifecycle-browser", - "@kbn/saved-objects-tagging-oss-plugin", - "@kbn/dom-drag-drop", - "@kbn/kibana-utils-plugin", "@kbn/content-management-utils" ], "exclude": [ diff --git a/src/plugins/event_annotation_listing/.i18nrc.json b/src/plugins/event_annotation_listing/.i18nrc.json new file mode 100755 index 0000000000000..752ded7bf7e66 --- /dev/null +++ b/src/plugins/event_annotation_listing/.i18nrc.json @@ -0,0 +1,6 @@ +{ + "prefix": "eventAnnotationsApplication", + "paths": { + "eventAnnotationsApplication": "." + } +} diff --git a/src/plugins/event_annotation_listing/README.md b/src/plugins/event_annotation_listing/README.md new file mode 100644 index 0000000000000..9580dd19d8531 --- /dev/null +++ b/src/plugins/event_annotation_listing/README.md @@ -0,0 +1,3 @@ +# Event Annotation listing + +This plugin contains the library listing page for event annotation groups. diff --git a/src/plugins/event_annotation_listing/jest.config.js b/src/plugins/event_annotation_listing/jest.config.js new file mode 100644 index 0000000000000..2c28dee174da1 --- /dev/null +++ b/src/plugins/event_annotation_listing/jest.config.js @@ -0,0 +1,19 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/event_annotation_listing'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/event_annotation_listing', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/src/plugins/event_annotation_listing/{common,public,server}/**/*.{ts,tsx}', + ], + setupFiles: ['jest-canvas-mock'], +}; diff --git a/src/plugins/event_annotation_listing/kibana.jsonc b/src/plugins/event_annotation_listing/kibana.jsonc new file mode 100644 index 0000000000000..e8f51a817b977 --- /dev/null +++ b/src/plugins/event_annotation_listing/kibana.jsonc @@ -0,0 +1,30 @@ +{ + "type": "plugin", + "id": "@kbn/event-annotation-listing-plugin", + "owner": "@elastic/kibana-visualizations", + "description": "The listing page for event annotations.", + "plugin": { + "id": "eventAnnotationListing", + "server": false, + "browser": true, + "requiredPlugins": [ + "savedObjectsManagement", + "eventAnnotation", + "data", + "presentationUtil", + "visualizations", + "dataViews", + "unifiedSearch", + "kibanaUtils", + "contentManagement", + ], + "optionalPlugins": [ + "savedObjectsTagging", + "lens", + ], + "requiredBundles": [ + "kibanaReact", + ], + "extraPublicDirs": [] + } +} diff --git a/packages/kbn-event-annotation-components/components/__snapshots__/group_editor_flyout.test.tsx.snap b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/__snapshots__/group_editor_flyout.test.tsx.snap similarity index 95% rename from packages/kbn-event-annotation-components/components/__snapshots__/group_editor_flyout.test.tsx.snap rename to src/plugins/event_annotation_listing/public/components/group_editor_flyout/__snapshots__/group_editor_flyout.test.tsx.snap index 49def88aecb70..6dc7b5ecdc2ae 100644 --- a/packages/kbn-event-annotation-components/components/__snapshots__/group_editor_flyout.test.tsx.snap +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/__snapshots__/group_editor_flyout.test.tsx.snap @@ -3,7 +3,6 @@ exports[`group editor flyout renders controls 1`] = ` Object { "TagSelector": [MockFunction], - "createDataView": [MockFunction], "dataViews": Array [ Object { "id": "some-id", @@ -29,6 +28,7 @@ Object { "tags": Array [], "title": "My group", }, + "isAdHocDataView": [Function], "queryInputServices": Object {}, "selectedAnnotation": undefined, "setSelectedAnnotation": [Function], diff --git a/packages/kbn-event-annotation-components/components/group_editor_controls/annotation_list.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/annotation_list.tsx similarity index 75% rename from packages/kbn-event-annotation-components/components/group_editor_controls/annotation_list.tsx rename to src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/annotation_list.tsx index 72a4546659420..176190d07374b 100644 --- a/packages/kbn-event-annotation-components/components/group_editor_controls/annotation_list.tsx +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/annotation_list.tsx @@ -24,7 +24,7 @@ import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; import type { EventAnnotationConfig } from '@kbn/event-annotation-common'; import { createCopiedAnnotation } from '@kbn/event-annotation-common'; -import { getAnnotationAccessor } from '..'; +import { getAnnotationAccessor } from '@kbn/event-annotation-components'; export const AnnotationList = ({ annotations, @@ -40,7 +40,7 @@ export const AnnotationList = ({ setNewAnnotationId(uuidv4()); }, [annotations.length]); - const addAnnotationText = i18n.translate('eventAnnotationComponents.annotationList.add', { + const addAnnotationText = i18n.translate('eventAnnotationListing.annotationList.add', { defaultMessage: 'Add annotation', }); @@ -88,14 +88,26 @@ export const AnnotationList = ({ const [{ dragging }] = useDragDropContext(); return ( -
+
{annotations.map((annotation, index) => (
selectAnnotation(annotation)} @@ -136,34 +148,28 @@ export const AnnotationList = ({ ))} -
addNewAnnotation(sourceId)} > - addNewAnnotation(sourceId)} - > - addNewAnnotation()} - /> - -
+ addNewAnnotation()} + /> +
); }; diff --git a/packages/kbn-event-annotation-components/components/group_editor_controls/group_editor_controls.test.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/group_editor_controls.test.tsx similarity index 71% rename from packages/kbn-event-annotation-components/components/group_editor_controls/group_editor_controls.test.tsx rename to src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/group_editor_controls.test.tsx index 5af119599ca6b..d41045e7cee6a 100644 --- a/packages/kbn-event-annotation-components/components/group_editor_controls/group_editor_controls.test.tsx +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/group_editor_controls.test.tsx @@ -16,7 +16,7 @@ import { EuiTextAreaProps, EuiTextProps } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { act } from 'react-dom/test-utils'; import type { QueryInputServices } from '@kbn/visualization-ui-components'; -import { AnnotationEditorControls, ENABLE_INDIVIDUAL_ANNOTATION_EDITING } from '..'; +import { AnnotationEditorControls } from '@kbn/event-annotation-components'; jest.mock('@elastic/eui', () => { return { @@ -68,15 +68,9 @@ describe('event annotation group editor', () => { } selectedAnnotation={undefined} setSelectedAnnotation={setSelectedAnnotationMock} - createDataView={(spec) => - Promise.resolve({ - id: spec.id, - title: spec.title, - toSpec: () => spec, - } as unknown as DataView) - } queryInputServices={{} as QueryInputServices} showValidation={false} + isAdHocDataView={() => false} /> ); @@ -174,63 +168,54 @@ describe('event annotation group editor', () => { `); }); - if (ENABLE_INDIVIDUAL_ANNOTATION_EDITING) { - it('adds a new annotation group', () => { - act(() => { - wrapper.find('button[data-test-subj="addAnnotation"]').simulate('click'); - }); + // it('adds a new annotation group', () => { + // act(() => { + // wrapper.find('button[data-test-subj="addAnnotation"]').simulate('click'); + // }); - expect(updateMock).toHaveBeenCalledTimes(2); - const newAnnotations = (updateMock.mock.calls[0][0] as EventAnnotationGroupConfig) - .annotations; - expect(newAnnotations.length).toBe(group.annotations.length + 1); - expect(wrapper.exists(AnnotationEditorControls)); // annotation controls opened - }); + // expect(updateMock).toHaveBeenCalledTimes(2); + // const newAnnotations = (updateMock.mock.calls[0][0] as EventAnnotationGroupConfig).annotations; + // expect(newAnnotations.length).toBe(group.annotations.length + 1); + // expect(wrapper.exists(AnnotationEditorControls)); // annotation controls opened + // }); - it('incorporates annotation updates into group', () => { - const annotations = [ - getDefaultManualAnnotation('1', ''), - getDefaultManualAnnotation('2', ''), - ]; - - act(() => { - wrapper.setProps({ - selectedAnnotation: annotations[0], - group: { ...group, annotations }, - }); - }); + it('incorporates annotation updates into group', () => { + const annotations = [getDefaultManualAnnotation('1', ''), getDefaultManualAnnotation('2', '')]; - wrapper.find(AnnotationEditorControls).prop('onAnnotationChange')({ - ...annotations[0], - color: 'newColor', + act(() => { + wrapper.setProps({ + selectedAnnotation: annotations[0], + group: { ...group, annotations }, }); + }); - expect(updateMock).toHaveBeenCalledTimes(1); - expect(updateMock.mock.calls[0][0].annotations[0].color).toBe('newColor'); - expect(setSelectedAnnotationMock).toHaveBeenCalledTimes(1); + wrapper.find(AnnotationEditorControls).prop('onAnnotationChange')({ + ...annotations[0], + color: 'newColor', }); - it('removes an annotation from a group', () => { - const annotations = [ - getDefaultManualAnnotation('1', ''), - getDefaultManualAnnotation('2', ''), - ]; + expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock.mock.calls[0][0].annotations[0].color).toBe('newColor'); + expect(setSelectedAnnotationMock).toHaveBeenCalledTimes(1); + }); - act(() => { - wrapper.setProps({ - group: { ...group, annotations }, - }); - }); + it('removes an annotation from a group', () => { + const annotations = [getDefaultManualAnnotation('1', ''), getDefaultManualAnnotation('2', '')]; - act(() => { - wrapper - .find('button[data-test-subj="indexPattern-dimension-remove"]') - .last() - .simulate('click'); + act(() => { + wrapper.setProps({ + group: { ...group, annotations }, }); + }); - expect(updateMock).toHaveBeenCalledTimes(1); - expect(updateMock.mock.calls[0][0].annotations).toEqual(annotations.slice(0, 1)); + act(() => { + wrapper + .find('button[data-test-subj="indexPattern-dimension-remove"]') + .last() + .simulate('click'); }); - } + + expect(updateMock).toHaveBeenCalledTimes(1); + expect(updateMock.mock.calls[0][0].annotations).toEqual(annotations.slice(0, 1)); + }); }); diff --git a/packages/kbn-event-annotation-components/components/group_editor_controls/group_editor_controls.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/group_editor_controls.tsx similarity index 52% rename from packages/kbn-event-annotation-components/components/group_editor_controls/group_editor_controls.tsx rename to src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/group_editor_controls.tsx index eb5a0dc7ad2fb..329b5228048aa 100644 --- a/packages/kbn-event-annotation-components/components/group_editor_controls/group_editor_controls.tsx +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/group_editor_controls.tsx @@ -16,26 +16,30 @@ import { EuiTitle, } from '@elastic/eui'; import { css } from '@emotion/react'; -import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { DataView } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { SavedObjectsTaggingApiUiComponent } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { euiThemeVars } from '@kbn/ui-theme'; import { QueryInputServices } from '@kbn/visualization-ui-components'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import type { EventAnnotationConfig, EventAnnotationGroupConfig, } from '@kbn/event-annotation-common'; -import { EVENT_ANNOTATION_APP_NAME } from '../../constants'; -import { AnnotationEditorControls } from '../annotation_editor_controls'; +import { + EVENT_ANNOTATION_APP_NAME, + AnnotationEditorControls, +} from '@kbn/event-annotation-components'; import { AnnotationList } from './annotation_list'; -export const ENABLE_INDIVIDUAL_ANNOTATION_EDITING = false; - const isTitleValid = (title: string) => Boolean(title.length); -export const isGroupValid = (group: EventAnnotationGroupConfig) => isTitleValid(group.title); +const isDataViewValid = (dataView: DataView | undefined) => Boolean(dataView?.id); + +export const isGroupValid = (group: EventAnnotationGroupConfig, dataViews: DataView[]) => + isTitleValid(group.title) && + isDataViewValid(dataViews.find(({ id }) => id === group.indexPatternId)); export const GroupEditorControls = ({ group, @@ -43,10 +47,10 @@ export const GroupEditorControls = ({ setSelectedAnnotation: _setSelectedAnnotation, selectedAnnotation, TagSelector, - dataViews: globalDataViews, - createDataView, + dataViews, queryInputServices, showValidation, + isAdHocDataView, }: { group: EventAnnotationGroupConfig; update: (group: EventAnnotationGroupConfig) => void; @@ -54,19 +58,10 @@ export const GroupEditorControls = ({ setSelectedAnnotation: (annotation: EventAnnotationConfig) => void; TagSelector: SavedObjectsTaggingApiUiComponent['SavedObjectSaveModalTagSelector']; dataViews: DataView[]; - createDataView: (spec: DataViewSpec) => Promise; queryInputServices: QueryInputServices; showValidation: boolean; + isAdHocDataView: (id: string) => boolean; }) => { - // save the spec for the life of the component since the user might change their mind after selecting another data view - const [adHocDataView, setAdHocDataView] = useState(); - - useEffect(() => { - if (group.dataViewSpec) { - createDataView(group.dataViewSpec).then(setAdHocDataView); - } - }, [createDataView, group.dataViewSpec]); - const setSelectedAnnotation = useCallback( (newSelection: EventAnnotationConfig) => { update({ @@ -80,45 +75,38 @@ export const GroupEditorControls = ({ [_setSelectedAnnotation, group, update] ); - const dataViews = useMemo(() => { - const items = [...globalDataViews]; - if (adHocDataView) { - items.push(adHocDataView); - } - return items; - }, [adHocDataView, globalDataViews]); - const currentDataView = useMemo( - () => dataViews.find((dataView) => dataView.id === group.indexPatternId) || dataViews[0], + () => dataViews.find((dataView) => dataView.id === group.indexPatternId), [dataViews, group.indexPatternId] ); return !selectedAnnotation ? ( <> -

+

-

+
} > @@ -158,6 +147,7 @@ export const GroupEditorControls = ({ update({ ...group, @@ -166,46 +156,92 @@ export const GroupEditorControls = ({ } /> - {ENABLE_INDIVIDUAL_ANNOTATION_EDITING && ( - <> - - ({ - value, - text: name ?? title, - }))} - value={group.indexPatternId} - onChange={({ target: { value } }) => - update({ - ...group, - indexPatternId: value, - dataViewSpec: - value === adHocDataView?.id ? adHocDataView.toSpec(false) : undefined, - }) - } - /> - - - update({ ...group, annotations: newAnnotations })} - /> - - - )} + + ({ + value, + text: name ?? title, + }))} + value={isDataViewValid(currentDataView) ? group.indexPatternId : undefined} + hasNoInitialSelection={true} + onChange={({ target: { value } }) => { + const selectedDataView = dataViews.find(({ id }) => id === value); + + if (!selectedDataView?.id) { + return; + } + + update({ + ...group, + indexPatternId: value, + dataViewSpec: isAdHocDataView(selectedDataView.id) + ? selectedDataView.toSpec(false) + : undefined, + }); + }} + /> + +
+ +

+ +

+
+ + + {}} + update={(newAnnotations) => update({ ...group, annotations: newAnnotations })} + /> + + +
- ) : ( + ) : currentDataView ? ( setSelectedAnnotation({ ...selectedAnnotation, ...changes })} @@ -214,5 +250,5 @@ export const GroupEditorControls = ({ queryInputServices={queryInputServices} appName={EVENT_ANNOTATION_APP_NAME} /> - ); + ) : null; }; diff --git a/packages/kbn-event-annotation-components/components/group_editor_controls/index.ts b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/index.ts similarity index 100% rename from packages/kbn-event-annotation-components/components/group_editor_controls/index.ts rename to src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_controls/index.ts diff --git a/packages/kbn-event-annotation-components/components/group_editor_flyout.test.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.test.tsx similarity index 81% rename from packages/kbn-event-annotation-components/components/group_editor_flyout.test.tsx rename to src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.test.tsx index 72ba259e1ad49..8f6fcbeceaa6c 100644 --- a/packages/kbn-event-annotation-components/components/group_editor_flyout.test.tsx +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.test.tsx @@ -16,6 +16,7 @@ import { GroupEditorControls } from './group_editor_controls'; import { GroupEditorFlyout } from './group_editor_flyout'; import { DataView } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components'; +import { EmbeddableComponent } from '@kbn/lens-plugin/public'; const simulateButtonClick = (component: ShallowWrapper, selector: string) => { (component.find(selector) as ShallowWrapper[0]>).prop('onClick')!( @@ -27,6 +28,7 @@ const SELECTORS = { SAVE_BUTTON: '[data-test-subj="saveAnnotationGroup"]', CANCEL_BUTTON: '[data-test-subj="cancelGroupEdit"]', BACK_BUTTON: '[data-test-subj="backToGroupSettings"]', + TOP_BACK_BUTTON: '[data-test-subj="backToGroupSettingsTop"]', }; const assertGroupEditingState = (component: ShallowWrapper) => { @@ -59,14 +61,15 @@ describe('group editor flyout', () => { let onSave: jest.Mock; let onClose: jest.Mock; let updateGroup: jest.Mock; + const LensEmbeddableComponent: EmbeddableComponent = jest.fn(); - beforeEach(() => { + const mountComponent = (groupToUse: EventAnnotationGroupConfig) => { onSave = jest.fn(); onClose = jest.fn(); updateGroup = jest.fn(); - component = shallow( + return shallow( { savedObjectsTagging={mockTaggingApi} createDataView={jest.fn()} queryInputServices={{} as QueryInputServices} + LensEmbeddableComponent={LensEmbeddableComponent} + searchSessionId={'searchSessionId'} + refreshSearchSession={jest.fn()} + timePickerQuickRanges={[]} /> ); + }; + + beforeEach(() => { + component = mountComponent(group); }); it('renders controls', () => { @@ -112,17 +123,20 @@ describe('group editor flyout', () => { expect(updateGroup).toHaveBeenCalledWith(newGroup); }); - test('specific annotation editing', () => { - assertGroupEditingState(component); + test.each([SELECTORS.BACK_BUTTON, SELECTORS.TOP_BACK_BUTTON])( + 'specific annotation editing', + (backButtonSelector) => { + assertGroupEditingState(component); - component.find(GroupEditorControls).prop('setSelectedAnnotation')(annotation); + component.find(GroupEditorControls).prop('setSelectedAnnotation')(annotation); - assertAnnotationEditingState(component); + assertAnnotationEditingState(component); - component.find(SELECTORS.BACK_BUTTON).simulate('click'); + component.find(backButtonSelector).simulate('click'); - assertGroupEditingState(component); - }); + assertGroupEditingState(component); + } + ); it('removes active annotation instead of signaling close', () => { component.find(GroupEditorControls).prop('setSelectedAnnotation')(annotation); diff --git a/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.tsx new file mode 100644 index 0000000000000..7bd15fcb16ddd --- /dev/null +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_editor_flyout.tsx @@ -0,0 +1,253 @@ +/* + * 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 { + EuiFlyout, + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiButton, + htmlIdGenerator, + useIsWithinBreakpoints, + EuiButtonIcon, +} from '@elastic/eui'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { QueryInputServices } from '@kbn/visualization-ui-components'; +import type { + EventAnnotationConfig, + EventAnnotationGroupConfig, +} from '@kbn/event-annotation-common'; +import { css } from '@emotion/react'; +import type { EmbeddableComponent as LensEmbeddableComponent } from '@kbn/lens-plugin/public'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; +import { GroupEditorControls, isGroupValid } from './group_editor_controls'; +import { GroupPreview } from './group_preview'; + +export const GroupEditorFlyout = ({ + group, + updateGroup, + onClose: parentOnClose, + onSave, + savedObjectsTagging, + dataViews: globalDataViews, + createDataView, + LensEmbeddableComponent, + queryInputServices, + searchSessionId, + refreshSearchSession, + timePickerQuickRanges, +}: { + group: EventAnnotationGroupConfig; + updateGroup: (newGroup: EventAnnotationGroupConfig) => void; + onClose: () => void; + onSave: () => void; + savedObjectsTagging: SavedObjectsTaggingApi; + dataViews: DataView[]; + createDataView: (spec: DataViewSpec) => Promise; + LensEmbeddableComponent: LensEmbeddableComponent; + queryInputServices: QueryInputServices; + searchSessionId: string; + refreshSearchSession: () => void; + timePickerQuickRanges: Array<{ from: string; to: string; display: string }> | undefined; +}) => { + const flyoutHeadingId = useMemo(() => htmlIdGenerator()(), []); + const flyoutBodyOverflowRef = useRef(null); + useEffect(() => { + if (!flyoutBodyOverflowRef.current) { + flyoutBodyOverflowRef.current = document.querySelector('.euiFlyoutBody__overflow'); + } + }, []); + + const [hasAttemptedSave, setHasAttemptedSave] = useState(false); + + const resetContentScroll = useCallback( + () => flyoutBodyOverflowRef.current && flyoutBodyOverflowRef.current.scroll(0, 0), + [] + ); + + // save the spec for the life of the component since the user might change their mind after selecting another data view + const [adHocDataView, setAdHocDataView] = useState(); + + useEffect(() => { + if (group.dataViewSpec) { + createDataView(group.dataViewSpec).then(setAdHocDataView); + } + }, [createDataView, group.dataViewSpec]); + + const dataViews = useMemo(() => { + const items = [...globalDataViews]; + if (adHocDataView) { + items.push(adHocDataView); + } + return items; + }, [adHocDataView, globalDataViews]); + + const [selectedAnnotation, _setSelectedAnnotation] = useState(); + const setSelectedAnnotation = useCallback( + (newValue: EventAnnotationConfig | undefined) => { + if ((!newValue && selectedAnnotation) || (newValue && !selectedAnnotation)) + resetContentScroll(); + _setSelectedAnnotation(newValue); + }, + [resetContentScroll, selectedAnnotation] + ); + const onClose = () => (selectedAnnotation ? setSelectedAnnotation(undefined) : parentOnClose()); + + const showPreview = !useIsWithinBreakpoints(['xs', 's', 'm']); + + return ( + + + + + +

+ {selectedAnnotation ? ( + + + setSelectedAnnotation(undefined)} + data-test-subj="backToGroupSettingsTop" + /> + + + + + + ) : ( + + )} +

+
+
+ + + id === adHocDataView?.id} + /> + + + + + {selectedAnnotation ? ( + + setSelectedAnnotation(undefined)} + > + + + + ) : ( + <> + + + + + + + { + setHasAttemptedSave(true); + + if (isGroupValid(group, dataViews)) { + onSave(); + } + }} + > + + + + + )} + + +
+ {showPreview && ( + + + + )} +
+
+ ); +}; diff --git a/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.test.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.test.tsx new file mode 100644 index 0000000000000..09c7c5a265355 --- /dev/null +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.test.tsx @@ -0,0 +1,307 @@ +/* + * 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 { getDefaultManualAnnotation } from '@kbn/event-annotation-common'; +import type { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; +import React from 'react'; +import { + DataView, + DataViewField, + DataViewFieldMap, + IIndexPatternFieldList, +} from '@kbn/data-views-plugin/common'; +import { + EmbeddableComponent, + FieldBasedIndexPatternColumn, + TypedLensByValueInput, +} from '@kbn/lens-plugin/public'; +import { Datatable } from '@kbn/expressions-plugin/common'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import userEvent from '@testing-library/user-event'; +import { I18nProvider } from '@kbn/i18n-react'; +import { GroupPreview } from './group_preview'; +import { LensByValueInput } from '@kbn/lens-plugin/public/embeddable'; +import { DATA_LAYER_ID, DATE_HISTOGRAM_COLUMN_ID, getCurrentTimeField } from './lens_attributes'; +import moment from 'moment'; + +class EuiSuperDatePickerTestHarness { + public static get currentCommonlyUsedRange() { + return screen.queryByTestId('superDatePickerShowDatesButton')?.textContent ?? ''; + } + + // TODO - add assertion with date formatting + public static get currentRange() { + if (screen.queryByTestId('superDatePickerShowDatesButton')) { + // showing a commonly-used range + return { from: '', to: '' }; + } + + return { + from: screen.getByTestId('superDatePickerstartDatePopoverButton').textContent, + to: screen.getByTestId('superDatePickerendDatePopoverButton').textContent, + }; + } + + static togglePopover() { + userEvent.click(screen.getByRole('button', { name: 'Date quick select' })); + } + + static async selectCommonlyUsedRange(label: string) { + if (!screen.queryByText('Commonly used')) this.togglePopover(); + + // Using fireEvent here because userEvent erroneously claims that + // pointer-events is set to 'none'. + // + // I have verified that this fixed on the latest version of the @testing-library/user-event package + fireEvent.click(await screen.findByText(label)); + } + + static refresh() { + userEvent.click(screen.getByRole('button', { name: 'Refresh' })); + } +} + +describe('group editor preview', () => { + const annotation = getDefaultManualAnnotation('my-id', 'some-timestamp'); + + const group: EventAnnotationGroupConfig = { + annotations: [annotation], + description: '', + tags: [], + indexPatternId: 'some-id', + title: 'My group', + ignoreGlobalFilters: false, + }; + + const BRUSH_RANGE = [0, 100]; + + const LensEmbeddableComponent: EmbeddableComponent = (props) => ( +
+
{JSON.stringify(props.timeRange)}
+
{props.searchSessionId}
+
+ {JSON.stringify((props as LensByValueInput).attributes)} +
+
+ ); + + const getEmbeddableTimeRange = () => { + const serialized = screen.getByTestId('chartTimeRange').textContent; + return serialized ? JSON.parse(serialized) : null; + }; + + const getEmbeddableSearchSessionId = () => { + return screen.getByTestId('chartSearchSessionId').textContent; + }; + + const getLensAttributes = () => { + const serialized = screen.queryByTestId('lensAttributes')?.textContent; + return serialized ? JSON.parse(serialized) : null; + }; + + let rerender: (ui: React.ReactElement>) => void; + + const defaultProps: Parameters[0] = { + group, + dataViews: [ + { + id: 'some-id', + title: 'My Data View', + timeFieldName: '@timestamp', + fields: { + getByType: jest.fn(() => [ + { + type: 'date', + name: '@timestamp', + } as DataViewField, + { + type: 'date', + name: 'other-time-field', + } as DataViewField, + ]), + } as unknown as IIndexPatternFieldList & { toSpec: () => DataViewFieldMap }, + } as DataView, + { + id: 'a-different-id', + title: 'My Data View', + timeFieldName: 'other-time-field', + fields: { + getByType: jest.fn(() => [ + { + type: 'date', + name: '@timestamp', + } as DataViewField, + { + type: 'date', + name: 'other-time-field', + } as DataViewField, + ]), + } as unknown as IIndexPatternFieldList & { toSpec: () => DataViewFieldMap }, + } as DataView, + ], + LensEmbeddableComponent, + searchSessionId: 'some-search-session-id', + refreshSearchSession: jest.fn(), + timePickerQuickRanges: [{ from: 'now/d', to: 'now/d', display: 'Today' }], + }; + + beforeEach(() => { + const renderResult = render( + + + + ); + + rerender = renderResult.rerender; + }); + + it('updates the chart time range', async () => { + // default + expect(EuiSuperDatePickerTestHarness.currentCommonlyUsedRange).toBe('Last 15 minutes'); + expect(getEmbeddableTimeRange()).toEqual({ from: 'now-15m', to: 'now' }); + + // from time picker + await EuiSuperDatePickerTestHarness.selectCommonlyUsedRange('Today'); + + expect(EuiSuperDatePickerTestHarness.currentCommonlyUsedRange).toBe('Today'); + expect(getEmbeddableTimeRange()).toEqual({ from: 'now/d', to: 'now/d' }); + + // from chart brush + userEvent.click(screen.getByTestId('brushEnd')); + + const format = 'MMM D, YYYY @ HH:mm:ss.SSS'; // from https://github.com/elastic/eui/blob/6a30eba7c2a154691c96a1d17c8b2f3506d351a3/src/components/date_picker/super_date_picker/super_date_picker.tsx#L222; + expect(EuiSuperDatePickerTestHarness.currentRange).toEqual({ + from: moment(BRUSH_RANGE[0]).format(format), + to: moment(BRUSH_RANGE[1]).format(format), + }); + expect(getEmbeddableTimeRange()).toEqual({ + from: new Date(BRUSH_RANGE[0]).toISOString(), + to: new Date(BRUSH_RANGE[1]).toISOString(), + }); + }); + + it('updates the time field', async () => { + EuiSuperDatePickerTestHarness.togglePopover(); + + const select = screen.getByRole('combobox', { name: 'Time field' }); + + expect(select).toHaveValue('@timestamp'); + expect(getCurrentTimeField(getLensAttributes())).toBe('@timestamp'); + + userEvent.selectOptions(select, 'other-time-field'); + + expect(select).toHaveValue('other-time-field'); + + await waitFor(() => { + expect(getCurrentTimeField(getLensAttributes())).toBe('other-time-field'); + }); + }); + + it('refreshes the chart data', () => { + expect(defaultProps.refreshSearchSession).not.toHaveBeenCalled(); + expect(getEmbeddableSearchSessionId()).toBe(defaultProps.searchSessionId); + + EuiSuperDatePickerTestHarness.refresh(); + + expect(defaultProps.refreshSearchSession).toHaveBeenCalled(); + + rerender( + + + + ); + + expect(getEmbeddableSearchSessionId()).toBe('new-search-session-id'); + }); + + describe('data views', () => { + const assertDataView = (id: string, attributes: TypedLensByValueInput['attributes']) => + expect(attributes.references[0].id).toBe(id); + const assertTimeField = (fieldName: string, attributes: TypedLensByValueInput['attributes']) => + expect( + ( + attributes.state.datasourceStates.formBased.layers[DATA_LAYER_ID].columns[ + DATE_HISTOGRAM_COLUMN_ID + ] as FieldBasedIndexPatternColumn + ).sourceField + ).toBe(fieldName); + + it('uses correct data view', async () => { + assertDataView(group.indexPatternId, getLensAttributes()); + + rerender( + + + + ); + + await waitFor(() => { + assertDataView('a-different-id', getLensAttributes()); + assertTimeField('other-time-field', getLensAttributes()); + }); + }); + + it('supports ad-hoc data view', () => { + const adHocDataView = { + id: 'adhoc-1', + title: 'my-pattern*', + timeFieldName: '@timestamp', + sourceFilters: [], + fieldFormats: {}, + runtimeFieldMap: {}, + fieldAttrs: {}, + allowNoIndex: false, + name: 'My ad-hoc data view', + }; + + rerender( + + + + ); + + waitFor(() => { + const attributes = getLensAttributes(); + expect(attributes.references).toHaveLength(0); + expect(attributes.state.adHocDataViews![adHocDataView.id!]).toEqual(adHocDataView); + expect(attributes.state.internalReferences).toHaveLength(1); + expect(attributes.state.internalReferences![0].id).toBe(adHocDataView.id); + }); + }); + + it('handles missing data view', async () => { + rerender( + + + + ); + + await waitFor(() => { + expect( + screen.getByRole('heading', { name: 'Select a valid data view' }) + ).toBeInTheDocument(); + }); + expect(getLensAttributes()).toBeNull(); // chart shouldn't be rendered + }); + }); +}); diff --git a/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.tsx b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.tsx new file mode 100644 index 0000000000000..3ee1a9a11f5fc --- /dev/null +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/group_preview.tsx @@ -0,0 +1,260 @@ +/* + * 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 { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiSelect, + EuiSuperDatePicker, + EuiSuperDatePickerProps, + EuiTitle, +} from '@elastic/eui'; +import { TimeRange } from '@kbn/es-query'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; +import type { + EmbeddableComponent as LensEmbeddableComponent, + TypedLensByValueInput, +} from '@kbn/lens-plugin/public'; +import { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; +import { css } from '@emotion/react'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { i18n } from '@kbn/i18n'; +import { getLensAttributes } from './lens_attributes'; + +export const GroupPreview = ({ + group, + dataViews, + LensEmbeddableComponent, + searchSessionId, + refreshSearchSession, + timePickerQuickRanges, +}: { + group: EventAnnotationGroupConfig; + dataViews: DataView[]; + LensEmbeddableComponent: LensEmbeddableComponent; + searchSessionId: string; + refreshSearchSession: () => void; + timePickerQuickRanges: Array<{ from: string; to: string; display: string }> | undefined; +}) => { + const [chartTimeRange, setChartTimeRange] = useState({ from: 'now-15m', to: 'now' }); + const commonlyUsedRanges = useMemo( + () => + timePickerQuickRanges?.map( + ({ from, to, display }: { from: string; to: string; display: string }) => { + return { + start: from, + end: to, + label: display, + }; + } + ) ?? [], + [timePickerQuickRanges] + ); + + const customQuickSelectRender = useCallback< + Required['customQuickSelectRender'] + >( + ({ quickSelect, commonlyUsedRanges: ranges, customQuickSelectPanels }) => + ( + <> + {customQuickSelectPanels} + {quickSelect} + {ranges} + + ) as React.ReactNode, + [] + ); + + const currentDataView = useMemo( + () => dataViews.find((dataView) => dataView.id === group.indexPatternId), + [dataViews, group.indexPatternId] + ); + + const timeFieldNames = useMemo( + () => currentDataView?.fields.getByType('date').map((field) => field.name) ?? [], + [currentDataView?.fields] + ); + + // We can assume that there is at least one time field because we don't allow annotation groups to be created without one + const defaultTimeFieldName = useMemo( + () => currentDataView?.timeFieldName ?? timeFieldNames[0], + [currentDataView?.timeFieldName, timeFieldNames] + ); + + const [currentTimeFieldName, setCurrentTimeFieldName] = useState(defaultTimeFieldName); + + const [lensAttributes, setLensAttributes] = useState( + getLensAttributes(group, currentTimeFieldName) + ); + + // we don't use currentDataView directly to hide/show the missing prompt because we want to delay + // the embeddable render until the lensAttributes have been updated in useDebounce + // in the case that the user selects a new data view + const [showMissingDataViewPrompt, setShowMissingDataViewPrompt] = useState( + !currentDataView + ); + + useEffect(() => { + setCurrentTimeFieldName(defaultTimeFieldName); + }, [defaultTimeFieldName]); + + useDebounce( + () => { + setLensAttributes(getLensAttributes(group, currentTimeFieldName)); + setShowMissingDataViewPrompt(!currentDataView); + }, + 250, + [group, currentTimeFieldName] + ); + + return ( + <> + + + + +

+ +

+
+
+ + setChartTimeRange({ from, to })} + onRefresh={({ start: from, end: to }) => { + setChartTimeRange({ from, to }); + refreshSearchSession(); + }} + start={chartTimeRange.from} + end={chartTimeRange.to} + compressed + commonlyUsedRanges={commonlyUsedRanges} + updateButtonProps={{ + iconOnly: true, + fill: false, + }} + customQuickSelectRender={customQuickSelectRender} + customQuickSelectPanels={[ + { + title: i18n.translate('eventAnnotationListing.timeField', { + defaultMessage: 'Time field', + }), + content: ( + ({ + text: name, + }))} + value={currentTimeFieldName} + onChange={(e) => setCurrentTimeFieldName(e.target.value)} + /> + ), + }, + ]} + /> + +
+
+ + {!showMissingDataViewPrompt ? ( + + +
div { + height: 400px; + width: 100%; + } + `} + > + + setChartTimeRange({ + from: new Date(range[0]).toISOString(), + to: new Date(range[1]).toISOString(), + }) + } + searchSessionId={searchSessionId} + /> +
+
+
+ ) : ( + + + + + + } + body={ +

+ +

+ } + /> +
+
+ )} +
+ + ); +}; diff --git a/src/plugins/event_annotation_listing/public/components/group_editor_flyout/index.ts b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/index.ts new file mode 100644 index 0000000000000..312fe66a3e406 --- /dev/null +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export { GroupEditorFlyout } from './group_editor_flyout'; diff --git a/src/plugins/event_annotation_listing/public/components/group_editor_flyout/lens_attributes.ts b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/lens_attributes.ts new file mode 100644 index 0000000000000..7943b1d905b6a --- /dev/null +++ b/src/plugins/event_annotation_listing/public/components/group_editor_flyout/lens_attributes.ts @@ -0,0 +1,153 @@ +/* + * 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 { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; +import { FieldBasedIndexPatternColumn, TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import { XYPersistedByValueAnnotationLayerConfig } from '@kbn/lens-plugin/public/async_services'; + +export const DATA_LAYER_ID = 'data-layer-id'; +export const DATE_HISTOGRAM_COLUMN_ID = 'date-histogram-column-id'; +const ANNOTATION_LAYER_ID = 'annotation-layer-id'; + +export const getLensAttributes = (group: EventAnnotationGroupConfig, timeField: string) => + ({ + title: 'Line visualization with annotation layer', // TODO - should this be translated? + description: '', + visualizationType: 'lnsXY', + type: 'lens', + state: { + visualization: { + legend: { + isVisible: true, + position: 'right', + }, + valueLabels: 'hide', + fittingFunction: 'None', + axisTitlesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + tickLabelsVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + labelsOrientation: { + x: 0, + yLeft: 0, + yRight: 0, + }, + gridlinesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + preferredSeriesType: 'line', + layers: [ + { + layerId: DATA_LAYER_ID, + accessors: ['a7264a99-cd42-4b3f-855f-05364df71a71'], + position: 'top', + seriesType: 'line', + showGridlines: false, + layerType: 'data', + xAccessor: [DATE_HISTOGRAM_COLUMN_ID], + }, + { + layerId: ANNOTATION_LAYER_ID, + layerType: 'annotations', + persistanceType: 'byValue', + ...group, + } as XYPersistedByValueAnnotationLayerConfig, + ], + }, + query: { + query: '', + language: 'kuery', + }, + filters: [], + datasourceStates: { + formBased: { + layers: { + [DATA_LAYER_ID]: { + columns: { + [DATE_HISTOGRAM_COLUMN_ID]: { + label: 'timestamp', + dataType: 'date', + operationType: 'date_histogram', + sourceField: timeField, + isBucketed: true, + scale: 'interval', + params: { + interval: 'auto', + includeEmptyRows: true, + dropPartials: false, + }, + } as FieldBasedIndexPatternColumn, + 'a7264a99-cd42-4b3f-855f-05364df71a71': { + label: 'Count of records', + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: '___records___', + params: { + emptyAsNull: true, + }, + } as FieldBasedIndexPatternColumn, + }, + columnOrder: [DATE_HISTOGRAM_COLUMN_ID, 'a7264a99-cd42-4b3f-855f-05364df71a71'], + incompleteColumns: {}, + sampling: 1, + }, + }, + }, + textBased: { + layers: {}, + }, + }, + + ...(group.dataViewSpec + ? { + internalReferences: [ + { + type: 'index-pattern', + id: group.dataViewSpec.id!, + name: `indexpattern-datasource-layer-${DATA_LAYER_ID}`, + }, + { + type: 'index-pattern', + id: group.dataViewSpec.id!, + name: `xy-visualization-layer-${ANNOTATION_LAYER_ID}`, + }, + ], + adHocDataViews: { + [group.dataViewSpec.id!]: group.dataViewSpec, + }, + } + : { internalReferences: [], adHocDataViews: {} }), + }, + references: group.dataViewSpec + ? [] + : [ + { + type: 'index-pattern', + id: group.indexPatternId, + name: `indexpattern-datasource-layer-${DATA_LAYER_ID}`, + }, + ], + } as TypedLensByValueInput['attributes']); + +export const getCurrentTimeField = (attributes: TypedLensByValueInput['attributes']) => { + return ( + attributes.state.datasourceStates.formBased.layers[DATA_LAYER_ID].columns[ + DATE_HISTOGRAM_COLUMN_ID + ] as FieldBasedIndexPatternColumn + ).sourceField; +}; diff --git a/packages/kbn-event-annotation-components/components/table_list.test.tsx b/src/plugins/event_annotation_listing/public/components/table_list.test.tsx similarity index 85% rename from packages/kbn-event-annotation-components/components/table_list.test.tsx rename to src/plugins/event_annotation_listing/public/components/table_list.test.tsx index d8e124e37ef6e..65970aeca3c7d 100644 --- a/packages/kbn-event-annotation-components/components/table_list.test.tsx +++ b/src/plugins/event_annotation_listing/public/components/table_list.test.tsx @@ -16,7 +16,7 @@ import { TableListViewTable, type UserContentCommonSchema, } from '@kbn/content-management-table-list-view-table'; -import type { EventAnnotationServiceType } from '../types'; +import type { EventAnnotationServiceType } from '@kbn/event-annotation-components/types'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { shallow, ShallowWrapper } from 'enzyme'; import { @@ -31,6 +31,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { QueryInputServices } from '@kbn/visualization-ui-components'; import { toastsServiceMock } from '@kbn/core-notifications-browser-mocks/src/toasts_service.mock'; import { IToasts } from '@kbn/core-notifications-browser'; +import { ISessionService } from '@kbn/data-plugin/public'; describe('annotation list view', () => { const adHocDVId = 'ad-hoc'; @@ -51,6 +52,7 @@ describe('annotation list view', () => { let wrapper: ShallowWrapper; let mockEventAnnotationService: EventAnnotationServiceType; let mockToasts: IToasts; + const searchSessionStartMethod = jest.fn(() => 'some-session-id'); beforeEach(() => { mockEventAnnotationService = { @@ -95,6 +97,10 @@ describe('annotation list view', () => { queryInputServices={{} as QueryInputServices} toasts={mockToasts} navigateToLens={() => {}} + LensEmbeddableComponent={() =>
} + sessionService={ + { start: searchSessionStartMethod } as Partial as ISessionService + } /> ); }); @@ -190,6 +196,7 @@ describe('annotation list view', () => { expect(mockEventAnnotationService.loadAnnotationGroup).toHaveBeenCalledWith('1234'); expect(wrapper.find(GroupEditorFlyout).exists()).toBeTruthy(); + expect(wrapper.find(GroupEditorFlyout).prop('searchSessionId')).toBe('some-session-id'); const updatedGroup = { ...group, tags: ['my-new-tag'] }; @@ -210,6 +217,24 @@ describe('annotation list view', () => { ); // (should refresh list) }); + it('refreshes the search session', async () => { + act(() => { + wrapper.find(TableListViewTable).prop('editItem')!({ + id: '1234', + } as UserContentCommonSchema); + }); + + // wait one tick to give promise time to settle + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(wrapper.find(GroupEditorFlyout).prop('searchSessionId')).toBe('some-session-id'); + + searchSessionStartMethod.mockReturnValue('new-session-id'); + wrapper.find(GroupEditorFlyout).prop('refreshSearchSession')(); + + expect(wrapper.find(GroupEditorFlyout).prop('searchSessionId')).toBe('new-session-id'); + }); + it('opens editor when title is clicked', async () => { act(() => { wrapper.find(TableListViewTable).prop('onClickTitle')!({ diff --git a/packages/kbn-event-annotation-components/components/table_list.tsx b/src/plugins/event_annotation_listing/public/components/table_list.tsx similarity index 71% rename from packages/kbn-event-annotation-components/components/table_list.tsx rename to src/plugins/event_annotation_listing/public/components/table_list.tsx index 0085678cbd786..603d39e193360 100644 --- a/packages/kbn-event-annotation-components/components/table_list.tsx +++ b/src/plugins/event_annotation_listing/public/components/table_list.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { TableListViewTable } from '@kbn/content-management-table-list-view-table'; import type { TableListTabParentProps } from '@kbn/content-management-tabbed-table-list-view'; import { i18n } from '@kbn/i18n'; @@ -16,13 +16,16 @@ import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plug import { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components'; import { IToasts } from '@kbn/core-notifications-browser'; -import { EuiButton, EuiEmptyPrompt, EuiTitle } from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt, EuiIcon, EuiText, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { EmbeddableComponent as LensEmbeddableComponent } from '@kbn/lens-plugin/public'; import type { EventAnnotationGroupConfig, EventAnnotationGroupContent, } from '@kbn/event-annotation-common'; -import type { EventAnnotationServiceType } from '../types'; +import { ISessionService, UI_SETTINGS } from '@kbn/data-plugin/public'; +import { EventAnnotationServiceType } from '@kbn/event-annotation-components'; +import { css } from '@emotion/react'; import { GroupEditorFlyout } from './group_editor_flyout'; export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; @@ -35,7 +38,7 @@ const getCustomColumn = (dataViews: DataView[]) => { return { field: 'dataView', - name: i18n.translate('eventAnnotationComponents.tableList.dataView', { + name: i18n.translate('eventAnnotationListing.tableList.dataView', { defaultMessage: 'Data view', }), sortable: false, @@ -44,7 +47,24 @@ const getCustomColumn = (dataViews: DataView[]) => {
{record.attributes.dataViewSpec ? record.attributes.dataViewSpec.name - : dataViewNameMap[record.attributes.indexPatternId]} + : dataViewNameMap[record.attributes.indexPatternId] ?? ( + + + ), + }} + /> + + )}
), }; @@ -53,6 +73,7 @@ const getCustomColumn = (dataViews: DataView[]) => { export const EventAnnotationGroupTableList = ({ uiSettings, eventAnnotationService, + sessionService, visualizeCapabilities, savedObjectsTagging, parentProps, @@ -61,9 +82,11 @@ export const EventAnnotationGroupTableList = ({ queryInputServices, toasts, navigateToLens, + LensEmbeddableComponent, }: { uiSettings: IUiSettingsClient; eventAnnotationService: EventAnnotationServiceType; + sessionService: ISessionService; visualizeCapabilities: Record>; savedObjectsTagging: SavedObjectsTaggingApi; parentProps: TableListTabParentProps; @@ -72,10 +95,23 @@ export const EventAnnotationGroupTableList = ({ queryInputServices: QueryInputServices; toasts: IToasts; navigateToLens: () => void; + LensEmbeddableComponent: LensEmbeddableComponent; }) => { const listingLimit = uiSettings.get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = uiSettings.get(SAVED_OBJECTS_PER_PAGE_SETTING); + const [searchSessionId, setSearchSessionId] = useState(sessionService.start()); + + const refreshSearchSession = useCallback(() => { + setSearchSessionId(sessionService.start()); + }, [sessionService]); + + useEffect(() => { + return () => { + sessionService.clear(); + }; + }, [sessionService]); + const [refreshListBouncer, setRefreshListBouncer] = useState(false); const refreshList = useCallback(() => { @@ -138,15 +174,20 @@ export const EventAnnotationGroupTableList = ({ savedObjectsTagging={savedObjectsTagging} dataViews={dataViews} createDataView={createDataView} + LensEmbeddableComponent={LensEmbeddableComponent} queryInputServices={queryInputServices} + searchSessionId={searchSessionId} + refreshSearchSession={refreshSearchSession} + timePickerQuickRanges={uiSettings.get(UI_SETTINGS.TIMEPICKER_QUICK_RANGES)} /> ) : undefined; return ( <> + id="eventAnnotation" refreshListBouncer={refreshListBouncer} - tableCaption={i18n.translate('eventAnnotationComponents.tableList.listTitle', { + tableCaption={i18n.translate('eventAnnotationListing.tableList.listTitle', { defaultMessage: 'Annotation Library', })} findItems={fetchItems} @@ -162,11 +203,12 @@ export const EventAnnotationGroupTableList = ({ customTableColumn={getCustomColumn(dataViews)} emptyPrompt={

@@ -175,27 +217,26 @@ export const EventAnnotationGroupTableList = ({ body={

} actions={ } iconType="flag" /> } - entityName={i18n.translate('eventAnnotationComponents.tableList.entityName', { + entityName={i18n.translate('eventAnnotationListing.tableList.entityName', { defaultMessage: 'annotation group', })} - entityNamePlural={i18n.translate('eventAnnotationComponents.tableList.entityNamePlural', { + entityNamePlural={i18n.translate('eventAnnotationListing.tableList.entityNamePlural', { defaultMessage: 'annotation groups', })} onClickTitle={editItem} diff --git a/src/plugins/event_annotation_listing/public/constants.ts b/src/plugins/event_annotation_listing/public/constants.ts new file mode 100644 index 0000000000000..7bd5fb8c9c1a7 --- /dev/null +++ b/src/plugins/event_annotation_listing/public/constants.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export const EVENT_ANNOTATION_APP_NAME = 'annotations'; diff --git a/src/plugins/event_annotation/public/get_table_list.tsx b/src/plugins/event_annotation_listing/public/get_table_list.tsx similarity index 82% rename from src/plugins/event_annotation/public/get_table_list.tsx rename to src/plugins/event_annotation_listing/public/get_table_list.tsx index d05eba6a0ecc8..0bcffc803cc94 100644 --- a/src/plugins/event_annotation/public/get_table_list.tsx +++ b/src/plugins/event_annotation_listing/public/get_table_list.tsx @@ -16,8 +16,10 @@ import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plug import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common'; import type { QueryInputServices } from '@kbn/visualization-ui-components'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; -import { EventAnnotationGroupTableList } from '@kbn/event-annotation-components'; -import type { EventAnnotationServiceType } from '.'; +import type { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; +import type { EmbeddableComponent as LensEmbeddableComponent } from '@kbn/lens-plugin/public'; +import { ISessionService } from '@kbn/data-plugin/public'; +import { EventAnnotationGroupTableList } from './components/table_list'; export interface EventAnnotationListingPageServices { core: CoreStart; @@ -27,6 +29,8 @@ export interface EventAnnotationListingPageServices { dataViews: DataView[]; createDataView: (spec: DataViewSpec) => Promise; queryInputServices: QueryInputServices; + LensEmbeddableComponent: LensEmbeddableComponent; + sessionService: ISessionService; } export const getTableList = ( @@ -54,6 +58,8 @@ export const getTableList = ( createDataView={services.createDataView} queryInputServices={services.queryInputServices} navigateToLens={() => services.core.application.navigateToApp('lens')} + LensEmbeddableComponent={services.LensEmbeddableComponent} + sessionService={services.sessionService} /> diff --git a/src/plugins/event_annotation_listing/public/index.ts b/src/plugins/event_annotation_listing/public/index.ts new file mode 100644 index 0000000000000..b81bd6424ba7a --- /dev/null +++ b/src/plugins/event_annotation_listing/public/index.ts @@ -0,0 +1,26 @@ +/* + * 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 { EventAnnotationListingPlugin } from './plugin'; +export const plugin = () => new EventAnnotationListingPlugin(); +export type { + EventAnnotationListingPluginSetup as eventAnnotationListingPluginSetup, + EventAnnotationListingPluginStart as eventAnnotationListingPluginStart, +} from './plugin'; +export { + defaultAnnotationColor, + defaultAnnotationRangeColor, + isRangeAnnotationConfig, + isManualPointAnnotationConfig, + isQueryAnnotationConfig, +} from '@kbn/event-annotation-common'; +export { + AnnotationEditorControls, + annotationsIconSet, + getAnnotationAccessor, +} from '@kbn/event-annotation-components'; diff --git a/src/plugins/event_annotation_listing/public/plugin.ts b/src/plugins/event_annotation_listing/public/plugin.ts new file mode 100644 index 0000000000000..a5db1a5ae135f --- /dev/null +++ b/src/plugins/event_annotation_listing/public/plugin.ts @@ -0,0 +1,101 @@ +/* + * 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 type { Plugin, CoreSetup, CoreStart } from '@kbn/core/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public/types'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { EventAnnotationPluginStart } from '@kbn/event-annotation-plugin/public'; +import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import type { EventAnnotationListingPageServices } from './get_table_list'; + +export interface EventAnnotationListingStartDependencies { + savedObjectsManagement: SavedObjectsManagementPluginStart; + eventAnnotation: EventAnnotationPluginStart; + data: DataPublicPluginStart; + savedObjectsTagging: SavedObjectTaggingPluginStart; + presentationUtil: PresentationUtilPluginStart; + dataViews: DataViewsPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; + contentManagement: ContentManagementPublicStart; + lens: LensPublicStart; +} + +interface SetupDependencies { + visualizations: VisualizationsSetup; +} + +/** @public */ +export type EventAnnotationListingPluginStart = void; +export type EventAnnotationListingPluginSetup = void; + +/** @public */ +export class EventAnnotationListingPlugin + implements + Plugin< + EventAnnotationListingPluginSetup, + EventAnnotationListingPluginStart, + SetupDependencies, + EventAnnotationListingStartDependencies + > +{ + public setup( + core: CoreSetup, + dependencies: SetupDependencies + ) { + dependencies.visualizations.listingViewRegistry.add({ + title: i18n.translate('eventAnnotationListing.listingViewTitle', { + defaultMessage: 'Annotation groups', + }), + id: 'annotations', + getTableList: async (props) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + + const eventAnnotationService = await pluginsStart.eventAnnotation.getService(); + + const ids = await pluginsStart.dataViews.getIds(); + const dataViews = await Promise.all(ids.map((id) => pluginsStart.dataViews.get(id))); + + const services: EventAnnotationListingPageServices = { + core: coreStart, + LensEmbeddableComponent: pluginsStart.lens.EmbeddableComponent, + savedObjectsTagging: pluginsStart.savedObjectsTagging, + eventAnnotationService, + PresentationUtilContextProvider: pluginsStart.presentationUtil.ContextProvider, + dataViews, + createDataView: pluginsStart.dataViews.create.bind(pluginsStart.dataViews), + sessionService: pluginsStart.data.search.session, + queryInputServices: { + http: coreStart.http, + docLinks: coreStart.docLinks, + notifications: coreStart.notifications, + uiSettings: coreStart.uiSettings, + dataViews: pluginsStart.dataViews, + unifiedSearch: pluginsStart.unifiedSearch, + data: pluginsStart.data, + storage: new Storage(localStorage), + }, + }; + + const { getTableList } = await import('./get_table_list'); + return getTableList(props, services); + }, + }); + } + + public start(core: CoreStart, plugins: object): void { + // nothing to do here + } +} diff --git a/src/plugins/event_annotation_listing/tsconfig.json b/src/plugins/event_annotation_listing/tsconfig.json new file mode 100644 index 0000000000000..7d9a6b8d9b3be --- /dev/null +++ b/src/plugins/event_annotation_listing/tsconfig.json @@ -0,0 +1,48 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/data-plugin", + "@kbn/i18n", + "@kbn/data-views-plugin", + "@kbn/saved-objects-management-plugin", + "@kbn/saved-objects-tagging-plugin", + "@kbn/presentation-util-plugin", + "@kbn/visualizations-plugin", + "@kbn/data-views-plugin", + "@kbn/visualization-ui-components", + "@kbn/dom-drag-drop", + "@kbn/i18n-react", + "@kbn/saved-objects-tagging-oss-plugin", + "@kbn/kibana-react-plugin", + "@kbn/core-lifecycle-browser", + "@kbn/kibana-utils-plugin", + "@kbn/unified-search-plugin", + "@kbn/content-management-table-list-view-table", + "@kbn/content-management-tabbed-table-list-view", + "@kbn/content-management-plugin", + "@kbn/event-annotation-plugin", + "@kbn/event-annotation-components", + "@kbn/event-annotation-common", + "@kbn/lens-plugin", + "@kbn/ui-theme", + "@kbn/test-jest-helpers", + "@kbn/expressions-plugin", + "@kbn/es-query", + "@kbn/core-ui-settings-browser", + "@kbn/core-notifications-browser-mocks", + "@kbn/core-notifications-browser", + "@kbn/core-saved-objects-api-browser" + ], + "exclude": [ + "target/**/*", + ] +} diff --git a/test/functional/apps/visualize/group3/_annotation_listing.ts b/test/functional/apps/visualize/group3/_annotation_listing.ts new file mode 100644 index 0000000000000..cdb0cb615be47 --- /dev/null +++ b/test/functional/apps/visualize/group3/_annotation_listing.ts @@ -0,0 +1,182 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['visualize', 'annotationEditor']); + const listingTable = getService('listingTable'); + const kibanaServer = getService('kibanaServer'); + const find = getService('find'); + const retry = getService('retry'); + const log = getService('log'); + + describe('annotation listing page', function () { + before(async function () { + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/annotation_listing_page_search' + ); + // we need to test the missing data view scenario, so delete one of them + // (can't just omit it from the archive because Kibana won't import with broken references) + log.info(`deleting one data view to replicate missing data view scenario...`); + await kibanaServer.request({ + method: 'DELETE', + path: `/api/data_views/data_view/data-view-to-delete`, + }); + + await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.visualize.selectAnnotationsTab(); + }); + + after(async function () { + log.info(`unloading annotations and data views`); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/annotation_listing_page_search' + ); + }); + + describe('search', function () { + afterEach(async function () { + await listingTable.clearSearchFilter(); + }); + + describe('by text', () => { + it('matches on the first word', async function () { + await listingTable.searchForItemWithName('search'); + await listingTable.expectItemsCount('eventAnnotation', 1); + }); + + it('matches the second word', async function () { + await listingTable.searchForItemWithName('for'); + await listingTable.expectItemsCount('eventAnnotation', 1); + }); + + it('matches the second word prefix', async function () { + await listingTable.searchForItemWithName('fo'); + await listingTable.expectItemsCount('eventAnnotation', 1); + }); + + it('does not match mid word', async function () { + await listingTable.searchForItemWithName('earc'); + // custom timeout so this test moves faster + await listingTable.expectItemsCount('eventAnnotation', 0, 1000); + }); + + it('is case insensitive', async function () { + await listingTable.searchForItemWithName('SEARCH'); + await listingTable.expectItemsCount('eventAnnotation', 1); + }); + + it('is using AND operator', async function () { + await listingTable.searchForItemWithName('search banana'); + // custom timeout so this test moves faster + await listingTable.expectItemsCount('eventAnnotation', 0, 1000); + }); + + it('matches on description', async function () { + await listingTable.searchForItemWithName('i have a description'); + await listingTable.expectItemsCount('eventAnnotation', 1); + }); + }); + + describe('by tag', () => { + it('filters by tag', async () => { + await listingTable.selectFilterTags('tag'); + await listingTable.expectItemsCount('eventAnnotation', 7); + }); + }); + }); + + describe('delete', function () { + it('deletes some groups', async function () { + await listingTable.deleteItem('to delete 1'); + await listingTable.deleteItem('to delete 2'); + await listingTable.searchForItemWithName('to delete'); + await listingTable.expectItemsCount('eventAnnotation', 0, 1000); + await listingTable.clearSearchFilter(); + }); + }); + + describe('edit', function () { + it('edits group metadata', async function () { + await listingTable.clickItemLink('eventAnnotation', 'group 3'); + await PageObjects.annotationEditor.editGroupMetadata({ + title: 'edited title', + description: 'edited description', + }); + await PageObjects.annotationEditor.saveGroup(); + + await listingTable.searchForItemWithName('edited title'); + await listingTable.expectItemsCount('eventAnnotation', 1); + + await listingTable.searchForItemWithName('edited description'); + await listingTable.expectItemsCount('eventAnnotation', 1); + }); + + describe('individual annotations', () => { + it('edits an existing annotation', async function () { + await listingTable.clickItemLink('eventAnnotation', 'edited title'); + expect(await PageObjects.annotationEditor.getAnnotationCount()).to.be(1); + + await PageObjects.annotationEditor.openAnnotation(); + await PageObjects.annotationEditor.configureAnnotation({ + query: 'my query', + lineThickness: 5, + color: '#FF0000', + }); + }); + + it('adds a new annotation', async function () { + await PageObjects.annotationEditor.addAnnotation({ + query: 'other query', + lineThickness: 3, + color: '#00FF00', + }); + + retry.try(async () => { + expect(await PageObjects.annotationEditor.getAnnotationCount()).to.be(2); + }); + }); + + it('removes an annotation', async function () { + await PageObjects.annotationEditor.removeAnnotation(); + + await retry.try(async () => { + expect(await PageObjects.annotationEditor.getAnnotationCount()).to.be(1); + }); + + await PageObjects.annotationEditor.saveGroup(); + await listingTable.clearSearchFilter(); + }); + }); + + describe.skip('data view switching', () => { + it('recovers from missing data view', async () => { + await listingTable.clickItemLink('eventAnnotation', 'missing data view'); + + await retry.try(async () => { + expect(await PageObjects.annotationEditor.showingMissingDataViewPrompt()).to.be(true); + }); + + await retry.try(async () => { + await PageObjects.annotationEditor.editGroupMetadata({ + dataView: 'logs*', + }); + expect(await PageObjects.annotationEditor.showingMissingDataViewPrompt()).to.be(false); + expect(await find.byCssSelector('canvas')).to.be.ok(); + }); + + await PageObjects.annotationEditor.saveGroup(); + }); + + it('recovers from missing field in data view', () => {}); + }); + }); + }); +} diff --git a/test/functional/apps/visualize/group3/index.ts b/test/functional/apps/visualize/group3/index.ts index 317e003d4ea01..b62d9569ac2c9 100644 --- a/test/functional/apps/visualize/group3/index.ts +++ b/test/functional/apps/visualize/group3/index.ts @@ -30,5 +30,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_visualize_listing')); loadTestFile(require.resolve('./_add_to_dashboard.ts')); loadTestFile(require.resolve('./_pie_chart')); + loadTestFile(require.resolve('./_annotation_listing')); }); } diff --git a/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json b/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json new file mode 100644 index 0000000000000..34f4fb8ed1b48 --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/annotation_listing_page_search.json @@ -0,0 +1,832 @@ +{ + "attributes": { + "fieldAttrs": "{}", + "fieldFormatMap": "{}", + "fields": "[]", + "name": "logs*", + "runtimeFieldMap": "{}", + "sourceFilters": "[]", + "timeFieldName": "@timestamp", + "title": "logs*", + "typeMeta": "{}" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-07T17:23:20.906Z", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "managed": false, + "references": [], + "type": "index-pattern", + "typeMigrationVersion": "8.0.0", + "updated_at": "2023-09-11T15:50:59.227Z", + "version": "WzIyNywxXQ==" +} + +{ + "attributes": { + "fieldAttrs": "{}", + "fieldFormatMap": "{\"hour_of_day\":{}}", + "fields": "[]", + "name": "To Delete!", + "runtimeFieldMap": "{\"hour_of_day\":{\"type\":\"long\",\"script\":{\"source\":\"emit(doc['timestamp'].value.getHour());\"}}}", + "sourceFilters": "[]", + "timeFieldName": "timestamp", + "title": "kibana_sample_data_logs", + "typeMeta": "{}" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-07T17:23:20.906Z", + "id": "data-view-to-delete", + "managed": false, + "references": [], + "type": "index-pattern", + "typeMigrationVersion": "8.0.0", + "updated_at": "2023-09-11T15:50:59.227Z", + "version": "WzIyNywxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 19" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T14:00:20.704Z", + "id": "b6071e00-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T14:00:20.704Z", + "version": "WzMxNywxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "i have a description", + "ignoreGlobalFilters": true, + "title": "group 21" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T14:00:12.994Z", + "id": "b16eaa20-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T14:00:12.994Z", + "version": "WzMxNiwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "search for me" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T14:01:40.768Z", + "id": "e5bfc2f0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T14:02:00.597Z", + "version": "WzMyMCwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "to delete 1" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:53.977Z", + "id": "a618e690-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T14:00:06.429Z", + "version": "WzMxNSwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "to delete 2" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:47.961Z", + "id": "a282ee90-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:47.961Z", + "version": "WzMxMiwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 16" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:42.118Z", + "id": "9f075c60-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:42.118Z", + "version": "WzMxMSwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "missing data view" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:36.447Z", + "id": "9ba608f0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "data-view-to-delete", + "name": "event-annotation-group_dataView-ref-data-view-to-delete", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:36.447Z", + "version": "WzMxMCwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 14" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:30.997Z", + "id": "98666e50-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:30.997Z", + "version": "WzMwOSwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 13" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:25.669Z", + "id": "95397150-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:25.669Z", + "version": "WzMwOCwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 12" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:20.653Z", + "id": "923c0fd0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:20.653Z", + "version": "WzMwNywxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 11" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:14.944Z", + "id": "8ed4f000-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:14.944Z", + "version": "WzMwNiwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 10" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:09.600Z", + "id": "8ba58200-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:09.600Z", + "version": "WzMwNSwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 9" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:59:02.338Z", + "id": "87514310-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:59:02.338Z", + "version": "WzMwNCwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 8" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:55.464Z", + "id": "83388680-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:55.464Z", + "version": "WzMwMywxXQ==" +} + +{ + "attributes": { + "color": "#dac7c4", + "description": "a tag to filter by", + "name": "tag" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-08T18:40:42.018Z", + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "managed": false, + "references": [], + "type": "tag", + "typeMigrationVersion": "8.0.0", + "updated_at": "2023-09-08T18:40:42.018Z", + "version": "WzI3NDUsMV0=" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 7" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:46.671Z", + "id": "7dfad1f0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:46.671Z", + "version": "WzMwMiwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 6" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:37.886Z", + "id": "78be55e0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:37.886Z", + "version": "WzMwMSwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 5" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:32.312Z", + "id": "756bcf80-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:32.312Z", + "version": "WzMwMCwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 4" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:26.947Z", + "id": "72392d30-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:26.947Z", + "version": "WzI5OSwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 3" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:21.136Z", + "id": "6ec27d00-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:21.136Z", + "version": "WzI5OCwxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 2" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:15.691Z", + "id": "6b83a5b0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:15.691Z", + "version": "WzI5NywxXQ==" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "3d28ce7e-fc5e-409b-aea3-4d9e15010843", + "key": { + "timestamp": "2023-09-13T16:30:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "group 1" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-09-12T13:58:07.740Z", + "id": "66c66bc0-5174-11ee-a5c4-7dce2e3293a7", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + }, + { + "id": "36a8f020-4e77-11ee-bb97-116581699678", + "name": "36a8f020-4e77-11ee-bb97-116581699678", + "type": "tag" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-09-12T13:58:07.740Z", + "version": "WzI5NiwxXQ==" +} \ No newline at end of file diff --git a/test/functional/page_objects/annotation_library_editor_page.ts b/test/functional/page_objects/annotation_library_editor_page.ts new file mode 100644 index 0000000000000..f5c66b9f6c1e9 --- /dev/null +++ b/test/functional/page_objects/annotation_library_editor_page.ts @@ -0,0 +1,84 @@ +/* + * 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 { FtrService } from '../ftr_provider_context'; + +export class AnnotationEditorPageObject extends FtrService { + private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly retry = this.ctx.getService('retry'); + + /** + * Fills out group metadata + */ + public async editGroupMetadata(metadata: { + title?: string; + description?: string; + dataView?: string; + }) { + if (metadata.title) { + await this.testSubjects.setValue('annotationGroupTitle', metadata.title); + } + + if (metadata.description) { + await this.testSubjects.setValue('annotationGroupDescription', metadata.description); + } + + if (metadata.dataView) { + await this.testSubjects.setValue('annotationDataViewSelection', metadata.dataView); + } + } + + public async saveGroup() { + await this.testSubjects.click('saveAnnotationGroup'); + } + + public async getAnnotationCount() { + const triggers = await this.testSubjects.findAll('lns-dimensionTrigger'); + return triggers.length; + } + + public async openAnnotation() { + await this.testSubjects.click('lns-dimensionTrigger'); + } + + public async configureAnnotation(config: { + query: string; + lineThickness: number; + color: string; + }) { + await this.testSubjects.click('lnsXY_annotation_query'); + + const queryInput = await this.testSubjects.find('annotation-query-based-query-input'); + await queryInput.type(config.query); + + await this.testSubjects.setValue('lnsXYThickness', '' + config.lineThickness); + + await this.testSubjects.setValue( + 'euiColorPickerAnchor indexPattern-dimension-colorPicker', + config.color + ); + + await this.retry.waitFor('annotation editor UI to close', async () => { + await this.testSubjects.click('backToGroupSettings'); + return !(await this.testSubjects.exists('backToGroupSettings')); + }); + } + + public async addAnnotation(config: { query: string; lineThickness: number; color: string }) { + await this.testSubjects.click('addAnnotation'); + await this.configureAnnotation(config); + } + + public async removeAnnotation() { + await this.testSubjects.click('indexPattern-dimension-remove'); + } + + public async showingMissingDataViewPrompt() { + return await this.testSubjects.exists('missingDataViewPrompt'); + } +} diff --git a/test/functional/page_objects/index.ts b/test/functional/page_objects/index.ts index 63cd23e6e718f..9a2312e0fedee 100644 --- a/test/functional/page_objects/index.ts +++ b/test/functional/page_objects/index.ts @@ -34,8 +34,10 @@ import { DashboardPageControls } from './dashboard_page_controls'; import { UnifiedSearchPageObject } from './unified_search_page'; import { UnifiedFieldListPageObject } from './unified_field_list'; import { FilesManagementPageObject } from './files_management'; +import { AnnotationEditorPageObject } from './annotation_library_editor_page'; export const pageObjects = { + annotationEditor: AnnotationEditorPageObject, common: CommonPageObject, console: ConsolePageObject, context: ContextPageObject, diff --git a/test/functional/services/listing_table.ts b/test/functional/services/listing_table.ts index 71d71a157ceb9..486d49a3f7c75 100644 --- a/test/functional/services/listing_table.ts +++ b/test/functional/services/listing_table.ts @@ -10,7 +10,12 @@ import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; type AppName = keyof typeof PREFIX_MAP; -const PREFIX_MAP = { visualize: 'vis', dashboard: 'dashboard', map: 'map' }; +const PREFIX_MAP = { + visualize: 'vis', + dashboard: 'dashboard', + map: 'map', + eventAnnotation: 'eventAnnotation', +}; export class ListingTableService extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); @@ -186,10 +191,11 @@ export class ListingTableService extends FtrService { /** * Returns items count on landing page */ - public async expectItemsCount(appName: AppName, count: number) { + public async expectItemsCount(appName: AppName, count: number, findTimeout?: number) { await this.retry.try(async () => { const elements = await this.find.allByCssSelector( - `[data-test-subj^="${PREFIX_MAP[appName]}ListingTitleLink"]` + `[data-test-subj^="${PREFIX_MAP[appName]}ListingTitleLink"]`, + findTimeout ?? 10000 ); expect(elements.length).to.equal(count); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index 04071f12af018..31381c8271962 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -722,6 +722,8 @@ "@kbn/event-annotation-common/*": ["packages/kbn-event-annotation-common/*"], "@kbn/event-annotation-components": ["packages/kbn-event-annotation-components"], "@kbn/event-annotation-components/*": ["packages/kbn-event-annotation-components/*"], + "@kbn/event-annotation-listing-plugin": ["src/plugins/event_annotation_listing"], + "@kbn/event-annotation-listing-plugin/*": ["src/plugins/event_annotation_listing/*"], "@kbn/event-annotation-plugin": ["src/plugins/event_annotation"], "@kbn/event-annotation-plugin/*": ["src/plugins/event_annotation/*"], "@kbn/event-log-fixture-plugin": ["x-pack/test/plugin_api_integration/plugins/event_log"], diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 67764b46f6609..b4bdb5783591b 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -461,7 +461,6 @@ export class Embeddable this.expressionRenderer = deps.expressionRenderer; this.initializeSavedVis(initialInput) .then(() => { - this.loadUserMessages(); this.reload(); }) .catch((e) => this.onFatalError(e)); @@ -595,17 +594,6 @@ export class Embeddable private fullAttributes: LensSavedObjectAttributes | undefined; public getUserMessages: UserMessagesGetter = (locationId, filters) => { - return filterAndSortUserMessages( - [...this._userMessages, ...Object.values(this.additionalUserMessages)], - locationId, - filters ?? {} - ); - }; - - private _userMessages: UserMessage[] = []; - - // loads all available user messages - private loadUserMessages() { const userMessages: UserMessage[] = []; userMessages.push( @@ -672,8 +660,12 @@ export class Embeddable }) ?? []) ); - this._userMessages = userMessages; - } + return filterAndSortUserMessages( + [...userMessages, ...Object.values(this.additionalUserMessages)], + locationId, + filters ?? {} + ); + }; private additionalUserMessages: Record = {}; @@ -908,12 +900,7 @@ export class Embeddable this.activeData = newActiveData; - // Refresh messanges if info type is found as with active data - // these messages can be enriched - if (this._userMessages.some(({ severity }) => severity === 'info')) { - this.loadUserMessages(); - this.renderUserMessages(); - } + this.renderUserMessages(); }; private onRender: ExpressionWrapperProps['onRender$'] = () => { @@ -992,6 +979,7 @@ export class Embeddable return; } super.render(domNode as HTMLElement); + if (this.input.onLoad) { this.input.onLoad(true); } @@ -1291,7 +1279,6 @@ export class Embeddable this.expression = ast; - this.loadUserMessages(); this.reload(); } }; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index 0377765ef04a4..d93e3729a6541 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -86,6 +86,8 @@ export type EmbeddableComponentProps = (TypedLensByValueInput | LensByReferenceI showInspector?: boolean; }; +export type EmbeddableComponent = React.ComponentType; + interface PluginsStartDependencies { uiActions: UiActionsStart; embeddable: EmbeddableStart; diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 65d5ca12df094..ebf64a1915a43 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -9,6 +9,7 @@ import { LensPlugin } from './plugin'; export type { EmbeddableComponentProps, + EmbeddableComponent, TypedLensByValueInput, } from './embeddable/embeddable_component'; export type { diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 957422da5d6a4..63eab92016d50 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -113,10 +113,7 @@ import { visualizeDashboardVisualizePanelction } from './trigger_actions/dashboa import type { LensEmbeddableInput } from './embeddable'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; -import { - EmbeddableComponentProps, - getEmbeddableComponent, -} from './embeddable/embeddable_component'; +import { EmbeddableComponent, getEmbeddableComponent } from './embeddable/embeddable_component'; import { getSaveModalComponent } from './app_plugin/shared/saved_modal_lazy'; import type { SaveModalContainerProps } from './app_plugin/save_modal_container'; @@ -209,7 +206,7 @@ export interface LensPublicStart { * * @experimental */ - EmbeddableComponent: React.ComponentType; + EmbeddableComponent: EmbeddableComponent; /** * React component which can be used to embed a Lens Visualization Save Modal Component. * See `x-pack/examples/embedded_lens_example` for exemplary usage. diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index c1777305c2be3..ba2f315ae12fd 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -7030,7 +7030,6 @@ "visualizationUiComponents.dimensionButtonIcon.colorIndicatorLabel": "Couleur de cette dimension : {hex}", "visualizationUiComponents.queryInput.queryPlaceholderKql": "{example}", "visualizationUiComponents.queryInput.queryPlaceholderLucene": "{example}", - "visualizationUiComponents.colorPicker.seriesColor.auto": "Auto", "visualizationUiComponents.colorPicker.seriesColor.label": "Couleur de la série", "visualizationUiComponents.colorPicker.tooltip.auto": "Lens choisit automatiquement des couleurs à votre place sauf si vous spécifiez une couleur personnalisée.", "visualizationUiComponents.colorPicker.tooltip.custom": "Effacez la couleur personnalisée pour revenir au mode \"Auto\".", @@ -39430,7 +39429,6 @@ "eventAnnotation.group.args.annotationConfigs.ignoreGlobalFilters.help": "Basculer pour ignorer les filtres globaux pour l'annotation", "eventAnnotation.group.args.annotationGroups": "Groupe d'annotations", "eventAnnotation.group.description": "Groupe d'annotations d'événement", - "eventAnnotation.listingViewTitle": "Groupes d'annotations", "eventAnnotation.manualAnnotation.args.color": "Couleur de la ligne", "eventAnnotation.manualAnnotation.args.icon": "Icône facultative utilisée pour les lignes d'annotation", "eventAnnotation.manualAnnotation.args.id": "ID de l'annotation", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b83d747469b97..a75617f2b449f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7046,7 +7046,6 @@ "visualizationUiComponents.dimensionButtonIcon.colorIndicatorLabel": "このディメンションの色:{hex}", "visualizationUiComponents.queryInput.queryPlaceholderKql": "{example}", "visualizationUiComponents.queryInput.queryPlaceholderLucene": "{example}", - "visualizationUiComponents.colorPicker.seriesColor.auto": "自動", "visualizationUiComponents.colorPicker.seriesColor.label": "系列色", "visualizationUiComponents.colorPicker.tooltip.auto": "カスタム色を指定しない場合、Lensは自動的に色を選択します。", "visualizationUiComponents.colorPicker.tooltip.custom": "[自動]モードに戻すには、カスタム色をオフにしてください。", @@ -39421,7 +39420,6 @@ "eventAnnotation.group.args.annotationConfigs.ignoreGlobalFilters.help": "注釈のグローバルフィルターを無視するスイッチ", "eventAnnotation.group.args.annotationGroups": "注釈グループ", "eventAnnotation.group.description": "イベント注釈グループ", - "eventAnnotation.listingViewTitle": "注釈グループ", "eventAnnotation.manualAnnotation.args.color": "行の色", "eventAnnotation.manualAnnotation.args.icon": "注釈行で使用される任意のアイコン", "eventAnnotation.manualAnnotation.args.id": "注釈のID", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 303f08601b52e..93ac340c503f1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7045,7 +7045,6 @@ "visualizationUiComponents.dimensionButtonIcon.colorIndicatorLabel": "此维度的颜色:{hex}", "visualizationUiComponents.queryInput.queryPlaceholderKql": "{example}", "visualizationUiComponents.queryInput.queryPlaceholderLucene": "{example}", - "visualizationUiComponents.colorPicker.seriesColor.auto": "自动", "visualizationUiComponents.colorPicker.seriesColor.label": "系列颜色", "visualizationUiComponents.colorPicker.tooltip.auto": "Lens 自动为您选取颜色,除非您指定定制颜色。", "visualizationUiComponents.colorPicker.tooltip.custom": "清除定制颜色以返回到“自动”模式。", @@ -39415,7 +39414,6 @@ "eventAnnotation.group.args.annotationConfigs.ignoreGlobalFilters.help": "进行切换以忽略标注的全局筛选", "eventAnnotation.group.args.annotationGroups": "标注组", "eventAnnotation.group.description": "事件标注组", - "eventAnnotation.listingViewTitle": "标注组", "eventAnnotation.manualAnnotation.args.color": "线条的颜色", "eventAnnotation.manualAnnotation.args.icon": "用于标注线条的可选图标", "eventAnnotation.manualAnnotation.args.id": "标注的 ID", diff --git a/yarn.lock b/yarn.lock index 44a363ed46c11..748bbecdcff2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4393,6 +4393,10 @@ version "0.0.0" uid "" +"@kbn/event-annotation-listing-plugin@link:src/plugins/event_annotation_listing": + version "0.0.0" + uid "" + "@kbn/event-annotation-plugin@link:src/plugins/event_annotation": version "0.0.0" uid ""