diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 5cf6efe324ac3f..38fed4aca19dcb 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -25,7 +25,7 @@ pipeline { durabilityHint('PERFORMANCE_OPTIMIZED') } triggers { - issueCommentTrigger('(?i).*jenkins\\W+run\\W+(?:the\\W+)?e2e(?:\\W+please)?.*') + issueCommentTrigger('(?i)(retest|.*jenkins\\W+run\\W+(?:the\\W+)?e2e?.*)') } parameters { booleanParam(name: 'FORCE', defaultValue: false, description: 'Whether to force the run.') @@ -60,8 +60,14 @@ pipeline { } } steps { + notifyStatus('Starting services', 'PENDING') dir("${APM_ITS}"){ - sh './scripts/compose.py start master --no-kibana --no-xpack-secure' + sh './scripts/compose.py start master --no-kibana' + } + } + post { + unsuccessful { + notifyStatus('Environmental issue', 'FAILURE') } } } @@ -77,10 +83,16 @@ pipeline { JENKINS_NODE_COOKIE = 'dontKillMe' } steps { + notifyStatus('Preparing kibana', 'PENDING') dir("${BASE_DIR}"){ sh script: "${CYPRESS_DIR}/ci/prepare-kibana.sh" } } + post { + unsuccessful { + notifyStatus('Kibana warm up failed', 'FAILURE') + } + } } stage('Smoke Tests'){ options { skipDefaultCheckout() } @@ -91,6 +103,7 @@ pipeline { } } steps{ + notifyStatus('Running smoke tests', 'PENDING') dir("${BASE_DIR}"){ sh ''' jobs -l @@ -112,6 +125,12 @@ pipeline { archiveArtifacts(allowEmptyArchive: false, artifacts: 'apm-its.log') } } + unsuccessful { + notifyStatus('Test failures', 'FAILURE') + } + success { + notifyStatus('Tests passed', 'SUCCESS') + } } } } @@ -123,3 +142,7 @@ pipeline { } } } + +def notifyStatus(String description, String status) { + withGithubNotify.notify('end2end-for-apm-ui', description, status, getBlueoceanDisplayURL()) +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bf1e341c796fa0..56db8d3793f57c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -72,7 +72,8 @@ # Observability UIs /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui /x-pack/plugins/infra/ @elastic/logs-metrics-ui -/x-pack/legacy/plugins/integrations_manager/ @elastic/epm +/x-pack/plugins/ingest_manager/ @elastic/ingest +/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest /x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest # Machine Learning @@ -149,6 +150,7 @@ # Kibana Alerting Services /x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services /x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services +/x-pack/plugins/alerting/ @elastic/kibana-alerting-services /x-pack/plugins/actions/ @elastic/kibana-alerting-services /x-pack/plugins/event_log/ @elastic/kibana-alerting-services /x-pack/plugins/task_manager/ @elastic/kibana-alerting-services @@ -156,6 +158,7 @@ /x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services /x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services +/x-pack/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services @@ -170,8 +173,14 @@ /x-pack/legacy/plugins/index_lifecycle_management/ @elastic/es-ui /x-pack/legacy/plugins/index_management/ @elastic/es-ui /x-pack/legacy/plugins/license_management/ @elastic/es-ui -/x-pack/legacy/plugins/remote_clusters/ @elastic/es-ui +/x-pack/plugins/remote_clusters/ @elastic/es-ui /x-pack/legacy/plugins/rollup/ @elastic/es-ui /x-pack/plugins/searchprofiler/ @elastic/es-ui /x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui /x-pack/plugins/watcher/ @elastic/es-ui + +# Endpoint +/x-pack/plugins/endpoint/ @elastic/endpoint-app-team +/x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team +/x-pack/test/functional/apps/endpoint/ @elastic/endpoint-app-team +/x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9cba1ee909f6da..bd7868adb511ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,11 +55,9 @@ Granted that you share your thoughts, we might even be able to come up with crea First of all, **sorry about that!** We want you to have a great time with Kibana. -Hosting meaningful discussions on GitHub can be challenging. For that reason, we'll sometimes ask that you join us on IRC _([#kibana](https://kiwiirc.com/client/irc.freenode.net/?#kibana) on freenode)_ to chat about your issues. You may also experience **faster response times** when engaging us via IRC. - There's hundreds of open issues and prioritizing what to work on is an important aspect of our daily jobs. We prioritize issues according to impact and difficulty, so some issues can be neglected while we work on more pressing issues. -Feel free to bump your issues if you think they've been neglected for a prolonged period, or just jump on IRC and let us have it! +Feel free to bump your issues if you think they've been neglected for a prolonged period. ### "I want to help!" @@ -471,11 +469,11 @@ The following table outlines possible test file locations and how to invoke them | Test runner | Test location | Runner command (working directory is kibana root) | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| Jest | `src/**/*.test.js`
`src/**/*.test.ts` | `node scripts/jest -t regexp [test path]` | -| Jest (integration) | `**/integration_tests/**/*.test.js` | `node scripts/jest_integration -t regexp [test path]` | +| Jest | `src/**/*.test.js`
`src/**/*.test.ts` | `yarn test:jest -t regexp [test path]` | +| Jest (integration) | `**/integration_tests/**/*.test.js` | `yarn test:jest_integration -t regexp [test path]` | | Mocha | `src/**/__tests__/**/*.js`
`!src/**/public/__tests__/*.js`
`packages/kbn-datemath/test/**/*.js`
`packages/kbn-dev-utils/src/**/__tests__/**/*.js`
`tasks/**/__tests__/**/*.js` | `node scripts/mocha --grep=regexp [test path]` | -| Functional | `test/*integration/**/config.js`
`test/*functional/**/config.js`
`test/accessibility/config.js` | `node scripts/functional_tests_server --config test/[directory]/config.js`
`node scripts/functional_test_runner --config test/[directory]/config.js --grep=regexp` | -| Karma | `src/**/public/__tests__/*.js` | `npm run test:dev` | +| Functional | `test/*integration/**/config.js`
`test/*functional/**/config.js`
`test/accessibility/config.js` | `yarn test:ftr:server --config test/[directory]/config.js`
`yarn test:ftr:runner --config test/[directory]/config.js --grep=regexp` | +| Karma | `src/**/public/__tests__/*.js` | `yarn test:karma:debug` | For X-Pack tests located in `x-pack/` see [X-Pack Testing](x-pack/README.md#testing) @@ -486,56 +484,38 @@ Test runner arguments: Examples: - Run the entire elasticsearch_service test suite: ``` - node scripts/jest src/core/server/elasticsearch/elasticsearch_service.test.ts + yarn test:jest src/core/server/elasticsearch/elasticsearch_service.test.ts ``` - Run the jest test case whose description matches `stops both admin and data clients`: ``` - node scripts/jest -t 'stops both admin and data clients' src/core/server/elasticsearch/elasticsearch_service.test.ts + yarn test:jest -t 'stops both admin and data clients' src/core/server/elasticsearch/elasticsearch_service.test.ts ``` - Run the api integration test case whose description matches the given string: ``` - node scripts/functional_tests_server --config test/api_integration/config.js - node scripts/functional_test_runner --config test/api_integration/config.js --grep='should return 404 if id does not match any sample data sets' + yarn test:ftr:server --config test/api_integration/config.js + yarn test:ftr:runner --config test/api_integration/config.js --grep='should return 404 if id does not match any sample data sets' ``` ### Debugging Unit Tests The standard `yarn test` task runs several sub tasks and can take several minutes to complete, making debugging failures pretty painful. In order to ease the pain specialized tasks provide alternate methods for running the tests. -To execute both server and browser tests, but skip linting, use `yarn test:quick`. - -```bash -yarn test:quick -``` - -Use `yarn test:mocha` when you want to run the mocha tests. - -```bash -yarn test:mocha -``` - -When you'd like to execute individual server-side test files, you can use the command below. Note that this command takes care of configuring Mocha with Babel compilation for you, and you'll be better off avoiding a globally installed `mocha` package. This command is great for development and for quickly identifying bugs. - -```bash -node scripts/mocha -``` - You could also add the `--debug` option so that `node` is run using the `--debug-brk` flag. You'll need to connect a remote debugger such as [`node-inspector`](https://github.com/node-inspector/node-inspector) to proceed in this mode. ```bash node scripts/mocha --debug ``` -With `yarn test:browser`, you can run only the browser tests. Coverage reports are available for browser tests by running `yarn test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion. +With `yarn test:karma`, you can run only the browser tests. Coverage reports are available for browser tests by running `yarn test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion. ```bash -yarn test:browser +yarn test:karma ``` -Using `yarn test:dev` initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests. +Using `yarn test:karma:debug` initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests. ```bash -yarn test:dev +yarn test:karma:debug ``` In the screenshot below, you'll notice the URL is `localhost:9876/debug.html`. You can append a `grep` query parameter to this URL and set it to a string value which will be used to exclude tests which don't match. For example, if you changed the URL to `localhost:9876/debug.html?query=my test` and then refreshed the browser, you'd only see tests run which contain "my test" in the test description. @@ -551,7 +531,7 @@ To run the tests for just your particular plugin run the following command from ```bash yarn test:mocha -yarn test:browser --dev # remove the --dev flag to run them once and close +yarn test:karma:debug # remove the debug flag to run them once and close ``` ### Automated Accessibility Testing diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 0c6ea2bcf59334..14817719ec7ee6 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -16,7 +16,7 @@ experimental[] Import dashboards and corresponding saved objects. `force`:: (Optional, boolean) Overwrite any existing objects on ID conflict. - + `exclude`:: (Optional, array) Saved object types that you want to exclude from the import. @@ -28,14 +28,14 @@ Use the complete response body from the < {kib} > Advanced Settings*. . Scroll or search for the setting you want to modify. . Enter a new value for the setting. +. Click *Save changes*. [float] @@ -34,7 +35,7 @@ removes it from {kib} permanently. [float] [[kibana-general-settings]] -=== General settings +==== General [horizontal] `csv:quoteValues`:: Set this property to `true` to quote exported values. @@ -109,7 +110,7 @@ cluster alert notifications from Monitoring. [float] [[kibana-accessibility-settings]] -=== Accessibility settings +==== Accessibility [horizontal] `accessibility:disableAnimations`:: Turns off all unnecessary animations in the @@ -117,14 +118,14 @@ cluster alert notifications from Monitoring. [float] [[kibana-dashboard-settings]] -=== Dashboard settings +==== Dashboard [horizontal] `xpackDashboardMode:roles`:: The roles that belong to <>. [float] [[kibana-discover-settings]] -=== Discover settings +==== Discover [horizontal] `context:defaultSize`:: The number of surrounding entries to display in the context view. The default value is 5. @@ -150,7 +151,7 @@ working on big documents. [float] [[kibana-notification-settings]] -=== Notifications settings +==== Notifications [horizontal] `notifications:banner`:: A custom banner intended for temporary notices to all users. @@ -169,7 +170,7 @@ displays. The default value is 10000. Set this field to `Infinity` to disable wa [float] [[kibana-reporting-settings]] -=== Reporting settings +==== Reporting [horizontal] `xpackReporting:customPdfLogo`:: A custom image to use in the footer of the PDF. @@ -177,7 +178,7 @@ displays. The default value is 10000. Set this field to `Infinity` to disable wa [float] [[kibana-rollups-settings]] -=== Rollup settings +==== Rollup [horizontal] `rollups:enableIndexPatterns`:: Enables the creation of index patterns that @@ -187,7 +188,7 @@ Refresh the page to apply the changes. [float] [[kibana-search-settings]] -=== Search settings +==== Search [horizontal] `courier:batchSearches`:: **Deprecated in 7.6. Starting in 8.0, this setting will be optimized internally.** @@ -215,21 +216,21 @@ might increase the search time. This setting is off by default. Users must opt-i [float] [[kibana-siem-settings]] -=== SIEM settings +==== SIEM [horizontal] `siem:defaultAnomalyScore`:: The threshold above which Machine Learning job anomalies are displayed in the SIEM app. `siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events. -`siem:enableNewsFeed`:: Enables the security news feed on the SIEM *Overview* +`siem:enableNewsFeed`:: Enables the security news feed on the SIEM *Overview* page. -`siem:newsFeedUrl`:: The URL from which the security news feed content is +`siem:newsFeedUrl`:: The URL from which the security news feed content is retrieved. `siem:refreshIntervalDefaults`:: The default refresh interval for the SIEM time filter, in milliseconds. `siem:timeDefaults`:: The default period of time in the SIEM time filter. [float] [[kibana-timelion-settings]] -=== Timelion settings +==== Timelion [horizontal] `timelion:default_columns`:: The default number of columns to use on a Timelion sheet. @@ -252,7 +253,7 @@ this is the number of buckets to try to represent. [float] [[kibana-visualization-settings]] -=== Visualization settings +==== Visualization [horizontal] `visualization:colorMapping`:: Maps values to specified colors in visualizations. @@ -273,7 +274,7 @@ If disabled, only visualizations that are considered production-ready are availa [float] [[kibana-telemetry-settings]] -=== Usage data settings +==== Usage data Helps improve the Elastic Stack by providing usage statistics for basic features. This data will not be shared outside of Elastic. diff --git a/examples/demo_search/public/demo_search_strategy.ts b/examples/demo_search/public/demo_search_strategy.ts index d2854151e14c85..cb2480c8a5f194 100644 --- a/examples/demo_search/public/demo_search_strategy.ts +++ b/examples/demo_search/public/demo_search_strategy.ts @@ -18,11 +18,7 @@ */ import { Observable } from 'rxjs'; -import { - ISearchContext, - SYNC_SEARCH_STRATEGY, - ISearchGeneric, -} from '../../../src/plugins/data/public'; +import { ISearchContext, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public'; import { TSearchStrategyProvider, ISearchStrategy } from '../../../src/plugins/data/public'; import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; @@ -54,15 +50,15 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common'; * @param search - a search function to access other strategies that have already been registered. */ export const demoClientSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext, - search: ISearchGeneric + context: ISearchContext ): ISearchStrategy => { + const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); + const { search } = syncStrategyProvider(context); return { - search: (request, options) => - search( - { ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, - options, - SYNC_SEARCH_STRATEGY - ) as Observable, + search: (request, options) => { + return search({ ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, options) as Observable< + IDemoResponse + >; + }, }; }; diff --git a/examples/demo_search/public/index.ts b/examples/demo_search/public/index.ts index 7790c2950ac22c..0a97ac6b72ea7f 100644 --- a/examples/demo_search/public/index.ts +++ b/examples/demo_search/public/index.ts @@ -17,12 +17,10 @@ * under the License. */ -import { PluginInitializer, PluginInitializerContext } from 'kibana/public'; +import { PluginInitializer } from 'kibana/public'; import { DemoDataPlugin } from './plugin'; export { DEMO_SEARCH_STRATEGY } from '../common'; -export const plugin: PluginInitializer = ( - initializerContext: PluginInitializerContext -) => new DemoDataPlugin(initializerContext); +export const plugin: PluginInitializer = () => new DemoDataPlugin(); diff --git a/examples/demo_search/public/plugin.ts b/examples/demo_search/public/plugin.ts index 81ba585b991902..62c912716e627c 100644 --- a/examples/demo_search/public/plugin.ts +++ b/examples/demo_search/public/plugin.ts @@ -18,7 +18,7 @@ */ import { DataPublicPluginSetup } from '../../../src/plugins/data/public'; -import { Plugin, CoreSetup, PluginInitializerContext } from '../../../src/core/public'; +import { Plugin, CoreSetup } from '../../../src/core/public'; import { DEMO_SEARCH_STRATEGY } from '../common'; import { demoClientSearchStrategyProvider } from './demo_search_strategy'; import { IDemoRequest, IDemoResponse } from '../common'; @@ -47,10 +47,8 @@ declare module '../../../src/plugins/data/public' { } export class DemoDataPlugin implements Plugin { - constructor(private initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup, deps: DemoDataSearchSetupDependencies) { deps.data.search.registerSearchStrategyProvider( - this.initializerContext.opaqueId, DEMO_SEARCH_STRATEGY, demoClientSearchStrategyProvider ); diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts index 37ac63e380f966..a54201b157a6ca 100644 --- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts +++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts @@ -23,9 +23,13 @@ import { MultiTaskTodoEmbeddable, MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoInput, + MultiTaskTodoOutput, } from './multi_task_todo_embeddable'; -export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory { +export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory< + MultiTaskTodoInput, + MultiTaskTodoOutput +> { public readonly type = MULTI_TASK_TODO_EMBEDDABLE; public isEditable() { @@ -36,6 +40,15 @@ export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory { return new MultiTaskTodoEmbeddable(initialInput, parent); } + /** + * Check out todo_embeddable_factory for a better example that asks for data from + * the user. This just returns default data. That's okay too though, if you want to + * start with default data and expose an "edit" action to modify it. + */ + public async getExplicitInput() { + return { title: 'default title', tasks: ['Im default data'] }; + } + public getDisplayName() { return i18n.translate('embeddableExamples.multiTaskTodo.displayName', { defaultMessage: 'Multi-task todo item', diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index e75165bfbef181..b7a4f5c078d546 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -17,11 +17,20 @@ * under the License. */ -import { IEmbeddableSetup, IEmbeddableStart } from '../../../src/plugins/embeddable/public'; +import { + IEmbeddableSetup, + IEmbeddableStart, + EmbeddableFactory, +} from '../../../src/plugins/embeddable/public'; import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public'; import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world'; -import { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo'; -import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo'; +import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo'; +import { + MULTI_TASK_TODO_EMBEDDABLE, + MultiTaskTodoEmbeddableFactory, + MultiTaskTodoOutput, + MultiTaskTodoInput, +} from './multi_task_todo'; import { SEARCHABLE_LIST_CONTAINER, SearchableListContainerFactory, @@ -45,12 +54,9 @@ export class EmbeddableExamplesPlugin new HelloWorldEmbeddableFactory() ); - deps.embeddable.registerEmbeddableFactory(TODO_EMBEDDABLE, new TodoEmbeddableFactory()); - - deps.embeddable.registerEmbeddableFactory( - MULTI_TASK_TODO_EMBEDDABLE, - new MultiTaskTodoEmbeddableFactory() - ); + deps.embeddable.registerEmbeddableFactory< + EmbeddableFactory + >(MULTI_TASK_TODO_EMBEDDABLE, new MultiTaskTodoEmbeddableFactory()); } public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) { @@ -66,6 +72,11 @@ export class EmbeddableExamplesPlugin LIST_CONTAINER, new ListContainerFactory(deps.embeddable.getEmbeddableFactory) ); + + deps.embeddable.registerEmbeddableFactory>( + TODO_EMBEDDABLE, + new TodoEmbeddableFactory(core.overlays.openModal) + ); } public stop() {} diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts b/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts deleted file mode 100644 index 386b3f296d998f..00000000000000 --- a/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@kbn/i18n'; -import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public'; -import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput } from './todo_embeddable'; - -export class TodoEmbeddableFactory extends EmbeddableFactory { - public readonly type = TODO_EMBEDDABLE; - - public isEditable() { - return true; - } - - public async create(initialInput: TodoInput, parent?: IContainer) { - return new TodoEmbeddable(initialInput, parent); - } - - public getDisplayName() { - return i18n.translate('embeddableExamples.todo.displayName', { - defaultMessage: 'Todo item', - }); - } -} diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx new file mode 100644 index 00000000000000..dd2168bb39eeeb --- /dev/null +++ b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useState } from 'react'; +import { EuiModalBody } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { OverlayStart } from 'kibana/public'; +import { EuiFieldText } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; +import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; +import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public'; +import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput, TodoOutput } from './todo_embeddable'; + +function TaskInput({ onSave }: { onSave: (task: string) => void }) { + const [task, setTask] = useState(''); + return ( + + setTask(e.target.value)} + /> + onSave(task)}> + Save + + + ); +} + +export class TodoEmbeddableFactory extends EmbeddableFactory< + TodoInput, + TodoOutput, + TodoEmbeddable +> { + public readonly type = TODO_EMBEDDABLE; + + constructor(private openModal: OverlayStart['openModal']) { + super(); + } + + public isEditable() { + return true; + } + + public async create(initialInput: TodoInput, parent?: IContainer) { + return new TodoEmbeddable(initialInput, parent); + } + + /** + * This function is used when dynamically creating a new embeddable to add to a + * container. Some input may be inherited from the container, but not all. This can be + * used to collect specific embeddable input that the container will not provide, like + * in this case, the task string. + */ + public async getExplicitInput() { + return new Promise<{ task: string }>(resolve => { + const onSave = (task: string) => resolve({ task }); + const overlay = this.openModal( + toMountPoint( + { + onSave(task); + overlay.close(); + }} + /> + ) + ); + }); + } + + public getDisplayName() { + return i18n.translate('embeddableExamples.todo.displayName', { + defaultMessage: 'Todo item', + }); + } +} diff --git a/examples/embeddable_explorer/kibana.json b/examples/embeddable_explorer/kibana.json index 4ca63e1a362425..6c27bcd39f12d0 100644 --- a/examples/embeddable_explorer/kibana.json +++ b/examples/embeddable_explorer/kibana.json @@ -5,6 +5,6 @@ "configPath": ["embeddable_explorer"], "server": false, "ui": true, - "requiredPlugins": ["embeddable", "embeddableExamples"], + "requiredPlugins": ["uiActions", "inspector", "embeddable", "embeddableExamples"], "optionalPlugins": [] } diff --git a/examples/embeddable_explorer/public/app.tsx b/examples/embeddable_explorer/public/app.tsx index be27fd04556eae..da7e8cc188e31f 100644 --- a/examples/embeddable_explorer/public/app.tsx +++ b/examples/embeddable_explorer/public/app.tsx @@ -23,11 +23,21 @@ import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui'; -import { IEmbeddableStart } from 'src/plugins/embeddable/public'; -import { AppMountContext, AppMountParameters, CoreStart } from '../../../src/core/public'; +import { IEmbeddableStart } from '../../../src/plugins/embeddable/public'; +import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; +import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public'; +import { + AppMountContext, + AppMountParameters, + CoreStart, + SavedObjectsStart, + IUiSettingsClient, + OverlayStart, +} from '../../../src/core/public'; import { HelloWorldEmbeddableExample } from './hello_world_embeddable_example'; import { TodoEmbeddableExample } from './todo_embeddable_example'; import { ListContainerExample } from './list_container_example'; +import { EmbeddablePanelExample } from './embeddable_panel_example'; interface PageDef { title: string; @@ -61,15 +71,29 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => { ); }); +interface Props { + basename: string; + navigateToApp: CoreStart['application']['navigateToApp']; + embeddableApi: IEmbeddableStart; + uiActionsApi: UiActionsStart; + overlays: OverlayStart; + notifications: CoreStart['notifications']; + inspector: InspectorStartContract; + savedObject: SavedObjectsStart; + uiSettingsClient: IUiSettingsClient; +} + const EmbeddableExplorerApp = ({ basename, navigateToApp, embeddableApi, -}: { - basename: string; - navigateToApp: CoreStart['application']['navigateToApp']; - embeddableApi: IEmbeddableStart; -}) => { + inspector, + uiSettingsClient, + savedObject, + overlays, + uiActionsApi, + notifications, +}: Props) => { const pages: PageDef[] = [ { title: 'Hello world embeddable', @@ -90,6 +114,22 @@ const EmbeddableExplorerApp = ({ id: 'listContainerSection', component: , }, + { + title: 'Dynamically adding children to a container', + id: 'embeddablePanelExamplae', + component: ( + + ), + }, ]; const routes = pages.map((page, i) => ( @@ -108,19 +148,8 @@ const EmbeddableExplorerApp = ({ ); }; -export const renderApp = ( - core: CoreStart, - embeddableApi: IEmbeddableStart, - { appBasePath, element }: AppMountParameters -) => { - ReactDOM.render( - , - element - ); +export const renderApp = (props: Props, element: AppMountParameters['element']) => { + ReactDOM.render(, element); return () => ReactDOM.unmountComponentAtNode(element); }; diff --git a/examples/embeddable_explorer/public/embeddable_panel_example.tsx b/examples/embeddable_explorer/public/embeddable_panel_example.tsx new file mode 100644 index 00000000000000..e6687d8563f59d --- /dev/null +++ b/examples/embeddable_explorer/public/embeddable_panel_example.tsx @@ -0,0 +1,164 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState, useEffect, useRef } from 'react'; +import { + EuiPanel, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiTitle, + EuiText, +} from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; +import { OverlayStart, CoreStart, SavedObjectsStart, IUiSettingsClient } from 'kibana/public'; +import { + GetEmbeddableFactory, + EmbeddablePanel, + IEmbeddableStart, + IEmbeddable, +} from '../../../src/plugins/embeddable/public'; +import { + HELLO_WORLD_EMBEDDABLE, + TODO_EMBEDDABLE, + MULTI_TASK_TODO_EMBEDDABLE, + SEARCHABLE_LIST_CONTAINER, +} from '../../embeddable_examples/public'; +import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; +import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public'; +import { getSavedObjectFinder } from '../../../src/plugins/saved_objects/public'; + +interface Props { + getAllEmbeddableFactories: IEmbeddableStart['getEmbeddableFactories']; + getEmbeddableFactory: GetEmbeddableFactory; + uiActionsApi: UiActionsStart; + overlays: OverlayStart; + notifications: CoreStart['notifications']; + inspector: InspectorStartContract; + savedObject: SavedObjectsStart; + uiSettingsClient: IUiSettingsClient; +} + +export function EmbeddablePanelExample({ + inspector, + notifications, + overlays, + getAllEmbeddableFactories, + getEmbeddableFactory, + uiActionsApi, + savedObject, + uiSettingsClient, +}: Props) { + const searchableInput = { + id: '1', + title: 'My searchable todo list', + panels: { + '1': { + type: HELLO_WORLD_EMBEDDABLE, + explicitInput: { + id: '1', + title: 'Hello', + }, + }, + '2': { + type: TODO_EMBEDDABLE, + explicitInput: { + id: '2', + task: 'Goes out on Wednesdays!', + icon: 'broom', + title: 'Take out the trash', + }, + }, + '3': { + type: MULTI_TASK_TODO_EMBEDDABLE, + explicitInput: { + id: '3', + icon: 'searchProfilerApp', + title: 'Learn more', + tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'], + }, + }, + }, + }; + + const [embeddable, setEmbeddable] = useState(undefined); + + const ref = useRef(false); + + useEffect(() => { + ref.current = true; + if (!embeddable) { + const factory = getEmbeddableFactory(SEARCHABLE_LIST_CONTAINER); + const promise = factory?.create(searchableInput); + if (promise) { + promise.then(e => { + if (ref.current) { + setEmbeddable(e); + } + }); + } + } + return () => { + ref.current = false; + }; + }); + + return ( + + + + +

The embeddable panel component

+
+
+
+ + + + You can render your embeddable inside the EmbeddablePanel component. This adds some + extra rendering and offers a context menu with pluggable actions. Using EmbeddablePanel + to render your embeddable means you get access to the "e;Add panel flyout"e;. + Now you can see how to add embeddables to your container, and how + "e;getExplicitInput"e; is used to grab input not provided by the container. + + + {embeddable ? ( + + ) : ( + Loading... + )} + + + + + +
+ ); +} diff --git a/examples/embeddable_explorer/public/list_container_example.tsx b/examples/embeddable_explorer/public/list_container_example.tsx index 49cfae0d4e455a..2c7b12a27d963a 100644 --- a/examples/embeddable_explorer/public/list_container_example.tsx +++ b/examples/embeddable_explorer/public/list_container_example.tsx @@ -60,7 +60,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) { type: TODO_EMBEDDABLE, explicitInput: { id: '2', - task: 'Goes out on Wenesdays!', + task: 'Goes out on Wednesdays!', icon: 'broom', title: 'Take out the trash', }, @@ -91,7 +91,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) { type: TODO_EMBEDDABLE, explicitInput: { id: '2', - task: 'Goes out on Wenesdays!', + task: 'Goes out on Wednesdays!', icon: 'broom', title: 'Take out the trash', }, @@ -102,7 +102,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) { id: '3', icon: 'searchProfilerApp', title: 'Learn more', - tasks: ['Go to school', 'Watch planet earth', 'Read the encylopedia'], + tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'], }, }, }, @@ -151,6 +151,11 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) { The first HelloWorldEmbeddable does not emit the hasMatch output variable, so the container chooses to hide it.

+ +

+ Check out the "e;Dynamically adding children"e; section, to see how to add + children to this container, and see it rendered inside an `EmbeddablePanel` component. +

diff --git a/examples/embeddable_explorer/public/plugin.tsx b/examples/embeddable_explorer/public/plugin.tsx index 2576dea0cadbc8..1294e0c89c9e7d 100644 --- a/examples/embeddable_explorer/public/plugin.tsx +++ b/examples/embeddable_explorer/public/plugin.tsx @@ -18,17 +18,38 @@ */ import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; +import { UiActionsService } from '../../../src/plugins/ui_actions/public'; import { IEmbeddableStart } from '../../../src/plugins/embeddable/public'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; -export class EmbeddableExplorerPlugin implements Plugin { - public setup(core: CoreSetup<{ embeddable: IEmbeddableStart }>) { +interface StartDeps { + uiActions: UiActionsService; + embeddable: IEmbeddableStart; + inspector: InspectorStart; +} + +export class EmbeddableExplorerPlugin implements Plugin { + public setup(core: CoreSetup) { core.application.register({ id: 'embeddableExplorer', title: 'Embeddable explorer', async mount(params: AppMountParameters) { const [coreStart, depsStart] = await core.getStartServices(); const { renderApp } = await import('./app'); - return renderApp(coreStart, depsStart.embeddable, params); + return renderApp( + { + notifications: coreStart.notifications, + inspector: depsStart.inspector, + embeddableApi: depsStart.embeddable, + uiActionsApi: depsStart.uiActions, + basename: params.appBasePath, + uiSettingsClient: coreStart.uiSettings, + savedObject: coreStart.savedObjects, + overlays: coreStart.overlays, + navigateToApp: coreStart.application.navigateToApp, + }, + params.element + ); }, }); } diff --git a/examples/search_explorer/public/application.tsx b/examples/search_explorer/public/application.tsx index 801a3c615ac613..7d921adc1d29bf 100644 --- a/examples/search_explorer/public/application.tsx +++ b/examples/search_explorer/public/application.tsx @@ -28,12 +28,13 @@ import { EuiSideNav, } from '@elastic/eui'; -import { AppMountContext, AppMountParameters } from '../../../src/core/public'; +import { AppMountParameters, CoreStart } from '../../../src/core/public'; import { EsSearchTest } from './es_strategy'; import { Page } from './page'; import { DemoStrategy } from './demo_strategy'; import { DocumentationPage } from './documentation'; import { SearchApiPage } from './search_api'; +import { AppPluginStartDependencies, SearchBarComponentParams } from './types'; const Home = () => ; @@ -44,7 +45,7 @@ interface PageDef { } type NavProps = RouteComponentProps & { - navigateToApp: AppMountContext['core']['application']['navigateToApp']; + navigateToApp: CoreStart['application']['navigateToApp']; pages: PageDef[]; }; @@ -71,7 +72,7 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => { const buildPage = (page: PageDef) => {page.component}; -const SearchApp = ({ basename, context }: { basename: string; context: AppMountContext }) => { +const SearchApp = ({ basename, data, application }: SearchBarComponentParams) => { const pages: PageDef[] = [ { id: 'home', @@ -86,12 +87,12 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC { title: 'ES search strategy', id: 'esSearch', - component: , + component: , }, { title: 'Demo search strategy', id: 'demoSearch', - component: , + component: , }, ]; @@ -103,7 +104,7 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC -